<commit_before>export const InjectableMixin = (superClass) => class extends superClass {
    static get inject() {
        return [];
    }

    initialize(...args) {
        super.initialize(...args);
        let inject = this.constructor.inject;
        if (Array.isArray(inject)) {
            inject.forEach((name) => this.inject(name));
        } else if (typeof inject === 'object') {
            for (let name in inject) {
                if (inject.hasOwnProperty(name)) {
                    this.inject(name, inject[name]);
                    this.inject(name);
                }
            }
        }
    }

    inject(name, Fn) {
        let owner = this;
        if (typeof this.getOwner === 'function') {
            owner = this.getOwner();
        }
        this.injected = this.injected || {};
        if (typeof Fn === 'undefined') {
            this.injected[name] = owner.factory(name);
        } else if (owner !== this) {
            owner.inject(name, Fn);
        } else {
            owner.injected[name] = new Fn(owner);
        }
    }

    factory(name) {
        return this.injected && this.injected[name];
    }
};
<commit_msg>feat: handle injectors<commit_after>export const InjectableMixin = (superClass) => class extends superClass {
    static get inject() {
        return [];
    }

    initialize(...args) {
        super.initialize(...args);
        if (this.constructor.injectors) {
            this.registerInject(this.constructor.injectors);
        }
        if (this.constructor.inject) {
            this.inject(this.constructor.inject);
        }
    }

    registerInject(inject, Fn) {
        if (Array.isArray(inject)) {
            inject.forEach((name) => this.registerInject(name));
        } else if (typeof inject === 'object') {
            for (let name in inject) {
                if (inject.hasOwnProperty(name)) {
                    this.registerInject(name, inject[name]);
                }
            }
        } else {
            let owner = this;
            if (typeof this.getOwner === 'function') {
                owner = this.getOwner();
            }
            owner.injected = owner.injected || {};
            owner.injected[inject] = new Fn(owner);
        }
    }

    inject(inject) {
        if (Array.isArray(inject)) {
            inject.forEach((name) => this.inject(name));
        } else if (typeof inject === 'object') {
            for (let name in inject) {
                if (inject.hasOwnProperty(name)) {
                    this.inject(name);
                }
            }
        } else {
            let owner = this;
            if (typeof this.getOwner === 'function') {
                owner = this.getOwner();
            }
            this.injected = this.injected || {};
            this.injected[inject] = owner.factory(inject);
        }
    }

    factory(name) {
        return this.injected && this.injected[name];
    }
};
<|endoftext|>


<commit_before>'use strict';

ocpu.seturl("https://public.opencpu.org/ocpu/library/stats/R");

var app = angular.module("app",['ui.ace']);

app.controller("controller", ["$scope",  function($scope) {
		$scope.aceOptions = {
			theme : 'solarized_dark',
			mode : 'r',
			useWrapMode : 'true'
		}
		$scope.example = "Example Works!";

        $scope.makecode = function(funct){
            switch(funct){
                case "rnorm":
                    $scope.code = "rnorm was selected";
                    break;
                case "qnorm":
                    $scope.code = "qnorm was selected";
                    break;
                case "pnorm":
                    $scope.code = "pnorm was selected";
                    break;
                default:
                    $scope.code = "function unknown";
            }
        }

        $scope.runcode = function(){

            ocpu.call("rnorm", {n:100}, function(session){
                session.getObject(function(data){
                    alert("First few values:" + data.slice(0,3));
                });
            });

            /*alert("runcode clicked");*/
        }

}]);

app.controller("controller2",["$scope", function($scope){

	$scope.functions = [
		{ 'name' : 'rnorm' , 'description' : 'description of rnorm'},
		{ 'name' : 'qnorm' , 'description' : 'description of qnorm'},
		{ 'name' : 'pnorm' , 'description' : 'description of pnorm'}
	];

	$scope.test = "Controller 2 works!"

    ;
}]);

<commit_msg>ui design<commit_after>'use strict';

ocpu.seturl("https://public.opencpu.org/ocpu/library/stats/R");

var app = angular.module("app",['ui.ace']);

app.controller("controller", ["$scope",  function($scope) {

        $scope.code = "This is your code editor";

		$scope.aceOptions = {
			theme : 'solarized_dark',
			mode : 'r',
			useWrapMode : 'true'
		}
		$scope.example = "Example Works!";

        $scope.makecode = function(funct){
            switch(funct){
                case "rnorm":
                    $scope.code = "rnorm was selected";
                    break;
                case "qnorm":
                    $scope.code = "qnorm was selected";
                    break;
                case "pnorm":
                    $scope.code = "pnorm was selected";
                    break;
                default:
                    $scope.code = "function unknown";
            }
        }

        $scope.runcode = function(){

            ocpu.call("rnorm", {n:100}, function(session){
                session.getObject(function(data){
                    alert("First few values:" + data.slice(0,3));
                });
            });

            /*alert("runcode clicked");*/
        }

}]);

app.controller("controller2",["$scope", function($scope){

	$scope.functions = [
		{ 'name' : 'rnorm' , 'description' : 'description of rnorm'},
		{ 'name' : 'qnorm' , 'description' : 'description of qnorm'},
		{ 'name' : 'pnorm' , 'description' : 'description of pnorm'}
	];

	$scope.test = "Controller 2 works!"

    ;
}]);

<|endoftext|>


<commit_before>var mirrorKey = require('../mirrorkey');

describe('MirrorKey', function() {
	it('throws an exception if the passed in argument is not an object or an array', function() {
		var errorMsg = 'The first argument to mirrorKey must be a object or an array.';

		expect(function() {
			mirrorKey(null);
		}).toThrow(errorMsg);

		expect(function() {
			mirrorKey(1);
		}).toThrow(errorMsg);

		expect(function() {
			mirrorKey('bob');
		}).toThrow(errorMsg);
	});

	it('throws an exception if transformType is unknown', function() {
		expect(function() {
			mirrorKey({}, '404-transform');
		}).toThrow('Unknown value for transformType. Valid values: none, camel-case, lower-case, dashed, lower-dashed');
	});

	it('returns an empty object if the argument is an empty object', function() {
		var result = mirrorKey({});

		expect(Object.keys(result).length).toEqual(0);
	});

	it('returns an empty object if the argument is an empty array', function() {
		var result = mirrorKey([]);

		expect(Object.keys(result).length).toEqual(0);
	});

	it('adds values to each key', function() {
		var result = mirrorKey({
			MY_KEY1: null,
			MY_KEY2: null
		});

		expect(result.MY_KEY1).toEqual('MY_KEY1');
		expect(result.MY_KEY2).toEqual('MY_KEY2');
	});

	it('adds values in camel-case', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'camel-case'
		);

		expect(result.MY_KEY1).toEqual('MyKey1');
	});

	it('adds values in lower-case', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'lower-case'
		);

		expect(result.MY_KEY1).toEqual('my_key1');
	});

	it('adds values in dashed', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'dashed'
		);

		expect(result.MY_KEY1).toEqual('MY-KEY1');
	});

	it('adds values in lower-dashed', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'lower-dashed'
		);

		expect(result.MY_KEY1).toEqual('my-key1');
	});
});<commit_msg>add test for arrays<commit_after>var mirrorKey = require('../mirrorkey');

describe('MirrorKey', function() {
	it('throws an exception if the passed in argument is not an object or an array', function() {
		var errorMsg = 'The first argument to mirrorKey must be a object or an array.';

		expect(function() {
			mirrorKey(null);
		}).toThrow(errorMsg);

		expect(function() {
			mirrorKey(1);
		}).toThrow(errorMsg);

		expect(function() {
			mirrorKey('bob');
		}).toThrow(errorMsg);
	});

	it('throws an exception if transformType is unknown', function() {
		expect(function() {
			mirrorKey({}, '404-transform');
		}).toThrow('Unknown value for transformType. Valid values: none, camel-case, lower-case, dashed, lower-dashed');
	});

	it('returns an empty object if the argument is an empty object', function() {
		var result = mirrorKey({});

		expect(Object.keys(result).length).toEqual(0);
	});

	it('returns an empty object if the argument is an empty array', function() {
		var result = mirrorKey([]);

		expect(Object.keys(result).length).toEqual(0);
	});

	it('adds values to each key', function() {
		var result = mirrorKey({
			MY_KEY1: null,
			MY_KEY2: null
		});

		expect(result.MY_KEY1).toEqual('MY_KEY1');
		expect(result.MY_KEY2).toEqual('MY_KEY2');
	});

	it('adds values to each key (array)', function() {
		var result = mirrorKey([
			'MY_KEY1',
			'MY_KEY2'
		]);

		expect(result.MY_KEY1).toEqual('MY_KEY1');
		expect(result.MY_KEY2).toEqual('MY_KEY2');
	});

	it('adds values in camel-case', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'camel-case'
		);

		expect(result.MY_KEY1).toEqual('MyKey1');
	});

	it('adds values in lower-case', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'lower-case'
		);

		expect(result.MY_KEY1).toEqual('my_key1');
	});

	it('adds values in dashed', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'dashed'
		);

		expect(result.MY_KEY1).toEqual('MY-KEY1');
	});

	it('adds values in lower-dashed', function() {
		var result = mirrorKey(
			{
				MY_KEY1: null
			},
			'lower-dashed'
		);

		expect(result.MY_KEY1).toEqual('my-key1');
	});
});<|endoftext|>


<commit_before>/**
* Tock by Mr Chimp - github.com/mrchimp/tock
* Based on code by James Edwards:
*    sitepoint.com/creating-accurate-timers-in-javascript/
*/
var Tock = (function(options) {
  var go = false,
      interval = options.interval || 10,
      countdown = options.countdown || false,
      final_time = 0,
      callback = options.callback,
      complete = options.complete;

  /**
   * Reset the clock
   */
  function reset() {
    if (countdown) {
      return false;
    }
    stop();
    start_time = 0;
    time = 0;
    elapsed = '0.0';
  }

  /**
   * Start the clock.
   */
  function start(time) {
    if (countdown) {
      _startCountdown(time);
    } else {
      _startTimer();
    }
  }

  /**
   * Called every tick for countdown clocks.
   * i.e. once every this.interval ms
   */
  function _tick() {
    time += interval;
    elapsed = Math.floor(time / interval) / 10;

    if (Math.round(elapsed) === elapsed) { elapsed += '.0'; }

    var t = this,
        diff = (Date.now() - start_time) - time,
        next_interval_in = interval - diff;

    if (callback !== undefined) {
      callback(this);
    }

    if (countdown && (duration_ms - time < 0)) {
      final_time = 0;
      go = false;
      complete();
    }

    if (next_interval_in <= 0) {
      missed_ticks = Math.floor(Math.abs(next_interval_in) / interval)
      time += missed_ticks * interval
      if (go) {
        _tick();
      }
    } else {
      if (go) {
        timeout = window.setTimeout(_tick, next_interval_in);
      }
    }
  }

  /**
   * Stop the clock.
   */
  function stop() {
    go = false;
    final_time = (Date.now() - start_time);
    window.clearTimeout(timeout);
  }

  /**
   * Get the current clock time in ms.
   * Use with Tock.msToTime() to make it look nice.
   */
  function lap() {
    if (go) {
      var now;

      if (countdown) {
        now = duration_ms - (Date.now() - start_time);
      } else {
        now = (Date.now() - start_time);
      }

      return now;
    }

    return final_time;
  }

  /**
   * Format milliseconds as a string.
   */
  function msToTime(ms) {
    if (ms <= 0) {
      return "00:00.000";
    }

    var milliseconds = (ms % 1000).toString(),
        seconds = Math.floor((ms / 1000) % 60).toString(),
        minutes = Math.floor((ms / (60 * 1000)) % 60).toString();

    if (milliseconds.length === 1) {
      milliseconds = '00' + milliseconds;
    } else if (milliseconds.length === 2) {
      milliseconds = '0' + milliseconds;
    }
    if (seconds.length === 1) {
      seconds = '0' + seconds;
    }
    if (minutes.length === 1) {
      minutes = '0' + minutes;
    }
    return minutes + ":" + seconds + "." + milliseconds;
  }

  /**
   * Convert a time string to milliseconds
   * Todo: handle this a bit better
   *
   * Possible inputs:
   * MM:SS
   * MM:SS:ms
   * yyyy-mm-dd HH:MM:SS.ms
   */
  function timeToMS(time) {
    var ms = new Date(time).getTime();

    if (!ms) {
      var time_split = time.split(':'),
          ms;

      ms = parseInt(time_split[0], 10) * 60000;

      if (time_split.length > 1) {
        ms += parseInt(time_split[1], 10) * 1000;
      }

      if (time_split.length > 2) {
        ms += parseInt(time_split[2], 10);
      }
    }

    return ms;
  }

  /**
   * Called by Tock internally - use start() instead
   */
  function _startCountdown(duration) {
    duration_ms = duration;
    start_time = Date.now();
    end_time = this.start_time + this.duration;
    time = 0;
    elapsed = '0.0';
    go = true;
    var t = this;
    this.timeout = window.setTimeout(_tick, 100);
  };

  /**
   * Called by Tock internally - use start() instead
   */
  function _startTimer() {
    start_time = Date.now();
    time = 0;
    elapsed = '0.0';
    go = true;
    var t = this;
    this.timeout = window.setTimeout(_tick, 100);
  }

  return {
    start: start,
    stop: stop,
    reset: reset,
    lap: lap,
    msToTime: msToTime,
    timeToMS: timeToMS
  };
});
<commit_msg>Fix multiple timeouts bug.<commit_after>/**
* Tock by Mr Chimp - github.com/mrchimp/tock
* Based on code by James Edwards:
*    sitepoint.com/creating-accurate-timers-in-javascript/
*/
var Tock = (function(options) {

  Tock.instances = (Tock.instances || 0) + 1;

  var go = false,
      interval = options.interval || 10,
      countdown = options.countdown || false,
      final_time = 0,
      start_time = 0,
      time = 0,
      elapsed = 0,
      callback = options.callback,
      complete = options.complete,
      prefix = "Tock" + Tock.instances;

  /**
   * Reset the clock
   */
  function reset() {
    if (countdown) {
      return false;
    }
    stop();
    start_time = 0;
    time = 0;
    elapsed = '0.0';
  }

  /**
   * Start the clock.
   */
  function start(time) {
    if (countdown) {
      _startCountdown(time);
    } else {
      _startTimer();
    }
  }

  /**
   * Called every tick for countdown clocks.
   * i.e. once every this.interval ms
   */
  function _tick() {
    time += interval;
    elapsed = Math.floor(time / interval) / 10;

    if (Math.round(elapsed) === elapsed) { elapsed += '.0'; }

    var t = this,
        diff = (Date.now() - start_time) - time,
        next_interval_in = interval - diff;

    if (callback !== undefined) {
      callback(this);
    }

    if (countdown && (duration_ms - time < 0)) {
      final_time = 0;
      go = false;
      complete();
    }

    if (next_interval_in <= 0) {
      missed_ticks = Math.floor(Math.abs(next_interval_in) / interval)
      time += missed_ticks * interval
      if (go) {
        _tick();
      }
    } else {
      if (go) {
        timeout = window.setTimeout(_tick, next_interval_in);
        timeout = prefix + timeout;
      }
    }
  }

  /**
   * Stop the clock.
   */
  function stop() {
    go = false;
    final_time = (Date.now() - start_time);
    window.clearTimeout(timeout.replace(prefix));
  }

  /**
   * Get the current clock time in ms.
   * Use with Tock.msToTime() to make it look nice.
   */
  function lap() {
    if (go) {
      var now;

      if (countdown) {
        now = duration_ms - (Date.now() - start_time);
      } else {
        now = (Date.now() - start_time);
      }

      return now;
    }

    return final_time;
  }

  /**
   * Format milliseconds as a string.
   */
  function msToTime(ms) {
    if (ms <= 0) {
      return "00:00.000";
    }

    var milliseconds = (ms % 1000).toString(),
        seconds = Math.floor((ms / 1000) % 60).toString(),
        minutes = Math.floor((ms / (60 * 1000)) % 60).toString();

    if (milliseconds.length === 1) {
      milliseconds = '00' + milliseconds;
    } else if (milliseconds.length === 2) {
      milliseconds = '0' + milliseconds;
    }
    if (seconds.length === 1) {
      seconds = '0' + seconds;
    }
    if (minutes.length === 1) {
      minutes = '0' + minutes;
    }
    return minutes + ":" + seconds + "." + milliseconds;
  }

  /**
   * Convert a time string to milliseconds
   * Todo: handle this a bit better
   *
   * Possible inputs:
   * MM:SS
   * MM:SS:ms
   * yyyy-mm-dd HH:MM:SS.ms
   */
  function timeToMS(time) {
    var ms = new Date(time).getTime();

    if (!ms) {
      var time_split = time.split(':'),
          ms;

      ms = parseInt(time_split[0], 10) * 60000;

      if (time_split.length > 1) {
        ms += parseInt(time_split[1], 10) * 1000;
      }

      if (time_split.length > 2) {
        ms += parseInt(time_split[2], 10);
      }
    }

    return ms;
  }

  /**
   * Called by Tock internally - use start() instead
   */
  function _startCountdown(duration) {
    duration_ms = duration;
    start_time = Date.now();
    end_time = this.start_time + this.duration;
    time = 0;
    elapsed = '0.0';
    go = true;
    var t = this;
    this.timeout = window.setTimeout(_tick, 100);
  };

  /**
   * Called by Tock internally - use start() instead
   */
  function _startTimer() {
    start_time = Date.now();
    time = 0;
    elapsed = '0.0';
    go = true;
    var t = this;
    this.timeout = window.setTimeout(_tick, 100);
  }

  return {
    start: start,
    stop: stop,
    reset: reset,
    lap: lap,
    msToTime: msToTime,
    timeToMS: timeToMS
  };
});
<|endoftext|>


<commit_before>var   util = require('../../util/util')
    , conditions = require('./conditions')

var internal = {};
    internal._unknown = -1;

exports.parse = function parse(flow, source, depends, existing) {

        //start with given value if any
    existing = existing || {};

        //check for the defines flag on the build object
    var list = [];
        //first check the root defines
    var node = source.project.build;
    if(node) {
        for(index in node.defines) {
            list.push({
                name:source.project.build.defines[index],
                file:source.__path
            });
        }
    }

        //then check for any if: conditional
    if(source.if) {
        for(flag in source.if) {
            var node = source.if[flag].build;
            if(node && node.defines) {
                for(index in node.defines) {
                    list.push({
                        name:node.defines[index],
                        condition:flag,
                        file:source.__path
                    });
                }
            } //node && node.defines
        } //flag in if
    } //if source.if

        //for each define, parse it
    for(index in list) {

        var define = internal.parse_define(list[index]);

            //only store if not found, to respect child project overrides
        if(!existing[define.name]) {
            existing[define.name] = define;
        }

    } //each in the list

        //parse any dependency tree, if available
    if(depends) {
        for(name in depends) {
            var depend = depends[name];
                //possible for a null project,
                //i.e no *.flow in dependency
            if(depend.project) {
                existing = exports.parse(flow, depend.project, null, existing);
            }
        }
    } //depends

    return existing;

} //defines

    //post parsing strip based on existing defines + known targets
exports.filter = function filter(flow, defines) {

    var results = {};

    internal.resolve_defines(flow, defines);

        //now push a simplified list as the results, only by met type
    for(name in defines) {
        var define = defines[name];
        if(define.met == true) {
            results[name] = { name:define.name };
            if(define.value) {
                results[name].value = define.value
            };
        }
    }

    return results;

}

    // just satisfy the single condition. this should be called only after
    // the initial conditionals have all been parsed
exports.satisfy = function satisfy(flow, prepared, condition) {

    flow.log(5, 'defines - satisfy check', condition);

    var cond = conditions.conditions[condition];
    if(cond && cond.length > 1) {
        return internal.resolve_multi(flow, prepared.defines_all, cond)
    } else {
        return prepared.defines[condition] ? true : false;
    }

} //satisfy

internal.resolve_single = function(flow, defines, define) {

    var condition = define.condition;
    var inverse = false;

        //if this is an inverse condition
    if(condition.indexOf('!') != -1) {
        inverse = true;
        condition = define.condition.replace('!','').trim();
    }

        //if its even found at all
    if(defines[condition]) {

            //check if the define is known yet
        if(defines[condition].met == -1) {
            flow.log(4, '   conditional unknown yet %s / %s', define.name, condition);
            return -1;
        }

        if(defines[condition].met === true) {
            return inverse ? false : true;
        } else {
            return inverse ? true : false;
        }

    } else {
        flow.log(4, 'defines - condition against %s failed as its not found at all', condition);
        return false;
    }

} //resolve_single

internal.resolve_multi = function(flow, defines_all, tokenized) {

    //first we check if ALL of the defines in question
        //are unknown, there is nothing we can do yet
    var is_unknown = true;
    for(index in tokenized) {
        var tokened = tokenized[index];
        var define = defines_all[tokened.condition];
        if(define) {
            if(define.met != -1) {
                is_unknown = false;
            }
        } else {
            defines_all[tokened.condition] = { name:tokened.condition, met:false, nonexistant:true };
            is_unknown = false;
        }
    }

        //all of the potential flags in question are unknown, so don't bother
    if(is_unknown) {
        flow.log(2, 'defines - unknown flags for all conditions of ', tokenized);
        return -1;
    }

        //for each step in the condition, we have a final resulting value of true or false
    var state;

    for(index in tokenized) {

            //we only want to do n-1 amount,
            //because we look ahead at the next item.
            //we can assume there will be > 1 because of the check
            //before calling this function
        if(index == tokenized.length-1) {
            continue;
        }

        var i = parseInt(index);
        var tokened = tokenized[i];
        var next = tokenized[i+1];

        var curdef = defines_all[tokened.condition];
        var nextdef = defines_all[next.condition];

            //if both are known, we can build state
        if(curdef.met != -1 && nextdef.met != -1) {

            var curmet = tokened.inverse ? !curdef.met : curdef.met;
            var nextmet = next.inverse ? !nextdef.met : nextdef.met;

            if(state === undefined) {
                state = curmet;
            }

            if(tokened.as == '||') {
                state = state || (curmet || nextmet);
            } else if(tokened.as == '&&') {
                state = state && (curmet && nextmet);
            }

        } else {

                //one of these isn't known yet, if it's an || and one of them is met
                //we can already determine that the condition is met by or logic
            if(tokened.as == '||') {

                if(curdef.met == -1) {
                    var nextmet = next.inverse ? !nextdef.met : nextdef.met;
                    if(nextmet === true) {
                        return true;
                    }
                } else if(nextdef.met == -1) {
                    var curmet = tokened.inverse ? !curdef.met : curdef.met;
                    if(curmet === true) {
                        return true;
                    }
                }

            } //if ||

        } //curdef.met !-1 nextdef.met !-1

    } //each tokenized

    flow.log(5, 'defines - satisfy multi condition state', state);

    return state;

} //resolve_multi

    //walk down the list attempting to satisfy each
    //condition in the list. if a condition is met,
    //it is removed from the working list
internal.resolve_defines = function(flow, defines) {

    var found_unknown = true;
    var max_depth = 20;
    var depth = 0;

    while(found_unknown && (depth<max_depth)) {

            //first do all simple conditions to flatten the
            //amount of unresolved conditionals

        for(name in defines) {
            var define = defines[name];
            if(define.condition) {
                var condition = conditions.conditions[define.condition];
                if(condition && condition.length == 1) {
                    define.met = internal.resolve_single(flow, defines, define);
                } //if condition
            } else {
                if(define.met === undefined || define.met == -1) {
                    define.met = true;
                }
            }
        } //each define

            //then run against all complex conditionals

        for(name in defines) {
            var define = defines[name];
            if(define.condition) {
                var condition = conditions.conditions[define.condition];
                if(condition && condition.length > 1) {
                    flow.log(5, 'defines - satisfy check', define.condition);
                    define.met = internal.resolve_multi(flow, defines, condition);
                } //condition.as
            }
        } //each define

        var still_unknown = false;
        for(name in defines) {
            if(defines[name].met == -1) {
                flow.log(5, 'defines - unknown found %s', name);
                still_unknown = true;
                break;
            }
        }

        if(!still_unknown) {
            flow.log(5, 'defines - stopping because found no more unknowns');
            found_unknown = false;
        } else {
            flow.log(5, 'defines - still found unknowns');
        }

        depth++;

    } //while found unknowns or reached a max depth

    if(depth >= max_depth) {
        flow.log(5, 'defines - stopping because too many loops in define tree(%d). check for cyclic references in your defines', max_depth);
    }

} //satisfy conditions

internal.parse_define = function(def) {

    var split = def.name.split('=');
    for(item in split) {
        split[item] = split[item].trim();
    }

    var define = {
        name : split[0],
        file : def.file,
        met : internal._unknown
    };

    if(split.length > 1) {
        define.value = split[1];
    }

    if(def.condition) {
        define.condition = def.condition;
    }

    return define;

} //parse_define<commit_msg>defines; fix single condition flags with not (!) character from always returning false<commit_after>var   util = require('../../util/util')
    , conditions = require('./conditions')

var internal = {};
    internal._unknown = -1;

exports.parse = function parse(flow, source, depends, existing) {

        //start with given value if any
    existing = existing || {};

        //check for the defines flag on the build object
    var list = [];
        //first check the root defines
    var node = source.project.build;
    if(node) {
        for(index in node.defines) {
            list.push({
                name:source.project.build.defines[index],
                file:source.__path
            });
        }
    }

        //then check for any if: conditional
    if(source.if) {
        for(flag in source.if) {
            var node = source.if[flag].build;
            if(node && node.defines) {
                for(index in node.defines) {
                    list.push({
                        name:node.defines[index],
                        condition:flag,
                        file:source.__path
                    });
                }
            } //node && node.defines
        } //flag in if
    } //if source.if

        //for each define, parse it
    for(index in list) {

        var define = internal.parse_define(list[index]);

            //only store if not found, to respect child project overrides
        if(!existing[define.name]) {
            existing[define.name] = define;
        }

    } //each in the list

        //parse any dependency tree, if available
    if(depends) {
        for(name in depends) {
            var depend = depends[name];
                //possible for a null project,
                //i.e no *.flow in dependency
            if(depend.project) {
                existing = exports.parse(flow, depend.project, null, existing);
            }
        }
    } //depends

    return existing;

} //defines

    //post parsing strip based on existing defines + known targets
exports.filter = function filter(flow, defines) {

    var results = {};

    internal.resolve_defines(flow, defines);

        //now push a simplified list as the results, only by met type
    for(name in defines) {
        var define = defines[name];
        if(define.met == true) {
            results[name] = { name:define.name };
            if(define.value) {
                results[name].value = define.value
            };
        }
    }

    return results;

}

    // just satisfy the single condition. this should be called only after
    // the initial conditionals have all been parsed
exports.satisfy = function satisfy(flow, prepared, condition) {

    flow.log(5, 'defines - satisfy check', condition);

    var cond = conditions.conditions[condition];
    if(cond && cond.length > 1) {

        return internal.resolve_multi(flow, prepared.defines_all, cond);

    } else {

        var satisfied = false;
           //check if this is an inverse condition
        var inverse = false;
        if(condition.indexOf('!') != -1) {
            inverse = true;
            condition = condition.replace('!','').trim();
        }

        var defines = prepared.defines_all;
        var define = defines[condition];

        if(define) {
            if(defines[condition].met === true) {
                satisfied = inverse ? false : true;
            } else {
                satisfied = inverse ? true : false;
            }
        } else {
                //not defined, so if the inverse is queried, return true
            if(inverse) satisfied = true;
        }

        flow.log(5, 'defines - single condition satisfy result? %s ', satisfied);

        return satisfied;

    } //multi condition

} //satisfy

internal.resolve_single = function(flow, defines, define) {

    var condition = define.condition;
    var inverse = false;

        //if this is an inverse condition
    if(condition.indexOf('!') != -1) {
        inverse = true;
        condition = define.condition.replace('!','').trim();
    }

        //if its even found at all
    if(defines[condition]) {

            //check if the define is known yet
        if(defines[condition].met == -1) {
            flow.log(4, '   conditional unknown yet %s / %s', define.name, condition);
            return -1;
        }

        if(defines[condition].met === true) {
            return inverse ? false : true;
        } else {
            return inverse ? true : false;
        }

    } else {
        if(!inverse) {
            flow.log(4, 'defines - condition against %s failed as its not found at all', condition);
            return false;
        } else {
            flow.log(4, 'defines - condition against %s success, its not defined, but had !condition', condition);
            return true;
        }
    }

} //resolve_single

internal.resolve_multi = function(flow, defines_all, tokenized) {

    //first we check if ALL of the defines in question
        //are unknown, there is nothing we can do yet
    var is_unknown = true;
    for(index in tokenized) {
        var tokened = tokenized[index];
        var define = defines_all[tokened.condition];
        if(define) {
            if(define.met != -1) {
                is_unknown = false;
            }
        } else {
            defines_all[tokened.condition] = { name:tokened.condition, met:false, nonexistant:true };
            is_unknown = false;
        }
    }

        //all of the potential flags in question are unknown, so don't bother
    if(is_unknown) {
        flow.log(2, 'defines - unknown flags for all conditions of ', tokenized);
        return -1;
    }

        //for each step in the condition, we have a final resulting value of true or false
    var state;

    for(index in tokenized) {

            //we only want to do n-1 amount,
            //because we look ahead at the next item.
            //we can assume there will be > 1 because of the check
            //before calling this function
        if(index == tokenized.length-1) {
            continue;
        }

        var i = parseInt(index);
        var tokened = tokenized[i];
        var next = tokenized[i+1];

        var curdef = defines_all[tokened.condition];
        var nextdef = defines_all[next.condition];

            //if both are known, we can build state
        if(curdef.met != -1 && nextdef.met != -1) {

            var curmet = tokened.inverse ? !curdef.met : curdef.met;
            var nextmet = next.inverse ? !nextdef.met : nextdef.met;

            if(state === undefined) {
                state = curmet;
            }

            if(tokened.as == '||') {
                state = state || (curmet || nextmet);
            } else if(tokened.as == '&&') {
                state = state && (curmet && nextmet);
            }

        } else {

                //one of these isn't known yet, if it's an || and one of them is met
                //we can already determine that the condition is met by or logic
            if(tokened.as == '||') {

                if(curdef.met == -1) {
                    var nextmet = next.inverse ? !nextdef.met : nextdef.met;
                    if(nextmet === true) {
                        return true;
                    }
                } else if(nextdef.met == -1) {
                    var curmet = tokened.inverse ? !curdef.met : curdef.met;
                    if(curmet === true) {
                        return true;
                    }
                }

            } //if ||

        } //curdef.met !-1 nextdef.met !-1

    } //each tokenized

    flow.log(5, 'defines - satisfy multi condition state', state);

    return state;

} //resolve_multi

    //walk down the list attempting to satisfy each
    //condition in the list. if a condition is met,
    //it is removed from the working list
internal.resolve_defines = function(flow, defines) {

    var found_unknown = true;
    var max_depth = 20;
    var depth = 0;

    while(found_unknown && (depth<max_depth)) {

            //first do all simple conditions to flatten the
            //amount of unresolved conditionals

        for(name in defines) {
            var define = defines[name];
            if(define.condition) {
                var condition = conditions.conditions[define.condition];
                if(condition && condition.length == 1) {
                    define.met = internal.resolve_single(flow, defines, define);
                } //if condition
            } else {
                if(define.met === undefined || define.met == -1) {
                    define.met = true;
                }
            }
        } //each define

            //then run against all complex conditionals

        for(name in defines) {
            var define = defines[name];
            if(define.condition) {
                var condition = conditions.conditions[define.condition];
                if(condition && condition.length > 1) {
                    flow.log(5, 'defines - satisfy check', define.condition);
                    define.met = internal.resolve_multi(flow, defines, condition);
                } //condition.as
            }
        } //each define

        var still_unknown = false;
        for(name in defines) {
            if(defines[name].met == -1) {
                flow.log(5, 'defines - unknown found %s', name);
                still_unknown = true;
                break;
            }
        }

        if(!still_unknown) {
            flow.log(5, 'defines - stopping because found no more unknowns');
            found_unknown = false;
        } else {
            flow.log(5, 'defines - still found unknowns');
        }

        depth++;

    } //while found unknowns or reached a max depth

    if(depth >= max_depth) {
        flow.log(5, 'defines - stopping because too many loops in define tree(%d). check for cyclic references in your defines', max_depth);
    }

} //satisfy conditions

internal.parse_define = function(def) {

    var split = def.name.split('=');
    for(item in split) {
        split[item] = split[item].trim();
    }

    var define = {
        name : split[0],
        file : def.file,
        met : internal._unknown
    };

    if(split.length > 1) {
        define.value = split[1];
    }

    if(def.condition) {
        define.condition = def.condition;
    }

    return define;

} //parse_define<|endoftext|>


<commit_before>import _find from 'lodash/find'
import _forEach from 'lodash/forEach'
import _kebabCase from 'lodash/kebabCase'

export default class Card {
  constructor (card) {
    this.update(card)
    this.image = `http://gatherer.wizards.com/Handlers/Image.ashx?type=card&multiverseid=${this.multiverseid}`
    this.setIcon = `ss ss-${card.setCode} ss-${card.rarity.toLowerCase()}`
    if (!this.cardUrl) this.cardUrl = _kebabCase(this.imageName)
  }

  update (raw) {
    for (const prop in raw) this[prop] = raw[prop]
  }

  decrease (variantId) {
    // This mutates the object, so make sure to call it on a copied card
    const variantToUpdate = _find(this.variants, { id: variantId })
    variantToUpdate.cardsInCollection--
    this.cardsInCollection--
  }

  copy () {
    return new Card(this)
  }

  formatForFirebase () {
    const { cardsInCollection, variants } = this

    const formattedCard = { cardsInCollection }

    if (variants) {
      formattedCard.variants = {}
      _forEach(variants, variant => {
        formattedCard.variants[variant.id] = variant.formatForFirebase()
      })
    }

    return formattedCard
  }
}
<commit_msg>Added alternative path for fetching images<commit_after>import _find from 'lodash/find'
import _forEach from 'lodash/forEach'
import _kebabCase from 'lodash/kebabCase'

export default class Card {
  constructor (card) {
    this.update(card)
    this.image = `http://gatherer.wizards.com/Handlers/Image.ashx?type=card&multiverseid=${this.multiverseid}`
    // this.image = `http://magiccards.info/scans/en/${card.setCode}/${card.number}.jpg`
    this.setIcon = `ss ss-${card.setCode} ss-${card.rarity.toLowerCase()}`
    if (!this.cardUrl) this.cardUrl = _kebabCase(this.imageName)
  }

  update (raw) {
    for (const prop in raw) this[prop] = raw[prop]
  }

  decrease (variantId) {
    // This mutates the object, so make sure to call it on a copied card
    const variantToUpdate = _find(this.variants, { id: variantId })
    variantToUpdate.cardsInCollection--
    this.cardsInCollection--
  }

  copy () {
    return new Card(this)
  }

  formatForFirebase () {
    const { cardsInCollection, variants } = this

    const formattedCard = { cardsInCollection }

    if (variants) {
      formattedCard.variants = {}
      _forEach(variants, variant => {
        formattedCard.variants[variant.id] = variant.formatForFirebase()
      })
    }

    return formattedCard
  }
}
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * 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.
 */

 foam.CLASS({
  package: 'foam.u2.view',
  name: 'ScrollTableView',
  extends: 'foam.u2.Element',

  requires: [
    'foam.u2.view.TableView',
    'foam.graphics.ScrollCView',
    'foam.dao.FnSink',
    'foam.mlang.sink.Count',
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'Int',
      name: 'limit',
      value: 30,
      // TODO make this a funciton of the height.
    },
    {
      class: 'Int',
      name: 'skip',
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'scrolledDao',
      expression: function(data, limit, skip) {
        return data.limit(limit).skip(skip);
      },
    },
    {
      name: 'scrollView',
      factory: function() {
        var self = this;
        return this.ScrollCView.create({
          value$: this.skip$,
          extent$: this.limit$,
          height: 600, // TODO use window height.
          width: 40,
          handleSize: 40,
          // TODO wire up mouse wheel
          // TODO clicking away from scroller should deselect it.
        });
      },
    },
    {
      name: 'tableView',
      factory: function() {
        return this.TableView.create({data$: this.scrolledDao$});
      },
    },
  ],

  listeners: [
    {
      // TODO Avoid onDaoUpdate approaches.
      name: 'onDaoUpdate',
      isFramed: true,
      code: function() {
        var self = this;
        this.data$proxy.select(this.Count.create()).then(function(s) {
          self.scrollView.size = s.value;
        })
      },
    },
  ],

  methods: [
    function init() {
      this.onDetach(this.data$proxy.pipe(this.FnSink.create({fn:this.onDaoUpdate})));
    },
    function initE() {
      // TODO probably shouldn't be using a table.
      this.start('table').
        start('tr').
          start('td').style({ 'vertical-align': 'top' }).add(this.tableView).end().
          start('td').add(this.scrollView).end().
        end().
      end();
    }
  ]
});
<commit_msg>Formatting.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 *
 * 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.
 */

 foam.CLASS({
  package: 'foam.u2.view',
  name: 'ScrollTableView',
  extends: 'foam.u2.Element',

  requires: [
    'foam.dao.FnSink',
    'foam.graphics.ScrollCView',
    'foam.mlang.sink.Count',
    'foam.u2.view.TableView'
  ],

  properties: [
    {
      class: 'foam.dao.DAOProperty',
      name: 'data'
    },
    {
      class: 'Int',
      name: 'limit',
      value: 30,
      // TODO make this a funciton of the height.
    },
    {
      class: 'Int',
      name: 'skip',
    },
    {
      class: 'foam.dao.DAOProperty',
      name: 'scrolledDao',
      expression: function(data, limit, skip) {
        return data.limit(limit).skip(skip);
      },
    },
    {
      name: 'scrollView',
      factory: function() {
        var self = this;
        return this.ScrollCView.create({
          value$: this.skip$,
          extent$: this.limit$,
          height: 600, // TODO use window height.
          width: 40,
          handleSize: 40,
          // TODO wire up mouse wheel
          // TODO clicking away from scroller should deselect it.
        });
      },
    },
    {
      name: 'tableView',
      factory: function() {
        return this.TableView.create({data$: this.scrolledDao$});
      },
    },
  ],

  listeners: [
    {
      // TODO Avoid onDaoUpdate approaches.
      name: 'onDaoUpdate',
      isFramed: true,
      code: function() {
        var self = this;
        this.data$proxy.select(this.Count.create()).then(function(s) {
          self.scrollView.size = s.value;
        })
      },
    },
  ],

  methods: [
    function init() {
      this.onDetach(this.data$proxy.pipe(this.FnSink.create({fn:this.onDaoUpdate})));
    },

    function initE() {
      // TODO probably shouldn't be using a table.
      this.start('table').
        start('tr').
          start('td').style({ 'vertical-align': 'top' }).add(this.tableView).end().
          start('td').add(this.scrollView).end().
        end().
      end();
    }
  ]
});
<|endoftext|>


<commit_before>/**
 * @class ApplicationFactoryTests
 *
 * @author: darryl.west@raincitysoftware.com
 * @created: 3/22/14 7:07 AM
 */
var should = require('chai').should(),
    dash = require('lodash' ),
    Config = require('../../app/controllers/Config' ),
    ApplicationFactory = require('../../app/controllers/ApplicationFactory' );

describe('ApplicationFactory', function() {
    'use strict';

    var createOptions = function() {
        var opts = Config.test();

        // logging...

        return opts;
    };

    describe('#instance', function() {
        var factory = new ApplicationFactory( createOptions() ),
            methods = [
                'createConfigurationWebService',
                'createConfigurationDataService',
                'createConfigurationDao',
                'createDataSourceFactory',
                'createWebServices',
                'startWebApplication',
                // inherited methods
                'addService',
                'assignRoutes',
                'createIndexPageService',
                'createLogManager',
                'createLogger',
                'createMiddlewareDelegate',
                'createRoutePath',
                'createValidator',
                'createWebServices',
                'createWebStatusService',
                'findService',
                'getConfiguration',
                'getServiceCount',
                'getServices'
            ];

        it('should create an instance of ApplicationFactory', function() {
            should.exist( factory );

            factory.should.be.instanceof( ApplicationFactory );
        });

        it('should have all expected methods by size', function() {
            dash.methods( factory ).length.should.equal( methods.length );
        });
    });
});<commit_msg>added factory tests<commit_after>/**
 * @class ApplicationFactoryTests
 *
 * @author: darryl.west@raincitysoftware.com
 * @created: 3/22/14 7:07 AM
 */
var should = require('chai').should(),
    dash = require('lodash' ),
    Config = require('../../app/controllers/Config' ),
    ApplicationFactory = require('../../app/controllers/ApplicationFactory' );

describe('ApplicationFactory', function() {
    'use strict';

    var createOptions = function() {
        var opts = Config.test();

        // logging...

        return opts;
    };

    describe('#instance', function() {
        var factory = new ApplicationFactory( createOptions() ),
            methods = [
                'createConfigurationWebService',
                'createConfigurationDataService',
                'createConfigurationDao',
                'createDataSourceFactory',
                'createWebServices',
                'startWebApplication',
                // inherited methods
                'addService',
                'assignRoutes',
                'createIndexPageService',
                'createLogManager',
                'createLogger',
                'createMiddlewareDelegate',
                'createRoutePath',
                'createValidator',
                'createWebServices',
                'createWebStatusService',
                'findService',
                'getConfiguration',
                'getServiceCount',
                'getServices'
            ];

        it('should create an instance of ApplicationFactory', function() {
            should.exist( factory );

            factory.should.be.instanceof( ApplicationFactory );
        });

        it('should have all expected methods by size', function() {
            dash.methods( factory ).length.should.equal( methods.length );
        });

        it('should execute all known methods', function() {
            var obj;

            methods.forEach(function(method) {
                if (method === 'createRoutePath') {
                    obj = factory.createRoutePath('this', 'that');
                    should.exist( obj );
                } else if (method.indexOf('create') === 0) {
                    obj = factory[ method ]();
                    should.exist( obj );
                } else if (method.indexOf('get') === 0) {
                    factory[ method ]();
                }
            });
        });
    });

    describe('#Configuration', function() {
        it('should create configuration dao');
        it('should create configuration data service');
        it('should create configuration web service');
    });
});<|endoftext|>


<commit_before>// var chai = require('chai'),
//  expect = chai.expect;

var expect = require('chai').expect;

// I can list my tasks...
var taskList = [ ];

function listTasks() {

}

// I can add a task to my list

function addTaskToList(task, list){
  // what is the task? @param task
  // where is the task going? @param list
  // what order / priority? on the bottom
    return list.push(task);
}

expect(taskList.length).to.equal(0);
addTaskToList("Remember the milk", taskList);
expect(taskList[0]).to.equal("Remember the milk");
expect(taskList.length).to.equal(1);

// expect what?
addTaskToList("Take out the trash", taskList)
// expectt what now?
console.log(taskList[0]);

// testing git add, commit and push
<commit_msg>Added tasks to task list<commit_after>// var chai = require('chai'),
//  expect = chai.expect;

var expect = require('chai').expect;

// I can list my tasks...
var taskList = [ ];

function listTasks() {

}

// I can add a task to my list

function addTaskToList(task, list){
  // what is the task? @param task
  // where is the task going? @param list
  // what order / priority? on the bottom
    return list.push(task);
}

expect(taskList.length).to.equal(0);
addTaskToList("Remember the milk", taskList);
expect(taskList[0]).to.equal("Remember the milk");
expect(taskList.length).to.equal(1);

// expect what?
expect(taskList.length).to.equal(1);
addTaskToList("Take out the trash", taskList);
expect(taskList[1]).to.equal("Take out the trash");
expect(taskList.length).to.equal(2);

expect(taskList.length).to.equal(2);
addTaskToList("Clean the bathroom", taskList);
expect(taskList[2]).to.equal("Clean the bathroom");
expect(taskList.length).to.equal(3);

expect(taskList.length).to.equal(3);
addTaskToList("Feed the dog", taskList);
expect(taskList[3]).to.equal("Feed the dog");
expect(taskList.length).to.equal(4);
// expectt what now?
console.log(taskList[0]);
console.log(taskList[1]);
console.log(taskList[2]);
console.log(taskList[3]);

// testing git add, commit and push
<|endoftext|>


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

  this.wrap = function(elem, wrapper) {
    elem.parentNode.insertBefore(wrapper, elem);
    wrapper.appendChild(elem);
  }

  this.unwrap = function(elem) {
    var wrapper = elem.parentNode;
    wrapper.parentNode.replaceChild(elem, wrapper);
  }

  this.createElement = function(tagName, className) {
    var elem = document.createElement(tagName);
    elem.classList.add(className);
    return elem;
  }

  // For old browsers
  this.each = function(node, callback) {
    var array = Array.prototype.slice.call(node);
    for(var i = 0; i < array.length; i++) callback(array[i]);
  }

  // Viewable on local
  if (window.location.protocol === "file:") Turbolinks.supported = false;

  document.addEventListener("turbolinks:load", function() {
    var guidesMenu = document.getElementById("guidesMenu");
    var guides     = document.getElementById("guides");

    guidesMenu.addEventListener("click", function(e) {
      e.preventDefault();
      guides.classList.toggle("visible");
    });

    each(document.querySelectorAll("#guides a"), function(element) {
      element.addEventListener("click", function(e) {
        guides.classList.toggle("visible");
      });
    });

    document.addEventListener("keyup", function(e) {
      if (e.key === "Escape" && guides.classList.contains("visible")) {
        guides.classList.remove("visible");
      }
    });

    var guidesIndexItem   = document.querySelector("select.guides-index-item");
    var currentGuidePath  = window.location.pathname;
    guidesIndexItem.value = currentGuidePath.substring(currentGuidePath.lastIndexOf("/") + 1);

    guidesIndexItem.addEventListener("change", function(e) {
      if (Turbolinks.supported) {
        Turbolinks.visit(e.target.value);
      } else {
        window.location = e.target.value;
      }
    });

    var moreInfoButton = document.querySelector(".more-info-button");
    var moreInfoLinks  = document.querySelector(".more-info-links");

    moreInfoButton.addEventListener("click", function(e) {
      e.preventDefault();

      if (moreInfoLinks.classList.contains("s-hidden")) {
        wrap(moreInfoLinks, createElement("div", "more-info-container"));
        moreInfoLinks.classList.remove("s-hidden");
      } else {
        moreInfoLinks.classList.add("s-hidden");
        unwrap(moreInfoLinks);
      }
    });

    var clipboard = new ClipboardJS('.clipboard-button');
    clipboard.on('success', function(e) {
      var trigger = e.trigger;
      var triggerLabel = trigger.innerHTML;
      trigger.innerHTML = 'Copied!';
      setTimeout(function(){
        trigger.innerHTML = triggerLabel;
      }, 3000);
      e.clearSelection();
    });
  });
}).call(this);
<commit_msg>Show Guides Index as selected for `/` on small screens [ci-skip]<commit_after>(function() {
  "use strict";

  this.wrap = function(elem, wrapper) {
    elem.parentNode.insertBefore(wrapper, elem);
    wrapper.appendChild(elem);
  }

  this.unwrap = function(elem) {
    var wrapper = elem.parentNode;
    wrapper.parentNode.replaceChild(elem, wrapper);
  }

  this.createElement = function(tagName, className) {
    var elem = document.createElement(tagName);
    elem.classList.add(className);
    return elem;
  }

  // For old browsers
  this.each = function(node, callback) {
    var array = Array.prototype.slice.call(node);
    for(var i = 0; i < array.length; i++) callback(array[i]);
  }

  // Viewable on local
  if (window.location.protocol === "file:") Turbolinks.supported = false;

  document.addEventListener("turbolinks:load", function() {
    var guidesMenu = document.getElementById("guidesMenu");
    var guides     = document.getElementById("guides");

    guidesMenu.addEventListener("click", function(e) {
      e.preventDefault();
      guides.classList.toggle("visible");
    });

    each(document.querySelectorAll("#guides a"), function(element) {
      element.addEventListener("click", function(e) {
        guides.classList.toggle("visible");
      });
    });

    document.addEventListener("keyup", function(e) {
      if (e.key === "Escape" && guides.classList.contains("visible")) {
        guides.classList.remove("visible");
      }
    });

    var guidesIndexItem   = document.querySelector("select.guides-index-item");
    var currentGuidePath  = window.location.pathname;
    guidesIndexItem.value = currentGuidePath.substring(currentGuidePath.lastIndexOf("/") + 1) || 'index.html';

    guidesIndexItem.addEventListener("change", function(e) {
      if (Turbolinks.supported) {
        Turbolinks.visit(e.target.value);
      } else {
        window.location = e.target.value;
      }
    });

    var moreInfoButton = document.querySelector(".more-info-button");
    var moreInfoLinks  = document.querySelector(".more-info-links");

    moreInfoButton.addEventListener("click", function(e) {
      e.preventDefault();

      if (moreInfoLinks.classList.contains("s-hidden")) {
        wrap(moreInfoLinks, createElement("div", "more-info-container"));
        moreInfoLinks.classList.remove("s-hidden");
      } else {
        moreInfoLinks.classList.add("s-hidden");
        unwrap(moreInfoLinks);
      }
    });

    var clipboard = new ClipboardJS('.clipboard-button');
    clipboard.on('success', function(e) {
      var trigger = e.trigger;
      var triggerLabel = trigger.innerHTML;
      trigger.innerHTML = 'Copied!';
      setTimeout(function(){
        trigger.innerHTML = triggerLabel;
      }, 3000);
      e.clearSelection();
    });
  });
}).call(this);
<|endoftext|>


<commit_before>describe('Rivets.Binding', function() {
  var model, el, view, binding, opts;

  beforeEach(function() {
    adapter = {
      subscribe: function() {},
      unsubscribe: function() {},
      read: function() { return {} },
      publish: function() {}
    };

    rivets.adapters['.'] = adapter;

    el = document.createElement('div');
    el.setAttribute('data-text', 'obj.name');

    view = rivets.bind(el, {obj: {}});
    binding = view.bindings[0];
    model = binding.model;
  });

  it('gets assigned the proper binder routine matching the identifier', function() {
    expect(binding.binder.routine).toBe(rivets.binders.text);
  });

  describe('bind()', function() {
    it('subscribes to the model for changes via the adapter', function() {
      spyOn(adapter, 'subscribe');
      binding.bind();
      expect(adapter.subscribe).toHaveBeenCalledWith(model, 'name', binding.sync);
    });

    it("calls the binder's bind method if one exists", function() {
      expect(function(){
        binding.bind();
      }).not.toThrow(new Error());

      binding.binder.bind = function(){};
      spyOn(binding.binder, 'bind');
      binding.bind();
      expect(binding.binder.bind).toHaveBeenCalled();
    });

    describe('with preloadData set to true', function() {
      beforeEach(function() {
        rivets.config.preloadData = true;
      });

      it('sets the initial value via the adapter', function() {
        spyOn(binding, 'set');
        spyOn(adapter, 'read');
        binding.bind();
        expect(adapter.read).toHaveBeenCalledWith(model, 'name');
        expect(binding.set).toHaveBeenCalled();
      });
    });

    describe('with dependencies', function() {
      beforeEach(function() {
        binding.options.dependencies = ['.fname', '.lname'];
      });

      it('sets up observers on the dependant attributes', function() {
        spyOn(adapter, 'subscribe');
        binding.bind();
        expect(adapter.subscribe).toHaveBeenCalledWith(model, 'fname', binding.sync);
        expect(adapter.subscribe).toHaveBeenCalledWith(model, 'lname', binding.sync);
      });
    });
  });

  describe('unbind()', function() {
    it("calls the binder's unbind method if one exists", function() {
      expect(function(){
        binding.unbind();
      }).not.toThrow(new Error());

      binding.binder.unbind = function(){};
      spyOn(binding.binder, 'unbind');
      binding.unbind();
      expect(binding.binder.unbind).toHaveBeenCalled();
    });
  });

  describe('set()', function() {
    it('performs the binding routine with the supplied value', function() {
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'sweater');
    });

    it('applies any formatters to the value before performing the routine', function() {
      view.formatters.awesome = function(value) { return 'awesome ' + value; };
      binding.formatters.push('awesome');
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'awesome sweater');
    });

    it('calls methods with the object as context', function() {
      binding.model = {foo: 'bar'};
      spyOn(binding.binder, 'routine');
      binding.set(function() { return this.foo; });
      expect(binding.binder.routine).toHaveBeenCalledWith(el, binding.model.foo);
    });
  });

  describe('publish()', function() {
    it("should publish the value of a number input", function() {
      numberInput = document.createElement('input');
      numberInput.setAttribute('type', 'number');
      numberInput.setAttribute('data-value', 'obj.num');

      view = rivets.bind(numberInput, {obj: {num: 42}});
      binding = view.bindings[0];
      model = binding.model;

      numberInput.value = 42;

      spyOn(adapter, 'publish');
      binding.publish({target: numberInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'num', '42');
    });
  });

  describe('publishTwoWay()', function() {

    it('applies a two-way read formatter to function same as a single-way', function() {
      view.formatters.awesome = {
        read: function(value) { return 'awesome ' + value; },
      };
      binding.formatters.push('awesome');
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'awesome sweater');
    });

    it("should publish the value of a number input", function() {
      rivets.formatters.awesome = {
        publish: function(value) { return 'awesome ' + value; },
      };

      numberInput = document.createElement('input');
      numberInput.setAttribute('type', 'number');
      numberInput.setAttribute('data-value', 'obj.num | awesome');

      view = rivets.bind(numberInput, {obj: {num: 42}});
      binding = view.bindings[0];
      model = binding.model;

      numberInput.value = 42;

      spyOn(adapter, 'publish');
      binding.publish({target: numberInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'num', 'awesome 42');
    });

    it("should format a value in both directions", function() {
      rivets.formatters.awesome = {
        publish: function(value) { return 'awesome ' + value; },
        read: function(value) { return value + ' is awesome'; }
      };
      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(adapter, 'publish');
      valueInput.value = 'charles';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'awesome charles');

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome');
    });

    it("should not fail or format if the specified binding function doesn't exist", function() {
      rivets.formatters.awesome = { };
      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(adapter, 'publish');
      valueInput.value = 'charles';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'charles');

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred');
    });

    it("should apply read binders left to right, and write binders right to left", function() {
      rivets.formatters.totally = {
        publish: function(value) { return value + ' totally'; },
        read: function(value) { return value + ' totally'; }
      };
      rivets.formatters.awesome = {
        publish: function(value) { return value + ' is awesome'; },
        read: function(value) { return value + ' is awesome'; }
      };

      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome | totally');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome totally');

      spyOn(adapter, 'publish');
      valueInput.value = 'fred';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'fred totally is awesome');
    });

     it("binders in a chain should be skipped if they're not there", function() {
      rivets.formatters.totally = {
        publish: function(value) { return value + ' totally'; },
        read: function(value) { return value + ' totally'; }
      };
      rivets.formatters.radical = {
        publish: function(value) { return value + ' is radical'; },
      };
      rivets.formatters.awesome = function(value) { return value + ' is awesome'; };

      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome | radical | totally');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome totally');

      spyOn(adapter, 'publish');
      valueInput.value = 'fred';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'fred totally is radical');
    });

  });

  describe('formattedValue()', function() {
    it('applies the current formatters on the supplied value', function() {
      view.formatters.awesome = function(value) { return 'awesome ' + value; };
      binding.formatters.push('awesome');
      expect(binding.formattedValue('hat')).toBe('awesome hat');
    });

    it('uses formatters on the model', function() {
      model.modelAwesome = function(value) { return 'model awesome ' + value; };
      binding.formatters.push('modelAwesome');
      expect(binding.formattedValue('hat')).toBe('model awesome hat');
    });

    describe('with a multi-argument formatter string', function() {
      beforeEach(function() {
        view.formatters.awesome = function(value, prefix) {
          return prefix + ' awesome ' + value;
        };
        binding.formatters.push('awesome super');
      });

      it('applies the formatter with arguments', function() {
        expect(binding.formattedValue('jacket')).toBe('super awesome jacket');
      });
    });
  });
});
<commit_msg>Don't overwrite the default adapter in the binding spec.<commit_after>describe('Rivets.Binding', function() {
  var model, el, view, binding, opts;

  beforeEach(function() {
    adapter = rivets.adapters['.']

    el = document.createElement('div');
    el.setAttribute('data-text', 'obj.name');

    view = rivets.bind(el, {obj: {}});
    binding = view.bindings[0];
    model = binding.model;
  });

  it('gets assigned the proper binder routine matching the identifier', function() {
    expect(binding.binder.routine).toBe(rivets.binders.text);
  });

  describe('bind()', function() {
    it('subscribes to the model for changes via the adapter', function() {
      spyOn(adapter, 'subscribe');
      binding.bind();
      expect(adapter.subscribe).toHaveBeenCalledWith(model, 'name', binding.sync);
    });

    it("calls the binder's bind method if one exists", function() {
      expect(function(){
        binding.bind();
      }).not.toThrow(new Error());

      binding.binder.bind = function(){};
      spyOn(binding.binder, 'bind');
      binding.bind();
      expect(binding.binder.bind).toHaveBeenCalled();
    });

    describe('with preloadData set to true', function() {
      beforeEach(function() {
        rivets.config.preloadData = true;
      });

      it('sets the initial value via the adapter', function() {
        spyOn(binding, 'set');
        spyOn(adapter, 'read');
        binding.bind();
        expect(adapter.read).toHaveBeenCalledWith(model, 'name');
        expect(binding.set).toHaveBeenCalled();
      });
    });

    describe('with dependencies', function() {
      beforeEach(function() {
        binding.options.dependencies = ['.fname', '.lname'];
      });

      it('sets up observers on the dependant attributes', function() {
        spyOn(adapter, 'subscribe');
        binding.bind();
        expect(adapter.subscribe).toHaveBeenCalledWith(model, 'fname', binding.sync);
        expect(adapter.subscribe).toHaveBeenCalledWith(model, 'lname', binding.sync);
      });
    });
  });

  describe('unbind()', function() {
    it("calls the binder's unbind method if one exists", function() {
      expect(function(){
        binding.unbind();
      }).not.toThrow(new Error());

      binding.binder.unbind = function(){};
      spyOn(binding.binder, 'unbind');
      binding.unbind();
      expect(binding.binder.unbind).toHaveBeenCalled();
    });
  });

  describe('set()', function() {
    it('performs the binding routine with the supplied value', function() {
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'sweater');
    });

    it('applies any formatters to the value before performing the routine', function() {
      view.formatters.awesome = function(value) { return 'awesome ' + value; };
      binding.formatters.push('awesome');
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'awesome sweater');
    });

    it('calls methods with the object as context', function() {
      binding.model = {foo: 'bar'};
      spyOn(binding.binder, 'routine');
      binding.set(function() { return this.foo; });
      expect(binding.binder.routine).toHaveBeenCalledWith(el, binding.model.foo);
    });
  });

  describe('publish()', function() {
    it("should publish the value of a number input", function() {
      numberInput = document.createElement('input');
      numberInput.setAttribute('type', 'number');
      numberInput.setAttribute('data-value', 'obj.num');

      view = rivets.bind(numberInput, {obj: {num: 42}});
      binding = view.bindings[0];
      model = binding.model;

      numberInput.value = 42;

      spyOn(adapter, 'publish');
      binding.publish({target: numberInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'num', '42');
    });
  });

  describe('publishTwoWay()', function() {

    it('applies a two-way read formatter to function same as a single-way', function() {
      view.formatters.awesome = {
        read: function(value) { return 'awesome ' + value; },
      };
      binding.formatters.push('awesome');
      spyOn(binding.binder, 'routine');
      binding.set('sweater');
      expect(binding.binder.routine).toHaveBeenCalledWith(el, 'awesome sweater');
    });

    it("should publish the value of a number input", function() {
      rivets.formatters.awesome = {
        publish: function(value) { return 'awesome ' + value; },
      };

      numberInput = document.createElement('input');
      numberInput.setAttribute('type', 'number');
      numberInput.setAttribute('data-value', 'obj.num | awesome');

      view = rivets.bind(numberInput, {obj: {num: 42}});
      binding = view.bindings[0];
      model = binding.model;

      numberInput.value = 42;

      spyOn(adapter, 'publish');
      binding.publish({target: numberInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'num', 'awesome 42');
    });

    it("should format a value in both directions", function() {
      rivets.formatters.awesome = {
        publish: function(value) { return 'awesome ' + value; },
        read: function(value) { return value + ' is awesome'; }
      };
      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(adapter, 'publish');
      valueInput.value = 'charles';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'awesome charles');

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome');
    });

    it("should not fail or format if the specified binding function doesn't exist", function() {
      rivets.formatters.awesome = { };
      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(adapter, 'publish');
      valueInput.value = 'charles';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'charles');

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred');
    });

    it("should apply read binders left to right, and write binders right to left", function() {
      rivets.formatters.totally = {
        publish: function(value) { return value + ' totally'; },
        read: function(value) { return value + ' totally'; }
      };
      rivets.formatters.awesome = {
        publish: function(value) { return value + ' is awesome'; },
        read: function(value) { return value + ' is awesome'; }
      };

      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome | totally');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome totally');

      spyOn(adapter, 'publish');
      valueInput.value = 'fred';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'fred totally is awesome');
    });

     it("binders in a chain should be skipped if they're not there", function() {
      rivets.formatters.totally = {
        publish: function(value) { return value + ' totally'; },
        read: function(value) { return value + ' totally'; }
      };
      rivets.formatters.radical = {
        publish: function(value) { return value + ' is radical'; },
      };
      rivets.formatters.awesome = function(value) { return value + ' is awesome'; };

      valueInput = document.createElement('input');
      valueInput.setAttribute('type','text');
      valueInput.setAttribute('data-value', 'obj.name | awesome | radical | totally');

      view = rivets.bind(valueInput, {obj: { name: 'nothing' }});
      binding = view.bindings[0];
      model = binding.model;

      spyOn(binding.binder, 'routine');
      binding.set('fred');
      expect(binding.binder.routine).toHaveBeenCalledWith(valueInput, 'fred is awesome totally');

      spyOn(adapter, 'publish');
      valueInput.value = 'fred';
      binding.publish({target: valueInput});
      expect(adapter.publish).toHaveBeenCalledWith(model, 'name', 'fred totally is radical');
    });

  });

  describe('formattedValue()', function() {
    it('applies the current formatters on the supplied value', function() {
      view.formatters.awesome = function(value) { return 'awesome ' + value; };
      binding.formatters.push('awesome');
      expect(binding.formattedValue('hat')).toBe('awesome hat');
    });

    it('uses formatters on the model', function() {
      model.modelAwesome = function(value) { return 'model awesome ' + value; };
      binding.formatters.push('modelAwesome');
      expect(binding.formattedValue('hat')).toBe('model awesome hat');
    });

    describe('with a multi-argument formatter string', function() {
      beforeEach(function() {
        view.formatters.awesome = function(value, prefix) {
          return prefix + ' awesome ' + value;
        };
        binding.formatters.push('awesome super');
      });

      it('applies the formatter with arguments', function() {
        expect(binding.formattedValue('jacket')).toBe('super awesome jacket');
      });
    });
  });
});
<|endoftext|>


<commit_before>mintcache = {
	cache:"",
	"last-updated":new Date()
}<commit_msg>removed the install file as we are using mongoose now, which adds it with the schema<commit_after><|endoftext|>


<commit_before>'use strict';

/**
 * ButtressJS - Realtime datastore for software
 *
 * @file schemaModel.js
 * @description A default model for schemas
 * @module Model
 * @exports model, schema, constants
 * @author Chris Bates-Keegan
 *
 */

const ObjectId = require('mongodb').ObjectId;
const Logging = require('../logging');
const Shared = require('./shared');
// const Helpers = require('../helpers');
const shortId = require('../helpers').ShortId;

/* ********************************************************************************
 *
 * LOCALS
 *
 **********************************************************************************/

class SchemaModel {
	constructor(MongoDb, schemaData, app) {
		this.schemaData = schemaData;
		this.app = app || null;

		this.appShortId = (app) ? shortId(app._id) : null;
		this.collectionName = `${schemaData.collection}`;

		if (this.appShortId) {
			this.collectionName = `${this.appShortId}-${this.collectionName}`;
		}

		this.collection = MongoDb.collection(this.collectionName);
	}

	__doValidation(body) {
		const res = {
			isValid: true,
			missing: [],
			invalid: [],
		};

		const app = Shared.validateAppProperties(this.schemaData, body);
		if (app.isValid === false) {
			res.isValid = false;
			res.invalid = res.invalid.concat(app.invalid);
			res.missing = res.missing.concat(app.missing);
		}

		return res;
	}
	validate(body) {
		if (body instanceof Array === false) {
			body = [body];
		}
		const validation = body.map((b) => this.__doValidation(b)).filter((v) => v.isValid === false);

		return validation.length >= 1 ? validation[0] : {isValid: true};
	}

	generateRoleFilterQuery(token, roles, Model) {
		if (!roles.schema || !roles.schema.authFilter) {
			return Promise.resolve({});
		}

		// Parse schema authFilter.query object and apply env items.
		const __parseQuery = (query, envFlat, output) => {
			for (const property in query) {
				if (!query.hasOwnProperty(property)) continue;
				const command = query[property];

				if (property === '$or' && Array.isArray(command)) {
					output['$or'] = command.map((q) => __parseQuery(q, envFlat, {}));
				} else if (property === '$and' && Array.isArray(command)) {
					output['$and'] = command.map((q) => __parseQuery(q, envFlat, {}));
				} else {
					for (const operator in command) {
						if (!command.hasOwnProperty(operator)) continue;
						let operand = command[operator];

						// Check to see if operand is a path and fetch value
						if (operand.indexOf && operand.indexOf('.') !== -1) {
							let path = operand.split('.');
							const key = path.shift();

							path = path.join('.');

							if (key === 'env' && envFlat[path]) {
								operand = envFlat[path];
							} else {
								throw new Error(`Unable to find ${path} in schema.authFilter.env`);
							}
						}

						if (!output[property]) {
							output[property] = {};
						}
						output[property][`$${operator}`] = operand;
					}
				}
			}

			return output;
		};

		// Parse schema authFilter.env object, gather information that's needed.
		const buildEnv = (authFilter) => {
			const env = {
				authUserId: token._user,
			};

			const tasks = [];

			if (authFilter.env) {
				for (const property in roles.schema.authFilter.env) {
					if (!roles.schema.authFilter.env.hasOwnProperty(property)) continue;
					const query = roles.schema.authFilter.env[property];

					let propertyMap = '_id';
					if (query.map) {
						propertyMap = query.map;
					}
					for (const command in query) {
						if (!query.hasOwnProperty(command)) continue;

						if (command.includes('schema.')) {
							const commandPath = command.split('.');
							commandPath.shift(); // Remove "schema"
							const collectionName = `${this.appShortId}-${commandPath.shift()}`;
							const propertyPath = commandPath.join('.');

							let propertyQuery = {};
							propertyQuery[propertyPath] = query[command];
							propertyQuery = __parseQuery(propertyQuery, env, {});

							const fields = {};
							fields[propertyPath] = true;

							tasks.push(() => {
								return Model[collectionName].find(propertyQuery, fields)
									.then((res) => {
										// Map fetched properties into a array.
										env[property] = res.map((i) => i[propertyMap]);
										// Hack - Flattern any sub arrays down to the single level.
										env[property] = [].concat.apply([], env[property]);
									});
							});
						} else {
							// Unknown operation
						}
					}
				}
			}

			// The env process may need to query other collections so group them into tasks.
			return tasks.reduce((prev, task) => prev.then(() => task()), Promise.resolve())
				.then(() => env);
		};

		// Build the query structure with the built env object applyed to it.
		const buildQuery = (schemaQuery, env) => {
			return new Promise((resolve) => {
				const query = (schemaQuery) ? schemaQuery : {};

				resolve(__parseQuery(query, env, {}));
			});
		};

		// Engage.
		return buildEnv(roles.schema.authFilter)
			.then((env) => buildQuery(roles.schema.authFilter.query, env));
	}

	/*
	* @param {Object} body - body passed through from a POST request
	* @return {Promise} - returns a promise that is fulfilled when the database request is completed
	*/
	__add(body, internals) {
		return (prev) => {
			const entity = Object.assign({}, internals);

			if (body.id) {
				entity._id = new ObjectId(body.id);
			}

			if (this.schemaData.extends && this.schemaData.extends.includes('timestamps')) {
				entity.createdAt = new Date();
				entity.updatedAt = null;
			}

			const validated = Shared.applyAppProperties(this.schemaData, body);
			return prev.concat([Object.assign(validated, entity)]);
		};
	}
	add(body, internals) {
		const sharedAddFn = Shared.add(this.collection, (item) => this.__add(item, internals));
		return sharedAddFn(body);
	}

	update(query, id) {
		Logging.logSilly(`update: ${this.collectionName} ${id} ${query}`);

		return new Promise((resolve, reject) => {
			this.collection.updateOne({_id: id}, {
				$set: query,
			}, (err, object) => {
				if (err) throw new Error(err);

				resolve(object);
			});
		});
	}
	validateUpdate(body) {
		const sharedFn = Shared.validateUpdate({}, this.schemaData);
		return sharedFn(body);
	}
	updateByPath(body, id) {
		const sharedFn = Shared.updateByPath({}, this.schemaData, this.collection);

		if (body instanceof Array === false) {
			body = [body];
		}

		if (this.schemaData.extends && this.schemaData.extends.includes('timestamps')) {
			body.push({
				path: 'updatedAt',
				value: new Date(),
				contextPath: '^updatedAt$',
			});
		}

		return sharedFn(body, id);
	}

	exists(id) {
		Logging.logSilly(`exists: ${this.collectionName} ${id}`);

		return this.collection.find({_id: new ObjectId(id)})
			.limit(1)
			.count()
			.then((count) => count > 0);
	}

	/*
	* @return {Promise} - returns a promise that is fulfilled when the database request is completed
	*/
	isDuplicate(details) {
		return Promise.resolve(false);
	}

	/**
	 * @param {App} entity - entity object to be deleted
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rm(entity) {
		Logging.log(`DELETING: ${entity._id}`, Logging.Constants.LogLevel.DEBUG);
		return new Promise((resolve) => {
			this.collection.deleteOne({_id: new ObjectId(entity._id)}, (err, cursor) => {
				if (err) throw err;
				resolve(cursor);
			});
		});
	}

	/**
	 * @param {Array} ids - Array of entity ids to delete
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rmBulk(ids) {
		Logging.log(`rmBulk: ${this.collectionName} ${ids}`, Logging.Constants.LogLevel.SILLY);
		return this.rmAll({_id: {$in: ids}});
	}

	/*
	 * @param {Object} query - mongoDB query
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rmAll(query) {
		if (!query) query = {};
		Logging.logSilly(`rmAll: ${this.collectionName} ${query}`);

		return new Promise((resolve) => {
			this.collection.deleteMany(query, (err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {String} id - entity id to get
	 * @return {Promise} - resolves to an array of Companies
	 */
	findById(id) {
		Logging.logSilly(`findById: ${this.collectionName} ${id}`);

		return new Promise((resolve) => {
			this.collection.findOne({_id: new ObjectId(id)}, {metadata: 0}, (err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {Object} query - mongoDB query
	 * @param {Object} excludes - mongoDB query excludes
	 * @param {Boolean} stream - should return a stream
	 * @return {Promise} - resolves to an array of docs
	 */
	find(query, excludes = {}, stream = false) {
		Logging.logSilly(`find: ${this.collectionName} ${query}`);

		if (stream) {
			return this.collection.find(query, excludes);
		}

		return new Promise((resolve) => {
			this.collection.find(query, excludes).toArray((err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {Object} query - mongoDB query
	 * @param {Object} excludes - mongoDB query excludes
	 * @return {Promise} - resolves to an array of docs
	 */
	findOne(query, excludes = {}) {
		Logging.logSilly(`findOne: ${this.collectionName} ${query}`);

		return new Promise((resolve) => {
			this.collection.find(query, excludes).toArray((err, doc) => {
				if (err) throw err;
				resolve(doc[0]);
			});
		});
	}

	/**
	 * @return {Promise} - resolves to an array of Companies
	 */
	findAll() {
		Logging.logSilly(`findAll: ${this.collectionName}`);

		return this.collection.find({});
	}

	/**
	 * @param {Array} ids - Array of entities ids to get
	 * @return {Promise} - resolves to an array of Companies
	 */
	findAllById(ids) {
		Logging.logSilly(`update: ${this.collectionName} ${ids}`);

		return this.collection.find({_id: {$in: ids.map((id) => new ObjectId(id))}}, {metadata: 0});
	}
}

module.exports = SchemaModel;
<commit_msg>REFACTOR: parseQuery to be a static method under SchemaModel<commit_after>'use strict';

/**
 * ButtressJS - Realtime datastore for software
 *
 * @file schemaModel.js
 * @description A default model for schemas
 * @module Model
 * @exports model, schema, constants
 * @author Chris Bates-Keegan
 *
 */

const ObjectId = require('mongodb').ObjectId;
const Logging = require('../logging');
const Shared = require('./shared');
// const Helpers = require('../helpers');
const shortId = require('../helpers').ShortId;

/* ********************************************************************************
 *
 * LOCALS
 *
 **********************************************************************************/

class SchemaModel {
	constructor(MongoDb, schemaData, app) {
		this.schemaData = schemaData;
		this.app = app || null;

		this.appShortId = (app) ? shortId(app._id) : null;
		this.collectionName = `${schemaData.collection}`;

		if (this.appShortId) {
			this.collectionName = `${this.appShortId}-${this.collectionName}`;
		}

		this.collection = MongoDb.collection(this.collectionName);
	}

	__doValidation(body) {
		const res = {
			isValid: true,
			missing: [],
			invalid: [],
		};

		const app = Shared.validateAppProperties(this.schemaData, body);
		if (app.isValid === false) {
			res.isValid = false;
			res.invalid = res.invalid.concat(app.invalid);
			res.missing = res.missing.concat(app.missing);
		}

		return res;
	}
	validate(body) {
		if (body instanceof Array === false) {
			body = [body];
		}
		const validation = body.map((b) => this.__doValidation(b)).filter((v) => v.isValid === false);

		return validation.length >= 1 ? validation[0] : {isValid: true};
	}

	static parseQuery(query, envFlat = {}, output = {}) {
		for (const property in query) {
			if (!query.hasOwnProperty(property)) continue;
			const command = query[property];

			if (property === '$or' && Array.isArray(command)) {
				output['$or'] = command.map((q) => SchemaModel.parseQuery(q, envFlat, {}));
			} else if (property === '$and' && Array.isArray(command)) {
				output['$and'] = command.map((q) => SchemaModel.parseQuery(q, envFlat, {}));
			} else {
				for (const operator in command) {
					if (!command.hasOwnProperty(operator)) continue;
					let operand = command[operator];

					// Check to see if operand is a path and fetch value
					if (operand.indexOf && operand.indexOf('.') !== -1) {
						let path = operand.split('.');
						const key = path.shift();

						path = path.join('.');

						if (key === 'env' && envFlat[path]) {
							operand = envFlat[path];
						} else {
							throw new Error(`Unable to find ${path} in schema.authFilter.env`);
						}
					}

					if (!output[property]) {
						output[property] = {};
					}
					output[property][`${operator}`] = operand;
				}
			}
		}

		return output;
	}

	generateRoleFilterQuery(token, roles, Model) {
		if (!roles.schema || !roles.schema.authFilter) {
			return Promise.resolve({});
		}

		// Parse schema authFilter.env object, gather information that's needed.
		const buildEnv = (authFilter) => {
			const env = {
				authUserId: token._user,
			};

			const tasks = [];

			if (authFilter.env) {
				for (const property in roles.schema.authFilter.env) {
					if (!roles.schema.authFilter.env.hasOwnProperty(property)) continue;
					const query = roles.schema.authFilter.env[property];

					let propertyMap = '_id';
					if (query.map) {
						propertyMap = query.map;
					}
					for (const command in query) {
						if (!query.hasOwnProperty(command)) continue;

						if (command.includes('schema.')) {
							const commandPath = command.split('.');
							commandPath.shift(); // Remove "schema"
							const collectionName = `${this.appShortId}-${commandPath.shift()}`;
							const propertyPath = commandPath.join('.');

							let propertyQuery = {};
							propertyQuery[propertyPath] = query[command];
							propertyQuery = SchemaModel.parseQuery(propertyQuery, env, {});

							const fields = {};
							fields[propertyPath] = true;

							tasks.push(() => {
								return Model[collectionName].find(propertyQuery, fields)
									.then((res) => {
										// Map fetched properties into a array.
										env[property] = res.map((i) => i[propertyMap]);
										// Hack - Flattern any sub arrays down to the single level.
										env[property] = [].concat.apply([], env[property]);
									});
							});
						} else {
							// Unknown operation
						}
					}
				}
			}

			// The env process may need to query other collections so group them into tasks.
			return tasks.reduce((prev, task) => prev.then(() => task()), Promise.resolve())
				.then(() => env);
		};

		// Build the query structure with the built env object applyed to it.
		const buildQuery = (schemaQuery, env) => {
			return new Promise((resolve) => {
				const query = (schemaQuery) ? schemaQuery : {};

				resolve(SchemaModel.parseQuery(query, env, {}));
			});
		};

		// Engage.
		return buildEnv(roles.schema.authFilter)
			.then((env) => buildQuery(roles.schema.authFilter.query, env));
	}

	/*
	* @param {Object} body - body passed through from a POST request
	* @return {Promise} - returns a promise that is fulfilled when the database request is completed
	*/
	__add(body, internals) {
		return (prev) => {
			const entity = Object.assign({}, internals);

			if (body.id) {
				entity._id = new ObjectId(body.id);
			}

			if (this.schemaData.extends && this.schemaData.extends.includes('timestamps')) {
				entity.createdAt = new Date();
				entity.updatedAt = null;
			}

			const validated = Shared.applyAppProperties(this.schemaData, body);
			return prev.concat([Object.assign(validated, entity)]);
		};
	}
	add(body, internals) {
		const sharedAddFn = Shared.add(this.collection, (item) => this.__add(item, internals));
		return sharedAddFn(body);
	}

	update(query, id) {
		Logging.logSilly(`update: ${this.collectionName} ${id} ${query}`);

		return new Promise((resolve, reject) => {
			this.collection.updateOne({_id: id}, {
				$set: query,
			}, (err, object) => {
				if (err) throw new Error(err);

				resolve(object);
			});
		});
	}
	validateUpdate(body) {
		const sharedFn = Shared.validateUpdate({}, this.schemaData);
		return sharedFn(body);
	}
	updateByPath(body, id) {
		const sharedFn = Shared.updateByPath({}, this.schemaData, this.collection);

		if (body instanceof Array === false) {
			body = [body];
		}

		if (this.schemaData.extends && this.schemaData.extends.includes('timestamps')) {
			body.push({
				path: 'updatedAt',
				value: new Date(),
				contextPath: '^updatedAt$',
			});
		}

		return sharedFn(body, id);
	}

	exists(id) {
		Logging.logSilly(`exists: ${this.collectionName} ${id}`);

		return this.collection.find({_id: new ObjectId(id)})
			.limit(1)
			.count()
			.then((count) => count > 0);
	}

	/*
	* @return {Promise} - returns a promise that is fulfilled when the database request is completed
	*/
	isDuplicate(details) {
		return Promise.resolve(false);
	}

	/**
	 * @param {App} entity - entity object to be deleted
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rm(entity) {
		Logging.log(`DELETING: ${entity._id}`, Logging.Constants.LogLevel.DEBUG);
		return new Promise((resolve) => {
			this.collection.deleteOne({_id: new ObjectId(entity._id)}, (err, cursor) => {
				if (err) throw err;
				resolve(cursor);
			});
		});
	}

	/**
	 * @param {Array} ids - Array of entity ids to delete
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rmBulk(ids) {
		Logging.log(`rmBulk: ${this.collectionName} ${ids}`, Logging.Constants.LogLevel.SILLY);
		return this.rmAll({_id: {$in: ids}});
	}

	/*
	 * @param {Object} query - mongoDB query
	 * @return {Promise} - returns a promise that is fulfilled when the database request is completed
	 */
	rmAll(query) {
		if (!query) query = {};
		Logging.logSilly(`rmAll: ${this.collectionName} ${query}`);

		return new Promise((resolve) => {
			this.collection.deleteMany(query, (err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {String} id - entity id to get
	 * @return {Promise} - resolves to an array of Companies
	 */
	findById(id) {
		Logging.logSilly(`findById: ${this.collectionName} ${id}`);

		return new Promise((resolve) => {
			this.collection.findOne({_id: new ObjectId(id)}, {metadata: 0}, (err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {Object} query - mongoDB query
	 * @param {Object} excludes - mongoDB query excludes
	 * @param {Boolean} stream - should return a stream
	 * @return {Promise} - resolves to an array of docs
	 */
	find(query, excludes = {}, stream = false) {
		Logging.logSilly(`find: ${this.collectionName} ${query}`);

		if (stream) {
			return this.collection.find(query, excludes);
		}

		return new Promise((resolve) => {
			this.collection.find(query, excludes).toArray((err, doc) => {
				if (err) throw err;
				resolve(doc);
			});
		});
	}

	/**
	 * @param {Object} query - mongoDB query
	 * @param {Object} excludes - mongoDB query excludes
	 * @return {Promise} - resolves to an array of docs
	 */
	findOne(query, excludes = {}) {
		Logging.logSilly(`findOne: ${this.collectionName} ${query}`);

		return new Promise((resolve) => {
			this.collection.find(query, excludes).toArray((err, doc) => {
				if (err) throw err;
				resolve(doc[0]);
			});
		});
	}

	/**
	 * @return {Promise} - resolves to an array of Companies
	 */
	findAll() {
		Logging.logSilly(`findAll: ${this.collectionName}`);

		return this.collection.find({});
	}

	/**
	 * @param {Array} ids - Array of entities ids to get
	 * @return {Promise} - resolves to an array of Companies
	 */
	findAllById(ids) {
		Logging.logSilly(`update: ${this.collectionName} ${ids}`);

		return this.collection.find({_id: {$in: ids.map((id) => new ObjectId(id))}}, {metadata: 0});
	}
}

module.exports = SchemaModel;
<|endoftext|>