<commit_before>import React, {PropTypes} from 'react'
import FormBuilderPropTypes from '../../FormBuilderPropTypes'
import ItemForm from './ItemForm'
import ItemPreview from './ItemPreview'
import ArrayContainer from './ArrayContainer'
import {createFieldValue} from '../../state/FormBuilderState'
import styles from './styles/Array.css'
import Button from '../../buttons/Default'
import EditItem from './EditItem'

export default class Arr extends React.Component {
  static displayName = 'Array';

  static valueContainer = ArrayContainer;

  static propTypes = {
    type: FormBuilderPropTypes.type,
    field: FormBuilderPropTypes.field,
    value: PropTypes.instanceOf(ArrayContainer),
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {}
  };

  static contextTypes = {
    resolveInputComponent: PropTypes.func,
    schema: PropTypes.object
  };

  constructor(props, context) {
    super(props, context)
    this.handleAddBtnClick = this.handleAddBtnClick.bind(this)
    this.handleItemChange = this.handleItemChange.bind(this)
    this.handleItemEdit = this.handleItemEdit.bind(this)
    this.handleRemoveItem = this.handleRemoveItem.bind(this)
    this.handleClose = this.handleClose.bind(this)

    this.state = {
      selectType: false,
      addItemField: null,
      editIndex: -1
    }
  }

  handleAddBtnClick() {
    if (this.props.type.of.length > 1) {
      this.setState({selectType: true})
      return
    }
    this.handleAddItem(this.props.type.of[0])
  }

  handleAddItem(field) {
    const addItemValue = createFieldValue(void 0, {
      field: field,
      schema: this.context.schema,
      // not too elegant atm.
      resolveContainer: (_field, type) => this.context.resolveInputComponent(_field, type).valueContainer
    })

    this.props.onChange({
      patch: {$unshift: [addItemValue]}
    })

    this.setState({
      selectType: false,
      editIndex: 0
    })
  }

  handleRemoveItem(index) {
    if (index === this.state.editIndex) {
      this.setState({editIndex: -1})
    }
    this.props.onChange({
      patch: {
        $splice: [[index, 1]]
      }
    })
  }

  handleClose() {
    const {editIndex} = this.state
    const itemValue = this.props.value.at(editIndex)
    if (itemValue.isEmpty()) {
      this.handleRemoveItem(editIndex)
    }
    this.setState({editIndex: -1})
  }

  renderSelectType() {
    const {type} = this.props
    return type.of.map(field => {
      return (
        <Button
          key={field.type}
          onClick={() => this.handleAddItem(field)}
          type="button"
        >
          {field.title || field.type}
        </Button>
      )
    })
  }

  handleItemChange(event, index) {
    const patch = {[index]: event.patch}
    this.props.onChange({patch})
  }

  handleItemEdit(index) {
    this.setState({editIndex: index})
  }

  renderEditItemForm(index) {
    const itemValue = this.props.value.at(index)
    const itemField = this.getItemField(itemValue)
    return (
      <EditItem title={itemField.title} onClose={this.handleClose}>
        <ItemForm
          index={index}
          field={itemField}
          value={itemValue}
          onChange={this.handleItemChange}
          onRemove={this.handleRemoveItem}
        />
      </EditItem>
    )
  }

  getItemField(item) {
    const {type} = this.props
    return type.of.find(ofType => ofType.type === item.context.field.type)
  }

  render() {
    const {type, value} = this.props
    const {selectType, editIndex} = this.state
    return (

        <Button type="button" onClick={this.handleAddBtnClick}>+ add #TYPE</Button>
        {selectType && this.renderSelectType()}
        {value && value.map((item, i) => {
          if (editIndex === i) {
            return this.renderEditItemForm(editIndex)
          }
          const itemField = this.getItemField(item)

          if (!itemField) {
            return (

Invalid type:
{JSON.stringify(item.context.field.type)}

Only allowed types are:
{JSON.stringify(type.of)}

            )
          }

          return (

              <ItemPreview
                index={i}
                field={itemField}
                value={item}
                onEdit={this.handleItemEdit}
                onRemove={this.handleRemoveItem}
              />

          )
        })}

    )
  }
}
<commit_msg>Set field title on Add button<commit_after>import React, {PropTypes} from 'react'
import FormBuilderPropTypes from '../../FormBuilderPropTypes'
import ItemForm from './ItemForm'
import ItemPreview from './ItemPreview'
import ArrayContainer from './ArrayContainer'
import {createFieldValue} from '../../state/FormBuilderState'
import styles from './styles/Array.css'
import Button from '../../buttons/Default'
import EditItem from './EditItem'

export default class Arr extends React.Component {
  static displayName = 'Array';

  static valueContainer = ArrayContainer;

  static propTypes = {
    type: FormBuilderPropTypes.type,
    field: FormBuilderPropTypes.field,
    value: PropTypes.instanceOf(ArrayContainer),
    onChange: PropTypes.func
  };

  static defaultProps = {
    onChange() {}
  };

  static contextTypes = {
    resolveInputComponent: PropTypes.func,
    schema: PropTypes.object
  };

  constructor(props, context) {
    super(props, context)
    this.handleAddBtnClick = this.handleAddBtnClick.bind(this)
    this.handleItemChange = this.handleItemChange.bind(this)
    this.handleItemEdit = this.handleItemEdit.bind(this)
    this.handleRemoveItem = this.handleRemoveItem.bind(this)
    this.handleClose = this.handleClose.bind(this)

    this.state = {
      selectType: false,
      addItemField: null,
      editIndex: -1
    }
  }

  handleAddBtnClick() {
    if (this.props.type.of.length > 1) {
      this.setState({selectType: true})
      return
    }
    this.handleAddItem(this.props.type.of[0])
  }

  handleAddItem(field) {
    const addItemValue = createFieldValue(void 0, {
      field: field,
      schema: this.context.schema,
      // not too elegant atm.
      resolveContainer: (_field, type) => this.context.resolveInputComponent(_field, type).valueContainer
    })

    this.props.onChange({
      patch: {$unshift: [addItemValue]}
    })

    this.setState({
      selectType: false,
      editIndex: 0
    })
  }

  handleRemoveItem(index) {
    if (index === this.state.editIndex) {
      this.setState({editIndex: -1})
    }
    this.props.onChange({
      patch: {
        $splice: [[index, 1]]
      }
    })
  }

  handleClose() {
    const {editIndex} = this.state
    const itemValue = this.props.value.at(editIndex)
    if (itemValue.isEmpty()) {
      this.handleRemoveItem(editIndex)
    }
    this.setState({editIndex: -1})
  }

  renderSelectType() {
    const {type} = this.props
    return type.of.map(field => {
      return (
        <Button
          key={field.type}
          onClick={() => this.handleAddItem(field)}
          type="button"
        >
          {field.title || field.type}
        </Button>
      )
    })
  }

  handleItemChange(event, index) {
    const patch = {[index]: event.patch}
    this.props.onChange({patch})
  }

  handleItemEdit(index) {
    this.setState({editIndex: index})
  }

  renderEditItemForm(index) {
    const itemValue = this.props.value.at(index)
    const itemField = this.getItemField(itemValue)
    return (
      <EditItem title={itemField.title} onClose={this.handleClose}>
        <ItemForm
          index={index}
          field={itemField}
          value={itemValue}
          onChange={this.handleItemChange}
          onRemove={this.handleRemoveItem}
        />
      </EditItem>
    )
  }

  getItemField(item) {
    const {type} = this.props
    return type.of.find(ofType => ofType.type === item.context.field.type)
  }

  render() {
    const {type, field, value} = this.props
    const {selectType, editIndex} = this.state
    return (

        <Button type="button" onClick={this.handleAddBtnClick}>+ add {field.title}</Button>
        {selectType && this.renderSelectType()}
        {value && value.map((item, i) => {
          if (editIndex === i) {
            return this.renderEditItemForm(editIndex)
          }
          const itemField = this.getItemField(item)

          if (!itemField) {
            return (

Invalid type:
{JSON.stringify(item.context.field.type)}

Only allowed types are:
{JSON.stringify(type.of)}

            )
          }

          return (

              <ItemPreview
                index={i}
                field={itemField}
                value={item}
                onEdit={this.handleItemEdit}
                onRemove={this.handleRemoveItem}
              />

          )
        })}

    )
  }
}
<|endoftext|>


<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 = {};

    if(!config._cache[handlerKey]){
        // Create handlers and prototype only once per configuration
        var proto;
        if(canProto){
            var F = function () {
            };
            F.prototype = elem.__proto__;
            proto = new F();
        }

        var handlers = {};
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if (config[prop] === undefined) {
                if(proto) delete proto[prop];
            } else {
                if (config[prop].a !== undefined) {
                    var attrName = config[prop].id || prop;
                    var handler = new config[prop].a(attrName, config[prop].params);
                    handlers[attrName.toLowerCase()] = handler;
                    if(proto) {
                        try {
                            Object.defineProperty(proto, prop, handler.desc);
                        } catch (e) {
                            XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                        }
                    }

                } else if (config[prop].m !== undefined) {
                    if(proto) proto[prop] = config[prop].m;
                } else if (config[prop].p !== undefined) {
                    if(proto){
                        try {
                            Object.defineProperty(proto, prop, config[prop].p);
                        } catch (e) {
                            XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                        }
                    }
                }else
                    XML3D.debug.logError("Can't configure " + elem.nodeName + "::" + prop);
            }
        }
        config._cache[handlerKey] = handlers;
        config._cache[protoKey] = proto;
    }
    // Set and initialize handlers for element
    this.handlers = config._cache[handlerKey];
    if(canProto){
        elem.__proto__ = config._cache[protoKey];
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if(config[prop] && config[prop].a !== undefined){
                var attrName = config[prop].id || prop;
                var handler = this.handlers[attrName.toLowerCase()];
                handler.init && handler.init(elem, this.storage);
                delete elem[prop];
            }
        }
    }
    else{
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if (config[prop] === undefined) {
                delete elem[prop];
            }
            else if (config[prop].a !== undefined){
                var attrName = config[prop].id || prop;
                var handler = this.handlers[attrName.toLowerCase()];
                handler.init && handler.init(elem, this.storage);
                try {
                    Object.defineProperty(elem, prop, handler.desc);
                } catch (e) {
                    XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                }
            }else if (config[prop].m !== undefined) {
                elem[prop] = config[prop].m;
            } else if (config[prop].p !== undefined) {
                try {
                    Object.defineProperty(elem, prop, config[prop].p);
                } catch (e) {
                    XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                }
            }
        }
    }

    return elem;
};

ElementHandler.prototype.registerMixed = function() {
    mutObserver.observe(this.element, { childList: true,  attributes: true, attributeOldValue: true, characterData: true, subtree: true} );
};

/**
 * @param evt
 */
ElementHandler.prototype.notify =  function(evt) {
    var adapters = this.adapters;
    for(var a in adapters) {
        try {
            adapters[a].notifyChanged(evt);
        } catch (e) {
            XML3D.debug.logException(e);
        }
    }
};

/**
 * @param {MutationRecord} mutation
 */
ElementHandler.prototype.attributeChangedCallback =  function(mutation) {
    var adapters = this.adapters;
    for(var a in adapters) {
        XML3D.debug.assert(adapters[a].attributeChangedCallback, "Adapter implements 'attributeChangedCallback': " + this.element.nodeName + " (" + a + ")");
        try {
            adapters[a].attributeChangedCallback(mutation.attributeName, mutation.oldValue, this.element.getAttribute(mutation.attributeName), mutation.attributeNamespace);
        } catch (e) {
            XML3D.debug.logException(e);
        }
    }
};

/*
 * Get called, if the related node gets removed from the DOM
 */
ElementHandler.prototype.remove = function(evt) {
    for(var h in this.adapters) {
        var adapter = this.adapters[h];
        if(adapter.dispose)
            adapter.dispose();
        if(adapter.clearAdapterHandles)
            adapter.clearAdapterHandles();
    }
    this.adapters = {};
    for(var h in this.handlers) {
        var handler = this.handlers[h];
        if(handler.remove)
            handler.remove();
    }

};

ElementHandler.prototype.toString = function() {
    return "ElementHandler ("+this.element.nodeName + ", id: "+this.element.id+")";
};

var delegateProperties = ["clientHeight", "clientLeft", "clientTop", "clientWidth",
    "offsetHeight", "offsetLeft", "offsetTop", "offsetWidth"];
function delegateProp(name, elem, canvas) {
    var desc = {
        configurable : true,
        get : function() {
            return canvas[name];
        }
    };
    try {
        Object.defineProperty(elem, name, desc);
    } catch (e){
        XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + name);
    }
}

var XML3DHandler = function(elem) {
    ElementHandler.call(this, elem);
    var c = document.createElement("canvas");
    c.width = 800;
    c.height = 600;
    this.canvas = c;
    this.canvasHandler = { destroy:function() {} };

    for(var i in delegateProperties) {
        delegateProp(delegateProperties[i], elem, c);
    }

    elem.getBoundingClientRect = function() {
        return c.getBoundingClientRect();
    };

    this.destroy = function() {
        for(var i in delegateProperties) {
            delete elem[delegateProperties[i]];
        }
        this.canvasHandler.destroy();
    }
};

XML3D.createClass(XML3DHandler, ElementHandler);

var config = {};
var webComponentCounter = 0;

/**
 * @param {Element} element
 * @return {undefined}
 */
config.element = function(element) {
    if (element._configured === undefined ) {
        var classInfo = ClassInfo[element.localName];
        if (classInfo === undefined) {
            if (element.nodeType !== 1) {
                return; //Not an element node so ignore this
            }
            if (element.nodeName.indexOf("-") !== -1) {
                classInfo = ClassInfo["_web-component_"];
                element.componentId = ++webComponentCounter;
            } else {
                classInfo = ClassInfo["_dummy_"];
            }
        }

        element._configured = element.localName == "xml3d" ?
            new XML3DHandler(element)
            : new ElementHandler(element);
        element._configured.registerAttributes(classInfo);
        var n = element.firstElementChild;
        if (!n && element.shadowRoot) {
            n = element.shadowRoot.firstElementChild;
        }

        Resource.notifyNodeIdChange(element, null, element.getAttribute("id"));

        while(n) {
            config.element(n);
            n = n.nextElementSibling;
        }
    }
};

/**
 * @param {Element} element
 * @return {undefined}
 */
config.configure = function(element) {
    if (Array.isArray(element)) {
        element.forEach(function(el) {
            config.element(el);
        });
    } else {
        config.element(element);
    }
};

module.exports = {
    ElementHandler : ElementHandler,
    XML3DHandler : XML3DHandler,
    config : config
};
<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 = {};

    if(!config._cache[handlerKey]){
        // Create handlers and prototype only once per configuration
        var proto;
        if(canProto){
            var F = function () {
            };
            F.prototype = elem.__proto__;
            proto = new F();
        }

        var handlers = {};
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if (config[prop] === undefined) {
                if(proto) delete proto[prop];
            } else {
                if (config[prop].a !== undefined) {
                    var attrName = config[prop].id || prop;
                    var handler = new config[prop].a(attrName, config[prop].params);
                    handlers[attrName.toLowerCase()] = handler;
                    if(proto) {
                        try {
                            Object.defineProperty(proto, prop, handler.desc);
                        } catch (e) {
                            XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                        }
                    }

                } else if (config[prop].m !== undefined) {
                    if(proto) proto[prop] = config[prop].m;
                } else if (config[prop].p !== undefined) {
                    if(proto){
                        try {
                            Object.defineProperty(proto, prop, config[prop].p);
                        } catch (e) {
                            XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                        }
                    }
                }else
                    XML3D.debug.logError("Can't configure " + elem.nodeName + "::" + prop);
            }
        }
        config._cache[handlerKey] = handlers;
        config._cache[protoKey] = proto;
    }
    // Set and initialize handlers for element
    this.handlers = config._cache[handlerKey];
    if(canProto){
        elem.__proto__ = config._cache[protoKey];
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if(config[prop] && config[prop].a !== undefined){
                var attrName = config[prop].id || prop;
                var handler = this.handlers[attrName.toLowerCase()];
                handler.init && handler.init(elem, this.storage);
                delete elem[prop];
            }
        }
    }
    else{
        for ( var prop in config) {
            if(prop =="_cache") continue;
            if (config[prop] === undefined) {
                delete elem[prop];
            }
            else if (config[prop].a !== undefined){
                var attrName = config[prop].id || prop;
                var handler = this.handlers[attrName.toLowerCase()];
                handler.init && handler.init(elem, this.storage);
                try {
                    Object.defineProperty(elem, prop, handler.desc);
                } catch (e) {
                    XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                }
            }else if (config[prop].m !== undefined) {
                elem[prop] = config[prop].m;
            } else if (config[prop].p !== undefined) {
                try {
                    Object.defineProperty(elem, prop, config[prop].p);
                } catch (e) {
                    XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + prop);
                }
            }
        }
    }

    return elem;
};

ElementHandler.prototype.registerMixed = function() {
    mutObserver.observe(this.element, { childList: true,  attributes: true, attributeOldValue: true, characterData: true, subtree: true} );
};

/**
 * @param evt
 */
ElementHandler.prototype.notify =  function(evt) {
    var adapters = this.adapters;
    for(var a in adapters) {
        try {
            adapters[a].notifyChanged(evt);
        } catch (e) {
            XML3D.debug.logException(e);
        }
    }
};

/**
 * @param {MutationRecord} mutation
 */
ElementHandler.prototype.attributeChangedCallback =  function(mutation) {
    var adapters = this.adapters;
    for(var a in adapters) {
        XML3D.debug.assert(adapters[a].attributeChangedCallback, "Adapter implements 'attributeChangedCallback': " + this.element.nodeName + " (" + a + ")");
        try {
            adapters[a].attributeChangedCallback(mutation.attributeName, mutation.oldValue, this.element.getAttribute(mutation.attributeName), mutation.attributeNamespace);
        } catch (e) {
            XML3D.debug.logException(e);
        }
    }
};

/*
 * Get called, if the related node gets removed from the DOM
 */
ElementHandler.prototype.remove = function(evt) {
    for(var h in this.adapters) {
        var adapter = this.adapters[h];
        if(adapter.dispose)
            adapter.dispose();
        if(adapter.clearAdapterHandles)
            adapter.clearAdapterHandles();
    }
    this.adapters = {};
    for(var h in this.handlers) {
        var handler = this.handlers[h];
        if(handler.remove)
            handler.remove();
    }

};

ElementHandler.prototype.toString = function() {
    return "ElementHandler ("+this.element.nodeName + ", id: "+this.element.id+")";
};

var delegateProperties = ["clientHeight", "clientLeft", "clientTop", "clientWidth",
    "offsetHeight", "offsetLeft", "offsetTop", "offsetWidth"];
function delegateProp(name, elem, canvas) {
    var desc = {
        configurable : true,
        get : function() {
            return canvas[name];
        }
    };
    try {
        Object.defineProperty(elem, name, desc);
    } catch (e){
        XML3D.debug.logWarning("Can't configure " + elem.nodeName + "::" + name);
    }
}

var XML3DHandler = function(elem) {
    ElementHandler.call(this, elem);
    var c = document.createElement("canvas");
    c.width = 800;
    c.height = 600;
    this.canvas = c;
    this.canvasHandler = { destroy:function() {} };

    for(var i in delegateProperties) {
        delegateProp(delegateProperties[i], elem, c);
    }

    elem.getBoundingClientRect = function() {
        return c.getBoundingClientRect();
    };

    this.destroy = function() {
        for(var i in delegateProperties) {
            delete elem[delegateProperties[i]];
        }
        this.canvasHandler.destroy();
    }
};

XML3D.createClass(XML3DHandler, ElementHandler);

var config = {};
var webComponentCounter = 0;

/**
 * @param {Element} element
 * @return {undefined}
 */
config.element = function(element) {
    if (element._configured === undefined ) {
        var classInfo = ClassInfo[element.localName];
        if (classInfo === undefined) {
            if (element.nodeType !== 1) {
                return; //Not an element node so ignore this
            }
            if (element.nodeName.indexOf("-") !== -1) {
                classInfo = ClassInfo["_web-component_"];
                element.componentId = ++webComponentCounter;
            } else {
                classInfo = ClassInfo["_dummy_"];
            }
        }

        element._configured = element.localName == "xml3d" ?
            new XML3DHandler(element)
            : new ElementHandler(element);
        element._configured.registerAttributes(classInfo);
        var n = element.firstElementChild;
        if (!n && element.shadowRoot) {
            n = element.shadowRoot.firstElementChild;
        }

        Resource.notifyNodeIdChange(element, null, element.getAttribute("id"));

        while(n) {
            config.element(n);
            n = n.nextElementSibling;
        }
    }
};

/**
 * @param {Element} element
 * @return {undefined}
 */
config.configure = function(element) {
    if (Array.isArray(element)) {
        element.forEach(function(el) {
            config.element(el);
        });
    } else {
        config.element(element);
    }
};

module.exports = {
    ElementHandler : ElementHandler,
    XML3DHandler : XML3DHandler,
    config : config
};
<|endoftext|>


<commit_before>/**
 * @fileoverview Wraps services in a format needed internally for invocation.
 *
 * This effectly converts between two calling styles of services.
 *
 * The unwrapped services have the needed injections and args in a sequential
 * list.
 * e.g. function(a, b, $c, d)
 *
 * The wrapped service split args and injections:
 * e.g. function(wireArgArray, possibleInjects)
 * where wireArgArray is a list of args as sent over the wire (i.e. those not
 * including injections).
 * possibleInjects is a map from injection name to value. This may include
 * injections that the function doesn't actually use.
 *
 * Wrapped services also define a signature function if one does not exist
 * on the underlying service.
 */

module.exports = wrapService;

var argHelper = require('../lib/arg_helper');
var Signature = require('../vdl/signature');
var vError = require('../lib/verror');
var vlog = require('../lib/vlog');

/**
  * @param {Service} service Unwrapped service
  * @param {Descriptor=} desc Service descriptor, used for signature().
  * See signature.js for more details.
  * @return {WrappedService} wrappedService Wrapped service
  */
function wrapService(service, desc) {
    var wrappedService = {};
    for (var methodName in service) {
        if (typeof service[methodName] === 'function') {
            var method = service[methodName];
            var injectionPositions = argHelper.getInjectionPositions(method);
            var argOffsets = argHelper.getArgOffsets(method);
            wrappedService[methodName] = invoke.bind(null, service,
              method, argOffsets, injectionPositions);
        }
    }
    if (!wrappedService.hasOwnProperty('signature')) {
        wrappedService.signature = function() {
          return new Signature(service, desc);
        };
    }
    return wrappedService;
}

/**
  * Translates args to the native list-of-injections-and-non-injections style.
  * @param {int[]} argOffsets List of original indicies of args. See
  * getArgOffsets().
  * @param {map[string]int} injectionPositions Map from injection name to
  * position.
  * @param {Object[]} args List of args coming off of the wire.
  * @param {map[string]Object} Object containing possible injection values.
  * @return {string[]} A list of combined injections and args off the wire.
  */
function translateToTrueCallArgs(argOffsets, injectionPositions, args,
  potentialInjections) {
    var callArgs = new Array(argOffsets.length +
      Object.keys(injectionPositions).length);
    for (var i = 0; i < args.length; i++) {
        callArgs[argOffsets[i]] = args[i];
    }
    for (var injection in injectionPositions) {
        if (injectionPositions.hasOwnProperty(injection)) {
          if (!potentialInjections.hasOwnProperty(injection)) {
            vlog.warn('Function received unknown injection ' + injection);
          }
          callArgs[injectionPositions[injection]] =
            potentialInjections[injection];
        }
    }
    return callArgs;
}

/**
  * Invoke a service method.
  * @param {Service} service The service object to invoke a method on.
  * @param {Function} method The method to invoke
  * @param {int[]} argOffsets List of original indicies of args. See
  * getArgOffsets().
  * @param {map[string]int} injectionPositions Map from injection name to
  * position.
  * @param {Object[]} args List of args coming off of the wire.
  * @param {map[string]Object} Object containing possible injection values.
  * @return {string[]} A list of combined injections and args off the wire.
  */
function invoke(
  service, method, argOffsets, injectionPositions, // Bound by wrapService().
  args, potentialInjections // Passed on invocation.
  ) {
    if (args.length !== argOffsets.length) {
        throw new vError.BadArgError('Expected ' + argOffsets + ' args, got ' +
          args);
    }
    var callArgs = translateToTrueCallArgs(argOffsets, injectionPositions,
      args, potentialInjections);

    return method.apply(service, callArgs);
}
<commit_msg>Fix doc<commit_after>/**
 * @fileoverview Wraps services in a format needed internally for invocation.
 *
 * This effectly converts between two calling styles of services.
 *
 * The unwrapped services have the needed injections and args in a sequential
 * list.
 * e.g. function(a, b, $c, d)
 *
 * The wrapped service split args and injections:
 * e.g. function(wireArgArray, possibleInjects)
 * where wireArgArray is a list of args as sent over the wire (i.e. those not
 * including injections).
 * possibleInjects is a map from injection name to value. This may include
 * injections that the function doesn't actually use.
 *
 * Wrapped services also define a signature function if one does not exist
 * on the underlying service.
 */

module.exports = wrapService;

var argHelper = require('../lib/arg_helper');
var Signature = require('../vdl/signature');
var vError = require('../lib/verror');
var vlog = require('../lib/vlog');

/**
  * @param {Service} service Unwrapped service
  * @param {Descriptor=} desc Service descriptor, used for signature().
  * See signature.js for more details.
  * @return {WrappedService} wrappedService Wrapped service
  */
function wrapService(service, desc) {
    var wrappedService = {};
    for (var methodName in service) {
        if (typeof service[methodName] === 'function') {
            var method = service[methodName];
            var injectionPositions = argHelper.getInjectionPositions(method);
            var argOffsets = argHelper.getArgOffsets(method);
            wrappedService[methodName] = invoke.bind(null, service,
              method, argOffsets, injectionPositions);
        }
    }
    if (!wrappedService.hasOwnProperty('signature')) {
        wrappedService.signature = function() {
          return new Signature(service, desc);
        };
    }
    return wrappedService;
}

/**
  * Translates args to the native list-of-injections-and-non-injections style.
  * @param {int[]} argOffsets List of original indicies of args. See
  * getArgOffsets().
  * @param {Object.<string,number>} injectionPositions Map from injection name to
  * position.
  * @param {Object[]} args List of args coming off of the wire.
  * @param {Object.<string,Object>} Object containing possible injection values.
  * @return {string[]} A list of combined injections and args off the wire.
  */
function translateToTrueCallArgs(argOffsets, injectionPositions, args,
  potentialInjections) {
    var callArgs = new Array(argOffsets.length +
      Object.keys(injectionPositions).length);
    for (var i = 0; i < args.length; i++) {
        callArgs[argOffsets[i]] = args[i];
    }
    for (var injection in injectionPositions) {
        if (injectionPositions.hasOwnProperty(injection)) {
          if (!potentialInjections.hasOwnProperty(injection)) {
            vlog.warn('Function received unknown injection ' + injection);
          }
          callArgs[injectionPositions[injection]] =
            potentialInjections[injection];
        }
    }
    return callArgs;
}

/**
  * Invoke a service method.
  * @param {Service} service The service object to invoke a method on.
  * @param {Function} method The method to invoke
  * @param {int[]} argOffsets List of original indicies of args. See
  * getArgOffsets().
  * @param {Object.<string,number>} injectionPositions Map from injection name to
  * position.
  * @param {Object[]} args List of args coming off of the wire.
  * @param {Object.<string,Object>} Object containing possible injection values.
  * @return {string[]} A list of combined injections and args off the wire.
  */
function invoke(
  service, method, argOffsets, injectionPositions, // Bound by wrapService().
  args, potentialInjections // Passed on invocation.
  ) {
    if (args.length !== argOffsets.length) {
        throw new vError.BadArgError('Expected ' + argOffsets + ' args, got ' +
          args);
    }
    var callArgs = translateToTrueCallArgs(argOffsets, injectionPositions,
      args, potentialInjections);

    return method.apply(service, callArgs);
}
<|endoftext|>


<commit_before>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/*
 * Boolean Geometric Path Operations
 *
 * This is mostly written for clarity and compatibility, not optimised for
 * performance, and has to be tested heavily for stability.
 *
 * Supported
 *  - paperjs Path and CompoundPath objects
 *  - Boolean Union
 *  - Boolean Intersection
 *  - Boolean Subtraction
 *  - Resolving a self-intersecting Path
 *
 * Not supported yet
 *  - Boolean operations on self-intersecting Paths
 *  - Paths are clones of each other that ovelap exactly on top of each other!
 *
 * @author Harikrishnan Gopalakrishnan
 * http://hkrish.com/playground/paperjs/booleanStudy.html
 */

PathItem.inject({

	// A boolean operator is a binary operator function of the form
	// function(isPath1, isInPath1, isInPath2)
	//
	// Operators return true if a curve in the operands is to be removed,
	// and they aare called for each curve segment in the graph after all the
	// intersections between the operands are calculated and curves in the
	// operands were split at intersections.
	//
	// These functions should have a name ("union", "subtraction" etc. below),
	// if we need to do operator specific operations on paths inside the
	// computeBoolean function.
	// For example: If the name of the operator is "subtraction" then we need to
	// reverse the second operand. Subtraction is neither associative nor
	// commutative.
	//
	//  The boolean operator return a Boolean value indicating whether to
	// keep the curve or not.
	//  return true - discard the curve
	//  return false - keep the curve
	unite: function(path, _cache) {
		return this._computeBoolean(this, path,
				function union(isPath1, isInPath1, isInPath2) {
					return isInPath1 || isInPath2;
				}, _cache);
	},

	intersect: function(path, _cache) {
		return this._computeBoolean(this, path,
				function intersection(isPath1, isInPath1, isInPath2) {
					return !(isInPath1 || isInPath2);
				}, _cache);
	},

	subtract: function(path, _cache) {
		return this._computeBoolean(this, path,
				function subtraction(isPath1, isInPath1, isInPath2) {
					return isPath1 && isInPath2 || !isPath1 && !isInPath1;
				}, _cache);
	},

	// Compound boolean operators combine the basic boolean operations such as
	// union, intersection, subtract etc.
	// TODO: cache the split objects and find a way to properly clone them!
	// a.k.a. eXclusiveOR
	exclude: function(path) {
		var res1 = this.subtract(path);
		var res2 = path.subtract(this);
		var res = new Group([res1, res2]);
		return res;
	},

	// Divide path1 by path2
	divide: function(path) {
		var res1 = this.subtract(path);
		var res2 = this.intersect(path);
		var res = new Group([res1, res2]);
		return res;
	},

	_splitPath: function(_ixs, other) {
		// Sort function for sorting intersections in the descending order
		function sortIx(a, b) { return b.parameter - a.parameter; }
		other = other || false;
		var i, j, k, l, len, ixs, ix, path, crv, vals;
		var ixPoint, nuSeg;
		var paths = {}, lastPathId = null;
		for (i = 0, l = _ixs.length; i < l; i++) {
			ix = (other)? _ixs[i].getIntersection() : _ixs[i];
			if (!paths[ix.path.id]) {
				paths[ix.path.id] = ix.path;
			}
			if (!ix.curve._ixParams) {ix.curve._ixParams = []; }
			ix.curve._ixParams.push({ parameter: ix.parameter, pair: ix.getIntersection() });
		}
		for (k in paths) {
			if (!paths.hasOwnProperty(k)) { continue; }
			path = paths[k];
			var lastNode = path.lastSegment, firstNode = path.firstSegment;
			var nextNode = null, left = null, right = null, parts = null, isLinear;
			var handleIn, handleOut;
			while (nextNode !== firstNode) {
				nextNode = (nextNode)? nextNode.previous: lastNode;
				if (nextNode.curve._ixParams) {
					ixs = nextNode.curve._ixParams;
					ixs.sort(sortIx);
					crv = nextNode.getCurve();
					isLinear = crv.isLinear();
					crv = vals = null;
					for (i = 0, l = ixs.length; i < l; i++) {
						ix = ixs[i];
						crv = nextNode.getCurve();
						if (!vals) vals = crv.getValues();
						if (ix.parameter === 0.0 || ix.parameter === 1.0) {
							// Intersection is on an existing node
							// no need to create a new segment,
							// we just link the corresponding intersections together
							nuSeg = (ix.parameter === 0.0)? crv.segment1 : crv.segment2;
							nuSeg._ixPair = ix.pair;
							nuSeg._ixPair._segment = nuSeg;
						} else {
							parts = Curve.subdivide(vals, ix.parameter);
							left = parts[0];
							right = parts[1];
							handleIn = handleOut = null;
							ixPoint = new Point(right[0], right[1]);
							if (!isLinear) {
								crv.segment1.handleOut = new Point(left[2] - left[0], left[3] - left[1]);
								crv.segment2.handleIn = new Point(right[4] - right[6], right[5] - right[7]);
								handleIn = new Point(left[4] - ixPoint.x, left[5] - ixPoint.y);
								handleOut = new Point(right[2] - ixPoint.x, right[3] - ixPoint.y);
							}
							nuSeg = new Segment(ixPoint, handleIn, handleOut);
							nuSeg._ixPair = ix.pair;
							nuSeg._ixPair._segment = nuSeg;
							path.insert(nextNode.index + 1,  nuSeg);
						}
						for (j = i + 1; j < l; j++) {
							ixs[j].parameter = ixs[j].parameter / ix.parameter;
						}
						vals = left;
					}
				}
			}
		}
	},

	/**
	 * To deal with a HTML canvas requirement where CompoundPaths' child contours
	 * has to be of different winding direction for correctly filling holes.
	 * But if some individual countours are disjoint, i.e. islands, we have to
	 * reorient them so that
	 *   the holes have opposit winding direction (already handled by paperjs)
	 *   islands has to have same winding direction (as the first child of the path)
	 *
	 * Does NOT handle selfIntersecting CompoundPaths.
	 *
	 * @param  {CompoundPath} path - Input CompoundPath, Note: This path could be modified if need be.
	 * @return {boolean}	  the winding direction of the base contour(true if clockwise)
	 */
	_reorientCompoundPath: function(path) {
		if (!(path instanceof CompoundPath)) {
			path.closed = true;
			return path.clockwise;
		}
		var children = path.children, len = children.length, baseWinding;
		var bounds = new Array(len);
		var tmparray = new Array(len);
		baseWinding = children[0].clockwise;
		// Omit the first path
		for (i = 0; i < len; i++) {
			children[i].closed = true;
			bounds[i] = children[i].bounds;
			tmparray[i] = 0;
		}
		for (i = 0; i < len; i++) {
			var p1 = children[i];
			for (j = 0; j < len; j++) {
				var p2 = children[j];
				if (i !== j && bounds[i].contains(bounds[j])) {
					tmparray[j]++;
				}
			}
		}
		for (i = 1; i < len; i++) {
			if (tmparray[i] % 2 === 0) {
				children[i].clockwise = baseWinding;
			}
		}
		return baseWinding;
	},

	_reversePath: function(path) {
		var baseWinding;
		if (path instanceof CompoundPath) {
			var children = path.children, i, len;
			for (i = 0, len = children.length; i < len; i++) {
				children[i].reverse();
				children[i]._curves = null;
			}
			baseWinding = children[0].clockwise;
		} else {
			path.reverse();
			baseWinding = path.clockwise;
			path._curves = null;
		}
		return baseWinding;
	},

	_computeBoolean: function(path1, path2, operator, _splitCache) {
		var _path1, _path2, path1Clockwise, path2Clockwise;
		var ixs, path1Id, path2Id;
		// We do not modify the operands themselves
		// The result might not belong to the same type
		// i.e. subtraction(A:Path, B:Path):CompoundPath etc.
		_path1 = path1.clone();
		_path2 = path2.clone();
		_path1.style = _path2.style = null;
		_path1.selected = _path2.selected = false;
		path1Clockwise = this._reorientCompoundPath(_path1);
		path2Clockwise = this._reorientCompoundPath(_path2);
		path1Id = _path1.id;
		path2Id = _path2.id;
		// Calculate all the intersections
		ixs = (_splitCache && _splitCache.intersections)?
		_splitCache.intersections : _path1.getIntersections(_path2);
		// if we have a empty _splitCache object as an operand,
		// skip calculating boolean and cache the intersections
		if (_splitCache && !_splitCache.intersections) {
			_splitCache.intersections = ixs;
			return;
		}
		this._splitPath(ixs);
		this._splitPath(ixs, true);
		path1Id = _path1.id;
		path2Id = _path2.id;
		// Do operator specific calculations before we begin
		if (operator.name === "subtraction") {
			path2Clockwise = this._reversePath(_path2);
		}

		var i, j, len, path, crv;
		var paths = [];
		if (_path1 instanceof CompoundPath) {
			paths = paths.concat(_path1.children);
		} else {
			paths = [ _path1 ];
		}
		if (_path2 instanceof CompoundPath) {
			paths = paths.concat(_path2.children);
		} else {
			paths.push(_path2);
		}
		// step 1: discard invalid links according to the boolean operator
		var lastNode, firstNode, nextNode, midPoint, insidePath1, insidePath2;
		var thisId, thisWinding, contains, subtractionOp = (operator.name === 'subtraction');
		for (i = 0, len = paths.length; i < len; i++) {
			insidePath1 = insidePath2 = false;
			path = paths[i];
			thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
			thisWinding = path.clockwise;
			lastNode = path.lastSegment;
			firstNode = path.firstSegment;
			nextNode = null;
			while (nextNode !== firstNode) {
				nextNode = (nextNode)? nextNode.previous: lastNode;
				crv = nextNode.curve;
				midPoint = crv.getPoint(0.5);
				if (thisId !== path1Id) {
					contains = _path1.
					contains(midPoint);
					insidePath1 = (thisWinding === path1Clockwise || subtractionOp)? contains :
					contains && !this._testOnCurve(_path1, midPoint);
				}
				if (thisId !== path2Id) {
					contains = _path2.contains(midPoint);
					insidePath2 = (thisWinding === path2Clockwise)? contains :
					contains && !this._testOnCurve(_path2, midPoint);
				}
				if (operator(thisId === path1Id, insidePath1, insidePath2)) {
					crv._INVALID = true;
					// markPoint(midPoint, '+');
				}
			}
		}

		// Final step: Retrieve the resulting paths from the graph
		var boolResult = new CompoundPath();
		var node, nuNode, nuPath, nodeList = [], handle;
		for (i = 0, len = paths.length; i < len; i++) {
			nodeList = nodeList.concat(paths[i].segments);
		}
		for (i = 0, len = nodeList.length; i < len; i++) {
			node = nodeList[i];
			if (node.curve._INVALID || node._visited) { continue; }
			path = node.path;
			thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
			thisWinding = path.clockwise;
			nuPath = new Path();
			firstNode = null;
			firstNode_ix = null;
			if (node.previous.curve._INVALID) {
				node.handleIn = (node._ixPair)?
				node._ixPair.getIntersection()._segment.handleIn : [ 0, 0 ];
			}
			while (node && !node._visited && (node !== firstNode && node !== firstNode_ix)) {
				node._visited = true;
				firstNode = (firstNode)? firstNode: node;
				firstNode_ix = (!firstNode_ix && firstNode._ixPair)?
				firstNode._ixPair.getIntersection()._segment: firstNode_ix;
				// node._ixPair is this node's intersection CurveLocation object
				// node._ixPair.getIntersection() is the other CurveLocation object this node intersects with
				nextNode = (node._ixPair && node.curve._INVALID)? node._ixPair.getIntersection()._segment : node;
				if (node._ixPair) {
					nextNode._visited = true;
					nuNode = new Segment(node.point, node.handleIn, nextNode.handleOut);
					nuPath.add(nuNode);
					node = nextNode;
					path = node.path;
					thisWinding = path.clockwise;
				} else {
					nuPath.add(node);
				}
				node = node.next;
			}
			if (nuPath.segments.length > 1) {
				// avoid stray segments and incomplete paths
				if (nuPath.segments.length > 2 || !nuPath.curves[0].isLinear()) {
					nuPath.closed = true;
					boolResult.addChild(nuPath, true);
				}
			}
		}
		// Delete the proxies
		_path1.remove();
		_path2.remove();
		// And then, we are done.
		return boolResult.reduce();
	},

	_testOnCurve: function(path, point) {
		var res = 0;
		var crv = path.getCurves();
		var i = 0;
		var bounds = path.bounds;
		if (bounds && bounds.contains(point)) {
			for(i = 0; i < crv.length && !res; i++) {
				var crvi = crv[i];
				if (crvi.bounds.contains(point) && crvi.getParameterOf(point)) {
					res = 1;
				}
			}
		}
		return res;
	}
});
<commit_msg>Simplify PathItem#exclude() and #divide()<commit_after>/*
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2013, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/*
 * Boolean Geometric Path Operations
 *
 * This is mostly written for clarity and compatibility, not optimised for
 * performance, and has to be tested heavily for stability.
 *
 * Supported
 *  - paperjs Path and CompoundPath objects
 *  - Boolean Union
 *  - Boolean Intersection
 *  - Boolean Subtraction
 *  - Resolving a self-intersecting Path
 *
 * Not supported yet
 *  - Boolean operations on self-intersecting Paths
 *  - Paths are clones of each other that ovelap exactly on top of each other!
 *
 * @author Harikrishnan Gopalakrishnan
 * http://hkrish.com/playground/paperjs/booleanStudy.html
 */

PathItem.inject({

	// A boolean operator is a binary operator function of the form
	// function(isPath1, isInPath1, isInPath2)
	//
	// Operators return true if a curve in the operands is to be removed,
	// and they aare called for each curve segment in the graph after all the
	// intersections between the operands are calculated and curves in the
	// operands were split at intersections.
	//
	// These functions should have a name ("union", "subtraction" etc. below),
	// if we need to do operator specific operations on paths inside the
	// computeBoolean function.
	// For example: If the name of the operator is "subtraction" then we need to
	// reverse the second operand. Subtraction is neither associative nor
	// commutative.
	//
	//  The boolean operator return a Boolean value indicating whether to
	// keep the curve or not.
	//  return true - discard the curve
	//  return false - keep the curve
	unite: function(path, _cache) {
		return this._computeBoolean(this, path,
				function union(isPath1, isInPath1, isInPath2) {
					return isInPath1 || isInPath2;
				}, _cache);
	},

	intersect: function(path, _cache) {
		return this._computeBoolean(this, path,
				function intersection(isPath1, isInPath1, isInPath2) {
					return !(isInPath1 || isInPath2);
				}, _cache);
	},

	subtract: function(path, _cache) {
		return this._computeBoolean(this, path,
				function subtraction(isPath1, isInPath1, isInPath2) {
					return isPath1 && isInPath2 || !isPath1 && !isInPath1;
				}, _cache);
	},

	// Compound boolean operators combine the basic boolean operations such as
	// union, intersection, subtract etc.
	// TODO: cache the split objects and find a way to properly clone them!
	// a.k.a. eXclusiveOR
	exclude: function(path) {
		return new Group([this.subtract(path), path.subtract(this)]);
	},

	// Divide path1 by path2
	divide: function(path) {
		return new Group([this.subtract(path), this.intersect(path)]);
	},

	_splitPath: function(_ixs, other) {
		// Sort function for sorting intersections in the descending order
		function sortIx(a, b) { return b.parameter - a.parameter; }
		other = other || false;
		var i, j, k, l, len, ixs, ix, path, crv, vals;
		var ixPoint, nuSeg;
		var paths = {}, lastPathId = null;
		for (i = 0, l = _ixs.length; i < l; i++) {
			ix = (other)? _ixs[i].getIntersection() : _ixs[i];
			if (!paths[ix.path.id]) {
				paths[ix.path.id] = ix.path;
			}
			if (!ix.curve._ixParams) {ix.curve._ixParams = []; }
			ix.curve._ixParams.push({ parameter: ix.parameter, pair: ix.getIntersection() });
		}
		for (k in paths) {
			if (!paths.hasOwnProperty(k)) { continue; }
			path = paths[k];
			var lastNode = path.lastSegment, firstNode = path.firstSegment;
			var nextNode = null, left = null, right = null, parts = null, isLinear;
			var handleIn, handleOut;
			while (nextNode !== firstNode) {
				nextNode = (nextNode)? nextNode.previous: lastNode;
				if (nextNode.curve._ixParams) {
					ixs = nextNode.curve._ixParams;
					ixs.sort(sortIx);
					crv = nextNode.getCurve();
					isLinear = crv.isLinear();
					crv = vals = null;
					for (i = 0, l = ixs.length; i < l; i++) {
						ix = ixs[i];
						crv = nextNode.getCurve();
						if (!vals) vals = crv.getValues();
						if (ix.parameter === 0.0 || ix.parameter === 1.0) {
							// Intersection is on an existing node
							// no need to create a new segment,
							// we just link the corresponding intersections together
							nuSeg = (ix.parameter === 0.0)? crv.segment1 : crv.segment2;
							nuSeg._ixPair = ix.pair;
							nuSeg._ixPair._segment = nuSeg;
						} else {
							parts = Curve.subdivide(vals, ix.parameter);
							left = parts[0];
							right = parts[1];
							handleIn = handleOut = null;
							ixPoint = new Point(right[0], right[1]);
							if (!isLinear) {
								crv.segment1.handleOut = new Point(left[2] - left[0], left[3] - left[1]);
								crv.segment2.handleIn = new Point(right[4] - right[6], right[5] - right[7]);
								handleIn = new Point(left[4] - ixPoint.x, left[5] - ixPoint.y);
								handleOut = new Point(right[2] - ixPoint.x, right[3] - ixPoint.y);
							}
							nuSeg = new Segment(ixPoint, handleIn, handleOut);
							nuSeg._ixPair = ix.pair;
							nuSeg._ixPair._segment = nuSeg;
							path.insert(nextNode.index + 1,  nuSeg);
						}
						for (j = i + 1; j < l; j++) {
							ixs[j].parameter = ixs[j].parameter / ix.parameter;
						}
						vals = left;
					}
				}
			}
		}
	},

	/**
	 * To deal with a HTML canvas requirement where CompoundPaths' child contours
	 * has to be of different winding direction for correctly filling holes.
	 * But if some individual countours are disjoint, i.e. islands, we have to
	 * reorient them so that
	 *   the holes have opposit winding direction (already handled by paperjs)
	 *   islands has to have same winding direction (as the first child of the path)
	 *
	 * Does NOT handle selfIntersecting CompoundPaths.
	 *
	 * @param  {CompoundPath} path - Input CompoundPath, Note: This path could be modified if need be.
	 * @return {boolean}	  the winding direction of the base contour(true if clockwise)
	 */
	_reorientCompoundPath: function(path) {
		if (!(path instanceof CompoundPath)) {
			path.closed = true;
			return path.clockwise;
		}
		var children = path.children, len = children.length, baseWinding;
		var bounds = new Array(len);
		var tmparray = new Array(len);
		baseWinding = children[0].clockwise;
		// Omit the first path
		for (i = 0; i < len; i++) {
			children[i].closed = true;
			bounds[i] = children[i].bounds;
			tmparray[i] = 0;
		}
		for (i = 0; i < len; i++) {
			var p1 = children[i];
			for (j = 0; j < len; j++) {
				var p2 = children[j];
				if (i !== j && bounds[i].contains(bounds[j])) {
					tmparray[j]++;
				}
			}
		}
		for (i = 1; i < len; i++) {
			if (tmparray[i] % 2 === 0) {
				children[i].clockwise = baseWinding;
			}
		}
		return baseWinding;
	},

	_reversePath: function(path) {
		var baseWinding;
		if (path instanceof CompoundPath) {
			var children = path.children, i, len;
			for (i = 0, len = children.length; i < len; i++) {
				children[i].reverse();
				children[i]._curves = null;
			}
			baseWinding = children[0].clockwise;
		} else {
			path.reverse();
			baseWinding = path.clockwise;
			path._curves = null;
		}
		return baseWinding;
	},

	_computeBoolean: function(path1, path2, operator, _splitCache) {
		var _path1, _path2, path1Clockwise, path2Clockwise;
		var ixs, path1Id, path2Id;
		// We do not modify the operands themselves
		// The result might not belong to the same type
		// i.e. subtraction(A:Path, B:Path):CompoundPath etc.
		_path1 = path1.clone();
		_path2 = path2.clone();
		_path1.style = _path2.style = null;
		_path1.selected = _path2.selected = false;
		path1Clockwise = this._reorientCompoundPath(_path1);
		path2Clockwise = this._reorientCompoundPath(_path2);
		path1Id = _path1.id;
		path2Id = _path2.id;
		// Calculate all the intersections
		ixs = (_splitCache && _splitCache.intersections)?
		_splitCache.intersections : _path1.getIntersections(_path2);
		// if we have a empty _splitCache object as an operand,
		// skip calculating boolean and cache the intersections
		if (_splitCache && !_splitCache.intersections) {
			_splitCache.intersections = ixs;
			return;
		}
		this._splitPath(ixs);
		this._splitPath(ixs, true);
		path1Id = _path1.id;
		path2Id = _path2.id;
		// Do operator specific calculations before we begin
		if (operator.name === "subtraction") {
			path2Clockwise = this._reversePath(_path2);
		}

		var i, j, len, path, crv;
		var paths = [];
		if (_path1 instanceof CompoundPath) {
			paths = paths.concat(_path1.children);
		} else {
			paths = [ _path1 ];
		}
		if (_path2 instanceof CompoundPath) {
			paths = paths.concat(_path2.children);
		} else {
			paths.push(_path2);
		}
		// step 1: discard invalid links according to the boolean operator
		var lastNode, firstNode, nextNode, midPoint, insidePath1, insidePath2;
		var thisId, thisWinding, contains, subtractionOp = (operator.name === 'subtraction');
		for (i = 0, len = paths.length; i < len; i++) {
			insidePath1 = insidePath2 = false;
			path = paths[i];
			thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
			thisWinding = path.clockwise;
			lastNode = path.lastSegment;
			firstNode = path.firstSegment;
			nextNode = null;
			while (nextNode !== firstNode) {
				nextNode = (nextNode)? nextNode.previous: lastNode;
				crv = nextNode.curve;
				midPoint = crv.getPoint(0.5);
				if (thisId !== path1Id) {
					contains = _path1.
					contains(midPoint);
					insidePath1 = (thisWinding === path1Clockwise || subtractionOp)? contains :
					contains && !this._testOnCurve(_path1, midPoint);
				}
				if (thisId !== path2Id) {
					contains = _path2.contains(midPoint);
					insidePath2 = (thisWinding === path2Clockwise)? contains :
					contains && !this._testOnCurve(_path2, midPoint);
				}
				if (operator(thisId === path1Id, insidePath1, insidePath2)) {
					crv._INVALID = true;
					// markPoint(midPoint, '+');
				}
			}
		}

		// Final step: Retrieve the resulting paths from the graph
		var boolResult = new CompoundPath();
		var node, nuNode, nuPath, nodeList = [], handle;
		for (i = 0, len = paths.length; i < len; i++) {
			nodeList = nodeList.concat(paths[i].segments);
		}
		for (i = 0, len = nodeList.length; i < len; i++) {
			node = nodeList[i];
			if (node.curve._INVALID || node._visited) { continue; }
			path = node.path;
			thisId = (path.parent instanceof CompoundPath)? path.parent.id : path.id;
			thisWinding = path.clockwise;
			nuPath = new Path();
			firstNode = null;
			firstNode_ix = null;
			if (node.previous.curve._INVALID) {
				node.handleIn = (node._ixPair)?
				node._ixPair.getIntersection()._segment.handleIn : [ 0, 0 ];
			}
			while (node && !node._visited && (node !== firstNode && node !== firstNode_ix)) {
				node._visited = true;
				firstNode = (firstNode)? firstNode: node;
				firstNode_ix = (!firstNode_ix && firstNode._ixPair)?
				firstNode._ixPair.getIntersection()._segment: firstNode_ix;
				// node._ixPair is this node's intersection CurveLocation object
				// node._ixPair.getIntersection() is the other CurveLocation object this node intersects with
				nextNode = (node._ixPair && node.curve._INVALID)? node._ixPair.getIntersection()._segment : node;
				if (node._ixPair) {
					nextNode._visited = true;
					nuNode = new Segment(node.point, node.handleIn, nextNode.handleOut);
					nuPath.add(nuNode);
					node = nextNode;
					path = node.path;
					thisWinding = path.clockwise;
				} else {
					nuPath.add(node);
				}
				node = node.next;
			}
			if (nuPath.segments.length > 1) {
				// avoid stray segments and incomplete paths
				if (nuPath.segments.length > 2 || !nuPath.curves[0].isLinear()) {
					nuPath.closed = true;
					boolResult.addChild(nuPath, true);
				}
			}
		}
		// Delete the proxies
		_path1.remove();
		_path2.remove();
		// And then, we are done.
		return boolResult.reduce();
	},

	_testOnCurve: function(path, point) {
		var res = 0;
		var crv = path.getCurves();
		var i = 0;
		var bounds = path.bounds;
		if (bounds && bounds.contains(point)) {
			for(i = 0; i < crv.length && !res; i++) {
				var crvi = crv[i];
				if (crvi.bounds.contains(point) && crvi.getParameterOf(point)) {
					res = 1;
				}
			}
		}
		return res;
	}
});
<|endoftext|>


<commit_before>'use strict';

/*!
 * Collection
 * https://github.com/kobezzza/Collection
 *
 * Released under the MIT license
 * https://github.com/kobezzza/Collection/blob/master/LICENSE
 */

import { Collection } from '../core';
import { isFunction, isArray } from '../helpers/types';
import { any } from '../helpers/gcc';

/**
 * Returns true if the specified element contains in the collection
 *
 * @see Collection.prototype.forEach
 * @param {?} searchElement - element for search
 * @param {($$CollectionFilter|$$CollectionSingleBase)=} [opt_filter] - function filter or an array of functions
 * @param {?$$CollectionSingleBase=} [opt_params] - additional parameters
 * @return {(boolean|!Promise<boolean>)}
 */
Collection.prototype.includes = function (searchElement, opt_filter, opt_params) {
	let p = opt_params || {};

	if (!isArray(opt_filter) && !isFunction(opt_filter)) {
		p = opt_filter || p;
		opt_filter = null;
	}

	const
		f = Number.isNaN(searchElement) ? (el) => Number.isNaN(el) : (el) => el === searchElement;

	this._initParams(p, opt_filter, f);
	p = any(Object.assign(Object.create(this.p), p, {mult: true, result: false}));

	const
		returnVal = any(this.forEach(() => p.result = true, p));

	if (returnVal !== this) {
		return returnVal;
	}

	return p.result;
};
<commit_msg>Fixed includes<commit_after>'use strict';

/*!
 * Collection
 * https://github.com/kobezzza/Collection
 *
 * Released under the MIT license
 * https://github.com/kobezzza/Collection/blob/master/LICENSE
 */

import { Collection } from '../core';
import { isFunction, isArray } from '../helpers/types';
import { any } from '../helpers/gcc';

/**
 * Returns true if the specified element contains in the collection
 *
 * @see Collection.prototype.forEach
 * @param {?} searchElement - element for search
 * @param {($$CollectionFilter|$$CollectionSingleBase)=} [opt_filter] - function filter or an array of functions
 * @param {?$$CollectionSingleBase=} [opt_params] - additional parameters
 * @return {(boolean|!Promise<boolean>)}
 */
Collection.prototype.includes = function (searchElement, opt_filter, opt_params) {
	let p = opt_params || {};

	if (!isArray(opt_filter) && !isFunction(opt_filter)) {
		p = opt_filter || p;
		opt_filter = null;
	}

	const
		f = Number.isNaN(searchElement) ? (el) => Number.isNaN(el) : (el) => el === searchElement;

	this._initParams(p, [].concat(opt_filter || [], f));
	p = any(Object.assign(Object.create(this.p), p, {mult: false, result: false}));

	const
		returnVal = any(this.forEach(() => p.result = true, p));

	if (returnVal !== this) {
		return returnVal;
	}

	return p.result;
};
<|endoftext|>


<commit_before>/*
 * jQuery Mass Animate, version 0.2 (2011-03-30)
 *
 * Copyright(c) 2011 Kai Schlamp
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 */
(function($) {
	// hack $.css for getting styles from CSS rule
	var oldCss = $.css;
	$.css = function(elem, name, extra) {
		if (!(elem.selectorText)) {
			return oldCss(elem, name, extra);
		}
		else {
			if (name == "opacity" && elem.style.filter) {
				// use hook for IE inside jQuery
				return oldCss(elem, name, extra);
			}
			else {
				var origName = $.camelCase(name),
				prop = $.cssProps[origName] || origName;
				return elem.style[prop];
			}
		}
	}

	// hack $.data for animations in IE 7, IE 8
	var cache = {};
	var mediator = function(original){
		return function (elem) {
			var id = elem.selectorText;
			if (id) {
				arguments[0] = cache[id] = cache[id] || {};
			}
			return original.apply($, arguments);
		};
	};
	$.data = mediator($.data);
	$.removeData = mediator($.removeData);

	// add additional style sheet that massanimate acts on
	var sheet = null;
	$(document).ready(function() {
		var elem = $('<style rel="mass-animate" type="text/css"></style>')
		.appendTo($('head'))[0];

		if (elem.sheet) {
			sheet = elem.sheet;
		}
		else {
			sheet = elem.styleSheet;
		}
	});

	function fetchRule(sheet, selector) {
		var rules = sheet.cssRules || sheet.rules;
		for (var i = 0, length = rules.length; i < length; i++) {
			// TODO: selectorText may not be correct in IE<9
			// as it splits selectors with ',' into multiple rules
			if (rules[i].selectorText.toLowerCase() == selector.toLowerCase()) {
				return rules[i];
			}
		}
		return null;
	}

	function insertRule(sheet, selector) {
		if (sheet.insertRule) {
			sheet.insertRule(selector + ' { }', 0);
		}
		else if (sheet.addRule) {
			sheet.addRule(selector, null, 0);
		}

		var rule = null;
		if (sheet.cssRules) {
			rule = sheet.cssRules[0];
		} else {
			rule = sheet.rules[0];
		}

		return rule;
	}

	function deleteRule(rule) {
		var rules = sheet.cssRules || sheet.rules;
		for (var i = 0, length = rules.length; i < length; i++) {
			if (rules[i] === rule) {
				if (sheet.deleteRule) {
					sheet.deleteRule(i);
				}
				else if (sheet.removeRule) {
					sheet.removeRule(i);
				}
				break;
			}
		}
	}

	function setStyle(rule, name, value) {
		$.access([rule], name, value, true, function(elem, name, value) {
			$.style(elem, name, value);
		});
	}

	$.massanimate = function(selector) {
		var rule = fetchRule(sheet, selector);
		if (!rule) {
			rule = insertRule(sheet, selector);
		}

		return {
			css: function(name, value) {
				setStyle(rule, name, value);
				return this;
			},
			animate: function(properties, duration, easing, callback) {
				$.fn.animate.call($(rule), properties, duration, easing, callback);
				return this;
			},
			remove: function() {
				var queue = $.queue(rule);
				if (queue && queue[0] === "inprogress") {
					$.queue(rule, "fx", function(next) {
						deleteRule(rule);
						next();
					});
				}
				else {
					deleteRule(rule);
				}
			}
		}
	}
})(jQuery)
<commit_msg>More concise.<commit_after>/*
 * jQuery Mass Animate, version 0.2 (2011-03-30)
 *
 * Copyright(c) 2011 Kai Schlamp
 *
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 */
(function($) {
	// hack $.css for getting styles from CSS rule
	var oldCss = $.css;
	$.css = function(elem, name, extra) {
		if (!(elem.selectorText)) {
			return oldCss(elem, name, extra);
		}
		else {
			if (name == "opacity" && elem.style.filter) {
				// use hook for IE inside jQuery
				return oldCss(elem, name, extra);
			}
			else {
				var origName = $.camelCase(name),
				prop = $.cssProps[origName] || origName;
				return elem.style[prop];
			}
		}
	}

	// hack $.data for animations in IE 7, IE 8
	var cache = {};
	var mediator = function(original){
		return function (elem) {
			var id = elem.selectorText;
			if (id) {
				arguments[0] = cache[id] = cache[id] || {};
			}
			return original.apply($, arguments);
		};
	};
	$.data = mediator($.data);
	$.removeData = mediator($.removeData);

	// add additional style sheet that massanimate acts on
	var sheet = null;
	$(document).ready(function() {
		var elem = $('<style rel="mass-animate" type="text/css"></style>')
		.appendTo($('head'))[0];

		sheet = elem.sheet || elem.styleSheet;
	});

	function fetchRule(sheet, selector) {
		var rules = sheet.cssRules || sheet.rules;
		for (var i = 0, length = rules.length; i < length; i++) {
			// TODO: selectorText may not be correct in IE<9
			// as it splits selectors with ',' into multiple rules
			if (rules[i].selectorText.toLowerCase() == selector.toLowerCase()) {
				return rules[i];
			}
		}
		return null;
	}

	function insertRule(sheet, selector) {
		if (sheet.insertRule) {
			sheet.insertRule(selector + ' { }', 0);
		}
		else if (sheet.addRule) {
			sheet.addRule(selector, null, 0);
		}

		var rule = null;
		if (sheet.cssRules) {
			rule = sheet.cssRules[0];
		} else {
			rule = sheet.rules[0];
		}

		return rule;
	}

	function deleteRule(rule) {
		var rules = sheet.cssRules || sheet.rules;
		for (var i = 0, length = rules.length; i < length; i++) {
			if (rules[i] === rule) {
				if (sheet.deleteRule) {
					sheet.deleteRule(i);
				}
				else if (sheet.removeRule) {
					sheet.removeRule(i);
				}
				break;
			}
		}
	}

	function setStyle(rule, name, value) {
		$.access([rule], name, value, true, function(elem, name, value) {
			$.style(elem, name, value);
		});
	}

	$.massanimate = function(selector) {
		var rule = fetchRule(sheet, selector);
		if (!rule) {
			rule = insertRule(sheet, selector);
		}

		return {
			css: function(name, value) {
				setStyle(rule, name, value);
				return this;
			},
			animate: function(properties, duration, easing, callback) {
				$.fn.animate.call($(rule), properties, duration, easing, callback);
				return this;
			},
			remove: function() {
				var queue = $.queue(rule);
				if (queue && queue[0] === "inprogress") {
					$.queue(rule, "fx", function(next) {
						deleteRule(rule);
						next();
					});
				}
				else {
					deleteRule(rule);
				}
			}
		}
	}
})(jQuery)
<|endoftext|>


<commit_before>'use strict'
import ko from 'knockout'
require('knockout.validation') // No variable here is deliberate!
import marked from 'marked'
marked.setOptions({sanitize: true})

var ajax = require('./ajax')
var api = require('./api-endpoints')
var browser = require('./browser')
var querystring = require('./getUrlParam')
var validation = require('./validation')

let ActionGroup = function (data, listener) {
  var self = this
  self.id = data.id
  self.name = data.name
  self.description = marked(data.description)
  self.synopsis = data.synopsis
  self.newsUrl = data.newsUrl
  self.slug = data.name.toLowerCase().replace(/ /g, '-').replace('\'', '')
  self.selectActionGroup = () => {
    listener.actionGroupSelected(self)
  }
}

let Section = function () {
  var self = this
  self.isActive = ko.observable(false)
}

function Model () {
  var self = this

  self.setActiveSection = (sectionIndex) => {
    let sections = [self.section1, self.section2, self.section3, self.section4]
    sections.forEach((s) => s.isActive(false))
    sections[sectionIndex - 1].isActive(true)
    self.activeSection(sectionIndex)
    if (self.activeSection() > 0) {
      browser.scrollTo('.js-pledge')
    }
  }

  self.setSection1Active = () => {
    self.setActiveSection(1)
  }

  self.actionGroupSelected = (actionGroup) => {
    self.selectedActionGroup(actionGroup)
    self.setActiveSection(2)
    let hashbang = querystring.buildHashbang(actionGroup.slug)
    browser.pushHistory({}, actionGroup.name + ' Action Group', hashbang)
  }

  self.init = () => {
    self.actionGroups = ko.observableArray()
    self.selectedActionGroup = ko.observable()

    validation.initialise(ko.validation)
    self.formModel = ko.validatedObservable({
      firstName: ko.observable().extend({ required: true }),
      lastName: ko.observable().extend({ required: true }),
      pledge: ko.observable().extend({ required: true }),
      organisation: ko.observable(),
      email: ko.observable().extend({ required: true, email: true }),
      isOptedIn: ko.observable()
    })
    self.fieldErrors = validation.getValidationGroup(ko.validation, self.formModel)

    self.section1 = new Section()
    self.section2 = new Section()
    self.section3 = new Section()
    self.section4 = new Section()
    self.activeSection = ko.observable(-1)

    browser.loading()

    ajax
      .get(api.actionGroups)
      .then((result) => {
        let actionGroups = result.data
          .map((g) => new ActionGroup(g, self))
        self.actionGroups(actionGroups)
        browser.setOnHistoryPop(self.setSection1Active)

        let hashbang = querystring.hashbang()
        if (hashbang.length > 0) {
          let matchingActionGroups = self.actionGroups().filter((ag) => ag.slug === hashbang)
          if (matchingActionGroups.length === 0) browser.redirect('/404')
          self.selectedActionGroup(matchingActionGroups[0])
          self.setActiveSection(2)
        } else {
          self.setActiveSection(1)
        }

        browser.loaded()
      }, () => {
        browser.redirect('/500/')
      })
  }

  let buildFormData = () => {
    return {
      firstName: self.formModel().firstName(),
      lastName: self.formModel().lastName(),
      email: self.formModel().email(),
      organisation: self.formModel().organisation(),
      isOptedIn: self.formModel().isOptedIn(),
      message: self.formModel().pledge()
    }
  }

  let submitForm = () => {
    browser.loading()
    self.setActiveSection(3)

    let endpoint = api.actionGroups + '/' + self.selectedActionGroup().id + '/joining-enquiries'
    let data = buildFormData()

    ajax
      .post(endpoint, data)
      .then((result) => {
        browser.loaded()
        if (result.statusCode === 400) {
          self.setActiveSection(2)
        }
        if (result.statusCode === 201) {
          self.setActiveSection(4)
        }
      }, () => {
        browser.redirect('/500/')
      })
  }

  self.submitPledge = () => {
    if (self.formModel.isValid()) {
      submitForm()
    } else {
      self.fieldErrors.showAllMessages()
    }
  }

  self.init()
}

module.exports = Model
<commit_msg>stop jump on action group page<commit_after>'use strict'
import ko from 'knockout'
require('knockout.validation') // No variable here is deliberate!
import marked from 'marked'
marked.setOptions({sanitize: true})

var ajax = require('./ajax')
var api = require('./api-endpoints')
var browser = require('./browser')
var querystring = require('./getUrlParam')
var validation = require('./validation')

let ActionGroup = function (data, listener) {
  var self = this
  self.id = data.id
  self.name = data.name
  self.description = marked(data.description)
  self.synopsis = data.synopsis
  self.newsUrl = data.newsUrl
  self.slug = data.name.toLowerCase().replace(/ /g, '-').replace('\'', '')
  self.selectActionGroup = () => {
    listener.actionGroupSelected(self)
  }
}

let Section = function () {
  var self = this
  self.isActive = ko.observable(false)
}

function Model () {
  var self = this

  self.setActiveSection = (sectionIndex) => {
    let sections = [self.section1, self.section2, self.section3, self.section4]
    sections.forEach((s) => s.isActive(false))
    sections[sectionIndex - 1].isActive(true)
    self.activeSection(sectionIndex)
    if (self.activeSection() > 1) {
      browser.scrollTo('.js-pledge')
    }
  }

  self.setSection1Active = () => {
    self.setActiveSection(1)
  }

  self.actionGroupSelected = (actionGroup) => {
    self.selectedActionGroup(actionGroup)
    self.setActiveSection(2)
    let hashbang = querystring.buildHashbang(actionGroup.slug)
    browser.pushHistory({}, actionGroup.name + ' Action Group', hashbang)
  }

  self.init = () => {
    self.actionGroups = ko.observableArray()
    self.selectedActionGroup = ko.observable()

    validation.initialise(ko.validation)
    self.formModel = ko.validatedObservable({
      firstName: ko.observable().extend({ required: true }),
      lastName: ko.observable().extend({ required: true }),
      pledge: ko.observable().extend({ required: true }),
      organisation: ko.observable(),
      email: ko.observable().extend({ required: true, email: true }),
      isOptedIn: ko.observable()
    })
    self.fieldErrors = validation.getValidationGroup(ko.validation, self.formModel)

    self.section1 = new Section()
    self.section2 = new Section()
    self.section3 = new Section()
    self.section4 = new Section()
    self.activeSection = ko.observable(-1)

    browser.loading()

    ajax
      .get(api.actionGroups)
      .then((result) => {
        let actionGroups = result.data
          .map((g) => new ActionGroup(g, self))
        self.actionGroups(actionGroups)
        browser.setOnHistoryPop(self.setSection1Active)

        let hashbang = querystring.hashbang()
        if (hashbang.length > 0) {
          let matchingActionGroups = self.actionGroups().filter((ag) => ag.slug === hashbang)
          if (matchingActionGroups.length === 0) browser.redirect('/404')
          self.selectedActionGroup(matchingActionGroups[0])
          self.setActiveSection(2)
        } else {
          self.setActiveSection(1)
        }

        browser.loaded()
      }, () => {
        browser.redirect('/500/')
      })
  }

  let buildFormData = () => {
    return {
      firstName: self.formModel().firstName(),
      lastName: self.formModel().lastName(),
      email: self.formModel().email(),
      organisation: self.formModel().organisation(),
      isOptedIn: self.formModel().isOptedIn(),
      message: self.formModel().pledge()
    }
  }

  let submitForm = () => {
    browser.loading()
    self.setActiveSection(3)

    let endpoint = api.actionGroups + '/' + self.selectedActionGroup().id + '/joining-enquiries'
    let data = buildFormData()

    ajax
      .post(endpoint, data)
      .then((result) => {
        browser.loaded()
        if (result.statusCode === 400) {
          self.setActiveSection(2)
        }
        if (result.statusCode === 201) {
          self.setActiveSection(4)
        }
      }, () => {
        browser.redirect('/500/')
      })
  }

  self.submitPledge = () => {
    if (self.formModel.isValid()) {
      submitForm()
    } else {
      self.fieldErrors.showAllMessages()
    }
  }

  self.init()
}

module.exports = Model
<|endoftext|>