<commit_before>
import {polymerWrap, closest} from './helpers/dom/element';
import {isWebComponentSupportedNatively} from './helpers/browser';

/**
 * Event DOM manager for internal use in Handsontable.
 *
 * @class EventManager
 * @private
 * @util
 */
class EventManager {
  /**
   * @param {Object} [context=null]
   */
  constructor(context = null) {
    this.context = context || this;

    if (!this.context.eventListeners) {
      this.context.eventListeners = [];
    }
  }

  /**
   * Add event
   *
   * @param {Element} element
   * @param {String} eventName
   * @param {Function} callback
   * @returns {Function} Returns function which you can easily call to remove that event
   */
  addEventListener(element, eventName, callback) {
    let context = this.context;

    function callbackProxy(event) {
      if (event.target == void 0 && event.srcElement != void 0) {
        if (event.definePoperty) {
          event.definePoperty('target', {
            value: event.srcElement
          });
        } else {
          event.target = event.srcElement;
        }
      }
      if (event.preventDefault == void 0) {
        if (event.definePoperty) {
          event.definePoperty('preventDefault', {
            value: function() {
              this.returnValue = false;
            }
          });
        } else {
          event.preventDefault = function() {
            this.returnValue = false;
          };
        }
      }
      event = extendEvent(context, event);

      /* jshint validthis:true */
      callback.call(this, event);
    }
    this.context.eventListeners.push({
      element: element,
      event: eventName,
      callback: callback,
      callbackProxy: callbackProxy,
    });

    if (window.addEventListener) {
      element.addEventListener(eventName, callbackProxy, false);
    } else {
      element.attachEvent('on' + eventName, callbackProxy);
    }
    Handsontable.countEventManagerListeners++;

    return () => {
      this.removeEventListener(element, eventName, callback);
    };
  }

  /**
   * Remove event
   *
   * @param {Element} element
   * @param {String} eventName
   * @param {Function} callback
   */
  removeEventListener(element, eventName, callback) {
    let len = this.context.eventListeners.length;
    let tmpEvent;

    while (len--) {
      tmpEvent = this.context.eventListeners[len];

      if (tmpEvent.event == eventName && tmpEvent.element == element) {
        if (callback && callback != tmpEvent.callback) {
          continue;
        }
        this.context.eventListeners.splice(len, 1);

        if (tmpEvent.element.removeEventListener) {
          tmpEvent.element.removeEventListener(tmpEvent.event, tmpEvent.callbackProxy, false);
        } else {
          tmpEvent.element.detachEvent('on' + tmpEvent.event, tmpEvent.callbackProxy);
        }
        Handsontable.countEventManagerListeners--;
      }
    }
  }

  /**
   * Clear all events
   *
   * @since 0.15.0-beta3
   */
  clearEvents() {
    if (!this.context) {
      return;
    }
    let len = this.context.eventListeners.length;

    while (len--) {
      let event = this.context.eventListeners[len];

      if (event) {
        this.removeEventListener(event.element, event.event, event.callback);
      }
    }
  }

  /**
   * Clear all events
   */
  clear() {
    this.clearEvents();
  }

  /**
   * Destroy instance
   */
  destroy() {
    this.clearEvents();
    this.context = null;
  }

  /**
   * Trigger event
   *
   * @param {Element} element
   * @param {String} eventName
   */
  fireEvent(element, eventName) {
    let options = {
      bubbles: true,
      cancelable: (eventName !== 'mousemove'),
      view: window,
      detail: 0,
      screenX: 0,
      screenY: 0,
      clientX: 1,
      clientY: 1,
      ctrlKey: false,
      altKey: false,
      shiftKey: false,
      metaKey: false,
      button: 0,
      relatedTarget: undefined,
    };
    var event;

    if (document.createEvent) {
      event = document.createEvent('MouseEvents');
      event.initMouseEvent(eventName, options.bubbles, options.cancelable,
        options.view, options.detail,
        options.screenX, options.screenY, options.clientX, options.clientY,
        options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
        options.button, options.relatedTarget || document.body.parentNode);

    } else {
      event = document.createEventObject();
    }

    if (element.dispatchEvent) {
      element.dispatchEvent(event);
    } else {
      element.fireEvent('on' + eventName, event);
    }
  }
}

/**
 * @param {Object} context
 * @param {Event} event
 * @private
 * @returns {*}
 */
function extendEvent(context, event) {
  let componentName = 'HOT-TABLE';
  let isHotTableSpotted;
  let fromElement;
  let realTarget;
  let target;
  let len;

  event.isTargetWebComponent = false;
  event.realTarget = event.target;

  if (!Handsontable.eventManager.isHotTableEnv) {
    return event;
  }
  event = polymerWrap(event);
  len = event.path ? event.path.length : 0;

  while (len--) {
    if (event.path[len].nodeName === componentName) {
      isHotTableSpotted = true;

    } else if (isHotTableSpotted && event.path[len].shadowRoot) {
      target = event.path[len];

      break;
    }
    if (len === 0 && !target) {
      target = event.path[len];
    }
  }
  if (!target) {
    target = event.target;
  }
  event.isTargetWebComponent = true;

  if (isWebComponentSupportedNatively()) {
    event.realTarget = event.srcElement || event.toElement;

  } else if (context instanceof Handsontable.Core || context instanceof Walkontable) {
    // Polymer doesn't support `event.target` property properly we must emulate it ourselves
    if (context instanceof Handsontable.Core) {
      fromElement = context.view ? context.view.wt.wtTable.TABLE : null;

    } else if (context instanceof Walkontable) {
      // .wtHider
      fromElement = context.wtTable.TABLE.parentNode.parentNode;
    }
    realTarget = closest(event.target, [componentName], fromElement);

    if (realTarget) {
      event.realTarget = fromElement.querySelector(componentName) || event.target;
    } else {
      event.realTarget = event.target;
    }
  }

  Object.defineProperty(event, 'target', {
    get: function() {
      return polymerWrap(target);
    },
    enumerable: true,
    configurable: true,
  });

  return event;
}

export {EventManager, eventManager};

window.Handsontable = window.Handsontable || {};
// used to debug memory leaks
Handsontable.countEventManagerListeners = 0;
// support for older versions of Handsontable, deprecated
Handsontable.eventManager = eventManager;

function eventManager(context) {
  return new EventManager(context);
}
<commit_msg>Fix issues #2207 and #1717<commit_after>
import {polymerWrap, closest} from './helpers/dom/element';
import {isWebComponentSupportedNatively} from './helpers/browser';
import {stopImmediatePropagation as _stopImmediatePropagation} from './helpers/dom/event';

/**
 * Event DOM manager for internal use in Handsontable.
 *
 * @class EventManager
 * @private
 * @util
 */
class EventManager {
  /**
   * @param {Object} [context=null]
   */
  constructor(context = null) {
    this.context = context || this;

    if (!this.context.eventListeners) {
      this.context.eventListeners = [];
    }
  }

  /**
   * Add event
   *
   * @param {Element} element
   * @param {String} eventName
   * @param {Function} callback
   * @returns {Function} Returns function which you can easily call to remove that event
   */
  addEventListener(element, eventName, callback) {
    let context = this.context;

    function callbackProxy(event) {
      if (event.target == void 0 && event.srcElement != void 0) {
        if (event.definePoperty) {
          event.definePoperty('target', {
            value: event.srcElement
          });
        } else {
          event.target = event.srcElement;
        }
      }
      if (event.preventDefault == void 0) {
        if (event.definePoperty) {
          event.definePoperty('preventDefault', {
            value: function() {
              this.returnValue = false;
            }
          });
        } else {
          event.preventDefault = function() {
            this.returnValue = false;
          };
        }
      }
      event = extendEvent(context, event);

      /* jshint validthis:true */
      callback.call(this, event);
    }
    this.context.eventListeners.push({
      element: element,
      event: eventName,
      callback: callback,
      callbackProxy: callbackProxy,
    });

    if (window.addEventListener) {
      element.addEventListener(eventName, callbackProxy, false);
    } else {
      element.attachEvent('on' + eventName, callbackProxy);
    }
    Handsontable.countEventManagerListeners++;

    return () => {
      this.removeEventListener(element, eventName, callback);
    };
  }

  /**
   * Remove event
   *
   * @param {Element} element
   * @param {String} eventName
   * @param {Function} callback
   */
  removeEventListener(element, eventName, callback) {
    let len = this.context.eventListeners.length;
    let tmpEvent;

    while (len--) {
      tmpEvent = this.context.eventListeners[len];

      if (tmpEvent.event == eventName && tmpEvent.element == element) {
        if (callback && callback != tmpEvent.callback) {
          continue;
        }
        this.context.eventListeners.splice(len, 1);

        if (tmpEvent.element.removeEventListener) {
          tmpEvent.element.removeEventListener(tmpEvent.event, tmpEvent.callbackProxy, false);
        } else {
          tmpEvent.element.detachEvent('on' + tmpEvent.event, tmpEvent.callbackProxy);
        }
        Handsontable.countEventManagerListeners--;
      }
    }
  }

  /**
   * Clear all events
   *
   * @since 0.15.0-beta3
   */
  clearEvents() {
    if (!this.context) {
      return;
    }
    let len = this.context.eventListeners.length;

    while (len--) {
      let event = this.context.eventListeners[len];

      if (event) {
        this.removeEventListener(event.element, event.event, event.callback);
      }
    }
  }

  /**
   * Clear all events
   */
  clear() {
    this.clearEvents();
  }

  /**
   * Destroy instance
   */
  destroy() {
    this.clearEvents();
    this.context = null;
  }

  /**
   * Trigger event
   *
   * @param {Element} element
   * @param {String} eventName
   */
  fireEvent(element, eventName) {
    let options = {
      bubbles: true,
      cancelable: (eventName !== 'mousemove'),
      view: window,
      detail: 0,
      screenX: 0,
      screenY: 0,
      clientX: 1,
      clientY: 1,
      ctrlKey: false,
      altKey: false,
      shiftKey: false,
      metaKey: false,
      button: 0,
      relatedTarget: undefined,
    };
    var event;

    if (document.createEvent) {
      event = document.createEvent('MouseEvents');
      event.initMouseEvent(eventName, options.bubbles, options.cancelable,
        options.view, options.detail,
        options.screenX, options.screenY, options.clientX, options.clientY,
        options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
        options.button, options.relatedTarget || document.body.parentNode);

    } else {
      event = document.createEventObject();
    }

    if (element.dispatchEvent) {
      element.dispatchEvent(event);
    } else {
      element.fireEvent('on' + eventName, event);
    }
  }
}

/**
 * @param {Object} context
 * @param {Event} event
 * @private
 * @returns {*}
 */
function extendEvent(context, event) {
  let componentName = 'HOT-TABLE';
  let isHotTableSpotted;
  let fromElement;
  let realTarget;
  let target;
  let len;
  let nativeStopImmediatePropagation;

  event.isTargetWebComponent = false;
  event.realTarget = event.target;

  nativeStopImmediatePropagation = event.stopImmediatePropagation;
  event.stopImmediatePropagation = function() {
    nativeStopImmediatePropagation.apply(this);
    _stopImmediatePropagation(this);
  };

  if (!Handsontable.eventManager.isHotTableEnv) {
    return event;
  }
  event = polymerWrap(event);
  len = event.path ? event.path.length : 0;

  while (len--) {
    if (event.path[len].nodeName === componentName) {
      isHotTableSpotted = true;

    } else if (isHotTableSpotted && event.path[len].shadowRoot) {
      target = event.path[len];

      break;
    }
    if (len === 0 && !target) {
      target = event.path[len];
    }
  }
  if (!target) {
    target = event.target;
  }
  event.isTargetWebComponent = true;

  if (isWebComponentSupportedNatively()) {
    event.realTarget = event.srcElement || event.toElement;

  } else if (context instanceof Handsontable.Core || context instanceof Walkontable) {
    // Polymer doesn't support `event.target` property properly we must emulate it ourselves
    if (context instanceof Handsontable.Core) {
      fromElement = context.view ? context.view.wt.wtTable.TABLE : null;

    } else if (context instanceof Walkontable) {
      // .wtHider
      fromElement = context.wtTable.TABLE.parentNode.parentNode;
    }
    realTarget = closest(event.target, [componentName], fromElement);

    if (realTarget) {
      event.realTarget = fromElement.querySelector(componentName) || event.target;
    } else {
      event.realTarget = event.target;
    }
  }

  Object.defineProperty(event, 'target', {
    get: function() {
      return polymerWrap(target);
    },
    enumerable: true,
    configurable: true,
  });

  return event;
}

export {EventManager, eventManager};

window.Handsontable = window.Handsontable || {};
// used to debug memory leaks
Handsontable.countEventManagerListeners = 0;
// support for older versions of Handsontable, deprecated
Handsontable.eventManager = eventManager;

function eventManager(context) {
  return new EventManager(context);
}
<|endoftext|>


<commit_before>'use strict'

var fs = require('fs')
var mongojs = require('mongojs')
var Wreck = require('wreck')
var config = require('../config')
var pkg = require('../package.json')
var dbprofile = mongojs(config.DB_CONNECTION_PROFILE)
var dbmessage = mongojs(config.DB_CONNECTION_MESSAGE)
var profiles = dbprofile.collection('profiles')
var messages = dbmessage.collection('messages')
var wreckOptions = {
  json: true
}

function showFeedbackPage (request, reply) {
  var viewOptions = {
    version: pkg.version,
    versionName: pkg.louie.versionName,
    versionVideoUrl: pkg.louie.versionVideoUrl,
    systemName: pkg.louie.systemName,
    githubUrl: pkg.repository.url,
    credentials: request.auth.credentials
  }
  reply.view('feedback', viewOptions)
}

function addFeedback (request, reply) {
  var data = request.payload
  var url = config.GITHUB_FEEDBACK_URL
  var title = pkg.louie.systemName + ' - ' + pkg.version + ' - ' + new Date()
  var issue = {
    title: title,
    body: data.description + '\n\n' + request.auth.credentials.data.cn,
    labels: [
      'portalen'
    ]
  }
  var auth = 'Basic ' + new Buffer(config.GITHUB_USER + ':' + config.GITHUB_TOKEN).toString('base64')

  wreckOptions.headers = {
    'Authorization': auth,
    'User-Agent': config.GITHUB_USER,
    'Accept': 'application/vnd.github.v3+json',
    'Content-Type': 'application/json'
  }

  wreckOptions.payload = JSON.stringify(issue)

  Wreck.post(url, wreckOptions, function (error, response, payload) {
    if (error) {
      reply(error)
    } else {
      reply.redirect('/?feedbackCollected=true')
    }
  })
}

module.exports.showFeedbackPage = showFeedbackPage

module.exports.addFeedback = addFeedback
<commit_msg>Fixes Code Style<commit_after>'use strict'

var Wreck = require('wreck')
var config = require('../config')
var pkg = require('../package.json')
var wreckOptions = {
  json: true
}

function showFeedbackPage (request, reply) {
  var viewOptions = {
    version: pkg.version,
    versionName: pkg.louie.versionName,
    versionVideoUrl: pkg.louie.versionVideoUrl,
    systemName: pkg.louie.systemName,
    githubUrl: pkg.repository.url,
    credentials: request.auth.credentials
  }
  reply.view('feedback', viewOptions)
}

function addFeedback (request, reply) {
  var data = request.payload
  var url = config.GITHUB_FEEDBACK_URL
  var title = pkg.louie.systemName + ' - ' + pkg.version + ' - ' + new Date()
  var issue = {
    title: title,
    body: data.description + '\n\n' + request.auth.credentials.data.cn,
    labels: [
      'portalen'
    ]
  }
  var auth = 'Basic ' + new Buffer(config.GITHUB_USER + ':' + config.GITHUB_TOKEN).toString('base64')

  wreckOptions.headers = {
    'Authorization': auth,
    'User-Agent': config.GITHUB_USER,
    'Accept': 'application/vnd.github.v3+json',
    'Content-Type': 'application/json'
  }

  wreckOptions.payload = JSON.stringify(issue)

  Wreck.post(url, wreckOptions, function (error, response, payload) {
    if (error) {
      reply(error)
    } else {
      reply.redirect('/?feedbackCollected=true')
    }
  })
}

module.exports.showFeedbackPage = showFeedbackPage

module.exports.addFeedback = addFeedback
<|endoftext|>


<commit_before>var sys = require('sys'),
    exec = require('child_process').exec,
    fs = require('fs'),
    child,
    color = {
      default: '\033[0m',
      red: '\033[0;31m',
      green: '\033[0;32m',
      brown: '\033[0;33m',
      blue: '\033[0;34m',
      magenta: '\033[0;35m',
      cyan: '\033[0;36m',
      lightgray: '\033[0;37m',
    };

function removePhpFiles(callback) {
  exec("rm ./tests/*.php", function(error) {
    // if(error !== null) {
    //  sys.print('removePhpFiles error: ' + error + '\n');
    // }
    callback && callback();
  });
}

function compileMammouth(callback) {
  sys.print(color.cyan + '\n===COMPILATION START===\n' + color.default);
  child = exec("node ./bin/mammouth --compile --output ./tests ./tests", function(error, stdout, stderr) {
    if(stdout) {
      sys.print('stdout: ' + stdout);
    }
    if(stderr) {
      sys.print('stderr: ' + stderr);
    }
    if (error === null) {
      sys.print(color.cyan + '===COMPILATION END===\n' + color.default);
      callback && callback();
    } else {
      sys.print('compileMammouth error: ' + error + '\n');
    }
  });
}

function doTheTests(callback) {
  var phpFileName,
      fileLoop,
      path = './tests/',
      ctn = fs.readdirSync(path);
  console.log('Files in ./test/ directory after compilation: ', ctn);
  sys.print(color.cyan + '\n===TEST BEGIN===\n' + color.default);

  (fileLoop = function(i) {
    if(ctn.length === i) {
      return callback && callback();;
    }
    if(ctn[i].substr(-9).toLowerCase() !== '.mammouth') {
      // ommit php files from list
      return fileLoop(i ? ++i : 1);
    }
    phpFileName = ctn[i].replace(/.mammouth$/, '') + '.php';
    if(ctn.indexOf(phpFileName) === -1) {
      sys.print(color.red + 'FAIL'+ color.default +' | ' + ctn[i] + ' | (not even compiled)\n');
      return fileLoop(i ? ++i : 1);
    } else {
      exec('php -f ' + path + phpFileName, function(error, stdout, stderr) {
        if(stderr) {
          sys.print('stderr: ' + stderr + '\n');
        }
        if (error === null) {
          var result = stdout.toString();
          if(result.toLowerCase() === 'pass') {
            sys.print(color.green + 'PASS'+ color.default +' | ' + ctn[i] + '\n');
          } else {
            sys.print(color.red + 'FAIL'+ color.default +' | ' + ctn[i] + ' | "pass" !== "' + result + '"\n');
          }
        } else {
          sys.print(phpFileName + ' execution error: ' + error + '\n');
        }
        return fileLoop(i ? ++i : 1);
      });
    }
  })(0);
}

function finishTest() {
  sys.print(color.cyan + '===TEST END===\n' + color.default);
};

removePhpFiles(function() {
  compileMammouth(function() {
    doTheTests(function() {
      removePhpFiles();
      finishTest();
    })
  });
});
<commit_msg>small code cleanup<commit_after>var sys = require('sys'),
    exec = require('child_process').exec,
    fs = require('fs'),
    child,
    color = {
      default: '\033[0m',
      red: '\033[0;31m',
      green: '\033[0;32m',
      brown: '\033[0;33m',
      blue: '\033[0;34m',
      magenta: '\033[0;35m',
      cyan: '\033[0;36m',
      lightgray: '\033[0;37m'
    };

function removePhpFiles(callback) {
  exec("rm ./tests/*.php", function(error) {
    callback && callback();
  });
}

function compileMammouth(callback) {
  sys.print(color.cyan + '\n===COMPILATION START===\n' + color.default);
  child = exec("node ./bin/mammouth --compile --output ./tests ./tests", function(error, stdout, stderr) {
    if(stdout) {
      sys.print('stdout: ' + stdout);
    }
    if(stderr) {
      sys.print('stderr: ' + stderr);
    }
    if (error === null) {
      sys.print(color.cyan + '===COMPILATION END===\n' + color.default);
      callback && callback();
    } else {
      sys.print('compileMammouth error: ' + error + '\n');
    }
  });
}

function doTheTests(callback) {
  var phpFileName,
      fileLoop,
      path = './tests/',
      ctn = fs.readdirSync(path);
  console.log('Files in ./test/ directory after compilation: \n', ctn);
  sys.print(color.cyan + '\n===TEST BEGIN===\n' + color.default);

  (fileLoop = function(i) {
    if(ctn.length === i) {
      return callback && callback();
    }
    if(ctn[i].substr(-9).toLowerCase() !== '.mammouth') {
      // ommit php files from list
      return fileLoop(++i);
    }
    phpFileName = ctn[i].replace(/.mammouth$/, '') + '.php';
    if(ctn.indexOf(phpFileName) === -1) {
      sys.print(color.red + 'FAIL'+ color.default +' | ' + ctn[i] + ' | (not even compiled)\n');
      return fileLoop(++i);
    } else {
      exec('php -f ' + path + phpFileName, function(error, stdout, stderr) {
        if(stderr) {
          sys.print('stderr: ' + stderr + '\n');
        }
        if (error === null) {
          var result = stdout.toString();
          if(result.toLowerCase() === 'pass') {
            sys.print(color.green + 'PASS'+ color.default +' | ' + ctn[i] + '\n');
          } else {
            sys.print(color.red + 'FAIL'+ color.default +' | ' + ctn[i] + ' | "pass" !== "' + result + '"\n');
          }
        } else {
          sys.print(phpFileName + ' execution error: ' + error + '\n');
        }
        return fileLoop(++i);
      });
    }
  })(0);
}

function finishTest() {
  sys.print(color.cyan + '===TEST END===\n' + color.default);
}

removePhpFiles(function() {
  compileMammouth(function() {
    doTheTests(function() {
      removePhpFiles();
      finishTest();
    });
  });
});
<|endoftext|>


<commit_before>var should = require('chai').should();
var RSVP = require('rsvp');

var Promise = RSVP.Promise;

var Belt = require('../lib/index.js');

var schemas = {
    "person": {
        "name": String,
        "age": Number,
        "vehicle": [ "vehicle" ],
        "partner": { ref: 'person', inverse: 'partner' },
        "sibling": [{ ref: 'person', inverse: 'sibling' }]
    },
    "vehicle": {
        "model": String,
        "age": Number,
        "owner": "person"
    }
};

// create repositories
function createRepositories( belt, schemas ) {
    var promises = [];

    for ( schema in schemas ) {
        promises.push(new Promise(function( resolve ) {
            belt.schema( schema, schemas[ schema ] );

            resolve();
        }));
    }

    return promises;
}

var fixtures = {
  "person": [
    {
      "name": "Dilbert",
      "age": 21
    },
    {
      "name": "Wally",
      "age": 42
    }
  ],
  "vehicle": [
    {
      "model": "White van",
      "age": 3
    },
    {
      "model": "Sports car",
      "age": 10
    }
  ]
};

// create resources
function createResources( belt, fixtures, ids ) {
    var promises = [];

    for ( fixture in fixtures ) {
        fixtures[ fixture ].forEach(function( data ) {
            promises.push(new Promise(function( resolve ) {
                belt.create( fixture, data )
                    .then(function( response ) {
                        ids[fixture] = ids[fixture] || [];

                        ids[fixture].push( { id: response.id, rev: response.rev });

                        resolve();
                    });
            }));
        });
    }

    return promises;
}

describe('using an adapter', function () {
    var ids = {};
    var belt = new Belt({ db : require('memdown') });

    before(function (done) {
        RSVP.all(createRepositories( belt, schemas ))
            .then(function() { return RSVP.all(createResources( belt, fixtures, ids )); })
            .then(function () {
                done();
            })
            .catch(function( error ) {
                throw new Error('Failed to initialize adapter.');
            });
    });

    describe('getting a list of resources', function () {

        it('in collection one', function (done) {
            // test that list is returned correctly

            // test there is no error

            done();
        });

    });

    /*

    describe('getting each individual resource', function () {

        it('in collection one', function (done) {
            // test that the resource is correct and valid

            // test there is no error

            done();
        });

    });

    describe('many to one association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('one to many association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('one to one association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('many to many association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    */

    after(function (done) {
        // delete all resources in the repositories

        /*
        RSVP.all(ids[key].map(function (id) {
        })).then(function () {
          done();
        }, function () {
          throw new Error('Failed to delete resources.');
        });
        */

        done();
    });

});
<commit_msg>initialize new belt instance and add schemas and data<commit_after>var should = require('chai').should();
var RSVP = require('rsvp');

var Promise = RSVP.Promise;

var Belt = require('../lib/index.js');

var schemas = {
    "person": {
        "name": String,
        "age": Number,
        "vehicle": [ "vehicle" ],
        "partner": { ref: 'person', inverse: 'partner' },
        "sibling": [{ ref: 'person', inverse: 'sibling' }]
    },
    "vehicle": {
        "model": String,
        "age": Number,
        "owner": "person"
    }
};

// create repositories
function createRepositories( belt, schemas ) {
    var promises = [];

    for ( schema in schemas ) {
        promises.push(new Promise(function( resolve ) {
            belt.schema( schema, schemas[ schema ] );

            resolve();
        }));
    }

    return promises;
}

var fixtures = {
  "person": [
    {
      "name": "Dilbert",
      "age": 21
    },
    {
      "name": "Wally",
      "age": 42
    }
  ],
  "vehicle": [
    {
      "model": "White van",
      "age": 3
    },
    {
      "model": "Sports car",
      "age": 10
    }
  ]
};

// create resources
function createResources( belt, fixtures, ids ) {
    var promises = [];

    for ( fixture in fixtures ) {
        fixtures[ fixture ].forEach(function( data ) {
            promises.push(new Promise(function( resolve ) {
                belt.create( fixture, data )
                    .then(function( response ) {
                        console.log( response )

                        ids[fixture] = ids[fixture] || [];

                        ids[fixture].push( { id: response.id, rev: response.rev });

                        resolve();
                    });
            }));
        });
    }

    return promises;
}

describe('using an adapter', function () {
    var ids = {};
    var belt = new Belt({ db : require('memdown') });

    before(function (done) {
        RSVP.all(createRepositories( belt, schemas ))
            .then(function() { return RSVP.all(createResources( belt, fixtures, ids )); })
            .then(function () {
                done();
            })
            .catch(function( error ) {
                throw new Error('Failed to initialize adapter.');
            });
    });

    describe('getting a list of resources', function () {

        it('in collection one', function (done) {
            // test that list is returned correctly

            // test there is no error

            done();
        });

    });

    /*

    describe('getting each individual resource', function () {

        it('in collection one', function (done) {
            // test that the resource is correct and valid

            // test there is no error

            done();
        });

    });

    describe('many to one association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('one to many association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('one to one association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    describe('many to many association', function () {

        it('should be able to associate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

        it('should be able to dissociate', function (done) {
            // test that the resource is correct and valid

            // test that all referenc3d links are correct

            // test there is no error

            done();
        });

    });

    */

    after(function (done) {
        // delete all resources in the repositories

        /*
        RSVP.all(ids[key].map(function (id) {
        })).then(function () {
          done();
        }, function () {
          throw new Error('Failed to delete resources.');
        });
        */

        done();
    });

});
<|endoftext|>


<commit_before>/**
 * Tests Two.js Svg Rendering Functionality:
 */

(function() {

  module('SvgRenderer');

  test('Two.makeLine', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var line = two.makeLine(0, 0, two.width, two.height);
    var elem = two.renderer.domElement.querySelector('#two-' + line.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -200.000 -200.000 L 200.000 200.000', 'Two.makeLine applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeRectangle', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var rect = two.makeRectangle(two.width / 2, two.height / 2, 100, 100);
    var elem = two.renderer.domElement.querySelector('#two-' + rect.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 50.000 50.000 L -50.000 50.000 L -50.000 -50.000 L 50.000 -50.000 Z', 'Two.makeRectangle applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeEllipse', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var ellipse = two.makeEllipse(two.width / 2, two.height / 2, 100, 100);
    var elem = two.renderer.domElement.querySelector('#two-' + ellipse.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 100.000 0.000 C 100.000 25.257 88.570 52.851 70.711 70.711 C 52.851 88.570 25.257 100.000 0.000 100.000 C -25.257 100.000 -52.851 88.570 -70.711 70.711 C -88.570 52.851 -100.000 25.257 -100.000 0.000 C -100.000 -25.257 -88.570 -52.851 -70.711 -70.711 C -52.851 -88.570 -25.257 -100.000 -0.000 -100.000 C 25.257 -100.000 52.851 -88.570 70.711 -70.711 C 88.570 -52.851 100.000 -25.257 100.000 0.000 Z', 'Two.makeEllipse applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeCircle', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var circle = two.makeCircle(two.width / 2, two.height / 2, 50);
    var elem = two.renderer.domElement.querySelector('#two-' + circle.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 50.000 0.000 C 50.000 12.629 44.285 26.426 35.355 35.355 C 26.426 44.285 12.629 50.000 0.000 50.000 C -12.629 50.000 -26.426 44.285 -35.355 35.355 C -44.285 26.426 -50.000 12.629 -50.000 0.000 C -50.000 -12.629 -44.285 -26.426 -35.355 -35.355 C -26.426 -44.285 -12.629 -50.000 -0.000 -50.000 C 12.629 -50.000 26.426 -44.285 35.355 -35.355 C 44.285 -26.426 50.000 -12.629 50.000 0.000 Z', 'Two.makeCircle applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makePolygon', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var amount = 20;
    var phi = 6;
    var points = _.map(_.range(amount), function(i) {
      var pct = i / amount;
      var x = pct * 300 + 50;
      var y = i % 2 ? 25 : 75;
      return new Two.Vector(x, y);
    });
    var poly = two.makePolygon(points, true);
    var elem = two.renderer.domElement.querySelector('#two-' + poly.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -142.500 25.000 L -127.500 -25.000 L -112.500 25.000 L -97.500 -25.000 L -82.500 25.000 L -67.500 -25.000 L -52.500 25.000 L -37.500 -25.000 L -22.500 25.000 L -7.500 -25.000 L 7.500 25.000 L 22.500 -25.000 L 37.500 25.000 L 52.500 -25.000 L 67.500 25.000 L 82.500 -25.000 L 97.500 25.000 L 112.500 -25.000 L 127.500 25.000 L 142.500 -25.000', 'Two.makePolygon applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeCurve', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var amount = 20;
    var phi = 6;
    var points = _.map(_.range(amount), function(i) {
      var pct = i / amount;
      var x = pct * 300 + 50;
      var y = i % 2 ? 375 : 325;
      return new Two.Vector(x, y);
    });
    var curve = two.makeCurve(points, true);
    var elem = two.renderer.domElement.querySelector('#two-' + curve.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -142.500 -25.000 C -142.500 -25.000 -144.727 25.000 -127.500 25.000 C -110.273 25.000 -129.727 -25.000 -112.500 -25.000 C -95.273 -25.000 -114.727 25.000 -97.500 25.000 C -80.273 25.000 -99.727 -25.000 -82.500 -25.000 C -65.273 -25.000 -84.727 25.000 -67.500 25.000 C -50.273 25.000 -69.727 -25.000 -52.500 -25.000 C -35.273 -25.000 -54.727 25.000 -37.500 25.000 C -20.273 25.000 -39.727 -25.000 -22.500 -25.000 C -5.273 -25.000 -24.727 25.000 -7.500 25.000 C 9.727 25.000 -9.727 -25.000 7.500 -25.000 C 24.727 -25.000 5.273 25.000 22.500 25.000 C 39.727 25.000 20.273 -25.000 37.500 -25.000 C 54.727 -25.000 35.273 25.000 52.500 25.000 C 69.727 25.000 50.273 -25.000 67.500 -25.000 C 84.727 -25.000 65.273 25.000 82.500 25.000 C 99.727 25.000 80.273 -25.000 97.500 -25.000 C 114.727 -25.000 95.273 25.000 112.500 25.000 C 129.727 25.000 110.273 -25.000 127.500 -25.000 C 144.727 -25.000 142.500 25.000 142.500 25.000', 'Two.makeCurve applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Styles', 8, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var shape = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);
    var elem = two.renderer.domElement.querySelector('#two-' + shape.id);

    shape.rotation = Math.PI / 2;
    shape.scale = 0.5;

    shape.fill = 'lightcoral';
    shape.stroke = '#333';
    shape.linewidth = 10;
    shape.opacity = 0.5;
    shape.join = 'miter';
    shape.cap = 'butt';
    shape.miter = 10;

    shape.closed = false;
    shape.curved = true;

    shape.visible = false;
    shape.visible = true;

    // Update Rendering
    two.render();

    var matrix = elem.getAttribute('transform');
    equal(matrix.match(/matrix\((.*)\)/)[1], shape._matrix.toString(), 'Two.Shape._matrix gets and sets proplery.');

    equal(elem.getAttribute('fill'), shape.fill, 'Two.Shape.fill gets and sets properly.');
    equal(elem.getAttribute('stroke'), shape.stroke, 'Two.Shape.stroke gets and sets properly.');
    equal(elem.getAttribute('stroke-linejoin'), shape.join, 'Two.Shape.join gets and sets properly.');
    equal(elem.getAttribute('stroke-linecap'), shape.cap, 'Two.Shape.cap gets and sets properly.');
    equal(elem.getAttribute('visibility'), 'visible', 'Two.Shape.visible gets and sets properly.');
    equal(elem.getAttribute('stroke-miterlimit'), shape.miter, 'Two.Shape.miter gets and sets properly.');
    ok(elem.getAttribute('stroke-opacity') == shape.opacity
      && elem.getAttribute('fill-opacity') == shape.opacity, 'Two.Shape.opacity gets and sets properly.');

    addSvgToTest(o, two);

  });

  function addSvgToTest(o, two) {

    var domElement = document.createElement('li');
    domElement.appendChild(two.renderer.domElement);

    _.delay(function() {
      document.querySelector('#' + o.id + ' ol').appendChild(domElement);
    }, 100);

  }

})();
<commit_msg>modified test in order to pass svg make curve<commit_after>/**
 * Tests Two.js Svg Rendering Functionality:
 */

(function() {

  module('SvgRenderer');

  test('Two.makeLine', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var line = two.makeLine(0, 0, two.width, two.height);
    var elem = two.renderer.domElement.querySelector('#two-' + line.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -200.000 -200.000 L 200.000 200.000', 'Two.makeLine applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeRectangle', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var rect = two.makeRectangle(two.width / 2, two.height / 2, 100, 100);
    var elem = two.renderer.domElement.querySelector('#two-' + rect.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 50.000 50.000 L -50.000 50.000 L -50.000 -50.000 L 50.000 -50.000 Z', 'Two.makeRectangle applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeEllipse', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var ellipse = two.makeEllipse(two.width / 2, two.height / 2, 100, 100);
    var elem = two.renderer.domElement.querySelector('#two-' + ellipse.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 100.000 0.000 C 100.000 25.257 88.570 52.851 70.711 70.711 C 52.851 88.570 25.257 100.000 0.000 100.000 C -25.257 100.000 -52.851 88.570 -70.711 70.711 C -88.570 52.851 -100.000 25.257 -100.000 0.000 C -100.000 -25.257 -88.570 -52.851 -70.711 -70.711 C -52.851 -88.570 -25.257 -100.000 -0.000 -100.000 C 25.257 -100.000 52.851 -88.570 70.711 -70.711 C 88.570 -52.851 100.000 -25.257 100.000 0.000 Z', 'Two.makeEllipse applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeCircle', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var circle = two.makeCircle(two.width / 2, two.height / 2, 50);
    var elem = two.renderer.domElement.querySelector('#two-' + circle.id);

    two.render();

    equal(elem.getAttribute('d'), 'M 50.000 0.000 C 50.000 12.629 44.285 26.426 35.355 35.355 C 26.426 44.285 12.629 50.000 0.000 50.000 C -12.629 50.000 -26.426 44.285 -35.355 35.355 C -44.285 26.426 -50.000 12.629 -50.000 0.000 C -50.000 -12.629 -44.285 -26.426 -35.355 -35.355 C -26.426 -44.285 -12.629 -50.000 -0.000 -50.000 C 12.629 -50.000 26.426 -44.285 35.355 -35.355 C 44.285 -26.426 50.000 -12.629 50.000 0.000 Z', 'Two.makeCircle applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makePolygon', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var amount = 20;
    var phi = 6;
    var points = _.map(_.range(amount), function(i) {
      var pct = i / amount;
      var x = pct * 300 + 50;
      var y = i % 2 ? 25 : 75;
      return new Two.Vector(x, y);
    });
    var poly = two.makePolygon(points, true);
    var elem = two.renderer.domElement.querySelector('#two-' + poly.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -142.500 25.000 L -127.500 -25.000 L -112.500 25.000 L -97.500 -25.000 L -82.500 25.000 L -67.500 -25.000 L -52.500 25.000 L -37.500 -25.000 L -22.500 25.000 L -7.500 -25.000 L 7.500 25.000 L 22.500 -25.000 L 37.500 25.000 L 52.500 -25.000 L 67.500 25.000 L 82.500 -25.000 L 97.500 25.000 L 112.500 -25.000 L 127.500 25.000 L 142.500 -25.000', 'Two.makePolygon applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Two.makeCurve', 1, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var amount = 20;
    var phi = 6;
    var points = _.map(_.range(amount), function(i) {
      var pct = i / amount;
      var x = pct * 300 + 50;
      var y = i % 2 ? 25 : 75;
      return new Two.Vector(x, y);
    });
    var curve = two.makeCurve(points, true);
    var elem = two.renderer.domElement.querySelector('#two-' + curve.id);

    two.render();

    equal(elem.getAttribute('d'), 'M -142.500 25.000 C 50.000 75.000 47.773 25.000 -127.500 -25.000 C 82.227 25.000 62.773 75.000 -112.500 25.000 C 97.227 75.000 77.773 25.000 -97.500 -25.000 C 112.227 25.000 92.773 75.000 -82.500 25.000 C 127.227 75.000 107.773 25.000 -67.500 -25.000 C 142.227 25.000 122.773 75.000 -52.500 25.000 C 157.227 75.000 137.773 25.000 -37.500 -25.000 C 172.227 25.000 152.773 75.000 -22.500 25.000 C 187.227 75.000 167.773 25.000 -7.500 -25.000 C 202.227 25.000 182.773 75.000 7.500 25.000 C 217.227 75.000 197.773 25.000 22.500 -25.000 C 232.227 25.000 212.773 75.000 37.500 25.000 C 247.227 75.000 227.773 25.000 52.500 -25.000 C 262.227 25.000 242.773 75.000 67.500 25.000 C 277.227 75.000 257.773 25.000 82.500 -25.000 C 292.227 25.000 272.773 75.000 97.500 25.000 C 307.227 75.000 287.773 25.000 112.500 -25.000 C 322.227 25.000 302.773 75.000 127.500 25.000 C 337.227 75.000 335.000 25.000 142.500 -25.000', 'Two.makeCurve applies d attribute properly.');

    addSvgToTest(o, two);

  });

  test('Styles', 8, function(o) {

    var two = new Two({
      width: 400,
      height: 400
    });

    var shape = two.makeRectangle(two.width / 2, two.height / 2, 50, 50);
    var elem = two.renderer.domElement.querySelector('#two-' + shape.id);

    shape.rotation = Math.PI / 2;
    shape.scale = 0.5;

    shape.fill = 'lightcoral';
    shape.stroke = '#333';
    shape.linewidth = 10;
    shape.opacity = 0.5;
    shape.join = 'miter';
    shape.cap = 'butt';
    shape.miter = 10;

    shape.closed = false;
    shape.curved = true;

    shape.visible = false;
    shape.visible = true;

    // Update Rendering
    two.render();

    var matrix = elem.getAttribute('transform');
    equal(matrix.match(/matrix\((.*)\)/)[1], shape._matrix.toString(), 'Two.Shape._matrix gets and sets proplery.');

    equal(elem.getAttribute('fill'), shape.fill, 'Two.Shape.fill gets and sets properly.');
    equal(elem.getAttribute('stroke'), shape.stroke, 'Two.Shape.stroke gets and sets properly.');
    equal(elem.getAttribute('stroke-linejoin'), shape.join, 'Two.Shape.join gets and sets properly.');
    equal(elem.getAttribute('stroke-linecap'), shape.cap, 'Two.Shape.cap gets and sets properly.');
    equal(elem.getAttribute('visibility'), 'visible', 'Two.Shape.visible gets and sets properly.');
    equal(elem.getAttribute('stroke-miterlimit'), shape.miter, 'Two.Shape.miter gets and sets properly.');
    ok(elem.getAttribute('stroke-opacity') == shape.opacity
      && elem.getAttribute('fill-opacity') == shape.opacity, 'Two.Shape.opacity gets and sets properly.');

    addSvgToTest(o, two);

  });

  function addSvgToTest(o, two) {

    var domElement = document.createElement('li');
    domElement.appendChild(two.renderer.domElement);

    _.delay(function() {
      document.querySelector('#' + o.id + ' ol').appendChild(domElement);
    }, 100);

  }

})();
<|endoftext|>


<commit_before>/*
 *  thing_set.js
 *
 *  David Janes
 *  IOTDB
 *  2014-01-01
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  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.
 */

"use strict";

const _ = require("./helpers");
const attribute = require("./attribute");
const model = require("./model");
const events = require('events');
const util = require('util');
const assert = require('assert');

const logger = _.logger.make({
    name: 'iotdb',
    module: 'thing_set',
});

/* --- constants --- */
const VERBOSE = true;

const KEY_TAG = 'TAG';
const KEY_SETTER = 'SETTER';

let sid = 0;

const make = function() {
    const self = Object.assign({}, events.EventEmitter.prototype);

    // events
    const _emitter = new events.EventEmitter();
    _emitter.setMaxListeners(0);

    // internals (some cheat visibility for downstream sets)
    self._sid = '_thing_set' + sid++;
    self._isThingArray = true;

    let _things = [];
    let _persistds = [];

    // array compatibility
    self.every = (f) => self.all().every(f);
    self.filter = (f) => self.all().filter(f);
    self.find = (f) => self.all().find(f);
    self.forEach = (f) => self.all().forEach(f);
    self.map = (f) => self.all().map(f);
    self.reduce = (f, i) => self.all().map(f, i);

    // new "array like" stuff
    self.all = () => _things;
    self.any = () => _.is.Empty(_things) ? null : _.first(_things);
    self.count = () => _things.length;
    self.empty = () => _things.length === 0;

    // --- set specific stuff
    self.add = function (thing) {
        if (!_.is.Thing(thing)) {
            throw new Error("attempt to add a non-Thing on a ThingArray");
        }

        if (self.find(t => t === thing)) {
            return;
        }

        thing._sidd = thing._sidd || {};
        thing._sidd[self._sid] = true;

        _do_pre(thing);
        _things.push(thing);
        _emitter.emit("thing", thing);
        _do_post(thing);

        _emitter.emit("changed", self);
    };

    self.connect = function (modeld, initd, metad) {
        const other_set = require('./iotdb').iot().connect(modeld, initd, metad);

        self._update(other_set, () => true);

        other_set.on("changed", () => self._update(other_set, () => true));

        return self;
    };

    self.disconnect = function () {
        return _apply_persist(model.Model.prototype.disconnect, arguments);
    };

    self.name = function (name) {
        assert(_.is.String(name));

        return _apply_persist(model.Model.prototype.name, arguments);
    };

    self.zones = function (zones) {
        assert(_.is.String(zones) || _.is.Array(zones));

        return _apply_persist(model.Model.prototype.zones, arguments);
    };

    self.facets = function (facets) {
        assert(_.is.String(facets) || _.is.Array(facets));

        return _apply_persist(model.Model.prototype.facets, arguments);
    };

    self.set = function () {
        return _apply_persist(model.Model.prototype.set, arguments, KEY_SETTER);
    };

    self.update = function () {
        return _apply_persist(model.Model.prototype.update, arguments, KEY_SETTER);
    };

    self.pull = function () {
        return _apply_persist(model.Model.prototype.pull, arguments);
    };

    self.tag = function () {
        return _apply_persist(model.Model.prototype.tag, arguments, KEY_TAG);
    };

    self.on = function (what, callback) {
        if (_.contains([ "istate", "ostate", "state", "meta", "model", "connection" ], what)) {
            _apply_persist(model.Model.prototype.on, arguments);
        } else {
            _emitter.on(what, callback);
        }

        return self;
    };

    self.reachable = function () {
        return self
            .map((thing) => thing.reachable() ? 1 : 0)
            .reduce(( sum, reachable ) => sum + reachable, 0);
    };

    self.search = function (queryd) {
        const result_set = make();

        result_set._update(self, thing => self._search_test(queryd, thing));

        self.on("changed", () => result_set._update(self, thing => self._search_test(queryd, thing)));

        return self;
    };

    self.with_id = (id) => self.search({ "meta:iot:thing-id": id, });
    self.with_code = (code) => self.search({ "meta:iot:model-id": _.id.to_dash_case(code), });
    self.with_name = (name) => self.search({ "meta:schema:name": name });
    self.with_zone = (name) => self.search({ "meta:iot:zone": name });
    self.with_number = (number) => self.search({ "meta:iot:thing-number": parseInt(number) });
    self.with_tag = (tag) => self.search({ "transient:tag": tag });
    self.with_facet = (facet) => self.search({ "meta:iot:facet": facet, });

    // -- internals
    const _search_parse = queryd => _.values(_.mapObject(queryd, ( query_value, query_key ) => {
        const match = query_key.match(/^(meta|model|istate|ostate|connection|transient):(.+)$/);
        if (!match) {
            throw new Error("bad search: key=" + query_key);
        }

        return {
            query_band: match[1],
            query_inner_key: match[2],
            query_values: _.ld.list(queryd, query_key, []),
        };
    }));

    self._search_test = function (queryd, thing) {
        const meta = thing.meta();

        _search_parse(queryd)
            .map((matchd) => {
            switch (matchd.query_band) {
            case "meta":
            case "connection":
                matchd.query_values = _.ld.expand(matchd.query_values);

                const thing_state = thing.state(matchd.query_band);
                const thing_values = _.ld.expand(_.ld.list(thing_state, matchd.query_inner_key, []));

                return _.intersection(matchd.query_values, thing_values).length > 0;

            case "transient":
                if (matchd.query_inner_key === "tag") {
                    return _.ld.intersects(thing.initd, "tag", matchd.query_values);
                } else {
                    return false;
                }

            case "ostate":
            case "istate":
            case "model":
                logger.error({
                    method: "_search_test",
                    query_band: matchd.query_band,
                    query_key: matchd.query_key,
                }, "function not implemented (yet)");

                return false;

            default:
                logger.error({
                    method: "_search_test",
                    cause: "programming error - self should never happen",
                    query_band: matchd.query_band,
                    query_key: matchd.query_key,
                }, "bad band");

                return false;
            }

            return true;
        }).find(tf => tf === false) !== false;
    };

    const _is_pre_key = key => [ KEY_TAG ].indexOf(key) > -1;

    const _do_pre = thing => _persistds
        .filter(pd => _is_pre_key(pd.key))
        .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

    const _do_post = thing => _persistds
        .filter(pd => !_is_pre_key(pd.key))
        .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

    const _persist = function (f, av, key) {
        if (key === KEY_SETTER) {
            _persistds = _persistds.filter(p => p.key !== key);
        }

        _persistds.push({
            f: f,
            av: av,
            key: key
        });
    };

    const _apply = (f, av) => self.forEach(thing => f.apply(thing, Array.prototype.slice.call(av)));

    const _apply_persist = (f, av, key) => {
        _apply(f, av);
        _persist(f, av, key);

        return self;
    };

    self._update = ( other_set, filter ) => {
        const existing_things = self.filter(thing => thing._sidd[other_set._sid]);
        const other_things = other_set.all().filter(filter);

        const removed_things = _.without(existing_things, other_things);
        const added_things = _.without(other_things, existing_things);

        if (_.is.Empty(removed_things) && _.is.Empty(added_things)) {
            return;
        }

        removed_things.every(thing => _emitter.emit("removed", thing));
        added_things.every(thing => {
            _persistds
                .filter(pd => _is_pre_key(pd.key))
                .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

            _do_pre(thing);
            _things.push(thing);
            _do_post(thing);

            _emitter.emit("thing", thing);
        });

        _emitter.emit("changed", self);
    };

    return self;
};

/**
 *  API
 */
exports.make = make;
<commit_msg>more search cleanup<commit_after>/*
 *  thing_set.js
 *
 *  David Janes
 *  IOTDB
 *  2014-01-01
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  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.
 */

"use strict";

const _ = require("./helpers");
const attribute = require("./attribute");
const model = require("./model");
const events = require('events');
const util = require('util');
const assert = require('assert');

const logger = _.logger.make({
    name: 'iotdb',
    module: 'thing_set',
});

/* --- constants --- */
const VERBOSE = true;

const KEY_TAG = 'TAG';
const KEY_SETTER = 'SETTER';

let sid = 0;

const make = function() {
    const self = Object.assign({}, events.EventEmitter.prototype);

    // events
    const _emitter = new events.EventEmitter();
    _emitter.setMaxListeners(0);

    // internals (some cheat visibility for downstream sets)
    self._sid = '_thing_set' + sid++;
    self._isThingArray = true;

    let _things = [];
    let _persistds = [];

    // array compatibility
    self.every = (f) => self.all().every(f);
    self.filter = (f) => self.all().filter(f);
    self.find = (f) => self.all().find(f);
    self.forEach = (f) => self.all().forEach(f);
    self.map = (f) => self.all().map(f);
    self.reduce = (f, i) => self.all().map(f, i);

    // new "array like" stuff
    self.all = () => _things;
    self.any = () => _.is.Empty(_things) ? null : _.first(_things);
    self.count = () => _things.length;
    self.empty = () => _things.length === 0;

    // --- set specific stuff
    self.add = function (thing) {
        if (!_.is.Thing(thing)) {
            throw new Error("attempt to add a non-Thing on a ThingArray");
        }

        if (self.find(t => t === thing)) {
            return;
        }

        thing._sidd = thing._sidd || {};
        thing._sidd[self._sid] = true;

        _do_pre(thing);
        _things.push(thing);
        _emitter.emit("thing", thing);
        _do_post(thing);

        _emitter.emit("changed", self);
    };

    self.connect = function (modeld, initd, metad) {
        const other_set = require('./iotdb').iot().connect(modeld, initd, metad);

        self._update(other_set, () => true);

        other_set.on("changed", () => self._update(other_set, () => true));

        return self;
    };

    self.disconnect = function () {
        return _apply_persist(model.Model.prototype.disconnect, arguments);
    };

    self.name = function (name) {
        assert(_.is.String(name));

        return _apply_persist(model.Model.prototype.name, arguments);
    };

    self.zones = function (zones) {
        assert(_.is.String(zones) || _.is.Array(zones));

        return _apply_persist(model.Model.prototype.zones, arguments);
    };

    self.facets = function (facets) {
        assert(_.is.String(facets) || _.is.Array(facets));

        return _apply_persist(model.Model.prototype.facets, arguments);
    };

    self.set = function () {
        return _apply_persist(model.Model.prototype.set, arguments, KEY_SETTER);
    };

    self.update = function () {
        return _apply_persist(model.Model.prototype.update, arguments, KEY_SETTER);
    };

    self.pull = function () {
        return _apply_persist(model.Model.prototype.pull, arguments);
    };

    self.tag = function () {
        return _apply_persist(model.Model.prototype.tag, arguments, KEY_TAG);
    };

    self.on = function (what, callback) {
        if (_.contains([ "istate", "ostate", "state", "meta", "model", "connection" ], what)) {
            _apply_persist(model.Model.prototype.on, arguments);
        } else {
            _emitter.on(what, callback);
        }

        return self;
    };

    self.reachable = function () {
        return self
            .map((thing) => thing.reachable() ? 1 : 0)
            .reduce(( sum, reachable ) => sum + reachable, 0);
    };

    self.search = function (queryd) {
        const result_set = make();

        result_set._update(self, thing => _search_filter(queryd, thing));

        self.on("changed", () => result_set._update(self, thing => _search_filter(queryd, thing)));

        return result_set;
    };

    self.with_id = (id) => self.search({ "meta:iot:thing-id": id, });
    self.with_code = (code) => self.search({ "meta:iot:model-id": _.id.to_dash_case(code), });
    self.with_name = (name) => self.search({ "meta:schema:name": name });
    self.with_zone = (name) => self.search({ "meta:iot:zone": name });
    self.with_number = (number) => self.search({ "meta:iot:thing-number": parseInt(number) });
    self.with_tag = (tag) => self.search({ "transient:tag": tag });
    self.with_facet = (facet) => self.search({ "meta:iot:facet": facet, });

    // -- internals
    const _search_parse = queryd => _.values(_.mapObject(queryd, ( query_value, query_key ) => {
        const match = query_key.match(/^(meta|model|istate|ostate|connection|transient):(.+)$/);
        if (!match) {
            throw new Error("bad search: key=" + query_key);
        }

        return {
            query_band: match[1],
            query_inner_key: match[2],
            query_values: _.ld.list(queryd, query_key, []),
        };
    }));

    const _search_match = (matchd, thing) => {
        const meta = thing.meta();

        switch (matchd.query_band) {
        case "meta":
        case "connection":
            matchd.query_values = _.ld.expand(matchd.query_values);

            const thing_state = thing.state(matchd.query_band);
            const thing_values = _.ld.expand(_.ld.list(thing_state, matchd.query_inner_key, []));

            return _.intersection(matchd.query_values, thing_values).length > 0;

        case "transient":
            if (matchd.query_inner_key === "tag") {
                return _.ld.intersects(thing.initd, "tag", matchd.query_values);
            } else {
                return false;
            }

        case "ostate":
        case "istate":
        case "model":
            logger.error({
                method: "_search_match",
                query_band: matchd.query_band,
                query_key: matchd.query_key,
            }, "function not implemented (yet)");

            return false;

        default:
            logger.error({
                method: "_search_match",
                cause: "programming error - self should never happen",
                query_band: matchd.query_band,
                query_key: matchd.query_key,
            }, "bad band");

            return false;
        }

        return true;
    };

    const _search_filter = ( queryd, thing ) => _search_parse(queryd)
        .map(matchd => _search_match(matchd, thing))
        .find(tf => (tf === false)) === undefined ? thing : null;

    const _is_pre_key = key => [ KEY_TAG ].indexOf(key) > -1;

    const _do_pre = thing => _persistds
        .filter(pd => _is_pre_key(pd.key))
        .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

    const _do_post = thing => _persistds
        .filter(pd => !_is_pre_key(pd.key))
        .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

    const _persist = function (f, av, key) {
        if (key === KEY_SETTER) {
            _persistds = _persistds.filter(p => p.key !== key);
        }

        _persistds.push({
            f: f,
            av: av,
            key: key
        });
    };

    const _apply = (f, av) => self.forEach(thing => f.apply(thing, Array.prototype.slice.call(av)));

    const _apply_persist = (f, av, key) => {
        _apply(f, av);
        _persist(f, av, key);

        return self;
    };

    self._update = ( other_set, filter ) => {
        const existing_things = self.filter(thing => thing._sidd[other_set._sid]);
        const other_things = other_set.all().filter(filter);

        const removed_things = _.without(existing_things, other_things);
        const added_things = _.without(other_things, existing_things);

        if (_.is.Empty(removed_things) && _.is.Empty(added_things)) {
            return;
        }

        removed_things.every(thing => _emitter.emit("removed", thing));
        added_things.every(thing => {
            _persistds
                .filter(pd => _is_pre_key(pd.key))
                .forEach(pd => pd.f.apply(thing, Array.prototype.slice.call(pd.av)));

            _do_pre(thing);
            _things.push(thing);
            _do_post(thing);

            _emitter.emit("thing", thing);
        });

        _emitter.emit("changed", self);
    };

    return self;
};

/**
 *  API
 */
exports.make = make;
<|endoftext|>


<commit_before>/**
 * TicTacToe
 * constructor for the class
 *
 * @param scores {Object} persisted scores state, pulled from storage or defaults
 * @param nextGame {Function} callback method to start a new game
 *
 * @constructor
 */
var TicTacToe = function (scores, nextGame) {

    this.scores = scores;
    this.nextGame = nextGame;

    this.el = document.getElementById('ticTacToe');

    this.gameInPlay = true;
    this.symbol = true; // false = x; true = o
    this.squares = [];
    this.wins   = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],

        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],

        [6, 4, 2],
        [0, 4, 8]
    ];

    this.buildBoard()
        .setScores();
};

/**
 * getSquare
 * gets the square as indicated by the passed in index, throws out of range
 * error.
 *
 * @param index {Number} 0-8
 * @returns {Object}
 */
TicTacToe.prototype.getSquare = function (index) {

    var square = this.squares[index];

    if (square) {
        return square;
    }

    throw new Error('Square out of range: ' + index);
};

/**
 * getStatus
 * gets the status of a square being played and matching the symbol in play
 *
 * @returns {Boolean}
 */
TicTacToe.prototype.getStatus = function (index) {

    var square = this.getSquare(index),
        symbol = square.className === 'cross';

     return !!square.played && (symbol === this.symbol);
};

/**
 * getSquaresInPlay
 * gets the remaining squares in play that haven't been played, optional param
 * to only return the counts
 *
 * @param counts {Boolean} !optional
 * @returns {Number|Array} either the total counts or an array of squares
 */
TicTacToe.prototype.getSquaresInPlay = function (counts) {

    var squares = this.squares.filter(function (square) {
        return !square.played;
    });

    if (counts) {
        return squares.length;
    }

    return squares;
};

/**
 * getRandom
 * randomly selects an un-played square
 *
 * @TODO this method needs to use the AI class to work out where the user will play next o_O
 *
 * @returns {number}
 */
TicTacToe.prototype.getRandom = function () {

    var squaresLeft = this.getSquaresInPlay(),
        rand = Math.round(Math.random() * (squaresLeft.length - 1));

    if (squaresLeft[rand]) {
        return +squaresLeft[rand].getAttribute('data-index');
    }
};

/**
 * getCompleteText
 * gets the complete text to display at the end of a game
 *
 * @returns {string}
 */
TicTacToe.prototype.getCompleteText = function () {

    var symbol = this.symbol ? 'crosses' : 'noughts',
        text = 'Game over, ' + symbol + ' win this time. \n',
        squaresLeft = this.getSquaresInPlay(true);

    if (!squaresLeft) {
        // @TODO still possible to have used up all the squares and won
        text = 'No winner this time. \n';
    }
    return text;
};

/**
 * buildBoard
 * creates all the dom elements for the game to start and bind events to the
 * squares
 *
 * @returns TicTacToe {Object}
 */
TicTacToe.prototype.buildBoard = function () {

    var tictactoe = this,
        list = document.createElement('ul'),
        item,
        i;

    list.className = 'tictactoe';

    for (i = 0; i < 9; i += 1) {

        item = document.createElement('li');
        item.setAttribute('data-index', i);

        item.addEventListener('click', function (evt) {

            if (tictactoe.gameInPlay) {
                tictactoe.playSquare.call(tictactoe, evt);
            }
        }, false);

        this.squares.push(item);

        list.appendChild(item);
    }

    this.el.innerHTML = '';
    this.el.appendChild(list);

    return this;
};

/**
 * playSquare
 * event handler for when a square is clicked
 *
 * @param evt {Event.Object} !optional if sending through index
 * @param index {Number} !optional if event handler
 */
TicTacToe.prototype.playSquare = function (evt, index) {

    var square;

    index = evt ? evt.currentTarget.getAttribute('data-index') : index;

    square = this.getSquare(index);

    if (square.played) {
        return;
    }

    if (this.symbol) {
        square.classList.add('cross');
        square.cross = true;
    } else {
        square.classList.add('nought');
        square.nought = true;
    }

    square.played = true;

    this.checkForWin(index);
};

/**
 * checkForWin
 *
 * @param index {Number|String}
 */
TicTacToe.prototype.checkForWin = function (index) {

    var accumulative,
        gameOver = false,
        squaresLeft = this.getSquaresInPlay(true);

    index = +index;

    // iterate the possibles to check for win
    this.wins.forEach(function (wins) {

        accumulative = true;

        if (wins.indexOf(index) < 0 || gameOver) {
            return;
        }

        wins.forEach(function (win) {
            if (!this.getStatus(win)) {
                accumulative = false;
            }
        }, this);

        if (accumulative || !squaresLeft) {
            gameOver = this.gameOver(accumulative);
        }
    }, this);

    // only swap to next player if game still in play
    if (this.gameInPlay) {
        this.symbol = !this.symbol;

        if (!this.symbol) {
            this.computerTurn();
        }
    }
};

/**
 * computerTurn
 * works out the computers next move and stops the user getting three in a row
 */
TicTacToe.prototype.computerTurn = function () {

    // find possible nought win
    var nextMove = this.getNextPlayable('nought');

    // if no win possible check for user win and block
    if (!nextMove) {
        nextMove = this.getNextPlayable('cross');

        // if play still not set - select a random position
        if (!nextMove) {
            nextMove = this.getRandom();
        }
    }

    // play chosen square
    this.playSquare(null, nextMove);
};

/**
 * getNextPlayable
 * iterates the possible wins and returns a play square to play next
 *
 * @param className {String}
 * @returns {*}
 */
TicTacToe.prototype.getNextPlayable = function (className) {

    var squareToPlay = null,
        winningSquare,
        playedCount,
        marker,
        square;

    this.wins.forEach(function (wins) {

        winningSquare = 0;
        marker = null;
        playedCount = 0;

        wins.forEach(function (win) {

            square = this.getSquare(win);

            if (square.played) {
                playedCount += 1;
            }

            if (square.className === className) {
                winningSquare += 1;
            } else {
                marker = win;
            }
        }, this);

        if (winningSquare === 2 && playedCount < 3) {
            squareToPlay = marker || null;
        }
    }, this);

    return squareToPlay;
};

/**
 * gameOver
 * stops play
 *
 * @param win {Boolean} is there a win on the board
 */
TicTacToe.prototype.gameOver = function (win) {

    var score = document.createElement('div'),
        button = document.createElement('button');

    button.innerText = 'Play Again';
    button.addEventListener('click', this.nextGame, false);

    score.className = 'message';
    score.innerText = this.getCompleteText();
    score.appendChild(button);

    this.el.appendChild(score);

    this.updateScores(win);
    this.gameInPlay = false;

    return this;
};

/**
 * updateScores
 * updates the dom with the winning score
 *
 * @param win {Boolean}
 */
TicTacToe.prototype.updateScores = function (win) {

    var winner = 'ties';

    if (win) {
        winner = this.symbol ? 'you' : 'computer';
    }

    this.scores[winner] += 1;
    this.setScores();

    localStorage.setItem('scores', JSON.stringify(this.scores));
};

/**
 * setScores
 * sets the scores to the DOM
 */
TicTacToe.prototype.setScores = function () {

    var prop;

    for (prop in this.scores) {
        if (this.scores.hasOwnProperty(prop)) {
            document.querySelector('.' + prop).innerHTML = this.scores[prop];
        }
    }
};

// instantiate the class and start the game
var saved = localStorage.getItem('scores'),
    defaults = {
        you: 0,
        ties: 0,
        computer: 0
    },
    scores = saved ? JSON.parse(saved) : defaults,

    // loop for continual play
    newGame = function () {
        return new TicTacToe(scores, newGame);
    };

// start the game loop
newGame();
<commit_msg>Update to incorrect finish when all squares used<commit_after>/**
 * TicTacToe
 * constructor for the class
 *
 * @param scores {Object} persisted scores state, pulled from storage or defaults
 * @param nextGame {Function} callback method to start a new game
 *
 * @constructor
 */
var TicTacToe = function (scores, nextGame) {

    this.scores = scores;
    this.nextGame = nextGame;

    this.el = document.getElementById('ticTacToe');

    this.gameInPlay = true;
    this.symbol = true; // false = x; true = o
    this.squares = [];
    this.wins   = [
        [0, 1, 2],
        [3, 4, 5],
        [6, 7, 8],

        [0, 3, 6],
        [1, 4, 7],
        [2, 5, 8],

        [6, 4, 2],
        [0, 4, 8]
    ];

    this.buildBoard()
        .setScores();
};

/**
 * getSquare
 * gets the square as indicated by the passed in index, throws out of range
 * error.
 *
 * @param index {Number} 0-8
 * @returns {Object}
 */
TicTacToe.prototype.getSquare = function (index) {

    var square = this.squares[index];

    if (square) {
        return square;
    }

    throw new Error('Square out of range: ' + index);
};

/**
 * getStatus
 * gets the status of a square being played and matching the symbol in play
 *
 * @returns {Boolean}
 */
TicTacToe.prototype.getStatus = function (index) {

    var square = this.getSquare(index),
        symbol = square.className === 'cross';

     return !!square.played && (symbol === this.symbol);
};

/**
 * getSquaresInPlay
 * gets the remaining squares in play that haven't been played, optional param
 * to only return the counts
 *
 * @param counts {Boolean} !optional
 * @returns {Number|Array} either the total counts or an array of squares
 */
TicTacToe.prototype.getSquaresInPlay = function (counts) {

    var squares = this.squares.filter(function (square) {
        return !square.played;
    });

    if (counts) {
        return squares.length;
    }

    return squares;
};

/**
 * getRandom
 * randomly selects an un-played square
 *
 * @TODO this method needs to use the AI class to work out where the user will play next o_O
 *
 * @returns {number}
 */
TicTacToe.prototype.getRandom = function () {

    var squaresLeft = this.getSquaresInPlay(),
        rand = Math.round(Math.random() * (squaresLeft.length - 1));

    if (squaresLeft[rand]) {
        return +squaresLeft[rand].getAttribute('data-index');
    }
};

/**
 * getCompleteText
 * gets the complete text to display at the end of a game
 *
 * @returns {string}
 */
TicTacToe.prototype.getCompleteText = function (win) {

    var symbol = this.symbol ? 'crosses' : 'noughts';

    if (!win) {
        return 'No winner this time. \n';
    }

    return 'Game over, ' + symbol + ' win this time. \n';
};

/**
 * buildBoard
 * creates all the dom elements for the game to start and bind events to the
 * squares
 *
 * @returns TicTacToe {Object}
 */
TicTacToe.prototype.buildBoard = function () {

    var tictactoe = this,
        list = document.createElement('ul'),
        item,
        i;

    list.className = 'tictactoe';

    for (i = 0; i < 9; i += 1) {

        item = document.createElement('li');
        item.setAttribute('data-index', i);

        item.addEventListener('click', function (evt) {

            if (tictactoe.gameInPlay) {
                tictactoe.playSquare.call(tictactoe, evt);
            }
        }, false);

        this.squares.push(item);

        list.appendChild(item);
    }

    this.el.innerHTML = '';
    this.el.appendChild(list);

    return this;
};

/**
 * playSquare
 * event handler for when a square is clicked
 *
 * @param evt {Event.Object} !optional if sending through index
 * @param index {Number} !optional if event handler
 */
TicTacToe.prototype.playSquare = function (evt, index) {

    var square;

    index = evt ? evt.currentTarget.getAttribute('data-index') : index;

    square = this.getSquare(index);

    if (square.played) {
        return;
    }

    if (this.symbol) {
        square.classList.add('cross');
        square.cross = true;
    } else {
        square.classList.add('nought');
        square.nought = true;
    }

    square.played = true;

    this.checkForWin(index);
};

/**
 * checkForWin
 *
 * @param index {Number|String}
 */
TicTacToe.prototype.checkForWin = function (index) {

    var winningLine,
        gameOver = false,
        squaresLeft = this.getSquaresInPlay(true);

    index = +index;

    // iterate the possibles to check for win
    this.wins.forEach(function (wins) {

        winningLine = true;

        if (wins.indexOf(index) < 0 || gameOver) {
            return;
        }

        wins.forEach(function (win) {
            if (!this.getStatus(win)) {
                winningLine = false;
            }
        }, this);

        if (winningLine || !squaresLeft) {
            gameOver = this.gameOver(winningLine);
        }
    }, this);

    // only swap to next player if game still in play
    if (this.gameInPlay) {
        this.symbol = !this.symbol;

        if (!this.symbol) {
            this.computerTurn();
        }
    }
};

/**
 * computerTurn
 * works out the computers next move and stops the user getting three in a row
 */
TicTacToe.prototype.computerTurn = function () {

    // find possible nought win
    var nextMove = this.getNextPlayable('nought');

    // if no win possible check for user win and block
    if (!nextMove) {
        nextMove = this.getNextPlayable('cross');

        // if play still not set - select a random position
        if (!nextMove) {
            nextMove = this.getRandom();
        }
    }

    // play chosen square
    this.playSquare(null, nextMove);
};

/**
 * getNextPlayable
 * iterates the possible wins and returns a play square to play next
 *
 * @param className {String}
 * @returns {*}
 */
TicTacToe.prototype.getNextPlayable = function (className) {

    var squareToPlay = null,
        winningSquare,
        playedCount,
        marker,
        square;

    this.wins.forEach(function (wins) {

        winningSquare = 0;
        marker = null;
        playedCount = 0;

        wins.forEach(function (win) {

            square = this.getSquare(win);

            if (square.played) {
                playedCount += 1;
            }

            if (square.className === className) {
                winningSquare += 1;
            } else {
                marker = win;
            }
        }, this);

        if (winningSquare === 2 && playedCount < 3) {
            squareToPlay = marker || null;
        }
    }, this);

    return squareToPlay;
};

/**
 * gameOver
 * stops play
 *
 * @param win {Boolean} is there a win on the board
 */
TicTacToe.prototype.gameOver = function (win) {

    var score = document.createElement('div'),
        button = document.createElement('button');

    button.innerText = 'Play Again';
    button.addEventListener('click', this.nextGame, false);

    score.className = 'message';
    score.innerText = this.getCompleteText(win);
    score.appendChild(button);

    this.el.appendChild(score);

    this.updateScores(win);
    this.gameInPlay = false;

    return this;
};

/**
 * updateScores
 * updates the dom with the winning score
 *
 * @param win {Boolean}
 */
TicTacToe.prototype.updateScores = function (win) {

    var winner = 'ties';

    if (win) {
        winner = this.symbol ? 'you' : 'computer';
    }

    this.scores[winner] += 1;
    this.setScores();

    localStorage.setItem('scores', JSON.stringify(this.scores));
};

/**
 * setScores
 * sets the scores to the DOM
 */
TicTacToe.prototype.setScores = function () {

    var prop;

    for (prop in this.scores) {
        if (this.scores.hasOwnProperty(prop)) {
            document.querySelector('.' + prop).innerHTML = this.scores[prop];
        }
    }
};

// instantiate the class and start the game
var saved = localStorage.getItem('scores'),
    defaults = {
        you: 0,
        ties: 0,
        computer: 0
    },
    scores = saved ? JSON.parse(saved) : defaults,

    // loop for continual play
    newGame = function () {
        return new TicTacToe(scores, newGame);
    };

// start the game loop
newGame();
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import Springs from './Springs';
import {toArr, clone} from './utils';

if (!String.prototype.includes) {
  String.prototype.includes = function() {'use strict';
    return String.prototype.indexOf.apply(this, arguments) !== -1;
  };
}

export default React.createClass({
  getInitialState: function() {
    return {
      todos: {
        // creation date => task name
        1: {text: 'Board the plane', isDone: false},
        2: {text: 'Sleep', isDone: false},
        3: {text: 'Try to finish coneference slides', isDone: false},
        4: {text: 'Eat cheese and drink wine', isDone: false},
        5: {text: 'Go around in Uber', isDone: false},
        6: {text: 'Talk with conf attendees', isDone: false},
        7: {text: 'Show Demo 1', isDone: false},
        8: {text: 'Show Demo 2', isDone: false},
        9: {text: 'Lament about the state of animation', isDone: false},
        10: {text: 'Show Secret Demo', isDone: false},
        11: {text: 'Go home', isDone: false},
      },
      value: '',
    };
  },

  handleChange: function({target: {value}}) {
    this.setState({value});
  },

  handleSubmit: function(e) {
    e.preventDefault();
    let {todos, value} = this.state;
    this.setState({
      todos: {
        ...todos,
        [Date.now()]: {text: value, isDone: false},
      },
    });
  },

  handleDone: function(key) {
    let {todos} = this.state;
    let newState = clone(todos);
    newState[key].isDone = !newState[key].isDone;
    this.setState({todos: newState});
  },

  handleToggleAll: function() {
    let {todos} = this.state;
    let allIsDone = false;
    // If all todos are done, we toggle all of them back
    for(let prop in todos) {
      if(!todos[prop].isDone) {
        allIsDone = true;
        break;
      }
    }

    let newTodos = {};
    for (let prop in todos) {
      newTodos[prop] = {text: todos[prop].text, isDone: allIsDone};
    }
    this.setState({todos: newTodos});
  },

  render: function() {
    let {todos, value} = this.state;
    return (

todos

          <form onSubmit={this.handleSubmit}>
            <input className="new-todo" placeholder="What needs to be done?" autoFocus={true} value={value} onChange={this.handleChange}/>
          </form>

          <input className="toggle-all" type="checkbox" onChange={this.handleToggleAll}/>
          <Springs
            className="demo2"
            finalVals={(currVals, tween) => {
              let configs = {};
              Object.keys(todos)
                .filter(date => {
                  return todos[date].text.toUpperCase().includes(value.toUpperCase());
                })
                .forEach(date => {
                  configs[date] = {height: 60, opacity: 1};
                });
              return tween(configs);
            }}
            // TODO: default: reached dest, v = 0
            shouldRemove={(key, tween, destVals, currVals, currV) => {
              return currVals[key].opacity <= 0 && currV[key].opacity <= 0 ?
                null :
                tween({height: 0, opacity: 0});
            }}
            // TODO: default: destVals[key]
            // lifttable
            missingCurrentKey={() => ({height: 0, opacity: 1})}>
            {configs =>

                {Object.keys(configs).map(date =>

                      <input className="toggle" type="checkbox" onChange={this.handleDone.bind(null, date)} checked={todos[date].isDone}/>
                      {todos[date].text}

                )}

            }
          </Springs>

          {Object.keys(todos).filter(key => !todos[key].isDone).length} item left

              All

              Active

              Completed

          <button className="clear-completed">Clear completed</button>

    );
  }
});
<commit_msg>Added filtering active/complete/all.<commit_after>'use strict';

import React from 'react';
import Springs from './Springs';
import {toArr, clone} from './utils';

if (!String.prototype.includes) {
  String.prototype.includes = function() {'use strict';
    return String.prototype.indexOf.apply(this, arguments) !== -1;
  };
}

export default React.createClass({
  getInitialState: function() {
    return {
      todos: {
        // creation date => task name
        1: {text: 'Board the plane', isDone: false},
        2: {text: 'Sleep', isDone: false},
        3: {text: 'Try to finish coneference slides', isDone: false},
        4: {text: 'Eat cheese and drink wine', isDone: false},
        5: {text: 'Go around in Uber', isDone: false},
        6: {text: 'Talk with conf attendees', isDone: false},
        7: {text: 'Show Demo 1', isDone: false},
        8: {text: 'Show Demo 2', isDone: false},
        9: {text: 'Lament about the state of animation', isDone: false},
        10: {text: 'Show Secret Demo', isDone: false},
        11: {text: 'Go home', isDone: false},
      },
      value: '',
      selected: 'all'
    };
  },

  handleChange: function({target: {value}}) {
    this.setState({value});
  },

  handleSubmit: function(e) {
    e.preventDefault();
    let {todos, value} = this.state;
    this.setState({
      todos: {
        ...todos,
        [Date.now()]: {text: value, isDone: false},
      },
    });
  },

  handleDone: function(key) {
    let {todos} = this.state;
    let newState = clone(todos);
    newState[key].isDone = !newState[key].isDone;
    this.setState({todos: newState});
  },

  handleToggleAll: function() {
    let {todos} = this.state;
    let allIsDone = false;
    // If all todos are done, we toggle all of them back
    for(let prop in todos) {
      if(!todos[prop].isDone) {
        allIsDone = true;
        break;
      }
    }

    let newTodos = {};
    for (let prop in todos) {
      newTodos[prop] = {text: todos[prop].text, isDone: allIsDone};
    }
    this.setState({todos: newTodos});
  },

  handleSelect: function(selected) {
    this.setState({
      selected: selected
    });
  },

  render: function() {
    let {todos, value, selected} = this.state;
    return (

todos

          <form onSubmit={this.handleSubmit}>
            <input className="new-todo" placeholder="What needs to be done?" autoFocus={true} value={value} onChange={this.handleChange}/>
          </form>

          <input className="toggle-all" type="checkbox" onChange={this.handleToggleAll}/>
          <Springs
            className="demo2"
            finalVals={(currVals, tween) => {
              let configs = {};
              Object.keys(todos)
                .filter(date => {
                  return todos[date].text.toUpperCase().includes(value.toUpperCase()) &&
                         (selected === 'completed' && todos[date].isDone ||
                          selected === 'active' && !todos[date].isDone ||
                          selected === 'all');
                })
                .forEach(date => {
                  configs[date] = {height: 60, opacity: 1};
                });
              return tween(configs);
            }}
            // TODO: default: reached dest, v = 0
            shouldRemove={(key, tween, destVals, currVals, currV) => {
              return currVals[key].opacity <= 0 && currV[key].opacity <= 0 ?
                null :
                tween({height: 0, opacity: 0});
            }}
            // TODO: default: destVals[key]
            // lifttable
            missingCurrentKey={() => ({height: 0, opacity: 1})}>
            {configs =>

                {Object.keys(configs).map(date =>

                      <input className="toggle" type="checkbox" onChange={this.handleDone.bind(null, date)} checked={todos[date].isDone}/>
                      {todos[date].text}

                )}

            }
          </Springs>

          {Object.keys(todos).filter(key => !todos[key].isDone).length} item left

              All

              Active

              Completed

    );
  }
});
<|endoftext|>