<commit_before>"use strict";

const setupTask = require('utils').setupTask;
const calcTasks = require("calcTasks");

module.exports = {

    getGettingTask : function(creep){
        //here the prioritization of one source is missing
		var room = creep.room
		var creepsByTask = _(Game.creeps).filter( (c) => c.task && c.task.roomName == room.name).groupBy('task.type').value();
        var getEnergyTasks = calcTasks.calcGetEnergyTasks(room,creepsByTask);
        var myIndex = -1;
        myIndex = _.findIndex(getEnergyTasks, (t) => t.amountAvailable == Game.getObjectById(t.id).storeCapacity);
        if(myIndex != -1){
            creep.task=getEnergyTasks[myIndex];
            return OK;
        }
        var pickupTasks = calcTasks.calcPickupTasks(room,creepsByTask);
        myIndex = _.findIndex(pickupTasks,(t) => t.amountLeft > 50); //here a better check than > 50 would be nice
        if(myIndex != -1){
            creep.task=pickupTasks[myIndex];
            return OK;
        }
        myIndex = _.findIndex(getEnergyTasks,(t) => t.amountAvailable >= (creep.carryCapacity - creep.carry.energy));
        if(myIndex != -1){
            creep.task=getEnergyTasks[myIndex];
            return OK;
        }
		if(!creep.room.storage && creep.role == ROLE_RUNNER){
			myIndex = _.findIndex(getEnergyTasks,(t) => t.amountAvailable > 0);
	        if(myIndex != -1){
	            creep.task=getEnergyTasks[myIndex];
            	return OK;
			}
		}
        //this should work now that we do creep-based tasks, but should reset when empty
        if(creep.room.storage && creep.room.storage.store.energy >= (creep.carryCapacity - creep.carry.energy)){
            creep.task= setupTask(TASK_GET_ENERGY,creep.room.storage);
        }
    },

    getDeliveringTask : function(creep){
        //need some prioritizing logic here, for now only spawns and extensions are accounted for
        //does not account for getting the closest first yet
		var room = creep.room
		var creepsByTask = _(Game.creeps).filter( (c) => c.task && c.task.roomName == room.name).groupBy('task.type').value();
        var deliverList = calcTasks.calcFillTasks(room,creepsByTask);
        var myIndex = _.findIndex(deliverList, (t) => t.amountNeeded > 0);
        if(myIndex != -1){
            creep.task=deliverList[myIndex];
            return OK;
        }
		else if(creep.room.storage) {
			creep.task=setupTask(TASK_FILL,creep.room.storage);
			return OK;
		}
    },

	run : function(creep){
		if(creep.memory.delivering && creep.carry.energy == 0) {
            creep.memory.delivering = false;
			creep.task = undefined;
            creep.say('getting');
	    }
	    if(!creep.memory.delivering && creep.carry.energy == creep.carryCapacity) {
	        creep.memory.delivering = true;
            creep.task = undefined;
	        creep.say('delivering');
		}

        if(!creep.task){
            if(creep.memory.delivering){
                this.getDeliveringTask(creep);
            }
            else{
                if(creep.ticksToLive < 50){
                    creep.role = ROLE_RECYCLER;
                }
                else{
                    this.getGettingTask(creep);
                }
            }
        }
	}
}
<commit_msg>runners should now fill closest first<commit_after>"use strict";

const setupTask = require('utils').setupTask;
const calcTasks = require("calcTasks");

module.exports = {

    getGettingTask : function(creep){
        //here the prioritization of one source is missing
		var room = creep.room
		var creepsByTask = _(Game.creeps).filter( (c) => c.task && c.task.roomName == room.name).groupBy('task.type').value();
        var getEnergyTasks = calcTasks.calcGetEnergyTasks(room,creepsByTask);
        var myIndex = -1;
        myIndex = _.findIndex(getEnergyTasks, (t) => t.amountAvailable == Game.getObjectById(t.id).storeCapacity);
        if(myIndex != -1){
            creep.task=getEnergyTasks[myIndex];
            return OK;
        }
        var pickupTasks = calcTasks.calcPickupTasks(room,creepsByTask);
        myIndex = _.findIndex(pickupTasks,(t) => t.amountLeft > 50); //here a better check than > 50 would be nice
        if(myIndex != -1){
            creep.task=pickupTasks[myIndex];
            return OK;
        }
        myIndex = _.findIndex(getEnergyTasks,(t) => t.amountAvailable >= (creep.carryCapacity - creep.carry.energy));
        if(myIndex != -1){
            creep.task=getEnergyTasks[myIndex];
            return OK;
        }
		if(!creep.room.storage && creep.role == ROLE_RUNNER){
			myIndex = _.findIndex(getEnergyTasks,(t) => t.amountAvailable > 0);
	        if(myIndex != -1){
	            creep.task=getEnergyTasks[myIndex];
            	return OK;
			}
		}
        //this should work now that we do creep-based tasks, but should reset when empty
        if(creep.room.storage && creep.room.storage.store.energy >= (creep.carryCapacity - creep.carry.energy)){
            creep.task= setupTask(TASK_GET_ENERGY,creep.room.storage);
        }
    },

    getDeliveringTask : function(creep){
        //need some prioritizing logic here, for now only spawns and extensions are accounted for
        //does not account for getting the closest first yet
		var room = creep.room
		var creepsByTask = _(Game.creeps).filter( (c) => c.task && c.task.roomName == room.name).groupBy('task.type').value();
        var deliverList = calcTasks.calcFillTasks(room,creepsByTask);
        deliverList = _.sortBy(deliverList, (x) => creep.pos.getRangeTo(x)); //so they deliver to closest first
        var myIndex = _.findIndex(deliverList, (t) => t.amountNeeded > 0);
        if(myIndex != -1){
            creep.task=deliverList[myIndex];
            return OK;
        }
		else if(creep.room.storage) {
			creep.task=setupTask(TASK_FILL,creep.room.storage);
			return OK;
		}
    },

	run : function(creep){
		if(creep.memory.delivering && creep.carry.energy == 0) {
            creep.memory.delivering = false;
			creep.task = undefined;
            creep.say('getting');
	    }
	    if(!creep.memory.delivering && creep.carry.energy == creep.carryCapacity) {
	        creep.memory.delivering = true;
            creep.task = undefined;
	        creep.say('delivering');
		}

        if(!creep.task){
            if(creep.memory.delivering){
                this.getDeliveringTask(creep);
            }
            else{
                if(creep.ticksToLive < 50){
                    creep.role = ROLE_RECYCLER;
                }
                else{
                    this.getGettingTask(creep);
                }
            }
        }
	}
}
<|endoftext|>


<commit_before>function c_a(a, b) {
    "use strict";
    var c,
        d,
        l_0,
        l_1,
        obj = {"difference": [], "same_elements": []};

        function trim(a) {
            var result;
            function p(v) {
                if (!!v.toString().match(/\(\*\)/g)) {
                    return false;
                }
                return true;
            }
            result = a.filter(p);
            return result;
        }

        function splice_it(k, l, m, n) {
            var i = 0,
                j,
                buffer = [];

            do {
                j = 0;
                do {
                    if (l[j] === k[i]) {
                        buffer.push(l[j]);
                        l.splice(j, 1, l[j] + " (*)");
                        k.splice(i, 1, l[i] + " (*)");
                    }
                    j++;
                } while (j < n);
                i++;
            } while (i < m);

            return {"difference": trim(k).concat(trim(l)), "same_elements": buffer};
        }

    if (typeof(a) === "object" && typeof(b) === "object") {
        c = a.slice();
        d = b.slice();
        l_0 = c.length;
        l_1 = d.length;

        if (c.length >= d.length) {
            obj = splice_it(c, d, l_0, l_1);
        } else {
            obj = splice_it(d, c, l_1, l_0);
        }
    }
    return obj;
}
<commit_msg>Line 42 and 48<commit_after>function c_a(a, b) {
    "use strict";
    var c,
        d,
        l_0,
        l_1,
        obj = {"difference": [], "same_elements": []};

        function trim(a) {
            var result;
            function p(v) {
                if (!!v.toString().match(/\(\*\)/g)) {
                    return false;
                }
                return true;
            }
            result = a.filter(p);
            return result;
        }

        function splice_it(k, l, m, n) {
            var i = 0,
                j,
                buffer = [];

            do {
                j = 0;
                do {
                    if (l[j] === k[i]) {
                        buffer.push(l[j]);
                        l.splice(j, 1, l[j] + " (*)");
                        k.splice(i, 1, l[i] + " (*)");
                    }
                    j++;
                } while (j < n);
                i++;
            } while (i < m);

            return {"difference": trim(k).concat(trim(l)), "same_elements": buffer};
        }

    if (Array.isArray(a) && Array.isArray(b)) {
        c = a.slice();
        d = b.slice();
        l_0 = c.length;
        l_1 = d.length;

        if (l_0 >= l_1) {
            obj = splice_it(c, d, l_0, l_1);
        } else {
            obj = splice_it(d, c, l_1, l_0);
        }
    }
    return obj;
}
<|endoftext|>


<commit_before>/*
 * net-dolisten-test.js: Basic tests for the lib/net.js module
 *
 * (C) 2011 stolsma
 * MIT LICENCE
 *
 */

var assert = require('assert'),
    path = require('path'),
    spawn = require('child_process').spawn,
    vows = require('vows'),
    helper = require('../helper/macros.js'),
    carapace = require('../../lib/carapace');

var script = path.join(__dirname, '..', 'fixtures' ,'multi-server.js'),
    IOPORT = 5060,
    testPort = 8000,
    argv = ['--hook-port', IOPORT, '--hook-name', 'carapace', script];

vows.describe('carapace/net/dolisten').addBatch({
  "When using haibu-carapace": helper.assertListen(IOPORT, {
    "spawning the server-dolisten.js script the child carapace": {
      topic: function () {
        var that = this,
            child,
            result = {
              events: [],
              exitCode: -1
            };
        child = spawn(carapace.bin, argv);

        child.stdout.on('data', function (data) {
          process.stdout.write(data);
        });

        child.stderr.on('data', function (data) {
          process.stdout.write(data);
        });

        child.on('exit', function (code) {
          result.exitCode = code;
          // process all events before asserting
          process.nextTick(function () {
            that.callback(null, result, child);
          });
        });

        carapace.on('*::carapace::port', function onPort (info) {
          result.events.push({
            event: this.event,
            info: info
          });
        });
      },
      "should exit": {
        topic: function (info, child) {
          this.callback(null, info, child);
        },
        "with the correct exit code": function (_, info, child) {
          assert.equal(info.exitCode, 0);
        },
        "and 3x emit the `*::carapace::port` event with the correct port": function (_, info, child) {
          var desired = testPort,
              basePort = desired;
          assert.equal(info.events.length, 3);
          info.events.forEach(function (event) {
            assert.equal(event.info.port, basePort++);
            assert.equal(event.info.desired, desired);
          });
        }
      }
    }
  })
}).export(module);
<commit_msg>[test] Use the `fork` API in `net-multiple-servers-test`<commit_after>/*
 * net-dolisten-test.js: Basic tests for the lib/net.js module
 *
 * (C) 2011 stolsma
 * MIT LICENCE
 *
 */

var assert = require('assert'),
    path = require('path'),
    fork = require('child_process').fork,
    vows = require('vows'),
    helper = require('../helper/macros.js'),
    carapace = require('../../lib/carapace');

var script = path.join(__dirname, '..', 'fixtures' ,'multi-server.js'),
    testPort = 8000,
    argv = [script];

vows.describe('carapace/net/dolisten').addBatch({
  "When using haibu-carapace": {
    "spawning the server-dolisten.js script the child carapace": {
      topic: function () {
        var that = this,
            child,
            result = {
              events: [],
              exitCode: -1
            };
        child = fork(carapace.bin, argv);

        child.on('exit', function (code) {
          result.exitCode = code;
          // process all events before asserting
          process.nextTick(function () {
            that.callback(null, result, child);
          });
        });

        child.on('message', function onPort (info) {
          info.event == 'port' && result.events.push({
            event: info.event,
            info: info.data
          });
        });
      },
      "should exit": {
        topic: function (info, child) {
          this.callback(null, info, child);
        },
        "with the correct exit code": function (_, info, child) {
          assert.equal(info.exitCode, 0);
        },
        "and 3x emit the `port` event with the correct port": function (_, info, child) {
          var desired = testPort,
              basePort = desired;
          assert.equal(info.events.length, 3);
          info.events.forEach(function (event) {
            assert.equal(event.info.port, basePort++);
            assert.equal(event.info.desired, desired);
          });
        }
      }
    }
  }
}).export(module);
<|endoftext|>


<commit_before>const fs = require('fs');

const original = require('../lib/rules-wrapper');

const texts = {};

var total = 0,
    unique = 0,
    analogous = 0,
    instances = 0,
    redundant = 0,
    suspects = [];

const normaliseCopy = (c) => {
    return c.toLowerCase().replace(/[\s,\.]/g, '');
};

for (let p in original)
    for (let s in original[p].sections)
        for (let r in original[p].sections[s].rules) {
            if (texts.hasOwnProperty(original[p].sections[s].rules[r]))
                texts[original[p].sections[s].rules[r]].push({p: p, s: s, sectionName: original[p].sections[s].name, r: r});
            else
                texts[original[p].sections[s].rules[r]] = [{p: p, s: s, sectionName: original[p].sections[s].name, r: r}];
            total++;
        }

for (let t in texts)
    if (1 === texts[t].length)
        unique++;
    else {
        const equalToFirst = (e) => (e.s === texts[t][0].s && e.sectionName === texts[t][0].sectionName && e.r === texts[t][0].r);
        if (texts[t].every(equalToFirst)) {
            instances += texts[t].length;
            analogous++;
        } else
            redundant++;
    }

console.log(`Analogous: ${analogous} (${instances} instances); redundant: ${redundant}; unique: ${unique}. Total: ${total}`);

const compact = JSON.parse(JSON.stringify(original));

if (analogous > 0)
    compact['*'] = {sections: {}};

for (let t in texts)
    if (texts[t].length > 1) {
        const equalToFirst = (e) => (e.s === texts[t][0].s && e.sectionName === texts[t][0].sectionName && e.r === texts[t][0].r);
        if (texts[t].every(equalToFirst)) {
            const sample = texts[t][0];
            if (compact['*'].sections.hasOwnProperty(sample.s))
                compact['*'].sections[sample.s].rules[sample.r] = t;
            else {
                const rules = {};
                rules[sample.r] = t;
                compact['*'].sections[sample.s] = {name: sample.sectionName, rules: rules};
            }
            for (let item in texts[t]) {
                const e = texts[t][item];
                // delete compact[e.p].sections[e.s].rules[e.r];
                compact[e.p].sections[e.s].rules[e.r] = true;
                if (Object.keys(compact[e.p].sections[e.s].rules).length < 1) {
                    // delete compact[e.p].sections[e.s];
                    compact[e.p].sections[e.s] = true;
                    if (Object.keys(compact[e.p].sections).length < 1)
                        delete compact[e.p];
                }

            }
        }
    } else
        suspects.push(normaliseCopy(t));

suspects = suspects.sort();

for (let s in suspects)
    console.log(suspects[s].substr(0, 200));

fs.writeFile('../lib/rules.json', JSON.stringify(compact, null, 4));
<commit_msg>Refactoring script, removed<commit_after><|endoftext|>


<commit_before>export * from './inputs';
export { default as SelectField } from './SelectField';
export { default as TextareaField } from './TextareaField';
export { default as Field } from './field';
export { default as formContext } from './formContext';
<commit_msg>renames field to Field<commit_after>export * from './inputs';
export { default as SelectField } from './SelectField';
export { default as TextareaField } from './TextareaField';
export { default as Field } from './Field';
export { default as formContext } from './formContext';
<|endoftext|>


<commit_before>const DocumentDBClient = require('documentdb').DocumentClient;
const config = {
    DatabaseId: "mean-dev",
    CollectionId: "users",
    Host: process.env.DocDb_Host,
    AuthKey: process.env.DocDb_AuthKey,
};

config.CollLink = 'dbs/' + config.DatabaseId + '/colls/' + config.CollectionId

module.exports = function (context, message) {

    let err = null;

    const docDbClient = new DocumentDBClient(config.Host, { masterKey: config.AuthKey });
    const query = 'SELECT * FROM c WHERE (c.twitter[\'$id\'] = \'' + message.userid + '\' AND \'twitter\' = \'' + message.platform + '\') OR (c.instagram[\'$id\'] = \'' + message.userid + '\' AND \'instagram\' = \'' + message.platform + '\') OR (c.facebook[\'$id\'] = \'' + message.userid + '\' AND \'facebook\' = \'' + message.platform + '\')';

    docDbClient.queryDocuments(config.CollLink, query).toArray(function (err, results) {

        let userdata = results[0];

        if (!userdata || userdata == undefined) {
            context.log('Not tracking user');

            err = new Error('Not tracking user');
            return;
        }

        if ((!userdata.twitter || userdata.twitter == undefined)
            && (!userdata.facebook || userdata.facebook == undefined)
            || (!userdata.instagram || userdata.instagram == undefined)) {
            context.log('No social profiles');

            err = new Error('No social profiles');
            return;
        }

        if (!userdata.id) {
            context.log('No user id');

            err = new Error('No social profiles');
            return;
        }

        let data = new Object();

        data.userid = userdata.id;
        data.platform = message.platform;
        data.mediaid = message.mediaid;

        if (userdata.twitter) {
            context.log('adding twitter');

            data.twitter = {
                id: userdata.twitter.$id,
                token: userdata.twitter.token,
                username: userdata.twitter.username,
            };
        }

        if (userdata.instagram) {
            context.log('adding instagram');

            data.instagram = {
                id: userdata.instagram.$id,
                token: userdata.instagram.token,
                username: userdata.instagram.username,
            };
        }

        if (userdata.facebook) {
            context.log('adding facebook');

            data.facebook = {
                id: userdata.facebook.$id,
                token: userdata.facebook.token,
                username: userdata.facebook.email,
            };
        }

        context.done(err, data);
    });
};<commit_msg>bind to out<commit_after>const DocumentDBClient = require('documentdb').DocumentClient;
const config = {
    DatabaseId: "mean-dev",
    CollectionId: "users",
    Host: process.env.DocDb_Host,
    AuthKey: process.env.DocDb_AuthKey,
};

config.CollLink = 'dbs/' + config.DatabaseId + '/colls/' + config.CollectionId

module.exports = function (context, message) {

    let err = null;

    const docDbClient = new DocumentDBClient(config.Host, { masterKey: config.AuthKey });
    const query = 'SELECT * FROM c WHERE (c.twitter[\'$id\'] = \'' + message.userid + '\' AND \'twitter\' = \'' + message.platform + '\') OR (c.instagram[\'$id\'] = \'' + message.userid + '\' AND \'instagram\' = \'' + message.platform + '\') OR (c.facebook[\'$id\'] = \'' + message.userid + '\' AND \'facebook\' = \'' + message.platform + '\')';

    docDbClient.queryDocuments(config.CollLink, query).toArray(function (err, results) {

        let userdata = results[0];

        if (!userdata || userdata == undefined) {
            context.log('Not tracking user');

            err = new Error('Not tracking user');
            return;
        }

        if ((!userdata.twitter || userdata.twitter == undefined)
            && (!userdata.facebook || userdata.facebook == undefined)
            || (!userdata.instagram || userdata.instagram == undefined)) {
            context.log('No social profiles');

            err = new Error('No social profiles');
            return;
        }

        if (!userdata.id) {
            context.log('No user id');

            err = new Error('No social profiles');
            return;
        }

        let data = new Object();

        data.userid = userdata.id;
        data.platform = message.platform;
        data.mediaid = message.mediaid;

        if (userdata.twitter) {
            context.log('adding twitter');

            data.twitter = {
                id: userdata.twitter.$id,
                token: userdata.twitter.token,
                username: userdata.twitter.username,
            };
        }

        if (userdata.instagram) {
            context.log('adding instagram');

            data.instagram = {
                id: userdata.instagram.$id,
                token: userdata.instagram.token,
                username: userdata.instagram.username,
            };
        }

        if (userdata.facebook) {
            context.log('adding facebook');

            data.facebook = {
                id: userdata.facebook.$id,
                token: userdata.facebook.token,
                username: userdata.facebook.email,
            };
        }

        context.binding.out = data;

        context.done(err, data);
    });
};<|endoftext|>


<commit_before>module.exports = function (context, message) {
    context.bindings.outMessage = message;
    context.done();
};<commit_msg>comment<commit_after>module.exports = function (context, message) {
    //fliter on twitter handle
    context.bindings.outMessage = message;
    context.done();
};<|endoftext|>


<commit_before>/*
 * Browser compatibility.
 */

/*
 * Returns an object that CSS-related tags can be attached to before the DOM
 * is built. May be undefined or null if there is no such object.
 */
function _css_parent() {
    return document.head || document.documentElement || null;
}

/*
 * Trigger called when _css_parent() is available. May defer until with_dom().
 */
function with_css_parent(callback) {
    if(_css_parent()) {
        callback(_css_parent());
    } else {
        with_dom(function() {
            callback(_css_parent());
        });
    }
}

/*
 * Appends a <style> tag for the given CSS.
 */
function add_css(css) {
    if(css) {
        var tag = style_tag(css);
        _css_parent().insertBefore(tag, _css_parent().firstChild);
    }
}

/*
 * Adds a CSS resource to the page.
 */
function link_css(filename) {
    make_css_link(filename, function(tag) {
        var parent = _css_parent();
        parent.insertBefore(tag, parent.firstChild);
    });
}

/*
 * Sends a set_pref message to the backend. Don't do this too often, as
 * some browsers incur a significant overhead for each call.
 */
var set_pref = function(key, value) {
    log_debug("Writing preference:", key, "=", value);
    _send_message("set_pref", {"pref": key, "value": value});
};

var _request_initdata = function(want) {
    _send_message("get_initdata", {"want": want});
};

var _initdata_want = null;
var _initdata_hook = null;
var setup_browser = function(want, callback) {
    _checkpoint("startup");
    _initdata_want = want;
    _initdata_hook = callback;
    _request_initdata(want);
};

function _complete_setup(initdata) {
    _checkpoint("ready");
    var store = new Store();

    if(_initdata_want.prefs) {
        if(initdata.prefs !== undefined) {
            store.setup_prefs(initdata.prefs);
        } else {
            log_error("Backend sent wrong initdata: no prefs");
            return;
        }
    }
    if(_initdata_want.customcss) {
        if(initdata.emotes !== undefined && initdata.css !== undefined) {
            store.setup_customcss(initdata.emotes, initdata.css);
        } else {
            log_error("Backend sent wrong initdata: no custom css");
            return;
        }
    }

    _initdata_hook(store);
    _initdata_want = null;
    _initdata_hook = null;
}

// Missing attributes/methods:
var _send_message = null;
var make_css_link = null;
var linkify_options = null;

switch(platform) {
case "firefox-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        self.postMessage(data);
    };

    var _data_url = function(filename) {
        // FIXME: Hardcoding this sucks. It's likely to continue working for
        // a good long while, but we should prefer make a request to the
        // backend for the prefix (not wanting to do that is the reason for
        // hardcoding it). Ideally self.data.url() would be accessible to
        // content scripts, but it's not...
        return "resource://jid1-thrhdjxskvsicw-at-jetpack/data" + filename;
    };

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(_data_url(filename));
        callback(tag);
    };

    linkify_options = function(element) {
        // Firefox doesn't permit linking to resource:// links or something
        // equivalent.
        element.addEventListener("click", catch_errors(function(event) {
            _send_message("open_options");
        }), false);
    };

    self.on("message", catch_errors(function(message) {
        switch(message.method) {
        case "initdata":
            _complete_setup(message);
            break;

        default:
            log_error("Unknown request from Firefox background script: '" + message.method + "'");
            break;
        }
    }));
    break;

case "chrome-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        chrome.extension.sendMessage(data, _message_handler);
    };

    var _message_handler = catch_errors(function(message) {
        switch(message.method) {
        case "initdata":
            _complete_setup(message);
            break;

        default:
            log_error("Unknown request from Chrome background script: '" + message.method + "'");
            break;
        }
    });

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(chrome.extension.getURL(filename));
        callback(tag);
    };

    linkify_options = function(element) {
        element.href = chrome.extension.getURL("/options.html");
        element.target = "_blank";
    };
    break;

case "safari-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        safari.self.tab.dispatchMessage(data.method, data);
    };

    // Safari does message handling kind of weirdly since it has a message argument built in.
    safari.self.addEventListener("message", catch_errors(function(message) {
        switch(message.message.method) {
            case "initdata":
                _complete_setup(message.message);
                break;

            default:
                log_error("Unknown request from Safari background script: '" + message.message.method + "'");
                break;
        }
    }), false);

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(safari.extension.baseURI + filename.substr(1));
        callback(tag);
    };

    linkify_options = function(element) {
        element.href = safari.extension.baseURI + 'options.html';
        element.target = "_blank";
    };
}
<commit_msg>Add random error message<commit_after>/*
 * Browser compatibility.
 */

/*
 * Returns an object that CSS-related tags can be attached to before the DOM
 * is built. May be undefined or null if there is no such object.
 */
function _css_parent() {
    return document.head || document.documentElement || null;
}

/*
 * Trigger called when _css_parent() is available. May defer until with_dom().
 */
function with_css_parent(callback) {
    if(_css_parent()) {
        callback(_css_parent());
    } else {
        with_dom(function() {
            callback(_css_parent());
        });
    }
}

/*
 * Appends a <style> tag for the given CSS.
 */
function add_css(css) {
    if(css) {
        var tag = style_tag(css);
        _css_parent().insertBefore(tag, _css_parent().firstChild);
    }
}

/*
 * Adds a CSS resource to the page.
 */
function link_css(filename) {
    make_css_link(filename, function(tag) {
        var parent = _css_parent();
        parent.insertBefore(tag, parent.firstChild);
    });
}

/*
 * Sends a set_pref message to the backend. Don't do this too often, as
 * some browsers incur a significant overhead for each call.
 */
var set_pref = function(key, value) {
    log_debug("Writing preference:", key, "=", value);
    _send_message("set_pref", {"pref": key, "value": value});
};

var _request_initdata = function(want) {
    _send_message("get_initdata", {"want": want});
};

var _initdata_want = null;
var _initdata_hook = null;
var setup_browser = function(want, callback) {
    _checkpoint("startup");
    _initdata_want = want;
    _initdata_hook = callback;
    _request_initdata(want);
};

function _complete_setup(initdata) {
    _checkpoint("ready");
    var store = new Store();

    if(_initdata_want.prefs) {
        if(initdata.prefs !== undefined) {
            store.setup_prefs(initdata.prefs);
        } else {
            log_error("Backend sent wrong initdata: no prefs");
            return;
        }
    }
    if(_initdata_want.customcss) {
        if(initdata.emotes !== undefined && initdata.css !== undefined) {
            store.setup_customcss(initdata.emotes, initdata.css);
        } else {
            log_error("Backend sent wrong initdata: no custom css");
            return;
        }
    }

    _initdata_hook(store);
    _initdata_want = null;
    _initdata_hook = null;
}

// Missing attributes/methods:
var _send_message = null;
var make_css_link = null;
var linkify_options = null;

switch(platform) {
case "firefox-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        self.postMessage(data);
    };

    var _data_url = function(filename) {
        // FIXME: Hardcoding this sucks. It's likely to continue working for
        // a good long while, but we should prefer make a request to the
        // backend for the prefix (not wanting to do that is the reason for
        // hardcoding it). Ideally self.data.url() would be accessible to
        // content scripts, but it's not...
        return "resource://jid1-thrhdjxskvsicw-at-jetpack/data" + filename;
    };

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(_data_url(filename));
        callback(tag);
    };

    linkify_options = function(element) {
        // Firefox doesn't permit linking to resource:// links or something
        // equivalent.
        element.addEventListener("click", catch_errors(function(event) {
            _send_message("open_options");
        }), false);
    };

    self.on("message", catch_errors(function(message) {
        switch(message.method) {
        case "initdata":
            _complete_setup(message);
            break;

        default:
            log_error("Unknown request from Firefox background script: '" + message.method + "'");
            break;
        }
    }));
    break;

case "chrome-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        chrome.extension.sendMessage(data, _message_handler);
    };

    var _message_handler = catch_errors(function(message) {
        if(!message || !message.method) {
            log_error("Unknown request from Chrome background script: '" + message + "'");
            return;
        }

        switch(message.method) {
        case "initdata":
            _complete_setup(message);
            break;

        default:
            log_error("Unknown request from Chrome background script: '" + message.method + "'");
            break;
        }
    });

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(chrome.extension.getURL(filename));
        callback(tag);
    };

    linkify_options = function(element) {
        element.href = chrome.extension.getURL("/options.html");
        element.target = "_blank";
    };
    break;

case "safari-ext":
    _send_message = function(method, data) {
        if(data === undefined) {
            data = {};
        }
        data.method = method;
        log_debug("_send_message:", data);
        safari.self.tab.dispatchMessage(data.method, data);
    };

    // Safari does message handling kind of weirdly since it has a message argument built in.
    safari.self.addEventListener("message", catch_errors(function(message) {
        switch(message.message.method) {
            case "initdata":
                _complete_setup(message.message);
                break;

            default:
                log_error("Unknown request from Safari background script: '" + message.message.method + "'");
                break;
        }
    }), false);

    make_css_link = function(filename, callback) {
        var tag = stylesheet_link(safari.extension.baseURI + filename.substr(1));
        callback(tag);
    };

    linkify_options = function(element) {
        element.href = safari.extension.baseURI + 'options.html';
        element.target = "_blank";
    };
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';
import ModelDefinition from './model-definition';
import FixtureBuilderFactory from './builder/fixture-builder-factory';

const assign = Ember.assign || Ember.merge;
let modelDefinitions = {};

/**
 Given a fixture name like 'person' or 'dude' determine what model this name
 refers to. In this case it's 'person' for each one.

 @param {String} name  a fixture name could be model name like 'person'
 or a named person in model definition like 'dude'
 @returns {String} model  name associated with fixture name or undefined if not found
 */
let lookupModelForFixtureName = function(name) {
  let definition = lookupDefinitionForFixtureName(name);
  if (definition) {
    return definition.modelName;
  }
};

/**

 @param {String} name a fixture name could be model name like 'person'
 or a named person in model definition like 'dude'
 @returns {ModelDefinition} ModelDefinition associated with model or undefined if not found
 */
let lookupDefinitionForFixtureName = function(name) {
  for (let model in modelDefinitions) {
    let definition = modelDefinitions[model];
    if (definition.matchesName(name)) {
      return definition;
    }
  }
};

let extractArgumentsShort = function(...args) {
  let opts = {};
  if (Ember.typeOf(args[args.length - 1]) === 'object') {
    opts = args.pop();
  }
  // whatever is left are traits
  let traits = Ember.A(args).compact();
  return { opts: opts, traits: traits };
};

/**
 extract arguments for build and make function
 @param {String} name  fixture name
 @param {String} trait  optional trait names ( one or more )
 @param {Object} opts  optional fixture options that will override default fixture values
 @returns {Object} json fixture
 */
let extractArguments = function(...args) {
  let name = args.shift();
  if (!name) {
    throw new Error('Build needs a factory name to build');
  }
  return assign({ name: name }, extractArgumentsShort.apply(this, args));
};

class FactoryGuy {
  /**
   * Setting for FactoryGuy. For now, just logging settings
   *
   * logLevel: 0 is off, 1 is on
   *
   * @param opts
   */
  settings({ logLevel = 0 } = {}) {
    this.logLevel = logLevel;
  }

  setStore(aStore) {
    Ember.assert("FactoryGuy#setStore needs a valid store instance.You passed in [" + aStore + "]", aStore instanceof DS.Store);
    this.store = aStore;
    this.fixtureBuilderFactory = new FixtureBuilderFactory(this.store);
  }

  fixtureBuilder(modelName) {
    return this.fixtureBuilderFactory.fixtureBuilder(modelName);
  }

  updateHTTPMethod(modelName) {
    return this.fixtureBuilder(modelName).updateHTTPMethod || 'PUT';
  }

  /**
   ```javascript

   Person = DS.Model.extend({
     type: DS.attr('string'),
     name: DS.attr('string')
   })

   FactoryGuy.define('person', {
     sequences: {
       personName: function(num) {
         return 'person #' + num;
       },
       personType: function(num) {
         return 'person type #' + num;
       }
     },
     default: {
       type: 'normal',
       name: FactoryGuy.generate('personName')
     },
     dude: {
       type: FactoryGuy.generate('personType')
     },
   });

   ```

   For the Person model, you can define named fixtures like 'dude' or
   just use 'person' and get default values.

   And to get those fixtures you would call them this way:

   FactoryGuy.build('dude') or FactoryGuy.build('person')

   @param {String} model the model to define
   @param {Object} config your model definition
   */
  define(model, config) {
    modelDefinitions[model] = new ModelDefinition(model, config);
  }

  /*
   @param model name of named fixture type like: 'admin' or model name like 'user'
   @returns {ModelDefinition} if there is one matching that name
   */
  findModelDefinition(model) {
    return modelDefinitions[model];
  }

  /**
   Used in model definitions to declare use of a sequence. For example:

   ```

   FactoryGuy.define('person', {
     sequences: {
       personName: function(num) {
         return 'person #' + num;
       }
     },
     default: {
       name: FactoryGuy.generate('personName')
     }
   });

   ```

   @param   {String|Function} value previously declared sequence name or
   an inline function to use as the sequence
   @returns {Function} wrapper function that is called by the model
   definition containing the sequence
   */
  generate(nameOrFunction) {
    let sortaRandomName = Math.floor((1 + Math.random()) * 65536).toString(16) + Date.now();
    return function() {
      // this function will be called by ModelDefinition, which has it's own generate method
      if (Ember.typeOf(nameOrFunction) === 'function') {
        return this.generate(sortaRandomName, nameOrFunction);
      } else {
        return this.generate(nameOrFunction);
      }
    };
  }

  /**
   Used in model definitions to define a belongsTo association attribute.
   For example:

   ```
   FactoryGuy.define('project', {
       default: {
         title: 'Project'
       },

       // setup named project with built in associated user
       project_with_admin: {
         user: FactoryGuy.belongsTo('admin')
       }

       // or use as a trait
       traits: {
         with_admin: {
           user: FactoryGuy.belongsTo('admin')
         }
       }
     })
   ```

   @param   {String} fixtureName fixture name
   @param   {Object} opts options
   @returns {Function} wrapper function that will build the association json
   */
  belongsTo(fixtureName, opts) {
    return ()=> {
      return this.buildRaw(fixtureName, opts);
    };
  }

  /**
   Used in model definitions to define a hasMany association attribute.
   For example:

   ```
   FactoryGuy.define('user', {
     default: {
       name: 'Bob'
     },

     // define the named user type that will have projects
     user_with_projects: { FactoryGuy.hasMany('project', 2) }

     // or use as a trait
     traits: {
       with_projects: {
         projects: FactoryGuy.hasMany('project', 2)
       }
     }
   })

   ```

   @param   {String} fixtureName fixture name
   @param   {Number} number of hasMany association items to build
   @param   {Object} opts options
   @returns {Function} wrapper function that will build the association json
   */
  hasMany(...args) {
    return ()=> {
      return this.buildRawList.apply(this, args);
    };
  }

  /**
   Build fixtures for model or specific fixture name.

   For example:

   ```

   FactoryGuy.build('user') for User model
   FactoryGuy.build('bob') for a 'bob' User
   FactoryGuy.build('bob', 'dude') for a 'bob' User with dude traits
   FactoryGuy.build('bob', 'dude', 'funny') for a 'bob' User with dude and funny traits
   FactoryGuy.build('bob', 'dude', {name: 'wombat'}) for a 'bob' User with dude trait and custom attribute name of 'wombat'

   ```

   @param {String} name  fixture name
   @param {String} trait  optional trait names ( one or more )
   @param {Object} opts  optional fixture options that will override default fixture values
   @returns {Object} json fixture
   */
  build() {
    let args = extractArguments.apply(this, arguments);
    let fixture = this.buildRaw.apply(this, arguments);
    let modelName = lookupModelForFixtureName(args.name);

    return this.fixtureBuilder(modelName).convertForBuild(modelName, fixture);
  }

  buildRaw() {
    let args = extractArguments.apply(this, arguments);

    let definition = lookupDefinitionForFixtureName(args.name);
    if (!definition) {
      throw new Error('Can\'t find that factory named [' + args.name + ']');
    }

    return definition.build(args.name, args.opts, args.traits);
  }

  /**
   Build list of fixtures for model or specific fixture name. For example:

   ```

   FactoryGuy.buildList('user') // for 0 User models
   FactoryGuy.buildList('user', 2) // for 2 User models
   FactoryGuy.build('bob', 2) // for 2 User model with bob attributes
   FactoryGuy.build('bob', 'with_car', ['with_car',{name: "Dude"}])
   // 2 User model with bob attributes, where the first also has 'with_car' trait
   // the last has 'with_car' trait and name of "Dude"

   ```

   @param {String} name  fixture name
   @param {Number} number  number of fixtures to create
   @param {String} trait  optional traits (one or more)
   @param {Object} opts  optional fixture options that will override default fixture values
   @returns {Array} list of fixtures
   */
  buildList(...args) {
    Ember.assert("buildList needs at least a name ( of model or named factory definition )", args.length > 0);

    let list = this.buildRawList.apply(this, arguments);

    let name = args.shift();
    let modelName = lookupModelForFixtureName(name);

    return this.fixtureBuilder(modelName).convertForBuild(modelName, list);
  }

  buildRawList(...args) {
    let name = args.shift();
    let definition = lookupDefinitionForFixtureName(name);
    if (!definition) {
      throw new Error("Can't find that factory named [" + name + "]");
    }
    let number = args[0] || 0;
    if (typeof number === 'number') {
      args.shift();
      let parts = extractArgumentsShort.apply(this, args);
      return definition.buildList(name, number, parts.traits, parts.opts);
    }
    else {
      return args.map(function(innerArgs) {
        if (Ember.typeOf(innerArgs) !== 'array') {
          innerArgs = [innerArgs];
        }
        let parts = extractArgumentsShort.apply(this, innerArgs);
        return definition.build(name, parts.opts, parts.traits);
      });
    }
  }

  /**
   Make new fixture and save to store.

   @param {String} name  fixture name
   @param {String} trait  optional trait names ( one or more )
   @param {Object} options  optional fixture options that will override default fixture values
   @returns {DS.Model} record
   */
  make() {
    let args = extractArguments.apply(this, arguments);

    Ember.assert(
      `FactoryGuy does not have the application's store.
       Use manualSetup(this.container) in model/component test
       before using make/makeList`, this.store
    );

    let modelName = lookupModelForFixtureName(args.name);
    let fixture = this.buildRaw.apply(this, arguments);
    let data = this.fixtureBuilder(modelName).convertForMake(modelName, fixture);

    const model = Ember.run(()=> this.store.push(data));

    let definition = lookupDefinitionForFixtureName(args.name);
    if (definition.hasAfterMake()) {
      definition.applyAfterMake(model, args.opts);
    }
    return model;
  }

  /**
   Make a list of model instances

   ```
   FactoryGuy.makeList('bob') // makes 0 bob's

   FactoryGuy.makeList('bob', 2) // makes 2 bob's

   FactoryGuy.makeList('bob', 2, 'with_car' , {name: "Dude"})
   // makes 2 bob's that have 'with_car' trait and name of "Dude"

   FactoryGuy.makeList('bob', 'with_car', ['with_car',{name: "Dude"}])
   // 2 User model with bob attributes, where the first also has 'with_car' trait
   // the last has 'with_car' trait and name of "Dude"
   ```

   @param {String} name name of fixture
   @param {Number} number number to create
   @param {String} trait  optional trait names ( one or more )
   @param {Object} options  optional fixture options that will override default fixture values
   @returns {Array} list of json fixtures or records depending on the adapter type
   */
  makeList(...args) {
    Ember.assert(
      `FactoryGuy does not have the application's store.
       Use manualSetup(this.container) in model/component test
       before using make/makeList`, this.store
    );

    Ember.assert("makeList needs at least a name ( of model or named factory definition )", args.length >= 1);

    let name = args.shift();
    let definition = lookupDefinitionForFixtureName(name);
    Ember.assert("Can't find that factory named [" + name + "]", !!definition);

    let number = args[0] || 0;
    if (typeof number === 'number') {
      args.shift();
      let arr = [];
      for (let i = 0; i < number; i++) {
        arr.push(this.make.apply(this, [name, ...args]));
      }
      return arr;
    }

    return args.map((innerArgs)=> {
      if (Ember.typeOf(innerArgs) !== 'array') {
        innerArgs = [innerArgs];
      }
      return this.make(...[name, ...innerArgs]);
    });
  }

  /**
   Clear model instances from store cache.
   Reset the id sequence for the models back to zero.
   */
  clearStore() {
    this.resetDefinitions();
    this.clearModels();
  }

  /**
   Reset the id sequence for the models back to zero.
   */
  resetDefinitions() {
    for (let model in modelDefinitions) {
      let definition = modelDefinitions[model];
      definition.reset();
    }
  }

  /**
   Clear model instances from store cache.
   */
  clearModels() {
    this.store.unloadAll();
  }

  /**
   Push fixture to model's FIXTURES array.
   Used when store's adapter is a DS.FixtureAdapter.

   @param {DS.Model} modelClass
   @param {Object} fixture the fixture to add
   @returns {Object} json fixture data
   */
  pushFixture(modelClass, fixture) {
    let index;
    if (!modelClass.FIXTURES) {
      modelClass.FIXTURES = [];
    }

    index = this.indexOfFixture(modelClass.FIXTURES, fixture);

    if (index > -1) {
      modelClass.FIXTURES.splice(index, 1);
    }

    modelClass.FIXTURES.push(fixture);

    return fixture;
  }

  /**
   Used in compliment with pushFixture in order to
   ensure we don't push duplicate fixtures

   @private
   @param {Array} fixtures
   @param {String|Integer} id of fixture to find
   @returns {Object} fixture
   */
  indexOfFixture(fixtures, fixture) {
    let index = -1,
        id    = fixture.id + '';
    Ember.A(fixtures).find(function(r, i) {
      if ('' + Ember.get(r, 'id') === id) {
        index = i;
        return true;
      } else {
        return false;
      }
    });
    return index;
  }

  /**
   Clears all model definitions
   */
  clearDefinitions(opts) {
    if (!opts) {
      this.modelDefinitions = {};
    }
  }

  /**
   Build url's for the mockjax calls. Proxy to the adapters buildURL method.

   @param {String} typeName model type name like 'user' for User model
   @param {String} id
   @return {String} url
   */
  buildURL(modelName, id = null, requestType) {
    const adapter = this.store.adapterFor(modelName);
    return adapter.buildURL(modelName, id, null, requestType);
  }

  /**
   Change reload behavior to only used cached models for find/findAll.
   You still have to handle query calls, since they always ajax for data.

   @params {Array} except list of models you don't want to mark as cached
   */
  cacheOnlyMode({ except=[] }={}) {
    let store = this.store;
    let findAdapter = store.adapterFor.bind(store);

    store.adapterFor = function(name) {
      let adapter = findAdapter(name);
      let shouldCache = ()=> {
        if (Ember.isPresent(except)) {
          return (Ember.A(except).includes(name));
        }
        return false;
      };
      adapter.shouldBackgroundReloadAll = shouldCache;
      adapter.shouldBackgroundReloadRecord = shouldCache;
      adapter.shouldReloadRecord = shouldCache;
      adapter.shouldReloadAll = shouldCache;
      return adapter;
    };
  }
}

let factoryGuy = new FactoryGuy();

let make = factoryGuy.make.bind(factoryGuy);
let makeList = factoryGuy.makeList.bind(factoryGuy);
let build = factoryGuy.build.bind(factoryGuy);
let buildList = factoryGuy.buildList.bind(factoryGuy);
let clearStore = factoryGuy.clearStore.bind(factoryGuy);

export {make, makeList, build, buildList, clearStore};
export default factoryGuy;
<commit_msg>style fix<commit_after>import Ember from 'ember';
import DS from 'ember-data';
import ModelDefinition from './model-definition';
import FixtureBuilderFactory from './builder/fixture-builder-factory';

const assign = Ember.assign || Ember.merge;
let modelDefinitions = {};

/**
 Given a fixture name like 'person' or 'dude' determine what model this name
 refers to. In this case it's 'person' for each one.

 @param {String} name  a fixture name could be model name like 'person'
 or a named person in model definition like 'dude'
 @returns {String} model  name associated with fixture name or undefined if not found
 */
let lookupModelForFixtureName = function(name) {
  let definition = lookupDefinitionForFixtureName(name);
  if (definition) {
    return definition.modelName;
  }
};

/**

 @param {String} name a fixture name could be model name like 'person'
 or a named person in model definition like 'dude'
 @returns {ModelDefinition} ModelDefinition associated with model or undefined if not found
 */
let lookupDefinitionForFixtureName = function(name) {
  for (let model in modelDefinitions) {
    let definition = modelDefinitions[model];
    if (definition.matchesName(name)) {
      return definition;
    }
  }
};

let extractArgumentsShort = function(...args) {
  let opts = {};
  if (Ember.typeOf(args[args.length - 1]) === 'object') {
    opts = args.pop();
  }
  // whatever is left are traits
  let traits = Ember.A(args).compact();
  return { opts: opts, traits: traits };
};

/**
 extract arguments for build and make function
 @param {String} name  fixture name
 @param {String} trait  optional trait names ( one or more )
 @param {Object} opts  optional fixture options that will override default fixture values
 @returns {Object} json fixture
 */
let extractArguments = function(...args) {
  let name = args.shift();
  if (!name) {
    throw new Error('Build needs a factory name to build');
  }
  return assign({ name: name }, extractArgumentsShort.apply(this, args));
};

class FactoryGuy {
  /**
   * Setting for FactoryGuy. For now, just logging settings
   *
   * logLevel: 0 is off, 1 is on
   *
   * @param opts
   */
  settings({ logLevel = 0 } = {}) {
    this.logLevel = logLevel;
  }

  setStore(aStore) {
    Ember.assert("FactoryGuy#setStore needs a valid store instance.You passed in [" + aStore + "]", aStore instanceof DS.Store);
    this.store = aStore;
    this.fixtureBuilderFactory = new FixtureBuilderFactory(this.store);
  }

  fixtureBuilder(modelName) {
    return this.fixtureBuilderFactory.fixtureBuilder(modelName);
  }

  updateHTTPMethod(modelName) {
    return this.fixtureBuilder(modelName).updateHTTPMethod || 'PUT';
  }

  /**
   ```javascript

   Person = DS.Model.extend({
     type: DS.attr('string'),
     name: DS.attr('string')
   })

   FactoryGuy.define('person', {
     sequences: {
       personName: function(num) {
         return 'person #' + num;
       },
       personType: function(num) {
         return 'person type #' + num;
       }
     },
     default: {
       type: 'normal',
       name: FactoryGuy.generate('personName')
     },
     dude: {
       type: FactoryGuy.generate('personType')
     },
   });

   ```

   For the Person model, you can define named fixtures like 'dude' or
   just use 'person' and get default values.

   And to get those fixtures you would call them this way:

   FactoryGuy.build('dude') or FactoryGuy.build('person')

   @param {String} model the model to define
   @param {Object} config your model definition
   */
  define(model, config) {
    modelDefinitions[model] = new ModelDefinition(model, config);
  }

  /*
   @param model name of named fixture type like: 'admin' or model name like 'user'
   @returns {ModelDefinition} if there is one matching that name
   */
  findModelDefinition(model) {
    return modelDefinitions[model];
  }

  /**
   Used in model definitions to declare use of a sequence. For example:

   ```

   FactoryGuy.define('person', {
     sequences: {
       personName: function(num) {
         return 'person #' + num;
       }
     },
     default: {
       name: FactoryGuy.generate('personName')
     }
   });

   ```

   @param   {String|Function} value previously declared sequence name or
   an inline function to use as the sequence
   @returns {Function} wrapper function that is called by the model
   definition containing the sequence
   */
  generate(nameOrFunction) {
    let sortaRandomName = Math.floor((1 + Math.random()) * 65536).toString(16) + Date.now();
    return function() {
      // this function will be called by ModelDefinition, which has it's own generate method
      if (Ember.typeOf(nameOrFunction) === 'function') {
        return this.generate(sortaRandomName, nameOrFunction);
      } else {
        return this.generate(nameOrFunction);
      }
    };
  }

  /**
   Used in model definitions to define a belongsTo association attribute.
   For example:

   ```
   FactoryGuy.define('project', {
       default: {
         title: 'Project'
       },

       // setup named project with built in associated user
       project_with_admin: {
         user: FactoryGuy.belongsTo('admin')
       }

       // or use as a trait
       traits: {
         with_admin: {
           user: FactoryGuy.belongsTo('admin')
         }
       }
     })
   ```

   @param   {String} fixtureName fixture name
   @param   {Object} opts options
   @returns {Function} wrapper function that will build the association json
   */
  belongsTo(fixtureName, opts) {
    return ()=> this.buildRaw(fixtureName, opts);
  }

  /**
   Used in model definitions to define a hasMany association attribute.
   For example:

   ```
   FactoryGuy.define('user', {
     default: {
       name: 'Bob'
     },

     // define the named user type that will have projects
     user_with_projects: { FactoryGuy.hasMany('project', 2) }

     // or use as a trait
     traits: {
       with_projects: {
         projects: FactoryGuy.hasMany('project', 2)
       }
     }
   })

   ```

   @param   {String} fixtureName fixture name
   @param   {Number} number of hasMany association items to build
   @param   {Object} opts options
   @returns {Function} wrapper function that will build the association json
   */
  hasMany(...args) {
    return ()=> this.buildRawList.apply(this, args);
  }

  /**
   Build fixtures for model or specific fixture name.

   For example:

   ```

   FactoryGuy.build('user') for User model
   FactoryGuy.build('bob') for a 'bob' User
   FactoryGuy.build('bob', 'dude') for a 'bob' User with dude traits
   FactoryGuy.build('bob', 'dude', 'funny') for a 'bob' User with dude and funny traits
   FactoryGuy.build('bob', 'dude', {name: 'wombat'}) for a 'bob' User with dude trait and custom attribute name of 'wombat'

   ```

   @param {String} name  fixture name
   @param {String} trait  optional trait names ( one or more )
   @param {Object} opts  optional fixture options that will override default fixture values
   @returns {Object} json fixture
   */
  build() {
    let args = extractArguments.apply(this, arguments);
    let fixture = this.buildRaw.apply(this, arguments);
    let modelName = lookupModelForFixtureName(args.name);

    return this.fixtureBuilder(modelName).convertForBuild(modelName, fixture);
  }

  buildRaw() {
    let args = extractArguments.apply(this, arguments);

    let definition = lookupDefinitionForFixtureName(args.name);
    if (!definition) {
      throw new Error('Can\'t find that factory named [' + args.name + ']');
    }

    return definition.build(args.name, args.opts, args.traits);
  }

  /**
   Build list of fixtures for model or specific fixture name. For example:

   ```

   FactoryGuy.buildList('user') // for 0 User models
   FactoryGuy.buildList('user', 2) // for 2 User models
   FactoryGuy.build('bob', 2) // for 2 User model with bob attributes
   FactoryGuy.build('bob', 'with_car', ['with_car',{name: "Dude"}])
   // 2 User model with bob attributes, where the first also has 'with_car' trait
   // the last has 'with_car' trait and name of "Dude"

   ```

   @param {String} name  fixture name
   @param {Number} number  number of fixtures to create
   @param {String} trait  optional traits (one or more)
   @param {Object} opts  optional fixture options that will override default fixture values
   @returns {Array} list of fixtures
   */
  buildList(...args) {
    Ember.assert("buildList needs at least a name ( of model or named factory definition )", args.length > 0);

    let list = this.buildRawList.apply(this, arguments);

    let name = args.shift();
    let modelName = lookupModelForFixtureName(name);

    return this.fixtureBuilder(modelName).convertForBuild(modelName, list);
  }

  buildRawList(...args) {
    let name = args.shift();
    let definition = lookupDefinitionForFixtureName(name);
    if (!definition) {
      throw new Error("Can't find that factory named [" + name + "]");
    }
    let number = args[0] || 0;
    if (typeof number === 'number') {
      args.shift();
      let parts = extractArgumentsShort.apply(this, args);
      return definition.buildList(name, number, parts.traits, parts.opts);
    }
    else {
      return args.map(function(innerArgs) {
        if (Ember.typeOf(innerArgs) !== 'array') {
          innerArgs = [innerArgs];
        }
        let parts = extractArgumentsShort.apply(this, innerArgs);
        return definition.build(name, parts.opts, parts.traits);
      });
    }
  }

  /**
   Make new fixture and save to store.

   @param {String} name  fixture name
   @param {String} trait  optional trait names ( one or more )
   @param {Object} options  optional fixture options that will override default fixture values
   @returns {DS.Model} record
   */
  make() {
    let args = extractArguments.apply(this, arguments);

    Ember.assert(
      `FactoryGuy does not have the application's store.
       Use manualSetup(this.container) in model/component test
       before using make/makeList`, this.store
    );

    let modelName = lookupModelForFixtureName(args.name);
    let fixture = this.buildRaw.apply(this, arguments);
    let data = this.fixtureBuilder(modelName).convertForMake(modelName, fixture);

    const model = Ember.run(()=> this.store.push(data));

    let definition = lookupDefinitionForFixtureName(args.name);
    if (definition.hasAfterMake()) {
      definition.applyAfterMake(model, args.opts);
    }
    return model;
  }

  /**
   Make a list of model instances

   ```
   FactoryGuy.makeList('bob') // makes 0 bob's

   FactoryGuy.makeList('bob', 2) // makes 2 bob's

   FactoryGuy.makeList('bob', 2, 'with_car' , {name: "Dude"})
   // makes 2 bob's that have 'with_car' trait and name of "Dude"

   FactoryGuy.makeList('bob', 'with_car', ['with_car',{name: "Dude"}])
   // 2 User model with bob attributes, where the first also has 'with_car' trait
   // the last has 'with_car' trait and name of "Dude"
   ```

   @param {String} name name of fixture
   @param {Number} number number to create
   @param {String} trait  optional trait names ( one or more )
   @param {Object} options  optional fixture options that will override default fixture values
   @returns {Array} list of json fixtures or records depending on the adapter type
   */
  makeList(...args) {
    Ember.assert(
      `FactoryGuy does not have the application's store.
       Use manualSetup(this.container) in model/component test
       before using make/makeList`, this.store
    );

    Ember.assert("makeList needs at least a name ( of model or named factory definition )", args.length >= 1);

    let name = args.shift();
    let definition = lookupDefinitionForFixtureName(name);
    Ember.assert("Can't find that factory named [" + name + "]", !!definition);

    let number = args[0] || 0;
    if (typeof number === 'number') {
      args.shift();
      let arr = [];
      for (let i = 0; i < number; i++) {
        arr.push(this.make.apply(this, [name, ...args]));
      }
      return arr;
    }

    return args.map((innerArgs)=> {
      if (Ember.typeOf(innerArgs) !== 'array') {
        innerArgs = [innerArgs];
      }
      return this.make(...[name, ...innerArgs]);
    });
  }

  /**
   Clear model instances from store cache.
   Reset the id sequence for the models back to zero.
   */
  clearStore() {
    this.resetDefinitions();
    this.clearModels();
  }

  /**
   Reset the id sequence for the models back to zero.
   */
  resetDefinitions() {
    for (let model in modelDefinitions) {
      let definition = modelDefinitions[model];
      definition.reset();
    }
  }

  /**
   Clear model instances from store cache.
   */
  clearModels() {
    this.store.unloadAll();
  }

  /**
   Push fixture to model's FIXTURES array.
   Used when store's adapter is a DS.FixtureAdapter.

   @param {DS.Model} modelClass
   @param {Object} fixture the fixture to add
   @returns {Object} json fixture data
   */
  pushFixture(modelClass, fixture) {
    let index;
    if (!modelClass.FIXTURES) {
      modelClass.FIXTURES = [];
    }

    index = this.indexOfFixture(modelClass.FIXTURES, fixture);

    if (index > -1) {
      modelClass.FIXTURES.splice(index, 1);
    }

    modelClass.FIXTURES.push(fixture);

    return fixture;
  }

  /**
   Used in compliment with pushFixture in order to
   ensure we don't push duplicate fixtures

   @private
   @param {Array} fixtures
   @param {String|Integer} id of fixture to find
   @returns {Object} fixture
   */
  indexOfFixture(fixtures, fixture) {
    let index = -1,
        id    = fixture.id + '';
    Ember.A(fixtures).find(function(r, i) {
      if ('' + Ember.get(r, 'id') === id) {
        index = i;
        return true;
      } else {
        return false;
      }
    });
    return index;
  }

  /**
   Clears all model definitions
   */
  clearDefinitions(opts) {
    if (!opts) {
      this.modelDefinitions = {};
    }
  }

  /**
   Build url's for the mockjax calls. Proxy to the adapters buildURL method.

   @param {String} typeName model type name like 'user' for User model
   @param {String} id
   @return {String} url
   */
  buildURL(modelName, id = null, requestType) {
    const adapter = this.store.adapterFor(modelName);
    return adapter.buildURL(modelName, id, null, requestType);
  }

  /**
   Change reload behavior to only used cached models for find/findAll.
   You still have to handle query calls, since they always ajax for data.

   @params {Array} except list of models you don't want to mark as cached
   */
  cacheOnlyMode({ except=[] }={}) {
    let store = this.store;
    let findAdapter = store.adapterFor.bind(store);

    store.adapterFor = function(name) {
      let adapter = findAdapter(name);
      let shouldCache = ()=> {
        if (Ember.isPresent(except)) {
          return (Ember.A(except).includes(name));
        }
        return false;
      };
      adapter.shouldBackgroundReloadAll = shouldCache;
      adapter.shouldBackgroundReloadRecord = shouldCache;
      adapter.shouldReloadRecord = shouldCache;
      adapter.shouldReloadAll = shouldCache;
      return adapter;
    };
  }
}

let factoryGuy = new FactoryGuy();

let make = factoryGuy.make.bind(factoryGuy);
let makeList = factoryGuy.makeList.bind(factoryGuy);
let build = factoryGuy.build.bind(factoryGuy);
let buildList = factoryGuy.buildList.bind(factoryGuy);
let clearStore = factoryGuy.clearStore.bind(factoryGuy);

export {make, makeList, build, buildList, clearStore};
export default factoryGuy;
<|endoftext|>


<commit_before>import Ember from 'ember';
import config from 'ember-get-config';
import delimit from 'ember-hook/utils/delimit';
import returnWhenTesting from 'ember-hook/utils/return-when-testing';

const {
  Mixin,
  computed,
  get
} = Ember;

const hookName = get(config, 'emberHook.hookName') || 'hook';

export default Mixin.create({
  attributeBindings: ['_hookName:data-test'],

  _hookName: computed(hookName, {
    get() {
      const hook = get(this, hookName);

      return returnWhenTesting(config, delimit(hook));
    }
  }).readOnly()
});
<commit_msg>_hookName should only return when there is a hook present<commit_after>import Ember from 'ember';
import config from 'ember-get-config';
import delimit from 'ember-hook/utils/delimit';
import returnWhenTesting from 'ember-hook/utils/return-when-testing';

const {
  Mixin,
  computed,
  get
} = Ember;

const hookName = get(config, 'emberHook.hookName') || 'hook';

export default Mixin.create({
  attributeBindings: ['_hookName:data-test'],

  _hookName: computed(hookName, {
    get() {
      const hook = get(this, hookName);

      if (hook) {
        return returnWhenTesting(config, delimit(hook));
      }
    }
  }).readOnly()
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import DS from 'ember-data';

const { computed, RSVP } = Ember;
const { attr, belongsTo, hasMany, Model } = DS;
const { all, map } = RSVP;

export default Model.extend({
  lastName: attr('string'),
  firstName: attr('string'),
  middleName: attr('string'),
  phone: attr('string'),
  email:  attr('string'),
  addedViaIlios:  attr('boolean'),
  enabled:  attr('boolean'),
  campusId:  attr('string'),
  otherId:  attr('string'),
  examined:  attr('boolean'),
  userSyncIgnore:  attr('boolean'),
  icsFeedKey:  attr('string'),
  root: attr('boolean'),
  reminders: hasMany('user-made-reminder', {async: true}),
  reports: hasMany('report', {async: true}),
  school: belongsTo('school', {async: true}),
  authentication: belongsTo('authentication', {async: true}),
  directedCourses: hasMany('course', {
    async: true,
    inverse: 'directors'
  }),
  administeredCourses: hasMany('course', {
    async: true,
    inverse: 'administrators'
  }),
  learnerGroups: hasMany('learner-group', {
    async: true,
    inverse: 'users'
  }),
  instructedLearnerGroups: hasMany('learner-group', {
    async: true,
    inverse: 'instructors'
  }),
  instructorGroups: hasMany('instructor-group', {
    async: true,
    inverse: 'users'
  }),
  instructorIlmSessions: hasMany('ilm-session', {
    async: true,
    inverse: 'instructors'
  }),
  learnerIlmSessions: hasMany('ilm-session', {
    async: true,
    inverse: 'learners'
  }),
  offerings: hasMany('offering', {
    async: true,
    inverse: 'learners'
  }),
  instructedOfferings: hasMany('offering', {
    async: true,
    inverse: 'instructors'
  }),
  programYears: hasMany('program-year', {async: true}),
  roles: hasMany('user-role', {async: true}),
  directedSchools: hasMany('school', {
    async: true,
    inverse: 'directors'
  }),
  administeredSchools: hasMany('school', {
    async: true,
    inverse: 'administrators'
  }),
  administeredSessions: hasMany('session', {
    async: true,
    inverse: 'administrators'
  }),
  directedPrograms: hasMany('program', {
    async: true,
    inverse: 'directors'
  }),

  cohorts: hasMany('cohort', {
    async: true,
    inverse: 'users'
  }),
  primaryCohort: belongsTo('cohort', {async: true}),
  pendingUserUpdates: hasMany('pending-user-update', {async: true}),
  permissions: hasMany('permission', {async: true}),

  /**
   * Resolves to TRUE if this user has the "Student" role, otherwise FALSE.
   * @property isStudent
   * @type {Ember.computed}
   * @public
   */
  isStudent: computed('roles.[]', async function() {
    const roles = await this.get('roles');
    return !!roles.toArray().findBy('title', 'Student');
  }),

  /**
   * All schools that this user is associated with directly.
   * This includes the user's school affiliation, as well as any additional schools that
   * the user has read- and/or write-permissions to.
   * @property schools
   * @type {Ember.computed}
   * @public
   */
  schools: computed('school', async function(){
    const store = this.get('store');
    const primarySchool = await this.get('school');
    const permissions = await this.get('permissions');
    const schoolIds = permissions.toArray().filter(permission => {
      return 'school' === permission.get('tableName');
    }).mapBy('tableRowId');

    const schools = await all(schoolIds.map(id => {
      return store.findRecord('school', id);
    }));

    schools.pushObject(primarySchool);
    return schools.uniq();
  }),

  fullName: computed('firstName', 'middleName', 'lastName', function() {
    const { firstName, middleName, lastName } = this.getProperties('firstName', 'middleName', 'lastName');

    if (!firstName || !lastName) {
      return '';
    }

    const middleInitial = middleName?middleName.charAt(0):false;

    if (middleInitial) {
      return `${firstName} ${middleInitial}. ${lastName}`;
    } else {
      return `${firstName} ${lastName}`;
    }
  }),

  /**
   * A list of all courses that this user is associated with - be it as learner, instructor or director.
   * @property allRelatedCourses
   * @type {Ember.computed}
   * @public
   */
  allRelatedCourses: computed(
    'directedCourses.[]',
    'learnerGroups.[]',
    'instructorGroups.[]',
    'instructedOfferings.[]',
    'offerings.[]',
    'instructorGroupCourses.[]',
    'learnerIlmSessions.[]',
    'instructorIlmSessions.[]',
    async function() {
      const directedCourses = await this.get('directedCourses');
      const learnerGroups = await this.get('learnerGroups');
      const instructorGroups = await this.get('instructorGroups');
      const instructedOfferings = await this.get('instructedOfferings');
      const offerings = await this.get('offerings');
      const learnerIlmSessions = await this.get('learnerIlmSessions');
      const instructorIlmSessions = await this.get('instructorIlmSessions');

      // get lists of courses associated with this user's learner- and instructor-groups
      const groups = [];
      groups.pushObjects(learnerGroups.toArray());
      groups.pushObjects(instructorGroups.toArray());
      const listsOfCourses = await map(groups.mapBy('courses'), courses => {
        return courses.toArray();
      });

      // get a list of sessions associated with this user's offerings and ILMs
      const offeringsAndIlms = instructedOfferings.toArray();
      offeringsAndIlms.pushObjects(offerings.toArray());
      offeringsAndIlms.pushObjects(learnerIlmSessions.toArray());
      offeringsAndIlms.pushObjects(instructorIlmSessions.toArray());
      const sessions = await all(offeringsAndIlms.mapBy('session'));

      // get a list of courses from these sessions and add it to the lists of courses
      const listOfCourses = await all(sessions.uniq().mapBy('course'));
      listsOfCourses.pushObject(listOfCourses);

      // add the list of directed courses to the lists of courses
      listsOfCourses.pushObject(directedCourses.toArray());

      // flatten these lists down to one list of courses, and de-dupe that list
      return listsOfCourses.reduce((array, set) => {
        array.pushObjects(set);
        return array;
      }, []).uniq();
    }
  ),

  absoluteIcsUri: computed('icsFeedKey', function(){
    return window.location.protocol + '//' + window.location.hostname + '/ics/' + this.get('icsFeedKey');
  }),
  /**
   * Compare a user's learner groups to a list of learner groups and find the one
   * that is the lowest leaf in the learner group tree.
   * @property getLowestMemberGroupInALearnerGroupTree
   * @param {Array} learnerGroupTree all the groups we want to look into
   * @return {Object|null} The learner-group model or NULL if none could be found.
   * @type function
   * @public
   */
  async getLowestMemberGroupInALearnerGroupTree(learnerGroupTree){
    const learnerGroups = await this.get('learnerGroups');
    //all the groups a user is in that are in our current learner groups tree
    let relevantGroups = learnerGroups.toArray().filter(group => learnerGroupTree.includes(group));
    let relevantGroupIds = relevantGroups.mapBy('id');
    let lowestGroup = relevantGroups.find(group => {
      const childIds = group.hasMany('children').ids();
      const childGroupsWhoAreUserGroupMembers = childIds.filter(id => relevantGroupIds.includes(id));
      return (childGroupsWhoAreUserGroupMembers.length === 0);
    });
    return (lowestGroup ? lowestGroup : null);
  },

  secondaryCohorts: computed('primaryCohort', 'cohorts.[]', async function(){
    const cohorts = await this.get('cohorts');
    const primaryCohort = await this.get('primaryCohort');
    return cohorts.toArray().filter(cohort => cohort !== primaryCohort);
  })
});
<commit_msg>Cohorts do not keep track of primary users<commit_after>import Ember from 'ember';
import DS from 'ember-data';

const { computed, RSVP } = Ember;
const { attr, belongsTo, hasMany, Model } = DS;
const { all, map } = RSVP;

export default Model.extend({
  lastName: attr('string'),
  firstName: attr('string'),
  middleName: attr('string'),
  phone: attr('string'),
  email:  attr('string'),
  addedViaIlios:  attr('boolean'),
  enabled:  attr('boolean'),
  campusId:  attr('string'),
  otherId:  attr('string'),
  examined:  attr('boolean'),
  userSyncIgnore:  attr('boolean'),
  icsFeedKey:  attr('string'),
  root: attr('boolean'),
  reminders: hasMany('user-made-reminder', {async: true}),
  reports: hasMany('report', {async: true}),
  school: belongsTo('school', {async: true}),
  authentication: belongsTo('authentication', {async: true}),
  directedCourses: hasMany('course', {
    async: true,
    inverse: 'directors'
  }),
  administeredCourses: hasMany('course', {
    async: true,
    inverse: 'administrators'
  }),
  learnerGroups: hasMany('learner-group', {
    async: true,
    inverse: 'users'
  }),
  instructedLearnerGroups: hasMany('learner-group', {
    async: true,
    inverse: 'instructors'
  }),
  instructorGroups: hasMany('instructor-group', {
    async: true,
    inverse: 'users'
  }),
  instructorIlmSessions: hasMany('ilm-session', {
    async: true,
    inverse: 'instructors'
  }),
  learnerIlmSessions: hasMany('ilm-session', {
    async: true,
    inverse: 'learners'
  }),
  offerings: hasMany('offering', {
    async: true,
    inverse: 'learners'
  }),
  instructedOfferings: hasMany('offering', {
    async: true,
    inverse: 'instructors'
  }),
  programYears: hasMany('program-year', {async: true}),
  roles: hasMany('user-role', {async: true}),
  directedSchools: hasMany('school', {
    async: true,
    inverse: 'directors'
  }),
  administeredSchools: hasMany('school', {
    async: true,
    inverse: 'administrators'
  }),
  administeredSessions: hasMany('session', {
    async: true,
    inverse: 'administrators'
  }),
  directedPrograms: hasMany('program', {
    async: true,
    inverse: 'directors'
  }),

  cohorts: hasMany('cohort', {
    async: true,
    inverse: 'users'
  }),
  primaryCohort: belongsTo('cohort', {async: true, inverse: null}),
  pendingUserUpdates: hasMany('pending-user-update', {async: true}),
  permissions: hasMany('permission', {async: true}),

  /**
   * Resolves to TRUE if this user has the "Student" role, otherwise FALSE.
   * @property isStudent
   * @type {Ember.computed}
   * @public
   */
  isStudent: computed('roles.[]', async function() {
    const roles = await this.get('roles');
    return !!roles.toArray().findBy('title', 'Student');
  }),

  /**
   * All schools that this user is associated with directly.
   * This includes the user's school affiliation, as well as any additional schools that
   * the user has read- and/or write-permissions to.
   * @property schools
   * @type {Ember.computed}
   * @public
   */
  schools: computed('school', async function(){
    const store = this.get('store');
    const primarySchool = await this.get('school');
    const permissions = await this.get('permissions');
    const schoolIds = permissions.toArray().filter(permission => {
      return 'school' === permission.get('tableName');
    }).mapBy('tableRowId');

    const schools = await all(schoolIds.map(id => {
      return store.findRecord('school', id);
    }));

    schools.pushObject(primarySchool);
    return schools.uniq();
  }),

  fullName: computed('firstName', 'middleName', 'lastName', function() {
    const { firstName, middleName, lastName } = this.getProperties('firstName', 'middleName', 'lastName');

    if (!firstName || !lastName) {
      return '';
    }

    const middleInitial = middleName?middleName.charAt(0):false;

    if (middleInitial) {
      return `${firstName} ${middleInitial}. ${lastName}`;
    } else {
      return `${firstName} ${lastName}`;
    }
  }),

  /**
   * A list of all courses that this user is associated with - be it as learner, instructor or director.
   * @property allRelatedCourses
   * @type {Ember.computed}
   * @public
   */
  allRelatedCourses: computed(
    'directedCourses.[]',
    'learnerGroups.[]',
    'instructorGroups.[]',
    'instructedOfferings.[]',
    'offerings.[]',
    'instructorGroupCourses.[]',
    'learnerIlmSessions.[]',
    'instructorIlmSessions.[]',
    async function() {
      const directedCourses = await this.get('directedCourses');
      const learnerGroups = await this.get('learnerGroups');
      const instructorGroups = await this.get('instructorGroups');
      const instructedOfferings = await this.get('instructedOfferings');
      const offerings = await this.get('offerings');
      const learnerIlmSessions = await this.get('learnerIlmSessions');
      const instructorIlmSessions = await this.get('instructorIlmSessions');

      // get lists of courses associated with this user's learner- and instructor-groups
      const groups = [];
      groups.pushObjects(learnerGroups.toArray());
      groups.pushObjects(instructorGroups.toArray());
      const listsOfCourses = await map(groups.mapBy('courses'), courses => {
        return courses.toArray();
      });

      // get a list of sessions associated with this user's offerings and ILMs
      const offeringsAndIlms = instructedOfferings.toArray();
      offeringsAndIlms.pushObjects(offerings.toArray());
      offeringsAndIlms.pushObjects(learnerIlmSessions.toArray());
      offeringsAndIlms.pushObjects(instructorIlmSessions.toArray());
      const sessions = await all(offeringsAndIlms.mapBy('session'));

      // get a list of courses from these sessions and add it to the lists of courses
      const listOfCourses = await all(sessions.uniq().mapBy('course'));
      listsOfCourses.pushObject(listOfCourses);

      // add the list of directed courses to the lists of courses
      listsOfCourses.pushObject(directedCourses.toArray());

      // flatten these lists down to one list of courses, and de-dupe that list
      return listsOfCourses.reduce((array, set) => {
        array.pushObjects(set);
        return array;
      }, []).uniq();
    }
  ),

  absoluteIcsUri: computed('icsFeedKey', function(){
    return window.location.protocol + '//' + window.location.hostname + '/ics/' + this.get('icsFeedKey');
  }),
  /**
   * Compare a user's learner groups to a list of learner groups and find the one
   * that is the lowest leaf in the learner group tree.
   * @property getLowestMemberGroupInALearnerGroupTree
   * @param {Array} learnerGroupTree all the groups we want to look into
   * @return {Object|null} The learner-group model or NULL if none could be found.
   * @type function
   * @public
   */
  async getLowestMemberGroupInALearnerGroupTree(learnerGroupTree){
    const learnerGroups = await this.get('learnerGroups');
    //all the groups a user is in that are in our current learner groups tree
    let relevantGroups = learnerGroups.toArray().filter(group => learnerGroupTree.includes(group));
    let relevantGroupIds = relevantGroups.mapBy('id');
    let lowestGroup = relevantGroups.find(group => {
      const childIds = group.hasMany('children').ids();
      const childGroupsWhoAreUserGroupMembers = childIds.filter(id => relevantGroupIds.includes(id));
      return (childGroupsWhoAreUserGroupMembers.length === 0);
    });
    return (lowestGroup ? lowestGroup : null);
  },

  secondaryCohorts: computed('primaryCohort', 'cohorts.[]', async function(){
    const cohorts = await this.get('cohorts');
    const primaryCohort = await this.get('primaryCohort');
    return cohorts.toArray().filter(cohort => cohort !== primaryCohort);
  })
});
<|endoftext|>