<commit_before>import ENV from './environment';
import * as Utils from './utils';
import { getDataParams, querySelector, querySelectorAll, getAttribute, setAttribute, addEventListener, hasClass, addClass, removeClass } from './dom';
import Mojito from './main';
import { applyObserver, addObserver, callObserver } from './observer';
import { applyComputed } from './computed';

/**
    registers a controller in mojito, creates an instance
    and does all the magic for setting up all the controller stuff
*/
export function registerController(name, ControllerClass) {

    // make sure registerController has exactly two arguments
    if(arguments.length < 2) {
        throw '[Type Exeption] missing parameters';
    }

    // check name to make sure it is an array
    if(!Utils.isString(name) ) {
        throw '[Type Exeption] name has to be a string';
    }

    // check ControllerClass to make sure it is a function or object
    if(!Utils.isFunction(ControllerClass) && !Utils.isObject(ControllerClass) ) {
        throw '[Type Exeption] No ControllerClass found for '+name+' (maybe you forgot to export '+name+' as default)';
    }

    // check for controller instances array
    if(!Utils.isArray(Mojito._controllerInstances)) {
        throw '[Type Exeption] No _controllerInstances found';
    }

    // grab elements from DOM where this controller has been attached
    let elements = querySelectorAll('[data-'+ (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_DEF_SHORTHAND : ENV.HTMLDATA_CONTROLLER_DEF) +'="'+name+'"]');

    // loop through elements and create controller instances
    for(let i=0, max=elements.length; i<max; i++) {

        let element = elements[i];
        let params = [];

        // check if controller is already registered (has an id)
        if(getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_ID_SHORTHAND : ENV.HTMLDATA_CONTROLLER_ID))) {
            continue;
        }

        // check if there are any params for controller init
        if(getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_PARAMS_SHORTHAND : ENV.HTMLDATA_PARAMS))) {
            params = getDataParams(element);
        }

        // check if controller reference is set and apply it to instance
        let ref = getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_REF_SHORTHAND : ENV.HTMLDATA_CONTROLLER_REF));

        // merge params with config
        params = params.concat([{
            element: element,
            id: Utils.generateRandomString(16),
            ref: ref,
            className: name
        }]);

        // create new controller instance from class
        // instead of new ControllerClass() using the following code to apply
        // the params as an arguments array
        let controller = new (Function.prototype.bind.apply(ControllerClass, [null].concat(params)));

    }
}

export function applyController(controller) {

     // check name to make sure it is an array
    if(typeof controller === 'undefined' ) {
        throw '[Type Exeption] controller has to be a object';
    }

    // apply id to dome node
    setAttribute(controller._$, (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_ID_SHORTHAND : ENV.HTMLDATA_CONTROLLER_ID),controller.get('_id', true));

    // add controller instance to instances array for later access
    Mojito._controllerInstances.push(controller);

    // add all the computed properties on this controller
    applyComputed(controller);

    // add all the observers on this controller
    applyObserver(controller);

    // apply actions
    applyActionsToController(controller);

    // apply class bindings
    applyClassBindingsToController(controller);

    // apply input bindings
    applyInputBindingsToController(controller);

    // apply content bindings
    applyContentBindingsToController(controller);
}

export function applyActionsToController(controller) {

    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let actionElements = querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND : ENV.HTMLDATA_ACTION) + ']');

    let i = actionElements.length;

    while(i--) {
        let element = actionElements[i];
        let action = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND : ENV.HTMLDATA_ACTION));
        let id =  getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND_ID : ENV.HTMLDATA_ACTION_ID));
        let eventType =  getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_EVENT_SHORTHAND : ENV.HTMLDATA_EVENT));

        if(id) {
            continue;
        }

        if(Utils.isString(action)) {
            let actionParts = action.split('.');

            if(actionParts.length === 2
                && actionParts[0] === controller.get('_className', true)
                && actionParts[1] in controller) {

                    if(!eventType || ENV.EVENTTYPES.split(' ').indexOf(eventType) === -1) {
                        eventType = ENV.DEFAULT_EVENTTYPE;
                    }

                    setAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_ID_SHORTHAND : ENV.HTMLDATA_ACTION_ID), Utils.generateRandomString(16));
                    addEventListener(element, eventType, function(event) {
                        event.preventDefault();
                        let params = getDataParams(element);
                        controller[actionParts[1]].apply(controller, params.concat([event]));
                    }, controller);
            }
        }
    }
}

export function applyClassBindingsToController(controller) {
    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING) + ']');

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let bindings = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING)).split(' ');
        let j = bindings.length;

        while(j--) {
            let binding = bindings[j];

            if(!Utils.isString(binding)) {
                continue;
            }

            binding = binding.split('.');

            if(binding.length !== 2 || binding[0] !== controller.get('_className', true)) {
                continue;
            }

            binding = binding[1].split(':');

            if(!binding.length || !binding[0].length) {
                continue;
            }

            let trigger = binding[0];
            let class1 = (binding.length > 1 && binding[1].length) ? binding[1] : null;
            let class2 = (binding.length > 2 && binding[2].length) ? binding[2] : null;

            addObserver(controller, trigger, function(trigger, element, class1, class2) {
                if(this.get(trigger)) {
                    if(!!class1) {
                        addClass(element, class1);
                    } else {
                        addClass(element, trigger);
                    }
                    !!class2 && removeClass(element, class2);
                } else {
                    if(!!class1) {
                        removeClass(element, class1);
                    } else {
                        removeClass(element, trigger);
                    }
                    !!class2 && addClass(element, class2);
                }
            }, [trigger, element, class1, class2]);
            callObserver(controller, trigger);
        }
    }
}

export function applyInputBindingsToController(controller) {

    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_INPUTBINDING_SHORTHAND : ENV.HTMLDATA_INPUTBINDING) + ']');

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let binding = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_INPUTBINDING_SHORTHAND : ENV.HTMLDATA_INPUTBINDING));

        if(!Utils.isString(binding)) {
            continue;
        }

        binding = binding.split('.');

        if(binding.length < 2 || binding[0] !== controller.get('_className', true)) {
            continue;
        }

        let param = binding.slice(1).join('.');
        let events = '';

        switch (element.tagName.toLowerCase()) {
            case 'textarea':
            case 'input':
                events = 'input change paste';
                break;
            case 'select':
                events = 'change';
                break;
        }
        (function(element, events, controller, param) {
            addEventListener(element, events, function(event) {
                if(element.type === 'checkbox') {
                    value = element.checked;
                } else if(element.type !== 'radio' || element.checked) {
                    value = element.value;
                }

                if(value !== controller.get(param)) {
                    controller.set(param, value);
                }
            });
        })(element, events, controller, param);

        addObserver(controller, param, function(param, element) {
            switch (element.type) {
                case 'checkbox':
                    (element.checked !== !!controller.get(param)) && (element.checked = !!controller.get(param));
                    break;
                case 'radio':
                    element.checked = (controller.get(param) === element.value);
                    break;
                default:
                    if(controller.get(param) !== element.value) {
                        element.value = controller.get(param);
                    }
                break;
            }
        }, [param, element]);

        let value = null;
        if(element.type === 'checkbox') {
            value = element.checked;
        } else if(element.type !== 'radio' || element.checked) {
            value = element.value;
        }

        if(!!value) {
            controller.set(param, value);
        } else if(controller.get(param)){
            switch (element.type) {
                case 'checkbox':
                    (element.checked !== !!controller.get(param)) && (element.checked = !!controller.get(param));
                    break;
                case 'radio':
                    element.checked = (controller.get(param) === element.value);
                    break;
                default:
                    if(controller.get(param) !== element.value) {
                        element.value = controller.get(param);
                    }
                break;
            }
        }
    }

}

export function applyContentBindingsToController(controller) {
    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTENTBINDING_SHORTHAND : ENV.HTMLDATA_CONTENTBINDING) + ']');

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let binding = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTENTBINDING_SHORTHAND : ENV.HTMLDATA_CONTENTBINDING));

        if(!Utils.isString(binding)) {
            continue;
        }

        binding = binding.split('.');

        if(binding.length < 2 || binding[0] !== controller.get('_className', true)) {
            continue;
        }

        let keyName = binding.slice(1).join('.');

        addObserver(controller, keyName, function(keyName, element) {
            element.innerHTML = controller.get(keyName) ? controller.get(keyName) : '';
        }, [keyName, element]);
        callObserver(controller, keyName);
    }
}

export function register(type, name, Class) {

    if(arguments.length < 3) {
        throw '[Type Exeption] missing parameters';
    }

    if(!Utils.isString(type) || !Utils.isString(name) ) {
        throw '[Type Exeption] type and name have to be strings';
    }

    if(type === 'controller') {
        return registerController(name, Class);
    }

    return null;

}

export function getControllersByParam(param, value) {

    // check for controller instances array
    if(!Utils.isArray(Mojito._controllerInstances)) {
        throw '[Type Exeption] No _controllerInstances found';
    }

    // check id to make sure it is a string
    if(!Utils.isString(value)) {
        console.error('[Type Error] '+ param +' has to be a string');
        return null;
    }

    let i = Mojito._controllerInstances.length;
    let result = [];
    while(i--) {
        let controller = Mojito._controllerInstances[i];
        if(controller.get(param) === value) {
            result.push(controller);
        }
    }
    return result;

}

export function getControllerById(id) {

    const controllers = getControllersByParam('_id', id);
    return controllers.length ? controllers[0] : null;

}

export function getControllerByRef(ref) {

    const controllers = getControllersByParam('_ref', ref);
    return controllers.length ? controllers[0] : null;

}

export function getControllersByClassName(name) {

    return getControllersByParam('_className', name);

}
<commit_msg>actions & bindings now work on root element<commit_after>import ENV from './environment';
import * as Utils from './utils';
import { getDataParams, querySelector, querySelectorAll, getAttribute, setAttribute, addEventListener, hasClass, addClass, removeClass } from './dom';
import Mojito from './main';
import { applyObserver, addObserver, callObserver } from './observer';
import { applyComputed } from './computed';

/**
    registers a controller in mojito, creates an instance
    and does all the magic for setting up all the controller stuff
*/
export function registerController(name, ControllerClass) {

    // make sure registerController has exactly two arguments
    if(arguments.length < 2) {
        throw '[Type Exeption] missing parameters';
    }

    // check name to make sure it is an array
    if(!Utils.isString(name) ) {
        throw '[Type Exeption] name has to be a string';
    }

    // check ControllerClass to make sure it is a function or object
    if(!Utils.isFunction(ControllerClass) && !Utils.isObject(ControllerClass) ) {
        throw '[Type Exeption] No ControllerClass found for '+name+' (maybe you forgot to export '+name+' as default)';
    }

    // check for controller instances array
    if(!Utils.isArray(Mojito._controllerInstances)) {
        throw '[Type Exeption] No _controllerInstances found';
    }

    // grab elements from DOM where this controller has been attached
    let elements = querySelectorAll('[data-'+ (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_DEF_SHORTHAND : ENV.HTMLDATA_CONTROLLER_DEF) +'="'+name+'"]');

    // loop through elements and create controller instances
    for(let i=0, max=elements.length; i<max; i++) {

        let element = elements[i];
        let params = [];

        // check if controller is already registered (has an id)
        if(getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_ID_SHORTHAND : ENV.HTMLDATA_CONTROLLER_ID))) {
            continue;
        }

        // check if there are any params for controller init
        if(getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_PARAMS_SHORTHAND : ENV.HTMLDATA_PARAMS))) {
            params = getDataParams(element);
        }

        // check if controller reference is set and apply it to instance
        let ref = getAttribute(element, 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_REF_SHORTHAND : ENV.HTMLDATA_CONTROLLER_REF));

        // merge params with config
        params = params.concat([{
            element: element,
            id: Utils.generateRandomString(16),
            ref: ref,
            className: name
        }]);

        // create new controller instance from class
        // instead of new ControllerClass() using the following code to apply
        // the params as an arguments array
        let controller = new (Function.prototype.bind.apply(ControllerClass, [null].concat(params)));

    }
}

export function applyController(controller) {

     // check name to make sure it is an array
    if(typeof controller === 'undefined' ) {
        throw '[Type Exeption] controller has to be a object';
    }

    // apply id to dome node
    setAttribute(controller._$, (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTROLLER_ID_SHORTHAND : ENV.HTMLDATA_CONTROLLER_ID),controller.get('_id', true));

    // add controller instance to instances array for later access
    Mojito._controllerInstances.push(controller);

    // add all the computed properties on this controller
    applyComputed(controller);

    // add all the observers on this controller
    applyObserver(controller);

    // apply actions
    applyActionsToController(controller);

    // apply class bindings
    applyClassBindingsToController(controller);

    // apply input bindings
    applyInputBindingsToController(controller);

    // apply content bindings
    applyContentBindingsToController(controller);
}

export function applyActionsToController(controller) {

    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let actionElements = Array.prototype.slice.call(querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND : ENV.HTMLDATA_ACTION) + ']'));

    if(getAttribute(controller.get('_$', true), 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND : ENV.HTMLDATA_ACTION))) {
         actionElements.push(controller.get('_$', true));
    }

    let i = actionElements.length;

    while(i--) {
        let element = actionElements[i];
        let action = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND : ENV.HTMLDATA_ACTION));
        let id =  getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_SHORTHAND_ID : ENV.HTMLDATA_ACTION_ID));
        let eventType =  getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_EVENT_SHORTHAND : ENV.HTMLDATA_EVENT));

        if(id) {
            continue;
        }

        if(Utils.isString(action)) {
            let actionParts = action.split('.');

            if(actionParts.length === 2
                && actionParts[0] === controller.get('_className', true)
                && actionParts[1] in controller) {

                    if(!eventType || ENV.EVENTTYPES.split(' ').indexOf(eventType) === -1) {
                        eventType = ENV.DEFAULT_EVENTTYPE;
                    }

                    setAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_ACTION_ID_SHORTHAND : ENV.HTMLDATA_ACTION_ID), Utils.generateRandomString(16));
                    addEventListener(element, eventType, function(event) {
                        event.preventDefault();
                        let params = getDataParams(element);
                        controller[actionParts[1]].apply(controller, params.concat([event]));
                    }, controller);
            }
        }
    }
}

export function applyClassBindingsToController(controller) {
    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = Array.prototype.slice.call(querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING) + ']'));

    if(getAttribute(controller.get('_$', true), 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING))) {
         bindingElements.push(controller.get('_$', true));
    }

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let bindings = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING)).split(' ');
        let j = bindings.length;

        while(j--) {
            let binding = bindings[j];

            if(!Utils.isString(binding)) {
                continue;
            }

            binding = binding.split('.');

            if(binding.length !== 2 || binding[0] !== controller.get('_className', true)) {
                continue;
            }

            binding = binding[1].split(':');

            if(!binding.length || !binding[0].length) {
                continue;
            }

            let trigger = binding[0];
            let class1 = (binding.length > 1 && binding[1].length) ? binding[1] : null;
            let class2 = (binding.length > 2 && binding[2].length) ? binding[2] : null;

            addObserver(controller, trigger, function(trigger, element, class1, class2) {
                if(this.get(trigger)) {
                    if(!!class1) {
                        addClass(element, class1);
                    } else {
                        addClass(element, trigger);
                    }
                    !!class2 && removeClass(element, class2);
                } else {
                    if(!!class1) {
                        removeClass(element, class1);
                    } else {
                        removeClass(element, trigger);
                    }
                    !!class2 && addClass(element, class2);
                }
            }, [trigger, element, class1, class2]);
            callObserver(controller, trigger);
        }
    }
}

export function applyInputBindingsToController(controller) {

    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = Array.prototype.slice.call(querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_INPUTBINDING_SHORTHAND : ENV.HTMLDATA_INPUTBINDING) + ']'));

    if(getAttribute(controller.get('_$', true), 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING))) {
         bindingElements.push(controller.get('_$', true));
    }

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let binding = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_INPUTBINDING_SHORTHAND : ENV.HTMLDATA_INPUTBINDING));

        if(!Utils.isString(binding)) {
            continue;
        }

        binding = binding.split('.');

        if(binding.length < 2 || binding[0] !== controller.get('_className', true)) {
            continue;
        }

        let param = binding.slice(1).join('.');
        let events = '';

        switch (element.tagName.toLowerCase()) {
            case 'textarea':
            case 'input':
                events = 'input change paste';
                break;
            case 'select':
                events = 'change';
                break;
        }
        (function(element, events, controller, param) {
            addEventListener(element, events, function(event) {
                if(element.type === 'checkbox') {
                    value = element.checked;
                } else if(element.type !== 'radio' || element.checked) {
                    value = element.value;
                }

                if(value !== controller.get(param)) {
                    controller.set(param, value);
                }
            });
        })(element, events, controller, param);

        addObserver(controller, param, function(param, element) {
            switch (element.type) {
                case 'checkbox':
                    (element.checked !== !!controller.get(param)) && (element.checked = !!controller.get(param));
                    break;
                case 'radio':
                    element.checked = (controller.get(param) === element.value);
                    break;
                default:
                    if(controller.get(param) !== element.value) {
                        element.value = controller.get(param);
                    }
                break;
            }
        }, [param, element]);

        let value = null;
        if(element.type === 'checkbox') {
            value = element.checked;
        } else if(element.type !== 'radio' || element.checked) {
            value = element.value;
        }

        if(!!value) {
            controller.set(param, value);
        } else if(controller.get(param)){
            switch (element.type) {
                case 'checkbox':
                    (element.checked !== !!controller.get(param)) && (element.checked = !!controller.get(param));
                    break;
                case 'radio':
                    element.checked = (controller.get(param) === element.value);
                    break;
                default:
                    if(controller.get(param) !== element.value) {
                        element.value = controller.get(param);
                    }
                break;
            }
        }
    }

}

export function applyContentBindingsToController(controller) {
    // make sure applyDomToController has exactly one arguments
    if(arguments.length < 1) {
        throw '[Type Exeption] missing parameters';
    }

    // check controller to make sure it is an object
    if(!Utils.isObject(controller) ) {
        throw '[Type Exeption] controller has to be an object';
    }

    let bindingElements = Array.prototype.slice.call(querySelectorAll(controller.get('_$', true),
        '[data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTENTBINDING_SHORTHAND : ENV.HTMLDATA_CONTENTBINDING) + ']'));

    if(getAttribute(controller.get('_$', true), 'data-' + (ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CLASSBINDING_SHORTHAND : ENV.HTMLDATA_CLASSBINDING))) {
         bindingElements.push(controller.get('_$', true));
    }

    let i = bindingElements.length;

    while(i--) {
        let element = bindingElements[i];
        let binding = getAttribute(element, 'data-'+(ENV.HTMLDATA_SHORTHAND ? ENV.HTMLDATA_CONTENTBINDING_SHORTHAND : ENV.HTMLDATA_CONTENTBINDING));

        if(!Utils.isString(binding)) {
            continue;
        }

        binding = binding.split('.');

        if(binding.length < 2 || binding[0] !== controller.get('_className', true)) {
            continue;
        }

        let keyName = binding.slice(1).join('.');

        addObserver(controller, keyName, function(keyName, element) {
            element.innerHTML = controller.get(keyName) ? controller.get(keyName) : '';
        }, [keyName, element]);
        callObserver(controller, keyName);
    }
}

export function register(type, name, Class) {

    if(arguments.length < 3) {
        throw '[Type Exeption] missing parameters';
    }

    if(!Utils.isString(type) || !Utils.isString(name) ) {
        throw '[Type Exeption] type and name have to be strings';
    }

    if(type === 'controller') {
        return registerController(name, Class);
    }

    return null;

}

export function getControllersByParam(param, value) {

    // check for controller instances array
    if(!Utils.isArray(Mojito._controllerInstances)) {
        throw '[Type Exeption] No _controllerInstances found';
    }

    // check id to make sure it is a string
    if(!Utils.isString(value)) {
        console.error('[Type Error] '+ param +' has to be a string');
        return null;
    }

    let i = Mojito._controllerInstances.length;
    let result = [];
    while(i--) {
        let controller = Mojito._controllerInstances[i];
        if(controller.get(param) === value) {
            result.push(controller);
        }
    }
    return result;

}

export function getControllerById(id) {

    const controllers = getControllersByParam('_id', id);
    return controllers.length ? controllers[0] : null;

}

export function getControllerByRef(ref) {

    const controllers = getControllersByParam('_ref', ref);
    return controllers.length ? controllers[0] : null;

}

export function getControllersByClassName(name) {

    return getControllersByParam('_className', name);

}
<|endoftext|>


<commit_before>
var lists = require('./list');
var vectors = require('./vector');
var sets = require('./set');
var maps = require('./map');
var lazyseqs = require('./lazyseq');
var utils = require('./utils');

if (typeof cljs == 'undefined')
    cljs = {};

if (typeof cljs.core == 'undefined')
    cljs.core = {};

cljs.core.list = function () {
    return lists.create(arguments);
}

cljs.core.first = function (list) { return list == null ? null : list.first(); }

cljs.core.next = function (list) { return list.next(); }

cljs.core.rest = function (list) { return list.rest(); }

cljs.core.nth = function (list, n, defvalue) {
    if (list == null || lists.isEmptyList(list))
        if (defvalue != null)
            return defvalue;
        else
            return null;

    if (vectors.isVector(list)) {
        if (list.length() <= n)
            return list.get(n);

        if (defvalue != null)
            return defvalue;
        else
            return null;
    }

    while (n > 0) {
        list = list.next();
        n--;

        if (!list)
            if (defvalue != null)
                return defvalue;
            else
                return null;
    }

    return list.first();
}

cljs.core.cons = function (first, next) { return lists.list(first, cljs.core.seq(next)); }

cljs.core.seq = function (value) {
    if (value == null)
        return null;

    if (lists.isEmptyList(value))
        return null;

    if (vectors.isVector(value)) {
        if (value.length() == 0)
            return null;
        else
            return lists.create(value.asArray());
    }

    if (sets.isSet(value)) {
        var keys = value.getKeys();

        if (keys.length == 0)
            return null;

        return lists.create(keys);
    }

    if (lists.isList(value))
        return value;

    if (lazyseqs.isLazySeq(value))
        return value;

    if (maps.isMap(value)) {
        var result = [];
        var keys = value.getKeys();

        if (keys.length == 0)
            return null;

        for (var n in keys) {
            var key = keys[n];
            var val = value.get(key);
            result.push(vectors.create([key, val]));
        }

        return lists.create(result);
    }

    if (typeof value == 'string')
        return lists.create(value.split(''));

    if (Array.isArray(value))
        return lists.create(value);

    throw new Error("Don't know how to create sequence from: " + value);
}

cljs.core.length = function (value) { return value.length(); }

cljs.core.equals = function (value1, value2) {
    return utils.equals(value1, value2);
}

cljs.core.str = function () {
    var result = '';
    var l = arguments.length;

    for (var k = 0; k < l; k++) {
        var arg = arguments[k];
        if (arg.asString)
            result += arg.asString();
        else
            result += arg.toString();
    }

    return result;
}

cljs.core.throw = function (ex) { throw ex; }

function isEmpty(value) {
    if (value == null)
        return true;

    if (lists.isEmptyList(value))
        return true;

    if (value.isEmpty)
        return value.isEmpty();

    return false;
}<commit_msg>Fix nth in core<commit_after>
var lists = require('./list');
var vectors = require('./vector');
var sets = require('./set');
var maps = require('./map');
var lazyseqs = require('./lazyseq');
var utils = require('./utils');

if (typeof cljs == 'undefined')
    cljs = {};

if (typeof cljs.core == 'undefined')
    cljs.core = {};

cljs.core.list = function () {
    return lists.create(arguments);
}

cljs.core.first = function (list) { return list == null ? null : list.first(); }

cljs.core.next = function (list) { return list.next(); }

cljs.core.rest = function (list) { return list.rest(); }

cljs.core.nth = function (list, n, defvalue) {
    if (list == null || lists.isEmptyList(list))
        if (defvalue != null)
            return defvalue;
        else
            return null;

    if (vectors.isVector(list)) {
        if (n < list.length())
            return list.get(n);

        if (defvalue != null)
            return defvalue;
        else
            return null;
    }

    while (n > 0) {
        list = list.next();
        n--;

        if (!list)
            if (defvalue != null)
                return defvalue;
            else
                return null;
    }

    return list.first();
}

cljs.core.cons = function (first, next) { return lists.list(first, cljs.core.seq(next)); }

cljs.core.seq = function (value) {
    if (value == null)
        return null;

    if (lists.isEmptyList(value))
        return null;

    if (vectors.isVector(value)) {
        if (value.length() == 0)
            return null;
        else
            return lists.create(value.asArray());
    }

    if (sets.isSet(value)) {
        var keys = value.getKeys();

        if (keys.length == 0)
            return null;

        return lists.create(keys);
    }

    if (lists.isList(value))
        return value;

    if (lazyseqs.isLazySeq(value))
        return value;

    if (maps.isMap(value)) {
        var result = [];
        var keys = value.getKeys();

        if (keys.length == 0)
            return null;

        for (var n in keys) {
            var key = keys[n];
            var val = value.get(key);
            result.push(vectors.create([key, val]));
        }

        return lists.create(result);
    }

    if (typeof value == 'string')
        return lists.create(value.split(''));

    if (Array.isArray(value))
        return lists.create(value);

    throw new Error("Don't know how to create sequence from: " + value);
}

cljs.core.length = function (value) { return value.length(); }

cljs.core.equals = function (value1, value2) {
    return utils.equals(value1, value2);
}

cljs.core.str = function () {
    var result = '';
    var l = arguments.length;

    for (var k = 0; k < l; k++) {
        var arg = arguments[k];

        if (arg == null)
            result += 'nil';
        else if (arg.asString)
            result += arg.asString();
        else
            result += arg.toString();
    }

    return result;
}

cljs.core.throw = function (ex) { throw ex; }

function isEmpty(value) {
    if (value == null)
        return true;

    if (lists.isEmptyList(value))
        return true;

    if (value.isEmpty)
        return value.isEmpty();

    return false;
}<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import glob from 'glob';
import yaml from 'js-yaml';
import fastmatter from 'fastmatter';
import _ from 'lodash';

import renderMarkdown from './markdown';

export function removeExtension(filename) {
	return filename.replace(/\.\w+$/, '');
}

export function filepathToUrl(filename) {
	return '/' + removeExtension(filename);
}

export function parsePage(source, filepath) {
	let { attributes, body } = fastmatter(source);
	let extension = path.extname(filepath);
	if (extension === '.md') {
		body = renderMarkdown(body);
	}
	return _.merge(attributes, {
		sourcePath: filepath,
		url: filepathToUrl(filepath),
		content: body
	});
}

export function getSourceFilesList(folder) {
	return glob.sync(path.join(folder, '**/*.{md,html}'));
}

export function readFile(filepath) {
	return fs.readFileSync(filepath, {encoding: 'utf8'});
}

export function readYamlFile(filepath) {
	try {
		return yaml.safeLoad(readFile(filepath));
	}
	catch (e) {
		console.log(`Cannot read YAML file ${filepath}:`, e);
	}
}

export function readFiles(files) {
	return files.map(readFile);
}

export function getConfigFilesList(folder) {
	return glob.sync(path.join(folder, '*.yml'));
}

export function readConfigFiles(files) {
	return files.reduce((configs, filepath) => {
		let name = removeExtension(path.basename(filepath));
		if (name === 'default') {
			configs.default = readYamlFile(filepath);
		}
		else {
			configs.langs[name] = readYamlFile(filepath);
		}
		return configs;
	}, {default: {}, langs: {}});  // @todo use really default config
}

export function mergeConfigs(configs) {
	let { langs } = configs;
	if (_.isEmpty(langs)) {
		return {
			default: configs.default
		};
	}

	return Object.keys(langs).reduce((merged, lang) => {
		merged[lang] = _.merge({}, configs.default, langs[lang]);
		return merged;
	}, {});
}

export function readConfig(folder) {
	let files = getConfigFilesList(folder);
	let configs = readConfigFiles(files);
	return mergeConfigs(configs);
}

export function makeContext(page, config, helpers) {
	// Combine page data and config
	let context = _.merge({}, { config }, page);

	// Bind all helpers to context
	return Object.keys(helpers).reduce((ctx, name) => {
		ctx[name] = helpers[name].bind(ctx);
		return ctx;
	}, context);
}
<commit_msg>Simplify context creation.<commit_after>import fs from 'fs';
import path from 'path';
import glob from 'glob';
import yaml from 'js-yaml';
import fastmatter from 'fastmatter';
import _ from 'lodash';

import renderMarkdown from './markdown';

export function removeExtension(filename) {
	return filename.replace(/\.\w+$/, '');
}

export function filepathToUrl(filename) {
	return '/' + removeExtension(filename);
}

export function parsePage(source, filepath) {
	let { attributes, body } = fastmatter(source);
	let extension = path.extname(filepath);
	if (extension === '.md') {
		body = renderMarkdown(body);
	}
	return _.merge(attributes, {
		sourcePath: filepath,
		url: filepathToUrl(filepath),
		content: body
	});
}

export function getSourceFilesList(folder) {
	return glob.sync(path.join(folder, '**/*.{md,html}'));
}

export function readFile(filepath) {
	return fs.readFileSync(filepath, {encoding: 'utf8'});
}

export function readYamlFile(filepath) {
	try {
		return yaml.safeLoad(readFile(filepath));
	}
	catch (e) {
		console.log(`Cannot read YAML file ${filepath}:`, e);
	}
}

export function readFiles(files) {
	return files.map(readFile);
}

export function getConfigFilesList(folder) {
	return glob.sync(path.join(folder, '*.yml'));
}

export function readConfigFiles(files) {
	return files.reduce((configs, filepath) => {
		let name = removeExtension(path.basename(filepath));
		if (name === 'default') {
			configs.default = readYamlFile(filepath);
		}
		else {
			configs.langs[name] = readYamlFile(filepath);
		}
		return configs;
	}, {default: {}, langs: {}});  // @todo use really default config
}

export function mergeConfigs(configs) {
	let { langs } = configs;
	if (_.isEmpty(langs)) {
		return {
			default: configs.default
		};
	}

	return Object.keys(langs).reduce((merged, lang) => {
		merged[lang] = _.merge({}, configs.default, langs[lang]);
		return merged;
	}, {});
}

export function readConfig(folder) {
	let files = getConfigFilesList(folder);
	let configs = readConfigFiles(files);
	return mergeConfigs(configs);
}

export function makeContext(page, config, helpers) {
	return _.merge({}, helpers, { config }, page);
}
<|endoftext|>


<commit_before>var fs = require('fs'),
    watcher = require('chokidar'),
    EventEmitter = require('events').EventEmitter,
    util = require('../util'),
    constants = require('../conf/constants'),
    events = new EventEmitter(),
    config = util.getConfig(),
    ignored = config.excludes,
    isPaused = false,
    fswatcher = null;

var rebouncedFileChange = util.rebounce(
    onFileChange,
    onBigTransfer,
    constants.NUM_FILES_FOR_RSYNC,
    constants.FILE_CHANGE_COOLDOWN_MS
);

function onBigTransfer() {
    events.emit('large-change');
}

function filterAndRebounce(evt, filepath) {
    var relativePath = filepath.replace(config.sourceLocation, '');

    if (util.isExcluded(relativePath, ignored) || isPaused) return false;

    rebouncedFileChange(evt, filepath);
}

function onFileChange(evt, filepath) {
    if (util.isExcluded(filepath, ignored) || isPaused) return false;
    var fileContents = null;
    var fullDestinationPath = filepath.replace(config.sourceLocation, config.destinationLocation);

    if (evt === 'add' || evt === 'change') fileContents = fs.readFileSync(filepath).toString();

    events.emit('file-change', {
        subject: 'file',
        changeType: evt,
        filepath: fullDestinationPath,
        contents: fileContents
    });
}

function start() {
    if (fswatcher) fswatcher.close();

    fswatcher = watcher.watch(config.sourceLocation, {
        ignored: ignored,
        persistent: true,
        followSymlinks: config.followSymlinks,
        ignoreInitial: true
    }).on('all', filterAndRebounce);
}

function pauseWatch() {
    isPaused = true;
}

function unpauseWatch() {
    isPaused = false;
}

module.exports = {
    pauseWatch: pauseWatch,
    unpauseWatch: unpauseWatch,
    on: events.on.bind(events),
    start: start
};
<commit_msg>Fixing require<commit_after>var fs = require('fs'),
    watcher = require('chokidar'),
    EventEmitter = require('events').EventEmitter,
    util = require('../util'),
    constants = require('../../conf/constants'),
    events = new EventEmitter(),
    config = util.getConfig(),
    ignored = config.excludes,
    isPaused = false,
    fswatcher = null;

var rebouncedFileChange = util.rebounce(
    onFileChange,
    onBigTransfer,
    constants.NUM_FILES_FOR_RSYNC,
    constants.FILE_CHANGE_COOLDOWN_MS
);

function onBigTransfer() {
    events.emit('large-change');
}

function filterAndRebounce(evt, filepath) {
    var relativePath = filepath.replace(config.sourceLocation, '');

    if (util.isExcluded(relativePath, ignored) || isPaused) return false;

    rebouncedFileChange(evt, filepath);
}

function onFileChange(evt, filepath) {
    if (util.isExcluded(filepath, ignored) || isPaused) return false;
    var fileContents = null;
    var fullDestinationPath = filepath.replace(config.sourceLocation, config.destinationLocation);

    if (evt === 'add' || evt === 'change') fileContents = fs.readFileSync(filepath).toString();

    events.emit('file-change', {
        subject: 'file',
        changeType: evt,
        filepath: fullDestinationPath,
        contents: fileContents
    });
}

function start() {
    if (fswatcher) fswatcher.close();

    fswatcher = watcher.watch(config.sourceLocation, {
        ignored: ignored,
        persistent: true,
        followSymlinks: config.followSymlinks,
        ignoreInitial: true
    }).on('all', filterAndRebounce);
}

function pauseWatch() {
    isPaused = true;
}

function unpauseWatch() {
    isPaused = false;
}

module.exports = {
    pauseWatch: pauseWatch,
    unpauseWatch: unpauseWatch,
    on: events.on.bind(events),
    start: start
};
<|endoftext|>


<commit_before>var absURLRegEx = /^[^\/]+:\/\//;

function readMemberExpression(p, value) {
  var pParts = p.split('.');
  while (pParts.length)
    value = value[pParts.shift()];
  return value;
}

var baseURLCache = {};
function getBaseURLObj() {
  if (baseURLCache[this.baseURL])
    return baseURLCache[this.baseURL];

  // normalize baseURL if not already
  if (this.baseURL[this.baseURL.length - 1] != '/')
    this.baseURL += '/';

  var baseURL = new URL(this.baseURL, baseURI);

  this.baseURL = baseURL.href;

  return (baseURLCache[this.baseURL] = baseURL);
}

var baseURIObj = new URL(baseURI);

(function() {

hookConstructor(function(constructor) {
  return function() {
    constructor.call(this);

    // support baseURL
    this.baseURL = baseURI.substr(0, baseURI.lastIndexOf('/') + 1);

    // support the empty module, as a concept
    this.set('@empty', this.newModule({}));
  };
});

/*
  Normalization

  If a name is relative, we apply URL normalization to the page
  If a name is an absolute URL, we leave it as-is

  Plain names (neither of the above) run through the map and package
  normalization phases (applying before and after this one).

  The paths normalization phase applies last (paths extension), which
  defines the `normalizeSync` function and normalizes everything into
  a URL.

  The final normalization
 */
hook('normalize', function() {
  return function(name, parentName) {
    // relative URL-normalization
    if (name[0] == '.' || name[0] == '/')
      return new URL(name, parentName || baseURIObj).href;
    return name;
  };
});

/*
 * Fetch with authorization
 */
hook('fetch', function() {
  return function(load) {
    return new Promise(function(resolve, reject) {
      fetchTextFromURL(load.address, load.metadata.authorization, resolve, reject);
    });
  };
});

/*
  __useDefault

  When a module object looks like:
  newModule(
    __useDefault: true,
    default: 'some-module'
  })

  Then importing that module provides the 'some-module'
  result directly instead of the full module.

  Useful for eg module.exports = function() {}
*/
hook('import', function(systemImport) {
  return function(name, parentName, parentAddress) {
    return systemImport.call(this, name, parentName, parentAddress).then(function(module) {
      return module.__useDefault ? module['default'] : module;
    });
  };
});

/*
 Extend config merging one deep only

  loader.config({
    some: 'random',
    config: 'here',
    deep: {
      config: { too: 'too' }
    }
  });

  <=>

  loader.some = 'random';
  loader.config = 'here'
  loader.deep = loader.deep || {};
  loader.deep.config = { too: 'too' };

  Normalizes meta and package configs allowing for:

  System.config({
    meta: {
      './index.js': {}
    }
  });

  To become

  System.meta['https://thissite.com/index.js'] = {};

  For easy normalization canonicalization with latest URL support.

*/
SystemJSLoader.prototype.deprecationWarnings = true;
SystemJSLoader.prototype.config = function(cfg) {
  if ('deprecationWarnings' in cfg)
    this.deprecationWarnings = cfg.deprecationWarnings;

  // always configure baseURL first
  if (cfg.baseURL) {
    var hasConfig = false;
    function checkHasConfig(obj) {
      for (var p in obj)
        return true;
    }
    if (checkHasConfig(this.packages) || checkHasConfig(this.meta) || checkHasConfig(this.depCache) || checkHasConfig(this.bundles))
      throw new TypeError('baseURL should only be configured once and must be configured first.');

    this.baseURL = cfg.baseURL;

    // sanitize baseURL
    getBaseURLObj.call(this);
  }

  if (cfg.defaultJSExtensions) {
    this.defaultJSExtensions = cfg.defaultJSExtensions;
    dWarn.call(this, 'The defaultJSExtensions configuration option is deprecated, use packages configuration instead.');
  }

  if (cfg.pluginFirst)
    this.pluginFirst = cfg.pluginFirst;

  if (cfg.paths) {
    for (var p in cfg.paths)
      this.paths[p] = cfg.paths[p];
  }

  if (cfg.map) {
    for (var p in cfg.map) {
      var v = cfg.map[p];

      // object map backwards-compat into packages configuration
      if (typeof v !== 'string') {
        dWarn.call(this, 'The map configuration for "' + p + '" is an object, which is deprecated in global map.\nUpdate this to use package contextual map with System.config({ packages: { "' + p + '": { map: {...} } } }).');
        var normalized = this.normalizeSync(p);

        // if doing default js extensions, undo to get package name
        if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
          normalized = normalized.substr(0, normalized.length - 3);

        // if a package main, revert it
        var pkgMatch = '';
        for (var pkg in this.packages) {
          if (normalized.substr(0, pkg.length) == pkg
              && (!normalized[pkg.length] || normalized[pkg.length] == '/')
              && pkgMatch.split('/').length < pkg.split('/').length)
            pkgMatch = pkg;
        }
        if (pkgMatch && this.packages[pkgMatch].main)
          normalized = normalized.substr(0, normalized.length - this.packages[pkgMatch].main.length - 1);

        var pkg = this.packages[normalized] = this.packages[normalized] || {};
        pkg.map = v;
      }
      else {
        this.map[p] = v;
      }
    }
  }

  if (cfg.packageConfigPaths) {
    for (var i = 0; i < cfg.packageConfigPaths.length; i++) {
      var path = cfg.packageConfigPaths[i];
      var packageLength = Math.max(path.lastIndexOf('*') + 1, path.lastIndexOf('/'));
      var normalized = this.normalizeSync(path.substr(0, packageLength) + '/');
      if (this.defaultJSExtensions && path.substr(path.length - 3, 3) != '.js')
        normalized = normalized.substr(0, normalized.length - 3);
      cfg.packageConfigPaths[i] = normalized.substr(0, normalized.length - 1) + path.substr(packageLength);
    }
  }

  if (cfg.packages) {
    for (var p in cfg.packages) {
      if (p.match(/^([^\/]+:)?\/\/$/))
        throw new TypeError('"' + p + '" is not a valid package name.');

      // request with trailing "/" to get package name exactly
      var prop = this.normalizeSync(p + (p[p.length - 1] != '/' ? '/' : ''));
      prop = prop.substr(0, prop.length - 1);

      // if doing default js extensions, undo to get package name
      if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
        prop = prop.substr(0, prop.length - 3);

      this.packages[prop]= this.packages[prop] || {};
      for (var q in cfg.packages[p])
        if (indexOf.call(packageProperties, q) == -1 && typeof console != 'undefined' && console.warn)
          console.warn('"' + q + '" is not a valid package configuration option in package ' + p);

      extendMeta(this.packages[prop], cfg.packages[p]);
    }
  }

  if (cfg.bundles) {
    for (var p in cfg.bundles) {
      var bundle = [];
      for (var i = 0; i < cfg.bundles[p].length; i++)
        bundle.push(this.normalizeSync(cfg.bundles[p][i]));
      this.bundles[p] = bundle;
    }
  }

  for (var c in cfg) {
    var v = cfg[c];
    var normalizeProp = false, normalizeValArray = false;

    if (c == 'baseURL' || c == 'map' || c == 'packages' || c == 'bundles' || c == 'paths' || c == 'deprecationWarnings')
      continue;

    if (typeof v != 'object' || v instanceof Array) {
      this[c] = v;
    }
    else {
      this[c] = this[c] || {};

      if (c == 'meta' || c == 'depCache')
        normalizeProp = true;

      for (var p in v) {
        if (c == 'meta' && p[0] == '*')
          this[c][p] = v[p];
        else if (normalizeProp)
          this[c][this.normalizeSync(p)] = v[p];
        else
          this[c][p] = v[p];
      }
    }
  }
};

})();<commit_msg>make map object deprecation message less verbose<commit_after>var absURLRegEx = /^[^\/]+:\/\//;

function readMemberExpression(p, value) {
  var pParts = p.split('.');
  while (pParts.length)
    value = value[pParts.shift()];
  return value;
}

var baseURLCache = {};
function getBaseURLObj() {
  if (baseURLCache[this.baseURL])
    return baseURLCache[this.baseURL];

  // normalize baseURL if not already
  if (this.baseURL[this.baseURL.length - 1] != '/')
    this.baseURL += '/';

  var baseURL = new URL(this.baseURL, baseURI);

  this.baseURL = baseURL.href;

  return (baseURLCache[this.baseURL] = baseURL);
}

var baseURIObj = new URL(baseURI);

(function() {

hookConstructor(function(constructor) {
  return function() {
    constructor.call(this);

    // support baseURL
    this.baseURL = baseURI.substr(0, baseURI.lastIndexOf('/') + 1);

    // support the empty module, as a concept
    this.set('@empty', this.newModule({}));
  };
});

/*
  Normalization

  If a name is relative, we apply URL normalization to the page
  If a name is an absolute URL, we leave it as-is

  Plain names (neither of the above) run through the map and package
  normalization phases (applying before and after this one).

  The paths normalization phase applies last (paths extension), which
  defines the `normalizeSync` function and normalizes everything into
  a URL.

  The final normalization
 */
hook('normalize', function() {
  return function(name, parentName) {
    // relative URL-normalization
    if (name[0] == '.' || name[0] == '/')
      return new URL(name, parentName || baseURIObj).href;
    return name;
  };
});

/*
 * Fetch with authorization
 */
hook('fetch', function() {
  return function(load) {
    return new Promise(function(resolve, reject) {
      fetchTextFromURL(load.address, load.metadata.authorization, resolve, reject);
    });
  };
});

/*
  __useDefault

  When a module object looks like:
  newModule(
    __useDefault: true,
    default: 'some-module'
  })

  Then importing that module provides the 'some-module'
  result directly instead of the full module.

  Useful for eg module.exports = function() {}
*/
hook('import', function(systemImport) {
  return function(name, parentName, parentAddress) {
    return systemImport.call(this, name, parentName, parentAddress).then(function(module) {
      return module.__useDefault ? module['default'] : module;
    });
  };
});

/*
 Extend config merging one deep only

  loader.config({
    some: 'random',
    config: 'here',
    deep: {
      config: { too: 'too' }
    }
  });

  <=>

  loader.some = 'random';
  loader.config = 'here'
  loader.deep = loader.deep || {};
  loader.deep.config = { too: 'too' };

  Normalizes meta and package configs allowing for:

  System.config({
    meta: {
      './index.js': {}
    }
  });

  To become

  System.meta['https://thissite.com/index.js'] = {};

  For easy normalization canonicalization with latest URL support.

*/
SystemJSLoader.prototype.deprecationWarnings = true;
SystemJSLoader.prototype.config = function(cfg) {
  if ('deprecationWarnings' in cfg)
    this.deprecationWarnings = cfg.deprecationWarnings;

  // always configure baseURL first
  if (cfg.baseURL) {
    var hasConfig = false;
    function checkHasConfig(obj) {
      for (var p in obj)
        return true;
    }
    if (checkHasConfig(this.packages) || checkHasConfig(this.meta) || checkHasConfig(this.depCache) || checkHasConfig(this.bundles))
      throw new TypeError('baseURL should only be configured once and must be configured first.');

    this.baseURL = cfg.baseURL;

    // sanitize baseURL
    getBaseURLObj.call(this);
  }

  if (cfg.defaultJSExtensions) {
    this.defaultJSExtensions = cfg.defaultJSExtensions;
    dWarn.call(this, 'The defaultJSExtensions configuration option is deprecated, use packages configuration instead.');
  }

  if (cfg.pluginFirst)
    this.pluginFirst = cfg.pluginFirst;

  if (cfg.paths) {
    for (var p in cfg.paths)
      this.paths[p] = cfg.paths[p];
  }

  if (cfg.map) {
    var objMaps = '';
    for (var p in cfg.map) {
      var v = cfg.map[p];

      // object map backwards-compat into packages configuration
      if (typeof v !== 'string') {
        objMaps += (objMaps.length ? ', ' : '') + '"' + p + '"';
        var normalized = this.normalizeSync(p);

        // if doing default js extensions, undo to get package name
        if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
          normalized = normalized.substr(0, normalized.length - 3);

        // if a package main, revert it
        var pkgMatch = '';
        for (var pkg in this.packages) {
          if (normalized.substr(0, pkg.length) == pkg
              && (!normalized[pkg.length] || normalized[pkg.length] == '/')
              && pkgMatch.split('/').length < pkg.split('/').length)
            pkgMatch = pkg;
        }
        if (pkgMatch && this.packages[pkgMatch].main)
          normalized = normalized.substr(0, normalized.length - this.packages[pkgMatch].main.length - 1);

        var pkg = this.packages[normalized] = this.packages[normalized] || {};
        pkg.map = v;
      }
      else {
        this.map[p] = v;
      }
    }
    if (objMaps)
      dWarn.call(this, 'The map configuration for ' + objMaps + ' uses object submaps, which is deprecated in global map.\nUpdate this to use package contextual map with configs like System.config({ packages: { "' + p + '": { map: {...} } } }).');
  }

  if (cfg.packageConfigPaths) {
    for (var i = 0; i < cfg.packageConfigPaths.length; i++) {
      var path = cfg.packageConfigPaths[i];
      var packageLength = Math.max(path.lastIndexOf('*') + 1, path.lastIndexOf('/'));
      var normalized = this.normalizeSync(path.substr(0, packageLength) + '/');
      if (this.defaultJSExtensions && path.substr(path.length - 3, 3) != '.js')
        normalized = normalized.substr(0, normalized.length - 3);
      cfg.packageConfigPaths[i] = normalized.substr(0, normalized.length - 1) + path.substr(packageLength);
    }
  }

  if (cfg.packages) {
    for (var p in cfg.packages) {
      if (p.match(/^([^\/]+:)?\/\/$/))
        throw new TypeError('"' + p + '" is not a valid package name.');

      // request with trailing "/" to get package name exactly
      var prop = this.normalizeSync(p + (p[p.length - 1] != '/' ? '/' : ''));
      prop = prop.substr(0, prop.length - 1);

      // if doing default js extensions, undo to get package name
      if (this.defaultJSExtensions && p.substr(p.length - 3, 3) != '.js')
        prop = prop.substr(0, prop.length - 3);

      this.packages[prop]= this.packages[prop] || {};
      for (var q in cfg.packages[p])
        if (indexOf.call(packageProperties, q) == -1 && typeof console != 'undefined' && console.warn)
          console.warn('"' + q + '" is not a valid package configuration option in package ' + p);

      extendMeta(this.packages[prop], cfg.packages[p]);
    }
  }

  if (cfg.bundles) {
    for (var p in cfg.bundles) {
      var bundle = [];
      for (var i = 0; i < cfg.bundles[p].length; i++)
        bundle.push(this.normalizeSync(cfg.bundles[p][i]));
      this.bundles[p] = bundle;
    }
  }

  for (var c in cfg) {
    var v = cfg[c];
    var normalizeProp = false, normalizeValArray = false;

    if (c == 'baseURL' || c == 'map' || c == 'packages' || c == 'bundles' || c == 'paths' || c == 'deprecationWarnings')
      continue;

    if (typeof v != 'object' || v instanceof Array) {
      this[c] = v;
    }
    else {
      this[c] = this[c] || {};

      if (c == 'meta' || c == 'depCache')
        normalizeProp = true;

      for (var p in v) {
        if (c == 'meta' && p[0] == '*')
          this[c][p] = v[p];
        else if (normalizeProp)
          this[c][this.normalizeSync(p)] = v[p];
        else
          this[c][p] = v[p];
      }
    }
  }
};

})();<|endoftext|>


<commit_before>var WebSocket = require('ws'),
    EventEmitter = require('events').EventEmitter,
    remoteHelper = require('./remote-helper'),
    util = require('../util'),
    Crypt = require('../crypt'),
    config = util.getConfig(),
    crypt = null,
    events = null,
    ws = null,
    hostUrl = null;

function WSClient(params) {
    hostUrl = params.url;
    crypt = new Crypt(config.secret);
    events = new EventEmitter();
    connect();

    return {
        send: send,
        on: events.on.bind(events)
    };
}

function send(obj) {
    obj.secret = config.secret;
    ws.send(crypt.stringifyAndEncrypt(obj, config.prefersEncrypted));
}

function handleConnect() {
    events.emit('ready');
}

function handleDisconnect() {
    events.emit('disconnected');

    if (config.retryOnDisconnect) {
        return reconnect();
    }

    process.exit();
}

function reconnect() {
    var devbox = remoteHelper.start();
    events.emit('reconnecting');

    devbox.on('ready', connect);
    devbox.on('message', console.log.bind(console));
}

function connect() {
    ws = new WebSocket(hostUrl);
    ws.on('open', handleConnect);
    ws.on('close', handleDisconnect);
    ws.on('error', reconnect);
}

module.exports = WSClient;
<commit_msg>Updating remote helper usage<commit_after>var WebSocket = require('ws'),
    EventEmitter = require('events').EventEmitter,
    RemoteHelper = require('./remote-helper'),
    util = require('../util'),
    Crypt = require('../crypt'),
    config = util.getConfig(),
    crypt = null,
    events = null,
    ws = null,
    hostUrl = null;

function WSClient(params) {
    hostUrl = params.url;
    crypt = new Crypt(config.secret);
    events = new EventEmitter();
    connect();

    return {
        send: send,
        on: events.on.bind(events)
    };
}

function send(obj) {
    obj.secret = config.secret;
    ws.send(crypt.stringifyAndEncrypt(obj, config.prefersEncrypted));
}

function handleConnect() {
    events.emit('ready');
}

function handleDisconnect() {
    events.emit('disconnected');

    if (config.retryOnDisconnect) {
        return reconnect();
    }

    process.exit();
}

function reconnect() {
    var devbox = new RemoteHelper();
    events.emit('reconnecting');

    devbox.start();
    devbox.on('ready', connect);
    devbox.on('message', console.log.bind(console));
}

function connect() {
    ws = new WebSocket(hostUrl);
    ws.on('open', handleConnect);
    ws.on('close', handleDisconnect);
    ws.on('error', reconnect);
}

module.exports = WSClient;
<|endoftext|>


<commit_before>(function () {
    'use strict';

    var module = angular.module('Aeonium.models');

    //A service for message
    module.factory('Aeonium.models.MessageList', [
        '$http', 'djangoUrl', '$rootScope',
        'Aeonium.models.Message',
        function messageListFactory($http, djangoUrl, $rootScope, Message) {

            var MessageList = function () {
                var self = this;
                self.listCache = {};
                self.listCache.time = {};
                self.listCache.last_updated = {};
                self.listCache.ambiguity = {};
            };

            // class MessageList {
            //     listCache: dict;
            //     listCache.time: dict;
            //     listCache.last_updated: dict;
            //     listCache.ambiguity: dict;
            // }

            angular.extend(MessageList.prototype, {
                getList: function(order_by, page){
                    var self = this;
                    var apiUrl = djangoUrl.reverse('list');

                    var request = {
                        params: {
                            order_by: order_by || "time",
                            page: page || 1
                        }
                    };
                    if (self.listCache[order_by].hasOwnProperty(page)){
                        var data = self.listCache[order_by][page];
                        $rootScope.$broadcast("list::getList", data);
                        return false;
                    }

                    return $http.get(apiUrl, request)
                        .success(function (data) {

                            self.listCache[order_by][page] = data;
                            $rootScope.$broadcast("list::getList", data);
                        });

                },

            });

            return new Message();
        }
    ]);
})();
<commit_msg>fix typo<commit_after>(function () {
    'use strict';

    var module = angular.module('Aeonium.models');

    //A service for message
    module.factory('Aeonium.models.MessageList', [
        '$http', 'djangoUrl', '$rootScope',
        'Aeonium.models.Message',
        function messageListFactory($http, djangoUrl, $rootScope, Message) {

            var MessageList = function () {
                var self = this;
                self.listCache = {};
                self.listCache.time = {};
                self.listCache.last_updated = {};
                self.listCache.ambiguity = {};
            };

            // class MessageList {
            //     listCache: dict;
            //     listCache.time: dict;
            //     listCache.last_updated: dict;
            //     listCache.ambiguity: dict;
            // }

            angular.extend(MessageList.prototype, {
                getList: function(order_by, page){
                    var self = this;
                    var apiUrl = djangoUrl.reverse('list');

                    var request = {
                        params: {
                            order_by: order_by || "time",
                            page: page || 1
                        }
                    };
                    if (self.listCache[order_by].hasOwnProperty(page)){
                        var data = self.listCache[order_by][page];
                        $rootScope.$broadcast("list::getList", data);
                        return false;
                    }

                    return $http.get(apiUrl, request)
                        .success(function (data) {

                            self.listCache[order_by][page] = data;
                            $rootScope.$broadcast("list::getList", data);
                        });

                },

            });

            return new MessageList();
        }
    ]);
})();
<|endoftext|>


<commit_before>(function($, d3, global, undefined){

    var color = d3.scale.ordinal().range([
        "#4F2938",
        "#EABB7E",
        "#DB7E64",
        "#2B6367",
        "#244E66",
        "#B3424A",
        "#437BAB",
        "#D8DCBF",
        "#A5BBAE",
        "#8CB6DA",
        "#FFD991",
        "#70A09A"
    ]);

    function colourFor(selector) {
        return color(selector)
    }

    function donut(elSelector, data, valueFn, paletteFn, formatFn){
        formatFn = formatFn || valueFn

        var width = 200;

        // items are square s
        var height = width;

        var radius = Math.min(width, height) / 2;

        var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(radius - 50);
        var pie = d3.layout.pie().sort(null).value(valueFn);
        var svg = d3.select(elSelector).append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .append("g")
                        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        svg.selectAll(".arc").data(pie(data)).enter().append("g")
            .attr("class", "arc")
            .append("path")
                .attr("d", arc)
                .style("fill", function(d) { return colourFor(paletteFn(d.data)); })
                .append("title")
                    .text( function(d) { return paletteFn(d.data) + ": " + formatFn(d.data)})

    }

    function donutLegend(elSelector, itSelector, size, data, paletteFn){

        d3.select(elSelector).selectAll(itSelector).data(data).insert("svg")
            .attr("width", function() { return $(this).width() })
            .attr("height", function() { return $(this).height() })
            .append("rect")
                .attr("width", "100%")
                .attr("height", 46)
                .attr("width", 10)
                .style("fill", function(d) {
                    return colourFor(paletteFn(d));
                });
    }

    global.charts = global.charts || {}
    global.charts.donut = donut
    global.charts.donutLegend = donutLegend
    global.charts.colourFor = colourFor

})(jQuery, d3, this)
<commit_msg>Change height of donut legend<commit_after>(function($, d3, global, undefined){

    var color = d3.scale.ordinal().range([
        "#4F2938",
        "#EABB7E",
        "#DB7E64",
        "#2B6367",
        "#244E66",
        "#B3424A",
        "#437BAB",
        "#D8DCBF",
        "#A5BBAE",
        "#8CB6DA",
        "#FFD991",
        "#70A09A"
    ]);

    function colourFor(selector) {
        return color(selector)
    }

    function donut(elSelector, data, valueFn, paletteFn, formatFn){
        formatFn = formatFn || valueFn

        var width = 200;

        // items are square s
        var height = width;

        var radius = Math.min(width, height) / 2;

        var arc = d3.svg.arc().outerRadius(radius - 10).innerRadius(radius - 50);
        var pie = d3.layout.pie().sort(null).value(valueFn);
        var svg = d3.select(elSelector).append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .append("g")
                        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        svg.selectAll(".arc").data(pie(data)).enter().append("g")
            .attr("class", "arc")
            .append("path")
                .attr("d", arc)
                .style("fill", function(d) { return colourFor(paletteFn(d.data)); })
                .append("title")
                    .text( function(d) { return paletteFn(d.data) + ": " + formatFn(d.data)})

    }

    function donutLegend(elSelector, itSelector, size, data, paletteFn){

        d3.select(elSelector).selectAll(itSelector).data(data).insert("svg")
            .attr("width", function() { return $(this).width() })
            .attr("height", function() { return $(this).height() })
            .append("rect")
                .attr("width", "100%")
                .attr("height", 56)
                .attr("width", 10)
                .style("fill", function(d) {
                    return colourFor(paletteFn(d));
                });
    }

    global.charts = global.charts || {}
    global.charts.donut = donut
    global.charts.donutLegend = donutLegend
    global.charts.colourFor = colourFor

})(jQuery, d3, this)
<|endoftext|>


<commit_before>/**
 * Not type checking this file because flow doesn't like attaching
 * properties to Elements.
 */

import { looseEqual, looseIndexOf } from 'shared/util'
import { warn, isAndroid, isIE9, isIE, isEdge } from 'core/util/index'

const modelableTagRE = /^input|select|textarea|vue-component-[0-9]+(-[0-9a-zA-Z_\-]*)?$/

/* istanbul ignore if */
if (isIE9) {
  // http://www.matts411.com/post/internet-explorer-9-oninput/
  document.addEventListener('selectionchange', () => {
    const el = document.activeElement
    if (el && el.vmodel) {
      trigger(el, 'input')
    }
  })
}

export default {
  inserted (el, binding, vnode) {
    if (process.env.NODE_ENV !== 'production') {
      if (!modelableTagRE.test(vnode.tag)) {
        warn(
          `v-model is not supported on element type: <${vnode.tag}>. ` +
          'If you are working with contenteditable, it\'s recommended to ' +
          'wrap a library dedicated for that purpose inside a custom component.',
          vnode.context
        )
      }
    }
    if (vnode.tag === 'select') {
      const cb = () => {
        setSelected(el, binding, vnode.context)
      }
      cb()
      /* istanbul ignore if */
      if (isIE || isEdge) {
        setTimeout(cb, 0)
      }
    } else if (vnode.tag === 'textarea' || el.type === 'text') {
      if (!isAndroid) {
        el.addEventListener('compositionstart', onCompositionStart)
        el.addEventListener('compositionend', onCompositionEnd)
      }
      /* istanbul ignore if */
      if (isIE9) {
        el.vmodel = true
      }
    }
  },
  componentUpdated (el, binding, vnode) {
    if (vnode.tag === 'select') {
      setSelected(el, binding, vnode.context)
      // in case the options rendered by v-for have changed,
      // it's possible that the value is out-of-sync with the rendered options.
      // detect such cases and filter out values that no longer has a matchig
      // option in the DOM.
      const needReset = el.multiple
        ? binding.value.some(v => hasNoMatchingOption(v, el.options))
        : binding.value === binding.oldValue ? false : hasNoMatchingOption(binding.value, el.options)
      if (needReset) {
        trigger(el, 'change')
      }
    }
  }
}

function setSelected (el, binding, vm) {
  const value = binding.value
  const isMultiple = el.multiple
  if (isMultiple && !Array.isArray(value)) {
    process.env.NODE_ENV !== 'production' && warn(
      `<select multiple v-model="${binding.expression}"> ` +
      `expects an Array value for its binding, but got ${
        Object.prototype.toString.call(value).slice(8, -1)
      }`,
      vm
    )
    return
  }
  let selected, option
  for (let i = 0, l = el.options.length; i < l; i++) {
    option = el.options[i]
    if (isMultiple) {
      selected = looseIndexOf(value, getValue(option)) > -1
      if (option.selected !== selected) {
        option.selected = selected
      }
    } else {
      if (looseEqual(getValue(option), value)) {
        if (el.selectedIndex !== i) {
          el.selectedIndex = i
        }
        return
      }
    }
  }
  if (!isMultiple) {
    el.selectedIndex = -1
  }
}

function hasNoMatchingOption (value, options) {
  for (let i = 0, l = options.length; i < l; i++) {
    if (looseEqual(getValue(options[i]), value)) {
      return false
    }
  }
  return true
}

function getValue (option) {
  return '_value' in option
    ? option._value
    : option.value
}

function onCompositionStart (e) {
  e.target.composing = true
}

function onCompositionEnd (e) {
  e.target.composing = false
  trigger(e.target, 'input')
}

function trigger (el, type) {
  const e = document.createEvent('HTMLEvents')
  e.initEvent(type, true, true)
  el.dispatchEvent(e)
}
<commit_msg>small tweak<commit_after>/**
 * Not type checking this file because flow doesn't like attaching
 * properties to Elements.
 */

import { looseEqual, looseIndexOf } from 'shared/util'
import { warn, isAndroid, isIE9, isIE, isEdge } from 'core/util/index'

const modelableTagRE = /^input|select|textarea|vue-component-[0-9]+(-[0-9a-zA-Z_\-]*)?$/

/* istanbul ignore if */
if (isIE9) {
  // http://www.matts411.com/post/internet-explorer-9-oninput/
  document.addEventListener('selectionchange', () => {
    const el = document.activeElement
    if (el && el.vmodel) {
      trigger(el, 'input')
    }
  })
}

export default {
  inserted (el, binding, vnode) {
    if (process.env.NODE_ENV !== 'production') {
      if (!modelableTagRE.test(vnode.tag)) {
        warn(
          `v-model is not supported on element type: <${vnode.tag}>. ` +
          'If you are working with contenteditable, it\'s recommended to ' +
          'wrap a library dedicated for that purpose inside a custom component.',
          vnode.context
        )
      }
    }
    if (vnode.tag === 'select') {
      const cb = () => {
        setSelected(el, binding, vnode.context)
      }
      cb()
      /* istanbul ignore if */
      if (isIE || isEdge) {
        setTimeout(cb, 0)
      }
    } else if (vnode.tag === 'textarea' || el.type === 'text') {
      if (!isAndroid) {
        el.addEventListener('compositionstart', onCompositionStart)
        el.addEventListener('compositionend', onCompositionEnd)
      }
      /* istanbul ignore if */
      if (isIE9) {
        el.vmodel = true
      }
    }
  },
  componentUpdated (el, binding, vnode) {
    if (vnode.tag === 'select') {
      setSelected(el, binding, vnode.context)
      // in case the options rendered by v-for have changed,
      // it's possible that the value is out-of-sync with the rendered options.
      // detect such cases and filter out values that no longer has a matchig
      // option in the DOM.
      const needReset = el.multiple
        ? binding.value.some(v => hasNoMatchingOption(v, el.options))
        : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, el.options)
      if (needReset) {
        trigger(el, 'change')
      }
    }
  }
}

function setSelected (el, binding, vm) {
  const value = binding.value
  const isMultiple = el.multiple
  if (isMultiple && !Array.isArray(value)) {
    process.env.NODE_ENV !== 'production' && warn(
      `<select multiple v-model="${binding.expression}"> ` +
      `expects an Array value for its binding, but got ${
        Object.prototype.toString.call(value).slice(8, -1)
      }`,
      vm
    )
    return
  }
  let selected, option
  for (let i = 0, l = el.options.length; i < l; i++) {
    option = el.options[i]
    if (isMultiple) {
      selected = looseIndexOf(value, getValue(option)) > -1
      if (option.selected !== selected) {
        option.selected = selected
      }
    } else {
      if (looseEqual(getValue(option), value)) {
        if (el.selectedIndex !== i) {
          el.selectedIndex = i
        }
        return
      }
    }
  }
  if (!isMultiple) {
    el.selectedIndex = -1
  }
}

function hasNoMatchingOption (value, options) {
  for (let i = 0, l = options.length; i < l; i++) {
    if (looseEqual(getValue(options[i]), value)) {
      return false
    }
  }
  return true
}

function getValue (option) {
  return '_value' in option
    ? option._value
    : option.value
}

function onCompositionStart (e) {
  e.target.composing = true
}

function onCompositionEnd (e) {
  e.target.composing = false
  trigger(e.target, 'input')
}

function trigger (el, type) {
  const e = document.createEvent('HTMLEvents')
  e.initEvent(type, true, true)
  el.dispatchEvent(e)
}
<|endoftext|>


<commit_before>
/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement () {
    $('

').appendTo('body')
    return $('

').appendTo('body')
}

function evalAttentionChecks() {
  var check_percent = 1
  if (run_attention_checks) {
    var attention_check_trials = jsPsych.data.getTrialsOfType('attention-check')
    var checks_passed = 0
    for (var i = 0; i < attention_check_trials.length; i++) {
      if (attention_check_trials[i].correct === true) {
        checks_passed += 1
      }
    }
    check_percent = checks_passed/attention_check_trials.length
  }
  return check_percent
}

function addID() {
  jsPsych.data.addDataToLastTrial({'exp_id': 'digit_span'})
}

var getInstructFeedback = function() {
	return '

' + feedback_instruct_text + '

'
}

var arraysEqual = function(arr1, arr2) {
    if(arr1.length !== arr2.length)
        return false;
    for(var i = arr1.length; i--;) {
        if(arr1[i] !== arr2[i])
            return false;
    }
    return true;
}

var setStims = function() {
  curr_seq = []
  stim_array = []
  time_array = []
  for (var i=0; i< num_digits; i++) {
    var num = Math.floor(Math.random()*9)+1
    curr_seq.push(num)
    stim_array.push('

' + num.toString() + '

')
    time_array.push(stim_time)
  }
  total_time = num_digits * (stim_time + gap_time)
}

var getTestText = function() {
 return  '

' + num_digits + ' Digits

'
}

var getStims = function() {
  return stim_array
}

var getTimeArray = function() {
  return time_array
}

var getTotalTime = function() {
  return total_time
}

var getFeedback = function() {
  return '

' + feedback + '

'
}

var recordClick = function(elm) {
  response.push(Number($(elm).text()))
}

var clearResponse = function() {
  response = []
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
// generic task variables
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0    //ms
var instructTimeThresh = 5   ///in seconds

// task specific variables
var num_digits = 4
var num_trials = 10
var curr_seq = []
var stim_time = 800
var gap_time = 200
var time_array = []
var total_time = 0
var errors = 0
var error_lim = 3
var response = []
setStims()
var stim_array = getStims()

var response_grid =
    '
' +
    '<button id = button_1 class = "square num-button" onclick = "recordClick(this)">

1

</button>' +
    '<button id = button_2 class = "square num-button" onclick = "recordClick(this)">

2

</button>' +
    '<button id = button_3 class = "square num-button" onclick = "recordClick(this)">

3

</button>' +
    '<button id = button_4 class = "square num-button" onclick = "recordClick(this)">

4

</button>' +
    '<button id = button_5 class = "square num-button" onclick = "recordClick(this)">

5

</button>' +
    '<button id = button_6 class = "square num-button" onclick = "recordClick(this)">

6

</button>' +
    '<button id = button_7 class = "square num-button" onclick = "recordClick(this)">

7

</button>' +
    '<button id = button_8 class = "square num-button" onclick = "recordClick(this)">

8

</button>' +
    '<button id = button_9 class = "square num-button" onclick = "recordClick(this)">

9

</button>' +
    '<button class = clear_button id = "ClearButton" onclick = "clearResponse">Clear</button>' +
    '<button class = submit_button id = "SubmitButton">Submit Answer</button>
'

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
// Set up attention check node
var attention_check_block = {
  type: 'attention-check',
  data: {exp_id: "digit_span", trial_id: "attention_check"},
  timing_response: 30000,
  response_ends_trial: true,
  timing_post_trial: 200
}

var attention_node = {
  timeline: [attention_check_block],
  conditional_function: function() {
    return run_attention_checks
  }
}

/* define static blocks */
var welcome_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "welcome"},
  text: '

Welcome to the experiment. Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 0
};

var feedback_instruct_text = 'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  cont_key: [13],
  data: {exp_id: "digit_span", trial_id: "instruction"},
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 6000
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  data: {exp_id: "digit_span", trial_id: "instruction"},
  pages: [
  '

In this test you will have to try to remember a sequence of numbers that will appear on the screen one after the other. At the end of each trial, enter all the numbers into the presented numpad in the sequence in which they occurred.

If you correctly remember all of the numbers then the next list of numbers will be one number longer. If you make a mistake then the next list of numbers will be one number shorter.

After three errors, the test will end. Trials will start after you end instructions.

'
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
    timeline: instruction_trials,
	/* This function defines stopping criteria */
    loop_function: function(data){
		for(i=0;i<data.length;i++){
			if((data[i].trial_type=='poldrack-instructions') && (data[i].rt!=-1)){
				rt=data[i].rt
				sumInstructTime=sumInstructTime+rt
			}
		}
		if(sumInstructTime<=instructTimeThresh*1000){
			feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
			return true
		} else if(sumInstructTime>instructTimeThresh*1000){
			feedback_instruct_text = 'Done with instructions. Press enter to continue.'
			return false
		}
    }
}

var end_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "end"},
  text: '

Thanks for completing this task!

Press enter to continue.

',
  cont_key: [13],
  timing_post_trial: 0
};

var start_test_block = {
  type: 'poldrack-single-stim',
  is_html: true,
  stimulus: getTestText,
  data: {exp_id: "digit_span", trial_id: "test_intro"},
  choices: 'none',
  timing_stim: 1000,
  timing_response: 1000,
  response_ends_trial: false,
  timing_post_trial: 1000
};

var start_reverse_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "start_reverse"},
  text: '

In these next trials, instead of reporting back the sequence you just saw, report the reverse of that sequence. So the last item should be first in your response, the second to last should be the second in your response, etc...

Press enter to begin.

',
  cont_key: [13]
}

/* define test block */
var test_block = {
  type: 'poldrack-multi-stim-multi-response',
  stimuli: getStims,
  is_html: true,
  timing_stim: getTimeArray,
  timing_gap: gap_time,
  choices: [['none']],
  data: {exp_id: "digit_span", trial_id: "stim", exp_stage: 'test'},
  timing_response: getTotalTime,
  timing_post_trial: 0,
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"sequence": curr_seq, "num_digits": num_digits})
  }
}

var forward_response_block = {
  type: 'single-stim-button',
  stimulus: response_grid,
  button_class: 'submit_button',
  data: {exp_id: "digit_span", trial_id: "response", exp_stage: 'test'},
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"response": response, "sequence": curr_seq, "num_digits": num_digits,  "condition": "forward"})
      var fb = 0
      // staircase
      if (arraysEqual(response,curr_seq)) {
        num_digits += 1
        feedback = 'Correct!'
        stims = setStims()
        fb = 1
      } else {
        if (num_digits > 1) {
          num_digits -= 1
        }
        errors += 1
        feedback = 'Incorrect!'
        stims = setStims()
      }
    jsPsych.data.addDataToLastTrial({feedback: fb})
    response = []
  },
  timing_post_trial: 500
}

var reverse_response_block = {
  type: 'single-stim-button',
  stimulus: response_grid,
  button_class: 'submit_button',
  data: {exp_id: "digit_span", trial_id: "response", exp_stage: 'test'},
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"response": response, "sequence": curr_seq, "num_digits": num_digits, "condition": "reverse", feedback: fb})
      var fb = 0
      // staircase
      if (arraysEqual(response.reverse(),curr_seq)) {
        num_digits += 1
        feedback = 'Correct!'
        stims = setStims()
        fb = 1
      } else {
        if (num_digits > 1) {
          num_digits -= 1
        }
        errors += 1
        feedback = 'Incorrect!'
        stims = setStims()
      }
    jsPsych.data.addDataToLastTrial({feedback: fb})
    response = []
  },
  timing_post_trial: 500
}

var feedback_block = {
    type: 'poldrack-single-stim',
    stimulus: getFeedback,
    data: {exp_id: "digit_span", trial_id: "feedback"},
    is_html: true,
    choices: 'none',
    timing_stim: 1000,
    timing_response: 1000,
    response_ends_trial: true
}

var forward_node = {
  timeline: [start_test_block, test_block, forward_response_block, feedback_block],
  loop_function: function(data) {
    if (errors < error_lim) {
      return true
    } else {
      errors = 0
      num_digits = 4
      stims = setStims()
      return false
    }
  }
}

var reverse_node = {
  timeline: [start_test_block, test_block, reverse_response_block, feedback_block],
  loop_function: function(data) {
    if (errors < error_lim) {return true}
    else {return false}
  }
}

/* create experiment definition array */
var digit_span_experiment = [];
digit_span_experiment.push(welcome_block);
digit_span_experiment.push(instruction_node);
digit_span_experiment.push(forward_node)
digit_span_experiment.push(attention_node)
digit_span_experiment.push(start_reverse_block)
digit_span_experiment.push(reverse_node)
digit_span_experiment.push(end_block)
<commit_msg>This fixed #118 and I took out the part of the instructions that tells the subjects about the staircase<commit_after>
/* ************************************ */
/* Define helper functions */
/* ************************************ */
function getDisplayElement () {
    $('

').appendTo('body')
    return $('

').appendTo('body')
}

function evalAttentionChecks() {
  var check_percent = 1
  if (run_attention_checks) {
    var attention_check_trials = jsPsych.data.getTrialsOfType('attention-check')
    var checks_passed = 0
    for (var i = 0; i < attention_check_trials.length; i++) {
      if (attention_check_trials[i].correct === true) {
        checks_passed += 1
      }
    }
    check_percent = checks_passed/attention_check_trials.length
  }
  return check_percent
}

function addID() {
  jsPsych.data.addDataToLastTrial({'exp_id': 'digit_span'})
}

var getInstructFeedback = function() {
	return '

' + feedback_instruct_text + '

'
}

var arraysEqual = function(arr1, arr2) {
    if(arr1.length !== arr2.length)
        return false;
    for(var i = arr1.length; i--;) {
        if(arr1[i] !== arr2[i])
            return false;
    }
    return true;
}

var setStims = function() {
  curr_seq = []
  stim_array = []
  time_array = []
  for (var i=0; i< num_digits; i++) {
    var num = Math.floor(Math.random()*9)+1
    curr_seq.push(num)
    stim_array.push('

' + num.toString() + '

')
    time_array.push(stim_time)
  }
  total_time = num_digits * (stim_time + gap_time)
}

var getTestText = function() {
 return  '

' + num_digits + ' Digits

'
}

var getStims = function() {
  return stim_array
}

var getTimeArray = function() {
  return time_array
}

var getTotalTime = function() {
  return total_time
}

var getFeedback = function() {
  return '

' + feedback + '

'
}

var recordClick = function(elm) {
  response.push(Number($(elm).text()))
}

var clearResponse = function() {
  response = []
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
// generic task variables
var run_attention_checks = true
var attention_check_thresh = 0.65
var sumInstructTime = 0    //ms
var instructTimeThresh = 5   ///in seconds

// task specific variables
var num_digits = 4
var num_trials = 10
var curr_seq = []
var stim_time = 800
var gap_time = 200
var time_array = []
var total_time = 0
var errors = 0
var error_lim = 3
var response = []
setStims()
var stim_array = getStims()

var response_grid =
    '
' +
    '<button id = button_1 class = "square num-button" onclick = "recordClick(this)">

1

</button>' +
    '<button id = button_2 class = "square num-button" onclick = "recordClick(this)">

2

</button>' +
    '<button id = button_3 class = "square num-button" onclick = "recordClick(this)">

3

</button>' +
    '<button id = button_4 class = "square num-button" onclick = "recordClick(this)">

4

</button>' +
    '<button id = button_5 class = "square num-button" onclick = "recordClick(this)">

5

</button>' +
    '<button id = button_6 class = "square num-button" onclick = "recordClick(this)">

6

</button>' +
    '<button id = button_7 class = "square num-button" onclick = "recordClick(this)">

7

</button>' +
    '<button id = button_8 class = "square num-button" onclick = "recordClick(this)">

8

</button>' +
    '<button id = button_9 class = "square num-button" onclick = "recordClick(this)">

9

</button>' +
    '<button class = clear_button id = "ClearButton" onclick = "clearResponse">Clear</button>' +
    '<button class = submit_button id = "SubmitButton">Submit Answer</button>
'

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
// Set up attention check node
var attention_check_block = {
  type: 'attention-check',
  data: {exp_id: "digit_span", trial_id: "attention_check"},
  timing_response: 30000,
  response_ends_trial: true,
  timing_post_trial: 200
}

var attention_node = {
  timeline: [attention_check_block],
  conditional_function: function() {
    return run_attention_checks
  }
}

/* define static blocks */
var welcome_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "welcome"},
  text: '

Welcome to the experiment. Press enter to begin.

',
  cont_key: [13],
  timing_post_trial: 0
};

var feedback_instruct_text = 'Starting with instructions.  Press  Enter  to continue.'
var feedback_instruct_block = {
  type: 'poldrack-text',
  cont_key: [13],
  data: {exp_id: "digit_span", trial_id: "instruction"},
  text: getInstructFeedback,
  timing_post_trial: 0,
  timing_response: 6000
};
/// This ensures that the subject does not read through the instructions too quickly.  If they do it too quickly, then we will go over the loop again.
var instruction_trials = []
var instructions_block = {
  type: 'poldrack-instructions',
  data: {exp_id: "digit_span", trial_id: "instruction"},
  pages: [
  '

In this test you will have to try to remember a sequence of numbers that will appear on the screen one after the other. At the end of each trial, enter all the numbers into the presented numpad in the sequence in which they occurred.

After three errors, the test will end. Trials will start after you end instructions.

'
  ],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};
instruction_trials.push(feedback_instruct_block)
instruction_trials.push(instructions_block)

var instruction_node = {
    timeline: instruction_trials,
	/* This function defines stopping criteria */
    loop_function: function(data){
		for(i=0;i<data.length;i++){
			if((data[i].trial_type=='poldrack-instructions') && (data[i].rt!=-1)){
				rt=data[i].rt
				sumInstructTime=sumInstructTime+rt
			}
		}
		if(sumInstructTime<=instructTimeThresh*1000){
			feedback_instruct_text = 'Read through instructions too quickly.  Please take your time and make sure you understand the instructions.  Press enter to continue.'
			return true
		} else if(sumInstructTime>instructTimeThresh*1000){
			feedback_instruct_text = 'Done with instructions. Press enter to continue.'
			return false
		}
    }
}

var end_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "end"},
  text: '

Thanks for completing this task!

Press enter to continue.

',
  cont_key: [13],
  timing_post_trial: 0
};

var start_test_block = {
  type: 'poldrack-single-stim',
  is_html: true,
  stimulus: getTestText,
  data: {exp_id: "digit_span", trial_id: "test_intro"},
  choices: 'none',
  timing_stim: 1000,
  timing_response: 1000,
  response_ends_trial: false,
  timing_post_trial: 1000
};

var start_reverse_block = {
  type: 'poldrack-text',
  timing_response: 60000,
  data: {exp_id: "digit_span", trial_id: "start_reverse"},
  text: '

In these next trials, instead of reporting back the sequence you just saw, report the reverse of that sequence. So the last item should be first in your response, the second to last should be the second in your response, etc...

Press enter to begin.

',
  cont_key: [13]
}

/* define test block */
var test_block = {
  type: 'poldrack-multi-stim-multi-response',
  stimuli: getStims,
  is_html: true,
  timing_stim: getTimeArray,
  timing_gap: gap_time,
  choices: [['none']],
  data: {exp_id: "digit_span", trial_id: "stim", exp_stage: 'test'},
  timing_response: getTotalTime,
  timing_post_trial: 0,
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"sequence": curr_seq, "num_digits": num_digits})
  }
}

var forward_response_block = {
  type: 'single-stim-button',
  stimulus: response_grid,
  button_class: 'submit_button',
  data: {exp_id: "digit_span", trial_id: "response", exp_stage: 'test'},
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"response": response, "sequence": curr_seq, "num_digits": num_digits,  "condition": "forward"})
      var fb = 0
      // staircase
      if (arraysEqual(response,curr_seq)) {
        num_digits += 1
        feedback = 'Correct!'
        stims = setStims()
        fb = 1
      } else {
        if (num_digits > 1) {
          num_digits -= 1
        }
        errors += 1
        feedback = 'Incorrect!'
        stims = setStims()
      }
    jsPsych.data.addDataToLastTrial({feedback: fb})
    response = []
  },
  timing_post_trial: 500
}

var reverse_response_block = {
  type: 'single-stim-button',
  stimulus: response_grid,
  button_class: 'submit_button',
  data: {exp_id: "digit_span", trial_id: "response", exp_stage: 'test'},
  on_finish: function() {
    jsPsych.data.addDataToLastTrial({"response": response, "sequence": curr_seq, "num_digits": num_digits, "condition": "reverse", feedback: fb})
      var fb = 0
      // staircase
      if (arraysEqual(response.reverse(),curr_seq)) {
        num_digits += 1
        feedback = 'Correct!'
        stims = setStims()
        fb = 1
      } else {
        if (num_digits > 1) {
          num_digits -= 1
        }
        errors += 1
        feedback = 'Incorrect!'
        stims = setStims()
      }
    jsPsych.data.addDataToLastTrial({feedback: fb})
    response = []
  },
  timing_post_trial: 500
}

var feedback_block = {
    type: 'poldrack-single-stim',
    stimulus: getFeedback,
    data: {exp_id: "digit_span", trial_id: "feedback"},
    is_html: true,
    choices: 'none',
    timing_stim: 1000,
    timing_response: 1000,
    response_ends_trial: true
}

var forward_node = {
  timeline: [start_test_block, test_block, forward_response_block, feedback_block],
  loop_function: function(data) {
    if (errors < error_lim) {
      return true
    } else {
      errors = 0
      num_digits = 4
      stims = setStims()
      return false
    }
  }
}

var reverse_node = {
  timeline: [start_test_block, test_block, reverse_response_block, feedback_block],
  loop_function: function(data) {
    if (errors < error_lim) {return true}
    else {return false}
  }
}

/* create experiment definition array */
var digit_span_experiment = [];
digit_span_experiment.push(welcome_block);
digit_span_experiment.push(instruction_node);
digit_span_experiment.push(forward_node)
digit_span_experiment.push(attention_node)
digit_span_experiment.push(start_reverse_block)
digit_span_experiment.push(reverse_node)
digit_span_experiment.push(end_block)
<|endoftext|>