<commit_before>var events = require("./notification.js");
var ClassInfo = require("./configuration.js").classInfo;
var Resource = require("../resource");

var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver),
    mutObserver;

if(MutationObserver){
    mutObserver = new MutationObserver(handleMutations);
} else {
    XML3D.debug.logError("XML3D requires MutationObservers, which your browser does not support. Please consider upgrading to a newer version.");
    mutObserver = {
        takeRecords:function(){return []},
        observe: function(a,b) {}
    }
}

XML3D.flushDOMChanges = function(){
    var records = mutObserver.takeRecords();
    records.length && handleMutations(records);
};

function handleMutations(mutations) {
    for(var i = 0; i < mutations.length; ++i){
        var mutationRecord = mutations[i];
        if (mutationRecord.type === 'attributes') {
            handleAttributeChanged(mutationRecord);
        } else if (mutationRecord.type === 'childList') {
            handleChildListChanged(mutationRecord);
        } else if(mutationRecord.type == 'characterData'){
            handleCharacterDataChanged(mutationRecord);
        }
    }
}

function handleCharacterDataChanged(mutation) {
    var target = mutation.target;
    while(!target._configured && target.parentElement) {
        target = target.parentElement;
    }
    var elementHandler = target._configured;
    if (!elementHandler) {
        return;
    }
    var n = new events.NotificationWrapper(mutation, events.VALUE_MODIFIED, target);
    if (elementHandler.handlers.value !== undefined) {
        elementHandler.handlers.value.resetValue(elementHandler.storage);
    }
    elementHandler.notify(n);
}

function handleChildListChanged(mutation) {
    var addedNodes = mutation.addedNodes;
    for (var i = 0; i < addedNodes.length; i++) {
        if (addedNodes[i].nodeType === Node.TEXT_NODE){
            // This may have been the value of eg. a float3 element, we should treat it as a characterDataChanged event
            handleCharacterDataChanged(mutation);
            continue;
        }
        handleNodeInserted(addedNodes[i], mutation);
    }

    var removedNodes = mutation.removedNodes;
    for (var i=0; i < removedNodes.length; i++) {
        if (removedNodes[i].nodeType === Node.TEXT_NODE){
            continue; // characterDataChanged events were already handled in addedNodes
        }
        handleNodeRemoved(removedNodes[i], mutation);
    }
}

function handleNodeInserted(node, mutation) {
    if (!node.parentNode) {
        return; //Node may have been subsequently removed again in a mutation event that we haven't processed yet
    }
    var targetHandler = mutation.target._configured;
    if (!targetHandler) {
        return;
    }
    config.element(node);
    addRecursive(node);
    var n = new events.NotificationWrapper(mutation, events.NODE_INSERTED, node);
    targetHandler.notify(n);
}

function handleNodeRemoved(node, mutation) {
    var targetHandler = mutation.target._configured;
    if (!targetHandler) {
        return;
    }
    var n = new events.NotificationWrapper(mutation, events.NODE_REMOVED, node);
    targetHandler.notify(n);
    if(node._configured) {
        n.type = events.THIS_REMOVED;
        removeRecursive(node, n);
    } else if (node.nodeType === Node.TEXT_NODE){
        // This may have been the value of eg. a float3 element, we should also treat it as a characterDataChanged event
        handleCharacterDataChanged(mutation);
    }
}

function removeRecursive(element, evt) {
    if(element._configured) {
        Resource.notifyNodeIdChange(element, element.id, null);
        element._configured.notify(evt);
        delete element._configured;
    }
    var child = element.firstElementChild;
    while(child) {
        removeRecursive(child, evt);
        child = child.nextElementSibling;
    }
    if (element.shadowRoot) {
        removeRecursive(element.shadowRoot, evt);
    }

}

function addRecursive(element){
    var n = element.firstElementChild;
    while(n) {
        addRecursive(n);
        n = n.nextElementSibling;
    }
    // We call this here in addition to nodeInsertedIntoDocument, since the later is not supported by Firefox
    Resource.notifyNodeIdChange(element, null, element.id);
}

/**
 *
 * @param {MutationRecord} mutation
 */
function handleAttributeChanged(mutation) {
    var target = mutation.target;
    var elementHandler = target._configured;
    if (!elementHandler) {
        return;
    }

    var attributeHandler = elementHandler.handlers[mutation.attributeName] || elementHandler.handlers[mutation.attributeName.toLowerCase()];
    if (attributeHandler && attributeHandler.setFromAttribute) {
        var newValue = target.getAttribute(mutation.attributeName);
        var notified = attributeHandler.setFromAttribute(newValue, mutation.oldValue, target, elementHandler.storage);
        XML3D.debug.assert(!notified, "We assume no attribute handler notifies adapters anymore.");
    }
    elementHandler.attributeChangedCallback(mutation);
}


var ElementHandler = function(elem) {
    if (!elem) {
        return;
    }
    this.element = elem;
    this.handlers = null;
    this.storage = {};
    this.adapters = {};
    mutObserver.observe(elem, { childList: true,  attributes: true, attributeOldValue: true} );

};

ElementHandler.prototype.registerAttributes = function(config) {
    var elem = this.element;

    var isHTML = (elem instanceof HTMLElement);
    var keyPrefix = (isHTML ? "_html" : "_xml");
    var handlerKey = keyPrefix + "handlers",
        protoKey = keyPrefix + "proto";

    var canProto = !!elem.__proto__;

    if(!config._cache) config._cache = {};
<commit_msg>Fixed a bug in element proto caching that caused some elements to be created with the wrong prototype<commit_after>var events = require("./notification.js");
var ClassInfo = require("./configuration.js").classInfo;
var Resource = require("../resource");

var MutationObserver = (window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver),
    mutObserver;

if(MutationObserver){
    mutObserver = new MutationObserver(handleMutations);
} else {
    XML3D.debug.logError("XML3D requires MutationObservers, which your browser does not support. Please consider upgrading to a newer version.");
    mutObserver = {
        takeRecords:function(){return []},
        observe: function(a,b) {}
    }
}

XML3D.flushDOMChanges = function(){
    var records = mutObserver.takeRecords();
    records.length && handleMutations(records);
};

function handleMutations(mutations) {
    for(var i = 0; i < mutations.length; ++i){
        var mutationRecord = mutations[i];
        if (mutationRecord.type === 'attributes') {
            handleAttributeChanged(mutationRecord);
        } else if (mutationRecord.type === 'childList') {
            handleChildListChanged(mutationRecord);
        } else if(mutationRecord.type == 'characterData'){
            handleCharacterDataChanged(mutationRecord);
        }
    }
}

function handleCharacterDataChanged(mutation) {
    var target = mutation.target;
    while(!target._configured && target.parentElement) {
        target = target.parentElement;
    }
    var elementHandler = target._configured;
    if (!elementHandler) {
        return;
    }
    var n = new events.NotificationWrapper(mutation, events.VALUE_MODIFIED, target);
    if (elementHandler.handlers.value !== undefined) {
        elementHandler.handlers.value.resetValue(elementHandler.storage);
    }
    elementHandler.notify(n);
}

function handleChildListChanged(mutation) {
    var addedNodes = mutation.addedNodes;
    for (var i = 0; i < addedNodes.length; i++) {
        if (addedNodes[i].nodeType === Node.TEXT_NODE){
            // This may have been the value of eg. a float3 element, we should treat it as a characterDataChanged event
            handleCharacterDataChanged(mutation);
            continue;
        }
        handleNodeInserted(addedNodes[i], mutation);
    }

    var removedNodes = mutation.removedNodes;
    for (var i=0; i < removedNodes.length; i++) {
        if (removedNodes[i].nodeType === Node.TEXT_NODE){
            continue; // characterDataChanged events were already handled in addedNodes
        }
        handleNodeRemoved(removedNodes[i], mutation);
    }
}

function handleNodeInserted(node, mutation) {
    if (!node.parentNode) {
        return; //Node may have been subsequently removed again in a mutation event that we haven't processed yet
    }
    var targetHandler = mutation.target._configured;
    if (!targetHandler) {
        return;
    }
    config.element(node);
    addRecursive(node);
    var n = new events.NotificationWrapper(mutation, events.NODE_INSERTED, node);
    targetHandler.notify(n);
}

function handleNodeRemoved(node, mutation) {
    var targetHandler = mutation.target._configured;
    if (!targetHandler) {
        return;
    }
    var n = new events.NotificationWrapper(mutation, events.NODE_REMOVED, node);
    targetHandler.notify(n);
    if(node._configured) {
        n.type = events.THIS_REMOVED;
        removeRecursive(node, n);
    } else if (node.nodeType === Node.TEXT_NODE){
        // This may have been the value of eg. a float3 element, we should also treat it as a characterDataChanged event
        handleCharacterDataChanged(mutation);
    }
}

function removeRecursive(element, evt) {
    if(element._configured) {
        Resource.notifyNodeIdChange(element, element.id, null);
        element._configured.notify(evt);
        delete element._configured;
    }
    var child = element.firstElementChild;
    while(child) {
        removeRecursive(child, evt);
        child = child.nextElementSibling;
    }
    if (element.shadowRoot) {
        removeRecursive(element.shadowRoot, evt);
    }

}

function addRecursive(element){
    var n = element.firstElementChild;
    while(n) {
        addRecursive(n);
        n = n.nextElementSibling;
    }
    // We call this here in addition to nodeInsertedIntoDocument, since the later is not supported by Firefox
    Resource.notifyNodeIdChange(element, null, element.id);
}

/**
 *
 * @param {MutationRecord} mutation
 */
function handleAttributeChanged(mutation) {
    var target = mutation.target;
    var elementHandler = target._configured;
    if (!elementHandler) {
        return;
    }

    var attributeHandler = elementHandler.handlers[mutation.attributeName] || elementHandler.handlers[mutation.attributeName.toLowerCase()];
    if (attributeHandler && attributeHandler.setFromAttribute) {
        var newValue = target.getAttribute(mutation.attributeName);
        var notified = attributeHandler.setFromAttribute(newValue, mutation.oldValue, target, elementHandler.storage);
        XML3D.debug.assert(!notified, "We assume no attribute handler notifies adapters anymore.");
    }
    elementHandler.attributeChangedCallback(mutation);
}


var ElementHandler = function(elem) {
    if (!elem) {
        return;
    }
    this.element = elem;
    this.handlers = null;
    this.storage = {};
    this.adapters = {};
    mutObserver.observe(elem, { childList: true,  attributes: true, attributeOldValue: true} );

};

ElementHandler.prototype.registerAttributes = function(config) {
    var elem = this.element;

    var isHTML = (elem instanceof HTMLElement);
    var keyPrefix = (isHTML ? "_html" : "_xml");
    var handlerKey = keyPrefix + elem.nodeName + "handlers",
        protoKey = keyPrefix + elem.nodeName + "proto";

    var canProto = !!elem.__proto__;

    if(!config._cache) config._cache = {};

{'fraction_non_alphanumeric': 0.10789518753211166, 'fraction_numerical': 0.0015413598218873095, 'mean_word_length': 3.3064159292035398, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '88366', 'n_tokens_mistral': 3287, 'n_tokens_neox': 3253, 'n_words': 771}