<commit_before>/*jshint camelcase:false */
/**
 * @fileOverview The bare CRUD command implementations
 *
 */
var EventEmitter = require('events').EventEmitter;

var cip = require('cip');
var __  = require('lodash');
var appErr = require('nodeon-error');
var middlewarify = require('middlewarify');

var Create = require('./crud-ops/create');
var Read = require('./crud-ops/read');
var Update = require('./crud-ops/update');
var Delete = require('./crud-ops/delete');
var Pagination = require('./crud-ops/pagination');
var Handlers = require('./handlers');

// sign the error objects
appErr.setName('crude');

var CeventEmitter = cip.cast(EventEmitter);

/**
 * The CRUD Commands
 *
 * @param {string} baseUrl The baseUrl to use.
 * @param {Object} controller The controller.
 * @param {Express=} optExpress An express instance.
 * @contructor
 * @extends {EventEmitter}
 */
var Crude = module.exports = CeventEmitter.extend(function(baseUrl, controller,
  optExpress) {
  if (typeof baseUrl !== 'string') {
    throw new TypeError('First argument should be a string represeting the baseUrl');
  }

  /** @type {?Express} The Express instance. */
  this.express = optExpress || null;

  // will throw proper error
  this._validateController(controller);

  /** @type {Object} Options, look at setOptions() */
  this.opts = {};

  /** @type {string} The route to use. */
  this.baseUrl = baseUrl;

  /**
   * @type {Object} Object with the expected key / callbacks.
   *       @param {Function(Object)} create
   *       @param {Function(Object)} read
   *       @param {Function(Object)} readLimit
   *       @param {Function(Object)} readOne
   *       @param {Function(Object)} update
   *       @param {Function(Object)} count
   */
  this.controller = controller;

  // define CRUD middleware
  this.create = [];
  this.readOne = [];
  this.readList = [];
  this.update = [];
  this.delete = [];

  // define CRUD Query middleware
  function noopReturn(query) {return query;}
  middlewarify.make(this, 'createQuery', noopReturn);
  middlewarify.make(this, 'readOneQuery', noopReturn);
  middlewarify.make(this, 'readListQuery', noopReturn);
  middlewarify.make(this, 'updateQuery', noopReturn);
  middlewarify.make(this, 'deleteQuery', noopReturn);

  function use(middleware) {
    this.push(middleware);
  }
  this.create.use = use;
  this.readOne.use = use;
  this.readList.use = use;
  this.update.use = use;
  this.delete.use = use;

  this._setDefaults();

  if (this.express) {
    this.addRoutes(this.express);
  }

});
Crude.mixin(Create);
Crude.mixin(Read);
Crude.mixin(Update);
Crude.mixin(Delete);
Crude.mixin(Pagination);
Crude.mixin(Handlers);

/**
 * Mark the proto to check for proper inheritance of methods,
 * we do this so we can lax comparison by not relying 100% on "instanceof".
 *
 * @type {boolean}
 * @protected
 */
Crude.prototype._isCrude = true;

/**
 * Configure crude.
 *
 * @param {Object} options Key value pairs.
 * @return {self} Chainable.
 */
Crude.prototype.config = function(options) {
  __.extend(this.opts, options);
  return this;
};

/**
 * Set Default options.
 *
 * @return {self} Chainable.
 */
Crude.prototype._setDefaults = function() {
  // Map the ID visible in URLs to a DB attribute.
  this.opts.idField = 'id';
  // Define this key so 'from' & 'to' params query the right attribute
  this.opts.dateField = 'createdAt';
  // Set to false to not paginate.
  this.opts.pagination = true;
  // Callback to set the pagination query.
  this.opts.paginateQuery = null;
  // Default item to limit to on pagination.
  this.opts.paginateLimit = 6;

  this.opts.onSuccess = this.handleSuccess.bind(this);
  this.opts.onError = this.handleError.bind(this);
};

/**
 * Insert at the beginning of all routes the provided middleware.
 *
 * @param {Function} middleware The middleware to insert.
 * @return {self} Chainable.
 */
Crude.prototype.use = function(middleware) {
  this.create.use(middleware);
  this.readOne.use(middleware);
  this.readList.use(middleware);
  this.update.use(middleware);
  this.delete.use(middleware);

  return this;
};

/**
 * Applies query middleware to all CRUD OPs.
 *
 * @param {Function} middleware The middleware to insert.
 * @return {self} Chainable.
 */
Crude.prototype.useQuery = function(middleware) {
  this.readOneQuery.use(middleware);
  this.readListQuery.use(middleware);
  this.updateQuery.use(middleware);
  this.deleteQuery.use(middleware);

  return this;
};

/**
 * The actual route add operation, decoupled from add to skip instanceof checks.
 *
 * @param {Express} express The express instance.
 * @param {string} baseUrl The base URL to attach the routes.
 * @param {crude.Crude} crude an instance if crude.
 * @return {self} Chainable.
 */
Crude.prototype.addRoutes = function(express) {
  express.get(this.baseUrl, [this.readList, this._readList.bind(this)]);
  express.post(this.baseUrl, [this.create, this._create.bind(this)]);
  express.get(this.baseUrl + '/:id', [this.readOne, this._readOne.bind(this)]);
  express.put(this.baseUrl + '/:id', [this.update, this._update.bind(this)]);
  express.patch(this.baseUrl + '/:id', [this.update, this._update.bind(this)]);
  express.delete(this.baseUrl + '/:id', [this.delete, this._delete.bind(this)]);

  this.express = express;

  return this;
};

/**
 * Set the master callback for success on all CRUD ops.
 *
 * @param {Function} fn A callback.
 * @return {self} Chainable.
 */
Crude.prototype.onSuccess = function(fn) {
  this.opts.onSuccess = fn;
  return this;
};

/**
 * Set the master callback for errors on all CRUD ops.
 *
 * @param {Function} fn A callback.
 * @return {self} Chainable.
 */
Crude.prototype.onError = function(fn) {
  this.opts.onError = fn;
  return this;
};

/**
 * Validates a controller has the expected functions.
 *
 * @param {Object} ctrl The controller to validate.
 * @throws {TypeError} If not valid.
 * @protected
 */
Crude.prototype._validateController = function(ctrl) {
  if (!__.isObject(ctrl)) {
    throw new TypeError('Injected Controller not of type Object');
  }

  if (typeof ctrl.create !== 'function') {
    throw new TypeError('Injected Controller does not contain the "create" method');
  }
  if (typeof ctrl.read !== 'function') {
    throw new TypeError('Injected Controller does not contain the "read" method');
  }
  if (typeof ctrl.readLimit !== 'function') {
    throw new TypeError('Injected Controller does not contain the "readLimit" method');
  }
  if (typeof ctrl.readOne !== 'function') {
    throw new TypeError('Injected Controller does not contain the "readOne" method');
  }
  if (typeof ctrl.update !== 'function') {
    throw new TypeError('Injected Controller does not contain the "update" method');
  }
  if (typeof ctrl.count !== 'function') {
    throw new TypeError('Injected Controller does not contain the "count" method');
  }
};
<commit_msg>make plain middlewaring adding chainable<commit_after>/*jshint camelcase:false */
/**
 * @fileOverview The bare CRUD command implementations
 *
 */
var EventEmitter = require('events').EventEmitter;

var cip = require('cip');
var __  = require('lodash');
var appErr = require('nodeon-error');
var middlewarify = require('middlewarify');

var Create = require('./crud-ops/create');
var Read = require('./crud-ops/read');
var Update = require('./crud-ops/update');
var Delete = require('./crud-ops/delete');
var Pagination = require('./crud-ops/pagination');
var Handlers = require('./handlers');

// sign the error objects
appErr.setName('crude');

var CeventEmitter = cip.cast(EventEmitter);

/**
 * The CRUD Commands
 *
 * @param {string} baseUrl The baseUrl to use.
 * @param {Object} controller The controller.
 * @param {Express=} optExpress An express instance.
 * @contructor
 * @extends {EventEmitter}
 */
var Crude = module.exports = CeventEmitter.extend(function(baseUrl, controller,
  optExpress) {
  if (typeof baseUrl !== 'string') {
    throw new TypeError('First argument should be a string represeting the baseUrl');
  }

  /** @type {?Express} The Express instance. */
  this.express = optExpress || null;

  // will throw proper error
  this._validateController(controller);

  /** @type {Object} Options, look at setOptions() */
  this.opts = {};

  /** @type {string} The route to use. */
  this.baseUrl = baseUrl;

  /**
   * @type {Object} Object with the expected key / callbacks.
   *       @param {Function(Object)} create
   *       @param {Function(Object)} read
   *       @param {Function(Object)} readLimit
   *       @param {Function(Object)} readOne
   *       @param {Function(Object)} update
   *       @param {Function(Object)} count
   */
  this.controller = controller;

  // define CRUD middleware
  this.create = [];
  this.readOne = [];
  this.readList = [];
  this.update = [];
  this.delete = [];

  // define CRUD Query middleware
  function noopReturn(query) {return query;}
  middlewarify.make(this, 'createQuery', noopReturn);
  middlewarify.make(this, 'readOneQuery', noopReturn);
  middlewarify.make(this, 'readListQuery', noopReturn);
  middlewarify.make(this, 'updateQuery', noopReturn);
  middlewarify.make(this, 'deleteQuery', noopReturn);

  var self = this;
  function use(middleware) {
    this.push(middleware);
    return self;
  }
  this.create.use = use;
  this.readOne.use = use;
  this.readList.use = use;
  this.update.use = use;
  this.delete.use = use;

  this._setDefaults();

  if (this.express) {
    this.addRoutes(this.express);
  }

});
Crude.mixin(Create);
Crude.mixin(Read);
Crude.mixin(Update);
Crude.mixin(Delete);
Crude.mixin(Pagination);
Crude.mixin(Handlers);

/**
 * Mark the proto to check for proper inheritance of methods,
 * we do this so we can lax comparison by not relying 100% on "instanceof".
 *
 * @type {boolean}
 * @protected
 */
Crude.prototype._isCrude = true;

/**
 * Configure crude.
 *
 * @param {Object} options Key value pairs.
 * @return {self} Chainable.
 */
Crude.prototype.config = function(options) {
  __.extend(this.opts, options);
  return this;
};

/**
 * Set Default options.
 *
 * @return {self} Chainable.
 */
Crude.prototype._setDefaults = function() {
  // Map the ID visible in URLs to a DB attribute.
  this.opts.idField = 'id';
  // Define this key so 'from' & 'to' params query the right attribute
  this.opts.dateField = 'createdAt';
  // Set to false to not paginate.
  this.opts.pagination = true;
  // Callback to set the pagination query.
  this.opts.paginateQuery = null;
  // Default item to limit to on pagination.
  this.opts.paginateLimit = 6;

  this.opts.onSuccess = this.handleSuccess.bind(this);
  this.opts.onError = this.handleError.bind(this);
};

/**
 * Insert at the beginning of all routes the provided middleware.
 *
 * @param {Function} middleware The middleware to insert.
 * @return {self} Chainable.
 */
Crude.prototype.use = function(middleware) {
  this.create.use(middleware);
  this.readOne.use(middleware);
  this.readList.use(middleware);
  this.update.use(middleware);
  this.delete.use(middleware);

  return this;
};

/**
 * Applies query middleware to all CRUD OPs.
 *
 * @param {Function} middleware The middleware to insert.
 * @return {self} Chainable.
 */
Crude.prototype.useQuery = function(middleware) {
  this.readOneQuery.use(middleware);
  this.readListQuery.use(middleware);
  this.updateQuery.use(middleware);
  this.deleteQuery.use(middleware);

  return this;
};

/**
 * The actual route add operation, decoupled from add to skip instanceof checks.
 *
 * @param {Express} express The express instance.
 * @param {string} baseUrl The base URL to attach the routes.
 * @param {crude.Crude} crude an instance if crude.
 * @return {self} Chainable.
 */
Crude.prototype.addRoutes = function(express) {
  express.get(this.baseUrl, [this.readList, this._readList.bind(this)]);
  express.post(this.baseUrl, [this.create, this._create.bind(this)]);
  express.get(this.baseUrl + '/:id', [this.readOne, this._readOne.bind(this)]);
  express.put(this.baseUrl + '/:id', [this.update, this._update.bind(this)]);
  express.patch(this.baseUrl + '/:id', [this.update, this._update.bind(this)]);
  express.delete(this.baseUrl + '/:id', [this.delete, this._delete.bind(this)]);

  this.express = express;

  return this;
};

/**
 * Set the master callback for success on all CRUD ops.
 *
 * @param {Function} fn A callback.
 * @return {self} Chainable.
 */
Crude.prototype.onSuccess = function(fn) {
  this.opts.onSuccess = fn;
  return this;
};

/**
 * Set the master callback for errors on all CRUD ops.
 *
 * @param {Function} fn A callback.
 * @return {self} Chainable.
 */
Crude.prototype.onError = function(fn) {
  this.opts.onError = fn;
  return this;
};

/**
 * Validates a controller has the expected functions.
 *
 * @param {Object} ctrl The controller to validate.
 * @throws {TypeError} If not valid.
 * @protected
 */
Crude.prototype._validateController = function(ctrl) {
  if (!__.isObject(ctrl)) {
    throw new TypeError('Injected Controller not of type Object');
  }

  if (typeof ctrl.create !== 'function') {
    throw new TypeError('Injected Controller does not contain the "create" method');
  }
  if (typeof ctrl.read !== 'function') {
    throw new TypeError('Injected Controller does not contain the "read" method');
  }
  if (typeof ctrl.readLimit !== 'function') {
    throw new TypeError('Injected Controller does not contain the "readLimit" method');
  }
  if (typeof ctrl.readOne !== 'function') {
    throw new TypeError('Injected Controller does not contain the "readOne" method');
  }
  if (typeof ctrl.update !== 'function') {
    throw new TypeError('Injected Controller does not contain the "update" method');
  }
  if (typeof ctrl.count !== 'function') {
    throw new TypeError('Injected Controller does not contain the "count" method');
  }
};
<|endoftext|>


<commit_before>import { between } from '../../utils/format'
import { QBtn } from '../btn'
import { QInput } from '../input'
import extend from '../../utils/extend'
import { getEventKey } from '../../utils/event'

export default {
  name: 'q-pagination',
  props: {
    value: {
      type: Number,
      required: true
    },
    min: {
      type: Number,
      default: 1
    },
    max: {
      type: Number,
      required: true
    },
    color: {
      type: String,
      default: 'primary'
    },
    textColor: String,
    size: String,
    disable: Boolean,
    input: Boolean,
    boundaryLinks: {
      type: Boolean,
      default: null
    },
    boundaryNumbers: {
      type: Boolean,
      default: null
    },
    directionLinks: {
      type: Boolean,
      default: null
    },
    ellipses: {
      type: Boolean,
      default: null
    },
    maxPages: {
      type: Number,
      default: 0,
      validator: v => {
        if (v < 0) {
          console.error('maxPages should not be negative')
          return false
        }
        return true
      }
    }
  },
  data () {
    return {
      newPage: null
    }
  },
  watch: {
    min (value) {
      this.model = this.value
    },
    max (value) {
      this.model = this.value
    }
  },
  computed: {
    model: {
      get () {
        return this.value
      },
      set (val) {
        if (this.disable || !val || isNaN(val)) {
          return
        }
        const value = between(parseInt(val, 10), this.min, this.max)
        this.$emit('input', value)
        this.$nextTick(() => {
          if (JSON.stringify(value) !== JSON.stringify(this.value)) {
            this.$emit('change', value)
          }
        })
      }
    },
    inputPlaceholder () {
      return this.model + ' / ' + this.max
    },
    __boundaryLinks () {
      return this.__getBool(this.boundaryLinks, this.input)
    },
    __boundaryNumbers () {
      return this.__getBool(this.boundaryNumbers, !this.input)
    },
    __directionLinks () {
      return this.__getBool(this.directionLinks, this.input)
    },
    __ellipses () {
      return this.__getBool(this.ellipses, !this.input)
    }
  },
  methods: {
    set (value) {
      this.model = value
    },
    setByOffset (offset) {
      this.model = this.model + offset
    },
    __update () {
      this.model = this.newPage
      this.newPage = null
    },
    __repeatTimeout (count) {
      return Math.max(100, 300 - count * count * 10)
    },
    __getBool (val, otherwise) {
      return [true, false].includes(val)
        ? val
        : otherwise
    },
    __getBtn (h, props) {
      return h(QBtn, extend(true, {
        props: {
          color: this.color,
          flat: true,
          size: this.size
        }
      }, props))
    }
  },
  render (h) {
    const
      contentStart = [],
      contentEnd = [],
      contentMiddle = []

    if (this.__boundaryLinks) {
      contentStart.push(this.__getBtn(h, {
        key: 'bls',
        props: {
          disable: this.disable || this.value <= this.min,
          icon: this.$q.icon.pagination.first
        },
        on: {
          click: () => this.set(this.min)
        }
      }))
      contentEnd.unshift(this.__getBtn(h, {
        key: 'ble',
        props: {
          disable: this.disable || this.value >= this.max,
          icon: this.$q.icon.pagination.last
        },
        on: {
          click: () => this.set(this.max)
        }
      }))
    }

    if (this.__directionLinks) {
      contentStart.push(this.__getBtn(h, {
        key: 'bdp',
        props: {
          disable: this.disable || this.value <= this.min,
          icon: this.$q.icon.pagination.prev,
          repeatTimeout: this.__repeatTimeout
        },
        on: {
          click: () => this.setByOffset(-1)
        }
      }))
      contentEnd.unshift(this.__getBtn(h, {
        key: 'bdn',
        props: {
          disable: this.disable || this.value >= this.max,
          icon: this.$q.icon.pagination.next,
          repeatTimeout: this.__repeatTimeout
        },
        on: {
          click: () => this.setByOffset(1)
        }
      }))
    }

    if (this.input) {
      contentMiddle.push(h(QInput, {
        staticClass: 'inline no-padding',
        style: {
          width: `${this.inputPlaceholder.length}rem`
        },
        props: {
          type: 'number',
          value: this.newPage,
          noNumberToggle: true,
          min: this.min,
          max: this.max,
          color: this.color,
          placeholder: this.inputPlaceholder,
          disable: this.disable,
          hideUnderline: true
        },
        on: {
          input: value => (this.newPage = value),
          keydown: event => (getEventKey(event) === 13 && this.__update()),
          blur: () => this.__update()
        }
      }))
    }
    else { // is type select
      let
        maxPages = Math.max(
          this.maxPages,
          1 + (this.__ellipses ? 2 : 0) + (this.__boundaryNumbers ? 2 : 0)
        ),
        pgFrom = this.min,
        pgTo = this.max,
        ellipsesStart = false,
        ellipsesEnd = false,
        boundaryStart = false,
        boundaryEnd = false

      if (this.maxPages && maxPages < (this.max - this.min + 1)) {
        maxPages = 1 + Math.floor(maxPages / 2) * 2
        pgFrom = Math.max(this.min, Math.min(this.max - maxPages + 1, this.value - Math.floor(maxPages / 2)))
        pgTo = Math.min(this.max, pgFrom + maxPages - 1)
        if (this.__boundaryNumbers) {
          boundaryStart = true
          pgFrom += 1
        }
        if (this.__ellipses && pgFrom > (this.min + (this.__boundaryNumbers ? 1 : 0))) {
          ellipsesStart = true
          pgFrom += 1
        }
        if (this.__boundaryNumbers) {
          boundaryEnd = true
          pgTo -= 1
        }
        if (this.__ellipses && pgTo < (this.max - (this.__boundaryNumbers ? 1 : 0))) {
          ellipsesEnd = true
          pgTo -= 1
        }
      }
      const style = {
        minWidth: `${Math.max(1.5, String(this.max).length)}em`
      }
      if (boundaryStart) {
        const active = this.min === this.value
        contentStart.push(this.__getBtn(h, {
          key: 'bns',
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: this.min,
            noRipple: true
          },
          on: {
            click: () => this.set(this.min)
          }
        }))
      }
      if (boundaryEnd) {
        const active = this.max === this.value
        contentEnd.unshift(this.__getBtn(h, {
          key: 'bne',
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: this.max,
            noRipple: true
          },
          on: {
            click: () => this.set(this.max)
          }
        }))
      }
      if (ellipsesStart) {
        contentStart.push(this.__getBtn(h, {
          key: 'bes',
          style,
          props: {
            disable: this.disable,
            label: '…',
            repeatTimeout: this.__repeatTimeout
          },
          on: {
            click: () => this.set(pgFrom - 1)
          }
        }))
      }
      if (ellipsesEnd) {
        contentEnd.unshift(this.__getBtn(h, {
          key: 'bee',
          style,
          props: {
            disable: this.disable,
            label: '…',
            repeatTimeout: this.__repeatTimeout
          },
          on: {
            click: () => this.set(pgTo + 1)
          }
        }))
      }
      for (let i = pgFrom; i <= pgTo; i++) {
        const active = i === this.value
        contentMiddle.push(this.__getBtn(h, {
          key: `${i}.${active}`,
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: i,
            noRipple: true
          },
          on: {
            click: () => this.set(i)
          }
        }))
      }
    }

    return h('div', {
      staticClass: 'q-pagination row no-wrap items-center',
      'class': { disabled: this.disable }
    }, [
      contentStart,

      h('div', { staticClass: 'row justify-center' }, [
        contentMiddle
      ]),

      contentEnd
    ])
  }
}
<commit_msg>feat(QPagination): RTL support<commit_after>import { between } from '../../utils/format'
import { QBtn } from '../btn'
import { QInput } from '../input'
import extend from '../../utils/extend'
import { getEventKey } from '../../utils/event'

export default {
  name: 'q-pagination',
  props: {
    value: {
      type: Number,
      required: true
    },
    min: {
      type: Number,
      default: 1
    },
    max: {
      type: Number,
      required: true
    },
    color: {
      type: String,
      default: 'primary'
    },
    textColor: String,
    size: String,
    disable: Boolean,
    input: Boolean,
    boundaryLinks: {
      type: Boolean,
      default: null
    },
    boundaryNumbers: {
      type: Boolean,
      default: null
    },
    directionLinks: {
      type: Boolean,
      default: null
    },
    ellipses: {
      type: Boolean,
      default: null
    },
    maxPages: {
      type: Number,
      default: 0,
      validator: v => {
        if (v < 0) {
          console.error('maxPages should not be negative')
          return false
        }
        return true
      }
    }
  },
  data () {
    return {
      newPage: null
    }
  },
  watch: {
    min (value) {
      this.model = this.value
    },
    max (value) {
      this.model = this.value
    }
  },
  computed: {
    model: {
      get () {
        return this.value
      },
      set (val) {
        if (this.disable || !val || isNaN(val)) {
          return
        }
        const value = between(parseInt(val, 10), this.min, this.max)
        this.$emit('input', value)
        this.$nextTick(() => {
          if (JSON.stringify(value) !== JSON.stringify(this.value)) {
            this.$emit('change', value)
          }
        })
      }
    },
    inputPlaceholder () {
      return this.model + ' / ' + this.max
    },
    __boundaryLinks () {
      return this.__getBool(this.boundaryLinks, this.input)
    },
    __boundaryNumbers () {
      return this.__getBool(this.boundaryNumbers, !this.input)
    },
    __directionLinks () {
      return this.__getBool(this.directionLinks, this.input)
    },
    __ellipses () {
      return this.__getBool(this.ellipses, !this.input)
    },
    icons () {
      const ico = [
        this.$q.icon.pagination.first,
        this.$q.icon.pagination.prev,
        this.$q.icon.pagination.next,
        this.$q.icon.pagination.last
      ]
      return this.$q.i18n.rtl ? ico.reverse() : ico
    }
  },
  methods: {
    set (value) {
      this.model = value
    },
    setByOffset (offset) {
      this.model = this.model + offset
    },
    __update () {
      this.model = this.newPage
      this.newPage = null
    },
    __repeatTimeout (count) {
      return Math.max(100, 300 - count * count * 10)
    },
    __getBool (val, otherwise) {
      return [true, false].includes(val)
        ? val
        : otherwise
    },
    __getBtn (h, props) {
      return h(QBtn, extend(true, {
        props: {
          color: this.color,
          flat: true,
          size: this.size
        }
      }, props))
    }
  },
  render (h) {
    const
      contentStart = [],
      contentEnd = [],
      contentMiddle = []

    if (this.__boundaryLinks) {
      contentStart.push(this.__getBtn(h, {
        key: 'bls',
        props: {
          disable: this.disable || this.value <= this.min,
          icon: this.icons[0]
        },
        on: {
          click: () => this.set(this.min)
        }
      }))
      contentEnd.unshift(this.__getBtn(h, {
        key: 'ble',
        props: {
          disable: this.disable || this.value >= this.max,
          icon: this.icons[3]
        },
        on: {
          click: () => this.set(this.max)
        }
      }))
    }

    if (this.__directionLinks) {
      contentStart.push(this.__getBtn(h, {
        key: 'bdp',
        props: {
          disable: this.disable || this.value <= this.min,
          icon: this.icons[1],
          repeatTimeout: this.__repeatTimeout
        },
        on: {
          click: () => this.setByOffset(-1)
        }
      }))
      contentEnd.unshift(this.__getBtn(h, {
        key: 'bdn',
        props: {
          disable: this.disable || this.value >= this.max,
          icon: this.icons[2],
          repeatTimeout: this.__repeatTimeout
        },
        on: {
          click: () => this.setByOffset(1)
        }
      }))
    }

    if (this.input) {
      contentMiddle.push(h(QInput, {
        staticClass: 'inline no-padding',
        style: {
          width: `${this.inputPlaceholder.length}rem`
        },
        props: {
          type: 'number',
          value: this.newPage,
          noNumberToggle: true,
          min: this.min,
          max: this.max,
          color: this.color,
          placeholder: this.inputPlaceholder,
          disable: this.disable,
          hideUnderline: true
        },
        on: {
          input: value => (this.newPage = value),
          keydown: event => (getEventKey(event) === 13 && this.__update()),
          blur: () => this.__update()
        }
      }))
    }
    else { // is type select
      let
        maxPages = Math.max(
          this.maxPages,
          1 + (this.__ellipses ? 2 : 0) + (this.__boundaryNumbers ? 2 : 0)
        ),
        pgFrom = this.min,
        pgTo = this.max,
        ellipsesStart = false,
        ellipsesEnd = false,
        boundaryStart = false,
        boundaryEnd = false

      if (this.maxPages && maxPages < (this.max - this.min + 1)) {
        maxPages = 1 + Math.floor(maxPages / 2) * 2
        pgFrom = Math.max(this.min, Math.min(this.max - maxPages + 1, this.value - Math.floor(maxPages / 2)))
        pgTo = Math.min(this.max, pgFrom + maxPages - 1)
        if (this.__boundaryNumbers) {
          boundaryStart = true
          pgFrom += 1
        }
        if (this.__ellipses && pgFrom > (this.min + (this.__boundaryNumbers ? 1 : 0))) {
          ellipsesStart = true
          pgFrom += 1
        }
        if (this.__boundaryNumbers) {
          boundaryEnd = true
          pgTo -= 1
        }
        if (this.__ellipses && pgTo < (this.max - (this.__boundaryNumbers ? 1 : 0))) {
          ellipsesEnd = true
          pgTo -= 1
        }
      }
      const style = {
        minWidth: `${Math.max(1.5, String(this.max).length)}em`
      }
      if (boundaryStart) {
        const active = this.min === this.value
        contentStart.push(this.__getBtn(h, {
          key: 'bns',
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: this.min,
            noRipple: true
          },
          on: {
            click: () => this.set(this.min)
          }
        }))
      }
      if (boundaryEnd) {
        const active = this.max === this.value
        contentEnd.unshift(this.__getBtn(h, {
          key: 'bne',
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: this.max,
            noRipple: true
          },
          on: {
            click: () => this.set(this.max)
          }
        }))
      }
      if (ellipsesStart) {
        contentStart.push(this.__getBtn(h, {
          key: 'bes',
          style,
          props: {
            disable: this.disable,
            label: '…',
            repeatTimeout: this.__repeatTimeout
          },
          on: {
            click: () => this.set(pgFrom - 1)
          }
        }))
      }
      if (ellipsesEnd) {
        contentEnd.unshift(this.__getBtn(h, {
          key: 'bee',
          style,
          props: {
            disable: this.disable,
            label: '…',
            repeatTimeout: this.__repeatTimeout
          },
          on: {
            click: () => this.set(pgTo + 1)
          }
        }))
      }
      for (let i = pgFrom; i <= pgTo; i++) {
        const active = i === this.value
        contentMiddle.push(this.__getBtn(h, {
          key: `${i}.${active}`,
          style,
          props: {
            disable: this.disable,
            flat: !active,
            textColor: active ? this.textColor : null,
            label: i,
            noRipple: true
          },
          on: {
            click: () => this.set(i)
          }
        }))
      }
    }

    return h('div', {
      staticClass: 'q-pagination row no-wrap items-center',
      'class': { disabled: this.disable }
    }, [
      contentStart,

      h('div', { staticClass: 'row justify-center' }, [
        contentMiddle
      ]),

      contentEnd
    ])
  }
}
<|endoftext|>


<commit_before>// import { test } from '~util/testing'
// import VStepper from '~components/steppers/VStepper'
// import VStepperContent from '~components/steppers/VStepperContent'

// test('VStepper.js', ({ mount }) => {
//   it('should handle dynamic steps', () => {
//     const wrapper = mount(VStepper)
//   })
// })
<commit_msg>removed spec<commit_after><|endoftext|>


<commit_before>/*:
 * RS_YoutubePlayer.js
 * @plugindesc This plugin allows you to start playback of the YouTube video.
 * @author biud436
 * @help
 * In general, In Android Chrome and Mobile Safari, It doesn't automatically
 * start playback. and you can stop the video by clicking around the YouTube video.
 * This plugin will automatically end the video playback if you watches
 * the video until the end.
 * =============================================================================
 * Plugin Command
 * =============================================================================
 * - This plugin command will start playback of the YouTube video. Please enter
 * the URL as follows. If the Internet had not connected, It could have caused
 * the error.
 *
 * YTPlayer play https://www.youtube.com/watch?v=C4ze-KCSxQY
 *
 * - Stop playback of the YouTube Video.
 *
 * YTPlayer stop
 *
 * =============================================================================
 * Change Log
 * =============================================================================
 * 2016.05.08 (v1.0.0) - First Release
 * 2016.05.09 (v1.0.1) - Added Error Handler
 * 2016.05.12 (v1.0.2) - Fixed a function that parses a URL .
 */

var Imported = Imported || {};
Imported.RS_YoutubePlayer = true;

var player;

function YTPlayer() {
  throw new Error("This is a static class");
}

//----------------------------------------------------------------------------
// Youtube Event Handler
//
//
function onYouTubeIframeAPIReady() {
  player = new YT.Player('ytplayer-iframe', {
    height: '560',
    width: '315',
    videoId: 'BIbpYySZ-2Q',
    fs: 1,
    autoplay: 1,
    enablejsapi: 1,
    rel: 1,
    showinfo: 1,
    playsinline: 0,
    events: {
      'onReady': onPlayerReady,
      'onStateChange': onPlayerStateChange,
      'onError': onPlayerError
    }
  });
}

function onPlayerReady(event) {
  var target = event.target;
  target.playVideo();
}

function onPlayerError(event) {
  var errorLog = "";
  switch (event.data) {
    case 2:
      // 요청에 잘못된 매개변수 값이 포함되어 있습니다. 예를 들어 11자리가 아닌 동영상 ID를 지정하거나
      // 동영상 ID에 느낌표 또는 별표와 같은 잘못된 문자가 포함된 경우에 이 오류가 발생합니다.
      errorLog += "Error Code : 2" + '\r\n';
      errorLog += "The request contains an invalid parameter value. For example, " + '\r\n';
      errorLog += "this error occurs if you specify a video ID that does not have 11 characters, " + '\r\n';
      errorLog += "or if the video ID contains invalid characters, " + '\r\n';
      errorLog += "such as exclamation points or asterisks.      " + '\r\n';
      break;
    case 5:
      // 요청한 콘텐츠는 HTML5플레이어에서 재생할 수 없는, 또는 HTML5플레이어에 대한 별도의 에러가 발생했습니다.
      errorLog += "Error Code : 5" + '\r\n';
      errorLog += " The requested content cannot be played in an HTML5 player" + '\r\n';
      errorLog += "or another error related to the HTML5 player has occurred." + '\r\n';
      break;
    case 100:
      // 요청한 동영상을 찾을 수 없습니다.
      // 어떠한 이유로든 동영상이 삭제되었거나 비공개로 표시된 경우에 이 오류가 발생합니다.
      errorLog += "Error Code : 100" + '\r\n';
      errorLog += "The video requested was not found. " + '\r\n';
      errorLog += "This error occurs when a video has been removed (for any reason) " + '\r\n';
      errorLog += "or has been marked as private." + '\r\n';
      break;
    case 101:
    case 150:
      // 요청한 동영상의 소유자가 내장 플레이어에서 동영상을 재생하는 것을 허용하지 않습니다.
      errorLog += "Error Code : 101 or 150" + '\r\n';
      errorLog += "The owner of the requested video does not allow it to be played in embedded players.";
      break;
  }
  YTPlayer.stopVideo();
  window.alert(errorLog);
}
function onPlayerStateChange (event) {
  switch(event.data) {
      case YT.PlayerState.ENDED: // 종료됨
          console.log('Video has ended.');
          YTPlayer._status = YT.PlayerState.ENDED;
          YTPlayer.removeAllElement();
          break;
      case YT.PlayerState.PLAYING: // 재생 중
          console.log('Video is playing.');
          YTPlayer._status = YT.PlayerState.PLAYING;
          break;
      case YT.PlayerState.PAUSED: // 일시 중지
          console.log('Video is paused.');
          YTPlayer._status = YT.PlayerState.PAUSED
          break;
      case YT.PlayerState.BUFFERING: // 버퍼링
          console.log('Video is buffering.');
          YTPlayer._status = YT.PlayerState.BUFFERING;
          break;
      case YT.PlayerState.CUED: // 동영상 신호
          console.log('Video is cued.');
          YTPlayer._status = YT.PlayerState.CUED;
          break;
      default: // 시작 전
          console.log('Unrecognized state.');
          break;
  }
}

(function() {

  //----------------------------------------------------------------------------
  // YTPlayer
  //
  //
  YTPlayer.initialize = function() {
    this._init = false;
    this._status = -1;
    this._ytPlayer = document.createElement('div');
    this._ytPlayer.id = 'ytplayer';
    this._ytPlayer.style.width = '560px';
    this._ytPlayer.style.height = '315px';
    document.body.appendChild(this._ytPlayer);
    this._tag = document.createElement('script');
    this._tag.src = "https://www.youtube.com/iframe_api";
    this.createIframe();
  };

  YTPlayer.createIframe = function () {
    this._iframe = document.createElement('iframe');
    this._iframe.id = 'ytplayer-iframe';
    this._iframe.width = 560;
    this._iframe.height = 315;
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
    this._iframe.frameBorder = 0;
    this._iframe.allowfullscreen = true;
    Graphics._centerElement(this._iframe);
    this._ytPlayer.appendChild(this._iframe);
  };

  YTPlayer.preVideo = function(src) {
    var v;
    if(src) {
      if(re.test(src)) {
          // v = src.split(re)[1];
          v = RegExp.$1;
      } else {
          v = src.replace('https://www.youtube.com/watch?v=','').replace('&feature=youtu.be','');
      }
      this._iframe.src = 'https://www.youtube.com/embed/%1?enablejsapi=1&version=3'.format(v);
    }
    this._iframe.style.opacity = '1';
    this._iframe.style.zIndex = '20000';
    if(!this._init) {
      this._firstScriptTag = document.getElementsByTagName('script')[0];
      this._firstScriptTag.parentNode.insertBefore(this._tag, this._firstScriptTag);
      this._init = true;
    } else {
      window.onYouTubeIframeAPIReady();
    }
  };

  YTPlayer.playVideo = function(src) {
    if(!this._iframe) this.createIframe();
    this.preVideo(src);
    Graphics._canvas.style.opacity = '0.5';
    if($gamePlayer) $gamePlayer.lock();
  };

  YTPlayer.stopVideo = function() {
    if(!this._iframe) this.createIframe();
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
    Graphics._canvas.style.opacity = '1.0';
    this.callPlayer("stopVideo");
    if($gamePlayer) $gamePlayer.unlock();
  };

  YTPlayer.removeAllElement = function() {
    this.stopVideo();
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
  };

  YTPlayer.callPlayer = function(func, args) {
      var frame_id = 'ytplayer-iframe';
      var src = this._iframe.src;
      if (src.indexOf('youtube.com/embed') != -1) {
          this._iframe.contentWindow.postMessage(JSON.stringify({
              'event': 'command',
              'func': func,
              'args': args || [],
              'id': frame_id
          }), "*");
      }
  };

  YTPlayer.isOnPlayer = function() {
    if(!this._iframe) return false;
    if(!this._iframe.contentWindow) return false;
    return true;
  };

  // Checks whether the YoutubePlayer is playing.
  YTPlayer.isPlaying = function() {
    if(this._status === YT.PlayerState.PLAYING) {
      return true;
    }
    return false;
  }

  YTPlayer.isPaused = function() {
    if(this._status === YT.PlayerState.PAUSED) {
      return true;
    }
    return false;
  };

  YTPlayer.isBuffering = function() {
    if(this._status === YT.PlayerState.BUFFERING) {
      return true;
    }
    return false;
  };

  YTPlayer.isEnded = function() {
    if(this._status === YT.PlayerState.ENDED) {
      return true;
    }
    return false;
  };

  YTPlayer.getRect = function() {
    var w, h, gw, gh, x1, x2, y1, y2;
    w = 560 / 2;
    h = 315 / 2;
    gw = (Graphics.boxWidth / 2);
    gh = (Graphics.boxHeight / 2);
    x1 = gw - w;
    x2 = gw + w;
    y1 = gh - h;
    y2 = gh + h;
    return new Rectangle(x1, y1, x2, y2);
  }

  YTPlayer.isTouched = function() {
    var x = TouchInput.x;
    var y = TouchInput.y;
    var rect = this.getRect();
    return x >= rect.x && y >= rect.y && x < rect.width && y < rect.height;
  }

  //----------------------------------------------------------------------------
  // Graphics
  //
  //
  var alias_Graphics_initialize = Graphics.initialize;
  Graphics.initialize = function(width, height, type) {
    alias_Graphics_initialize.call(this, width, height, type);
    YTPlayer.initialize();
  }

  Graphics.playYoutube = function(src) {
    var lastStep;
    YTPlayer.playVideo(src);
    lastStep = setInterval(function() {
      YTPlayer.callPlayer('playVideo');
      if(YTPlayer.isOnPlayer()) {
          clearInterval(lastStep);
      }
    }, 2000);
  };

  //----------------------------------------------------------------------------
  // Game_Interpreter
  //
  //
  var alias_pluginCommand = Game_Interpreter.prototype.pluginCommand;
  Game_Interpreter.prototype.pluginCommand = function(command, args) {
      alias_pluginCommand.call(this, command, args);
      if(command === "YTPlayer") {
        switch (args[0].toLowerCase()) {
          case 'play':
            var src = args.slice(1, args.length).join('');
            setTimeout(function() {
              Graphics.playYoutube(src);
            }, 1);
            break;
          case 'stop':
            if(YTPlayer.isPlaying()) YTPlayer.stopVideo();
        }
      }
  };

  //----------------------------------------------------------------------------
  // Game_Player
  //
  //
  var alias_Game_Player_initMembers = Game_Player.prototype.initMembers;
  Game_Player.prototype.initMembers = function() {
    alias_Game_Player_initMembers.call(this);
    this._locked = false;
    this._prelockDirection = 2;
  }

  Game_Player.prototype.lock = function() {
    if (!this._locked) {
        this._prelockDirection = this.direction();
        this._locked = true;
    }
  };

  Game_Player.prototype.unlock = function() {
    if (this._locked) {
        this._locked = false;
        this.setDirection(this._prelockDirection);
    }
  };

  var alias_Game_Player_canMove = Game_Player.prototype.canMove;
  Game_Player.prototype.canMove = function() {
    if(this._locked) {
      return false;
    }
    return alias_Game_Player_canMove.call(this);
  };

  // ----------------------------------------------------------------------------
  // Scene_Map
  //
  //
  var alias_Scene_Map_update = Scene_Map.prototype.update;
  Scene_Map.prototype.update = function() {
    alias_Scene_Map_update.call(this);
    this.checkEscapeToYoutube();
  };

  Scene_Map.prototype.checkEscapeToYoutube = function() {
    if(TouchInput.isTriggered()) {
      if(!YTPlayer.isTouched() && (YTPlayer._status > 0) ) {
         YTPlayer.stopVideo();
      }
    }
  };

})();
<commit_msg>Fixed a function that parses a URL .<commit_after>/*:
 * RS_YoutubePlayer.js
 * @plugindesc This plugin allows you to start playback of the YouTube video.
 * @author biud436
 * @help
 * In general, In Android Chrome and Mobile Safari, It doesn't automatically
 * start playback. and you can stop the video by clicking around the YouTube video.
 * This plugin will automatically end the video playback if you watches
 * the video until the end.
 * =============================================================================
 * Plugin Command
 * =============================================================================
 * - This plugin command will start playback of the YouTube video. Please enter
 * the URL as follows. If the Internet had not connected, It could have caused
 * the error.
 *
 * YTPlayer play https://www.youtube.com/watch?v=C4ze-KCSxQY
 *
 * - Stop playback of the YouTube Video.
 *
 * YTPlayer stop
 *
 * =============================================================================
 * Change Log
 * =============================================================================
 * 2016.05.08 (v1.0.0) - First Release
 * 2016.05.09 (v1.0.1) - Added Error Handler
 * 2016.05.12 (v1.0.2) - Fixed a function that parses a URL .
 */

var Imported = Imported || {};
Imported.RS_YoutubePlayer = true;

var player;

function YTPlayer() {
  throw new Error("This is a static class");
}

//----------------------------------------------------------------------------
// Youtube Event Handler
//
//
function onYouTubeIframeAPIReady() {
  player = new YT.Player('ytplayer-iframe', {
    height: '560',
    width: '315',
    videoId: 'BIbpYySZ-2Q',
    fs: 1,
    autoplay: 1,
    enablejsapi: 1,
    rel: 1,
    showinfo: 1,
    playsinline: 0,
    events: {
      'onReady': onPlayerReady,
      'onStateChange': onPlayerStateChange,
      'onError': onPlayerError
    }
  });
}

function onPlayerReady(event) {
  var target = event.target;
  target.playVideo();
}

function onPlayerError(event) {
  var errorLog = "";
  switch (event.data) {
    case 2:
      // 요청에 잘못된 매개변수 값이 포함되어 있습니다. 예를 들어 11자리가 아닌 동영상 ID를 지정하거나
      // 동영상 ID에 느낌표 또는 별표와 같은 잘못된 문자가 포함된 경우에 이 오류가 발생합니다.
      errorLog += "Error Code : 2" + '\r\n';
      errorLog += "The request contains an invalid parameter value. For example, " + '\r\n';
      errorLog += "this error occurs if you specify a video ID that does not have 11 characters, " + '\r\n';
      errorLog += "or if the video ID contains invalid characters, " + '\r\n';
      errorLog += "such as exclamation points or asterisks.      " + '\r\n';
      break;
    case 5:
      // 요청한 콘텐츠는 HTML5플레이어에서 재생할 수 없는, 또는 HTML5플레이어에 대한 별도의 에러가 발생했습니다.
      errorLog += "Error Code : 5" + '\r\n';
      errorLog += " The requested content cannot be played in an HTML5 player" + '\r\n';
      errorLog += "or another error related to the HTML5 player has occurred." + '\r\n';
      break;
    case 100:
      // 요청한 동영상을 찾을 수 없습니다.
      // 어떠한 이유로든 동영상이 삭제되었거나 비공개로 표시된 경우에 이 오류가 발생합니다.
      errorLog += "Error Code : 100" + '\r\n';
      errorLog += "The video requested was not found. " + '\r\n';
      errorLog += "This error occurs when a video has been removed (for any reason) " + '\r\n';
      errorLog += "or has been marked as private." + '\r\n';
      break;
    case 101:
    case 150:
      // 요청한 동영상의 소유자가 내장 플레이어에서 동영상을 재생하는 것을 허용하지 않습니다.
      errorLog += "Error Code : 101 or 150" + '\r\n';
      errorLog += "The owner of the requested video does not allow it to be played in embedded players.";
      break;
  }
  YTPlayer.stopVideo();
  window.alert(errorLog);
}
function onPlayerStateChange (event) {
  switch(event.data) {
      case YT.PlayerState.ENDED: // 종료됨
          console.log('Video has ended.');
          YTPlayer._status = YT.PlayerState.ENDED;
          YTPlayer.removeAllElement();
          break;
      case YT.PlayerState.PLAYING: // 재생 중
          console.log('Video is playing.');
          YTPlayer._status = YT.PlayerState.PLAYING;
          break;
      case YT.PlayerState.PAUSED: // 일시 중지
          console.log('Video is paused.');
          YTPlayer._status = YT.PlayerState.PAUSED
          break;
      case YT.PlayerState.BUFFERING: // 버퍼링
          console.log('Video is buffering.');
          YTPlayer._status = YT.PlayerState.BUFFERING;
          break;
      case YT.PlayerState.CUED: // 동영상 신호
          console.log('Video is cued.');
          YTPlayer._status = YT.PlayerState.CUED;
          break;
      default: // 시작 전
          console.log('Unrecognized state.');
          break;
  }
}

(function() {

  var re = /(?:http|https)+(?:\:\/\/youtu.be\/)+(.*)/gi;

  //----------------------------------------------------------------------------
  // YTPlayer
  //
  //
  YTPlayer.initialize = function() {
    this._init = false;
    this._status = -1;
    this._ytPlayer = document.createElement('div');
    this._ytPlayer.id = 'ytplayer';
    this._ytPlayer.style.width = '560px';
    this._ytPlayer.style.height = '315px';
    document.body.appendChild(this._ytPlayer);
    this._tag = document.createElement('script');
    this._tag.src = "https://www.youtube.com/iframe_api";
    this.createIframe();
  };

  YTPlayer.createIframe = function () {
    this._iframe = document.createElement('iframe');
    this._iframe.id = 'ytplayer-iframe';
    this._iframe.width = 560;
    this._iframe.height = 315;
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
    this._iframe.frameBorder = 0;
    this._iframe.allowfullscreen = true;
    Graphics._centerElement(this._iframe);
    this._ytPlayer.appendChild(this._iframe);
  };

  YTPlayer.preVideo = function(src) {
    var v;
    if(src) {
      if(re.test(src)) {
          // v = src.split(re)[1];
          v = RegExp.$1;
      } else {
          v = src.replace('https://www.youtube.com/watch?v=','').replace('&feature=youtu.be','');
      }
      this._iframe.src = 'https://www.youtube.com/embed/%1?enablejsapi=1&version=3'.format(v);
    }
    this._iframe.style.opacity = '1';
    this._iframe.style.zIndex = '20000';
    if(!this._init) {
      this._firstScriptTag = document.getElementsByTagName('script')[0];
      this._firstScriptTag.parentNode.insertBefore(this._tag, this._firstScriptTag);
      this._init = true;
    } else {
      window.onYouTubeIframeAPIReady();
    }
  };

  YTPlayer.playVideo = function(src) {
    if(!this._iframe) this.createIframe();
    this.preVideo(src);
    Graphics._canvas.style.opacity = '0.5';
    if($gamePlayer) $gamePlayer.lock();
  };

  YTPlayer.stopVideo = function() {
    if(!this._iframe) this.createIframe();
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
    Graphics._canvas.style.opacity = '1.0';
    this.callPlayer("stopVideo");
    if($gamePlayer) $gamePlayer.unlock();
  };

  YTPlayer.removeAllElement = function() {
    this.stopVideo();
    this._iframe.style.opacity = '0';
    this._iframe.style.zIndex = '0';
  };

  YTPlayer.callPlayer = function(func, args) {
      var frame_id = 'ytplayer-iframe';
      var src = this._iframe.src;
      if (src.indexOf('youtube.com/embed') != -1) {
          this._iframe.contentWindow.postMessage(JSON.stringify({
              'event': 'command',
              'func': func,
              'args': args || [],
              'id': frame_id
          }), "*");
      }
  };

  YTPlayer.isOnPlayer = function() {
    if(!this._iframe) return false;
    if(!this._iframe.contentWindow) return false;
    return true;
  };

  // Checks whether the YoutubePlayer is playing.
  YTPlayer.isPlaying = function() {
    if(this._status === YT.PlayerState.PLAYING) {
      return true;
    }
    return false;
  }

  YTPlayer.isPaused = function() {
    if(this._status === YT.PlayerState.PAUSED) {
      return true;
    }
    return false;
  };

  YTPlayer.isBuffering = function() {
    if(this._status === YT.PlayerState.BUFFERING) {
      return true;
    }
    return false;
  };

  YTPlayer.isEnded = function() {
    if(this._status === YT.PlayerState.ENDED) {
      return true;
    }
    return false;
  };

  YTPlayer.getRect = function() {
    var w, h, gw, gh, x1, x2, y1, y2;
    w = 560 / 2;
    h = 315 / 2;
    gw = (Graphics.boxWidth / 2);
    gh = (Graphics.boxHeight / 2);
    x1 = gw - w;
    x2 = gw + w;
    y1 = gh - h;
    y2 = gh + h;
    return new Rectangle(x1, y1, x2, y2);
  }

  YTPlayer.isTouched = function() {
    var x = TouchInput.x;
    var y = TouchInput.y;
    var rect = this.getRect();
    return x >= rect.x && y >= rect.y && x < rect.width && y < rect.height;
  }

  //----------------------------------------------------------------------------
  // Graphics
  //
  //
  var alias_Graphics_initialize = Graphics.initialize;
  Graphics.initialize = function(width, height, type) {
    alias_Graphics_initialize.call(this, width, height, type);
    YTPlayer.initialize();
  }

  Graphics.playYoutube = function(src) {
    var lastStep;
    YTPlayer.playVideo(src);
    lastStep = setInterval(function() {
      YTPlayer.callPlayer('playVideo');
      if(YTPlayer.isOnPlayer()) {
          clearInterval(lastStep);
      }
    }, 2000);
  };

  //----------------------------------------------------------------------------
  // Game_Interpreter
  //
  //
  var alias_pluginCommand = Game_Interpreter.prototype.pluginCommand;
  Game_Interpreter.prototype.pluginCommand = function(command, args) {
      alias_pluginCommand.call(this, command, args);
      if(command === "YTPlayer") {
        switch (args[0].toLowerCase()) {
          case 'play':
            var src = args.slice(1, args.length).join('');
            setTimeout(function() {
              Graphics.playYoutube(src);
            }, 1);
            break;
          case 'stop':
            if(YTPlayer.isPlaying()) YTPlayer.stopVideo();
        }
      }
  };

  //----------------------------------------------------------------------------
  // Game_Player
  //
  //
  var alias_Game_Player_initMembers = Game_Player.prototype.initMembers;
  Game_Player.prototype.initMembers = function() {
    alias_Game_Player_initMembers.call(this);
    this._locked = false;
    this._prelockDirection = 2;
  }

  Game_Player.prototype.lock = function() {
    if (!this._locked) {
        this._prelockDirection = this.direction();
        this._locked = true;
    }
  };

  Game_Player.prototype.unlock = function() {
    if (this._locked) {
        this._locked = false;
        this.setDirection(this._prelockDirection);
    }
  };

  var alias_Game_Player_canMove = Game_Player.prototype.canMove;
  Game_Player.prototype.canMove = function() {
    if(this._locked) {
      return false;
    }
    return alias_Game_Player_canMove.call(this);
  };

  // ----------------------------------------------------------------------------
  // Scene_Map
  //
  //
  var alias_Scene_Map_update = Scene_Map.prototype.update;
  Scene_Map.prototype.update = function() {
    alias_Scene_Map_update.call(this);
    this.checkEscapeToYoutube();
  };

  Scene_Map.prototype.checkEscapeToYoutube = function() {
    if(TouchInput.isTriggered()) {
      if(!YTPlayer.isTouched() && (YTPlayer._status > 0) ) {
         YTPlayer.stopVideo();
      }
    }
  };

})();
<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import classNames from 'classnames';
import sdk from '../../index';
import dis from '../../dispatcher';
import Unread from '../../Unread';
import * as RoomNotifs from '../../RoomNotifs';
import * as FormattingUtils from '../../utils/FormattingUtils';
import IndicatorScrollbar from './IndicatorScrollbar';
import { KeyCode } from '../../Keyboard';
import { Group } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import RoomTile from "../views/rooms/RoomTile";
import LazyRenderList from "../views/elements/LazyRenderList";

// turn this on for drop & drag console debugging galore
const debug = false;

const RoomSubList = React.createClass({
    displayName: 'RoomSubList',

    debug: debug,

    propTypes: {
        list: PropTypes.arrayOf(PropTypes.object).isRequired,
        label: PropTypes.string.isRequired,
        tagName: PropTypes.string,

        order: PropTypes.string.isRequired,

        // passed through to RoomTile and used to highlight room with `!` regardless of notifications count
        isInvite: PropTypes.bool,

        startAsHidden: PropTypes.bool,
        showSpinner: PropTypes.bool, // true to show a spinner if 0 elements when expanded
        collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
        onHeaderClick: PropTypes.func,
        incomingCall: PropTypes.object,
        isFiltered: PropTypes.bool,
        headerItems: PropTypes.node, // content shown in the sublist header
        extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
    },

    getInitialState: function() {
        return {
            hidden: this.props.startAsHidden || false,
            // some values to get LazyRenderList starting
            scrollerHeight: 800,
            scrollTop: 0,
        };
    },

    getDefaultProps: function() {
        return {
            onHeaderClick: function() {
            }, // NOP
            extraTiles: [],
            isInvite: false,
        };
    },

    componentWillMount: function() {
        this.dispatcherRef = dis.register(this.onAction);
    },

    componentWillUnmount: function() {
        dis.unregister(this.dispatcherRef);
    },

    // The header is collapsable if it is hidden or not stuck
    // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
    isCollapsableOnClick: function() {
        const stuck = this.refs.header.dataset.stuck;
        if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
            return true;
        } else {
            return false;
        }
    },

    onAction: function(payload) {
        // XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched,
        // but this is no longer true, so we must do it here (and can apply the small
        // optimisation of checking that we care about the room being read).
        //
        // Ultimately we need to transition to a state pushing flow where something
        // explicitly notifies the components concerned that the notif count for a room
        // has change (e.g. a Flux store).
        if (payload.action === 'on_room_read' &&
            this.props.list.some((r) => r.roomId === payload.roomId)
        ) {
            this.forceUpdate();
        }
    },

    onClick: function(ev) {
        if (this.isCollapsableOnClick()) {
            // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
            const isHidden = !this.state.hidden;
            this.setState({hidden: isHidden}, () => {
                this.props.onHeaderClick(isHidden);
            });
        } else {
            // The header is stuck, so the click is to be interpreted as a scroll to the header
            this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
        }
    },

    onRoomTileClick(roomId, ev) {
        dis.dispatch({
            action: 'view_room',
            room_id: roomId,
            clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)),
        });
    },

    _updateSubListCount: function() {
        // Force an update by setting the state to the current state
        // Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
        // method is honoured
        this.setState(this.state);
    },

    makeRoomTile: function(room) {
        return <RoomTile
            room={room}
            roomSubList={this}
            tagName={this.props.tagName}
            key={room.roomId}
            collapsed={this.props.collapsed || false}
            unread={Unread.doesRoomHaveUnreadMessages(room)}
            highlight={room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite}
            notificationCount={room.getUnreadNotificationCount()}
            isInvite={this.props.isInvite}
            refreshSubList={this._updateSubListCount}
            incomingCall={null}
            onClick={this.onRoomTileClick}
        />;
    },

    _onNotifBadgeClick: function(e) {
        // prevent the roomsublist collapsing
        e.preventDefault();
        e.stopPropagation();
        const room = this.props.list.find(room => RoomNotifs.getRoomHasBadge(room));
        if (room) {
            dis.dispatch({
                action: 'view_room',
                room_id: room.roomId,
            });
        }
    },

    _onInviteBadgeClick: function(e) {
        // prevent the roomsublist collapsing
        e.preventDefault();
        e.stopPropagation();
        // switch to first room in sortedList as that'll be the top of the list for the user
        if (this.props.list && this.props.list.length > 0) {
            dis.dispatch({
                action: 'view_room',
                room_id: this.props.list[0].roomId,
            });
        } else if (this.props.extraTiles && this.props.extraTiles.length > 0) {
            // Group Invites are different in that they are all extra tiles and not rooms
            // XXX: this is a horrible special case because Group Invite sublist is a hack
            if (this.props.extraTiles[0].props && this.props.extraTiles[0].props.group instanceof Group) {
                dis.dispatch({
                    action: 'view_group',
                    group_id: this.props.extraTiles[0].props.group.groupId,
                });
            }
        }
    },

    _getHeaderJsx: function(isCollapsed) {
        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
        const subListNotifications = !this.props.isInvite ?
            RoomNotifs.aggregateNotificationCount(this.props.list) :
            {count: 0, highlight: true};
        const subListNotifCount = subListNotifications.count;
        const subListNotifHighlight = subListNotifications.highlight;

        let badge;
        if (!this.props.collapsed) {
            const badgeClasses = classNames({
                'mx_RoomSubList_badge': true,
                'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
            });
            if (subListNotifCount > 0) {
                badge =

                    { FormattingUtils.formatCount(subListNotifCount) }

;
            } else if (this.props.isInvite && this.props.list.length) {
                // no notifications but highlight anyway because this is an invite badge
                badge =
{this.props.list.length}
;
            }
        }

        // When collapsed, allow a long hover on the header to show user
        // the full tag name and room count
        let title;
        if (this.props.collapsed) {
            title = this.props.label;
        }

        let incomingCall;
        if (this.props.incomingCall) {
            // We can assume that if we have an incoming call then it is for this list
            const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
            incomingCall =
                <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={this.props.incomingCall} />;
        }

        let addRoomButton;
        if (this.props.onAddRoom) {
            addRoomButton = (
                <AccessibleButton onClick={ this.props.onAddRoom } className="mx_RoomSubList_addRoom" />
            );
        }

        const len = this.props.list.length + this.props.extraTiles.length;
        let chevron;
        if (len) {
            const chevronClasses = classNames({
                'mx_RoomSubList_chevron': true,
                'mx_RoomSubList_chevronRight': isCollapsed,
                'mx_RoomSubList_chevronDown': !isCollapsed,
            });
            chevron = (

);
        }

        const tabindex = this.props.isFiltered ? "0" : "-1";
        return (

                <AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
                    { chevron }
                    {this.props.label}
                    { incomingCall }
                </AccessibleButton>
                { badge }
                { addRoomButton }

        );
    },

    checkOverflow: function() {
        if (this.refs.scroller) {
            this.refs.scroller.checkOverflow();
        }
    },

    setHeight: function(height) {
        if (this.refs.subList) {
            this.refs.subList.style.height = `${height}px`;
        }
        this._updateLazyRenderHeight(height);
    },

    _updateLazyRenderHeight: function(height) {
        this.setState({scrollerHeight: height});
    },

    _onScroll: function() {
        this.setState({scrollTop: this.refs.scroller.getScrollTop()});
    },

    _getRenderItems: function() {
        // try our best to not create a new array
        // because LazyRenderList rerender when the items prop
        // is not the same object as the previous value
        const {list, extraTiles} = this.props;
        if (!extraTiles || !extraTiles.length) {
            return list;
        }
        if (!list || list.length) {
            return extraTiles;
        }
        return list.concat(extraTiles);
    },

    render: function() {
        const len = this.props.list.length + this.props.extraTiles.length;
        const isCollapsed = this.state.hidden && !this.props.forceExpand;
        if (len) {
            const subListClasses = classNames({
                "mx_RoomSubList": true,
                "mx_RoomSubList_hidden": isCollapsed,
                "mx_RoomSubList_nonEmpty": len && !isCollapsed,
            });

            if (isCollapsed) {
                return

                    {this._getHeaderJsx(isCollapsed)}

;
            } else {
                return

                    {this._getHeaderJsx(isCollapsed)}
                    <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
                        <LazyRenderList
                            scrollTop={this.state.scrollTop }
                            height={ this.state.scrollerHeight }
                            renderItem={ this.makeRoomTile }
                            itemHeight={34}
                            items={this._getRenderItems()} />
                    </IndicatorScrollbar>

;
            }
        } else {
            const Loader = sdk.getComponent("elements.Spinner");
            let content;
            if (this.props.showSpinner && !isCollapsed) {
                content = <Loader />;
            }

            return (

                    { this._getHeaderJsx(isCollapsed) }
                    { content }

            );
        }
    },
});

module.exports = RoomSubList;
<commit_msg>disable lazy list rendering if extraTiles are provided<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import React from 'react';
import classNames from 'classnames';
import sdk from '../../index';
import dis from '../../dispatcher';
import Unread from '../../Unread';
import * as RoomNotifs from '../../RoomNotifs';
import * as FormattingUtils from '../../utils/FormattingUtils';
import IndicatorScrollbar from './IndicatorScrollbar';
import { KeyCode } from '../../Keyboard';
import { Group } from 'matrix-js-sdk';
import PropTypes from 'prop-types';
import RoomTile from "../views/rooms/RoomTile";
import LazyRenderList from "../views/elements/LazyRenderList";

// turn this on for drop & drag console debugging galore
const debug = false;

const RoomSubList = React.createClass({
    displayName: 'RoomSubList',

    debug: debug,

    propTypes: {
        list: PropTypes.arrayOf(PropTypes.object).isRequired,
        label: PropTypes.string.isRequired,
        tagName: PropTypes.string,

        order: PropTypes.string.isRequired,

        // passed through to RoomTile and used to highlight room with `!` regardless of notifications count
        isInvite: PropTypes.bool,

        startAsHidden: PropTypes.bool,
        showSpinner: PropTypes.bool, // true to show a spinner if 0 elements when expanded
        collapsed: PropTypes.bool.isRequired, // is LeftPanel collapsed?
        onHeaderClick: PropTypes.func,
        incomingCall: PropTypes.object,
        isFiltered: PropTypes.bool,
        headerItems: PropTypes.node, // content shown in the sublist header
        extraTiles: PropTypes.arrayOf(PropTypes.node), // extra elements added beneath tiles
    },

    getInitialState: function() {
        return {
            hidden: this.props.startAsHidden || false,
            // some values to get LazyRenderList starting
            scrollerHeight: 800,
            scrollTop: 0,
        };
    },

    getDefaultProps: function() {
        return {
            onHeaderClick: function() {
            }, // NOP
            extraTiles: [],
            isInvite: false,
        };
    },

    componentWillMount: function() {
        this.dispatcherRef = dis.register(this.onAction);
    },

    componentWillUnmount: function() {
        dis.unregister(this.dispatcherRef);
    },

    // The header is collapsable if it is hidden or not stuck
    // The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
    isCollapsableOnClick: function() {
        const stuck = this.refs.header.dataset.stuck;
        if (!this.props.forceExpand && (this.state.hidden || stuck === undefined || stuck === "none")) {
            return true;
        } else {
            return false;
        }
    },

    onAction: function(payload) {
        // XXX: Previously RoomList would forceUpdate whenever on_room_read is dispatched,
        // but this is no longer true, so we must do it here (and can apply the small
        // optimisation of checking that we care about the room being read).
        //
        // Ultimately we need to transition to a state pushing flow where something
        // explicitly notifies the components concerned that the notif count for a room
        // has change (e.g. a Flux store).
        if (payload.action === 'on_room_read' &&
            this.props.list.some((r) => r.roomId === payload.roomId)
        ) {
            this.forceUpdate();
        }
    },

    onClick: function(ev) {
        if (this.isCollapsableOnClick()) {
            // The header isCollapsable, so the click is to be interpreted as collapse and truncation logic
            const isHidden = !this.state.hidden;
            this.setState({hidden: isHidden}, () => {
                this.props.onHeaderClick(isHidden);
            });
        } else {
            // The header is stuck, so the click is to be interpreted as a scroll to the header
            this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
        }
    },

    onRoomTileClick(roomId, ev) {
        dis.dispatch({
            action: 'view_room',
            room_id: roomId,
            clear_search: (ev && (ev.keyCode === KeyCode.ENTER || ev.keyCode === KeyCode.SPACE)),
        });
    },

    _updateSubListCount: function() {
        // Force an update by setting the state to the current state
        // Doing it this way rather than using forceUpdate(), so that the shouldComponentUpdate()
        // method is honoured
        this.setState(this.state);
    },

    makeRoomTile: function(room) {
        return <RoomTile
            room={room}
            roomSubList={this}
            tagName={this.props.tagName}
            key={room.roomId}
            collapsed={this.props.collapsed || false}
            unread={Unread.doesRoomHaveUnreadMessages(room)}
            highlight={room.getUnreadNotificationCount('highlight') > 0 || this.props.isInvite}
            notificationCount={room.getUnreadNotificationCount()}
            isInvite={this.props.isInvite}
            refreshSubList={this._updateSubListCount}
            incomingCall={null}
            onClick={this.onRoomTileClick}
        />;
    },

    _onNotifBadgeClick: function(e) {
        // prevent the roomsublist collapsing
        e.preventDefault();
        e.stopPropagation();
        const room = this.props.list.find(room => RoomNotifs.getRoomHasBadge(room));
        if (room) {
            dis.dispatch({
                action: 'view_room',
                room_id: room.roomId,
            });
        }
    },

    _onInviteBadgeClick: function(e) {
        // prevent the roomsublist collapsing
        e.preventDefault();
        e.stopPropagation();
        // switch to first room in sortedList as that'll be the top of the list for the user
        if (this.props.list && this.props.list.length > 0) {
            dis.dispatch({
                action: 'view_room',
                room_id: this.props.list[0].roomId,
            });
        } else if (this.props.extraTiles && this.props.extraTiles.length > 0) {
            // Group Invites are different in that they are all extra tiles and not rooms
            // XXX: this is a horrible special case because Group Invite sublist is a hack
            if (this.props.extraTiles[0].props && this.props.extraTiles[0].props.group instanceof Group) {
                dis.dispatch({
                    action: 'view_group',
                    group_id: this.props.extraTiles[0].props.group.groupId,
                });
            }
        }
    },

    _getHeaderJsx: function(isCollapsed) {
        const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
        const subListNotifications = !this.props.isInvite ?
            RoomNotifs.aggregateNotificationCount(this.props.list) :
            {count: 0, highlight: true};
        const subListNotifCount = subListNotifications.count;
        const subListNotifHighlight = subListNotifications.highlight;

        let badge;
        if (!this.props.collapsed) {
            const badgeClasses = classNames({
                'mx_RoomSubList_badge': true,
                'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
            });
            if (subListNotifCount > 0) {
                badge =

                    { FormattingUtils.formatCount(subListNotifCount) }

;
            } else if (this.props.isInvite && this.props.list.length) {
                // no notifications but highlight anyway because this is an invite badge
                badge =
{this.props.list.length}
;
            }
        }

        // When collapsed, allow a long hover on the header to show user
        // the full tag name and room count
        let title;
        if (this.props.collapsed) {
            title = this.props.label;
        }

        let incomingCall;
        if (this.props.incomingCall) {
            // We can assume that if we have an incoming call then it is for this list
            const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
            incomingCall =
                <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={this.props.incomingCall} />;
        }

        let addRoomButton;
        if (this.props.onAddRoom) {
            addRoomButton = (
                <AccessibleButton onClick={ this.props.onAddRoom } className="mx_RoomSubList_addRoom" />
            );
        }

        const len = this.props.list.length + this.props.extraTiles.length;
        let chevron;
        if (len) {
            const chevronClasses = classNames({
                'mx_RoomSubList_chevron': true,
                'mx_RoomSubList_chevronRight': isCollapsed,
                'mx_RoomSubList_chevronDown': !isCollapsed,
            });
            chevron = (

);
        }

        const tabindex = this.props.isFiltered ? "0" : "-1";
        return (

                <AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
                    { chevron }
                    {this.props.label}
                    { incomingCall }
                </AccessibleButton>
                { badge }
                { addRoomButton }

        );
    },

    checkOverflow: function() {
        if (this.refs.scroller) {
            this.refs.scroller.checkOverflow();
        }
    },

    setHeight: function(height) {
        if (this.refs.subList) {
            this.refs.subList.style.height = `${height}px`;
        }
        this._updateLazyRenderHeight(height);
    },

    _updateLazyRenderHeight: function(height) {
        this.setState({scrollerHeight: height});
    },

    _onScroll: function() {
        this.setState({scrollTop: this.refs.scroller.getScrollTop()});
    },

    _getList: function() {
        // try our best to not create a new array
        // because LazyRenderList rerender when the items prop
        // is not the same object as the previous value
        const {list, extraTiles} = this.props;
        if (!extraTiles || !extraTiles.length) {
            return list;
        }
        if (!list || list.length) {
            return extraTiles;
        }
        return list.concat(extraTiles);
    },

    _canUseLazyListRendering() {
        // for now disable lazy rendering as they are already rendered tiles
        // not rooms like props.list we pass to LazyRenderList
        return !this.props.extraTiles || !this.props.extraTiles.length;
    },

    render: function() {
        const len = this.props.list.length + this.props.extraTiles.length;
        const isCollapsed = this.state.hidden && !this.props.forceExpand;
        if (len) {
            const subListClasses = classNames({
                "mx_RoomSubList": true,
                "mx_RoomSubList_hidden": isCollapsed,
                "mx_RoomSubList_nonEmpty": len && !isCollapsed,
            });

            if (isCollapsed) {
                return

                    {this._getHeaderJsx(isCollapsed)}

;
            } else if (this._canUseLazyListRendering()) {
                return

                    {this._getHeaderJsx(isCollapsed)}
                    <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
                        <LazyRenderList
                            scrollTop={this.state.scrollTop }
                            height={ this.state.scrollerHeight }
                            renderItem={ this.makeRoomTile }
                            itemHeight={34}
                            items={ this.props.list } />
                    </IndicatorScrollbar>

;
            } else {
                const roomTiles = this.props.list.map(r => this.makeRoomTile(r));
                const tiles = roomTiles.concat(this.props.extraTiles);
                return

                    {this._getHeaderJsx(isCollapsed)}
                    <IndicatorScrollbar ref="scroller" className="mx_RoomSubList_scroll" onScroll={ this._onScroll }>
                        { tiles }
                    </IndicatorScrollbar>

;
            }
        } else {
            const Loader = sdk.getComponent("elements.Spinner");
            let content;
            if (this.props.showSpinner && !isCollapsed) {
                content = <Loader />;
            }

            return (

                    { this._getHeaderJsx(isCollapsed) }
                    { content }

            );
        }
    },
});

module.exports = RoomSubList;
<|endoftext|>


<commit_before>var selectorGraphs = Selector()
    .callback(function(d){
        $('#'+selectorGraphs.type()).html(d.name);
        $('#dimensions input[name='+selectorGraphs.type()+']').val(d.id).trigger('change');
        $('#modal-selector').modal('hide');
    });

function select_dimension(id) {
  d3.select("#modal-selector-content").call(selectorGraphs.type(id));
  $('#modal-selector').modal('show');
}

function clean_selection(id) {
  $(id).siblings('button').html('Select');
  $(id).siblings('input').val('all').trigger('change');
}

var BuildGraph = (function () {

    return {
        dataset: dataset,
        view: view,
        init: init
    }

    var view, dataset;

    function updateViews() {
        $.ajax({
            method: "GET",
            url: "/" + dataviva.language + "/build_graph/views/" + BuildGraph.dataset +"/" +
                $('#dimensions #filter0').val() + "/" +
                ($('#dimensions #filter1').val() == 'all' ? 'all' : $('#dimensions #filter1')[0].name) + "/" +
                ($('#dimensions #filter2').val() == 'all' ? 'all' : $('#dimensions #filter2')[0].name),
            data: {
                    filter1: $('#dimensions #filter1').val(),
                    filter2: $('#dimensions #filter2').val()
                },
            success: function (result) {
                setViews(result.views);
                if (BuildGraph.view && $.inArray(BuildGraph.view.id, Object.keys(result.views)) > -1) {
                    $('#views select').val(BuildGraph.view.id);
                } else {
                    BuildGraph.view = result.views[$('#views select').val()];
                }
            }
        });
    }

    function setDimensions(dimensions) {
        $('#dimensions').empty();
        dimensions.forEach(function(dimension, index) {
            var div = $('

').addClass('form-group'),
                label = $('').attr('for', dimension.id).addClass('control-label'),
                cleaner = $('<button></button>').attr('for', dimension.id).addClass('btn btn-xs btn-link pull-right')
                                        .html(dataviva.dictionary['clean_selection'])
                                        .attr('onclick', 'clean_selection(this)'),
                selector = $('<button></button>').attr('id', dimension.id).addClass('btn btn-block btn-outline btn-primary')
                                        .html(dataviva.dictionary['select'])
                                        .attr('onclick', 'select_dimension(id);'),
                filter = $('<input></input>').attr('type', 'hidden').attr('name', dimension.id).attr('id', 'filter'+index).val('all');

            label.html(dataviva.dictionary[dimension.id]);
            filter.change(updateViews);

            if (dimension.name == 'School') {
                div.append(filter);
            } else {
                div.append(filter).append(label).append(cleaner).append(selector);
            }

            $('#dimensions').append(div);
        });
    }

    function setViews(views) {
        $('#views').empty()

        var div = $('

').addClass('form-group');
            select = $('<select></select>').addClass('form-control'),
            label = $('').attr('for', 'titles').addClass('control-label'),

        label.html(dataviva.dictionary['views']);

        for(id in views){
            option = $('<option value="'+id+'">'+views[id].name+'</option>')
            select.append(option);
        }

        div.append(label).append(select);
        $('#views').append(div);
    }

    function setGraphs(graphs) {
        $('#graphs').empty()

        var div = $('

').attr('class', 'dropdown dropdown-select');
            graphButton = $('<button data-toggle="dropdown" aria-expanded="true"></button>')
                            .attr('id', 'graph-button')
                            .attr('class', 'btn btn-outline btn-block dropdown-toggle'),
            dropDownMenu = $('

')
                            .attr('class', 'dropdown-menu'),
            label = $('').attr('for', 'graph-button').addClass('control-label');

        label.html(dataviva.dictionary['graphs']);

        for(graph in graphs){
            option = $('<option value="'+graph+'">'+graph+'</option>')
            select.append(option);
        }

        div.on( 'click', '.dropdown-menu li a', function() {
           var target = $(this).html();

           //Adds active class to selected item
           $(this).parents('.dropdown-menu').find('li').removeClass('active');
           $(this).parent('li').addClass('active');

           //Displays selected text on dropdown-toggle button
           $(this).parents('.dropdown-select').find('.dropdown-toggle').html(target);
        });

        div.append(label).append(select);
        $('#graphs').append(div);
    }

    function changeDataSet() {
        BuildGraph.dataset = this.value;
        setDimensions(dataviva.datasets[this.value].dimensions);
        updateViews();
    }

    function init() {
        for (dataset in dataviva.datasets) {
            $('#datasets').append( $('<option value="'+dataset+'">'+dataviva.dictionary[dataset]+'</option>'));
        }

        $('#datasets').change(changeDataSet);
    }
})();

$(document).ready(function () {
    dataviva.requireAttrs(['datasets'], function() {
        BuildGraph.init();
    });
});

<commit_msg>Handles when selection made with no changes<commit_after>var selectorGraphs = Selector()
    .callback(function(d){
        if ($('#'+selectorGraphs.type()).siblings('input').val() != d.id) {
            $('#'+selectorGraphs.type()).html(d.name);
            $('#dimensions input[name='+selectorGraphs.type()+']').val(d.id).trigger('change');
        }

        $('#modal-selector').modal('hide');
    });

function select_dimension(id) {
    d3.select("#modal-selector-content").call(selectorGraphs.type(id));
    $('#modal-selector').modal('show');
}

function clean_selection(id) {
    if ($(id).siblings('input').val() != 'all') {
        $(id).html(dataviva.dictionary['select']);
        $(id).siblings('input').val('all').trigger('change');
    }
}

var BuildGraph = (function () {

    return {
        dataset: dataset,
        view: view,
        init: init
    }

    var view, dataset;

    function updateViews() {
        $.ajax({
            method: "GET",
            url: "/" + dataviva.language + "/build_graph/views/" + BuildGraph.dataset +"/" +
                $('#dimensions #filter0').val() + "/" +
                ($('#dimensions #filter1').val() == 'all' ? 'all' : $('#dimensions #filter1')[0].name) + "/" +
                ($('#dimensions #filter2').val() == 'all' ? 'all' : $('#dimensions #filter2')[0].name),
            data: {
                    filter1: $('#dimensions #filter1').val(),
                    filter2: $('#dimensions #filter2').val()
                },
            success: function (result) {
                setViews(result.views);
                if ($.inArray(BuildGraph.view, Object.keys(result.views)) > -1) {
                    $('#views select').val(BuildGraph.view);
                } else {
                    BuildGraph.view = result.views[$('#views select').val()].id;
                }
            }
        });
    }

    function setDimensions(dimensions) {
        $('#dimensions').empty();
        dimensions.forEach(function(dimension, index) {
            var div = $('

').addClass('form-group'),
                label = $('').attr('for', dimension.id).addClass('control-label'),
                cleaner = $('<button></button>').attr('for', dimension.id).addClass('btn btn-xs btn-link pull-right')
                                        .html(dataviva.dictionary['clean_selection'])
                                        .attr('onclick', 'clean_selection('+dimension.id+')'),
                selector = $('<button></button>').attr('id', dimension.id).addClass('btn btn-block btn-outline btn-primary')
                                        .html(dataviva.dictionary['select'])
                                        .attr('onclick', 'select_dimension(id);'),
                filter = $('<input></input>').attr('type', 'hidden').attr('name', dimension.id).attr('id', 'filter'+index).val('all');

            label.html(dataviva.dictionary[dimension.id]);
            filter.change(updateViews);

            if (dimension.name == 'School') {
                div.append(filter);
            } else {
                div.append(filter).append(label).append(cleaner).append(selector);
            }

            $('#dimensions').append(div);
        });
    }

    function setViews(views) {
        $('#views').empty()

        var div = $('

').addClass('form-group');
            select = $('<select></select>').addClass('form-control'),
            label = $('').attr('for', 'titles').addClass('control-label'),

        label.html(dataviva.dictionary['views']);

        for(id in views){
            option = $('<option value="'+id+'">'+views[id].name+'</option>')
            select.append(option);
        }

        select.change(function() {
            BuildGraph.view = this.value;
        });

        div.append(label).append(select);
        $('#views').append(div);
    }

    function setGraphs(graphs) {
        $('#graphs').empty()

        var div = $('

').attr('class', 'dropdown dropdown-select');
            graphButton = $('<button data-toggle="dropdown" aria-expanded="true"></button>')
                            .attr('id', 'graph-button')
                            .attr('class', 'btn btn-outline btn-block dropdown-toggle'),
            dropDownMenu = $('

')
                            .attr('class', 'dropdown-menu'),
            label = $('').attr('for', 'graph-button').addClass('control-label');

        label.html(dataviva.dictionary['graphs']);

        for(graph in graphs){
            option = $('<option value="'+graph+'">'+graph+'</option>')
            select.append(option);
        }

        div.on( 'click', '.dropdown-menu li a', function() {
           var target = $(this).html();

           //Adds active class to selected item
           $(this).parents('.dropdown-menu').find('li').removeClass('active');
           $(this).parent('li').addClass('active');

           //Displays selected text on dropdown-toggle button
           $(this).parents('.dropdown-select').find('.dropdown-toggle').html(target);
        });

        div.append(label).append(select);
        $('#graphs').append(div);
    }

    function changeDataSet() {
        BuildGraph.dataset = this.value;
        setDimensions(dataviva.datasets[this.value].dimensions);
        updateViews();
    }

    function init() {
        for (dataset in dataviva.datasets) {
            $('#datasets').append( $('<option value="'+dataset+'">'+dataviva.dictionary[dataset]+'</option>'));
        }

        $('#datasets').change(changeDataSet);
    }
})();

$(document).ready(function () {
    dataviva.requireAttrs(['datasets'], function() {
        BuildGraph.init();
    });
});

<|endoftext|>


<commit_before>export default `
29-09-2017 - Added T20 set bonuses. (by Chizu)
25-09-2017 - Added rest of the talent modules - Hand of Doom, GoSac, GoSyn, Darkglare and Demonbolt. (by Chizu)
23-09-2017 - Added second talent row modules - Impending Doom, Improved Dreadstalkers and Implosion. (by Chizu)
19-09-2017 - Added first talent row modules - Shadowy Inspiration, Shadowflame and Demonic Calling. (by Chizu)
19-09-2017 - Added the Soul Shard tracker. (by Chizu)
15-09-2017 - Added Demonology Warlock support by @Chizu and @WOPR.
`;
<commit_msg>Changelog<commit_after>export default `
30-09-2017 - Added Kazzak's Final Curse and Wilfred's Sigil of Superior Summoning modules. (by Chizu)
29-09-2017 - Added T20 set bonuses. (by Chizu)
25-09-2017 - Added rest of the talent modules - Hand of Doom, GoSac, GoSyn, Darkglare and Demonbolt. (by Chizu)
23-09-2017 - Added second talent row modules - Impending Doom, Improved Dreadstalkers and Implosion. (by Chizu)
19-09-2017 - Added first talent row modules - Shadowy Inspiration, Shadowflame and Demonic Calling. (by Chizu)
19-09-2017 - Added the Soul Shard tracker. (by Chizu)
15-09-2017 - Added Demonology Warlock support by @Chizu and @WOPR.
`;
<|endoftext|>