<commit_before>/**
 * DEVELOPED BY
 * GIL LOPES BUENO
 * gilbueno.mail@gmail.com
 *
 * WORKS WITH:
 * IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Rhino 1.7+
 *
 * FORK:
 * https://gist.github.com/1627705
 */

var $watchjs$ = {
    isFunction: function(functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call(functionToCheck) == '[object Function]';
    },

    isInt: function(x) {
        var y = parseInt(x);
        if (isNaN(y)) return false;
        return x == y && x.toString() == y.toString();
    },
    isArray: function(obj) {
        return Object.prototype.toString.call(obj) === '[object Array]';
    },
    defineGetAndSet: function(obj, propName, getter, setter){
        try{
                Object.defineProperty(obj, propName, {
                        get: getter,
                        set: setter,
                        enumerable: true,
                        configurable: true
                });
        }catch(error){
            try{
                Object.prototype.__defineGetter__.call(obj, propName, getter);
                Object.prototype.__defineSetter__.call(obj, propName, setter);
            }catch(error2){
                throw "watchJS error: browser not supported :/"
            }
        }
    },
    defineProp: function(obj, propName, value){
        try{
            Object.defineProperty(obj, propName, {
                    enumerable: false
                , configurable: true
                , writable: false
                , value: value
                });
        }catch(error){
            obj[propName] = value;
        }
    }
};

$watchjs$.defineProp(Object.prototype, "watch", function() {

    if (arguments.length == 1)
        this.watchAll.apply(this, arguments);
    else if ($watchjs$.isArray(arguments[0]))
        this.watchMany.apply(this, arguments);
    else
        this.watchOne.apply(this, arguments);

});

$watchjs$.defineProp(Object.prototype, "watchAll", function(watcher) {

    var obj = this;

    if (obj instanceof String || (!(obj instanceof Object) && !$watchjs$.isArray(obj))) //accepts only objects and array (not string)
        return;

    var props = [];

    if($watchjs$.isArray(obj)){
        for (var prop=0; prop<obj.length; prop++) { //for each item if obj is an array
            props.push(prop); //put in the props
        }
    }else{
        for (var prop2 in obj) { //for each attribute if obj is an object
            props.push(prop2); //put in the props
        }
    }

    obj.watchMany(props, watcher); //watch all itens of the props
});

$watchjs$.defineProp(Object.prototype, "watchMany", function(props, watcher) {
    var obj = this;

    if($watchjs$.isArray(obj)){
        for (var prop in props) { //watch each iten of "props" if is an array
            obj.watchOne(props[prop], watcher);
        }
    }else{
        for (var prop2 in props) { //watch each attribute of "props" if is an object
            obj.watchOne(props[prop2], watcher);
        }
    }
});

$watchjs$.defineProp(Object.prototype, "watchOne", function(prop, watcher) {
    var obj = this;

    var val = obj[prop];

    if(obj[prop]===undefined || $watchjs$.isFunction(obj[prop])) //dont watch if it is null or a function
        return;

    if(obj[prop]!=null)
        obj[prop].watchAll(watcher); //recursively watch all attributes of this

    obj.watchFunctions(prop);

    if (!obj.watchers) {
        $watchjs$.defineProp(obj, "watchers", {});
    }

    if (!obj.watchers[prop])
        obj.watchers[prop] = [];

    obj.watchers[prop].push(watcher); //add the new watcher in the watchers array

    var getter = function() {
        return val;
    };

    var setter = function(newval) {
        var oldval = val;
        val = newval;

        if(obj[prop])
            obj[prop].watchAll(watcher);

        obj.watchFunctions(prop);

        if (JSON.stringify(oldval) != JSON.stringify(newval) && arguments.callee.caller != watcher) {
            obj.callWatchers(prop);
        }
    };

        $watchjs$.defineGetAndSet(obj, prop, getter, setter);

});

$watchjs$.defineProp(Object.prototype, "callWatchers", function(prop) {
    var obj = this;

    for (var wr in obj.watchers[prop]) {
        if ($watchjs$.isInt(wr)){
            obj.watchers[prop][wr]();
        }
    }
});

$watchjs$.defineProp(Object.prototype, "watchFunctions", function(prop) {
    var obj = this;

    if((!obj[prop]) || (obj[prop] instanceof String) || (!$watchjs$.isArray(obj[prop])))
        return;

    var originalpop = obj[prop].pop;
    $watchjs$.defineProp(obj[prop], "pop", function() {
        var response = originalpop.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalpush = obj[prop].push;
    $watchjs$.defineProp(obj[prop], "push",  function() {
        var response = originalpush.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalreverse = obj[prop].reverse;
    $watchjs$.defineProp(obj[prop], "reverse", function() {
        var response = originalreverse.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalshift = obj[prop].shift;
    $watchjs$.defineProp(obj[prop], "shift", function() {
        var response = originalshift.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalsort = obj[prop].sort;
    $watchjs$.defineProp(obj[prop], "sort",  function() {
        var response = originalsort.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalslice = obj[prop].slice;
    $watchjs$.defineProp(obj[prop], "slice",  function() {
        var response = originalslice.apply(this, arguments);

        obj.watchOne(obj[prop]);

        return response;
    });

    var originalunshift = obj[prop].unshift;
    $watchjs$.defineProp(obj[prop], "unshift", function() {
        var response = originalunshift.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

});<commit_msg>Fix for issues #1 and #2.<commit_after>/**
 * DEVELOPED BY
 * GIL LOPES BUENO
 * gilbueno.mail@gmail.com
 *
 * WORKS WITH:
 * IE 9+, FF 4+, SF 5+, WebKit, CH 7+, OP 12+, BESEN, Rhino 1.7+
 *
 * FORK:
 * https://gist.github.com/1627705
 */

var $watchjs$ = {
    isFunction: function(functionToCheck) {
        var getType = {};
        return functionToCheck && getType.toString.call(functionToCheck) == '[object Function]';
    },

    isInt: function(x) {
        var y = parseInt(x);
        if (isNaN(y)) return false;
        return x == y && x.toString() == y.toString();
    },
    isArray: function(obj) {
        return Object.prototype.toString.call(obj) === '[object Array]';
    },
    defineGetAndSet: function(obj, propName, getter, setter){
        try{
                Object.defineProperty(obj, propName, {
                        get: getter,
                        set: setter,
                        enumerable: true,
                        configurable: true
                });
        }catch(error){
            try{
                Object.prototype.__defineGetter__.call(obj, propName, getter);
                Object.prototype.__defineSetter__.call(obj, propName, setter);
            }catch(error2){
                throw "watchJS error: browser not supported :/"
            }
        }
    },
    defineProp: function(obj, propName, value){
        try{
            Object.defineProperty(obj, propName, {
                    enumerable: false
                , configurable: true
                , writable: false
                , value: value
                });
        }catch(error){
            obj[propName] = value;
        }
    }
};

$watchjs$.defineProp(Object.prototype, "watch", function() {

    if (arguments.length == 1)
        this.watchAll.apply(this, arguments);
    else if ($watchjs$.isArray(arguments[0]))
        this.watchMany.apply(this, arguments);
    else
        this.watchOne.apply(this, arguments);

});

$watchjs$.defineProp(Object.prototype, "watchAll", function(watcher) {

    var obj = this;

    if (obj instanceof String || (!(obj instanceof Object) && !$watchjs$.isArray(obj))) //accepts only objects and array (not string)
        return;

    var props = [];

    if($watchjs$.isArray(obj)){
        for (var prop=0; prop<obj.length; prop++) { //for each item if obj is an array
            props.push(prop); //put in the props
        }
    }else{
        for (var prop2 in obj) { //for each attribute if obj is an object
            props.push(prop2); //put in the props
        }
    }

    obj.watchMany(props, watcher); //watch all itens of the props
});

$watchjs$.defineProp(Object.prototype, "watchMany", function(props, watcher) {
    var obj = this;

    if($watchjs$.isArray(obj)){
        for (var prop in props) { //watch each iten of "props" if is an array
            obj.watchOne(props[prop], watcher);
        }
    }else{
        for (var prop2 in props) { //watch each attribute of "props" if is an object
            obj.watchOne(props[prop2], watcher);
        }
    }
});

$watchjs$.defineProp(Object.prototype, "watchOne", function(prop, watcher) {
    var obj = this;

    var val = obj[prop];

    if(obj[prop]===undefined || $watchjs$.isFunction(obj[prop])) //dont watch if it is null or a function
        return;

    if(obj[prop]!=null)
        obj[prop].watchAll(watcher); //recursively watch all attributes of this

    obj.watchFunctions(prop);

    if (!obj.watchers) {
        $watchjs$.defineProp(obj, "watchers", {});
    }

    if (!obj.watchers[prop])
        obj.watchers[prop] = [];

    obj.watchers[prop].push(watcher); //add the new watcher in the watchers array

    var getter = function() {
        return val;
    };

    var setter = function(newval) {
        var oldval = val;
        val = newval;

        if(obj[prop])
            obj[prop].watchAll(watcher);

        obj.watchFunctions(prop);

        if (JSON.stringify(oldval) != JSON.stringify(newval)) {
            if (!$watchjs$.watcherInvocationRunning){
                $watchjs$.watcherInvocationRunning = true;
                obj.callWatchers(prop);
                $watchjs$.watcherInvocationRunning = false;
            }
        }
    };

        $watchjs$.defineGetAndSet(obj, prop, getter, setter);

});

$watchjs$.defineProp(Object.prototype, "callWatchers", function(prop) {
    var obj = this;

    for (var wr in obj.watchers[prop]) {
        if ($watchjs$.isInt(wr)){
            obj.watchers[prop][wr]();
        }
    }
});

$watchjs$.defineProp(Object.prototype, "watchFunctions", function(prop) {
    var obj = this;

    if((!obj[prop]) || (obj[prop] instanceof String) || (!$watchjs$.isArray(obj[prop])))
        return;

    var originalpop = obj[prop].pop;
    $watchjs$.defineProp(obj[prop], "pop", function() {
        var response = originalpop.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalpush = obj[prop].push;
    $watchjs$.defineProp(obj[prop], "push",  function() {
        var response = originalpush.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalreverse = obj[prop].reverse;
    $watchjs$.defineProp(obj[prop], "reverse", function() {
        var response = originalreverse.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalshift = obj[prop].shift;
    $watchjs$.defineProp(obj[prop], "shift", function() {
        var response = originalshift.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalsort = obj[prop].sort;
    $watchjs$.defineProp(obj[prop], "sort",  function() {
        var response = originalsort.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

    var originalslice = obj[prop].slice;
    $watchjs$.defineProp(obj[prop], "slice",  function() {
        var response = originalslice.apply(this, arguments);

        obj.watchOne(obj[prop]);

        return response;
    });

    var originalunshift = obj[prop].unshift;
    $watchjs$.defineProp(obj[prop], "unshift", function() {
        var response = originalunshift.apply(this, arguments);

        obj.watchOne(obj[prop]);
        obj.callWatchers(prop);

        return response;
    });

});
<|endoftext|>


<commit_before>// Description:
//  Look up names and contact info of primary officers and
//  committee heads within the SSE
//
// Configuration:
//  SSE_API_ROOT - root to the api
//
// Commands:
//  hubot sse whois <position> - See all SSE lingo entries
'use strict';

var api = require('./api')();
var Officers = api.Officers;
var Users = api.Users;

function getWhoisResponse(robot, msg, position) {
  Officers
    .all({ email: position, active: true })
    .then(function handle(body) {
      if (res.body.total === 0) {
        robot.send({ room: msg.envelope.user.name }, 'Unrecognized position: ' +
          position + '. `<position>` can be any of the following SSE positions:\n' +
          'president, vp, treasurer, secretary, mentoring' +
          ', projects, events, fundraising, technology, winter ball, ' +
          'pr, lab ops');
      } else {
        var officer = body.data[0];

        Users
          .one(officer.userDce)
          .then(function handleUser(userRes) {
            var user = userRes.body;
            robot.send({ room: msg.envelope.user.name }, position + ': ' + user.firstName + ' ' +
              user.lastName);
          });
      }
    });
}

module.exports = function listener(robot) {
  var listenerMetadata = {
    id: 'sse.whois',
    help: ['hubot sse whois <position> - See all SSE lingo entries'],
  };

  robot.respond(/sse whois (.+)/i, listenerMetadata, function handle(msg) {
    getWhoisResponse(robot, msg, msg.match[1]);
  });
};
<commit_msg>OKAY FOR REAL<commit_after>// Description:
//  Look up names and contact info of primary officers and
//  committee heads within the SSE
//
// Configuration:
//  SSE_API_ROOT - root to the api
//
// Commands:
//  hubot sse whois <position> - See all SSE lingo entries
'use strict';

var api = require('./api')();
var Officers = api.Officers;
var Users = api.Users;

function getWhoisResponse(robot, msg, position) {
  Officers
    .all({ email: position, active: true })
    .then(function handle(body) {
      if (body.total === 0) {
        robot.send({ room: msg.envelope.user.name }, 'Unrecognized position: ' +
          position + '. `<position>` can be any of the following SSE positions:\n' +
          'president, vp, treasurer, secretary, mentoring' +
          ', projects, events, fundraising, technology, winter ball, ' +
          'pr, lab ops');
      } else {
        var officer = body.data[0];

        Users
          .one(officer.userDce)
          .then(function handleUser(userRes) {
            var user = userRes.body;
            robot.send({ room: msg.envelope.user.name }, position + ': ' + user.firstName + ' ' +
              user.lastName);
          });
      }
    });
}

module.exports = function listener(robot) {
  var listenerMetadata = {
    id: 'sse.whois',
    help: ['hubot sse whois <position> - See all SSE lingo entries'],
  };

  robot.respond(/sse whois (.+)/i, listenerMetadata, function handle(msg) {
    getWhoisResponse(robot, msg, msg.match[1]);
  });
};
<|endoftext|>


<commit_before>'use strict';

import uniques from 'uniques';
import type from 'type-of';
import contains from 'string-contains';
import merge from 'deepmerge';

let makeShallow = (subject) => {
  let resp = {};

  for(let keyChain in subject){
    let shallow = false;

    for(let keyChain2 in subject){
      if(keyChain !== keyChain2 && keyChain2.indexOf(keyChain) === 0) {
        shallow = true;
      }
    }

    if(!shallow) {
      resp[keyChain] = subject[keyChain];
    }
  }
  return resp;
};

/**
 * Returns an objob object
 *
 * @param {(object|object[])} subject
 * @returns {(object|object[])}
 */
let ob = function (subject) {

  return {
    deselect: function(keys = []){
      let allKeys = ob(ob(subject).flatten()).keys();
      let keysToKeep = [];

      for( let subjectKey of allKeys ) {
        let keepKey = true;

        for( let keyToRemove of keys ){
          if(subjectKey === keyToRemove){
            keepKey = false;
          }
        }

        if(keepKey){
          keysToKeep.push(subjectKey);
        }
      }

      return ob(subject).select(keysToKeep);
    },
    expand: function(depth = 1){
      let res;
      subject = makeShallow(subject);

      // Determine if an array is represented by the flattened object
      let rootObjectPresent = true;
      if(depth === 1) {
        rootObjectPresent = false;
        for(let key in subject) {
          let rootArrayPresent = key.match(/^\d/ig);

          rootObjectPresent = (rootObjectPresent || !rootArrayPresent);
        }
      }

      if(rootObjectPresent === false && depth === 1) {
        res = [];
        for(let key in subject) {
          res.push(subject[key]);
        }
      } else {
        let keyChains =  ob(subject).keys();

        // When the object is just {'example.example': y}
        // One key and one value
        if(keyChains.length === 1) {
          let tmp = {};
          let keyChain = keyChains[0]; // something like 'first.another.another'
          let value = subject[keyChain];
          let count;

          res = tmp; // Poining to tmp so that we have a place holder before nesting
          count = 1;
          let keys = keyChain.split('.');
          for(let key of keys) {
            if(count === keys.length) {
              tmp[key] = value;
            } else {
              let isArray = contains(key, '[]');
              if(isArray) {
                key = key.replace('[]','');
                tmp[key] = [];
              } else {
                tmp[key] = {};
              }

              tmp = tmp[key];
            }
            count++;
          }

        } else {
          // If multiple keychains in the object, simplify our logic a bit
          res = {};
          for(let i in subject) {
            let tmp = {};
            tmp[i] = subject[i];
            res = merge(res, ob(tmp).expand(++depth));
          }
        }
      }
      return res;
      //return ob(res).removeUndefs();
    },
    flatten: function(prefix='', shallow=false, counter = 0){
      let res;

      if(type(subject) === 'array' && counter === 0) {
        res = [];

        for(let i of subject){
          res = res.concat(ob(i).flatten(prefix, shallow, counter++));
        }

        return res;
      } else {
        res = {};

        if(type(subject) === 'object' || type(subject) === 'array'){

          for(let i in subject) {
            let tmpPrefix;
            if(prefix === '') {
              tmpPrefix = `${i}`;
            } else {
              tmpPrefix = `${prefix}.${i}`;
            }

            if(type(subject[i]) === 'array') {
              tmpPrefix = tmpPrefix + '[]';
            }

            res[tmpPrefix] = subject[i];

            if(type(subject[i]) === 'array' && shallow) {
              res = merge(res, ob(subject[i][0]).flatten(tmpPrefix, shallow, counter++));
            } else {
              res = merge(res, ob(subject[i]).flatten(tmpPrefix, shallow, counter++));
            }
          }
        }
      }
      return res;
    },
    keys:function() {
      let keys = [];

      if(type(subject) === 'array') {
        for(let i of subject){
          keys = keys.concat(ob(i).keys());
        }
      } else {
        for(let k in subject) {
          keys.push(k);
        };
      }

      return uniques(keys);
    },
    many: (num = 2) => {
      let arr = [];

      if(type(subject) === 'array') {
        return subject;
      } else {
        for(let i = 0; i < num; i++){
          arr.push(subject);
        }
      }

      return arr;
    },
    removeUndefs: () => {
      let res;

      if(type(subject) === 'array') {
        res = [];
        for(let key in subject) {
          if(subject[key] === undefined) {
          } else {
            res.push(ob(subject[key]).removeUndefs());
          }
        }
      } else if(type(subject) === 'object') {
        for(let key in subject) {
          if(subject[key] === undefined) {
            delete subject[key];
          } else {
            subject[key] = ob(subject[key]).removeUndefs();
          }
        }

        return subject;
      } else {
        return subject;
      }

      return res;
    },
    select: (keys = []) => {
      let resp;

      if(type(subject) === 'array') {
        resp = [];

        for(let i of subject){
          resp = resp.concat(ob(i).select(keys));
        }
      } else {
        resp = {};

        let flat = ob(subject).flatten();

        for (let actualKey in flat){
          for (let desiredKey of keys){
            if(actualKey === desiredKey) {
              resp[actualKey] = flat[actualKey];
            }
          }
        }
        resp = ob(resp).expand();
      }

      return resp;
    },
    shallow: () => {
      let x = ob(subject).flatten();
      x = makeShallow(x);

      return ob(x).expand();
    },
    values:() => {
      let values = [];

      if(type(subject) === 'array') {
        for(let i of subject){
          values = values.concat(ob(i).values());
        }
      } else {
        for(let k in subject) {
          values.push(subject[k]);
        };
      }

      return uniques(values);
    },
  };
};

if (typeof module !== 'undefined') {
  module.exports = ob;
}
<commit_msg>Minor docs<commit_after>'use strict';

import uniques from 'uniques';
import type from 'type-of';
import contains from 'string-contains';
import merge from 'deepmerge';

let makeShallow = (subject) => {
  let resp = {};

  for(let keyChain in subject){
    let shallow = false;

    for(let keyChain2 in subject){
      if(keyChain !== keyChain2 && keyChain2.indexOf(keyChain) === 0) {
        shallow = true;
      }
    }

    if(!shallow) {
      resp[keyChain] = subject[keyChain];
    }
  }
  return resp;
};

/**
 * Returns an objob object
 *
 * @param {(object|object[])} subject
 * @returns {(object|object[])}
 */
let ob = function (subject) {

  return {
    deselect: function(keys = []){
      let allKeys = ob(ob(subject).flatten()).keys();
      let keysToKeep = [];

      for( let subjectKey of allKeys ) {
        let keepKey = true;

        for( let keyToRemove of keys ){
          if(subjectKey === keyToRemove){
            keepKey = false;
          }
        }

        if(keepKey){
          keysToKeep.push(subjectKey);
        }
      }

      return ob(subject).select(keysToKeep);
    },
    expand: function(depth = 1){
      let res;
      subject = makeShallow(subject);

      // Determine if an array is represented by the flattened object
      let rootObjectPresent = true;
      if(depth === 1) {
        rootObjectPresent = false;
        for(let key in subject) {
          let rootArrayPresent = key.match(/^\d/ig);

          rootObjectPresent = (rootObjectPresent || !rootArrayPresent);
        }
      }

      if(rootObjectPresent === false && depth === 1) {
        res = [];
        for(let key in subject) {
          res.push(subject[key]);
        }
      } else {
        let keyChains =  ob(subject).keys();

        // When the object is just {'example.example': y}
        // One key and one value
        if(keyChains.length === 1) {
          let tmp = {};
          let keyChain = keyChains[0]; // something like 'first.another.another'
          let value = subject[keyChain];
          let count;

          res = tmp; // Poining to tmp so that we have a place holder before nesting
          count = 1;
          let keys = keyChain.split('.');
          for(let key of keys) {
            if(count === keys.length) {
              tmp[key] = value;
            } else {
              let isArray = contains(key, '[]');
              if(isArray) {
                key = key.replace('[]','');
                tmp[key] = [];
              } else {
                tmp[key] = {};
              }

              tmp = tmp[key];
            }
            count++;
          }

        } else {
          // If multiple keychains in the object, simplify our logic a bit
          res = {};
          for(let i in subject) {
            let tmp = {};
            tmp[i] = subject[i];
            res = merge(res, ob(tmp).expand(++depth));
          }
        }
      }
      return res;
      //return ob(res).removeUndefs();
    },
    flatten: function(prefix='', shallow=false, depth = 1){
      // With shallow set, that means instead of getting all of the array, we only get the first array
      // item. Not sure if this is needed. May re-evaluate
      let res;

      if(type(subject) === 'array' && depth === 1) {
        res = [];

        for(let i of subject){
          res = res.concat(ob(i).flatten(prefix, shallow, ++depth));
        }

        return res;
      } else {
        res = {};

        if(type(subject) === 'object' || type(subject) === 'array'){

          for(let i in subject) {
            let tmpPrefix;
            if(prefix === '') {
              tmpPrefix = `${i}`;
            } else {
              tmpPrefix = `${prefix}.${i}`;
            }

            if(type(subject[i]) === 'array') {
              tmpPrefix = tmpPrefix + '[]';
            }

            res[tmpPrefix] = subject[i];

            if(type(subject[i]) === 'array' && shallow) {
              res = merge(res, ob(subject[i][0]).flatten(tmpPrefix, shallow, ++depth));
            } else {
              res = merge(res, ob(subject[i]).flatten(tmpPrefix, shallow, ++depth));
            }
          }
        }
      }
      return res;
    },
    keys:function() {
      let keys = [];

      if(type(subject) === 'array') {
        for(let i of subject){
          keys = keys.concat(ob(i).keys());
        }
      } else {
        for(let k in subject) {
          keys.push(k);
        };
      }

      return uniques(keys);
    },
    many: (num = 2) => {
      let arr = [];

      if(type(subject) === 'array') {
        return subject;
      } else {
        for(let i = 0; i < num; i++){
          arr.push(subject);
        }
      }

      return arr;
    },
    removeUndefs: () => {
      let res;

      if(type(subject) === 'array') {
        res = [];
        for(let key in subject) {
          if(subject[key] === undefined) {
          } else {
            res.push(ob(subject[key]).removeUndefs());
          }
        }
      } else if(type(subject) === 'object') {
        for(let key in subject) {
          if(subject[key] === undefined) {
            delete subject[key];
          } else {
            subject[key] = ob(subject[key]).removeUndefs();
          }
        }

        return subject;
      } else {
        return subject;
      }

      return res;
    },
    select: (keys = []) => {
      let resp;

      if(type(subject) === 'array') {
        resp = [];

        for(let i of subject){
          resp = resp.concat(ob(i).select(keys));
        }
      } else {
        resp = {};

        let flat = ob(subject).flatten();

        for (let actualKey in flat){
          for (let desiredKey of keys){
            if(actualKey === desiredKey) {
              resp[actualKey] = flat[actualKey];
            }
          }
        }
        resp = ob(resp).expand();
      }

      return resp;
    },
    shallow: () => {
      let x = ob(subject).flatten();
      x = makeShallow(x);

      return ob(x).expand();
    },
    values:() => {
      let values = [];

      if(type(subject) === 'array') {
        for(let i of subject){
          values = values.concat(ob(i).values());
        }
      } else {
        for(let k in subject) {
          values.push(subject[k]);
        };
      }

      return uniques(values);
    },
  };
};

if (typeof module !== 'undefined') {
  module.exports = ob;
}
<|endoftext|>


<commit_before><commit_msg>Added empty O3 class definition<commit_after>var O3 = function(id, opt_options) {
};

O3.prototype.setOptions = function(options) {
}

O3.prototype.setZone = function(zone) {
};

O3.prototype.setAngle = function(angle) {
};

O3.prototype.setLegs = function(prev, next) {
};

O3.prototype.getEffectiveAngle = function() {
};

O3.prototype.draw = function() {
};

if (typeof exports === 'object')
    module.exports = O3;
<|endoftext|>


<commit_before>'use strict';
var Parser  = require('./parse/index');
var _parse = Parser.parse;
var utils = require('./utils');

var blockTypes = {
  if: true,
  foreach: true,
  macro: true,
  noescape: true,
  define: true
};

var customBlocks = [];

/**
 * @param {string} str string to parse
 * @param {object} blocks self define blocks, such as `#cms(1) hello #end`
 * @return {array} ast array
 */
var parse = function(str, blocks) {
  var asts = _parse(str);
  customBlocks = blocks || {};

  /**
   * remove all newline after all direction such as `#set, #each`
   */
  utils.forEach(asts, function trim(ast, i) {
    var TRIM_REG = /^[ \t]*\n/;
    if (ast.type && ast.type !== 'references') {
      var _ast = asts[i + 1];
      if (typeof _ast === 'string' && TRIM_REG.test(_ast)) {
        asts[i + 1] = _ast.replace(TRIM_REG, '');
      }
    }
  });

  var ret = makeLevel(asts);

  return utils.isArray(ret) ? ret : ret.arr;
};

function makeLevel(block, index) {

  var len = block.length;
  index = index || 0;
  var ret = [];
  var ignore = index - 1;

  for (var i = index; i < len; i++) {

    if (i <= ignore) continue;

    var ast = block[i];
    var type = ast.type;

    var isBlockType = blockTypes[type];

    // 自定义类型支持
    if (!isBlockType && ast.type === 'macro_call' && customBlocks[ast.id]) {
      isBlockType = true;
      ast.type = ast.id;
      delete ast.id;
    }

    if (!isBlockType && type !== 'end') {

      ret.push(ast);

    } else if (type === 'end') {

      return {arr: ret, step: i};

    } else {

      var _ret = makeLevel(block, i + 1);
      ignore = _ret.step;
      _ret.arr.unshift(block[i]);
      ret.push(_ret.arr);

    }

  }

  return ret;
}

module.exports = parse;
<commit_msg>make newline trim configurable<commit_after>'use strict';
var Parser  = require('./parse/index');
var _parse = Parser.parse;
var utils = require('./utils');

var blockTypes = {
  if: true,
  foreach: true,
  macro: true,
  noescape: true,
  define: true
};

var customBlocks = [];

/**
 * @param {string} str string to parse
 * @param {object} blocks self define blocks, such as `#cms(1) hello #end`
 * @param {boolean} ignoreSpace if set true, then ignore the newline trim.
 * @return {array} ast array
 */
var parse = function(str, blocks, ignoreSpace) {
  var asts = _parse(str);
  customBlocks = blocks || {};

  /**
   * remove all newline after all direction such as `#set, #each`
   */
  ignoreSpace || utils.forEach(asts, function trim(ast, i) {
    var TRIM_REG = /^[ \t]*\n/;
    if (ast.type && ast.type !== 'references') {
      var _ast = asts[i + 1];
      if (typeof _ast === 'string' && TRIM_REG.test(_ast)) {
        asts[i + 1] = _ast.replace(TRIM_REG, '');
      }
    }
  });

  var ret = makeLevel(asts);

  return utils.isArray(ret) ? ret : ret.arr;
};

function makeLevel(block, index) {

  var len = block.length;
  index = index || 0;
  var ret = [];
  var ignore = index - 1;

  for (var i = index; i < len; i++) {

    if (i <= ignore) continue;

    var ast = block[i];
    var type = ast.type;

    var isBlockType = blockTypes[type];

    // 自定义类型支持
    if (!isBlockType && ast.type === 'macro_call' && customBlocks[ast.id]) {
      isBlockType = true;
      ast.type = ast.id;
      delete ast.id;
    }

    if (!isBlockType && type !== 'end') {

      ret.push(ast);

    } else if (type === 'end') {

      return {arr: ret, step: i};

    } else {

      var _ret = makeLevel(block, i + 1);
      ignore = _ret.step;
      _ret.arr.unshift(block[i]);
      ret.push(_ret.arr);

    }

  }

  return ret;
}

module.exports = parse;
<|endoftext|>


<commit_before>'use strict';
var $ = require("jquery"),
	utils = require('./utils.js'),
	yUtils = require('yasgui-utils'),
	imgs = require('./imgs.js');
require('jquery-ui/sortable');
require('pivottable');

var root = module.exports = function(yasr) {
	var drawOnGChartCallback = false;
	var loadingGChart = false;
	var renderers = $.pivotUtilities.renderers;
	var plugin = {};
	var options = $.extend(true, {}, root.defaults);

	if (options.useD3Chart) {
		try {
			var d3 = require('d3');
			require('../node_modules/pivottable/dist/d3_renderers.js');
		} catch (e) {
			//do nothing. just make sure we don't use this renderer
		}
		if (!$.pivotUtilities.d3_renderers) $.extend(true, renderers, $.pivotUtilities.d3_renderers);
	}

	var loadGoogleApi = function() {
		var finishAjax = function() {
			try {
				require('../node_modules/pivottable/dist/gchart_renderers.js');
				$.extend(true, renderers, $.pivotUtilities.gchart_renderers);
			} catch (e) {
				//hmm, something went wrong. forget about it;
			}
			loadingGChart = false;
			if (drawOnGChartCallback) draw();
			drawOnGChartCallback = false;
		};

		//cannot package google loader via browserify...
		$.ajax({
			  cache: true,
			  dataType: "script",
			  url: "//google.com/jsapi",
			})
			.done(function(data, textStatus, jqxhr) {
				var googleLoader = require('google');// shimmed to global google variable
				googleLoader.load("visualization", "1", {
					packages : [ "corechart", "charteditor" ],
					callback : finishAjax
				})
			})
			.fail(finishAjax);
	};

	//only load this script
	if (options.useGChart && !$.pivotUtilities.gchart_renderers) {
		loadingGChart = true;
		//gchart not loaded yet. load them!
		loadGoogleApi();
	}

	var $pivotWrapper;

	var formatForPivot = function(callback) {
		var vars = yasr.results.getVariables();
		var usedPrefixes = null;
		if (yasr.options.getUsedPrefixes) {
			usedPrefixes = (typeof yasr.options.getUsedPrefixes == "function"? yasr.options.getUsedPrefixes(yasr):  yasr.options.getUsedPrefixes);
		}
		yasr.results.getBindings().forEach(function(binding) {
			var rowObj = {};
			vars.forEach(function(variable) {
				if (variable in binding) {
					var val = binding[variable].value;
					if (binding.type == "uri") val = utils.uriToPrefixed(usedPrefixes, val);
					rowObj[variable] = val;
				} else {
					rowObj[variable] = null;
				}
			});
			callback(rowObj);
		});
	}
	var settingsPersistencyId = null
	var getPersistencyId = function() {
		if (settingsPersistencyId === null) {
			if (typeof options.persistency == "string") {
				settingsPersistencyId = options.persistency;
			} else if (typeof options.persistency == "function") {
				settingsPersistencyId = options.persistency(yasr);
			} else {
				settingsPersistencyId = false;
			}
		}
		return settingsPersistencyId;
	};
	var onRefresh = function(pivotObj) {
		if (getPersistencyId()) {
			var storeSettings = {
				cols: pivotObj.cols,
				rows: pivotObj.rows,
				rendererName: pivotObj.rendererName,
			}
			yUtils.storage.set(getPersistencyId(), storeSettings, "month");
		}
	};
	var getStoredSettings = function() {
		var settings = yUtils.storage.get(getPersistencyId());
		//validate settings. we may have different variables, or renderers might be gone
		if (settings) {
			var vars = yasr.results.getVariables();
			var keepColsAndRows = true;
			settings.cols.forEach(function(variable) {
				if (vars.indexOf(variable) < 0) keepColsAndRows = false;
			});
			if (keepColsAndRows) {
				settings.rows.forEach(function(variable) {
					if (vars.indexOf(variable) < 0) keepColsAndRows = false;
				});
			}
			if (!keepColsAndRows) {
				settings.cols = [];
				settings.rows = [];
			}
			if (!renderers[settings.rendererName]) delete settings.rendererName;
		} else {
			settings = {};
		}
		return settings;
	};
	var draw = function() {
		if (loadingGChart) {
			//postpone drawing until we've loaded everything
			drawOnGChartCallback = true;
			return;
		}

		$pivotWrapper = $('<div>', {class: 'pivotTable'}).appendTo($(yasr.resultsContainer));

		var renderers = $.pivotUtilities.renderers;
		var settings = $.extend(true, {}, getStoredSettings(), root.defaults.pivotTable);

		settings.onRefresh = (function() {
		    var originalRefresh = settings.onRefresh;
		    return function(pivotObj) {
		    	onRefresh(pivotObj);
		    	if (originalRefresh) originalRefresh(pivotObj);
		    };
		})();

		window.pivot = $pivotWrapper.pivotUI(formatForPivot, settings);

		/**
		 * post process
		 */
		//use 'move' handler for variables
		var icon = $(yUtils.svg.getElement(imgs.move, {width: '14px', height: '14px'}));
		$pivotWrapper.find('.pvtTriangle').replaceWith(icon);

		//add headers to selector rows
		$('.pvtCols').prepend($('<div>', {class: 'containerHeader'}).text("Columns"));
		$('.pvtRows').prepend($('<div>', {class: 'containerHeader'}).text("Rows"));
		$('.pvtUnused').prepend($('<div>', {class: 'containerHeader'}).text("Available Variables"));
		$('.pvtVals').prepend($('<div>', {class: 'containerHeader'}).text("Cells"));

	};
	var canHandleResults = function(){
		return yasr.results && yasr.results.getVariables && yasr.results.getVariables() && yasr.results.getVariables().length > 0;
	};

	return {
		draw: draw,
		name: "Pivot Table",
		canHandleResults: canHandleResults,
		getPriority: 4,
	}
};

root.defaults = {
	useGChart: true,
	useD3Chart: true,
	persistency: function(yasr) {return "yasr_pivot_" + $(yasr.container).closest('[id]').attr('id')},
	pivotTable: {}
};

root.version = {
	"YASR-rawResponse" : require("../package.json").version,
	"jquery": $.fn.jquery,
};<commit_msg>fixed issue with d3js not showing up as pivot option #24<commit_after>'use strict';
var $ = require("jquery"),
	utils = require('./utils.js'),
	yUtils = require('yasgui-utils'),
	imgs = require('./imgs.js');
require('jquery-ui/sortable');
require('pivottable');

var root = module.exports = function(yasr) {
	var drawOnGChartCallback = false;
	var loadingGChart = false;
	var renderers = $.pivotUtilities.renderers;
	var plugin = {};
	var options = $.extend(true, {}, root.defaults);

	if (options.useD3Chart) {
		try {
			var d3 = require('d3');
			require('../node_modules/pivottable/dist/d3_renderers.js');
		} catch (e) {
			//do nothing. just make sure we don't use this renderer
		}
		if ($.pivotUtilities.d3_renderers) $.extend(true, renderers, $.pivotUtilities.d3_renderers);
	}

	var loadGoogleApi = function() {
		var finishAjax = function() {
			try {
				require('../node_modules/pivottable/dist/gchart_renderers.js');
				$.extend(true, renderers, $.pivotUtilities.gchart_renderers);
			} catch (e) {
				//hmm, something went wrong. forget about it;
			}
			loadingGChart = false;
			if (drawOnGChartCallback) draw();
			drawOnGChartCallback = false;
		};

		//cannot package google loader via browserify...
		$.ajax({
			  cache: true,
			  dataType: "script",
			  url: "//google.com/jsapi",
			})
			.done(function(data, textStatus, jqxhr) {
				var googleLoader = require('google');// shimmed to global google variable
				googleLoader.load("visualization", "1", {
					packages : [ "corechart", "charteditor" ],
					callback : finishAjax
				})
			})
			.fail(finishAjax);
	};

	//only load this script
	if (options.useGChart && !$.pivotUtilities.gchart_renderers) {
		loadingGChart = true;
		//gchart not loaded yet. load them!
		loadGoogleApi();
	}

	var $pivotWrapper;

	var formatForPivot = function(callback) {
		var vars = yasr.results.getVariables();
		var usedPrefixes = null;
		if (yasr.options.getUsedPrefixes) {
			usedPrefixes = (typeof yasr.options.getUsedPrefixes == "function"? yasr.options.getUsedPrefixes(yasr):  yasr.options.getUsedPrefixes);
		}
		yasr.results.getBindings().forEach(function(binding) {
			var rowObj = {};
			vars.forEach(function(variable) {
				if (variable in binding) {
					var val = binding[variable].value;
					if (binding.type == "uri") val = utils.uriToPrefixed(usedPrefixes, val);
					rowObj[variable] = val;
				} else {
					rowObj[variable] = null;
				}
			});
			callback(rowObj);
		});
	}
	var settingsPersistencyId = null
	var getPersistencyId = function() {
		if (settingsPersistencyId === null) {
			if (typeof options.persistency == "string") {
				settingsPersistencyId = options.persistency;
			} else if (typeof options.persistency == "function") {
				settingsPersistencyId = options.persistency(yasr);
			} else {
				settingsPersistencyId = false;
			}
		}
		return settingsPersistencyId;
	};
	var onRefresh = function(pivotObj) {
		if (getPersistencyId()) {
			var storeSettings = {
				cols: pivotObj.cols,
				rows: pivotObj.rows,
				rendererName: pivotObj.rendererName,
			}
			yUtils.storage.set(getPersistencyId(), storeSettings, "month");
		}
	};
	var getStoredSettings = function() {
		var settings = yUtils.storage.get(getPersistencyId());
		//validate settings. we may have different variables, or renderers might be gone
		if (settings) {
			var vars = yasr.results.getVariables();
			var keepColsAndRows = true;
			settings.cols.forEach(function(variable) {
				if (vars.indexOf(variable) < 0) keepColsAndRows = false;
			});
			if (keepColsAndRows) {
				settings.rows.forEach(function(variable) {
					if (vars.indexOf(variable) < 0) keepColsAndRows = false;
				});
			}
			if (!keepColsAndRows) {
				settings.cols = [];
				settings.rows = [];
			}
			if (!renderers[settings.rendererName]) delete settings.rendererName;
		} else {
			settings = {};
		}
		return settings;
	};
	var draw = function() {
		if (loadingGChart) {
			//postpone drawing until we've loaded everything
			drawOnGChartCallback = true;
			return;
		}

		$pivotWrapper = $('<div>', {class: 'pivotTable'}).appendTo($(yasr.resultsContainer));

		var renderers = $.pivotUtilities.renderers;
		var settings = $.extend(true, {}, getStoredSettings(), root.defaults.pivotTable);

		settings.onRefresh = (function() {
		    var originalRefresh = settings.onRefresh;
		    return function(pivotObj) {
		    	onRefresh(pivotObj);
		    	if (originalRefresh) originalRefresh(pivotObj);
		    };
		})();

		window.pivot = $pivotWrapper.pivotUI(formatForPivot, settings);

		/**
		 * post process
		 */
		//use 'move' handler for variables
		var icon = $(yUtils.svg.getElement(imgs.move, {width: '14px', height: '14px'}));
		$pivotWrapper.find('.pvtTriangle').replaceWith(icon);

		//add headers to selector rows
		$('.pvtCols').prepend($('<div>', {class: 'containerHeader'}).text("Columns"));
		$('.pvtRows').prepend($('<div>', {class: 'containerHeader'}).text("Rows"));
		$('.pvtUnused').prepend($('<div>', {class: 'containerHeader'}).text("Available Variables"));
		$('.pvtVals').prepend($('<div>', {class: 'containerHeader'}).text("Cells"));

	};
	var canHandleResults = function(){
		return yasr.results && yasr.results.getVariables && yasr.results.getVariables() && yasr.results.getVariables().length > 0;
	};

	return {
		draw: draw,
		name: "Pivot Table",
		canHandleResults: canHandleResults,
		getPriority: 4,
	}
};

root.defaults = {
	useGChart: true,
	useD3Chart: true,
	persistency: function(yasr) {return "yasr_pivot_" + $(yasr.container).closest('[id]').attr('id')},
	pivotTable: {}
};

root.version = {
	"YASR-rawResponse" : require("../package.json").version,
	"jquery": $.fn.jquery,
};<|endoftext|>


<commit_before>/*globals PouchAdapter: true */

"use strict";

var Pouch = function Pouch(name, opts, callback) {

  if (!(this instanceof Pouch)) {
    return new Pouch(name, opts, callback);
  }

  if (typeof opts === 'function' || typeof opts === 'undefined') {
    callback = opts;
    opts = {};
  }

  if (typeof name === 'object') {
    opts = name;
    name = undefined;
  }

  var backend = Pouch.parseAdapter(opts.name || name);
  opts.name = opts.name || backend.name;
  opts.adapter = opts.adapter || backend.adapter;

  if (!Pouch.adapters[opts.adapter]) {
    throw 'Adapter is missing';
  }

  if (!Pouch.adapters[opts.adapter].valid()) {
    throw 'Invalid Adapter';
  }

  var adapter = new PouchAdapter(opts, function(err, db) {
    if (err) {
      if (callback) {
        callback(err);
      }
      return;
    }
    for (var plugin in Pouch.plugins) {
      // In future these will likely need to be async to allow the plugin
      // to initialise
      var pluginObj = Pouch.plugins[plugin](db);
      for (var api in pluginObj) {
        // We let things like the http adapter use its own implementation
        // as it shares a lot of code
        if (!(api in db)) {
          db[api] = pluginObj[api];
        }
      }
    }
    callback(null, db);
  });

  for (var j in adapter) {
    this[j] = adapter[j];
  }
};

Pouch.DEBUG = false;

Pouch.adapters = {};
Pouch.plugins = {};

Pouch.parseAdapter = function(name) {

  var match = name.match(/([a-z\-]*):\/\/(.*)/);
  if (match) {
    // the http adapter expects the fully qualified name
    name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2];
    var adapter = match[1];
    if (!Pouch.adapters[adapter].valid()) {
      throw 'Invalid adapter';
    }
    return {name: name, adapter: match[1]};
  }

  var rank = {'idb': 1, 'leveldb': 2, 'websql': 3, 'http': 4, 'https': 4};
  var rankedAdapter = Object.keys(Pouch.adapters).sort(function(a, b) {
    return rank[a] - rank[b];
  })[0];

  return {
    name: name,
    adapter: rankedAdapter
  };
};

Pouch.destroy = function(name, callback) {
  for (var plugin in Pouch.plugins) {
    Pouch.plugins[plugin]._delete(name);
  }
  var opts = Pouch.parseAdapter(name);
  Pouch.adapters[opts.adapter].destroy(opts.name, callback);
};

Pouch.adapter = function (id, obj) {
  if (obj.valid()) {
    Pouch.adapters[id] = obj;
  }
};

Pouch.plugin = function(id, obj) {
  Pouch.plugins[id] = obj;
};

// Enumerate errors, add the status code so we can reflect the HTTP api
// in future
Pouch.Errors = {
  MISSING_BULK_DOCS: {
    status: 400,
    error: 'bad_request',
    reason: "Missing JSON list of 'docs'"
  },
  MISSING_DOC: {
    status: 404,
    error: 'not_found',
    reason: 'missing'
  },
  REV_CONFLICT: {
    status: 409,
    error: 'conflict',
    reason: 'Document update conflict'
  },
  INVALID_ID: {
    status: 400,
    error: 'invalid_id',
    reason: '_id field must contain a string'
  },
  MISSING_ID: {
    status: 412,
    error: 'missing_id',
    reason: '_id is required for puts'
  },
  RESERVED_ID: {
    status: 400,
    error: 'bad_request',
    reason: 'Only reserved document ids may start with underscore.'
  },
  NOT_OPEN: {
    status: 412,
    error: 'precondition_failed',
    reason: 'Database not open so cannot close'
  },
  UNKNOWN_ERROR: {
    status: 500,
    error: 'unknown_error',
    reason: 'Database encountered an unknown error'
  },
  INVALID_REQUEST: {
    status: 400,
    error: 'invalid_request',
    reason: 'Request was invalid'
  },
  QUERY_PARSE_ERROR: {
    status: 400,
    error: 'query_parse_error',
    reason: 'Some query parameter is invalid'
  },
  BAD_REQUEST: {
    status: 400,
    error: 'bad_request',
    reason: 'Something wrong with the request'
  }
};

if (typeof module !== 'undefined' && module.exports) {
  global.Pouch = Pouch;
  Pouch.merge = require('./pouch.merge.js').merge;
  Pouch.collate = require('./pouch.collate.js').collate;
  Pouch.replicate = require('./pouch.replicate.js').replicate;
  Pouch.utils = require('./pouch.utils.js');
  module.exports = Pouch;

  var PouchAdapter = require('./pouch.adapter.js');
  // load adapters known to work under node
  var adapters = ['leveldb', 'http'];
  adapters.map(function(adapter) {
    var adapter_path = './adapters/pouch.'+adapter+'.js';
    require(adapter_path);
  });
  require('./plugins/pouchdb.mapreduce.js');
} else {
  Pouch = Pouch;
}
<commit_msg>(#416) - Restore Pouch on the global window object<commit_after>/*globals PouchAdapter: true */

"use strict";

var Pouch = function Pouch(name, opts, callback) {

  if (!(this instanceof Pouch)) {
    return new Pouch(name, opts, callback);
  }

  if (typeof opts === 'function' || typeof opts === 'undefined') {
    callback = opts;
    opts = {};
  }

  if (typeof name === 'object') {
    opts = name;
    name = undefined;
  }

  var backend = Pouch.parseAdapter(opts.name || name);
  opts.name = opts.name || backend.name;
  opts.adapter = opts.adapter || backend.adapter;

  if (!Pouch.adapters[opts.adapter]) {
    throw 'Adapter is missing';
  }

  if (!Pouch.adapters[opts.adapter].valid()) {
    throw 'Invalid Adapter';
  }

  var adapter = new PouchAdapter(opts, function(err, db) {
    if (err) {
      if (callback) {
        callback(err);
      }
      return;
    }
    for (var plugin in Pouch.plugins) {
      // In future these will likely need to be async to allow the plugin
      // to initialise
      var pluginObj = Pouch.plugins[plugin](db);
      for (var api in pluginObj) {
        // We let things like the http adapter use its own implementation
        // as it shares a lot of code
        if (!(api in db)) {
          db[api] = pluginObj[api];
        }
      }
    }
    callback(null, db);
  });

  for (var j in adapter) {
    this[j] = adapter[j];
  }
};

Pouch.DEBUG = false;

Pouch.adapters = {};
Pouch.plugins = {};

Pouch.parseAdapter = function(name) {

  var match = name.match(/([a-z\-]*):\/\/(.*)/);
  if (match) {
    // the http adapter expects the fully qualified name
    name = /http(s?)/.test(match[1]) ? match[1] + '://' + match[2] : match[2];
    var adapter = match[1];
    if (!Pouch.adapters[adapter].valid()) {
      throw 'Invalid adapter';
    }
    return {name: name, adapter: match[1]};
  }

  var rank = {'idb': 1, 'leveldb': 2, 'websql': 3, 'http': 4, 'https': 4};
  var rankedAdapter = Object.keys(Pouch.adapters).sort(function(a, b) {
    return rank[a] - rank[b];
  })[0];

  return {
    name: name,
    adapter: rankedAdapter
  };
};

Pouch.destroy = function(name, callback) {
  for (var plugin in Pouch.plugins) {
    Pouch.plugins[plugin]._delete(name);
  }
  var opts = Pouch.parseAdapter(name);
  Pouch.adapters[opts.adapter].destroy(opts.name, callback);
};

Pouch.adapter = function (id, obj) {
  if (obj.valid()) {
    Pouch.adapters[id] = obj;
  }
};

Pouch.plugin = function(id, obj) {
  Pouch.plugins[id] = obj;
};

// Enumerate errors, add the status code so we can reflect the HTTP api
// in future
Pouch.Errors = {
  MISSING_BULK_DOCS: {
    status: 400,
    error: 'bad_request',
    reason: "Missing JSON list of 'docs'"
  },
  MISSING_DOC: {
    status: 404,
    error: 'not_found',
    reason: 'missing'
  },
  REV_CONFLICT: {
    status: 409,
    error: 'conflict',
    reason: 'Document update conflict'
  },
  INVALID_ID: {
    status: 400,
    error: 'invalid_id',
    reason: '_id field must contain a string'
  },
  MISSING_ID: {
    status: 412,
    error: 'missing_id',
    reason: '_id is required for puts'
  },
  RESERVED_ID: {
    status: 400,
    error: 'bad_request',
    reason: 'Only reserved document ids may start with underscore.'
  },
  NOT_OPEN: {
    status: 412,
    error: 'precondition_failed',
    reason: 'Database not open so cannot close'
  },
  UNKNOWN_ERROR: {
    status: 500,
    error: 'unknown_error',
    reason: 'Database encountered an unknown error'
  },
  INVALID_REQUEST: {
    status: 400,
    error: 'invalid_request',
    reason: 'Request was invalid'
  },
  QUERY_PARSE_ERROR: {
    status: 400,
    error: 'query_parse_error',
    reason: 'Some query parameter is invalid'
  },
  BAD_REQUEST: {
    status: 400,
    error: 'bad_request',
    reason: 'Something wrong with the request'
  }
};

if (typeof module !== 'undefined' && module.exports) {
  global.Pouch = Pouch;
  Pouch.merge = require('./pouch.merge.js').merge;
  Pouch.collate = require('./pouch.collate.js').collate;
  Pouch.replicate = require('./pouch.replicate.js').replicate;
  Pouch.utils = require('./pouch.utils.js');
  module.exports = Pouch;

  var PouchAdapter = require('./pouch.adapter.js');
  // load adapters known to work under node
  var adapters = ['leveldb', 'http'];
  adapters.map(function(adapter) {
    var adapter_path = './adapters/pouch.'+adapter+'.js';
    require(adapter_path);
  });
  require('./plugins/pouchdb.mapreduce.js');
} else {
  window.Pouch = Pouch;
}
<|endoftext|>


<commit_before>'use strict';

var BOB_MAGNITUDE = 16;
var BOB_TIME = 1.0;
var LEVEL_EXIT_TIME = 0.5;
var LEVEL_ENTER_TIME = 0.5;

////////////////////////////////////////////////////////////////////////
// Door

function Door(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.target = info.Target;
	if (!this.target) {
		console.error('Door has no target');
	} else {
		level.gProps.doors[this.target] = this;
	}
}
Door.prototype.markerOffset = 64;
Door.prototype.interact = function() {
	var target = this.target;
	if (!this.target) {
		game.sound.play('buzz');
		console.error('Door has no target');
		return;
	}
	var level = this.level;
	this.sprite.frame = 1;
	level.setPaused(true);
	level.gProps.setTarget(null);
	game.sound.play('door');
	var timer = game.time.create(true);
	timer.add(
		LEVEL_EXIT_TIME * 1000, function() {
			level.changeLevel(target);
		});
	timer.start();
};
Door.prototype.spawnPlayer = function() {
	this.level.gPlayer.spawn(this.sprite.position);
	this.level.setPaused(true);
	this.sprite.frame = 1;
	var timer = game.time.create(true);
	timer.add(
		LEVEL_ENTER_TIME * 1000, function() {
			this.sprite.frame = 0;
			this.level.setPaused(false);
		}, this);
	timer.start();
};

////////////////////////////////////////////////////////////////////////
// Chest

function Chest(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.ident = info.Id;
	if (typeof this.ident == 'undefined') {
		console.error('Chest has no Id');
	} else {
		if (this.ident in level.gProps.chests) {
			console.error('Duplicate chest:', this.ident);
		} else {
			level.gProps.chests[this.ident] = this;
		}
		var chests = level.gState.chests[level.gLevelName];
		if (chests && (this.ident in chests)) {
			sprite.frame = 3;
			sprite.name = null;
		}
	}
}
Chest.prototype.markerOffset = 48;
Chest.prototype.interact = function() {
	this.sprite.name = null;
	var chests = this.level.gState.chests[this.level.gLevelName];
	if (!chests) {
		chests = {};
		this.level.gState.chests[this.level.gLevelName] = chests;
	}
	chests[this.ident] = 0;

	var level = this.level;
	this.sprite.frame = 3;
	level.setPaused(true);
	level.gProps.setTarget(null);
	var tsprite, tframe, tfunc;
	if (true) {
		tsprite = 'hearts';
		tframe = 0;
		tfunc = function(player) { player.addHeart(); };
	}
	var treasure = level.add.sprite(
		this.sprite.x,
		this.sprite.y - 24,
		tsprite,
		tframe);
	treasure.anchor.setTo(0.5, 0.5);
	var tpre = 0.25, tmove = 1.0;
	var tween = level.add.tween(treasure);
	tween.to(
		{y: this.sprite.y - 96}, tmove * 1000,
		Phaser.Easing.Sinusoidal.InOut,
		true, tpre * 1000, 0, false);
	game.sound.play('fanfare');
	tween.onComplete.addOnce(function() {
		tfunc(this.level.gPlayer);
		level.setPaused(false);
		treasure.destroy();
	}, this);
};

////////////////////////////////////////////////////////////////////////
// Save point

function SavePoint(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.ident = info.Id;
	if (typeof this.ident == 'undefined') {
		console.error('Save point has no Id');
	} else if (this.ident in level.gProps.savePoints) {
		console.error('Duplicate save point:', this.ident);
	} else {
		level.gProps.savePoints[this.ident] = this;
	}
}
SavePoint.prototype.markerOffset = 48;
SavePoint.prototype.dead = false;
SavePoint.prototype.interact = function() {
	this.level.gPlayer.healFull();
	this.level.gState.save(this.level.gLevelName, this.ident);
};
SavePoint.prototype.spawnPlayer = function() {
	this.level.gPlayer.spawn(this.sprite.position);
};

////////////////////////////////////////////////////////////////////////
// Prop factories

var PROP_TYPES = {
	0: Door,
	2: Chest,
	4: SavePoint,
};

////////////////////////////////////////////////////////////////////////
// Prop manager

function Props(level) {
	this.level = level;
	this.group = level.add.group();
	this.objs = {};
	this.counter = 0;
	this.markerSprite = null;
	this.markerTarget = null;
	this.markerTween = null;
	this.doors = {};
	this.chests = {};
	this.savePoints = {};
}

// Spawn a prop from the map.
Props.prototype.spawn = function(index, info) {
	var Constructor = PROP_TYPES[index];
	var sprite = this.group.create(
		info.x + 32, info.y - 32,
		'props', index);
	sprite.anchor.set(0.5, 0.5);
	if (Constructor) {
		var name = 'Prop ' + this.counter;
		this.counter++;
		sprite.name = name;
		var obj = new Constructor(this.level, sprite, info.properties || {});
		game.physics.arcade.enable(sprite);
		this.objs[name] = obj;
	}
};

// Update all props.
Props.prototype.update = function(index, info) {
	var player = this.level.gPlayer.getInteractSprite();
	if (!player) {
		this.setTarget(null);
		return;
	}
	var best_dist = 32, best_sprite = null;
	this.group.forEachAlive(function(sprite) {
		if (!sprite.name) {
			return;
		}
		var u = player.position, v = sprite.position;
		var dist = Math.max(
			Math.abs(u.x - v.x),
			Math.abs(u.y - v.y));
		if (dist < best_dist) {
			best_dist = dist;
			best_sprite = sprite;
		}
	});
	this.setTarget(best_sprite);
};

// Set the interaction target.
Props.prototype.setTarget = function(sprite) {
	if (!sprite) {
		if (this.markerTarget) {
			this.markerSprite.kill();
			this.markerTarget = null;
			this.markerTween.stop();
			this.markerTween = null;
		}
		return;
	}
	var obj = this.objs[sprite.name];
	if (!obj) {
		console.error('Invalid interaction target:', sprite);
		return;
	}
	if (this.markerTarget === obj) {
		return;
	}
	this.markerTarget = obj;
	var tx = sprite.x;
	var ty0 = sprite.y - BOB_MAGNITUDE -obj.markerOffset;
	var ty1 = ty0 + BOB_MAGNITUDE * 2;
	if (!this.markerSprite) {
		this.markerSprite = this.group.create(tx, ty0, 'props', 6);
		this.markerSprite.anchor.setTo(0.5, 0.5);
	} else {
		this.markerSprite.reset(tx, ty0);
	}
	if (this.markerTween) {
		this.markerTween.stop();
		this.markerTween = null;
	}
	this.markerTween = game.add.tween(this.markerSprite);
	this.markerTween.to(
		{x: tx, y: ty1}, BOB_TIME * 500, Phaser.Easing.Sinusoidal.InOut,
		true, 0, -1, true);
};

// Try to interact with something, as the player.
Props.prototype.interact = function() {
	if (!this.markerTarget) {
		game.sound.play('buzz');
	} else {
		this.markerTarget.interact(this.level);
	}
};

// Spawn player from a door.
Props.prototype.spawnPlayerFromDoor = function(ident) {
	var obj = this.doors[ident];
	if (!obj) {
		console.error('Missing door:', ident);
	} else {
		obj.spawnPlayer();
	}
};

// Spawn player from a spawn point.
Props.prototype.spawnPlayerFromSpawnPoint = function(ident) {
	var obj = this.spawnPoints[ident];
	if (!obj) {
		console.error('Missing spawn point:', ident);
	} else {
		obj.spawnPlayer();
	}
};

////////////////////////////////////////////////////////////////////////
// Exports

module.exports = {
	Props: Props,
};
<commit_msg>Fix save loading<commit_after>'use strict';

var BOB_MAGNITUDE = 16;
var BOB_TIME = 1.0;
var LEVEL_EXIT_TIME = 0.5;
var LEVEL_ENTER_TIME = 0.5;

////////////////////////////////////////////////////////////////////////
// Door

function Door(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.target = info.Target;
	if (!this.target) {
		console.error('Door has no target');
	} else {
		level.gProps.doors[this.target] = this;
	}
}
Door.prototype.markerOffset = 64;
Door.prototype.interact = function() {
	var target = this.target;
	if (!this.target) {
		game.sound.play('buzz');
		console.error('Door has no target');
		return;
	}
	var level = this.level;
	this.sprite.frame = 1;
	level.setPaused(true);
	level.gProps.setTarget(null);
	game.sound.play('door');
	var timer = game.time.create(true);
	timer.add(
		LEVEL_EXIT_TIME * 1000, function() {
			level.changeLevel(target);
		});
	timer.start();
};
Door.prototype.spawnPlayer = function() {
	this.level.gPlayer.spawn(this.sprite.position);
	this.level.setPaused(true);
	this.sprite.frame = 1;
	var timer = game.time.create(true);
	timer.add(
		LEVEL_ENTER_TIME * 1000, function() {
			this.sprite.frame = 0;
			this.level.setPaused(false);
		}, this);
	timer.start();
};

////////////////////////////////////////////////////////////////////////
// Chest

function Chest(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.ident = info.Id;
	if (typeof this.ident == 'undefined') {
		console.error('Chest has no Id');
	} else {
		if (this.ident in level.gProps.chests) {
			console.error('Duplicate chest:', this.ident);
		} else {
			level.gProps.chests[this.ident] = this;
		}
		var chests = level.gState.chests[level.gLevelName];
		if (chests && (this.ident in chests)) {
			sprite.frame = 3;
			sprite.name = null;
		}
	}
}
Chest.prototype.markerOffset = 48;
Chest.prototype.interact = function() {
	this.sprite.name = null;
	var chests = this.level.gState.chests[this.level.gLevelName];
	if (!chests) {
		chests = {};
		this.level.gState.chests[this.level.gLevelName] = chests;
	}
	chests[this.ident] = 0;

	var level = this.level;
	this.sprite.frame = 3;
	level.setPaused(true);
	level.gProps.setTarget(null);
	var tsprite, tframe, tfunc;
	if (true) {
		tsprite = 'hearts';
		tframe = 0;
		tfunc = function(player) { player.addHeart(); };
	}
	var treasure = level.add.sprite(
		this.sprite.x,
		this.sprite.y - 24,
		tsprite,
		tframe);
	treasure.anchor.setTo(0.5, 0.5);
	var tpre = 0.25, tmove = 1.0;
	var tween = level.add.tween(treasure);
	tween.to(
		{y: this.sprite.y - 96}, tmove * 1000,
		Phaser.Easing.Sinusoidal.InOut,
		true, tpre * 1000, 0, false);
	game.sound.play('fanfare');
	tween.onComplete.addOnce(function() {
		tfunc(this.level.gPlayer);
		level.setPaused(false);
		treasure.destroy();
	}, this);
};

////////////////////////////////////////////////////////////////////////
// Save point

function SavePoint(level, sprite, info) {
	this.level = level;
	this.sprite = sprite;
	this.ident = info.Id;
	if (typeof this.ident == 'undefined') {
		console.error('Save point has no Id');
	} else if (this.ident in level.gProps.savePoints) {
		console.error('Duplicate save point:', this.ident);
	} else {
		level.gProps.savePoints[this.ident] = this;
	}
}
SavePoint.prototype.markerOffset = 48;
SavePoint.prototype.dead = false;
SavePoint.prototype.interact = function() {
	this.level.gPlayer.healFull();
	this.level.gState.save(this.level.gLevelName, this.ident);
};
SavePoint.prototype.spawnPlayer = function() {
	this.level.gPlayer.spawn(this.sprite.position);
};

////////////////////////////////////////////////////////////////////////
// Prop factories

var PROP_TYPES = {
	0: Door,
	2: Chest,
	4: SavePoint,
};

////////////////////////////////////////////////////////////////////////
// Prop manager

function Props(level) {
	this.level = level;
	this.group = level.add.group();
	this.objs = {};
	this.counter = 0;
	this.markerSprite = null;
	this.markerTarget = null;
	this.markerTween = null;
	this.doors = {};
	this.chests = {};
	this.savePoints = {};
}

// Spawn a prop from the map.
Props.prototype.spawn = function(index, info) {
	var Constructor = PROP_TYPES[index];
	var sprite = this.group.create(
		info.x + 32, info.y - 32,
		'props', index);
	sprite.anchor.set(0.5, 0.5);
	if (Constructor) {
		var name = 'Prop ' + this.counter;
		this.counter++;
		sprite.name = name;
		var obj = new Constructor(this.level, sprite, info.properties || {});
		game.physics.arcade.enable(sprite);
		this.objs[name] = obj;
	}
};

// Update all props.
Props.prototype.update = function(index, info) {
	var player = this.level.gPlayer.getInteractSprite();
	if (!player) {
		this.setTarget(null);
		return;
	}
	var best_dist = 32, best_sprite = null;
	this.group.forEachAlive(function(sprite) {
		if (!sprite.name) {
			return;
		}
		var u = player.position, v = sprite.position;
		var dist = Math.max(
			Math.abs(u.x - v.x),
			Math.abs(u.y - v.y));
		if (dist < best_dist) {
			best_dist = dist;
			best_sprite = sprite;
		}
	});
	this.setTarget(best_sprite);
};

// Set the interaction target.
Props.prototype.setTarget = function(sprite) {
	if (!sprite) {
		if (this.markerTarget) {
			this.markerSprite.kill();
			this.markerTarget = null;
			this.markerTween.stop();
			this.markerTween = null;
		}
		return;
	}
	var obj = this.objs[sprite.name];
	if (!obj) {
		console.error('Invalid interaction target:', sprite);
		return;
	}
	if (this.markerTarget === obj) {
		return;
	}
	this.markerTarget = obj;
	var tx = sprite.x;
	var ty0 = sprite.y - BOB_MAGNITUDE -obj.markerOffset;
	var ty1 = ty0 + BOB_MAGNITUDE * 2;
	if (!this.markerSprite) {
		this.markerSprite = this.group.create(tx, ty0, 'props', 6);
		this.markerSprite.anchor.setTo(0.5, 0.5);
	} else {
		this.markerSprite.reset(tx, ty0);
	}
	if (this.markerTween) {
		this.markerTween.stop();
		this.markerTween = null;
	}
	this.markerTween = game.add.tween(this.markerSprite);
	this.markerTween.to(
		{x: tx, y: ty1}, BOB_TIME * 500, Phaser.Easing.Sinusoidal.InOut,
		true, 0, -1, true);
};

// Try to interact with something, as the player.
Props.prototype.interact = function() {
	if (!this.markerTarget) {
		game.sound.play('buzz');
	} else {
		this.markerTarget.interact(this.level);
	}
};

// Spawn player from a door.
Props.prototype.spawnPlayerFromDoor = function(ident) {
	var obj = this.doors[ident];
	if (!obj) {
		console.error('Missing door:', ident);
	} else {
		obj.spawnPlayer();
	}
};

// Spawn player from a save point.
Props.prototype.spawnPlayerFromSavePoint = function(ident) {
	var obj = this.savePoints[ident];
	if (!obj) {
		console.error('Missing save point:', ident);
	} else {
		obj.spawnPlayer();
	}
};

////////////////////////////////////////////////////////////////////////
// Exports

module.exports = {
	Props: Props,
};
<|endoftext|>


<commit_before>/* global $ moment */
var rp = (function() {
  var rp = {};

  var serverRootPath = (function() {
    var scripts = document.getElementsByTagName("script"),
      src = scripts[scripts.length-1].src;
    return src.substring(0, src.lastIndexOf('/'));
  })();

  // ajax requests
  // pieced together from: http://stackoverflow.com/questions/8567114/
  function ajax(url, method /*, data, callback */) {
    // get optional args
    var callback = null;
    var data = null;
    if(typeof(arguments[arguments.length-1]) === 'function')
      callback = arguments[arguments.length-1];
    if(arguments.length >= 3 && typeof(arguments[2]) === 'object')
      data = arguments[2];
    // request URL
    var reqUrl = serverRootPath + '/api/v1/rps/' + url;
    data = data || {};
    var req = new XMLHttpRequest();
    req.overrideMimeType('application/json');
    // callback function on success
    if(callback) req.onreadystatechange = function() {
      if(req.readyState === XMLHttpRequest.DONE && (''+req.status).startsWith('20')) {
        callback(req.responseText ? JSON.parse(req.responseText): '', req);
      }
    };
    // generate query string from data
    var queryString = null;
    if(data) {
      var query = [];
      for (var key in data) {
        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
      }
      queryString = query.join('&');
    }
    // apply things to request
    if(method === 'GET' && queryString) reqUrl += '?' + queryString;
    req.open(method, reqUrl, true);
    if(method === 'POST') req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    // send
    if(method === 'GET' || !queryString) req.send();
    else req.send(queryString);
  }

  // load a single page
  rp.fetchPage = function(url, pageNum, callback) {
    ajax(url + '/page/' + pageNum + '.json', 'GET', { page: pageNum }, function(data) {
      var charas = data.charas
        .map(function(chara) { return new Chara(chara); })
        .reduce(function(arr, chara) { arr[chara.id] = chara; return arr; }, []);
      var msgs = data.msgs.map(function(msg) {
        msg.chara = charas[msg.charaId];
        return new Message(msg);
      });
      // callback
      callback({ msgs: msgs, charas: charas });
    });
  };

  // load a chat object
  rp.chat = function(url, onLoad) {
    var chat = {};
    // chat variables
    var title, desc;
    var msgs = [];
    var charas = [];
    var updateCounter;
    var maxMsgs;
    // events
    var onMessage, onChara; /* onUpdateMessage, onUpdateChara, onUnloadMessage; */
    chat.onMessage = function(callback) { onMessage = callback; };
    chat.onChara = function(callback) { onChara = callback; };
    // properties
    Object.defineProperties(chat, {
      'url': {value: url},
      'title': {get: function() { return title; }},
      'desc': {get: function() { return desc; }},
      'charas': {get: function() { return charas; }},
      'msgs': {get: function() { return msgs; }},
      'maxMsgs': {get: function() { return maxMsgs; }}
    });
    // send message
    chat.sendMessage = function(content, voice, callback) {
      var data = { content: content };
      if(voice instanceof Chara) {
        data['type'] = 'chara';
        data.charaId = voice.id;
      }
      else {
        data['type'] = voice;
      }
      ajax(url + '/msg.json', 'POST', data, callback);
    };
    // send character
    chat.sendChara = function(name, color, callback) {
      var data = { name: name, color: color };
      ajax(url + '/chara.json', 'POST', data, callback);
    };

    // update loop
    function fetchUpdates() {
      ajax(url + '/updates.json', 'GET', { updateCounter: updateCounter }, function(data, req) {
        if (req.status === 200) processUpdates(data);
        setTimeout(fetchUpdates, 2000);
      });
    }
    function processUpdates(data) {
      // add new characters
      data.charas
        .map(function(x){return new Chara(x);})
        .forEach(function(chara) {
          charas[chara.id] = chara;
          onChara(chara);
        });
      // add new messages
      data.msgs
        .map(function(msg){
          msg.chara = charas[msg.charaId];
          return new Message(msg, charas);
        })
        .forEach(function(msg) {
          msgs.push(msg);
          onMessage(msg);
        });
      // update msg counter
      updateCounter = data.updateCounter;
    }

    // load...
    ajax(url + '.json', 'GET', function(data) {
      // set variables
      title = data.title;
      desc = data.desc;
      charas = data.charas.map(function(chara){ return new Chara(chara); });
      msgs = data.msgs.map(function(msg){
        msg.chara = charas[msg.charaId];
        return new Message(msg);
      });
      updateCounter = data.updateCounter;
      maxMsgs = data.postsPerPage;
      // callback
      if(onLoad) onLoad(chat);
      // start updating
      fetchUpdates();
    });
    // done.
  };

  // classes
  function Message(data) {
    Object.defineProperties(this, {
      'id': {value: data.id},
      'content': {value: data.content},
      'timeSent': {value: data.timestamp},
      'user': {value: new User(data.ipid)},
      'type': {value: data.type},
      'createElement': {value: function(timeFormat) {
        // outer element with the appropriate class
        var el = $('
', {
          'class': ({
            'narrator':'message message-narrator',
            'chara':'message message-chara',
            'ooc':'message message-ooc'
          })[this.type]
        });
        // character-specific
        if(this.type === 'chara') {
          // style
          if(this.chara) el.css({'background-color':this.chara.color, 'color':this.chara.textColor});
          // nametag
          el.append($('
', {
            'class': 'name',
            text: this.chara.name
          }));
        }
        // post details
        var ts = moment.unix(this.timestamp);
        if(!timeFormat || timeFormat === 'absolute') ts = ts.format('lll');
        else if(timeFormat === 'relative') ts = ts.calendar();
        else throw new Error('unknown time format');
        el.append(
          $('
', {'class': 'message-details'})
          // color ip box
          .append(this.user.createIcon())
          // timestamp
          .append($('', {
            'class': 'timestamp',
            text: ts
          }))
        );
        // message body
        el.append($('
', {
          'class': 'content',
          html: Message.formatContentReceived(this.content, this.chara)
        }));
        return el;
      }}
    });
    if(this.type==='chara') Object.defineProperties(this, {
      'charaId': {value: +data.charaId},
      'chara': {value: data.chara}
    });

  }
  function contrastColor(color) {
    return 'black';
  }
  function Chara(data) {
    Object.defineProperties(this, {
      'id': {value: data.id},
      'name': {value: data.name},
      'color': {value: data.color},
      'textColor': {value: contrastColor(data.color)},
      'user': {value: new User(data.ipid)},
      'createButton': {value: function(callback) {
        return $('<button/>', {
          text: this.name,
          'type': 'button',
          'style': 'background-color:' + this.color + ';' + 'color:' + this.textColor
        }).click(callback);
      }}
    });
  }
  function User(ipid) {
    Object.defineProperties(this, {
      'colors': {value: [
        ipid.substr(0,6),
        ipid.substr(6,6),
        ipid.substr(12,6)
      ]},
      'createIcon': {value: function(){
        return $('', { 'class': 'color-ip-box' })
          .append($('', { 'style': 'background-color: ' + this.colors[0] }))
          .append($('', { 'style': 'background-color: ' + this.colors[1] }))
          .append($('', { 'style': 'background-color: ' + this.colors[2] }));
      }}
    });
  }

  // format message content
  Message.formatContentReceived = function(text, chara) {
    // escape special characters
    var str = escapeHtml(text);
    // urls
    // http://stackoverflow.com/a/3890175
    str = str.replace(
      /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim,
      '$1'
    );
    // actions
    if(chara) {
      str = str.replace(/\*([^\r\n\*_]+)\*/g, '*$1*');
    }
    // bold
    str = str.replace(/(^|\s|(?:"))__([^\r\n_]+)__([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // italix
    str = str.replace(/(^|\s|(?:"))_([^\r\n_]+)_([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    str = str.replace(/(^|\s|(?:"))\/([^\r\n\/>]+)\/([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // both!
    str = str.replace(/(^|\s|(?:"))___([^\r\n_]+)___([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // line breaks
    // http://stackoverflow.com/questions/2919337/jquery-convert-line-breaks-to-br-nl2br-equivalent
    str = str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2');
    // mdash
    str = str.replace(/--/g, '—');

    // done.
    return str;
  };

  // escape html special chars from AJAX updates
  //  http://stackoverflow.com/questions/1787322/
  function escapeHtml(text) {
    var map = {
      '&': '&',
      '<': '<',
      '>': '>',
      '"': '"',
      "'": '''
    };
    return text.replace(/[&<>"']/g, function(m) { return map[m]; });
  }

  return rp;
})();

<commit_msg>Add contrastColor algorithm for character messages<commit_after>/* global $ moment */
var rp = (function() {
  var rp = {};

  var serverRootPath = (function() {
    var scripts = document.getElementsByTagName("script"),
      src = scripts[scripts.length-1].src;
    return src.substring(0, src.lastIndexOf('/'));
  })();

  // ajax requests
  // pieced together from: http://stackoverflow.com/questions/8567114/
  function ajax(url, method /*, data, callback */) {
    // get optional args
    var callback = null;
    var data = null;
    if(typeof(arguments[arguments.length-1]) === 'function')
      callback = arguments[arguments.length-1];
    if(arguments.length >= 3 && typeof(arguments[2]) === 'object')
      data = arguments[2];
    // request URL
    var reqUrl = serverRootPath + '/api/v1/rps/' + url;
    data = data || {};
    var req = new XMLHttpRequest();
    req.overrideMimeType('application/json');
    // callback function on success
    if(callback) req.onreadystatechange = function() {
      if(req.readyState === XMLHttpRequest.DONE && (''+req.status).startsWith('20')) {
        callback(req.responseText ? JSON.parse(req.responseText): '', req);
      }
    };
    // generate query string from data
    var queryString = null;
    if(data) {
      var query = [];
      for (var key in data) {
        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
      }
      queryString = query.join('&');
    }
    // apply things to request
    if(method === 'GET' && queryString) reqUrl += '?' + queryString;
    req.open(method, reqUrl, true);
    if(method === 'POST') req.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
    // send
    if(method === 'GET' || !queryString) req.send();
    else req.send(queryString);
  }

  // load a single page
  rp.fetchPage = function(url, pageNum, callback) {
    ajax(url + '/page/' + pageNum + '.json', 'GET', { page: pageNum }, function(data) {
      var charas = data.charas
        .map(function(chara) { return new Chara(chara); })
        .reduce(function(arr, chara) { arr[chara.id] = chara; return arr; }, []);
      var msgs = data.msgs.map(function(msg) {
        msg.chara = charas[msg.charaId];
        return new Message(msg);
      });
      // callback
      callback({ msgs: msgs, charas: charas });
    });
  };

  // load a chat object
  rp.chat = function(url, onLoad) {
    var chat = {};
    // chat variables
    var title, desc;
    var msgs = [];
    var charas = [];
    var updateCounter;
    var maxMsgs;
    // events
    var onMessage, onChara; /* onUpdateMessage, onUpdateChara, onUnloadMessage; */
    chat.onMessage = function(callback) { onMessage = callback; };
    chat.onChara = function(callback) { onChara = callback; };
    // properties
    Object.defineProperties(chat, {
      'url': {value: url},
      'title': {get: function() { return title; }},
      'desc': {get: function() { return desc; }},
      'charas': {get: function() { return charas; }},
      'msgs': {get: function() { return msgs; }},
      'maxMsgs': {get: function() { return maxMsgs; }}
    });
    // send message
    chat.sendMessage = function(content, voice, callback) {
      var data = { content: content };
      if(voice instanceof Chara) {
        data['type'] = 'chara';
        data.charaId = voice.id;
      }
      else {
        data['type'] = voice;
      }
      ajax(url + '/msg.json', 'POST', data, callback);
    };
    // send character
    chat.sendChara = function(name, color, callback) {
      var data = { name: name, color: color };
      ajax(url + '/chara.json', 'POST', data, callback);
    };

    // update loop
    function fetchUpdates() {
      ajax(url + '/updates.json', 'GET', { updateCounter: updateCounter }, function(data, req) {
        if (req.status === 200) processUpdates(data);
        setTimeout(fetchUpdates, 2000);
      });
    }
    function processUpdates(data) {
      // add new characters
      data.charas
        .map(function(x){return new Chara(x);})
        .forEach(function(chara) {
          charas[chara.id] = chara;
          onChara(chara);
        });
      // add new messages
      data.msgs
        .map(function(msg){
          msg.chara = charas[msg.charaId];
          return new Message(msg, charas);
        })
        .forEach(function(msg) {
          msgs.push(msg);
          onMessage(msg);
        });
      // update msg counter
      updateCounter = data.updateCounter;
    }

    // load...
    ajax(url + '.json', 'GET', function(data) {
      // set variables
      title = data.title;
      desc = data.desc;
      charas = data.charas.map(function(chara){ return new Chara(chara); });
      msgs = data.msgs.map(function(msg){
        msg.chara = charas[msg.charaId];
        return new Message(msg);
      });
      updateCounter = data.updateCounter;
      maxMsgs = data.postsPerPage;
      // callback
      if(onLoad) onLoad(chat);
      // start updating
      fetchUpdates();
    });
    // done.
  };

  // classes
  function Message(data) {
    Object.defineProperties(this, {
      'id': {value: data.id},
      'content': {value: data.content},
      'timeSent': {value: data.timestamp},
      'user': {value: new User(data.ipid)},
      'type': {value: data.type},
      'createElement': {value: function(timeFormat) {
        // outer element with the appropriate class
        var el = $('
', {
          'class': ({
            'narrator':'message message-narrator',
            'chara':'message message-chara',
            'ooc':'message message-ooc'
          })[this.type]
        });
        // character-specific
        if(this.type === 'chara') {
          // style
          if(this.chara) el.css({'background-color':this.chara.color, 'color':this.chara.textColor});
          // nametag
          el.append($('
', {
            'class': 'name',
            text: this.chara.name
          }));
        }
        // post details
        var ts = moment.unix(this.timestamp);
        if(!timeFormat || timeFormat === 'absolute') ts = ts.format('lll');
        else if(timeFormat === 'relative') ts = ts.calendar();
        else throw new Error('unknown time format');
        el.append(
          $('
', {'class': 'message-details'})
          // color ip box
          .append(this.user.createIcon())
          // timestamp
          .append($('', {
            'class': 'timestamp',
            text: ts
          }))
        );
        // message body
        el.append($('
', {
          'class': 'content',
          html: Message.formatContentReceived(this.content, this.chara)
        }));
        return el;
      }}
    });
    if(this.type==='chara') Object.defineProperties(this, {
      'charaId': {value: +data.charaId},
      'chara': {value: data.chara}
    });

  }
  function contrastColor(color) {
    //YIQ algorithm modified from:
    // http://24ways.org/2010/calculating-color-contrast/
    var components = [1,3,5].map(i => parseInt(color.substr(i, 2), 16));
    var yiq = components[0]*0.299 + components[1]*0.597 + components[2]*0.114;
    return (yiq >= 128) ? 'black' : 'white';
  }
  function Chara(data) {
    Object.defineProperties(this, {
      'id': {value: data.id},
      'name': {value: data.name},
      'color': {value: data.color},
      'textColor': {value: contrastColor(data.color)},
      'user': {value: new User(data.ipid)},
      'createButton': {value: function(callback) {
        return $('<button/>', {
          text: this.name,
          'type': 'button',
          'style': 'background-color:' + this.color + ';' + 'color:' + this.textColor
        }).click(callback);
      }}
    });
  }
  function User(ipid) {
    Object.defineProperties(this, {
      'colors': {value: [
        ipid.substr(0,6),
        ipid.substr(6,6),
        ipid.substr(12,6)
      ]},
      'createIcon': {value: function(){
        return $('', { 'class': 'color-ip-box' })
          .append($('', { 'style': 'background-color: ' + this.colors[0] }))
          .append($('', { 'style': 'background-color: ' + this.colors[1] }))
          .append($('', { 'style': 'background-color: ' + this.colors[2] }));
      }}
    });
  }

  // format message content
  Message.formatContentReceived = function(text, chara) {
    // escape special characters
    var str = escapeHtml(text);
    // urls
    // http://stackoverflow.com/a/3890175
    str = str.replace(
      /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim,
      '$1'
    );
    // actions
    if(chara) {
      str = str.replace(/\*([^\r\n\*_]+)\*/g, '*$1*');
    }
    // bold
    str = str.replace(/(^|\s|(?:"))__([^\r\n_]+)__([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // italix
    str = str.replace(/(^|\s|(?:"))_([^\r\n_]+)_([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    str = str.replace(/(^|\s|(?:"))\/([^\r\n\/>]+)\/([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // both!
    str = str.replace(/(^|\s|(?:"))___([^\r\n_]+)___([\s,\.\?!]|(?:")|$)/g, '$1$2$3');
    // line breaks
    // http://stackoverflow.com/questions/2919337/jquery-convert-line-breaks-to-br-nl2br-equivalent
    str = str.replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1
$2');
    // mdash
    str = str.replace(/--/g, '—');

    // done.
    return str;
  };

  // escape html special chars from AJAX updates
  //  http://stackoverflow.com/questions/1787322/
  function escapeHtml(text) {
    var map = {
      '&': '&',
      '<': '<',
      '>': '>',
      '"': '"',
      "'": '''
    };
    return text.replace(/[&<>"']/g, function(m) { return map[m]; });
  }

  return rp;
})();

<|endoftext|>