API Docs for: 0.2.41
Show:

File: src/base/ui_object.js

// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

import $ from 'clappr-zepto'
import result from 'lodash.result'
import {uniqueId, DomRecycler} from './utils'
import BaseObject from './base_object'

const delegateEventSplitter = /^(\S+)\s*(.*)$/

/**
 * A base class to create ui object.
 * @class UIObject
 * @constructor
 * @extends BaseObject
 * @module base
 */
export default class UIObject extends BaseObject {
  /**
   * a unique id prefixed with `'c'`, `c1, c232`
   *
   * @property cid
   * @type String
   */
  /**
   * the dom element itself
   *
   * @property el
   * @type HTMLElement
   */
  /**
   * the dom element wrapped by `$`
   *
   * @property $el
   * @type HTMLElement
   */

  /**
   * gets the tag name for the ui component
   * @method tagName
   * @default div
   * @return {String} tag's name
   */
  get tagName() { return 'div' }
  /**
   * a literal object mapping element's events to methods
   * @property events
   * @type Object
   * @example
   *
   *```javascript
   *
   * class MyButton extends UIObject {
   *   constructor(options) {
   *     super(options)
   *     this.myId = 0
   *   }
   *   get events() { return { 'click': 'myClick' } }
   *   myClick(){ this.myId = 42 }
   * }
   *
   * // when you click on MyButton the method `myClick` will be called
   *```
   */
  get events() { return {} }
  /**
   * a literal object mapping attributes and values to the element
   * element's attribute name and the value the attribute value
   * @property attributes
   * @type Object
   * @example
   *
   *```javascript
   *
   * class MyButton extends UIObject {
   *    constructor(options) { super(options) }
   *    get attributes() { return { class: 'my-button'} }
   * }
   *
   * // MyButton.el.className will be 'my-button'
   * ```
   */
  get attributes() { return {} }

  /**
   * it builds an ui component by:
   *  * creating an id for the component `cid`
   *  * making sure the element is created `$el`
   *  * delegating all `events` to the element
   * @method constructor
   * @param {Object} options the options object
   */
  constructor(options) {
    super(options)
    this.cid = uniqueId('c')
    this._ensureElement()
    this.delegateEvents()
  }

  /**
   * selects within the component.
   * @method $
   * @param {String} selector a selector to find within the component.
   * @return {HTMLElement} an element, if it exists.
   * @example
   * ```javascript
   * fullScreenBarUIComponent.$('.button-full') //will return only `.button-full` within the component
   * ```
   */
  $(selector) {
    return this.$el.find(selector)
  }

  /**
   * render the component, usually attach it to a real existent `element`
   * @method render
   * @return {UIObject} itself
   */
  render() {
    return this
  }

  /**
   * removes the ui component from DOM
   * @method remove
   * @return {UIObject} itself
   */
  remove() {
    this.$el.remove()
    this.stopListening()
    this.undelegateEvents()
    return this
  }

  /**
   * set element to `el` and `$el`
   * @method setElement
   * @param {HTMLElement} element
   * @param {Boolean} delegate whether is delegate or not
   * @return {UIObject} itself
   */
  setElement(element, delegate) {
    if (this.$el) {this.undelegateEvents()}
    this.$el = element instanceof $ ? element : $(element)
    this.el = this.$el[0]
    if (delegate !== false) {this.delegateEvents()}
    return this
  }

  /**
   * delegates all the original `events` on `element` to its callbacks
   * @method delegateEvents
   * @param {Object} events
   * @return {UIObject} itself
   */
  delegateEvents(events) {
    if (!(events || (events = result(this, 'events')))) {return this}
    this.undelegateEvents()
    for (const key in events) {
      let method = events[key]
      if ((method && method.constructor !== Function)) {method = this[events[key]]}
      if (!method) {continue}

      const match = key.match(delegateEventSplitter)
      let eventName = match[1], selector = match[2]
      //method = _.bind(method, this)
      eventName += '.delegateEvents' + this.cid
      if (selector === '') {
        this.$el.on(eventName, method.bind(this))
      } else {
        this.$el.on(eventName, selector, method.bind(this))
      }
    }
    return this
  }

  /**
   * undelegats all the `events`
   * @method undelegateEvents
   * @return {UIObject} itself
   */
  undelegateEvents() {
    this.$el.off('.delegateEvents' + this.cid)
    return this
  }

  /**
   * ensures the creation of this ui component
   * @method _ensureElement
   * @private
   */
  _ensureElement() {
    if (!this.el) {
      const attrs = $.extend({}, result(this, 'attributes'))
      if (this.id) {attrs.id = result(this, 'id')}
      if (this.className) {attrs['class'] = result(this, 'className')}
      const $el = DomRecycler.create(result(this, 'tagName')).attr(attrs)
      this.setElement($el, false)
    } else {
      this.setElement(result(this, 'el'), false)
    }
  }
}