<commit_before>// wrapper for localStorage and sessionStorage
var Storage = (function() {
    var _parse = function(_value) {
        // everything in local storage is a string
        // so let's convert booleans and numbers
        // to be true booleans and numbers
        // and return those
        if (_value===null || _value===undefined) {
            // localStorage["foo"] returns null
            // in some browsers even if
            // foo isn't there at all.
            // since foo is really undefined,
            // we are returning accordingly
            return undefined;
        }
        if (_value==="true") {
            return true;
        }
        if (_value==="false") {
            return false;
        }
        if (!isNaN(_value)) {
            return parseFloat(_value);
        }
        if (_value.indexOf && (_value.indexOf("{")===0 || _value.indexOf("[")===0) && window.JSON!==undefined) {
            return JSON.parse(_value);
        }
        return _value;
    };
    var _prepare = function(_value) {
        if (typeof _value==="object" && window.JSON!==undefined) {
            return JSON.stringify(_value);
        }
        return _value;
    };
    var _get = function(_type) {
        var _storage = window[_type];
        return function(_key) {
            if (_storage!==undefined) {
                var _value = _storage[_key];
                return _parse(_value);
            }
            return undefined;
        };
    };
    var _set = function(_type) {
        var _storage = window[_type];
        return function(_key, _value) {
            if (_storage!==undefined) {
                _storage.setItem(_key, _prepare(_value));
            }
            return undefined;
        };
    };
    var _remove = function(_type) {
        var _storage = window[_type];
        return function(_key) {
            if (_storage!==undefined) {
                return _storage.removeItem(_key);
            }
            return undefined;
        };
    };
    var _clear = function(_type) {
        var _storage = window[_type];
        return function() {
            if (_storage!==undefined) {
                _storage.clear();
            }
            return undefined;
        };
    };
    var _list = function(_type) {
        var _storage = window[_type];
        return function() {
            if (_storage!==undefined) {
                var i, il=_storage.length;
                if (il===0) {
                    return "No cookies set";
                }
                for (i in _storage) {
                    console.log(i, "=", _parse(_storage[i]));
                }
            }
            return undefined;
        };
    };
    return {
        Local: {
            get: _get("localStorage"),
            set: _set("localStorage"),
            remove: _remove("localStorage"),
            clear: _clear("localStorage"),
            list: _list("localStorage")
        },
        Session: {
            get: _get("sessionStorage"),
            set: _set("sessionStorage"),
            remove: _remove("sessionStorage"),
            clear: _clear("sessionStorage"),
            list: _list("sessionStorage")
        },
        Cookie: {
            get: function(_cookie) {
                var _cookies = document.cookie.split(";");
                var c, cl=_cookies.length;
                for (c=0; c<cl; c++) {
                    var _pair = _cookies[c].split("=");
                    _pair[0] = _pair[0].replace(/^[ ]/, "");
                    if (_pair[0] === _cookie) {
                        return _parse(_pair[1]);
                    }
                }
                return undefined;
            },
            set: function(_key, _value, _days, _path) {
                var _expires = "";
                if (_days!==undefined) {
                    var _date = new Date();
                    _date.setDate(_date.getDate()+_days);
                    _expires = "; expires=" + _date.toUTCString();
                }
                var _value = _prepare(_value) + _expires + (_path===undefined ? "" : "; path="+_path);
                document.cookie = _key + "=" + _value;
            },
            remove: function(_key) {
                this.set(_key, "", -1);
            },
            clear: function() {
                var _cookies = document.cookie.split(";");
                var c, cl=_cookies.length;
                for (c=0; c<cl; c++) {
                    var _pair = _cookies[c].split("=");
                    _pair[0] = _pair[0].replace(/^[ ]/, "");
                    this.set(_pair[0], "", -1);
                }
            },
            list: function() {
                var _cookies = document.cookie.split(";");
                var c, cl=_cookies.length;
                for (c=0; c<cl; c++) {
                    var _pair = _cookies[c].split("=");
                    _pair[0] = _pair[0].replace(/^[ ]/, "");
                    console.log(_pair[0], "=", _parse(_pair[1]));
                }
            }
        }
    };
}());
<commit_msg>adding isSupported()<commit_after>// wrapper for localStorage, sessionStorage and document.cookie
var Storage = (function() {
    var _parse = function(_value) {
        // everything in local storage is a string
        // so let's convert booleans and numbers
        // to be true booleans and numbers
        // and return those
        if (_value===null || _value===undefined) {
            // localStorage["foo"] returns null
            // in some browsers even if
            // foo isn't there at all.
            // since foo is really undefined,
            // we are returning accordingly
            return undefined;
        }
        if (_value==="true") {
            return true;
        }
        if (_value==="false") {
            return false;
        }
        if (!isNaN(_value)) {
            return parseFloat(_value);
        }
        if (_value.indexOf && (_value.indexOf("{")===0 || _value.indexOf("[")===0) && window.JSON!==undefined) {
            return JSON.parse(_value);
        }
        return _value;
    };
    var _prepare = function(_value) {
        if (typeof _value==="object" && window.JSON!==undefined) {
            return JSON.stringify(_value);
        }
        return _value;
    };
    var _get = function(_type) {
        var _storage = window[_type];
        return function(_key) {
            if (_storage!==undefined) {
                var _value = _storage[_key];
                return _parse(_value);
            }
            return undefined;
        };
    };
    var _set = function(_type) {
        var _storage = window[_type];
        return function(_key, _value) {
            if (_storage!==undefined) {
                _storage.setItem(_key, _prepare(_value));
            }
            return undefined;
        };
    };
    var _remove = function(_type) {
        var _storage = window[_type];
        return function(_key) {
            if (_storage!==undefined) {
                return _storage.removeItem(_key);
            }
            return undefined;
        };
    };
    var _clear = function(_type) {
        var _storage = window[_type];
        return function() {
            if (_storage!==undefined) {
                _storage.clear();
            }
            return undefined;
        };
    };
    var _list = function(_type) {
        var _storage = window[_type];
        return function() {
            if (_storage!==undefined) {
                var i, il=_storage.length;
                if (il===0) {
                    return "No cookies set";
                }
                for (i in _storage) {
                    console.log(i, "=", _parse(_storage[i]));
                }
            }
            return undefined;
        };
    };
    var _isSupported = function(_type) {
        var _storage = window[_type];
        return function() {
            return _storage!==undefined;
        };
    };
    var _local = {
        get: _get("localStorage"),
        set: _set("localStorage"),
        remove: _remove("localStorage"),
        clear: _clear("localStorage"),
        list: _list("localStorage"),
        isSupported: _isSupported("localStorage")
    };
    var _session = {
        get: _get("sessionStorage"),
        set: _set("sessionStorage"),
        remove: _remove("sessionStorage"),
        clear: _clear("sessionStorage"),
        list: _list("sessionStorage"),
        isSupported: _isSupported("sessionStorage")
    };
    var _cookie = {
        get: function(_cookie) {
            var _cookies = document.cookie.split(";");
            var c, cl=_cookies.length;
            for (c=0; c<cl; c++) {
                var _pair = _cookies[c].split("=");
                _pair[0] = _pair[0].replace(/^[ ]/, "");
                if (_pair[0] === _cookie) {
                    return _parse(_pair[1]);
                }
            }
            return undefined;
        },
        set: function(_key, _value, _days, _path) {
            var _expires = "";
            if (_days!==undefined) {
                var _date = new Date();
                _date.setDate(_date.getDate()+_days);
                _expires = "; expires=" + _date.toUTCString();
            }
            var _value = _prepare(_value) + _expires + (_path===undefined ? "" : "; path="+_path);
            document.cookie = _key + "=" + _value;
        },
        remove: function(_key) {
            this.set(_key, "", -1);
        },
        clear: function() {
            var _cookies = document.cookie.split(";");
            var c, cl=_cookies.length;
            for (c=0; c<cl; c++) {
                var _pair = _cookies[c].split("=");
                _pair[0] = _pair[0].replace(/^[ ]/, "");
                this.set(_pair[0], "", -1);
            }
        },
        list: function() {
            var _cookies = document.cookie.split(";");
            var c, cl=_cookies.length;
            for (c=0; c<cl; c++) {
                var _pair = _cookies[c].split("=");
                _pair[0] = _pair[0].replace(/^[ ]/, "");
                console.log(_pair[0], "=", _parse(_pair[1]));
            }
        }
    };
    return {
        Local: _local,
        Session: _session,
        Cookie: _cookie
    };
}());
<|endoftext|>


<commit_before>import Ember from 'ember';
import DirtyConfirmRouteMixin from 'ember-cli-dirty-confirm/mixins/dirty-confirm-route';

module('DirtyConfirmRouteMixin');

// Replace this with your real tests.
test('it works', function() {
  var DirtyConfirmRouteObject = Ember.Object.extend(DirtyConfirmRouteMixin);
  var subject = DirtyConfirmRouteObject.create();
  ok(subject);
});
<commit_msg>new test<commit_after>import Ember from 'ember';
import DirtyConfirmRouteMixin from '../../../mixins/dirty-confirm-route';
import { module, test } from 'qunit';

module('DirtyConfirmRouteMixin');

// Replace this with your real tests.
test('it works', function(assert) {
  var DirtyConfirmRouteObject = Ember.Object.extend(DirtyConfirmRouteMixin);
  var subject = DirtyConfirmRouteObject.create();
  assert.ok(subject);
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015-2019 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import "./individual-view.js";
import "./../commons/view/detail-tabs.js";

export default class IndividualDetail extends LitElement {

    constructor() {
        super();
        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            individualId: {
                type: String
            },
            individual: {
                type: Object
            },
            config: {
                type: Object
            },
        };
    }

    _init() {
        this._config = this.getDefaultConfig();
    }

    updated(changedProperties) {
        if (changedProperties.has("opencgaSession")) {
            this.individual = null;
        }

        if (changedProperties.has("individualId")) {
            this.individualIdObserver();
        }

        if (changedProperties.has("config")) {
            this._config = {...this.getDefaultConfig(), ...this.config};
        }
    }

    individualIdObserver() {
        if (this.opencgaSession && this.individualId) {
            this.opencgaSession.opencgaClient.individuals().info(this.individualId, {study: this.opencgaSession.study.fqn})
                .then(restResponse => {
                    this.individual = restResponse.getResult(0);
                })
                .catch(restResponse => {
                    console.error(restResponse);
                });
        }
    }

    getDefaultConfig() {
        return {
            // detail-tab configuration in individual-browser
        };
    }

    render() {
        return this.opencgaSession && this.individual ?
            html`
                <detail-tabs
                    .data="${this.individual}"
                    .config="${this._config}"
                    .opencgaSession="${this.opencgaSession}">
                </detail-tabs>
            ` : "";
    }

}

customElements.define("individual-detail", IndividualDetail);
<commit_msg>wc: fixed individual detail tabs are cleared #TASK-600<commit_after>/**
 * Copyright 2015-2019 OpenCB
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {LitElement, html} from "lit";
import "./individual-view.js";
import "./../commons/view/detail-tabs.js";

export default class IndividualDetail extends LitElement {

    constructor() {
        super();
        this._init();
    }

    createRenderRoot() {
        return this;
    }

    static get properties() {
        return {
            opencgaSession: {
                type: Object
            },
            individualId: {
                type: String
            },
            individual: {
                type: Object
            },
            config: {
                type: Object
            },
        };
    }

    _init() {
        this._config = this.getDefaultConfig();
    }

    updated(changedProperties) {
        if (changedProperties.has("opencgaSession")) {
            this.individual = null;
        }

        if (changedProperties.has("individualId")) {
            this.individualIdObserver();
        }

        if (changedProperties.has("config")) {
            this._config = {...this.getDefaultConfig(), ...this.config};
        }
    }

    individualIdObserver() {
        if (this.opencgaSession && this.individualId) {
            this.opencgaSession.opencgaClient.individuals().info(this.individualId, {study: this.opencgaSession.study.fqn})
                .then(restResponse => {
                    this.individual = restResponse.getResult(0);
                })
                .catch(restResponse => {
                    console.error(restResponse);
                });
        } else {
            this.individual = null;
        }
    }

    getDefaultConfig() {
        return {
            // detail-tab configuration in individual-browser
        };
    }

    render() {
        return this.opencgaSession && this.individual ?
            html`
                <detail-tabs
                    .data="${this.individual}"
                    .config="${this._config}"
                    .opencgaSession="${this.opencgaSession}">
                </detail-tabs>
            ` : "";
    }

}

customElements.define("individual-detail", IndividualDetail);
<|endoftext|>


<commit_before>
/*
  This script will initialize a local Mongo database
  on your machine so you can do development work.

  IMPORTANT: You should make sure the

      local_name

  variable matches its value in app.js  Otherwise, you'll have
  initialized the wrong database.
*/

var mongoose = require('mongoose');
var models   = require('./models');

// Connect to the Mongo database, whether locally or on Heroku
// MAKE SURE TO CHANGE THE NAME FROM 'lab7' TO ... IN OTHER PROJECTS
var local_name = 'cs247project';
var local_uri  = 'mongodb://localhost/' + local_name
var database_uri = process.env.MONGOLAB_URI || local_uri
mongoose.connect(database_uri);

// Do the initialization here

// Step 1: load the JSON data

// Step 2: Remove all existing documents
models.Match
  .find()
  .remove()
  .exec(onceClear); // callback to continue at

// Step 3: load the data from the JSON file
function onceClear(err) {
  if(err) console.log(err);
  models.NoMatch
    .find()
    .remove()
    .exec(onceClear2);
  console.log("done1");
}

function onceClear2(err) {
  if(err) console.log(err);
  models.Notification
    .find()
    .remove()
    .exec(onceClear3);
  console.log("done2");
}

function onceClear3(err) {
  var match2 = new models.Match({"uid1": "10152423734788293", "uid2": "10152435644103676", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
  match2.save(function(err, proj) {
    var match3 = new models.Match({"uid1": "10152423734788293", "uid2": "637148899698075", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
    match3.save(function(err, proj) {
      var match4 = new models.Match({"uid1": "10152423734788293", "uid2": "10152002738066150", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
      match4.save(function(err, proj) {
          var match = new models.Match({"uid1": "10152423734788293", "uid2": "696436097061985", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
          match.save(function(err, proj) {
          if(err) console.log(err);
          console.log("done3");
          var not = new models.Notification({"uid": "10152423734788293", "type": "match", "seen": false})
          not.save(function(err, proj) {
            if(err) console.log(err);
            console.log("done4");
            mongoose.connection.close();
          });
        });
      });
    });
  });

}<commit_msg>class<commit_after>
/*
  This script will initialize a local Mongo database
  on your machine so you can do development work.

  IMPORTANT: You should make sure the

      local_name

  variable matches its value in app.js  Otherwise, you'll have
  initialized the wrong database.
*/

var mongoose = require('mongoose');
var models   = require('./models');

// Connect to the Mongo database, whether locally or on Heroku
// MAKE SURE TO CHANGE THE NAME FROM 'lab7' TO ... IN OTHER PROJECTS
var local_name = 'cs247project';
var local_uri  = 'mongodb://localhost/' + local_name
var database_uri = process.env.MONGOLAB_URI || local_uri
mongoose.connect(database_uri);

// Do the initialization here

// Step 1: load the JSON data

// Step 2: Remove all existing documents
models.Match
  .find()
  .remove()
  .exec(onceClear); // callback to continue at

// Step 3: load the data from the JSON file
function onceClear(err) {
  if(err) console.log(err);
  models.NoMatch
    .find()
    .remove()
    .exec(onceClear2);
  console.log("done1");
}

function onceClear2(err) {
  if(err) console.log(err);
  models.Notification
    .find()
    .remove()
    .exec(onceClear3);
  console.log("done2");
}

function onceClear3(err) {
  var match2 = new models.Match({"uid1": "10152423734788293", "uid2": "2415271352488", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
  match2.save(function(err, proj) {
    /*var match3 = new models.Match({"uid1": "10152423734788293", "uid2": "637148899698075", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
    match3.save(function(err, proj) {
      var match4 = new models.Match({"uid1": "10152423734788293", "uid2": "10152002738066150", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
      match4.save(function(err, proj) {
          var match = new models.Match({"uid1": "10152423734788293", "uid2": "696436097061985", "recommenders": "hello,hi,conner,kennan,jordan", "numRecs": 5});
          match.save(function(err, proj) {
          */if(err) console.log(err);
          console.log("done3");
          var not = new models.Notification({"uid": "10152423734788293", "type": "match", "seen": false})
          not.save(function(err, proj) {
            if(err) console.log(err);
            console.log("done4");
            mongoose.connection.close();
          });
        /*});
      });
    });*/
  });
}<|endoftext|>


<commit_before>var events = require('events');
var util = require('util');

var mrf24j40 = require('node-mrf24j40');
var MRF24J40 = mrf24j40.MRF24J40;
var ControlRegister = mrf24j40.ControlRegister;
var FIFO = mrf24j40.FIFO;

var osnp = require('node-osnp');
var OSNPAddressTable = osnp.OSNPAddressTable;
var OSNPCommandQueue = osnp.OSNPCommandQueue;
var FrameType = osnp.FrameType;
var FrameVersion = osnp.FrameVersion;
var AddressingMode = osnp.AddressingMode;
var MACCommand = osnp.MACCommand;

var radio;
var txQueue;
var txFrame;

var addressTable;
var deviceQueues;

var deviceDiscoveryPeriods;

module.exports = new events.EventEmitter();
exports = module.exports;

function QueuedFrame(frame, callback) {
  this.frame = frame;
  this.callback = callback;
}

exports.start = function() {
  if (process.platform == 'linux') {
    radio = new MRF24J40('raspi');
  }

  // This must be persistent
  addressTable = new OSNPAddressTable();
  deviceQueues = {};
  txQueue = [];
  txFrame = null;

  osnp.setPANID(new Buffer([0xfe, 0xca]));
  osnp.setShortAddress(new Buffer([0x00, 0x00]));

  if (radio) {
    radio.on('frame', handleReceived);
    radio.on('transmitted', handleTransmitted);
    radio.initialize();
    radio.setPANCoordinator();
    radio.setPANID(osnp.getPANID());
    radio.setShortAddress(osnp.getShortAddress());
    radio.setChannel(2);
  }
}

exports.discoverDevices = function() {
  deviceDiscoveryPeriods = 0;
  transmitDiscoveryRequest();
}

exports.pair = function(device, pairingData, cb) {
  device.protocolInfo.shortAddress = new Buffer(2);
  device.protocolInfo.shortAddress.writeUInt16LE(addressTable.allocate(), 0);
  var frame = osnp.createPairingCommand(device.protocolInfo.eui, device.protocolInfo.shortAddress);
  addToQueue(frame, device, cb, device.protocolInfo.shortAddress);
}

exports.unpair = function(device, cb) {
  var frame = osnp.createUnpairingCommand(device.protocolInfo.shortAddress);
  addToQueue(frame, device, cb);
}

exports.send = function(device, data, cb) {
  var frame = osnp.createCommandPacket(data, device.protocolInfo.eui, device.protocolInfo.shortAddress, device.paired);
  addToQueue(frame, device, cb);
}

function getQueue(address) {
  var queue = deviceQueues[address.toString('hex')];

  if (!queue) {
    queue = new OSNPCommandQueue();
    deviceQueues[address.toString('hex')] = queue;
  }

  return queue;
}

function tryDequeue(queue) {
  var cmd = queue.dequeue();

  if (cmd) {
    txQueue.push(cmd.frame);
    tryTransmit();
  }
}

function addToQueue(frame, device, cb, address) {
  if (!address) {
    address = frame.destinationAddress;
  }

  var queue = getQueue(address, device);
  queue.device = device;
  queue.queue(new QueuedFrame(frame, cb));
  tryDequeue(queue);
}

function transmitDiscoveryRequest() {
  var frame = osnp.createDiscoveryRequest();
  txQueue.push(frame);
  tryTransmit();

  if (deviceDiscoveryPeriods < 240) {
    deviceDiscoveryPeriods++;
    setTimeout(transmitDiscoveryRequest, 250);
  } else {
    exports.emit('deviceDiscoveryFinished');
  }
}

function tryTransmit() {
  if (!txFrame && (txQueue.length > 0)) {
    var frame = txQueue.shift();
    txFrame = frame;
    var frameLength = frame.getEncodedLength();
    var buf = new Buffer(frameLength + 2);
    buf[0] = frameLength - frame.payload.length;
    buf[1] = frameLength;
    frame.encode(buf.slice(2));
    radio.transmit(buf);
  }
}

function handleMACFrame(frame) {
  switch (frame.payload[0]) {
  case MACCommand.ASSOCIATION_RESPONSE:
    //TODO: handle error
    var queue = getQueue(frame.sourceAddress);
    delete deviceQueues[queue.device.protocolInfo.eui];
    var cmd = queue.deviceResponded();
    queue.device.protocolInfo.capabilities = frame.payload[1];
    cmd.callback(queue.device);
    tryDequeue(queue);
    break;
  case MACCommand.DATA_REQUEST:
    break;
  case MACCommand.DISCOVER:
    var protocolInfo = new OSNPProtocolInfo(frame.sourceAddress);
    exports.emit('deviceDiscovered', protocolInfo.toID(), protocolInfo);
    break;
  }
}

function handleDataFrame(frame) {
  var queue = getQueue(frame.sourceAddress);

  //TODO: what if the queue has not been created yet? need to sort this at initialization, I think
  if (frame.payload[0] == 0xE2) {
    exports.emit('notificationReceived', queue.device, frame.payload);
    return;
  }

  var cmd = queue.deviceResponded();
  cmd.callback(frame.payload);
  tryDequeue(queue);
}

function handleReceived(rawFrame, lqi, rssi) {
  var frame = osnp.parseFrame(rawFrame);

  switch(frame.getFrameType()) {
  case FrameType.MAC_CMD:
    handleMACFrame(frame);
    break;
  case FrameType.DATA:
    handleDataFrame(frame);
    break;
  }
}

function handleTransmitted(txErr, ccaErr) {
  if(ccaErr) {
    txQueue.push(txFrame);
  } else if (txErr) {
    var queue = getQueue(txFrame.destinationAddress);
    var sentCmd = queue.deviceResponded();
    //TODO: handle error by invoking callback with a proper error code
  } else if (txFrame.payload[0] == MACCommand.DISASSOCIATED) {
    var queue = getQueue(txFrame.destinationAddress);
    delete deviceQueues[queue.device.protocolInfo.shortAddress];
    addressTable.free(queue.device.protocolInfo.shortAddress);
    var cmd = queue.deviceResponded();
    cmd.callback(queue.device);
  }

  txFrame = null;
  tryTransmit();
}

function OSNPProtocolInfo(eui) {
  this.eui = eui;
  this.shortAddress = null;
  this.capabilities = null;
}

OSNPProtocolInfo.prototype.toID = function() {
  return this.eui.toString('hex');
}

<commit_msg>added support for poll-driven devices<commit_after>var events = require('events');
var util = require('util');

var mrf24j40 = require('node-mrf24j40');
var MRF24J40 = mrf24j40.MRF24J40;
var ControlRegister = mrf24j40.ControlRegister;
var FIFO = mrf24j40.FIFO;

var osnp = require('node-osnp');
var OSNPAddressTable = osnp.OSNPAddressTable;
var OSNPCommandQueue = osnp.OSNPCommandQueue;
var FrameType = osnp.FrameType;
var FrameVersion = osnp.FrameVersion;
var AddressingMode = osnp.AddressingMode;
var MACCommand = osnp.MACCommand;
var RXMode = osnp.RXMode;

var radio;
var txQueue;
var txFrame;

var addressTable;
var deviceQueues;

var deviceDiscoveryPeriods;

module.exports = new events.EventEmitter();
exports = module.exports;

function QueuedFrame(frame, callback) {
  this.frame = frame;
  this.callback = callback;
}

exports.start = function() {
  if (process.platform == 'linux') {
    radio = new MRF24J40('raspi');
  }

  // This must be persistent
  addressTable = new OSNPAddressTable();
  deviceQueues = {};
  txQueue = [];
  txFrame = null;

  osnp.setPANID(new Buffer([0xfe, 0xca]));
  osnp.setShortAddress(new Buffer([0x00, 0x00]));

  if (radio) {
    radio.on('frame', handleReceived);
    radio.on('transmitted', handleTransmitted);
    radio.initialize();
    radio.setPANCoordinator();
    radio.setPANID(osnp.getPANID());
    radio.setShortAddress(osnp.getShortAddress());
    radio.setChannel(2);
  }
}

exports.discoverDevices = function() {
  deviceDiscoveryPeriods = 0;
  transmitDiscoveryRequest();
}

exports.pair = function(device, pairingData, cb) {
  device.protocolInfo.shortAddress = new Buffer(2);
  device.protocolInfo.shortAddress.writeUInt16LE(addressTable.allocate(), 0);
  var frame = osnp.createPairingCommand(device.protocolInfo.eui, device.protocolInfo.shortAddress);
  addToQueue(frame, device, cb, device.protocolInfo.shortAddress);
}

exports.unpair = function(device, cb) {
  var frame = osnp.createUnpairingCommand(device.protocolInfo.shortAddress);
  addToQueue(frame, device, cb);
}

exports.send = function(device, data, cb) {
  var frame = osnp.createCommandPacket(data, device.protocolInfo.eui, device.protocolInfo.shortAddress, device.paired);
  addToQueue(frame, device, cb);
}

function tryUnsetFramePending() {
  var empty = true;

  for(var queueName in deviceQueues) {
    var queue = deviceQueues[queueName];

    if (queue.device.protocolInfo.isPollDriven() && !queue.isEmpty) {
      empty = false;
      break;
    }
  }

  if (empty) {
    radio.setDataRequestFramePending(false);
  }
}

function getQueue(address) {
  var queue = deviceQueues[address.toString('hex')];

  if (!queue) {
    queue = new OSNPCommandQueue();
    deviceQueues[address.toString('hex')] = queue;
  }

  return queue;
}

function tryDequeue(queue) {
  var cmd;

  if (!device.protocolInfo.isPollDriven()) {
    cmd = queue.dequeue();
  } else if (queue.active) {
    cmd = queue.dequeue();

    if (queue.isEmpty()) {
      queue.active = false;
    } else
      //add the frame pending bit
      cmd.frame.frameControlLow = cmd.frame.frameControlLow | 0x10;
    }
  }

  if (cmd) {
    txQueue.push(cmd.frame);
    tryTransmit();
  }

  tryUnsetFramePending();
}

function addToQueue(frame, device, cb, address) {
  if (!address) {
    address = frame.destinationAddress;
  }

  if (device.protocolInfo.isPollDriven()) {
    radio.setDataRequestFramePending(true);
  }

  var queue = getQueue(address, device);
  queue.device = device;
  queue.queue(new QueuedFrame(frame, cb));
  tryDequeue(queue);
}

function transmitDiscoveryRequest() {
  var frame = osnp.createDiscoveryRequest();
  txQueue.push(frame);
  tryTransmit();

  if (deviceDiscoveryPeriods < 240) {
    deviceDiscoveryPeriods++;
    setTimeout(transmitDiscoveryRequest, 250);
  } else {
    exports.emit('deviceDiscoveryFinished');
  }
}

function tryTransmit() {
  if (!txFrame && (txQueue.length > 0)) {
    var frame = txQueue.shift();
    txFrame = frame;
    var frameLength = frame.getEncodedLength();
    var buf = new Buffer(frameLength + 2);
    buf[0] = frameLength - frame.payload.length;
    buf[1] = frameLength;
    frame.encode(buf.slice(2));
    radio.transmit(buf);
  }
}

function handleMACFrame(frame) {
  switch (frame.payload[0]) {
  case MACCommand.ASSOCIATION_RESPONSE:
    //TODO: handle error
    var queue = getQueue(frame.sourceAddress);
    delete deviceQueues[queue.device.protocolInfo.eui];
    var cmd = queue.deviceResponded();
    queue.device.protocolInfo.capabilities = frame.payload[1];
    cmd.callback(queue.device);
    tryDequeue(queue);
    break;
  case MACCommand.DATA_REQUEST:
    var queue = getQueue(frame.sourceAddress);
    queue.active = true;
    tryDequeue(queue);
    break;
  case MACCommand.DISCOVER:
    var protocolInfo = new OSNPProtocolInfo(frame.sourceAddress);
    exports.emit('deviceDiscovered', protocolInfo.toID(), protocolInfo);
    break;
  }
}

function handleDataFrame(frame) {
  var queue = getQueue(frame.sourceAddress);

  //TODO: what if the queue has not been created yet? need to sort this at initialization, I think
  if (frame.payload[0] == 0xE2) {
    exports.emit('notificationReceived', queue.device, frame.payload);
    return;
  }

  var cmd = queue.deviceResponded();
  cmd.callback(frame.payload);
  tryDequeue(queue);
}

function handleReceived(rawFrame, lqi, rssi) {
  var frame = osnp.parseFrame(rawFrame);

  switch(frame.getFrameType()) {
  case FrameType.MAC_CMD:
    handleMACFrame(frame);
    break;
  case FrameType.DATA:
    handleDataFrame(frame);
    break;
  }
}

function handleTransmitted(txErr, ccaErr) {
  if(ccaErr) {
    txQueue.push(txFrame);
  } else if (txErr) {
    var queue = getQueue(txFrame.destinationAddress);
    var sentCmd = queue.deviceResponded();
    //TODO: handle error by invoking callback with a proper error code
  } else if (txFrame.payload[0] == MACCommand.DISASSOCIATED) {
    var queue = getQueue(txFrame.destinationAddress);
    delete deviceQueues[queue.device.protocolInfo.shortAddress];
    addressTable.free(queue.device.protocolInfo.shortAddress);
    var cmd = queue.deviceResponded();
    cmd.callback(queue.device);
  }

  txFrame = null;
  tryTransmit();
}

function OSNPProtocolInfo(eui) {
  this.eui = eui;
  this.shortAddress = null;
  this.capabilities = null;
}

OSNPProtocolInfo.prototype.toID = function() {
  return this.eui.toString('hex');
}

OSNPProtocolInfo.prototype.isPollDriven = function() {
  return (this.capabilities !== null && ((this.capabilities & 0x01) == RXMode.POLL_DRIVEN));
}

<|endoftext|>


<commit_before>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';
import fetch from 'isomorphic-fetch';

import LoginTopBar from '../containers/LoginTopBar';
import Notification from '../containers/Notification';

import Input from '../components/CustomTextArea';
import PublicFooter from '../components/PublicFooter';
import Confirmation from '../components/Confirmation';

import appendGroupForm from '../actions/form/append_group';
import createGroup from '../actions/groups/create';
import notify from '../actions/notification/notify';
import validateSchema from '../actions/form/validate_schema';

import newGroupSchema from '../joi_schemas/newGroup';

import { uniq } from 'lodash';

export class NewGroup extends Component {

  constructor(props) {
    super(props);
    this.createRef = this.create.bind(this);
    this.state = {
      slugAvailable: true,
      showConfirmation: false
    };
  }

  componentWillMount() {
    const { groupType, appendGroupForm } = this.props;

    if (groupType) {
      appendGroupForm({tags: groupType});
    }
  }

  render() {
    const {
      appendGroupForm,
      newGroupForm,
      pushState,
      hostCollective,
      groupType
    } = this.props;

    const {
      slugAvailable,
      showConfirmation
    } = this.state;

    const {
      tags, // we should store this in a new `type` column
      name,
      slug,
      mission,
      description,
      contribute, // longDescription = description + contribute
      users
    } = newGroupForm.attributes;

    const groupTypes = new Map();
    groupTypes.set('association', {
      label: 'Association'
    });
    groupTypes.set('coop', {
      label: 'Cooperative'
    });
    groupTypes.set('meetup',{
      label: 'Meetup Group',
      placeholders: {
        name: 'Consciousness Hacking SF',
        slug: 'ConsciousnessHackingSF',
        mission: 'We are on a mission to explore and develop new technologies for psychological, emotional and spiritual flourishing.',
        description: "Consciousness Hacking is an inside-out perspective on how technology can serve us by changing our relationship to the world, rather than the world itself. Meetings will feature insightful talks, community building, and support for new ideas and concepts. Consciousness Hacking is an open exploration of how science and technology can support radically modern approaches to spirituality and human flourishing. We do this through a global network of communities, events, digital media and projects.",
        contribute: 'We need more people to join the community and attend our events. We are also looking for volunteers to help us organize our meetups.'
      }
    });
    groupTypes.set('movement', {
      label: 'Movement'
    });
    groupTypes.set('opensource', {
      label: 'Open Source Project',
      placeholders: {
        name: 'MochaJS',
        slug: 'mochajs',
        mission: 'We are on a mission to make asynchronous testing simple and fun.',
        description: 'Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.',
        contribute: 'We are looking for maintainers to help us deal with the large number of issues and pull requests. We also welcome financial contributions.'
      }
    });
    groupTypes.set('pta', {
      label: 'Parent Teacher Association'
    });
    groupTypes.set('studentclub', {
      label: 'Student Club'
    });
    groupTypes.set('default', {
      label: 'Other',
      placeholders: {
        name: 'The Mindful Collective',
        slug: 'mindfulcollective',
        mission: 'We are on a mission to bring mindfulness to our city',
        description: 'Every week, we organize a public sitting in silence. We invite everyone to come meditate with us. We are also giving a meditation class to the public school in our city.',
        contribute: 'We are looking for public school teachers who could open the door of their classroom. We are also looking for financial help to cover our expenses (printing posters, transportation, ...)',
        email: 'sn.goenka@dharma.org'
      }
    });

    const getPlaceholder = (attr) => {
      const placeholders = groupTypes.get(tags || 'default').placeholders || {};
      return placeholders[attr] || groupTypes.get('default').placeholders[attr];
    };

    const groupTypeBtns = [];
    groupTypes.forEach((value, key) => {
      const selected = (key === tags) ? 'selected' : '';
      groupTypeBtns.push(<button className={`GroupTypeBtn ${selected}`} onClick={() => appendGroupForm({tags:key})}>{value.label}</button>);
    });

    if (tags === 'opensource')
      return pushState(null, '/opensource/apply');

    const showGroupTypes = (!showConfirmation && !groupType);
    const showFormDetails = (!showConfirmation && (groupType || tags));

    const validateSlug = (slug) => {
      fetch(`/api/groups/${slug}`)
        .then(response => {
          this.setState( { slugAvailable: !(response.status === 200) })
        })
        .catch();
    }

    return (

        <Notification {...this.props} />
        <LoginTopBar />

          { hostCollective &&

{hostCollective.name}

}

Apply to create a collective

          {showGroupTypes &&

              What is your collective for?

                { groupTypeBtns }

          }

          {showFormDetails &&

                What is your collective name?
                <Input
                  name='name'
                  value={name}
                  onChange={(value) => appendGroupForm({name: value})}
                  maxLength={128}
                  rows={1}
                  placeholder={getPlaceholder('name')}/>

                What URL would you like to have?
                <Input
                  name='slug'
                  prepend='https://opencollective.com/'
                  value={slug}
                  onChange={(value) => appendGroupForm({slug: value})}
                  onBlur={validateSlug}
                  maxLength={32}
                  rows={1}
                  placeholder={getPlaceholder('slug')}/>
                { !slugAvailable && This URL is not available :-(}

                What is your collective's mission?
                <Input
                  name='mission'
                  value={mission}
                  onChange={(value) => appendGroupForm({mission: value})}
                  maxLength={100}
                  rows={2}
                  placeholder={getPlaceholder('mission')}/>

                How are you going to do that? Why should people join?

                Tell us more (you can always edit later)
                <Input
                  name='description'
                  value={description}
                  onChange={(value) => appendGroupForm({description: value})}
                  maxLength={512}
                  rows={5}
                  placeholder={getPlaceholder('description')} />

                What do you need help with? How can people contribute to your mission?
                <Input
                  name='contribute'
                  value={contribute}
                  onChange={(value) => appendGroupForm({contribute: value})}
                  maxLength={256}
                  rows={4}
                  placeholder={getPlaceholder('contribute')} />

                What is your personal email?
                <Input
                  email='email'
                  value={users[0].email}
                  onChange={(value) => appendGroupForm({users: [{email: value, role: 'ADMIN'}]})}
                  maxLength={128}
                  rows={1}
                  placeholder={getPlaceholder('email')}/>

                  <input type="checkbox" onChange={(event) => appendGroupForm({tos: event.target.checked})} />
                  By clicking here you agree to the terms of service

              <div className={`center AddGroup-Button ${slugAvailable ? '' : 'disabled'}`} onClick={ this.createRef }>submit

          }

          {showConfirmation &&
            <Confirmation>

Almost done!

To continue, click on the link that we sent you ({users[0].email}).

            </Confirmation>
          }

        <PublicFooter/>

    );
  }

  create() {
    const { newGroupForm, validateSchema, createGroup, utmSource, notify, hostCollective, hostId} = this.props;
    const attr = newGroupForm.attributes;
    let tags = attr.tags && attr.tags.split(',').map(x => x.trim());
    if (hostCollective) {
      tags = uniq([...tags, ...hostCollective.tags]);
    }

    const group = {
      tos: attr.tos,
      name: attr.name,
      slug: attr.slug,
      mission: attr.mission,
      longDescription: attr.contribute ? `${attr.description}\n\n# Contribute\n\n${attr.contribute}` : attr.description,
      website: attr.website,
      hostId: hostId,
      hostFeePercent: hostCollective && hostCollective.settings.hostFeePercent || 5,
      data: {
        utmSource
      },
      tags,
      users: attr.users
    };

    return validateSchema(group, newGroupSchema)
      .then(() => createGroup(group))
      .then(() => this.setState({showConfirmation: true}))
      .catch(({message}) => notify('error', message));
  }

}

export default connect(mapStateToProps, {
  appendGroupForm,
  pushState,
  notify,
  createGroup,
  validateSchema
})(NewGroup);

export function mapStateToProps({router, form, groups}) {

  const hostCollective = (groups && router.params.slug) ? groups[router.params.slug] : null;
  const groupType = router.params.type;
  const query = router.location.query;
  const utmSource = query.utm_source;
  const hostId = hostCollective && groups[router.params.slug].id;

  return {
    newGroupForm: form.addgroup,
    hostCollective,
    utmSource,
    groupType,
    hostId
  };
}
<commit_msg>Fix apply (#777)<commit_after>import React, { Component } from 'react';
import { connect } from 'react-redux';
import { pushState } from 'redux-router';
import fetch from 'isomorphic-fetch';

import LoginTopBar from '../containers/LoginTopBar';
import Notification from '../containers/Notification';

import Input from '../components/CustomTextArea';
import PublicFooter from '../components/PublicFooter';
import Confirmation from '../components/Confirmation';

import appendGroupForm from '../actions/form/append_group';
import createGroup from '../actions/groups/create';
import notify from '../actions/notification/notify';
import validateSchema from '../actions/form/validate_schema';

import newGroupSchema from '../joi_schemas/newGroup';

import { uniq } from 'lodash';

export class NewGroup extends Component {

  constructor(props) {
    super(props);
    this.createRef = this.create.bind(this);
    this.state = {
      slugAvailable: true,
      showConfirmation: false
    };
  }

  componentWillMount() {
    const { groupType, appendGroupForm } = this.props;

    if (groupType) {
      appendGroupForm({tags: groupType});
    }
  }

  render() {
    const {
      appendGroupForm,
      newGroupForm,
      pushState,
      hostCollective,
      groupType
    } = this.props;

    const {
      slugAvailable,
      showConfirmation
    } = this.state;

    const {
      tags, // we should store this in a new `type` column
      name,
      slug,
      mission,
      description,
      contribute, // longDescription = description + contribute
      users
    } = newGroupForm.attributes;

    const groupTypes = new Map();
    groupTypes.set('association', {
      label: 'Association'
    });
    groupTypes.set('coop', {
      label: 'Cooperative'
    });
    groupTypes.set('meetup',{
      label: 'Meetup Group',
      placeholders: {
        name: 'Consciousness Hacking SF',
        slug: 'ConsciousnessHackingSF',
        mission: 'We are on a mission to explore and develop new technologies for psychological, emotional and spiritual flourishing.',
        description: "Consciousness Hacking is an inside-out perspective on how technology can serve us by changing our relationship to the world, rather than the world itself. Meetings will feature insightful talks, community building, and support for new ideas and concepts. Consciousness Hacking is an open exploration of how science and technology can support radically modern approaches to spirituality and human flourishing. We do this through a global network of communities, events, digital media and projects.",
        contribute: 'We need more people to join the community and attend our events. We are also looking for volunteers to help us organize our meetups.'
      }
    });
    groupTypes.set('movement', {
      label: 'Movement'
    });
    groupTypes.set('opensource', {
      label: 'Open Source Project',
      placeholders: {
        name: 'MochaJS',
        slug: 'mochajs',
        mission: 'We are on a mission to make asynchronous testing simple and fun.',
        description: 'Mocha is a feature-rich JavaScript test framework running on Node.js and the browser, making asynchronous testing simple and fun. Mocha tests run serially, allowing for flexible and accurate reporting, while mapping uncaught exceptions to the correct test cases.',
        contribute: 'We are looking for maintainers to help us deal with the large number of issues and pull requests. We also welcome financial contributions.'
      }
    });
    groupTypes.set('pta', {
      label: 'Parent Teacher Association'
    });
    groupTypes.set('studentclub', {
      label: 'Student Club'
    });
    groupTypes.set('default', {
      label: 'Other',
      placeholders: {
        name: 'The Mindful Collective',
        slug: 'mindfulcollective',
        mission: 'We are on a mission to bring mindfulness to our city',
        description: 'Every week, we organize a public sitting in silence. We invite everyone to come meditate with us. We are also giving a meditation class to the public school in our city.',
        contribute: 'We are looking for public school teachers who could open the door of their classroom. We are also looking for financial help to cover our expenses (printing posters, transportation, ...)',
        email: 'sn.goenka@dharma.org'
      }
    });

    const getPlaceholder = (attr) => {
      const placeholders = groupTypes.get(tags || 'default').placeholders || {};
      return placeholders[attr] || groupTypes.get('default').placeholders[attr];
    };

    const groupTypeBtns = [];
    groupTypes.forEach((value, key) => {
      const selected = (key === tags) ? 'selected' : '';
      groupTypeBtns.push(<button className={`GroupTypeBtn ${selected}`} onClick={() => appendGroupForm({tags:key})}>{value.label}</button>);
    });

    if (tags === 'opensource')
      return pushState(null, '/opensource/apply');

    const showGroupTypes = (!showConfirmation && !groupType);
    const showFormDetails = (!showConfirmation && (groupType || tags));

    const validateSlug = (slug) => {
      fetch(`/api/groups/${slug}`)
        .then(response => {
          this.setState( { slugAvailable: !(response.status === 200) })
        })
        .catch();
    }

    return (

        <Notification {...this.props} />
        <LoginTopBar />

          { hostCollective &&

{hostCollective.name}

}

Apply to create a collective

          {showGroupTypes &&

              What is your collective for?

                { groupTypeBtns }

          }

          {showFormDetails &&

                What is your collective name?
                <Input
                  name='name'
                  value={name}
                  onChange={(value) => appendGroupForm({name: value})}
                  maxLength={128}
                  rows={1}
                  placeholder={getPlaceholder('name')}/>

                What URL would you like to have?
                <Input
                  name='slug'
                  prepend='https://opencollective.com/'
                  value={slug}
                  onChange={(value) => appendGroupForm({slug: value})}
                  onBlur={validateSlug}
                  maxLength={32}
                  rows={1}
                  placeholder={getPlaceholder('slug')}/>
                { !slugAvailable && This URL is not available :-(}

                What is your collective's mission?
                <Input
                  name='mission'
                  value={mission}
                  onChange={(value) => appendGroupForm({mission: value})}
                  maxLength={100}
                  rows={2}
                  placeholder={getPlaceholder('mission')}/>

                How are you going to do that? Why should people join?

                Tell us more (you can always edit later)
                <Input
                  name='description'
                  value={description}
                  onChange={(value) => appendGroupForm({description: value})}
                  maxLength={512}
                  rows={5}
                  placeholder={getPlaceholder('description')} />

                What do you need help with? How can people contribute to your mission?
                <Input
                  name='contribute'
                  value={contribute}
                  onChange={(value) => appendGroupForm({contribute: value})}
                  maxLength={256}
                  rows={4}
                  placeholder={getPlaceholder('contribute')} />

                What is your personal email?
                <Input
                  email='email'
                  value={users[0].email}
                  onChange={(value) => appendGroupForm({users: [{email: value, role: 'ADMIN'}]})}
                  maxLength={128}
                  rows={1}
                  placeholder={getPlaceholder('email')}/>

                  <input type="checkbox" onChange={(event) => appendGroupForm({tos: event.target.checked})} />
                  By clicking here you agree to the terms of service

              <div className={`center AddGroup-Button ${slugAvailable ? '' : 'disabled'}`} onClick={ this.createRef }>submit

          }

          {showConfirmation &&
            <Confirmation>

Almost done!

To continue, click on the link that we sent you ({users[0].email}).

            </Confirmation>
          }

        <PublicFooter/>

    );
  }

  create() {
    const { newGroupForm, validateSchema, createGroup, utmSource, notify, hostCollective, hostId} = this.props;
    const attr = newGroupForm.attributes;
    let tags = attr.tags && attr.tags.split(',').map(x => x.trim());

    if (hostCollective && hostCollective.tags) {
      tags = uniq([...tags, ...hostCollective.tags]);
    } else {
      tags = uniq([...tags])
    }

    const group = {
      tos: attr.tos,
      name: attr.name,
      slug: attr.slug,
      mission: attr.mission,
      longDescription: attr.contribute ? `${attr.description}\n\n# Contribute\n\n${attr.contribute}` : attr.description,
      website: attr.website,
      hostId: hostId,
      hostFeePercent: hostCollective && hostCollective.settings.hostFeePercent || 5,
      data: {
        utmSource
      },
      tags,
      users: attr.users
    };

    return validateSchema(group, newGroupSchema)
      .then(() => createGroup(group))
      .then(() => this.setState({showConfirmation: true}))
      .catch(({message}) => notify('error', message));
  }

}

export default connect(mapStateToProps, {
  appendGroupForm,
  pushState,
  notify,
  createGroup,
  validateSchema
})(NewGroup);

export function mapStateToProps({router, form, groups}) {

  const hostCollective = (groups && router.params.slug) ? groups[router.params.slug] : null;
  const groupType = router.params.type;
  const query = router.location.query;
  const utmSource = query.utm_source;
  const hostId = hostCollective && groups[router.params.slug].id;

  return {
    newGroupForm: form.addgroup,
    hostCollective,
    utmSource,
    groupType,
    hostId
  };
}
<|endoftext|>


<commit_before><commit_msg>[REF] web: FormTests: move test to correct section<commit_after><|endoftext|>


<commit_before>window.lab = (function(lab){

     var fac = [666,[0,-1],[1,0],[0,1],[-1,0]];

    /**
     * takes a state object and serialises it
     * @param {Object} state
     * @returns {string} the serialised object
     */
    lab.serialiseState = function(state) {
        return JSON.stringify(state);
    };

    /**
     * Checks an array for a value
     * @param {Object} needle
     * @param {Array} haystack
     * @returns {bool} whether or not needle was found
     */
    lab.inArray = function(needle,haystack){
        for(var i in haystack){
            if (haystack[i]==needle){
                return true;
            }
        }
        return false;
    };

    /**
     * returns a deep-copied clone
     * @param {Object} obj
     * @returns {Object} clone
     */
    lab.cloneObj = function(obj){
        var clone = new obj.constructor();
        for(var p in obj){
            clone[p] = typeof obj[p] === "object" ? lab.cloneObj(obj[p]) : obj[p];
        }
        return clone;
    };

    lab.calculateBorderName = function(x,y,dir){
        if (dir==1 || dir==4){
            return (x+fac[dir][0])+","+(y+fac[dir][1])+(dir==1?"s":"e");
        }
        return x+","+y+(dir==2?"e":"s");
    };

    /**
     * finds all collisions for the given entity and builds collision objects
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @param {bool} before
     * @returns {array} collisions an array of collision objects
     */
    lab.findCollisions = function(lvl,state,entitykey,before){
        var collisions = [], // key: {obj1: xxx, obj2: xxx}
            dir = state.entities[entitykey].dir,
            x = state.entities[entitykey].x, // + before ? fac[dir][0] : 0,
            y = state.entities[entitykey].y, // + before ? fac[dir][1] : 0;
            type = lab.getEntityType(lvl,state,entitykey),
            otherkey,
            othertype;
        if (before){ // borders, and next square with nextto prop
            // find borders
            if (lab.inArray(lab.calculateBorderName(x,y,dir),lvl.borders)){ // hit a border!
                collisions.push({key:"BORDER"});
            }
            // find squares
            otherkey = (x+fac[dir][0])+","+(y+fac[dir][1]);
            othertype = lab.getSquareType(lvl,state,otherkey);
            if (othertype) { // hit a square!
                collision = lvl.collisions[entitykey + "-" + othertype];
                if (collision && lvl.collisions[collision].kind != "on") {
                    collisions.push({
                        key: collision,
                        'with': otherkey
                    });
                }
            }
            // TODO - also check for entities on next square with correct dir
        }
        else { // current square and entities on same square
            otherkey = x+","+y;
            othertype = lab.getSquareType(lvl,state,otherkey);
            collision = lvl.collisions[entitykey+"-"+othertype];
            if (collision && lvl.collisions[collision].kind == "on"){
                collisions.push({key: collision,'with':otherkey});
            }
            // TODO - also check here for entities on same square
        }
        for(var c in collisions){
            collisions[c].me = entitykey;
        }
        return collisions;
    };

    /**
     * performs the given collisions and returns updated state & anims objects
     * @param {Object} lvl
     * @param {Object} state
     * @param {Object} anims
     * @param {Object} collision
     * @returns {Object} an object containing updated state & anims
     */
    lab.performCollision = function(lvl,state,anims,collision){ // collision object contains key,obj1,obj2
        if (collision.key=="BORDER"){
            state.entities[collision.me].dir = 0;
        }
        // TODO - add support for non-border collisions
        return {
            state: state,
            anims: anims
        };
    };

    /**
     * takes a state object and removes all properties not to be included when saved (like in-move data)
     * @param {Object} lvl
     * @param {Object} state
     * @returns {Object} updatedstate
     */
    lab.removeNonSaveStateProperties = function(lvl,state){
        // TODO - need to remove more properties?
        for(var e in state.entities){
            delete state.entities[e].dir;
            delete state.entities[e].movestarted;
            if (state.entities.type == lvl.entities[e].type){
                delete state.entities.type; // only need to store type in state if different from starttype
            }
        }
        return state;
    };

    /**
     * calculates what type a given entity is at this moment
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @returns {string} type
     */
    lab.getEntityType = function(lvl, state, entitykey){
        var type;
        type = state.entities[entitykey].type || lvl.entities[entitykey].type;
        // TODO - add support for conditionals
        return type;
    };

    /**
     * calculates what type a given Square is at this moment
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} squarekey
     * @returns {string} type
     */
    lab.getSquareType = function(lvl, state, squarekey){
        var type;
        type = lvl.squares[squarekey];
        // TODO - add support for conditionals
        return type;
    };

    /**
     * Calculates what dir an entity would (eventually) start to move in when a move is made
     * @param {Object} lvl
     * @param {Object} state
     * @param {int} dir
     * @param {string} entitykey
     * @returns {int} dir
     */
    lab.getEntityStartDir = function(lvl, state, dir, entitykey){
        var ret = 0, type = lab.getEntityType(lvl,state,entitykey);
        if (type == "dead"){
            return 0;
        }
        if (lvl.types[type].move === "grav"){
            ret = dir;
        }
        // TODO - add support for float and other things
        return ret;
    };

    /**
     * Tests if an entity hasn't gone overe the edge
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @bool if entity is within level limits or not
     */
    lab.testIfOutOfBounds = function(lvl,state,entitykey){
        var x = state.entities[entitykey].x,
            y = state.entities[entitykey].y;
        return x> lvl.cols || x<1 || y > lvl.rows || y < 1;
    };

    /**
     * Takes a state and moves in a given direction
     * @param {Object} lvl
     * @param {Object} state
     * @param {int} dir The direction to move in (1-4)
     * @returns {Object} an object containing updated state & anims
     */
    lab.analyseMove = function(lvl, state, dir){
        var anims = {0:{}}, movestate = lab.cloneObj(state), step = 0, before = true, movedir, sthmoving;
        // set all startdirs
        for(var e in lvl.entities){
            movedir = lab.getEntityStartDir(lvl,state,dir,e);
            if (movedir){
                movestate.entities[e].dir = movedir;
                movestate.entities[e].movestarted = 0;
                anims[0].slides = anims[0].slides || {};
                anims[0].slides[e] = {x: movestate.entities[e].x, y: movestate.entities[e].y};
            }
        }
        do {
            sthmoving = false;
            for(e in lvl.entities){
                var entitydir = movestate.entities[e].dir;
                if (entitydir){
                    if (!before){
                        movestate.entities[e].x += fac[entitydir][0];
                        movestate.entities[e].y += fac[entitydir][1];
                        anims[movestate.entities[e].movestarted].slides[e].x = movestate.entities[e].x;
                        anims[movestate.entities[e].movestarted].slides[e].y = movestate.entities[e].y;
                    }
                    var collisions = lab.findCollisions(lvl,movestate,e,before);
                    for(var c in collisions){
                        var collisionresult = lab.performCollision(lvl,movestate,anims,collisions[c]);
                        movestate = collisionresult.state;
                        anims = collisionresult.anims;
                    }
                    if (lab.testIfOutOfBounds(lvl,movestate,e)){
                        movestate.entities[e].type = type = "dead";
                        movestate.entities[e].dir = 0;
                        anims[step] = anims[step] || {changes:{}};
                        anims[step].changes[e] = "dead";
                        movestate.end = "GAMEOVER"; // TODO - only do this if it was plr that died
                    }
                    if (movestate.entities[e].dir){ // we're still on the move! :)
                        sthmoving = true;
                    }
                    // TODO: update movestate & anims for new/changed moves
                }
            }
            step += before ? 0 : 1;
            before = !before;
        }
        while(sthmoving);
        return {
            state: lab.removeNonSaveStateProperties(lvl,movestate),
            anims: anims
        };
    };

    /**
     * Analyses a level, creating the full state structure with inbetween transitions
     * @param {Object} lvl
     * @returns {Object} analysis
     */
    lab.analyseLevel = function(lvl){
        return lab.analyse(lvl, lab.buildStartState(lvl), {statekeys: {nextkey:0},states:{}}, 1).analysis;
    };

    /**
     * Build a starting state object for a given level
     * @param {Object} lvl
     * @returns {Object} state
     */
    lab.buildStartState = function(lvl){
        var state = {entities:{}};
        for(var e in lvl.entities){
            state.entities[e] = {
                x : lvl.entities[e].x,
                y : lvl.entities[e].y
            };
        }
        // TODO - build start state for lvl
        return state;
    };

    /**
     * updates an analysis for the given state. will also recursively analyse new states reached.
     * @param {Object} lvl
     * @param {Object} state
     * @param {Object} analysis
     * @param {Number} step
     * @returns {Object} object containing updated analysis and key for the analysed state
     */
    lab.analyse = function(lvl, state, analysis, step){
console.log(step,state,analysis);
        var serialisedstate = lab.serialiseState(state), key = analysis.statekeys.nextkey++;
        analysis.states[key] = {
            state: state,
            moves: {},
            steps: step
        };
        analysis.statekeys[serialisedstate] = key;
        for (var d = 1; d <= 4; d++) {
console.log(step,key,d);
            var moveresult, targetserialised, targetkey, end;
            moveresult = lab.analyseMove(lvl, state, d);
            end = moveresult.state.end;
console.log(moveresult,end);
            if (!end) { // game did not end, so we reached another state
                targetserialised = lab.serialiseState(moveresult.state);
                targetkey = analysis.statekeys[targetserialised];
                if (!targetkey) { // reached unanalysed state
console.log("Gonna recurse!");
                    var stateresult = lab.analyse(lvl, moveresult.state, analysis, step + 1);
console.log("recurserd!",stateresult);
                    analysis = stateresult.analysis;
                    targetkey = stateresult.key;
                }
                analysis.states[targetkey].steps = Math.min(analysis.states[targetkey].steps,step);
            }
            analysis.states[key].moves[d] = {
                target: end ? end : targetkey,
                anims: moveresult.anims
            };
        }
        return {
            analysis: analysis,
            key: key
        };
    };

    return lab;

})(window.lab || {});

<commit_msg>removed debug code<commit_after>window.lab = (function(lab){

     var fac = [666,[0,-1],[1,0],[0,1],[-1,0]];

    /**
     * takes a state object and serialises it
     * @param {Object} state
     * @returns {string} the serialised object
     */
    lab.serialiseState = function(state) {
        return JSON.stringify(state);
    };

    /**
     * Checks an array for a value
     * @param {Object} needle
     * @param {Array} haystack
     * @returns {bool} whether or not needle was found
     */
    lab.inArray = function(needle,haystack){
        for(var i in haystack){
            if (haystack[i]==needle){
                return true;
            }
        }
        return false;
    };

    /**
     * returns a deep-copied clone
     * @param {Object} obj
     * @returns {Object} clone
     */
    lab.cloneObj = function(obj){
        var clone = new obj.constructor();
        for(var p in obj){
            clone[p] = typeof obj[p] === "object" ? lab.cloneObj(obj[p]) : obj[p];
        }
        return clone;
    };

    lab.calculateBorderName = function(x,y,dir){
        if (dir==1 || dir==4){
            return (x+fac[dir][0])+","+(y+fac[dir][1])+(dir==1?"s":"e");
        }
        return x+","+y+(dir==2?"e":"s");
    };

    /**
     * finds all collisions for the given entity and builds collision objects
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @param {bool} before
     * @returns {array} collisions an array of collision objects
     */
    lab.findCollisions = function(lvl,state,entitykey,before){
        var collisions = [], // key: {obj1: xxx, obj2: xxx}
            dir = state.entities[entitykey].dir,
            x = state.entities[entitykey].x, // + before ? fac[dir][0] : 0,
            y = state.entities[entitykey].y, // + before ? fac[dir][1] : 0;
            type = lab.getEntityType(lvl,state,entitykey),
            otherkey,
            othertype;
        if (before){ // borders, and next square with nextto prop
            // find borders
            if (lab.inArray(lab.calculateBorderName(x,y,dir),lvl.borders)){ // hit a border!
                collisions.push({key:"BORDER"});
            }
            // find squares
            otherkey = (x+fac[dir][0])+","+(y+fac[dir][1]);
            othertype = lab.getSquareType(lvl,state,otherkey);
            if (othertype) { // hit a square!
                collision = lvl.collisions[entitykey + "-" + othertype];
                if (collision && lvl.collisions[collision].kind != "on") {
                    collisions.push({
                        key: collision,
                        'with': otherkey
                    });
                }
            }
            // TODO - also check for entities on next square with correct dir
        }
        else { // current square and entities on same square
            otherkey = x+","+y;
            othertype = lab.getSquareType(lvl,state,otherkey);
            collision = lvl.collisions[entitykey+"-"+othertype];
            if (collision && lvl.collisions[collision].kind == "on"){
                collisions.push({key: collision,'with':otherkey});
            }
            // TODO - also check here for entities on same square
        }
        for(var c in collisions){
            collisions[c].me = entitykey;
        }
        return collisions;
    };

    /**
     * performs the given collisions and returns updated state & anims objects
     * @param {Object} lvl
     * @param {Object} state
     * @param {Object} anims
     * @param {Object} collision
     * @returns {Object} an object containing updated state & anims
     */
    lab.performCollision = function(lvl,state,anims,collision){ // collision object contains key,obj1,obj2
        if (collision.key=="BORDER"){
            state.entities[collision.me].dir = 0;
        }
        // TODO - add support for non-border collisions
        return {
            state: state,
            anims: anims
        };
    };

    /**
     * takes a state object and removes all properties not to be included when saved (like in-move data)
     * @param {Object} lvl
     * @param {Object} state
     * @returns {Object} updatedstate
     */
    lab.removeNonSaveStateProperties = function(lvl,state){
        // TODO - need to remove more properties?
        for(var e in state.entities){
            delete state.entities[e].dir;
            delete state.entities[e].movestarted;
            if (state.entities.type == lvl.entities[e].type){
                delete state.entities.type; // only need to store type in state if different from starttype
            }
        }
        return state;
    };

    /**
     * calculates what type a given entity is at this moment
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @returns {string} type
     */
    lab.getEntityType = function(lvl, state, entitykey){
        var type;
        type = state.entities[entitykey].type || lvl.entities[entitykey].type;
        // TODO - add support for conditionals
        return type;
    };

    /**
     * calculates what type a given Square is at this moment
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} squarekey
     * @returns {string} type
     */
    lab.getSquareType = function(lvl, state, squarekey){
        var type;
        type = lvl.squares[squarekey];
        // TODO - add support for conditionals
        return type;
    };

    /**
     * Calculates what dir an entity would (eventually) start to move in when a move is made
     * @param {Object} lvl
     * @param {Object} state
     * @param {int} dir
     * @param {string} entitykey
     * @returns {int} dir
     */
    lab.getEntityStartDir = function(lvl, state, dir, entitykey){
        var ret = 0, type = lab.getEntityType(lvl,state,entitykey);
        if (type == "dead"){
            return 0;
        }
        if (lvl.types[type].move === "grav"){
            ret = dir;
        }
        // TODO - add support for float and other things
        return ret;
    };

    /**
     * Tests if an entity hasn't gone overe the edge
     * @param {Object} lvl
     * @param {Object} state
     * @param {string} entitykey
     * @bool if entity is within level limits or not
     */
    lab.testIfOutOfBounds = function(lvl,state,entitykey){
        var x = state.entities[entitykey].x,
            y = state.entities[entitykey].y;
        return x> lvl.cols || x<1 || y > lvl.rows || y < 1;
    };

    /**
     * Takes a state and moves in a given direction
     * @param {Object} lvl
     * @param {Object} state
     * @param {int} dir The direction to move in (1-4)
     * @returns {Object} an object containing updated state & anims
     */
    lab.analyseMove = function(lvl, state, dir){
        var anims = {0:{}}, movestate = lab.cloneObj(state), step = 0, before = true, movedir, sthmoving;
        // set all startdirs
        for(var e in lvl.entities){
            movedir = lab.getEntityStartDir(lvl,state,dir,e);
            if (movedir){
                movestate.entities[e].dir = movedir;
                movestate.entities[e].movestarted = 0;
                anims[0].slides = anims[0].slides || {};
                anims[0].slides[e] = {x: movestate.entities[e].x, y: movestate.entities[e].y};
            }
        }
        do {
            sthmoving = false;
            for(e in lvl.entities){
                var entitydir = movestate.entities[e].dir;
                if (entitydir){
                    if (!before){
                        movestate.entities[e].x += fac[entitydir][0];
                        movestate.entities[e].y += fac[entitydir][1];
                        anims[movestate.entities[e].movestarted].slides[e].x = movestate.entities[e].x;
                        anims[movestate.entities[e].movestarted].slides[e].y = movestate.entities[e].y;
                    }
                    var collisions = lab.findCollisions(lvl,movestate,e,before);
                    for(var c in collisions){
                        var collisionresult = lab.performCollision(lvl,movestate,anims,collisions[c]);
                        movestate = collisionresult.state;
                        anims = collisionresult.anims;
                    }
                    if (lab.testIfOutOfBounds(lvl,movestate,e)){
                        movestate.entities[e].type = type = "dead";
                        movestate.entities[e].dir = 0;
                        anims[step] = anims[step] || {changes:{}};
                        anims[step].changes[e] = "dead";
                        movestate.end = "GAMEOVER"; // TODO - only do this if it was plr that died
                    }
                    if (movestate.entities[e].dir){ // we're still on the move! :)
                        sthmoving = true;
                    }
                    // TODO: update movestate & anims for new/changed moves
                }
            }
            step += before ? 0 : 1;
            before = !before;
        }
        while(sthmoving);
        return {
            state: lab.removeNonSaveStateProperties(lvl,movestate),
            anims: anims
        };
    };

    /**
     * Analyses a level, creating the full state structure with inbetween transitions
     * @param {Object} lvl
     * @returns {Object} analysis
     */
    lab.analyseLevel = function(lvl){
        return lab.analyse(lvl, lab.buildStartState(lvl), {statekeys: {nextkey:0},states:{}}, 1).analysis;
    };

    /**
     * Build a starting state object for a given level
     * @param {Object} lvl
     * @returns {Object} state
     */
    lab.buildStartState = function(lvl){
        var state = {entities:{}};
        for(var e in lvl.entities){
            state.entities[e] = {
                x : lvl.entities[e].x,
                y : lvl.entities[e].y
            };
        }
        // TODO - build start state for lvl
        return state;
    };

    /**
     * updates an analysis for the given state. will also recursively analyse new states reached.
     * @param {Object} lvl
     * @param {Object} state
     * @param {Object} analysis
     * @param {Number} step
     * @returns {Object} object containing updated analysis and key for the analysed state
     */
    lab.analyse = function(lvl, state, analysis, step){
        var serialisedstate = lab.serialiseState(state), key = analysis.statekeys.nextkey++;
        analysis.states[key] = {
            state: state,
            moves: {},
            steps: step
        };
        analysis.statekeys[serialisedstate] = key;
        for (var d = 1; d <= 4; d++) {
            var moveresult, targetserialised, targetkey, end;
            moveresult = lab.analyseMove(lvl, state, d);
            end = moveresult.state.end;
            if (!end) { // game did not end, so we reached another state
                targetserialised = lab.serialiseState(moveresult.state);
                targetkey = analysis.statekeys[targetserialised];
                if (!targetkey) { // reached unanalysed state
                    var stateresult = lab.analyse(lvl, moveresult.state, analysis, step + 1);
                    analysis = stateresult.analysis;
                    targetkey = stateresult.key;
                }
                analysis.states[targetkey].steps = Math.min(analysis.states[targetkey].steps,step);
            }
            analysis.states[key].moves[d] = {
                target: end ? end : targetkey,
                anims: moveresult.anims
            };
        }
        return {
            analysis: analysis,
            key: key
        };
    };

    return lab;

})(window.lab || {});

<|endoftext|>


<commit_before>import j from 'jscodeshift'
import _ from 'lodash'

const getObject = (node) => (
  _.get(node, 'callee.object.name') || _.get(node, 'value.callee.object.name')
)
const getProperty = (node) => (
  _.get(node, 'callee.property.name') || _.get(node, 'value.callee.property.name')
)
const getArgs = (node) => _.get(node, 'arguments') || _.get(node, 'value.arguments')
const getSourceLocation = (node) => {
  let loc = _.get(node, 'loc') || _.get(node, 'value.loc')
  if (loc == null) {
    loc = _.get(node, 'parentPath.value.loc')
  }
  return {
    start: loc && loc.start,
    end: loc && loc.end,
  }
}
const getSimilarity = (node, object, property, args = []) => {
  let sameArgs = args.length == 0
  args = args || []
  const sameObject = getObject(node) == object
  const sameProperty = getProperty(node) == property
  if (!sameArgs) {
    sameArgs = getArgs(node).reduce((sameSoFar, node, i) => {
      if (!sameSoFar) return false
      return args.length > i ? nodeHasValue(node, args[i].value) : false
    }, true)
  }
  const obj = getObject(node)
  const prop = getProperty(node)
  const argu = getArgs(node)

  return { sameObject, sameProperty, sameArgs, }
}

const createArgumentNodes = (args = []) => args.map((arg) => {
  switch (arg.type) {
    case 'Identifier': {
      return j.identifier(arg.value)
    }
    case 'MemberExpression': {
      return j.memberExpression(
        j.identifier(arg.object),
        j.identifier(arg.property)
      )
    }
    default: {
      return j.literal(arg.value || "")
    }
  }
})

const inverseCreateArgumentNodes = (args = []) => args.map((arg) => {
  switch (arg.type) {
    case 'MemberExpression': {
      return {
        type: arg.type,
        object: arg.object.name,
        property: arg.property.name,
      }
    }
    case 'Identifier': {
      return {
        type: arg.type,
        value: arg.name,
      }
    }
    case 'Literal': {
      return {
        type: arg.type,
        value: arg.value,
      }
    }
    default: {
      return {}
    }
  }
})

const createExpressionStatement = (object, property, args) => (
  j.expressionStatement(j.callExpression(j.memberExpression(
    j.identifier(object), j.identifier(property)
  ), createArgumentNodes(args)))
)

/**
 * Finds the last call to a particular function object.property in
 * an ast, and returns the range of that last call
 */
const getLastMatchingExpressionRange = (ast, object, property) => {
  const matchingExpressions = ast.find(j.ExpressionStatement).paths().filter((node) => {
    const {sameObject, sameProperty, } = getSimilarity(
      node.value.expression, object, property
    )
    return (sameObject && sameProperty)
  })
  if (matchingExpressions.length === 0) return null
  return matchingExpressions[matchingExpressions.length - 1].value.range
}

const nodeHasValue = (node, value) => {
  switch (node.type) {
    case 'Literal': {
      return _.get(node, 'value') == value
    }
    case 'Identifier': {
      return _.get(node, 'name') == value
    }
    default: {
      return false
    }
  }
}

/**
 * Adds a function call as 'object.property(...args)'
 * immediately after the last call of the same funciton,
 * or at the top of the file if there isn't another call.
 *
 * @param  {String} object   Name of the object to call function on
 * @param  {String} property Name of the function on object to call
 * @param  {Array} args      List of Objects { type: 'Literal'|'Identifier', value: String }
 *                           if args is missing then it is a function call without arguments
 * @return {Collection}      Updated Collection
 */
export const addFunctionCall = function(object, property, args) {
  const rangeOfLastMatching = getLastMatchingExpressionRange(this, object, property)
  const body = _.get(this.nodes(), '[0].program.body')
  let newFunctionIndex = 0
  if (rangeOfLastMatching) {
    newFunctionIndex = _.map(body, 'range').indexOf(rangeOfLastMatching) + 1
  }

  const node = createExpressionStatement(object, property, args)
  body.splice(newFunctionIndex, 0, node)
  return this
}

/**
 * Updates the arguments of a function who matches on 'object.property'
 * @param Same as addFunctionCall
 * @return {Collection}      Updated Collection
 */
export const updateFunctionCall = function(object, property, args) {
  this.find(j.CallExpression).paths().filter((node) => {
    const {sameObject, sameProperty, } = getSimilarity(
      node, object, property, args
    )
    return (sameObject && sameProperty)
  }).map((node) => {
    _.set(node, 'value.arguments', createArgumentNodes(args))
  })
  return this
}

/**
 * Removes all functions that exactly match the description of 'object.property(...args)'
 * If args is undefined, it matches only on 'object.property'
 *
 * @param Same as addFunctionCall
 * @return Same as addFunctionCall
 */
export const removeFunctionCall = function(object, property, args) {
  const program = _.get(this.nodes(), '[0].program', {})
  if (program.body) {
    program.body = program.body.filter((node) => {
      if (node.type == 'ExpressionStatement') {
        const { sameObject, sameProperty, sameArgs, } = getSimilarity(
          node.expression, object, property, args
        )
        return (!sameObject || !sameProperty || !sameArgs)
      }
      return true
    })
  }
  return this
}

/**
 * Gets a list of all function calls matching on 'object.property(..args)'
 * If args is undefined, it matches only on 'object.property'
 * @param Same as addFunctionCall
 * @return {Array}  of type
 * [{
 * 	object: {String},
 * 	property: {String},
 *  source: { start: { line, column }, end: { line, column }}
 * 	args: [{
 * 	  // Format for type MemberExpression
 * 		type: 'MemberExpression',
 * 		object: {String},
 * 		property: {String},
 * 		args: {Array} (of same type of args)
 * 	}, {
 * 		// Format for type Identifier
 * 		type: 'Identifier',
 * 		value: {String}
 * 	}, {
 * 		// Format for type Literal
 *   	type: 'Literal',
 *   	value: {String}
 * 	}, {
 * 	...
 * 	}]
 * }]
 */
export const getAllMatchingFunctionCalls = function(object, property, args) {
  return this.find(j.CallExpression).paths().filter((node) => {
    const {sameObject, sameProperty, sameArgs, } = getSimilarity(
      node, object, property, args
    )
    return (sameObject && sameProperty && sameArgs)
  }).map((node) => {
    return {
      object: getObject(node),
      property: getProperty(node),
      source: getSourceLocation(node),
      args: inverseCreateArgumentNodes(getArgs(node)),
    }
  })
}
<commit_msg>default functions to end rather than beginning of file<commit_after>import j from 'jscodeshift'
import _ from 'lodash'

const getObject = (node) => (
  _.get(node, 'callee.object.name') || _.get(node, 'value.callee.object.name')
)
const getProperty = (node) => (
  _.get(node, 'callee.property.name') || _.get(node, 'value.callee.property.name')
)
const getArgs = (node) => _.get(node, 'arguments') || _.get(node, 'value.arguments')
const getSourceLocation = (node) => {
  let loc = _.get(node, 'loc') || _.get(node, 'value.loc')
  if (loc == null) {
    loc = _.get(node, 'parentPath.value.loc')
  }
  return {
    start: loc && loc.start,
    end: loc && loc.end,
  }
}
const getSimilarity = (node, object, property, args = []) => {
  let sameArgs = args.length == 0
  args = args || []
  const sameObject = getObject(node) == object
  const sameProperty = getProperty(node) == property
  if (!sameArgs) {
    sameArgs = getArgs(node).reduce((sameSoFar, node, i) => {
      if (!sameSoFar) return false
      return args.length > i ? nodeHasValue(node, args[i].value) : false
    }, true)
  }
  const obj = getObject(node)
  const prop = getProperty(node)
  const argu = getArgs(node)

  return { sameObject, sameProperty, sameArgs, }
}

const createArgumentNodes = (args = []) => args.map((arg) => {
  switch (arg.type) {
    case 'Identifier': {
      return j.identifier(arg.value)
    }
    case 'MemberExpression': {
      return j.memberExpression(
        j.identifier(arg.object),
        j.identifier(arg.property)
      )
    }
    default: {
      return j.literal(arg.value || "")
    }
  }
})

const inverseCreateArgumentNodes = (args = []) => args.map((arg) => {
  switch (arg.type) {
    case 'MemberExpression': {
      return {
        type: arg.type,
        object: arg.object.name,
        property: arg.property.name,
      }
    }
    case 'Identifier': {
      return {
        type: arg.type,
        value: arg.name,
      }
    }
    case 'Literal': {
      return {
        type: arg.type,
        value: arg.value,
      }
    }
    default: {
      return {}
    }
  }
})

const createExpressionStatement = (object, property, args) => (
  j.expressionStatement(j.callExpression(j.memberExpression(
    j.identifier(object), j.identifier(property)
  ), createArgumentNodes(args)))
)

/**
 * Finds the last call to a particular function object.property in
 * an ast, and returns the range of that last call
 */
const getLastMatchingExpressionRange = (ast, object, property) => {
  const matchingExpressions = ast.find(j.ExpressionStatement).paths().filter((node) => {
    const {sameObject, sameProperty, } = getSimilarity(
      node.value.expression, object, property
    )
    return (sameObject && sameProperty)
  })
  if (matchingExpressions.length === 0) return null
  return matchingExpressions[matchingExpressions.length - 1].value.range
}

const nodeHasValue = (node, value) => {
  switch (node.type) {
    case 'Literal': {
      return _.get(node, 'value') == value
    }
    case 'Identifier': {
      return _.get(node, 'name') == value
    }
    default: {
      return false
    }
  }
}

/**
 * Adds a function call as 'object.property(...args)'
 * immediately after the last call of the same funciton,
 * or at the top of the file if there isn't another call.
 *
 * @param  {String} object   Name of the object to call function on
 * @param  {String} property Name of the function on object to call
 * @param  {Array} args      List of Objects { type: 'Literal'|'Identifier', value: String }
 *                           if args is missing then it is a function call without arguments
 * @return {Collection}      Updated Collection
 */
export const addFunctionCall = function(object, property, args) {
  const rangeOfLastMatching = getLastMatchingExpressionRange(this, object, property)
  const body = _.get(this.nodes(), '[0].program.body')
  let newFunctionIndex = body.length
  if (rangeOfLastMatching) {
    newFunctionIndex = _.map(body, 'range').indexOf(rangeOfLastMatching) + 1
  }

  const node = createExpressionStatement(object, property, args)
  body.splice(newFunctionIndex, 0, node)
  return this
}

/**
 * Updates the arguments of a function who matches on 'object.property'
 * @param Same as addFunctionCall
 * @return {Collection}      Updated Collection
 */
export const updateFunctionCall = function(object, property, args) {
  this.find(j.CallExpression).paths().filter((node) => {
    const {sameObject, sameProperty, } = getSimilarity(
      node, object, property, args
    )
    return (sameObject && sameProperty)
  }).map((node) => {
    _.set(node, 'value.arguments', createArgumentNodes(args))
  })
  return this
}

/**
 * Removes all functions that exactly match the description of 'object.property(...args)'
 * If args is undefined, it matches only on 'object.property'
 *
 * @param Same as addFunctionCall
 * @return Same as addFunctionCall
 */
export const removeFunctionCall = function(object, property, args) {
  const program = _.get(this.nodes(), '[0].program', {})
  if (program.body) {
    program.body = program.body.filter((node) => {
      if (node.type == 'ExpressionStatement') {
        const { sameObject, sameProperty, sameArgs, } = getSimilarity(
          node.expression, object, property, args
        )
        return (!sameObject || !sameProperty || !sameArgs)
      }
      return true
    })
  }
  return this
}

/**
 * Gets a list of all function calls matching on 'object.property(..args)'
 * If args is undefined, it matches only on 'object.property'
 * @param Same as addFunctionCall
 * @return {Array}  of type
 * [{
 * 	object: {String},
 * 	property: {String},
 *  source: { start: { line, column }, end: { line, column }}
 * 	args: [{
 * 	  // Format for type MemberExpression
 * 		type: 'MemberExpression',
 * 		object: {String},
 * 		property: {String},
 * 		args: {Array} (of same type of args)
 * 	}, {
 * 		// Format for type Identifier
 * 		type: 'Identifier',
 * 		value: {String}
 * 	}, {
 * 		// Format for type Literal
 *   	type: 'Literal',
 *   	value: {String}
 * 	}, {
 * 	...
 * 	}]
 * }]
 */
export const getAllMatchingFunctionCalls = function(object, property, args) {
  return this.find(j.CallExpression).paths().filter((node) => {
    const {sameObject, sameProperty, sameArgs, } = getSimilarity(
      node, object, property, args
    )
    return (sameObject && sameProperty && sameArgs)
  }).map((node) => {
    return {
      object: getObject(node),
      property: getProperty(node),
      source: getSourceLocation(node),
      args: inverseCreateArgumentNodes(getArgs(node)),
    }
  })
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var chalk = require('chalk');

/**
 * The paginator keep trakcs of a pointer index in a list and return
 * a subset of the choices if the list is too long.
 */

var Paginator = module.exports = function () {
  this.pointer = 0;
  this.lastIndex = 0;
};

Paginator.prototype.paginate = function (output, active, pageSize) {
  pageSize = pageSize || 7;
  var lines = output.split('\n');

  // Make sure there's enough lines to paginate
  if (lines.length <= pageSize + 2) {
    return output;
  }

  // Move the pointer only when the user go down and limit it to 3
  if (this.pointer < 3 && this.lastIndex < active && active - this.lastIndex < 9) {
    this.pointer = Math.min(3, this.pointer + active - this.lastIndex);
  }
  this.lastIndex = active;

  // Duplicate the lines so it give an infinite list look
  var infinite = _.flatten([lines, lines, lines]);
  var topIndex = Math.max(0, active + lines.length - this.pointer);

  var section = infinite.splice(topIndex, pageSize).join('\n');
  return section + '\n' + chalk.dim('(Move up and down to reveal more choices)');
};
<commit_msg>Scroller fixed to middle of list (#496)<commit_after>'use strict';

var _ = require('lodash');
var chalk = require('chalk');

/**
 * The paginator keep trakcs of a pointer index in a list and return
 * a subset of the choices if the list is too long.
 */

var Paginator = module.exports = function () {
  this.pointer = 0;
  this.lastIndex = 0;
};

Paginator.prototype.paginate = function (output, active, pageSize) {
  pageSize = pageSize || 7;
  var middleOfList = Math.floor(pageSize / 2);
  var lines = output.split('\n');

  // Make sure there's enough lines to paginate
  if (lines.length <= pageSize + 2) {
    return output;
  }

  // Move the pointer only when the user go down and limit it to the middle of the list
  if (this.pointer < middleOfList && this.lastIndex < active && active - this.lastIndex < 9) {
    this.pointer = Math.min(middleOfList, this.pointer + active - this.lastIndex);
  }
  this.lastIndex = active;

  // Duplicate the lines so it give an infinite list look
  var infinite = _.flatten([lines, lines, lines]);
  var topIndex = Math.max(0, active + lines.length - this.pointer);

  var section = infinite.splice(topIndex, pageSize).join('\n');
  return section + '\n' + chalk.dim('(Move up and down to reveal more choices)');
};
<|endoftext|>