<commit_before>function Controls(options) {
  this.containerEl = options.controlsEl;
  this.videoEl = options.videoEl;
  this.isPlaying = options.autoplay;

  this.setupControls();
}

Controls.prototype.setupControls = function() {
  var handlePlayPause = this.handlePlayPause.bind(this);
  var handleFullscreen = this.handleFullscreen.bind(this);
  var handleTimeUpdate = this.handleTimeUpdate.bind(this);
  var handleScubberMouseDown = this.handleScubberMouseDown.bind(this);
  var handleTagToggle = this.handleTagToggle.bind(this);

  /* Play pause setup */
  this.playPause = document.createElement('button');
  this.playPause.classList.add('play-pause');

  if(this.isPlaying) {
    this.playPause.classList.add('pause')
  } else {
    this.playPause.classList.add('play');
  }

  /* Tag toggle setup */
  this.tagToggle = document.createElement('button');
  this.tagToggle.classList.add('tag-toggle');

  /* Fullscreen setup */
  this.fullscreen = document.createElement('button');
  this.fullscreen.classList.add('fullscreen');

  /* Button container setup */
  this.buttonsRight = document.createElement('span');
  this.buttonsLeft = document.createElement('span');
  this.buttonContainer = document.createElement('div');
  this.buttonsRight.classList.add('buttons-right');
  this.buttonsLeft.classList.add('buttons-left');

  /* Scrubber setup */
  this.scrubber = document.createElement('button');
  this.scrubberProgress = document.createElement('div');
  this.scrubberContainer = document.createElement('div');
  this.scrubberContainer.appendChild(this.scrubberProgress);
  this.scrubberContainer.appendChild(this.scrubber);
  this.scrubberContainer.classList.add('scrubber-container');
  this.scrubberProgress.classList.add('scrubber-progress');
  this.buttonContainer.classList.add('button-container');

  /* Event setup */
  this.playPause.addEventListener('click', handlePlayPause);
  this.videoEl.addEventListener('timeupdate', handleTimeUpdate);
  this.scrubber.addEventListener('mousedown', handleScubberMouseDown);
  this.fullscreen.addEventListener('click', handleFullscreen);
  this.tagToggle.addEventListener('click', handleTagToggle);

  /* Append to parent */
  this.containerEl.appendChild(this.scrubberContainer);
  this.buttonsLeft.appendChild(this.playPause);
  this.buttonsRight.appendChild(this.tagToggle);
  this.buttonsRight.appendChild(this.fullscreen);
  this.buttonContainer.appendChild(this.buttonsLeft);
  this.buttonContainer.appendChild(this.buttonsRight);
  this.containerEl.appendChild(this.buttonContainer);
};

Controls.prototype.handlePlayPause = function(event) {
  if(this.isPlaying) {
    this.videoEl.pause();
    this.playPause.classList.remove('pause');
    this.playPause.classList.add('play');
  } else {
    this.videoEl.play();
    this.playPause.classList.remove('play');
    this.playPause.classList.add('pause');
  }

  this.isPlaying = ! this.isPlaying;
};

Controls.prototype.handleTimeUpdate = function(event) {
  var currentTime = this.videoEl.currentTime;
  var duration = this.videoEl.duration;

  var currentPercent = currentTime / duration;
  var scrubberLeftPx = currentPercent*this.scrubberContainer.offsetWidth;

  this.scrubber.style.left = scrubberLeftPx  - currentPercent*14 + 'px';;
  this.scrubberProgress.style.width = scrubberLeftPx + 'px';
};

Controls.prototype.handleScubberMouseDown = function(event) {
  var handleSeek = this.handleSeek.bind(this);
  var wasPlaying = this.pauseIfPlaying();

  var handleMouseUp = function(event) {
    document.body.removeEventListener('mousemove', handleSeek);
    document.body.removeEventListener('mouseup', handleMouseUpBound);

    if(wasPlaying) {
      this.videoEl.play();
    }
  };

  var handleMouseUpBound = handleMouseUp.bind(this);

  document.body.addEventListener('mouseup', handleMouseUpBound);
  document.body.addEventListener('mousemove', handleSeek);
};

Controls.prototype.handleSeek = function(event) {
  var containerRext = this.scrubberContainer.getBoundingClientRect();
  var newPosition = event.pageX - containerRext.left;
  var progressAsPercent = newPosition / this.scrubberContainer.offsetWidth;

  if(progressAsPercent <= 1.0 && progressAsPercent >= 0) {
    this.videoEl.currentTime = progressAsPercent*this.videoEl.duration;
    this.scrubber.style.left =  newPosition - progressAsPercent*14 + 'px';
    this.scrubberProgress.style.width = newPosition + 'px';
  }
};

Controls.prototype.handleFullscreen = function() {
  if (this.videoEl.requestFullscreen) {
    this.videoEl.requestFullscreen();
  } else if (this.videoEl.msRequestFullscreen) {
    this.videoEl.msRequestFullscreen();
  } else if (this.videoEl.mozRequestFullScreen) {
    this.videoEl.mozRequestFullScreen();
  } else if (this.videoEl.webkitRequestFullscreen) {
    this.videoEl.webkitRequestFullscreen();
  }
};

Controls.prototype.handleTagToggle = function() {};

Controls.prototype.pauseIfPlaying = function() {
  if(this.isPlaying) {
    this.videoEl.pause();
  }

  return this.isPlaying;
};

module.exports = Controls;
<commit_msg>Make the scrubber move when the scrubber container gets clicked<commit_after>function Controls(options) {
  this.containerEl = options.controlsEl;
  this.videoEl = options.videoEl;
  this.isPlaying = options.autoplay;

  this.setupControls();
}

Controls.prototype.setupControls = function() {
  var handlePlayPause = this.handlePlayPause.bind(this);
  var handleFullscreen = this.handleFullscreen.bind(this);
  var handleTimeUpdate = this.handleTimeUpdate.bind(this);
  var handleScubberMouseDown = this.handleScubberMouseDown.bind(this);
  var handleTagToggle = this.handleTagToggle.bind(this);

  /* Play pause setup */
  this.playPause = document.createElement('button');
  this.playPause.classList.add('play-pause');

  if(this.isPlaying) {
    this.playPause.classList.add('pause')
  } else {
    this.playPause.classList.add('play');
  }

  /* Tag toggle setup */
  this.tagToggle = document.createElement('button');
  this.tagToggle.classList.add('tag-toggle');

  /* Fullscreen setup */
  this.fullscreen = document.createElement('button');
  this.fullscreen.classList.add('fullscreen');

  /* Button container setup */
  this.buttonsRight = document.createElement('span');
  this.buttonsLeft = document.createElement('span');
  this.buttonContainer = document.createElement('div');
  this.buttonsRight.classList.add('buttons-right');
  this.buttonsLeft.classList.add('buttons-left');

  /* Scrubber setup */
  this.scrubber = document.createElement('button');
  this.scrubberProgress = document.createElement('div');
  this.scrubberContainer = document.createElement('div');
  this.scrubberContainer.appendChild(this.scrubberProgress);
  this.scrubberContainer.appendChild(this.scrubber);
  this.scrubberContainer.classList.add('scrubber-container');
  this.scrubberProgress.classList.add('scrubber-progress');
  this.buttonContainer.classList.add('button-container');

  /* Event setup */
  this.playPause.addEventListener('click', handlePlayPause);
  this.videoEl.addEventListener('timeupdate', handleTimeUpdate);
  this.scrubber.addEventListener('mousedown', handleScubberMouseDown);
  this.scrubberContainer.addEventListener('mousedown', handleScubberMouseDown);
  this.fullscreen.addEventListener('click', handleFullscreen);
  this.tagToggle.addEventListener('click', handleTagToggle);

  /* Append to parent */
  this.containerEl.appendChild(this.scrubberContainer);
  this.buttonsLeft.appendChild(this.playPause);
  this.buttonsRight.appendChild(this.tagToggle);
  this.buttonsRight.appendChild(this.fullscreen);
  this.buttonContainer.appendChild(this.buttonsLeft);
  this.buttonContainer.appendChild(this.buttonsRight);
  this.containerEl.appendChild(this.buttonContainer);
};

Controls.prototype.handlePlayPause = function(event) {
  if(this.isPlaying) {
    this.videoEl.pause();
    this.playPause.classList.remove('pause');
    this.playPause.classList.add('play');
  } else {
    this.videoEl.play();
    this.playPause.classList.remove('play');
    this.playPause.classList.add('pause');
  }

  this.isPlaying = ! this.isPlaying;
};

Controls.prototype.handleTimeUpdate = function(event) {
  var currentTime = this.videoEl.currentTime;
  var duration = this.videoEl.duration;

  var currentPercent = currentTime / duration;
  var scrubberLeftPx = currentPercent*this.scrubberContainer.offsetWidth;

  this.scrubber.style.left = scrubberLeftPx  - currentPercent*14 + 'px';;
  this.scrubberProgress.style.width = scrubberLeftPx + 'px';
};

Controls.prototype.handleScubberMouseDown = function(event) {
  var handleSeek = this.handleSeek.bind(this);
  var wasPlaying = this.pauseIfPlaying();

  var handleMouseUp = function(event) {
    document.body.removeEventListener('mousemove', handleSeek);
    document.body.removeEventListener('mouseup', handleMouseUpBound);

    if(wasPlaying) {
      this.videoEl.play();
    }
  };

  var handleMouseUpBound = handleMouseUp.bind(this);

  document.body.addEventListener('mouseup', handleMouseUpBound);
  document.body.addEventListener('mousemove', handleSeek);

  handleSeek(event);
};

Controls.prototype.handleSeek = function(event) {
  var containerRext = this.scrubberContainer.getBoundingClientRect();
  var newPosition = event.pageX - containerRext.left;
  var progressAsPercent = newPosition / this.scrubberContainer.offsetWidth;

  if(progressAsPercent <= 1.0 && progressAsPercent >= 0) {
    this.videoEl.currentTime = progressAsPercent*this.videoEl.duration;
    this.scrubber.style.left =  newPosition - progressAsPercent*14 + 'px';
    this.scrubberProgress.style.width = newPosition + 'px';
  }
};

Controls.prototype.handleFullscreen = function() {
  if (this.videoEl.requestFullscreen) {
    this.videoEl.requestFullscreen();
  } else if (this.videoEl.msRequestFullscreen) {
    this.videoEl.msRequestFullscreen();
  } else if (this.videoEl.mozRequestFullScreen) {
    this.videoEl.mozRequestFullScreen();
  } else if (this.videoEl.webkitRequestFullscreen) {
    this.videoEl.webkitRequestFullscreen();
  }
};

Controls.prototype.handleTagToggle = function() {};

Controls.prototype.pauseIfPlaying = function() {
  if(this.isPlaying) {
    this.videoEl.pause();
  }

  return this.isPlaying;
};

module.exports = Controls;
<|endoftext|>


<commit_before>import {system} from '../polyfill';
import {ModuleSystem} from './ModuleSystem';
import {ModuleManager} from './ModuleManager';

class App extends ModuleSystem {
  simulate = false;
  updateEnabled = true;
  loops = [];

  constructor(modules = []) {
    super();
    this.manager = new ModuleManager(this);
    this.modules = modules;

    this.integrateModules();
  }

  // CONTROLS & UPDATING

  start() {
    const requestAnimFrame = (() => {
      return system.window.requestAnimationFrame
        || system.window.webkitRequestAnimationFrame
        || system.window.mozRequestAnimationFrame
        || function (callback) {
          system.window.setTimeout(callback, 1000 / 60);
        };
    })();

    function process() {
      if (!this.updateEnabled) return;

      requestAnimFrame(process.bind(this));

      for (let i = 0; i < this.loops.length; i++) {
        const e = this.loops[i];
        if (e.enabled) e.execute(e.clock);
      }
    }

    this.updateEnabled = true;
    (process.bind(this))();
  }

  stop() {
    this.updateEnabled = false;
  }

  addLoop(loop) {
    return new Promise(resolve => {
      this.loops.push(loop);
      resolve(loop);
    });
  }

  removeLoop(loop) {
    return new Promise(resolve => {
      this.loops.filter(l => l !== loop);
      resolve(loop);
    });
  }
}

export {
  App
};
<commit_msg>process() optimizations<commit_after>import {system} from '../polyfill';
import {ModuleSystem} from './ModuleSystem';
import {ModuleManager} from './ModuleManager';

class App extends ModuleSystem {
  simulate = false;
  updateEnabled = true;
  loops = [];

  constructor(modules = []) {
    super();
    this.manager = new ModuleManager(this);
    this.modules = modules;

    this.integrateModules();
  }

  // CONTROLS & UPDATING

  start() {
    const requestAnimFrame = (() => {
      return system.window.requestAnimationFrame
        || system.window.webkitRequestAnimationFrame
        || system.window.mozRequestAnimationFrame
        || function (callback) {
          system.window.setTimeout(callback, 1000 / 60);
        };
    })();

    const {loops, updateEnabled} = this;

    function process() {
      requestAnimFrame(process);
      if (!updateEnabled) return;

      for (let i = 0, ll = loops.length; i < ll; i++) {
        const e = loops[i];
        if (e.enabled) e.execute(e.clock);
      }
    }

    this.updateEnabled = true;
    process();
  }

  stop() {
    this.updateEnabled = false;
  }

  addLoop(loop) {
    return new Promise(resolve => {
      this.loops.push(loop);
      resolve(loop);
    });
  }

  removeLoop(loop) {
    return new Promise(resolve => {
      this.loops.filter(l => l !== loop);
      resolve(loop);
    });
  }
}

export {
  App
};
<|endoftext|>


<commit_before>/**
 * Joint abstract class.
 * A Joint represents a node in the hierarchy.
 */
define([
    'events-emitter/EventsEmitter',
    'services/broadcaster',
    '../util/extend',
    'mout/array/insert',
    'mout/array/remove',
    'has'
], function (EventsEmitter, broadcaster, extend, insert, remove, has) {

    'use strict';

    /**
     * Constructor.
     */
    function Joint() {
        this._downlinks = [];
        this._emitter = new EventsEmitter();
    }

    Joint.extend = extend;

    /**
     * Adds a listener for an upcast or broadcast event.
     * Duplicate listeners for the same event will be discarded.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context to be used to call the handler, defaults to the joint instance
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.on = function (event, fn, context) {
        context = context || this;

        this._emitter.on(event, fn, context);
        broadcaster.on(event, fn, context);

        return this;
    };

    /**
     * Adds a one time listener for an upcast or broadcast event.
     * Duplicate listeners for the same event will be discarded.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context to be used to call the handler, defaults to the joint instance
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.once = function (event, fn, context) {
        context = context || this;

        this._emitter.once(event, fn, context);
        broadcaster.once(event, fn, context);

        return this;
    };

    /**
     * Removes a previously added listener.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context passed to the on() method
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.off = function (event, fn, context) {
        context = context || this;

        this._emitter.off(event, fn, context);
        broadcaster.off(event, fn, context);

        return this;
    };

    /**
     * Destroys the instance, releasing all of its resources.
     * Note that all downlinks will also be destroyed.
     */
    Joint.prototype.destroy = function () {
        if (!this._destroyed) {
            this._onDestroy();
            this._destroyed = true;
        }
    };

    // --------------------------------------------

    /**
     * Creates a link between this joint and another one.
     *
     * @param {Joint} joint Another joint to link to this one
     *
     * @return {Joint} The joint passed in as the argument
     */
    Joint.prototype._link = function (joint) {
        if (has('debug') && joint._uplink && joint._uplink !== this) {
            throw new Error('"' + joint._uplink.$name + '" is already linked to other joint');
        }

        if (joint._uplink !== this) {
            joint._uplink = this;
            insert(this._downlinks, joint);
            joint._emitter.emit('link', this);
        }

        return joint;
    };

    /**
     * Removes a previously created link between this joint and another one.
     *
     * @param {Joint} joint Another joint to link to this one
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype._unlink = function (joint) {
        remove(this._downlinks, joint);

        if (joint._uplink === this) {
            joint._uplink = null;
            joint._emitter.emit('unlink', this);
        }

        return this;
    };

    /**
     * Fires an event upwards the chain.
     *
     * @param {String}   event  The event name
     * @param {...mixed} [args] The arguments to pass along with the event
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype._upcast = function (event, args) {
        // Check if the event will be handled locally
        // Otherwise we will keep upcasting upwards the chain
        if (this._emitter.has(event)) {
            this._emitter.emit.apply(this._emitter, arguments);
        } else if (this._uplink) {
            this._uplink._upcast.apply(this._uplink, arguments);
        } else if (has('debug')) {
            console.warn('Unhandled upcast event "' + event + '".');
        }

        return this;
    };

    /**
     * Fires an event to all the joints.
     *
     * @param {String}   event  The event name
     * @param {...mixed} [args] The arguments to pass along with the event
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype._broadcast = function (event, args) {
        broadcaster.broadcast.apply(broadcaster, arguments);

        return this;
    };

    /**
     * Function called by destroy().
     * Subclasses should override this method to release additional resources.
     *
     * The default implementation will also destroy any linked joints.
     */
    Joint.prototype._onDestroy = function () {
        var x,
            curr;

        // Remove the listeners from the broadcaster
        this._emitter.forEach(broadcaster.off, broadcaster);

        // Clear the listeners
        this._emitter.off();

        // Foreach uplink, automatically unlink this instance
        if (this._uplink) {
            this._uplink._unlink(this);
            this._uplink = null;
        }

        // Foreach downlink, automatically unlink it and destroy
        for (x = this._downlinks.length - 1; x >= 0; x -= 1) {
            curr = this._downlinks[x];
            this._unlink(curr);
            curr.destroy();
        }

        this._downlinks = null;
    };

    return Joint;
});
<commit_msg>Add ability to propagate events downwards the chain.<commit_after>/**
 * Joint abstract class.
 * A Joint represents a node in the hierarchy.
 */
define([
    'events-emitter/EventsEmitter',
    'services/broadcaster',
    '../util/extend',
    'mout/array/insert',
    'mout/array/remove',
    'has'
], function (EventsEmitter, broadcaster, extend, insert, remove, has) {

    'use strict';

    /**
     * Constructor.
     */
    function Joint() {
        this._downlinks = [];
        this._emitter = new EventsEmitter();
    }

    Joint.extend = extend;

    /**
     * Adds a listener for an upcast or broadcast event.
     * Duplicate listeners for the same event will be discarded.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context to be used to call the handler, defaults to the joint instance
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.on = function (event, fn, context) {
        context = context || this;

        this._emitter.on(event, fn, context);
        broadcaster.on(event, fn, context);

        return this;
    };

    /**
     * Adds a one time listener for an upcast or broadcast event.
     * Duplicate listeners for the same event will be discarded.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context to be used to call the handler, defaults to the joint instance
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.once = function (event, fn, context) {
        context = context || this;

        this._emitter.once(event, fn, context);
        broadcaster.once(event, fn, context);

        return this;
    };

    /**
     * Removes a previously added listener.
     *
     * @param {String}   event     The event name
     * @param {Function} fn        The handler
     * @param {Object}   [context] The context passed to the on() method
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype.off = function (event, fn, context) {
        context = context || this;

        this._emitter.off(event, fn, context);
        broadcaster.off(event, fn, context);

        return this;
    };

    /**
     * Destroys the instance, releasing all of its resources.
     * Note that all downlinks will also be destroyed.
     */
    Joint.prototype.destroy = function () {
        if (!this._destroyed) {
            this._onDestroy();
            this._destroyed = true;
        }
    };

    // --------------------------------------------

    /**
     * Creates a link between this joint and another one.
     *
     * @param {Joint} joint Another joint to link to this one
     *
     * @return {Joint} The joint passed in as the argument
     */
    Joint.prototype._link = function (joint) {
        if (has('debug') && joint._uplink && joint._uplink !== this) {
            throw new Error('"' + joint._uplink.$name + '" is already linked to other joint');
        }

        if (joint._uplink !== this) {
            joint._uplink = this;
            insert(this._downlinks, joint);
            joint._emitter.emit('link', this);
        }

        return joint;
    };

    /**
     * Removes a previously created link between this joint and another one.
     *
     * @param {Joint} joint Another joint to link to this one
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype._unlink = function (joint) {
        remove(this._downlinks, joint);

        if (joint._uplink === this) {
            joint._uplink = null;
            joint._emitter.emit('unlink', this);
        }

        return this;
    };

    /**
     * Fires an event upwards the chain, starting in this Joint.
     *
     * @param {String}   event  The event name
     * @param {...mixed} [args] The arguments to pass along with the event
     *
     * @return {Boolean} True if it was handled, false otherwise
     */
    Joint.prototype._upcast = function (event, args) {
        // Check if the event will be handled locally
        if (this._emitter.has(event)) {
            this._emitter.emit.apply(this._emitter, arguments);
            return true;
        }

        // Otherwise we will keep upcasting upwards the chain
        if (this._uplink) {
            return this._uplink._upcast.apply(this._uplink, arguments);
        }

        if (has('debug')) {
            console.warn('Unhandled upcast event "' + event + '".');
        }

        return false;
    };

    /**
     * Fires an event downwards the chain, starting in this Joint.
     *
     * @param {String}   event  The event name
     * @param {...mixed} [args] The arguments to pass along with the event
     *
     * @return {Boolean} True if it was handled, false otherwise
     */
    Joint.prototype._downcast = function (event, args) {
        var x,
            length,
            last,
            curr,
            currHandled,
            handled = false;

        // Check if the event will be handled locally
        if (this._emitter.has(event)) {
            this._emitter.emit.apply(this._emitter, arguments);
            return true;
        }

        // Cycle each downlink
        length = this._downlinks.length;
        for (x = 0; x < length; x += 1) {
            curr = this._downlinks[x];

            // Protect against triggered events that cause the destruction of links
            if (!curr || curr === last) {
                continue;
            }

            currHandled = curr._downcast.apply(curr, arguments);
            if (currHandled) {
                handled = true;
            }
        }

        return handled;
    };

    /**
     * Fires an event to all the joints.
     *
     * @param {String}   event  The event name
     * @param {...mixed} [args] The arguments to pass along with the event
     *
     * @return {Joint} The instance itself to allow chaining
     */
    Joint.prototype._broadcast = function (event, args) {
        broadcaster.broadcast.apply(broadcaster, arguments);

        return this;
    };

    /**
     * Function called by destroy().
     * Subclasses should override this method to release additional resources.
     *
     * The default implementation will also destroy any linked joints.
     */
    Joint.prototype._onDestroy = function () {
        var x,
            curr;

        // Remove the listeners from the broadcaster
        this._emitter.forEach(broadcaster.off, broadcaster);

        // Clear the listeners
        this._emitter.off();

        // Foreach uplink, automatically unlink this instance
        if (this._uplink) {
            this._uplink._unlink(this);
            this._uplink = null;
        }

        // Foreach downlink, automatically unlink it and destroy
        for (x = this._downlinks.length - 1; x >= 0; x -= 1) {
            curr = this._downlinks[x];
            this._unlink(curr);
            curr.destroy();
        }

        this._downlinks = null;
    };

    return Joint;
});
<|endoftext|>


<commit_before>/**

 @author: Tom Clement (tjclement)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import difference               from 'lodash/difference.js';
import {Injection}              from '../utils/Injection.js';
import {ObjectHelper}           from '../utils/ObjectHelper.js';
import {PrioritisedObject}      from '../data/PrioritisedObject.js';
import {DataSource}             from '../data/DataSource.js';

/**
 * A model that has attributes synced with the database.
 *
 * @example
 * export class Box extends Model {
 *  get label() {}
 *  get articleNumber() {}
 * }
 */
export class Model extends PrioritisedObject {

    /**
     * Creates a new instance of a model.
     * @param {String} id Optional: The identifier for this model. For a user model this might be a user ID, for example. It
     *           is used to build the path to the dataSource. This path is <root>/<model name appended with 's'>/<id>.
     *           If no id is given, a randomly generated one will be pushed to the dataSource. You can use this for
     *           creating new objects in the dataSource.
     * @param {Object} data Optional: The initial data to fill the model with. The model will be extended with any
     *                      properties present in the data parameter.
     * @param {Object} options Optional: Additional options. Currently used is "dataSnapshot", which if present is used
     *                          to fetch the initial model data. If not present, the model will add a one-time
     *                          subscription to the dataSource to fetch initial data.
     * @returns {Model} Model Instance.
     */
    constructor(id, data = null, options = {}) {

        /* Retrieve dataSource from the DI context */
        let dataSource = options.dataSource || Injection.get(DataSource);
        super();
        this._id = id;
        /* Replace all stub data fields of any subclass of Model with databinding accessors.
         * This causes changes to be synched to and from the dataSource. */
        this._replaceModelAccessorsWithDatabinding();

        /* Calculate path to model in dataSource, used if no dataSource or path are given.
         *
         * The this._name property can be set by Arva's babel-plugin-transform-runtime-constructor-name plugin.
         * This allows Arva code to be minified and mangled without losing automated model name resolving.
         * If the plugin is not set up to run, which is done e.g. when not minifying your code, we default back to the runtime constructor name. */
        let modelName = this.constructor._name || Object.getPrototypeOf(this).constructor.name;

        let pathRoot = modelName + 's';

        let dataIsSynced = new Promise((resolve) => this._dataIsSynced = resolve);
        let dataSourceOptions = {synced: dataIsSynced};

        if (options.dataSource && id) {
            this._dataSource = options.dataSource;
        } else if (options.dataSource) {
            /* No id is present, generate a random one by pushing a new entry to the dataSource. */
            this._dataSource = options.dataSource.push(data);
        } else if (options.path && id) {
            this._dataSource = dataSource.child(options.path + '/' + id || '', dataSourceOptions);
        } else if (options.dataSnapshot) {
            let {ref} = options.dataSnapshot;
            /* Getting the path from a snapshot requires some string modifications */
            this._dataSource = dataSource.child(ref.toString().substring(ref.root.toString().length), dataSourceOptions);
        } else if (id) {
            /* If an id is present, use it to locate our model. */
            this._dataSource = dataSource.child(pathRoot + '/' + id, dataSourceOptions);
        } else {
            /* No id is present, generate a random one by pushing a new entry to the dataSource. */
            if (options.path) {
                this._dataSource = dataSource.child(options.path).push(data);
            } else {
                this._dataSource = dataSource.child(pathRoot).push(data);
            }
        }

        /* Re-construct core PrioritisedObject with new dataSource */
        if (options.dataSnapshot) {
            this._buildFromSnapshot(options.dataSnapshot);
        } else {
            this._buildFromDataSource(this._dataSource);
        }
        if(!options.noInitialSync){
            /* Write local data to model, if any data is present. */
            this._writeLocalDataToModel(data).then(this._dataIsSynced);
        } else {
            this._dataIsSynced();
        }

    }

    /**
     * Check if the model has been synchonized with the database
     * @returns {Promise} Resolves when the model has been synchonized with the database
     */
    synced() {
        return this._dataSource.synced();
    }

    /**
     * Replaces all getters/setters defined on the model implementation with properties that trigger update events to the dataSource.
     * @returns {void}
     * @private
     */
    _replaceModelAccessorsWithDatabinding() {
        let prototype = Object.getPrototypeOf(this);

        if (~Object.getOwnPropertyNames(prototype).indexOf('id')) {
            console.log(`Don't define an id property to ${prototype.constructor.name}, as this property is internally used by the PrioritisedArray`);
        }

        /* If the code is minified, then this.constructor._name is defined, in that case that also goes for the inheriting classes */
        while (prototype.constructor._name || (!this.constructor._name && prototype.constructor.name !== 'Model')) {
            /* Get all properties except the id and constructor of this model */
            let propNames = difference(Object.getOwnPropertyNames(prototype), ['constructor', 'id']);

            for (let name of propNames) {
                let descriptor = Object.getOwnPropertyDescriptor(prototype, name);
                if (descriptor && descriptor.get) {
                    let value = this[name];
                    delete this[name];
                    ObjectHelper.addPropertyToObject(this, name, value, true, true, () => {
                        this._onSetterTriggered();
                    });
                }
            }

            prototype = Object.getPrototypeOf(prototype);
        }
    }

    /**
     * Writes data, if present, to the Model's dataSource. Uses a transaction, meaning that only one update is triggered to the dataSource,
     * even though multiple fields change.
     * @param {Object} data Data to write, can be null.
     * @returns {Promise} Resolves when the transaction is complete and synced
     * @private
     */
    _writeLocalDataToModel(data) {
        if (data) {
            let isDataDifferent = false;
            for (let name in data) {
                if (Object.getOwnPropertyDescriptor(this, name) && this[name] !== data[name]) {
                    isDataDifferent = true;
                    break;
                }
            }

            if (isDataDifferent) {
                return this.transaction(function () {
                    for (let name in data) {

                        // only map properties that exists on our model
                        if (Object.getOwnPropertyDescriptor(this, name)) {
                            let value = data[name];
                            this[name] = value;
                        }
                    }
                }.bind(this));
            }
        }
        return Promise.resolve();
    }
}
<commit_msg>Prevented model data from being written twice<commit_after>/**

 @author: Tom Clement (tjclement)
 @license NPOSL-3.0
 @copyright Bizboard, 2015

 */

import difference               from 'lodash/difference.js';
import {Injection}              from '../utils/Injection.js';
import {ObjectHelper}           from '../utils/ObjectHelper.js';
import {PrioritisedObject}      from '../data/PrioritisedObject.js';
import {DataSource}             from '../data/DataSource.js';

/**
 * A model that has attributes synced with the database.
 *
 * @example
 * export class Box extends Model {
 *  get label() {}
 *  get articleNumber() {}
 * }
 */
export class Model extends PrioritisedObject {

    /**
     * Creates a new instance of a model.
     * @param {String} id Optional: The identifier for this model. For a user model this might be a user ID, for example. It
     *           is used to build the path to the dataSource. This path is <root>/<model name appended with 's'>/<id>.
     *           If no id is given, a randomly generated one will be pushed to the dataSource. You can use this for
     *           creating new objects in the dataSource.
     * @param {Object} data Optional: The initial data to fill the model with. The model will be extended with any
     *                      properties present in the data parameter.
     * @param {Object} options Optional: Additional options. Currently used is "dataSnapshot", which if present is used
     *                          to fetch the initial model data. If not present, the model will add a one-time
     *                          subscription to the dataSource to fetch initial data.
     * @returns {Model} Model Instance.
     */
    constructor(id, data = null, options = {}) {

        /* Retrieve dataSource from the DI context */
        let dataSource = options.dataSource || Injection.get(DataSource);
        super();
        this._id = id;
        /* Replace all stub data fields of any subclass of Model with databinding accessors.
         * This causes changes to be synched to and from the dataSource. */
        this._replaceModelAccessorsWithDatabinding();

        /* Calculate path to model in dataSource, used if no dataSource or path are given.
         *
         * The this._name property can be set by Arva's babel-plugin-transform-runtime-constructor-name plugin.
         * This allows Arva code to be minified and mangled without losing automated model name resolving.
         * If the plugin is not set up to run, which is done e.g. when not minifying your code, we default back to the runtime constructor name. */
        let modelName = this.constructor._name || Object.getPrototypeOf(this).constructor.name;

        let pathRoot = modelName + 's';

        let dataWasPushed = false;
        let dataIsSynced = new Promise((resolve) => this._dataIsSynced = resolve);
        let dataSourceOptions = {synced: dataIsSynced};

        if (options.dataSource && id) {
            this._dataSource = options.dataSource;
        } else if (options.dataSource) {
            /* No id is present, generate a random one by pushing a new entry to the dataSource. */
            dataWasPushed = true;
            let dataSource = options.dataSource.push(data);
            /* Get the last part of the path and set to the ID */
            this._id = dataSource.path().toString().substring(dataSource.root().toString().length);
            this._dataSource = dataSource;
        } else if (options.path && id) {
            this._dataSource = dataSource.child(options.path + '/' + id || '', dataSourceOptions);
        } else if (options.dataSnapshot) {
            let {ref} = options.dataSnapshot;
            /* Getting the path from a snapshot requires some string modifications */
            this._dataSource = dataSource.child(ref.toString().substring(ref.root.toString().length), dataSourceOptions);
        } else if (id) {
            /* If an id is present, use it to locate our model. */
            this._dataSource = dataSource.child(pathRoot + '/' + id, dataSourceOptions);
        } else {
            dataWasPushed = true;
            /* No id is present, generate a random one by pushing a new entry to the dataSource. */
            if (options.path) {
                this._dataSource = dataSource.child(options.path).push(data);
            } else {
                this._dataSource = dataSource.child(pathRoot).push(data);
            }
        }

        /* Re-construct core PrioritisedObject with new dataSource */
        if (options.dataSnapshot) {
            this._buildFromSnapshot(options.dataSnapshot);
        } else {
            this._buildFromDataSource(this._dataSource);
        }
        if(!options.noInitialSync && !dataWasPushed){
            /* Write local data to model, if any data is present. */
            this._writeLocalDataToModel(data).then(this._dataIsSynced);
        } else {
            this._dataIsSynced();
        }
    }

    /**
     * Check if the model has been synchonized with the database
     * @returns {Promise} Resolves when the model has been synchonized with the database
     */
    synced() {
        return this._dataSource.synced();
    }

    /**
     * Replaces all getters/setters defined on the model implementation with properties that trigger update events to the dataSource.
     * @returns {void}
     * @private
     */
    _replaceModelAccessorsWithDatabinding() {
        let prototype = Object.getPrototypeOf(this);

        if (~Object.getOwnPropertyNames(prototype).indexOf('id')) {
            console.log(`Don't define an id property to ${prototype.constructor.name}, as this property is internally used by the PrioritisedArray`);
        }

        /* If the code is minified, then this.constructor._name is defined, in that case that also goes for the inheriting classes */
        while (prototype.constructor._name || (!this.constructor._name && prototype.constructor.name !== 'Model')) {
            /* Get all properties except the id and constructor of this model */
            let propNames = difference(Object.getOwnPropertyNames(prototype), ['constructor', 'id']);

            for (let name of propNames) {
                let descriptor = Object.getOwnPropertyDescriptor(prototype, name);
                if (descriptor && descriptor.get) {
                    let value = this[name];
                    delete this[name];
                    ObjectHelper.addPropertyToObject(this, name, value, true, true, () => {
                        this._onSetterTriggered();
                    });
                }
            }

            prototype = Object.getPrototypeOf(prototype);
        }
    }

    /**
     * Writes data, if present, to the Model's dataSource. Uses a transaction, meaning that only one update is triggered to the dataSource,
     * even though multiple fields change.
     * @param {Object} data Data to write, can be null.
     * @returns {Promise} Resolves when the transaction is complete and synced
     * @private
     */
    _writeLocalDataToModel(data) {
        if (data) {
            let isDataDifferent = false;
            for (let name in data) {
                if (Object.getOwnPropertyDescriptor(this, name) && this[name] !== data[name]) {
                    isDataDifferent = true;
                    break;
                }
            }

            if (isDataDifferent) {
                return this.transaction(function () {
                    for (let name in data) {

                        // only map properties that exists on our model
                        if (Object.getOwnPropertyDescriptor(this, name)) {
                            let value = data[name];
                            this[name] = value;
                        }
                    }
                }.bind(this));
            }
        }
        return Promise.resolve();
    }

}
<|endoftext|>


<commit_before>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2015 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* "This world is but a canvas to our imagination." - Henry David Thoreau
*
* A game has only one world. The world is an abstract place in which all game objects live. It is not bound
* by stage limits and can be any size. You look into the world via cameras. All game objects live within
* the world at world-based coordinates. By default a world is created the same size as your Stage.
*
* @class Phaser.World
* @extends Phaser.Group
* @constructor
* @param {Phaser.Game} game - Reference to the current game instance.
*/
Phaser.World = function (game) {

    Phaser.Group.call(this, game, null, '__world', false);

    /**
    * The World has no fixed size, but it does have a bounds outside of which objects are no longer considered as being "in world" and you should use this to clean-up the display list and purge dead objects.
    * By default we set the Bounds to be from 0,0 to Game.width,Game.height. I.e. it will match the size given to the game constructor with 0,0 representing the top-left of the display.
    * However 0,0 is actually the center of the world, and if you rotate or scale the world all of that will happen from 0,0.
    * So if you want to make a game in which the world itself will rotate you should adjust the bounds so that 0,0 is the center point, i.e. set them to -1000,-1000,2000,2000 for a 2000x2000 sized world centered around 0,0.
    * @property {Phaser.Rectangle} bounds - Bound of this world that objects can not escape from.
    */
    this.bounds = new Phaser.Rectangle(0, 0, game.width, game.height);

    /**
    * @property {Phaser.Camera} camera - Camera instance.
    */
    this.camera = null;

    /**
    * @property {boolean} _definedSize - True if the World has been given a specifically defined size (i.e. from a Tilemap or direct in code) or false if it's just matched to the Game dimensions.
    * @readonly
    */
    this._definedSize = false;

    /**
    * @property {number} width - The defined width of the World. Sometimes the bounds needs to grow larger than this (if you resize the game) but this retains the original requested dimension.
    */
    this._width = game.width;

    /**
    * @property {number} height - The defined height of the World. Sometimes the bounds needs to grow larger than this (if you resize the game) but this retains the original requested dimension.
    */
    this._height = game.height;

};

Phaser.World.prototype = Object.create(Phaser.Group.prototype);
Phaser.World.prototype.constructor = Phaser.World;

/**
* Initialises the game world.
*
* @method Phaser.World#boot
* @protected
*/
Phaser.World.prototype.boot = function () {

    this.camera = new Phaser.Camera(this.game, 0, 0, 0, this.game.width, this.game.height);

    this.camera.displayObject = this;

    this.camera.scale = this.scale;

    this.game.camera = this.camera;

    this.game.stage.addChild(this);

};

/**
* Updates the size of this world and sets World.x/y to the given values
* The Camera bounds and Physics bounds (if set) are also updated to match the new World bounds.
*
* @method Phaser.World#setBounds
* @param {number} x - Top left most corner of the world.
* @param {number} y - Top left most corner of the world.
* @param {number} width - New width of the game world in pixels.
* @param {number} height - New height of the game world in pixels.
*/
Phaser.World.prototype.setBounds = function (x, y, width, height) {

    this._definedSize = true;
    this._width = width;
    this._height = height;

    this.bounds.setTo(x, y, width, height);

    this.x = x;
    this.y = y;

    if (this.camera.bounds)
    {
        //  The Camera can never be smaller than the game size
        this.camera.bounds.setTo(x, y, Math.max(width, this.game.width), Math.max(height, this.game.height));
    }

    this.game.physics.setBoundsToWorld();

};

/**
* Updates the size of this world. Note that this doesn't modify the world x/y coordinates, just the width and height.
*
* @method Phaser.World#resize
* @param {number} width - New width of the game world in pixels.
* @param {number} height - New height of the game world in pixels.
*/
Phaser.World.prototype.resize = function (width, height) {

    //  Don't ever scale the World bounds lower than the original requested dimensions if it's a defined world size

    if (this._definedSize)
    {
        if (width < this._width)
        {
            width = this._width;
        }

        if (height < this._height)
        {
            height = this._height;
        }
    }

    this.bounds.width = width;
    this.bounds.height = height;

    this.game.camera.setBoundsToWorld();

    this.game.physics.setBoundsToWorld();

};

/**
* Destroyer of worlds.
*
* @method Phaser.World#shutdown
*/
Phaser.World.prototype.shutdown = function () {

    //  World is a Group, so run a soft destruction on this and all children.
    this.destroy(true, true);

};

/**
* This will take the given game object and check if its x/y coordinates fall outside of the world bounds.
* If they do it will reposition the object to the opposite side of the world, creating a wrap-around effect.
* If sprite has a P2 body then the body (sprite.body) should be passed as first parameter to the function.
*
* @method Phaser.World#wrap
* @param {Phaser.Sprite|Phaser.Image|Phaser.TileSprite|Phaser.Text} sprite - The object you wish to wrap around the world bounds.
* @param {number} [padding=0] - Extra padding added equally to the sprite.x and y coordinates before checking if within the world bounds. Ignored if useBounds is true.
* @param {boolean} [useBounds=false] - If useBounds is false wrap checks the object.x/y coordinates. If true it does a more accurate bounds check, which is more expensive.
* @param {boolean} [horizontal=true] - If horizontal is false, wrap will not wrap the object.x coordinates horizontally.
* @param {boolean} [vertical=true] - If vertical is false, wrap will not wrap the object.y coordinates vertically.
*/
Phaser.World.prototype.wrap = function (sprite, padding, useBounds, horizontal, vertical) {

    if (typeof padding === 'undefined') { padding = 0; }
    if (typeof useBounds === 'undefined') { useBounds = false; }
    if (typeof horizontal === 'undefined') { horizontal = true; }
    if (typeof vertical === 'undefined') { vertical = true; }

    if (!useBounds)
    {
        if (horizontal && sprite.x + padding < this.bounds.x)
        {
            sprite.x = this.bounds.right + padding;
        }
        else if (horizontal && sprite.x - padding > this.bounds.right)
        {
            sprite.x = this.bounds.left - padding;
        }

        if (vertical && sprite.y + padding < this.bounds.top)
        {
            sprite.y = this.bounds.bottom + padding;
        }
        else if (vertical && sprite.y - padding > this.bounds.bottom)
        {
            sprite.y = this.bounds.top - padding;
        }
    }
    else
    {
        sprite.getBounds();

        if (horizontal)
        {
            if ((sprite.x + sprite._currentBounds.width) < this.bounds.x)
            {
                sprite.x = this.bounds.right;
            }
            else if (sprite.x > this.bounds.right)
            {
                sprite.x = this.bounds.left;
            }
        }

        if (vertical)
        {
            if ((sprite.y + sprite._currentBounds.height) < this.bounds.top)
            {
                sprite.y = this.bounds.bottom;
            }
            else if (sprite.y > this.bounds.bottom)
            {
                sprite.y = this.bounds.top;
            }
        }
    }

};

/**
* @name Phaser.World#width
* @property {number} width - Gets or sets the current width of the game world. The world can never be smaller than the game (canvas) dimensions.
*/
Object.defineProperty(Phaser.World.prototype, "width", {

    get: function () {
        return this.bounds.width;
    },

    set: function (value) {

        if (value < this.game.width)
        {
            value = this.game.width;
        }

        this.bounds.width = value;
        this._width = value;
        this._definedSize = true;

    }

});

/**
* @name Phaser.World#height
* @property {number} height - Gets or sets the current height of the game world. The world can never be smaller than the game (canvas) dimensions.
*/
Object.defineProperty(Phaser.World.prototype, "height", {

    get: function () {
        return this.bounds.height;
    },

    set: function (value) {

        if (value < this.game.height)
        {
            value = this.game.height;
        }

        this.bounds.height = value;
        this._height = value;
        this._definedSize = true;

    }

});

/**
* @name Phaser.World#centerX
* @property {number} centerX - Gets the X position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerX", {

    get: function () {
        return this.bounds.halfWidth;
    }

});

/**
* @name Phaser.World#centerY
* @property {number} centerY - Gets the Y position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerY", {

    get: function () {
        return this.bounds.halfHeight;
    }

});

/**
* @name Phaser.World#randomX
* @property {number} randomX - Gets a random integer which is lesser than or equal to the current width of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomX", {

    get: function () {

        if (this.bounds.x < 0)
        {
            return this.game.rnd.integerInRange(this.bounds.x, (this.bounds.width - Math.abs(this.bounds.x)));
        }
        else
        {
            return this.game.rnd.integerInRange(this.bounds.x, this.bounds.width);
        }

    }

});

/**
* @name Phaser.World#randomY
* @property {number} randomY - Gets a random integer which is lesser than or equal to the current height of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomY", {

    get: function () {

        if (this.bounds.y < 0)
        {
            return this.game.rnd.integerInRange(this.bounds.y, (this.bounds.height - Math.abs(this.bounds.y)));
        }
        else
        {
            return this.game.rnd.integerInRange(this.bounds.y, this.bounds.height);
        }

    }

});
<commit_msg>Swapped to the shorter 'between' method.<commit_after>/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2015 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* "This world is but a canvas to our imagination." - Henry David Thoreau
*
* A game has only one world. The world is an abstract place in which all game objects live. It is not bound
* by stage limits and can be any size. You look into the world via cameras. All game objects live within
* the world at world-based coordinates. By default a world is created the same size as your Stage.
*
* @class Phaser.World
* @extends Phaser.Group
* @constructor
* @param {Phaser.Game} game - Reference to the current game instance.
*/
Phaser.World = function (game) {

    Phaser.Group.call(this, game, null, '__world', false);

    /**
    * The World has no fixed size, but it does have a bounds outside of which objects are no longer considered as being "in world" and you should use this to clean-up the display list and purge dead objects.
    * By default we set the Bounds to be from 0,0 to Game.width,Game.height. I.e. it will match the size given to the game constructor with 0,0 representing the top-left of the display.
    * However 0,0 is actually the center of the world, and if you rotate or scale the world all of that will happen from 0,0.
    * So if you want to make a game in which the world itself will rotate you should adjust the bounds so that 0,0 is the center point, i.e. set them to -1000,-1000,2000,2000 for a 2000x2000 sized world centered around 0,0.
    * @property {Phaser.Rectangle} bounds - Bound of this world that objects can not escape from.
    */
    this.bounds = new Phaser.Rectangle(0, 0, game.width, game.height);

    /**
    * @property {Phaser.Camera} camera - Camera instance.
    */
    this.camera = null;

    /**
    * @property {boolean} _definedSize - True if the World has been given a specifically defined size (i.e. from a Tilemap or direct in code) or false if it's just matched to the Game dimensions.
    * @readonly
    */
    this._definedSize = false;

    /**
    * @property {number} width - The defined width of the World. Sometimes the bounds needs to grow larger than this (if you resize the game) but this retains the original requested dimension.
    */
    this._width = game.width;

    /**
    * @property {number} height - The defined height of the World. Sometimes the bounds needs to grow larger than this (if you resize the game) but this retains the original requested dimension.
    */
    this._height = game.height;

};

Phaser.World.prototype = Object.create(Phaser.Group.prototype);
Phaser.World.prototype.constructor = Phaser.World;

/**
* Initialises the game world.
*
* @method Phaser.World#boot
* @protected
*/
Phaser.World.prototype.boot = function () {

    this.camera = new Phaser.Camera(this.game, 0, 0, 0, this.game.width, this.game.height);

    this.camera.displayObject = this;

    this.camera.scale = this.scale;

    this.game.camera = this.camera;

    this.game.stage.addChild(this);

};

/**
* Updates the size of this world and sets World.x/y to the given values
* The Camera bounds and Physics bounds (if set) are also updated to match the new World bounds.
*
* @method Phaser.World#setBounds
* @param {number} x - Top left most corner of the world.
* @param {number} y - Top left most corner of the world.
* @param {number} width - New width of the game world in pixels.
* @param {number} height - New height of the game world in pixels.
*/
Phaser.World.prototype.setBounds = function (x, y, width, height) {

    this._definedSize = true;
    this._width = width;
    this._height = height;

    this.bounds.setTo(x, y, width, height);

    this.x = x;
    this.y = y;

    if (this.camera.bounds)
    {
        //  The Camera can never be smaller than the game size
        this.camera.bounds.setTo(x, y, Math.max(width, this.game.width), Math.max(height, this.game.height));
    }

    this.game.physics.setBoundsToWorld();

};

/**
* Updates the size of this world. Note that this doesn't modify the world x/y coordinates, just the width and height.
*
* @method Phaser.World#resize
* @param {number} width - New width of the game world in pixels.
* @param {number} height - New height of the game world in pixels.
*/
Phaser.World.prototype.resize = function (width, height) {

    //  Don't ever scale the World bounds lower than the original requested dimensions if it's a defined world size

    if (this._definedSize)
    {
        if (width < this._width)
        {
            width = this._width;
        }

        if (height < this._height)
        {
            height = this._height;
        }
    }

    this.bounds.width = width;
    this.bounds.height = height;

    this.game.camera.setBoundsToWorld();

    this.game.physics.setBoundsToWorld();

};

/**
* Destroyer of worlds.
*
* @method Phaser.World#shutdown
*/
Phaser.World.prototype.shutdown = function () {

    //  World is a Group, so run a soft destruction on this and all children.
    this.destroy(true, true);

};

/**
* This will take the given game object and check if its x/y coordinates fall outside of the world bounds.
* If they do it will reposition the object to the opposite side of the world, creating a wrap-around effect.
* If sprite has a P2 body then the body (sprite.body) should be passed as first parameter to the function.
*
* @method Phaser.World#wrap
* @param {Phaser.Sprite|Phaser.Image|Phaser.TileSprite|Phaser.Text} sprite - The object you wish to wrap around the world bounds.
* @param {number} [padding=0] - Extra padding added equally to the sprite.x and y coordinates before checking if within the world bounds. Ignored if useBounds is true.
* @param {boolean} [useBounds=false] - If useBounds is false wrap checks the object.x/y coordinates. If true it does a more accurate bounds check, which is more expensive.
* @param {boolean} [horizontal=true] - If horizontal is false, wrap will not wrap the object.x coordinates horizontally.
* @param {boolean} [vertical=true] - If vertical is false, wrap will not wrap the object.y coordinates vertically.
*/
Phaser.World.prototype.wrap = function (sprite, padding, useBounds, horizontal, vertical) {

    if (typeof padding === 'undefined') { padding = 0; }
    if (typeof useBounds === 'undefined') { useBounds = false; }
    if (typeof horizontal === 'undefined') { horizontal = true; }
    if (typeof vertical === 'undefined') { vertical = true; }

    if (!useBounds)
    {
        if (horizontal && sprite.x + padding < this.bounds.x)
        {
            sprite.x = this.bounds.right + padding;
        }
        else if (horizontal && sprite.x - padding > this.bounds.right)
        {
            sprite.x = this.bounds.left - padding;
        }

        if (vertical && sprite.y + padding < this.bounds.top)
        {
            sprite.y = this.bounds.bottom + padding;
        }
        else if (vertical && sprite.y - padding > this.bounds.bottom)
        {
            sprite.y = this.bounds.top - padding;
        }
    }
    else
    {
        sprite.getBounds();

        if (horizontal)
        {
            if ((sprite.x + sprite._currentBounds.width) < this.bounds.x)
            {
                sprite.x = this.bounds.right;
            }
            else if (sprite.x > this.bounds.right)
            {
                sprite.x = this.bounds.left;
            }
        }

        if (vertical)
        {
            if ((sprite.y + sprite._currentBounds.height) < this.bounds.top)
            {
                sprite.y = this.bounds.bottom;
            }
            else if (sprite.y > this.bounds.bottom)
            {
                sprite.y = this.bounds.top;
            }
        }
    }

};

/**
* @name Phaser.World#width
* @property {number} width - Gets or sets the current width of the game world. The world can never be smaller than the game (canvas) dimensions.
*/
Object.defineProperty(Phaser.World.prototype, "width", {

    get: function () {
        return this.bounds.width;
    },

    set: function (value) {

        if (value < this.game.width)
        {
            value = this.game.width;
        }

        this.bounds.width = value;
        this._width = value;
        this._definedSize = true;

    }

});

/**
* @name Phaser.World#height
* @property {number} height - Gets or sets the current height of the game world. The world can never be smaller than the game (canvas) dimensions.
*/
Object.defineProperty(Phaser.World.prototype, "height", {

    get: function () {
        return this.bounds.height;
    },

    set: function (value) {

        if (value < this.game.height)
        {
            value = this.game.height;
        }

        this.bounds.height = value;
        this._height = value;
        this._definedSize = true;

    }

});

/**
* @name Phaser.World#centerX
* @property {number} centerX - Gets the X position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerX", {

    get: function () {
        return this.bounds.halfWidth;
    }

});

/**
* @name Phaser.World#centerY
* @property {number} centerY - Gets the Y position corresponding to the center point of the world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "centerY", {

    get: function () {
        return this.bounds.halfHeight;
    }

});

/**
* @name Phaser.World#randomX
* @property {number} randomX - Gets a random integer which is lesser than or equal to the current width of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomX", {

    get: function () {

        if (this.bounds.x < 0)
        {
            return this.game.rnd.between(this.bounds.x, (this.bounds.width - Math.abs(this.bounds.x)));
        }
        else
        {
            return this.game.rnd.between(this.bounds.x, this.bounds.width);
        }

    }

});

/**
* @name Phaser.World#randomY
* @property {number} randomY - Gets a random integer which is lesser than or equal to the current height of the game world.
* @readonly
*/
Object.defineProperty(Phaser.World.prototype, "randomY", {

    get: function () {

        if (this.bounds.y < 0)
        {
            return this.game.rnd.between(this.bounds.y, (this.bounds.height - Math.abs(this.bounds.y)));
        }
        else
        {
            return this.game.rnd.between(this.bounds.y, this.bounds.height);
        }

    }

});
<|endoftext|>


<commit_before>import helmet from 'helmet';
import bodyParser from 'body-parser';
import compression from 'compression';
import methodOverride from 'method-override';
import cookieParser from 'cookie-parser';
import passport from 'passport';
import session from 'express-session';
import cors from 'cors';
import fileUpload from 'express-fileupload';
import morgan from 'morgan';
import config from './../config';

export default (app) => {

  app.use(bodyParser.json({ limit: '5mb' }));
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(fileUpload());
  app.use(cookieParser());
  app.use(methodOverride());
  app.use(compression());
  app.use(helmet());
  app.use(cors({ origin: true, credentials: true }));

  if ("twitter" in config.oAuth && config.oAuth.twitter.enabled)
    app.use(session({ secret: config.secret, resave: false, saveUninitialized: false }));

  app.use(passport.initialize());
  app.use(passport.session());

  // Liverload
  if (config.mode === 'development' && config.livereload.enabled)
    app.use(require('connect-livereload')({ src: `http://${config.livereload.ip}:${config.livereload.port}/livereload.js` }));

  // Morgan
  if (config.log)
    app.use(morgan('dev'));

  // Mongo
  require('../lib/mongoose');

  // Redis
  require('../lib/redis');

  // Socket.io
  require('../lib/socket.io');

  // Services
  require('../auth/services');

  // You can add more require's here!
};
<commit_msg>added async to libs<commit_after>import helmet from 'helmet';
import bodyParser from 'body-parser';
import compression from 'compression';
import methodOverride from 'method-override';
import cookieParser from 'cookie-parser';
import passport from 'passport';
import session from 'express-session';
import cors from 'cors';
import fileUpload from 'express-fileupload';
import morgan from 'morgan';
import config from './../config';

export async function index(app) {

  app.use(bodyParser.json({ limit: '5mb' }));
  app.use(bodyParser.urlencoded({ extended: false }));
  app.use(fileUpload());
  app.use(cookieParser());
  app.use(methodOverride());
  app.use(compression());
  app.use(helmet());
  app.use(cors({ origin: true, credentials: true }));

  if ("twitter" in config.oAuth && config.oAuth.twitter.enabled)
    app.use(session({ secret: config.secret, resave: false, saveUninitialized: false }));

  app.use(passport.initialize());
  app.use(passport.session());

  // Liverload
  if (config.mode === 'development' && config.livereload.enabled)
    app.use(require('connect-livereload')({ src: `http://${config.livereload.ip}:${config.livereload.port}/livereload.js` }));

  // Morgan
  if (config.log)
    app.use(morgan('dev'));

  // Mongoose
  await require('../lib/mongoose').connect();

  // Redis
  await require('../lib/redis');

  if (config.redis.sessions.pubsub.enabled)
    await require('../lib/redis/pubsub');

  // Socket.io
  await require('../lib/socket.io').connect();

  // Services
  await require('../auth/services');

  // You can add more require's here!
};
<|endoftext|>


<commit_before>/**
 * logging
 */

(function() {

    // error management
    cdb.core.Error = Backbone.Model.extend({
        url: cdb.config.REPORT_ERROR_URL,
        initialize: function() {
            this.set({browser: JSON.stringify($.browser) });
        }
    });

    cdb.core.ErrorList = Backbone.Collection.extend({
        model: cdb.core.Error
    });

    /** contains all error for the application */
    cdb.errors = new cdb.core.ErrorList();

    // error tracking!
    if(cdb.config.ERROR_TRACK_ENABLED) {
        window.onerror = function(msg, url, line) {
            cdb.errors.create({
                msg: msg,
                url: url,
                line: line
            });
        };
    }

    // logging
    var _fake_console = function() {};
    _fake_console.prototype.error = function(){};
    _fake_console.prototype.log= function(){};

    //IE7 love
    if(typeof console !== "undefined") {
        _console = console;
        try {
          _console.error.apply(_console, ['test'])
        } catch(e) {
          _console = new _fake_console();
        }
    } else {
        _console = new _fake_console();
    }

    cdb.core.Log = Backbone.Model.extend({

        error: function() {
            _console.error.apply(_console, arguments);
            if(cdb.config.ERROR_TRACK_ENABLED) {
              cdb.errors.create({
                  msg: Array.prototype.slice.call(arguments).join('')
              });
            }
        },

        log: function() {
            _console.log.apply(_console, arguments);
        },

        info: function() {
            _console.log.apply(_console, arguments);
        },

        debug: function() {
            _console.log.apply(_console, arguments);
        }
    });

})();

cdb.log = new cdb.core.Log({tag: 'cdb'});
<commit_msg>print version as test<commit_after>/**
 * logging
 */

(function() {

    // error management
    cdb.core.Error = Backbone.Model.extend({
        url: cdb.config.REPORT_ERROR_URL,
        initialize: function() {
            this.set({browser: JSON.stringify($.browser) });
        }
    });

    cdb.core.ErrorList = Backbone.Collection.extend({
        model: cdb.core.Error
    });

    /** contains all error for the application */
    cdb.errors = new cdb.core.ErrorList();

    // error tracking!
    if(cdb.config.ERROR_TRACK_ENABLED) {
        window.onerror = function(msg, url, line) {
            cdb.errors.create({
                msg: msg,
                url: url,
                line: line
            });
        };
    }

    // logging
    var _fake_console = function() {};
    _fake_console.prototype.error = function(){};
    _fake_console.prototype.log= function(){};

    //IE7 love
    if(typeof console !== "undefined") {
        _console = console;
        try {
          _console.log.apply(_console, ['cartodb.js ' + cartodb.VERSION])
        } catch(e) {
          _console = new _fake_console();
        }
    } else {
        _console = new _fake_console();
    }

    cdb.core.Log = Backbone.Model.extend({

        error: function() {
            _console.error.apply(_console, arguments);
            if(cdb.config.ERROR_TRACK_ENABLED) {
              cdb.errors.create({
                  msg: Array.prototype.slice.call(arguments).join('')
              });
            }
        },

        log: function() {
            _console.log.apply(_console, arguments);
        },

        info: function() {
            _console.log.apply(_console, arguments);
        },

        debug: function() {
            _console.log.apply(_console, arguments);
        }
    });

})();

cdb.log = new cdb.core.Log({tag: 'cdb'});
<|endoftext|>


<commit_before>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

export Choropleth from './Choropleth.js';
export Color from './Color.js';
export Contract from './Contract.js';
export Dimension from './Dimension.js';
export DimensionSet from './DimensionSet.js';
export Faq from './Faq.js';
export Feature from './Feature.js';
export Font from './Font.js';
export FontFamily from './FontFamily.js';
export Highlight from './Highlight.js';
export InsetMap from './InsetMap.js';
export Job from './Job.js';
export JobResult from './JobResult.js';
export JobRevision from './JobRevision.js';
export JobShare from './JobShare.js';
export JobType from './JobType.js';
export Language from './Language.js';
export Layer from './Layer.js';
export Mapstyle from './Mapstyle.js';
export MapstyleSet from './MapstyleSet.js';
export Notification from './Notification.js';
export Organisation from './Organisation.js';
export Permission from './Permission.js';
export PlaceName from './PlaceName.js';
export Svg from './Svg.js';
export SvgSet from './SvgSet.js';
export SvgSetType from './SvgSetType.js';
export User from './User.js';

import CrudBase from './base/CrudBase';
import CrudSetBase from './base/CrudSetBase';
import ResourceBase from './base/ResourceBase';

/**
 * @private
 */
export const base = {
  CrudBase, CrudSetBase, ResourceBase,
};
<commit_msg>Add roles to crud export<commit_after>/*
 * BSD 3-Clause License
 *
 * Copyright (c) 2017, MapCreator
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 *  Neither the name of the copyright holder nor the names of its
 *   contributors may be used to endorse or promote products derived from
 *   this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

export Choropleth from './Choropleth.js';
export Color from './Color.js';
export Contract from './Contract.js';
export Dimension from './Dimension.js';
export DimensionSet from './DimensionSet.js';
export Faq from './Faq.js';
export Feature from './Feature.js';
export Font from './Font.js';
export FontFamily from './FontFamily.js';
export Highlight from './Highlight.js';
export InsetMap from './InsetMap.js';
export Job from './Job.js';
export JobResult from './JobResult.js';
export JobRevision from './JobRevision.js';
export JobShare from './JobShare.js';
export JobType from './JobType.js';
export Language from './Language.js';
export Layer from './Layer.js';
export Mapstyle from './Mapstyle.js';
export MapstyleSet from './MapstyleSet.js';
export Notification from './Notification.js';
export Organisation from './Organisation.js';
export Permission from './Permission.js';
export PlaceName from './PlaceName.js';
export Role from './Role.js';
export Svg from './Svg.js';
export SvgSet from './SvgSet.js';
export SvgSetType from './SvgSetType.js';
export User from './User.js';

import CrudBase from './base/CrudBase';
import CrudSetBase from './base/CrudSetBase';
import ResourceBase from './base/ResourceBase';

/**
 * @private
 */
export const base = {
  CrudBase, CrudSetBase, ResourceBase,
};
<|endoftext|>


<commit_before>import exec from './exec.js';
import Host from './host.js';

// private functions

const createHomeFolder = async (name) => {
    await exec('mkdir -p /var/www/{{name}}', { name });
    await exec('chown -R {{name}}:{{name}} /var/www/{{name}}', { name });
};

const createBackupFolder = async (name) => {
    await exec('mkdir -p /var/backup-www/{{name}}', { name });
};

const removeHomeFolder = async (name) => {
    await exec('rm -fr /var/www/{{name}}', { name });
};

// customer class

class Customer {
    constructor(name) {
        if (name.length > 16 || name.match(/[^a-z0-9]/i)) {
            throw `invalid customer name "${name}"`;
        }
        this.name = name;
        this.hostnames = {};
    }

    async create() {
        await exec('useradd --home-dir /var/www/{{name}} --shell /bin/false {{name}}', {
            name: this.name
        });
        await createHomeFolder(this.name);
    }

    async remove() {
        await createBackupFolder(this.name);

        // generate a compressed backup of the customer's home folder and then remove the home folder
        await exec('deluser --backup --backup-to /var/backup-www/{{name}} --remove-home {{name}}', {
            name: this.name
        });

        // for some reason, the '--remove-home' command is not working properly, so we have to delete the home folder "manually"
        await removeHomeFolder(this.name);
    }

    async addHost(hostname) {
        await (new Host(this, hostname)).create();
    }

    async removeHost(hostname) {
        await (new Host(this, hostname)).remove();
    }
}

export default Customer;
<commit_msg>update regexp an set user length to 32<commit_after>import exec from './exec.js';
import Host from './host.js';

// private functions

const createHomeFolder = async (name) => {
    await exec('mkdir -p /var/www/{{name}}', { name });
    await exec('chown -R {{name}}:{{name}} /var/www/{{name}}', { name });
};

const createBackupFolder = async (name) => {
    await exec('mkdir -p /var/backup-www/{{name}}', { name });
};

const removeHomeFolder = async (name) => {
    await exec('rm -fr /var/www/{{name}}', { name });
};

// customer class

class Customer {
    constructor(name) {
        if (name.length > 32 || !name.match(/^[a-z][-a-z0-9_]*$/)) {
            throw `invalid customer name "${name}"`;
        }
        this.name = name;
        this.hostnames = {};
    }

    async create() {
        await exec('useradd --home-dir /var/www/{{name}} --shell /bin/false {{name}}', {
            name: this.name
        });
        await createHomeFolder(this.name);
    }

    async remove() {
        await createBackupFolder(this.name);

        // generate a compressed backup of the customer's home folder and then remove the home folder
        await exec('deluser --backup --backup-to /var/backup-www/{{name}} --remove-home {{name}}', {
            name: this.name
        });

        // for some reason, the '--remove-home' command is not working properly, so we have to delete the home folder "manually"
        await removeHomeFolder(this.name);
    }

    async addHost(hostname) {
        await (new Host(this, hostname)).create();
    }

    async removeHost(hostname) {
        await (new Host(this, hostname)).remove();
    }
}

export default Customer;
<|endoftext|>


<commit_before>export const patterns = [{
	name:"Date in format dd/mm/yyyy",
	regex:/^(0?[1-9]|[12][0-9]|3[01])([ /\-])(0?[1-9]|1[012])\2([0-9][0-9][0-9][0-9])$/,
	description:"Will match dates with dashes, slashes or with spaces dd-mm-yyyy dd/mm/yyyy dd mm yyyy",
	tags:"date,time"
},
{
	name:"Time in 24-hour format",
	regex:/([01]?[0-9]|2[0-3]):[0-5][0-9]/,
	description: "Match times in 24 hour format",
	tags:"date,time"
},
{
	name: "Date and time in ISO-8601 format",
	regex: /^(?![+-]?\d{4,5}-?(?:\d{2}|W\d{2})T)(?:|(\d{4}|[+-]\d{5})-?(?:|(0\d|1[0-2])(?:|-?([0-2]\d|3[0-1]))|([0-2]\d{2}|3[0-5]\d|36[0-6])|W([0-4]\d|5[0-3])(?:|-?([1-7])))(?:(?!\d)|T(?=\d)))(?:|([01]\d|2[0-4])(?:|:?([0-5]\d)(?:|:?([0-5]\d)(?:|\.(\d{3})))(?:|[zZ]|([+-](?:[01]\d|2[0-4]))(?:|:?([0-5]\d)))))$/,
	description: "Will match a valid date and times in the ISO-8601 format, excludes durations.",
	tags: "date,time"
},
{
	name:"HTML tags",
	regex:/^<([a-z1-6]+)([^<]+)*(?:>(.*)<\/\1>| *\/>)$/,
	description:"Match opening and closing HTML tags with content between",
	tags:"markup,xml,html"
},
{
	name:"Username",
	regex:/^[a-zA-Z0-9_-]{3,16}$/,
	description:"A string between 3 and 16 characters, allowing alphanumeric characters and hyphens and underscores",
	tags:"username,validation"
},
{
	name:"Hex Color Value",
	regex:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
	description:"RGB hex colors",
	tags:"hex,color"
},
{
	name:"URL Slug",
	regex:/^[a-z0-9-]+$/,
	description:"Match valid URL slugs",
	tags:"URL"
},
{
	name:"Email",
	regex:/^.+@.+$/,
	description:"Verify that there is an @ symbol with something before it",
	tags:"email,validation"
},
{
	name:"SRC of image tag",
	regex:/^<\s*img[^>]+src\s*=\s*(["'])(.*?)\1[^>]*>$/,
	description:"Match the src attribute of an HTML image tag",
	tags:"html,tag,image"
},
{
	name:"URL",
	regex:/^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
	description:"Match URL with optional protocol",
	tags:"url,address,http"
},
{
	name:"IPv4 Address",
	regex:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
	description:"Match IP v4 addresses",
	tags:"tcpip,internet,address"
},
{
	name:"IPv6 Address",
	regex:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
	description: "Match IP v6 addresses",
	tags:"tcpip,internet,address"
},
{
	name:"Positive Integer",
	regex:/^\d+$/,
	descriptions:"Match whole numbers above zero",
	tags:"number"
},
{
	name:"Negative Integer",
	regex:/^-\d+$/,
	description:"Match whole numbers below zero",
	tags:"number"
},
{
	name:"Integer",
	regex:/^-?\d+$/,
	description:"Match whole numbers, above or below zero",
	tags:"number"
},
{
	name:"Positive number",
	regex:/^\d*\.?\d+$/,
	description:"Match integers or floats that are positive",
	tags:"float"
},
{
	name:"Negative number",
	regex:/^-\d*\.?\d+$/,
	description:"Match integers or floats that are negative",
	tags:"float"
},
{
	name:"Positive or negative number",
	regex:/^-?\d*\.?\d+$/,
	description:"Match integers or floats that are positive or negative",
	tags:"float"
},
{
	name:"Phone number",
	regex:/^\+?(\d.*){3,}$/,
	description:"Match phone numbers at least 3 digits long",
	tags:"validation"
},
{
	name:"New line",
	regex:/[\r\n]|$/,
	description:"Match new lines within text",
	tags:"text"
},
{
	name:"ID of Youtube video",
	regex:/https?:\/\/(?:youtu\.be\/|(?:[a-z]{2,3}\.)?youtube\.com\/watch(?:\?|#\!)v=)([\w-]{11}).*/gi,
	description:"Match the ID of a youtube video URL",
	tags:"video,youtube,url"
},
{
	name:"ID of Youtube Channel",
	regex:/https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/i,
	description:"Match the ID of a youtube channel URL",
	tags:"channel,youtube,url"
},
{
	name:"CSS comment",
	regex:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//,
	description:"Match standard CSS comments",
	tags:"css,comment,code"
},
{
	name:"Wordpress shortcodes",
	regex:/^\[([a-z-_0-9]+)([^\[]+)*(?:\](.*)\[\/\1\]|\s+\/\])$/,
	description:"Matches opening and closing shortcode tags with content in-between them.",
	tags:"wordpress,shortcodes,markup"
},
{
	name:"U.S./Canadian ZIP/Postal Code",
	regex:/(^\d{5}(-\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$)/,
	description:"Matches US ZIP, ZIP+4, and Canadian Postal Codes",
	tags:"address, postal, zip"
},
{
	name:"Brazilian ZIP/Postal Code",
	regex:/^[0-9]{5}-[0-9]{3}$/,
	description:"Matches BR ZIP/Postal Code",
	tags:"address, postal, zip"
},
{
	name:"Morse Code",
	regex:/^[.-]{1,5}(?:[ \t]+[.-]{1,5})*(?:[ \t]+[.-]{1,5}(?:[ \t]+[.-]{1,5})*)*$/,
	description:"Matches valid Morse Code",
	tags:"morse, code"
},
{
	name:"Image shortcode",
	regex:/\[img\](.*?)\[\/img\]/,
	description:"Matches the content in between [img][/img]. Useful for making dynamic WYSIWYG editors",
	tags:"img, shortcode, wysiwyg"
},
{
	name:"Brainfuck Code",
	regex:/^[+-<>.,\[\] \t\n\r]+$/,
	description:"Matches valid code for a brainfuck program.",
	tags:"brainfuck, code"
}
];
<commit_msg>Include optional time.<commit_after>export const patterns = [{
	name:"Date in format dd/mm/yyyy",
	regex:/^(0?[1-9]|[12][0-9]|3[01])([ /\-])(0?[1-9]|1[012])\2([0-9][0-9][0-9][0-9])(([ -])([0-1]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5]?[0-9])?$/,
	description:"Will match dates with dashes, slashes or with spaces (e.g. dd-mm-yyyy dd/mm/yyyy dd mm yyyy), and optional time separated by a space or a dash (e.g. dd-mm-yyyy-hh:mm:ss or dd/mm/yyyy hh:mm:ss).",
	tags:"date,time"
},
{
	name:"Time in 24-hour format",
	regex:/([01]?[0-9]|2[0-3]):[0-5][0-9]/,
	description: "Match times in 24 hour format",
	tags:"date,time"
},
{
	name: "Date and time in ISO-8601 format",
	regex: /^(?![+-]?\d{4,5}-?(?:\d{2}|W\d{2})T)(?:|(\d{4}|[+-]\d{5})-?(?:|(0\d|1[0-2])(?:|-?([0-2]\d|3[0-1]))|([0-2]\d{2}|3[0-5]\d|36[0-6])|W([0-4]\d|5[0-3])(?:|-?([1-7])))(?:(?!\d)|T(?=\d)))(?:|([01]\d|2[0-4])(?:|:?([0-5]\d)(?:|:?([0-5]\d)(?:|\.(\d{3})))(?:|[zZ]|([+-](?:[01]\d|2[0-4]))(?:|:?([0-5]\d)))))$/,
	description: "Will match a valid date and times in the ISO-8601 format, excludes durations.",
	tags: "date,time"
},
{
	name:"HTML tags",
	regex:/^<([a-z1-6]+)([^<]+)*(?:>(.*)<\/\1>| *\/>)$/,
	description:"Match opening and closing HTML tags with content between",
	tags:"markup,xml,html"
},
{
	name:"Username",
	regex:/^[a-zA-Z0-9_-]{3,16}$/,
	description:"A string between 3 and 16 characters, allowing alphanumeric characters and hyphens and underscores",
	tags:"username,validation"
},
{
	name:"Hex Color Value",
	regex:/^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$/,
	description:"RGB hex colors",
	tags:"hex,color"
},
{
	name:"URL Slug",
	regex:/^[a-z0-9-]+$/,
	description:"Match valid URL slugs",
	tags:"URL"
},
{
	name:"Email",
	regex:/^.+@.+$/,
	description:"Verify that there is an @ symbol with something before it",
	tags:"email,validation"
},
{
	name:"SRC of image tag",
	regex:/^<\s*img[^>]+src\s*=\s*(["'])(.*?)\1[^>]*>$/,
	description:"Match the src attribute of an HTML image tag",
	tags:"html,tag,image"
},
{
	name:"URL",
	regex:/^((https?|ftp|file):\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/,
	description:"Match URL with optional protocol",
	tags:"url,address,http"
},
{
	name:"IPv4 Address",
	regex:/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,
	description:"Match IP v4 addresses",
	tags:"tcpip,internet,address"
},
{
	name:"IPv6 Address",
	regex:/^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/,
	description: "Match IP v6 addresses",
	tags:"tcpip,internet,address"
},
{
	name:"Positive Integer",
	regex:/^\d+$/,
	descriptions:"Match whole numbers above zero",
	tags:"number"
},
{
	name:"Negative Integer",
	regex:/^-\d+$/,
	description:"Match whole numbers below zero",
	tags:"number"
},
{
	name:"Integer",
	regex:/^-?\d+$/,
	description:"Match whole numbers, above or below zero",
	tags:"number"
},
{
	name:"Positive number",
	regex:/^\d*\.?\d+$/,
	description:"Match integers or floats that are positive",
	tags:"float"
},
{
	name:"Negative number",
	regex:/^-\d*\.?\d+$/,
	description:"Match integers or floats that are negative",
	tags:"float"
},
{
	name:"Positive or negative number",
	regex:/^-?\d*\.?\d+$/,
	description:"Match integers or floats that are positive or negative",
	tags:"float"
},
{
	name:"Phone number",
	regex:/^\+?(\d.*){3,}$/,
	description:"Match phone numbers at least 3 digits long",
	tags:"validation"
},
{
	name:"New line",
	regex:/[\r\n]|$/,
	description:"Match new lines within text",
	tags:"text"
},
{
	name:"ID of Youtube video",
	regex:/https?:\/\/(?:youtu\.be\/|(?:[a-z]{2,3}\.)?youtube\.com\/watch(?:\?|#\!)v=)([\w-]{11}).*/gi,
	description:"Match the ID of a youtube video URL",
	tags:"video,youtube,url"
},
{
	name:"ID of Youtube Channel",
	regex:/https?:\/\/(www\.)?youtube.com\/channel\/UC([-_a-z0-9]{22})/i,
	description:"Match the ID of a youtube channel URL",
	tags:"channel,youtube,url"
},
{
	name:"CSS comment",
	regex:/\/\*[^*]*\*+([^/*][^*]*\*+)*\//,
	description:"Match standard CSS comments",
	tags:"css,comment,code"
},
{
	name:"Wordpress shortcodes",
	regex:/^\[([a-z-_0-9]+)([^\[]+)*(?:\](.*)\[\/\1\]|\s+\/\])$/,
	description:"Matches opening and closing shortcode tags with content in-between them.",
	tags:"wordpress,shortcodes,markup"
},
{
	name:"U.S./Canadian ZIP/Postal Code",
	regex:/(^\d{5}(-\d{4})?$)|(^[ABCEGHJKLMNPRSTVXY]{1}\d{1}[A-Z]{1} *\d{1}[A-Z]{1}\d{1}$)/,
	description:"Matches US ZIP, ZIP+4, and Canadian Postal Codes",
	tags:"address, postal, zip"
},
{
	name:"Brazilian ZIP/Postal Code",
	regex:/^[0-9]{5}-[0-9]{3}$/,
	description:"Matches BR ZIP/Postal Code",
	tags:"address, postal, zip"
},
{
	name:"Morse Code",
	regex:/^[.-]{1,5}(?:[ \t]+[.-]{1,5})*(?:[ \t]+[.-]{1,5}(?:[ \t]+[.-]{1,5})*)*$/,
	description:"Matches valid Morse Code",
	tags:"morse, code"
},
{
	name:"Image shortcode",
	regex:/\[img\](.*?)\[\/img\]/,
	description:"Matches the content in between [img][/img]. Useful for making dynamic WYSIWYG editors",
	tags:"img, shortcode, wysiwyg"
},
{
	name:"Brainfuck Code",
	regex:/^[+-<>.,\[\] \t\n\r]+$/,
	description:"Matches valid code for a brainfuck program.",
	tags:"brainfuck, code"
}
];
<|endoftext|>


<commit_before>shared_behaviors_for 'shared setup'

  before
    // REFACT: use JSpec.context = { foo : 'bar' } for this
    // helpers to simplify writing the tests
    this.enableEditor = function(options) {
      // need either callback or url or the inline editor will throw
      if ( ! options || ! ('callback' in options))
        options = $.extend({url:'nonexistant_url'}, options);
      return this.sandbox.editInPlace(options);
    }

    this.openEditor = function(options) {
      return this.enableEditor(options).click().find(':input');
    }

    this.edit = function(options, value) {
      value = (undefined !== value)
        ? value
        : 'text that is different to what was entered before';
      this.openEditor(options).val(value).submit();
      return this.sandbox;
    }

  end

  before_each
    // If this is missing each request will grab the current page (no url specified)
    // and on inserting it, that will whipe the test results. (I don't quite understand why)
    mock_request().and_return('fnord')

    // REFACT: use JSpec.context = { foo : 'bar' } for this
    this.sandbox = $('
Some text
')
    // Workaround to jquery-ui 1.7.3 bug that it can't reliably deal with document fragments not having a color at their root element
    this.sandbox.parent().css({ backgroundColor:'transparent' })
  end

end<commit_msg>comments<commit_after>shared_behaviors_for 'shared setup'

  before
    // REFACT: consider to use JSpec.context = { foo : 'bar' } for this
    // helpers to simplify writing the tests
    this.enableEditor = function(options) {
      // need either callback or url or the inline editor will throw
      if ( ! options || ! ('callback' in options))
        options = $.extend({url:'nonexistant_url'}, options);
      return this.sandbox.editInPlace(options);
    }

    this.openEditor = function(options) {
      return this.enableEditor(options).click().find(':input');
    }

    this.edit = function(options, value) {
      value = (undefined !== value)
        ? value
        : 'text that is different to what was entered before';
      this.openEditor(options).val(value).submit();
      return this.sandbox;
    }

  end

  before_each
    // If this is missing each request will grab the current page (no url specified)
    // and on inserting it, that will whipe the test results. (I don't quite understand why)
    mock_request().and_return('fnord')

    // REFACT: consider to use JSpec.context = { foo : 'bar' } for this
    this.sandbox = $('
Some text
')
    // Workaround to jquery-ui 1.7.3 bug that it can't reliably deal with document fragments not having a color at their root element
    this.sandbox.parent().css({ backgroundColor:'transparent' })
  end

end<|endoftext|>