<commit_before>'use strict'
// var Base = require('../')
var define = Object.defineProperty

/**
 * @function createContextGetter
 * @memberOf Base#
 * @param  {string} key - Key to create the context getter for
 */
exports.createContextGetter = function (key) {
  var value = this.contextCandidate(key)
  if (!value) {
    key = '_' + key
    value = this[key]
    if (
      value &&
      this.hasOwnProperty(key) &&
      !value.hasOwnProperty('_Constructor') &&
      value._Constructor
    ) {
      for (let val_key in value) {
        value.createContextGetter(val_key)
      }
    }
  } else if (value) {
    let privateKey = '_' + key
    this[privateKey] = value
    for (let val_key in value) {
      value.createContextGetter(val_key)
    }
    define(this, key, {
      get () {
        var value = this[privateKey]
        // console.log('get it!', privateKey)
        if (value) {
          if (!this.hasOwnProperty(privateKey)) {
            value._context = this
          } else if (this._context) {
            value._context = this._context
          } else {
            value.clearContext()
          }
        }
        return value
      },
      set (val) {
        this[privateKey] = val
      },
      configurable: true
    })
  }
}

/**
 * @function resolveContext
 * resolves context sets
 * @memberOf Base#
 * @param {*} val set value to be resolved
 * @param {event} event current event
 * @param {base} context resolve this context
 * @param {boolean} alwaysreturn when set to true always returns resolved base
 * @type {base|undefined}
 */
exports.resolveContext = function (val, event, context) {
  context = context || this._context
  if (context._context) {
    context = context.resolveContext(void 0, event, context._context, true)
  }
  var iterator = this
  var path = [] // again same thing reuse path here not allways make
  var prevContext

  var proto = Object.getPrototypeOf(context)
  while (iterator && iterator !== context && iterator !== proto) {
    var key = iterator.key
    path.unshift(key)
    iterator = iterator._parent
  }

  var pathLength = path.length
  var pathLengthCompare = pathLength - 1

  for (let i = 0; i < pathLength; i++) {
    if (context) {
      prevContext = context
      context = context.setKeyInternal( // maybe event faster?
        path[i],
        i === pathLengthCompare ? val : {},
        context[path[i]],
        event,
        true
      )

      if (!context) {
        context = prevContext[path[i]]
      }
    }
  }
  // make this fan out to multiple contexts
  // this.clearContextUpSpesh()
  // this.clearContext()
  // this.clearContextUp() // fucker bitch //prob need to restore here :/
  // debugger
  return context
}
<commit_msg>added fix (dirty)<commit_after>'use strict'
// var Base = require('../')
var define = Object.defineProperty

/**
 * @function createContextGetter
 * @memberOf Base#
 * @param  {string} key - Key to create the context getter for
 */
exports.createContextGetter = function (key) {
  var value = this.contextCandidate(key)
  if (!value) {
    key = '_' + key
    value = this[key]
    if (
      value &&
      this.hasOwnProperty(key) &&
      !value.hasOwnProperty('_Constructor') &&
      value._Constructor
    ) {
      for (let val_key in value) {
        value.createContextGetter(val_key)
      }
    }
  } else if (value) {
    let privateKey = '_' + key
    this[privateKey] = value
    for (let val_key in value) {
      value.createContextGetter(val_key)
    }
    define(this, key, {
      get () {
        var value = this[privateKey]
        // console.log('get it!', privateKey)
        if (value) {
          if (!this.hasOwnProperty(privateKey)) {
            value._context = this
          } else if (this._context) {
            value._context = this._context
          } else {
            value.clearContext()
          }
        }
        return value
      },
      set (val) {
        this[privateKey] = val
      },
      configurable: true
    })
  }
}

/**
 * @function resolveContext
 * resolves context sets
 * @memberOf Base#
 * @param {*} val set value to be resolved
 * @param {event} event current event
 * @param {base} context resolve this context
 * @param {boolean} alwaysreturn when set to true always returns resolved base
 * @type {base|undefined}
 */
exports.resolveContext = function (val, event, context) {
  context = context || this._context
  if (context._context) {
    context = context.resolveContext(void 0, event, context._context, true)
  }
  var iterator = this
  var path = [] // again same thing reuse path here not allways make
  var prevContext

  // var proto = Object.getPrototypeOf(context)
  //
  while (iterator && iterator !== context && iterator.key && !(iterator instanceof context._Constructor)) {
    var key = iterator.key
    path.unshift(key)
    iterator = iterator._parent
  }

  var pathLength = path.length
  var pathLengthCompare = pathLength - 1

  for (let i = 0; i < pathLength; i++) {
    if (context) {
      prevContext = context
      context = context.setKeyInternal( // maybe event faster?
        path[i],
        i === pathLengthCompare ? val : {},
        context[path[i]],
        event,
        true
      )

      if (!context) {
        context = prevContext[path[i]]
      }
    }
  }
  // make this fan out to multiple contexts
  // this.clearContextUpSpesh()
  // this.clearContext()
  // this.clearContextUp() // fucker bitch //prob need to restore here :/
  // debugger
  return context
}
<|endoftext|>


<commit_before>"use strict";

const EventEmitter = require("events").EventEmitter;
const util = require("util");
const EventSource = require("eventsource");

/**
 * Represents a Marathon Event Bus Client
 * @constructor
 * @param {object} options - The option map object.
 */
function MarathonEventBusClient (options) {

    if (!(this instanceof MarathonEventBusClient)) {
        return new MarathonEventBusClient(options);
    }

    // Inherit from EventEmitter
    EventEmitter.call(this);

    let self = this;

    // See https://github.com/mesosphere/marathon/blob/master/src/main/scala/mesosphere/marathon/core/event/Events.scala
    self.allowedEventTypes = ["pod_created_event", "pod_updated_event", "pod_deleted_event", "scheduler_registered_event", "scheduler_reregistered_event", "scheduler_disconnected_event", "subscribe_event", "unsubscribe_event", "event_stream_attached", "event_stream_detached", "add_health_check_event", "remove_health_check_event", "failed_health_check_event", "health_status_changed_event", "unhealthy_task_kill_event", "group_change_success", "group_change_failed", "deployment_info", "deployment_success", "deployment_failed", "deployment_step_success", "deployment_step_failure", "app_terminated_event", "status_update_event", "instance_changed_event", "unknown_instance_terminated_event", "instance_health_changed_event", "framework_message_event"];

    self.options = {};

    // Marathon endpoint discovery
    self.options.marathonUrl = options.marathonUrl || "master.mesos";
    self.options.marathonPort = parseInt(options.marathonPort) || 8080;
    self.options.marathonProtocol = options.marathonProtocol || "http";
    self.options.marathonUri = options.marathonUri || "/v2/events";

    // Whether to emit a "connectionId" event
    self.options.enableConnectionEvent = options.enableConnectionEvent || false;

    // Set the used eventTypes
    if (options.eventTypes && options.eventTypes.length > 0) {
        self.options.eventTypes = [];
            options.eventTypes.forEach(function (eventType) {
            if (self.allowedEventTypes.indexOf(eventType) > -1) {
                self.options.eventTypes.push(eventType);
            }
        });
    } else {
        // Just use the deployment events by default, to avoid channel flooding
        self.options.eventTypes = ["deployment_info", "deployment_success", "deployment_failed"];
    }

    if (self.options.enableConnectionEvent) {
        self.options.eventTypes.push("connectionId");
    }

    function isFunction(obj) {
        return !!(obj && obj.constructor && obj.call && obj.apply);
    }

    // Event handlers
    self.options.eventHandlers = {};

    // Add custom event handlers if present
    if (options.handlers && Object.getOwnPropertyNames(options.handlers).length > 0) {
        Object.getOwnPropertyNames(options.handlers).forEach(function (handlerName) {
            const lcHandlerName = handlerName.toLowerCase();
            // Check if name is in defined event types, is a function
            if (self.options.eventTypes.indexOf(lcHandlerName) > -1 && isFunction(options.handlers[handlerName])) {
                self.options.eventHandlers[lcHandlerName] = function (name, data) {
                    // Emit according event
                    self.emit(name, data);
                    // Call custom event handler
                    options.handlers[handlerName](name, data);
                }
            }
        });
    }

    // Check if an event handlers has been defined for each event type
    self.options.eventTypes.forEach(function (eventType) {
        // If there's no event handler defined yet, set a default event handler which just prints the event name
        if (!self.options.eventHandlers.hasOwnProperty(eventType)) {
            self.options.eventHandlers[eventType] = function (name, data) {
                // Emit according event
                self.emit(name, data);
                // Log event name
                console.log("Default handler for " + name + ". Do nothing.");
            }
        }
    });

}

// Inherit from EventEmitter
util.inherits(MarathonEventBusClient, EventEmitter);

/**
 * Subscribes the MarathonEventBusClient to the Marathon Event Bus
 */
MarathonEventBusClient.prototype.subscribe = function () {

    let self = this,
        url = self.options.marathonProtocol + "://" + self.options.marathonUrl + ":" + self.options.marathonPort+self.options.marathonUri;

    // Create EventSource for Marathon /v2/events endpoint
    self.es = new EventSource(url);

    self.es.on("open", function () {
        self.emit("subscribed", { "timestamp": ((new Date().getTime())/1000) })
    });

    self.es.on("error", function (error) {
        self.emit("error", { "timestamp": ((new Date().getTime())/1000), error: error })
    });

    // Iterate over event types
    self.options.eventTypes.forEach(function (type) {
        // Add event listeners
        self.es.addEventListener(type, function (e) {
            self.options.eventHandlers[type](e.type, JSON.parse(e.data));
        });

    });

};

/**
 * Unsubscribes from the Marathon Event Bus
 */
MarathonEventBusClient.prototype.unsubscribe = function () {
    let self = this;
    self.emit("unsubscribed");
    setTimeout(function () {
        self.es.close();
    }, 100);
};

module.exports = MarathonEventBusClient;
<commit_msg>Allow unauthorized HTTPS requests<commit_after>"use strict";

const EventEmitter = require("events").EventEmitter;
const util = require("util");
const EventSource = require("eventsource");

/**
 * Represents a Marathon Event Bus Client
 * @constructor
 * @param {object} options - The option map object.
 */
function MarathonEventBusClient (options) {

    if (!(this instanceof MarathonEventBusClient)) {
        return new MarathonEventBusClient(options);
    }

    // Inherit from EventEmitter
    EventEmitter.call(this);

    let self = this;

    // See https://github.com/mesosphere/marathon/blob/master/src/main/scala/mesosphere/marathon/core/event/Events.scala
    self.allowedEventTypes = ["pod_created_event", "pod_updated_event", "pod_deleted_event", "scheduler_registered_event", "scheduler_reregistered_event", "scheduler_disconnected_event", "subscribe_event", "unsubscribe_event", "event_stream_attached", "event_stream_detached", "add_health_check_event", "remove_health_check_event", "failed_health_check_event", "health_status_changed_event", "unhealthy_task_kill_event", "group_change_success", "group_change_failed", "deployment_info", "deployment_success", "deployment_failed", "deployment_step_success", "deployment_step_failure", "app_terminated_event", "status_update_event", "instance_changed_event", "unknown_instance_terminated_event", "instance_health_changed_event", "framework_message_event"];

    self.options = {};

    // Marathon endpoint discovery
    self.options.marathonUrl = options.marathonUrl || "master.mesos";
    self.options.marathonPort = parseInt(options.marathonPort) || 8080;
    self.options.marathonProtocol = options.marathonProtocol || "http";
    self.options.marathonUri = options.marathonUri || "/v2/events";

    // Whether to emit a "connectionId" event
    self.options.enableConnectionEvent = options.enableConnectionEvent || false;

    // Set the used eventTypes
    if (options.eventTypes && options.eventTypes.length > 0) {
        self.options.eventTypes = [];
            options.eventTypes.forEach(function (eventType) {
            if (self.allowedEventTypes.indexOf(eventType) > -1) {
                self.options.eventTypes.push(eventType);
            }
        });
    } else {
        // Just use the deployment events by default, to avoid channel flooding
        self.options.eventTypes = ["deployment_info", "deployment_success", "deployment_failed"];
    }

    if (self.options.enableConnectionEvent) {
        self.options.eventTypes.push("connectionId");
    }

    function isFunction(obj) {
        return !!(obj && obj.constructor && obj.call && obj.apply);
    }

    // Event handlers
    self.options.eventHandlers = {};

    // Add custom event handlers if present
    if (options.handlers && Object.getOwnPropertyNames(options.handlers).length > 0) {
        Object.getOwnPropertyNames(options.handlers).forEach(function (handlerName) {
            const lcHandlerName = handlerName.toLowerCase();
            // Check if name is in defined event types, is a function
            if (self.options.eventTypes.indexOf(lcHandlerName) > -1 && isFunction(options.handlers[handlerName])) {
                self.options.eventHandlers[lcHandlerName] = function (name, data) {
                    // Emit according event
                    self.emit(name, data);
                    // Call custom event handler
                    options.handlers[handlerName](name, data);
                }
            }
        });
    }

    // Check if an event handlers has been defined for each event type
    self.options.eventTypes.forEach(function (eventType) {
        // If there's no event handler defined yet, set a default event handler which just prints the event name
        if (!self.options.eventHandlers.hasOwnProperty(eventType)) {
            self.options.eventHandlers[eventType] = function (name, data) {
                // Emit according event
                self.emit(name, data);
                // Log event name
                console.log("Default handler for " + name + ". Do nothing.");
            }
        }
    });

}

// Inherit from EventEmitter
util.inherits(MarathonEventBusClient, EventEmitter);

/**
 * Subscribes the MarathonEventBusClient to the Marathon Event Bus
 */
MarathonEventBusClient.prototype.subscribe = function () {

    let self = this,
        url = self.options.marathonProtocol + "://" + self.options.marathonUrl + ":" + self.options.marathonPort+self.options.marathonUri;

    // Allow unauthorized HTTPS requests
    let eventSourceInitDict = { rejectUnauthorized: false };

    // Create EventSource for Marathon /v2/events endpoint
    self.es = new EventSource(url, eventSourceInitDict);

    self.es.on("open", function () {
        self.emit("subscribed", { "timestamp": ((new Date().getTime())/1000) })
    });

    self.es.on("error", function (error) {
        self.emit("error", { "timestamp": ((new Date().getTime())/1000), error: error })
    });

    // Iterate over event types
    self.options.eventTypes.forEach(function (type) {
        // Add event listeners
        self.es.addEventListener(type, function (e) {
            self.options.eventHandlers[type](e.type, JSON.parse(e.data));
        });

    });

};

/**
 * Unsubscribes from the Marathon Event Bus
 */
MarathonEventBusClient.prototype.unsubscribe = function () {
    let self = this;
    self.emit("unsubscribed");
    setTimeout(function () {
        self.es.close();
    }, 100);
};

module.exports = MarathonEventBusClient;
<|endoftext|>


<commit_before>/**
 * Attach an event to a DOM node
 * @param { String } name - event name
 * @param { Function } handler - event callback
 * @param { Object } dom - dom node
 * @param { Tag } tag - tag instance
 */
function setEventHandler(name, handler, dom, tag) {

  dom[name] = function(e) {

    var ptag = tag._parent,
      item = tag._item,
      el

    if (!item)
      while (ptag && !item) {
        item = ptag._item
        ptag = ptag._parent
      }

    // cross browser event fix
    e = e || window.event

    // override the event properties
    if (isWritable(e, 'currentTarget')) e.currentTarget = dom
    if (isWritable(e, 'target')) e.target = e.srcElement
    if (isWritable(e, 'which')) e.which = e.charCode || e.keyCode

    e.item = item

    // prevent default behaviour (by default)
    if (handler.call(tag, e) !== true && !/radio|check/.test(dom.type)) {
      if (e.preventDefault) e.preventDefault()
      e.returnValue = false
    }

    if (!e.preventUpdate) {
      el = item ? getImmediateCustomParentTag(ptag) : tag
      el.update()
    }

  }

}

/**
 * Insert a DOM node replacing another one (used by if- attribute)
 * @param   { Object } root - parent node
 * @param   { Object } node - node replaced
 * @param   { Object } before - node added
 */
function insertTo(root, node, before) {
  if (!root) return
  root.insertBefore(before, node)
  root.removeChild(node)
}

/**
 * Update the expressions in a Tag instance
 * @param   { Array } expressions - expression that must be re evaluated
 * @param   { Tag } tag - tag instance
 */
function update(expressions, tag) {

  each(expressions, function(expr, i) {

    var dom = expr.dom,
      attrName = expr.attr,
      value = tmpl(expr.expr, tag),
      parent = expr.dom.parentNode

    if (expr.bool)
      value = value ? attrName : false
    else if (value == null)
      value = ''

    // leave out riot- prefixes from strings inside textarea
    // fix #815: any value -> string
    if (parent && parent.tagName == 'TEXTAREA') {
      value = ('' + value).replace(/riot-/g, '')
      // change textarea's value
      parent.value = value
    }

    // no change
    if (expr.value === value) return
    expr.value = value

    // text node
    if (!attrName) {
      dom.nodeValue = '' + value    // #815 related
      return
    }

    // remove original attribute
    remAttr(dom, attrName)
    // event handler
    if (isFunction(value)) {
      setEventHandler(attrName, value, dom, tag)

    // if- conditional
    } else if (attrName == 'if') {
      var stub = expr.stub,
        add = function() { insertTo(stub.parentNode, stub, dom) },
        remove = function() { insertTo(dom.parentNode, dom, stub) }

      // add to DOM
      if (value) {
        if (stub) {
          add()
          dom.inStub = false
          // avoid to trigger the mount event if the tags is not visible yet
          // maybe we can optimize this avoiding to mount the tag at all
          if (!isInStub(dom)) {
            walk(dom, function(el) {
              if (el._tag && !el._tag.isMounted)
                el._tag.isMounted = !!el._tag.trigger('mount')
            })
          }
        }
      // remove from DOM
      } else {
        stub = expr.stub = stub || document.createTextNode('')
        // if the parentNode is defined we can easily replace the tag
        if (dom.parentNode)
          remove()
        // otherwise we need to wait the updated event
        else (tag.parent || tag).one('updated', remove)

        dom.inStub = true
      }
    // show / hide
    } else if (/^(show|hide)$/.test(attrName)) {
      if (attrName == 'hide') value = !value
      dom.style.display = value ? '' : 'none'

    // field value
    } else if (attrName == 'value') {
      dom.value = value

    //
    } else if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
      if (value)
        setAttr(dom, attrName.slice(RIOT_PREFIX.length), value)

    // <select> <option selected={true}> </select>
    } else if (parent.nodeName == 'SELECT' && attrName == 'selected' && value) {
      parent.value = dom.value
    } else {
      if (expr.bool) {
        dom[attrName] = value
        if (!value) return
      }

      if (value === 0 || value && typeof value !== T_OBJECT)
        setAttr(dom, attrName, value)

    }

  })

}
<commit_msg>bugfix (firefox): change selected attr for and OPTION in a SELECT<commit_after>/**
 * Attach an event to a DOM node
 * @param { String } name - event name
 * @param { Function } handler - event callback
 * @param { Object } dom - dom node
 * @param { Tag } tag - tag instance
 */
function setEventHandler(name, handler, dom, tag) {

  dom[name] = function(e) {

    var ptag = tag._parent,
      item = tag._item,
      el

    if (!item)
      while (ptag && !item) {
        item = ptag._item
        ptag = ptag._parent
      }

    // cross browser event fix
    e = e || window.event

    // override the event properties
    if (isWritable(e, 'currentTarget')) e.currentTarget = dom
    if (isWritable(e, 'target')) e.target = e.srcElement
    if (isWritable(e, 'which')) e.which = e.charCode || e.keyCode

    e.item = item

    // prevent default behaviour (by default)
    if (handler.call(tag, e) !== true && !/radio|check/.test(dom.type)) {
      if (e.preventDefault) e.preventDefault()
      e.returnValue = false
    }

    if (!e.preventUpdate) {
      el = item ? getImmediateCustomParentTag(ptag) : tag
      el.update()
    }

  }

}

/**
 * Insert a DOM node replacing another one (used by if- attribute)
 * @param   { Object } root - parent node
 * @param   { Object } node - node replaced
 * @param   { Object } before - node added
 */
function insertTo(root, node, before) {
  if (!root) return
  root.insertBefore(before, node)
  root.removeChild(node)
}

/**
 * Update the expressions in a Tag instance
 * @param   { Array } expressions - expression that must be re evaluated
 * @param   { Tag } tag - tag instance
 */
function update(expressions, tag) {

  each(expressions, function(expr, i) {

    var dom = expr.dom,
      attrName = expr.attr,
      value = tmpl(expr.expr, tag),
      parent = expr.dom.parentNode

    if (expr.bool)
      value = value ? attrName : false
    else if (value == null)
      value = ''

    // leave out riot- prefixes from strings inside textarea
    // fix #815: any value -> string
    if (parent && parent.tagName == 'TEXTAREA') {
      value = ('' + value).replace(/riot-/g, '')
      // change textarea's value
      parent.value = value
    }

    // no change
    if (expr.value === value) return
    expr.value = value

    // text node
    if (!attrName) {
      dom.nodeValue = '' + value    // #815 related
      return
    }

    // remove original attribute
    remAttr(dom, attrName)
    // event handler
    if (isFunction(value)) {
      setEventHandler(attrName, value, dom, tag)

    // if- conditional
    } else if (attrName == 'if') {
      var stub = expr.stub,
        add = function() { insertTo(stub.parentNode, stub, dom) },
        remove = function() { insertTo(dom.parentNode, dom, stub) }

      // add to DOM
      if (value) {
        if (stub) {
          add()
          dom.inStub = false
          // avoid to trigger the mount event if the tags is not visible yet
          // maybe we can optimize this avoiding to mount the tag at all
          if (!isInStub(dom)) {
            walk(dom, function(el) {
              if (el._tag && !el._tag.isMounted)
                el._tag.isMounted = !!el._tag.trigger('mount')
            })
          }
        }
      // remove from DOM
      } else {
        stub = expr.stub = stub || document.createTextNode('')
        // if the parentNode is defined we can easily replace the tag
        if (dom.parentNode)
          remove()
        // otherwise we need to wait the updated event
        else (tag.parent || tag).one('updated', remove)

        dom.inStub = true
      }
    // show / hide
    } else if (/^(show|hide)$/.test(attrName)) {
      if (attrName == 'hide') value = !value
      dom.style.display = value ? '' : 'none'

    // field value
    } else if (attrName == 'value') {
      dom.value = value

    //
    } else if (startsWith(attrName, RIOT_PREFIX) && attrName != RIOT_TAG) {
      if (value)
        setAttr(dom, attrName.slice(RIOT_PREFIX.length), value)

    } else {
      // <select> <option selected={true}> </select>
      if (parent && /^(SELECT|OPTGROUP)$/.test(parent.nodeName) && attrName == 'selected' && value)
        parent.value = dom.value

      if (expr.bool) {
        dom[attrName] = value
        if (!value) return
      }

      if (value === 0 || value && typeof value !== T_OBJECT)
        setAttr(dom, attrName, value)

    }

  })

}
<|endoftext|>


<commit_before>test( "Popcorn Subtitle Plugin", function() {

  var popped = Popcorn( "#video" ),
      popped2 = Popcorn( "#video2" ),
      expects = 12,
      count = 0,
      subTop = 9001,
      subLeft = 9001,
      subtitlediv,
      subtitle2div;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  ok( "subtitle" in popped, "subtitle is a method of the popped instance" );
  plus();

  popped.subtitle({
      start: 0,
      end: 2,
      text: "this is the first subtitle of 2011"
    })
  .subtitle({
      start: 2,
      end: 4,
      text: "this is the second subtitle of 2011"
    })
  .subtitle({
      start: 5,
      end: 7,
      text: "this is the third subtitle of 2011"
    } )
    .volume( 0 )
    .play();

  subtitlediv = popped.getTrackEvent( popped.getLastTrackEventId() ).container;

  popped.subtitle({
    start: 7,
    end: 9,
    text: "instance one test"
  });

  popped2.subtitle({
      start: 7,
      end: 9,
      text: "instance two test"
    })
    .volume( 0 )
    .play().pause();

  subtitle2div = popped2.getTrackEvent( popped2.getLastTrackEventId() ).container;

  popped.exec( 1, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 0 ].innerHTML, "this is the first subtitle of 2011", "subtitle displaying correct information" );
    plus();

    // capturing location now, to check against later,
    // a subtitle must be displayed to get valid data
    // which is why we do this in exec
    subLeft = subtitlediv.style.left;
    subTop  = subtitlediv.style.top;

    // changing position
    popped.media.style.position = "absolute";
    popped.media.style.left = "400px";
    popped.media.style.top = "600px";
    popped.media.play();

  });

  popped.exec( 3, function() {

    popped.media.pause();

    // check position of subttile that should of moved with video,
    // a subtitle must be displayed to get valid data
    ok( subtitlediv.style.left !== subLeft, "subtitle's left position has changed" );
    plus();
    ok( subtitlediv.style.top !== subTop, "subtitle's top position has changed" );
    plus();

    // we know values have changed, but how accurate are they?
    // check values against the video's values
    // we need four checks because if we just check against video's position,
    // and video's position hasn't updated either, we'll pass when we should fail
    equals( subtitlediv.style.left, popped.position().left + "px", "subtitle left position moved" );
    plus();
    ok( Popcorn.position( subtitlediv ).top > popped.position().top, "subtitle top position moved" );
    plus();

    equals( subtitlediv.children[ 1 ].innerHTML, "this is the second subtitle of 2011", "subtitle displaying correct information" );
    plus();

    popped.media.play();

  });

  popped.exec( 4, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 1 ].innerHTML, "", "subtitle is clear" );
    plus();

    popped.media.play();

  });

  popped.exec( 8, function() {
    popped.pause();
    popped2.currentTime( 8 ).play();
  });

  popped2.exec( 8, function() {
    popped2.media.pause();

    equals( subtitlediv.children[ 3 ].innerHTML, "instance one test", "subtitle displaying correct information" );
    plus();
    equals( subtitle2div.children[ 0 ].innerHTML, "instance two test", "subtitle displaying correct information" );
    plus();

    popped.media.play();
  });

  popped.exec( 10, function() {
    ok( document.getElementById( "subtitle-0" ).style.display === "none" &&
        document.getElementById( "subtitle-1" ).style.display === "none" &&
        document.getElementById( "subtitle-2" ).style.display === "none", "All subtitles are no longer visible" );
    plus();

    popped.pause().removeTrackEvent( popped.data.trackEvents.byStart[ 6 ]._id );

    ok( !document.getElementById( "subtitle-2" ), "removed subtitle div was properly destroyed"  );
    plus();
  });
});

test( "subtitle data tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 1,
      count = 0,
      container = document.getElementById( "sub-content" );

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    target: "sub-content"
  });

  popped.pause( 0 );

  equals( container.children[ 0 ].innerHTML, "", "subtitle with no text defaults to an empty string" );
  plus();
});

test( "subtitle container creation tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 3,
      containerAtLoad = document.getElementById("divThatDoesntExist"),
      containerAfterParse,
      count = 0;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    text: "My Text",
    target: "divThatDoesntExist"
  });

  popped.pause( 0 );

  containerAfterParse = document.getElementById("divThatDoesntExist");

  equals( !!containerAtLoad, false, "Container doesn't exist initially" );
  plus();
  equals( !!containerAfterParse, true, "Container exists now" );
  plus();
  equals( containerAfterParse.children[ 0 ].innerHTML, "My Text", "Subtitle displayed in created container" );
  plus();

  // Cleanup
  containerAfterParse.parentNode.removeChild( containerAfterParse );
});<commit_msg>Extended timeout for subtitle unit tests [#871]<commit_after>test( "Popcorn Subtitle Plugin", function() {

  var popped = Popcorn( "#video" ),
      popped2 = Popcorn( "#video2" ),
      expects = 12,
      count = 0,
      subTop = 9001,
      subLeft = 9001,
      subtitlediv,
      subtitle2div;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 15000 );

  ok( "subtitle" in popped, "subtitle is a method of the popped instance" );
  plus();

  popped.subtitle({
      start: 0,
      end: 2,
      text: "this is the first subtitle of 2011"
    })
  .subtitle({
      start: 2,
      end: 4,
      text: "this is the second subtitle of 2011"
    })
  .subtitle({
      start: 5,
      end: 7,
      text: "this is the third subtitle of 2011"
    } )
    .volume( 0 )
    .play();

  subtitlediv = popped.getTrackEvent( popped.getLastTrackEventId() ).container;

  popped.subtitle({
    start: 7,
    end: 9,
    text: "instance one test"
  });

  popped2.subtitle({
      start: 7,
      end: 9,
      text: "instance two test"
    })
    .volume( 0 )
    .play().pause();

  subtitle2div = popped2.getTrackEvent( popped2.getLastTrackEventId() ).container;

  popped.exec( 1, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 0 ].innerHTML, "this is the first subtitle of 2011", "subtitle displaying correct information" );
    plus();

    // capturing location now, to check against later,
    // a subtitle must be displayed to get valid data
    // which is why we do this in exec
    subLeft = subtitlediv.style.left;
    subTop  = subtitlediv.style.top;

    // changing position
    popped.media.style.position = "absolute";
    popped.media.style.left = "400px";
    popped.media.style.top = "600px";
    popped.media.play();

  });

  popped.exec( 3, function() {

    popped.media.pause();

    // check position of subttile that should of moved with video,
    // a subtitle must be displayed to get valid data
    ok( subtitlediv.style.left !== subLeft, "subtitle's left position has changed" );
    plus();
    ok( subtitlediv.style.top !== subTop, "subtitle's top position has changed" );
    plus();

    // we know values have changed, but how accurate are they?
    // check values against the video's values
    // we need four checks because if we just check against video's position,
    // and video's position hasn't updated either, we'll pass when we should fail
    equals( subtitlediv.style.left, popped.position().left + "px", "subtitle left position moved" );
    plus();
    ok( Popcorn.position( subtitlediv ).top > popped.position().top, "subtitle top position moved" );
    plus();

    equals( subtitlediv.children[ 1 ].innerHTML, "this is the second subtitle of 2011", "subtitle displaying correct information" );
    plus();

    popped.media.play();

  });

  popped.exec( 4, function() {

    popped.media.pause();
    equals( subtitlediv.children[ 1 ].innerHTML, "", "subtitle is clear" );
    plus();

    popped.media.play();

  });

  popped.exec( 8, function() {
    popped.pause();
    popped2.currentTime( 8 ).play();
  });

  popped2.exec( 8, function() {
    popped2.media.pause();

    equals( subtitlediv.children[ 3 ].innerHTML, "instance one test", "subtitle displaying correct information" );
    plus();
    equals( subtitle2div.children[ 0 ].innerHTML, "instance two test", "subtitle displaying correct information" );
    plus();

    popped.media.play();
  });

  popped.exec( 10, function() {
    ok( document.getElementById( "subtitle-0" ).style.display === "none" &&
        document.getElementById( "subtitle-1" ).style.display === "none" &&
        document.getElementById( "subtitle-2" ).style.display === "none", "All subtitles are no longer visible" );
    plus();

    popped.pause().removeTrackEvent( popped.data.trackEvents.byStart[ 6 ]._id );

    ok( !document.getElementById( "subtitle-2" ), "removed subtitle div was properly destroyed"  );
    plus();
  });
});

test( "subtitle data tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 1,
      count = 0,
      container = document.getElementById( "sub-content" );

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    target: "sub-content"
  });

  popped.pause( 0 );

  equals( container.children[ 0 ].innerHTML, "", "subtitle with no text defaults to an empty string" );
  plus();
});

test( "subtitle container creation tests", function() {

  var popped = Popcorn( "#video" ),
      expects = 3,
      containerAtLoad = document.getElementById("divThatDoesntExist"),
      containerAfterParse,
      count = 0;

  expect( expects );

  function plus() {
    if ( ++count === expects ) {
      start();
    }
  }

  stop( 12000 );

  popped.subtitle({
    start: 0,
    end: 10,
    text: "My Text",
    target: "divThatDoesntExist"
  });

  popped.pause( 0 );

  containerAfterParse = document.getElementById("divThatDoesntExist");

  equals( !!containerAtLoad, false, "Container doesn't exist initially" );
  plus();
  equals( !!containerAfterParse, true, "Container exists now" );
  plus();
  equals( containerAfterParse.children[ 0 ].innerHTML, "My Text", "Subtitle displayed in created container" );
  plus();

  // Cleanup
  containerAfterParse.parentNode.removeChild( containerAfterParse );
});<|endoftext|>


<commit_before>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.cea.Mp4CeaParser');

goog.require('goog.asserts');
goog.require('shaka.cea.ICeaParser');
goog.require('shaka.cea.SeiProcessor');
goog.require('shaka.util.DataViewReader');
goog.require('shaka.util.Error');
goog.require('shaka.util.Mp4Parser');
goog.require('shaka.util.Mp4BoxParsers');

/**
 * MPEG4 stream parser used for extracting 708 closed captions data.
 * @implements {shaka.cea.ICeaParser}
 */
shaka.cea.Mp4CeaParser = class {
  /** */
  constructor() {
    /**
     * SEI data processor.
     * @private
     * @const {!shaka.cea.SeiProcessor}
     */
    this.seiProcessor_ = new shaka.cea.SeiProcessor();

    /**
     * Map of track id to corresponding timescale.
     * @private {!Map<number, number>}
     */
    this.trackIdToTimescale_ = new Map();

    /**
     * Default sample duration, as specified by the TREX box.
     * @private {number}
     */
    this.defaultSampleDuration_ = 0;

    /**
     * Default sample size, as specified by the TREX box.
     * @private {number}
     */
    this.defaultSampleSize_ = 0;
  }

  /**
   * Parses the init segment. Gets Default Sample Duration and Size from the
   * TREX box, and constructs a map of Track IDs to timescales. Each TRAK box
   * contains a track header (TKHD) containing track ID, and a media header box
   * (MDHD) containing the timescale for the track
   * @override
   */
  init(initSegment) {
    const Mp4Parser = shaka.util.Mp4Parser;
    const trackIds = [];
    const timescales = [];

    new Mp4Parser()
        .box('moov', Mp4Parser.children)
        .box('mvex', Mp4Parser.children)
        .fullBox('trex', (box) => {
          const parsedTREXBox = shaka.util.Mp4BoxParsers.parseTREX(
              box.reader);

          this.defaultSampleDuration_ = parsedTREXBox.defaultSampleDuration;
          this.defaultSampleSize_ = parsedTREXBox.defaultSampleSize;
        })
        .box('trak', Mp4Parser.children)
        .fullBox('tkhd', (box) => {
          goog.asserts.assert(
              box.version != null,
              'TKHD is a full box and should have a valid version.');
          const parsedTKHDBox = shaka.util.Mp4BoxParsers.parseTKHD(
              box.reader, box.version);
          trackIds.push(parsedTKHDBox.trackId);
        })
        .box('mdia', Mp4Parser.children)
        .fullBox('mdhd', (box) => {
          goog.asserts.assert(
              box.version != null,
              'MDHD is a full box and should have a valid version.');
          const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
              box.reader, box.version);
          timescales.push(parsedMDHDBox.timescale);
        })
        .parse(initSegment, /* partialOkay= */ true);

    // At least one track should exist, and each track should have a
    // corresponding Id in TKHD box, and timescale in its MDHD box
    if (!trackIds.length|| !timescales.length ||
      trackIds.length != timescales.length) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.TEXT,
          shaka.util.Error.Code.INVALID_MP4_CEA);
    }

    // Populate the map from track Id to timescale
    trackIds.forEach((trackId, idx) => {
      this.trackIdToTimescale_.set(trackId, timescales[idx]);
    });
  }

  /**
   * Parses each video segment. In fragmented MP4s, MOOF and MDAT come in
   * pairs. The following logic gets the necessary info from MOOFs to parse
   * MDATs (base media decode time, sample sizes/offsets/durations, etc),
   * and then parses the MDAT boxes for CEA-708 packets using this information.
   * CEA-708 packets are returned in the callback.
   * @override
   */
  parse(mediaSegment) {
    const Mp4Parser = shaka.util.Mp4Parser;

    /** @type {!Array<!shaka.cea.ICeaParser.CaptionPacket>} **/
    const captionPackets = [];

    // Fields that are found in MOOF boxes
    let defaultSampleDuration = this.defaultSampleDuration_;
    let defaultSampleSize = this.defaultSampleSize_;
    let sampleData = [];
    let baseMediaDecodeTime = null;
    let timescale = shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE;

    new Mp4Parser()
        .box('moof', Mp4Parser.children)
        .box('traf', Mp4Parser.children)
        .fullBox('trun', (box) => {
          goog.asserts.assert(
              box.version != null && box.flags!=null,
              'TRUN is a full box and should have a valid version & flags.');

          const parsedTRUN = shaka.util.Mp4BoxParsers.parseTRUN(
              box.reader, box.version, box.flags);

          sampleData = parsedTRUN.sampleData;
        })

        .fullBox('tfhd', (box) => {
          goog.asserts.assert(
              box.flags != null,
              'TFHD is a full box and should have valid flags.');

          const parsedTFHD = shaka.util.Mp4BoxParsers.parseTFHD(
              box.reader, box.flags);

          // If specified, defaultSampleDuration and defaultSampleSize
          // override the ones specified in the TREX box
          defaultSampleDuration = parsedTFHD.defaultSampleDuration ||
              this.defaultSampleDuration_;

          defaultSampleSize = parsedTFHD.defaultSampleSize ||
              this.defaultSampleSize_;

          const trackId = parsedTFHD.trackId;

          // Get the timescale from the track Id
          if (this.trackIdToTimescale_.has(trackId)) {
            timescale = this.trackIdToTimescale_.get(trackId);
          }
        })

        .fullBox('tfdt', (box) => {
          goog.asserts.assert(
              box.version != null,
              'TFDT is a full box and should have a valid version.');

          const parsedTFDT = shaka.util.Mp4BoxParsers.parseTFDT(
              box.reader, box.version);

          baseMediaDecodeTime = parsedTFDT.baseMediaDecodeTime;
        })
        .box('mdat', (box) => {
          if (baseMediaDecodeTime === null) {
            // This field should have been populated by
            // the Base Media Decode time in the TFDT box
            throw new shaka.util.Error(
                shaka.util.Error.Severity.CRITICAL,
                shaka.util.Error.Category.TEXT,
                shaka.util.Error.Code.INVALID_MP4_CEA);
          }
          this.parseMdat_(box.reader, baseMediaDecodeTime, timescale,
              defaultSampleDuration, defaultSampleSize, sampleData,
              captionPackets);
        })
        .parse(mediaSegment, /* partialOkay= */ false);

    return captionPackets;
  }

  /**
   * Parse MDAT box.
   * @param {!shaka.util.DataViewReader} reader
   * @param {number} time
   * @param {number} timescale
   * @param {number} defaultSampleDuration
   * @param {number} defaultSampleSize
   * @param {!Array<shaka.util.ParsedTRUNSample>} sampleData
   * @param {!Array<!shaka.cea.ICeaParser.CaptionPacket>} captionPackets
   * @private
   */
  parseMdat_(reader, time, timescale, defaultSampleDuration,
      defaultSampleSize, sampleData, captionPackets) {
    let sampleIndex = 0;

    // The fields in each ParsedTRUNSample contained in the sampleData
    // array are nullable. In the case of sample data and sample duration,
    // we use the defaults provided by the TREX/TFHD boxes. For sample
    // composition time offset, we default to 0.
    let sampleSize = defaultSampleSize;

    if (sampleData.length) {
      sampleSize = sampleData[0].sampleSize || defaultSampleSize;
    }

    while (reader.hasMoreData()) {
      const naluSize = reader.readUint32();
      const naluType = reader.readUint8() & 0x1F;
      if (naluType == shaka.cea.ICeaParser.NALU_TYPE_SEI) {
        let timeOffset = 0;

        if (sampleData.length > sampleIndex) {
          timeOffset = sampleData[sampleIndex].sampleCompositionTimeOffset || 0;
        }

        const pts = (time + timeOffset)/timescale;
        for (const packet of this.seiProcessor_
            .process(reader.readBytes(naluSize - 1))) {
          captionPackets.push({
            packet,
            pts,
          });
        }
      } else {
        reader.skip(naluSize - 1);
      }
      sampleSize -= (naluSize + 4);
      if (sampleSize == 0) {
        if (sampleData.length > sampleIndex) {
          time += sampleData[sampleIndex].sampleDuration ||
              defaultSampleDuration;
        } else {
          time += defaultSampleDuration;
        }

        sampleIndex++;

        if (sampleData.length > sampleIndex) {
          sampleSize = sampleData[sampleIndex].sampleSize || defaultSampleSize;
        } else {
          sampleSize = defaultSampleSize;
        }
      }
    }
  }
};
<commit_msg>fix(cea): make a more robust CEA MP4 parser (#3965)<commit_after>/*! @license
 * Shaka Player
 * Copyright 2016 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

goog.provide('shaka.cea.Mp4CeaParser');

goog.require('goog.asserts');
goog.require('shaka.cea.ICeaParser');
goog.require('shaka.cea.SeiProcessor');
goog.require('shaka.util.DataViewReader');
goog.require('shaka.util.Error');
goog.require('shaka.util.Mp4Parser');
goog.require('shaka.util.Mp4BoxParsers');

/**
 * MPEG4 stream parser used for extracting 708 closed captions data.
 * @implements {shaka.cea.ICeaParser}
 */
shaka.cea.Mp4CeaParser = class {
  /** */
  constructor() {
    /**
     * SEI data processor.
     * @private
     * @const {!shaka.cea.SeiProcessor}
     */
    this.seiProcessor_ = new shaka.cea.SeiProcessor();

    /**
     * Map of track id to corresponding timescale.
     * @private {!Map<number, number>}
     */
    this.trackIdToTimescale_ = new Map();

    /**
     * Default sample duration, as specified by the TREX box.
     * @private {number}
     */
    this.defaultSampleDuration_ = 0;

    /**
     * Default sample size, as specified by the TREX box.
     * @private {number}
     */
    this.defaultSampleSize_ = 0;
  }

  /**
   * Parses the init segment. Gets Default Sample Duration and Size from the
   * TREX box, and constructs a map of Track IDs to timescales. Each TRAK box
   * contains a track header (TKHD) containing track ID, and a media header box
   * (MDHD) containing the timescale for the track
   * @override
   */
  init(initSegment) {
    const Mp4Parser = shaka.util.Mp4Parser;
    const trackIds = [];
    const timescales = [];

    new Mp4Parser()
        .box('moov', Mp4Parser.children)
        .box('mvex', Mp4Parser.children)
        .fullBox('trex', (box) => {
          const parsedTREXBox = shaka.util.Mp4BoxParsers.parseTREX(
              box.reader);

          this.defaultSampleDuration_ = parsedTREXBox.defaultSampleDuration;
          this.defaultSampleSize_ = parsedTREXBox.defaultSampleSize;
        })
        .box('trak', Mp4Parser.children)
        .fullBox('tkhd', (box) => {
          goog.asserts.assert(
              box.version != null,
              'TKHD is a full box and should have a valid version.');
          const parsedTKHDBox = shaka.util.Mp4BoxParsers.parseTKHD(
              box.reader, box.version);
          trackIds.push(parsedTKHDBox.trackId);
        })
        .box('mdia', Mp4Parser.children)
        .fullBox('mdhd', (box) => {
          goog.asserts.assert(
              box.version != null,
              'MDHD is a full box and should have a valid version.');
          const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
              box.reader, box.version);
          timescales.push(parsedMDHDBox.timescale);
        })
        .parse(initSegment, /* partialOkay= */ true);

    // At least one track should exist, and each track should have a
    // corresponding Id in TKHD box, and timescale in its MDHD box
    if (!trackIds.length|| !timescales.length ||
      trackIds.length != timescales.length) {
      throw new shaka.util.Error(
          shaka.util.Error.Severity.CRITICAL,
          shaka.util.Error.Category.TEXT,
          shaka.util.Error.Code.INVALID_MP4_CEA);
    }

    // Populate the map from track Id to timescale
    trackIds.forEach((trackId, idx) => {
      this.trackIdToTimescale_.set(trackId, timescales[idx]);
    });
  }

  /**
   * Parses each video segment. In fragmented MP4s, MOOF and MDAT come in
   * pairs. The following logic gets the necessary info from MOOFs to parse
   * MDATs (base media decode time, sample sizes/offsets/durations, etc),
   * and then parses the MDAT boxes for CEA-708 packets using this information.
   * CEA-708 packets are returned in the callback.
   * @override
   */
  parse(mediaSegment) {
    const Mp4Parser = shaka.util.Mp4Parser;

    /** @type {!Array<!shaka.cea.ICeaParser.CaptionPacket>} **/
    const captionPackets = [];

    // Fields that are found in MOOF boxes
    let defaultSampleDuration = this.defaultSampleDuration_;
    let defaultSampleSize = this.defaultSampleSize_;
    let sampleData = [];
    let baseMediaDecodeTime = null;
    let timescale = shaka.cea.ICeaParser.DEFAULT_TIMESCALE_VALUE;

    new Mp4Parser()
        .box('moof', Mp4Parser.children)
        .box('traf', Mp4Parser.children)
        .fullBox('trun', (box) => {
          goog.asserts.assert(
              box.version != null && box.flags!=null,
              'TRUN is a full box and should have a valid version & flags.');

          const parsedTRUN = shaka.util.Mp4BoxParsers.parseTRUN(
              box.reader, box.version, box.flags);

          sampleData = parsedTRUN.sampleData;
        })

        .fullBox('tfhd', (box) => {
          goog.asserts.assert(
              box.flags != null,
              'TFHD is a full box and should have valid flags.');

          const parsedTFHD = shaka.util.Mp4BoxParsers.parseTFHD(
              box.reader, box.flags);

          // If specified, defaultSampleDuration and defaultSampleSize
          // override the ones specified in the TREX box
          defaultSampleDuration = parsedTFHD.defaultSampleDuration ||
              this.defaultSampleDuration_;

          defaultSampleSize = parsedTFHD.defaultSampleSize ||
              this.defaultSampleSize_;

          const trackId = parsedTFHD.trackId;

          // Get the timescale from the track Id
          if (this.trackIdToTimescale_.has(trackId)) {
            timescale = this.trackIdToTimescale_.get(trackId);
          }
        })

        .fullBox('tfdt', (box) => {
          goog.asserts.assert(
              box.version != null,
              'TFDT is a full box and should have a valid version.');

          const parsedTFDT = shaka.util.Mp4BoxParsers.parseTFDT(
              box.reader, box.version);

          baseMediaDecodeTime = parsedTFDT.baseMediaDecodeTime;
        })
        .box('mdat', (box) => {
          if (baseMediaDecodeTime === null) {
            // This field should have been populated by
            // the Base Media Decode time in the TFDT box
            throw new shaka.util.Error(
                shaka.util.Error.Severity.CRITICAL,
                shaka.util.Error.Category.TEXT,
                shaka.util.Error.Code.INVALID_MP4_CEA);
          }
          this.parseMdat_(box.reader, baseMediaDecodeTime, timescale,
              defaultSampleDuration, defaultSampleSize, sampleData,
              captionPackets);
        })
        .parse(mediaSegment, /* partialOkay= */ false);

    return captionPackets;
  }

  /**
   * Parse MDAT box.
   * @param {!shaka.util.DataViewReader} reader
   * @param {number} time
   * @param {number} timescale
   * @param {number} defaultSampleDuration
   * @param {number} defaultSampleSize
   * @param {!Array<shaka.util.ParsedTRUNSample>} sampleData
   * @param {!Array<!shaka.cea.ICeaParser.CaptionPacket>} captionPackets
   * @private
   */
  parseMdat_(reader, time, timescale, defaultSampleDuration,
      defaultSampleSize, sampleData, captionPackets) {
    let sampleIndex = 0;

    // The fields in each ParsedTRUNSample contained in the sampleData
    // array are nullable. In the case of sample data and sample duration,
    // we use the defaults provided by the TREX/TFHD boxes. For sample
    // composition time offset, we default to 0.
    let sampleSize = defaultSampleSize;

    if (sampleData.length) {
      sampleSize = sampleData[0].sampleSize || defaultSampleSize;
    }

    while (reader.hasMoreData()) {
      const naluSize = reader.readUint32();
      const naluType = reader.readUint8() & 0x1F;
      if (naluType == shaka.cea.ICeaParser.NALU_TYPE_SEI) {
        let timeOffset = 0;

        if (sampleData.length > sampleIndex) {
          timeOffset = sampleData[sampleIndex].sampleCompositionTimeOffset || 0;
        }

        const pts = (time + timeOffset)/timescale;
        for (const packet of this.seiProcessor_
            .process(reader.readBytes(naluSize - 1))) {
          captionPackets.push({
            packet,
            pts,
          });
        }
      } else {
        try {
          reader.skip(naluSize - 1);
        } catch (e) {
          // It is necessary to ignore this error because it can break the start
          // of playback even if the user does not want to see the subtitles.
          break;
        }
      }
      sampleSize -= (naluSize + 4);
      if (sampleSize == 0) {
        if (sampleData.length > sampleIndex) {
          time += sampleData[sampleIndex].sampleDuration ||
              defaultSampleDuration;
        } else {
          time += defaultSampleDuration;
        }

        sampleIndex++;

        if (sampleData.length > sampleIndex) {
          sampleSize = sampleData[sampleIndex].sampleSize || defaultSampleSize;
        } else {
          sampleSize = defaultSampleSize;
        }
      }
    }
  }
};
<|endoftext|>


<commit_before>export { Base };

import * as ValueDesc from "../../../Unpacker/ValueDesc.js";
import * as PointDepthPoint from "../../PointDepthPoint.js";
import { Params } from "../Block_Params.js";
import { MobileNetV1 } from "./MobileNetV1.js";
import { MobileNetV2 } from "./MobileNetV2.js";
import { MobileNetV2_Thin } from "./MobileNetV2_Thin.js";
import { ShuffleNetV2 } from "./ShuffleNetV2.js";
import { ShuffleNetV2_ByPointwise22 } from "./ShuffleNetV2_ByPointwise22.js";
import { ShuffleNetV2_ByMobileNetV1 } from "./ShuffleNetV2_ByMobileNetV1.js";
import { ShuffleNetV2_ByMobileNetV1_padValid } from "./ShuffleNetV2_ByMobileNetV1_padValid.js";

/**
 * Basic class for all Params_to_PointDepthPointParams.Xxx classes.
 *
 * Note: In modern deep learning CNN, there is batch normalization after convolution and before activation. The batch normalization
 * has bias internally. We do not have batch normalization in architecture so an explicit bias will be used before every activation
 * function.
 *
 * @member {number} outChannels0
 *   The output0's channel count in current configuration.
 *
 * @member {number} outChannels1
 *   The output1's channel count in current configuration.
 *
 */
class Base {

  /**
   * @param {Params} blockParams
   *   The Block.Params object which provides basic parameters.
   */
  constructor( blockParams ) {
    this.blockParams = blockParams;

    this.inputHeight0 = this.inputWidth0 =
    this.channelCount0_pointwise1Before = this.channelCount1_pointwise1Before =
    this.pointwise1ChannelCount = this.bPointwise1Bias = this.pointwise1ActivationId =
    this.depthwise_AvgMax_Or_ChannelMultiplier = this.depthwiseFilterHeight = this.depthwiseFilterWidth =
    this.depthwiseStridesPad = this.bDepthwiseBias = this.depthwiseActivationId =
    this.pointwise21ChannelCount = this.bPointwise21Bias = this.pointwise21ActivationId =
    this.bOutput1Requested = this.bKeepInputTensor = undefined;

    this.stepCount = // How many step should be in the block.
    this.depthwiseFilterHeight_Default = this.depthwiseFilterWidth_Default = // The default depthwise filter size.
    this.depthwiseFilterHeight_Last = this.depthwiseFilterWidth_Last =       // The last step's depthwise filter size.
    this.outChannels0 = this.outChannels1 = -1;

    this.channelShuffler = undefined;
  }

  /** Called to determine stepCount, depthwiseFilterHeight_Default, depthwiseFilterWidth_Default, depthwiseFilterHeight_Last,
    * depthwiseFilterWidth_Last.
    *
    * Sub-class could override this method to adjust data members.
    */
  determine_stepCount_depthwiseFilterHeightWidth_Default_Last() {
    let blockParams = this.blockParams;
    this.stepCount = blockParams.stepCountRequested; // By default, the step count is just the original step count.

    // By default, all steps uses the original depthwise filter size.
    this.depthwiseFilterHeight_Default = this.depthwiseFilterHeight_Last = blockParams.depthwiseFilterHeight;
    this.depthwiseFilterWidth_Default = this.depthwiseFilterWidth_Last = blockParams.depthwiseFilterWidth;
  }

  /**
   * Called before step0 is about to be created. Sub-class should override this method to adjust data members.
   *
   * Step 0.
   *
   * The special points of a block's step 0 are:
   *   - halve the height x width. (Both ShuffleNetV2 and MobileNetV2) (by depthwise convolution with strides = 2)
   *   - Double channels. (Please see explanation of class Block.Base)
   */
  configTo_beforeStep0() {
    this.inputHeight0 = this.blockParams.sourceHeight; // step0 inputs the source image size.
    this.inputWidth0 = this.blockParams.sourceWidth;

    this.bias_activation_setup_forStep0(); // bias, activation of pointwise1, depthwise1, pointwise2
  }

  /**
   * Called after step0 is created (i.e. before step1, 2, 3, ...). Sub-class should override this method to adjust data members.
   */
  configTo_afterStep0() {
    this.inputHeight0 = this.blockParams.outputHeight; // all steps (except step0) inputs half the source image size.
    this.inputWidth0 = this.blockParams.outputWidth;
  }

  /**
   * Called before stepLast is about to be created. Sub-class could override this method to adjust data members.
   */
  configTo_beforeStepLast() {

//!!! (2022/05/04 Remarked) ShuffleNetV2_ByMobileNetV1 can not work.
//     // By default, the stepLast of this block (i.e. at-block-end) may use a different activation function after pointwise2 convolution.
//     //
//     // Even if in MobileNetV2 (pointwise2 convolution does not have activation function in default), this is still true.
//     this.pointwise21ActivationId = this.blockParams.nActivationIdAtBlockEnd;

    // Besides, the stepLast may use a different depthwise filter size. This is especially true for NotShuffleNet_NotMobileNet.
    this.depthwiseFilterHeight = this.depthwiseFilterHeight_Last;
    this.depthwiseFilterWidth = this.depthwiseFilterWidth_Last;
  }

  /**
   * Config the bias and activation of pointwise1, depthwise1, pointwise2.
   *
   * In original MobileNetV2:
   *   - pointwise1: bias, activation.
   *   - depthwise1: bias, activation.
   *   - pointwise2: bias, no activation.
   *
   * In original ShuffleNetV2:
   *   - pointwise1: bias, activation.
   *   - depthwise1: bias, no activation.
   *   - pointwise2: bias, activation.
   *
   * We use the former configuration (i.e. original MobileNetV2) in all classes Block.Params_to_PointDepthPointParams.Xxx.
   *
   *
   * 1. Reason
   *
   * The reason is for ShuffleNetV2_ByMobileNetV1 to undo activation escaping scales.
   *
   * In ShuffleNetV2_ByMobileNetV1, if an operation has activation function, it will scale its convolution filters for escaping
   * the activation function's non-linear parts. This results in its output is wrong (i.e. different from ShuffleNetV2). In order
   * to resolve this issue, the last operation (i.e. pointwise2) should have no activation (so it will not scale its convolution
   * filters for escaping the activation function's non-linear parts).
   *
   *
   * 2. Advantage
   *
   * Although it is for solving ShuffleNetV2_ByMobileNetV1's issue, it does have practical advantage. The output could have any
   * value (i.e. the whole number line). If the last operation (i.e. pointwise2) has activation function, the output value will
   * be restricted by the activation function (e.g. [ -1, +1 ] for tanh()).
   *
   *
   * 3. Drawback
   *
   * However, the original ShuffleNetV2 configuration also has its own advantage: the bias of its depthwise1 could be dropped
   * (and speed up performance). This is because:
   *
   *   "If an operation has no activation function, it can have no bias too. Because the next operation's bias can
   *    achieve the same result."
   *
   * In MobileNetV2 configuration, this advantage is disappeared. The no-activation is at pointwise2. It does not have a next
   * operation to remedy its bias. So it does not have chance to spped up performance by dropping bias.
   *
   */
  bias_activation_setup_forStep0() {
    this.bPointwise1Bias = true;
    this.pointwise1ActivationId = this.blockParams.nActivationId;

    this.bDepthwiseBias = true;
    this.depthwiseActivationId = this.blockParams.nActivationId;

    this.bPointwise21Bias = true;
    this.pointwise21ActivationId = ValueDesc.ActivationFunction.Singleton.Ids.NONE;
  }

  /**
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @return {PointDepthPoint.Params}
   *   Create and return a PointDepthPoint.Params according to this object's current state.
   */
  create_PointDepthPointParams( inputFloat32Array, byteOffsetBegin ) {
    let params = new PointDepthPoint.Params(
      inputFloat32Array, byteOffsetBegin,
      this.inputHeight0, this.inputWidth0,
      this.channelCount0_pointwise1Before,
      this.channelCount1_pointwise1Before,
      this.pointwise1ChannelCount, this.bPointwise1Bias, this.pointwise1ActivationId,
      this.depthwise_AvgMax_Or_ChannelMultiplier, this.depthwiseFilterHeight, this.depthwiseFilterWidth,
      this.depthwiseStridesPad, this.bDepthwiseBias, this.depthwiseActivationId,
      this.pointwise21ChannelCount, this.bPointwise21Bias, this.pointwise21ActivationId,
      this.bOutput1Requested,
      this.bKeepInputTensor
    );
    return params;
  }

  /**
   * @param {Params} blockParams
   *   The Block.Params object to be reference.
   *
   * @return {Base}
   *   Return newly created Block.Params_to_PointDepthPointParams.Xxx object according to blockParams.nConvBlockType.
   */
  static create_byBlockParams( blockParams ) {

    tf.util.assert( ( blockParams.stepCountRequested >= 2 ),
      `Block.StepParamsCreator.Base.create_byBlockParams(): `
        + `blockParams.stepCountRequested ( ${blockParams.stepCountRequested} ) must be >= 2.` );

    switch ( blockParams.nConvBlockType ) {
      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V1: // (0)
        return new Params_to_PointDepthPointParams.MobileNetV1( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V2: // (1)
        return new Params_to_PointDepthPointParams.MobileNetV2( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V2_THIN: // (2)
        return new Params_to_PointDepthPointParams.MobileNetV2_Thin( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2: // (3)
        return new Params_to_PointDepthPointParams.ShuffleNetV2( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_POINTWISE22: // (4)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByPointwise22( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1: // (5)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByMobileNetV1( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1_PAD_VALID: // (6)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByMobileNetV1_padValid( blockParams ); break;

      default:
        tf.util.assert( false,
          `Block.StepParamsCreator.Base.create_byBlockParams(): `
            + `unknown this.nConvBlockType ( ${blockParams.nConvBlockType} ) value.` );
        break;
    }
  }

}

<commit_msg>Update Base.js<commit_after>export { Base };

import * as ValueDesc from "../../../Unpacker/ValueDesc.js";
import * as PointDepthPoint from "../../PointDepthPoint.js";
import { Params } from "../Block_Params.js";
import { MobileNetV1 } from "./MobileNetV1.js";
import { MobileNetV2 } from "./MobileNetV2.js";
import { MobileNetV2_Thin } from "./MobileNetV2_Thin.js";
import { ShuffleNetV2 } from "./ShuffleNetV2.js";
import { ShuffleNetV2_ByPointwise22 } from "./ShuffleNetV2_ByPointwise22.js";
import { ShuffleNetV2_ByMobileNetV1 } from "./ShuffleNetV2_ByMobileNetV1.js";
import { ShuffleNetV2_ByMobileNetV1_padValid } from "./ShuffleNetV2_ByMobileNetV1_padValid.js";

/**
 * Basic class for all Params_to_PointDepthPointParams.Xxx classes.
 *
 * Note: In modern deep learning CNN, there is batch normalization after convolution and before activation. The batch normalization
 * has bias internally. We do not have batch normalization in architecture so an explicit bias will be used before every activation
 * function.
 *
 * @member {number} outChannels0
 *   The output0's channel count in current configuration.
 *
 * @member {number} outChannels1
 *   The output1's channel count in current configuration.
 *
 */
class Base {

  /**
   * @param {Params} blockParams
   *   The Block.Params object which provides basic parameters.
   */
  constructor( blockParams ) {
    this.blockParams = blockParams;

    this.inputHeight0 = this.inputWidth0 =
    this.channelCount0_pointwise1Before = this.channelCount1_pointwise1Before =
    this.pointwise1ChannelCount = this.bPointwise1Bias = this.pointwise1ActivationId =
    this.depthwise_AvgMax_Or_ChannelMultiplier = this.depthwiseFilterHeight = this.depthwiseFilterWidth =
    this.depthwiseStridesPad = this.bDepthwiseBias = this.depthwiseActivationId =
    this.pointwise21ChannelCount = this.bPointwise21Bias = this.pointwise21ActivationId =
    this.bOutput1Requested = this.bKeepInputTensor = undefined;

    this.stepCount = // How many step should be in the block.
    this.depthwiseFilterHeight_Default = this.depthwiseFilterWidth_Default = // The default depthwise filter size.
    this.depthwiseFilterHeight_Last = this.depthwiseFilterWidth_Last =       // The last step's depthwise filter size.
    this.outChannels0 = this.outChannels1 = -1;

    this.channelShuffler = undefined;
  }

  /** Called to determine stepCount, depthwiseFilterHeight_Default, depthwiseFilterWidth_Default, depthwiseFilterHeight_Last,
    * depthwiseFilterWidth_Last.
    *
    * Sub-class could override this method to adjust data members.
    */
  determine_stepCount_depthwiseFilterHeightWidth_Default_Last() {
    let blockParams = this.blockParams;
    this.stepCount = blockParams.stepCountRequested; // By default, the step count is just the original step count.

    // By default, all steps uses the original depthwise filter size.
    this.depthwiseFilterHeight_Default = this.depthwiseFilterHeight_Last = blockParams.depthwiseFilterHeight;
    this.depthwiseFilterWidth_Default = this.depthwiseFilterWidth_Last = blockParams.depthwiseFilterWidth;
  }

  /**
   * Called before step0 is about to be created. Sub-class should override this method to adjust data members.
   *
   * Step 0.
   *
   * The special points of a block's step 0 are:
   *   - halve the height x width. (Both ShuffleNetV2 and MobileNetV2) (by depthwise convolution with strides = 2)
   *   - Double channels. (Please see explanation of class Block.Base)
   */
  configTo_beforeStep0() {
    this.inputHeight0 = this.blockParams.sourceHeight; // step0 inputs the source image size.
    this.inputWidth0 = this.blockParams.sourceWidth;

    this.bias_activation_setup_forStep0(); // bias, activation of pointwise1, depthwise1, pointwise2
  }

  /**
   * Called after step0 is created (i.e. before step1, 2, 3, ...). Sub-class should override this method to adjust data members.
   */
  configTo_afterStep0() {
    this.inputHeight0 = this.blockParams.outputHeight; // all steps (except step0) inputs half the source image size.
    this.inputWidth0 = this.blockParams.outputWidth;
  }

  /**
   * Called before stepLast is about to be created. Sub-class could override this method to adjust data members.
   */
  configTo_beforeStepLast() {

//!!! (2022/05/04 Remarked) ShuffleNetV2_ByMobileNetV1 can not work.
//     // By default, the stepLast of this block (i.e. at-block-end) may use a different activation function after pointwise2 convolution.
//     //
//     // Even if in MobileNetV2 (pointwise2 convolution does not have activation function in default), this is still true.
//     this.pointwise21ActivationId = this.blockParams.nActivationIdAtBlockEnd;

    // Besides, the stepLast may use a different depthwise filter size. This is especially true for NotShuffleNet_NotMobileNet.
    this.depthwiseFilterHeight = this.depthwiseFilterHeight_Last;
    this.depthwiseFilterWidth = this.depthwiseFilterWidth_Last;
  }

  /**
   * Config the bias and activation of pointwise1, depthwise1, pointwise2.
   *
   * In original MobileNetV2:
   *   - pointwise1: bias, activation.
   *   - depthwise1: bias, activation.
   *   - pointwise2: bias, no activation.
   *
   * In original ShuffleNetV2:
   *   - pointwise1: bias, activation.
   *   - depthwise1: bias, no activation.
   *   - pointwise2: bias, activation.
   *
   * We use the former configuration (i.e. original MobileNetV2) in all classes Block.Params_to_PointDepthPointParams.Xxx.
   *
   *
   * 1. Reason
   *
   * The reason is for ShuffleNetV2_ByMobileNetV1 to undo activation escaping scales.
   *
   * In ShuffleNetV2_ByMobileNetV1, if an operation has activation function, it will scale its convolution filters for escaping
   * the activation function's non-linear parts. This results in its output is wrong (i.e. different from ShuffleNetV2). In order
   * to resolve this issue, the last operation (i.e. pointwise2) should have no activation (so it will not scale its convolution
   * filters for escaping the activation function's non-linear parts).
   *
   *
   * 2. Advantage
   *
   * Although it is for solving ShuffleNetV2_ByMobileNetV1's issue, it does have practical advantage. The output could have any
   * value (i.e. the whole number line). If the last operation (i.e. pointwise2) has activation function, the output value will
   * be restricted by the activation function (e.g. [ -1, +1 ] for tanh()).
   *

//!!! ...unfinished... (2022/05/04)
// All steps (except stepLast) could use ( this.bPointwise21Bias = true ).
// stepLast uses ( this.bPointwise21Bias = false ) is enough.

   *
   * 3. Drawback
   *
   * However, the original ShuffleNetV2 configuration also has its own advantage: the bias of its depthwise1 could be dropped
   * (and speed up performance). This is because:
   *
   *   "If an operation has no activation function, it can have no bias too. Because the next operation's bias can
   *    achieve the same result."
   *
   * In MobileNetV2 configuration, this advantage is disappeared. The no-activation is at pointwise2. It does not have a next
   * operation to remedy its bias. So it does not have chance to spped up performance by dropping bias.
   *
   */
  bias_activation_setup_forStep0() {
    this.bPointwise1Bias = true;
    this.pointwise1ActivationId = this.blockParams.nActivationId;

    this.bDepthwiseBias = true;
    this.depthwiseActivationId = this.blockParams.nActivationId;

//!!! ...unfinished... (2022/05/04)
// All steps (except stepLast) could use ( this.bPointwise21Bias = true ).
// stepLast uses ( this.bPointwise21Bias = false ) is enough.

    this.bPointwise21Bias = true;
    this.pointwise21ActivationId = ValueDesc.ActivationFunction.Singleton.Ids.NONE;
  }

  /**
   *
   * @param {Float32Array} inputFloat32Array
   *   A Float32Array whose values will be interpreted as weights.
   *
   * @param {number} byteOffsetBegin
   *   The position to start to decode from the inputFloat32Array. This is relative to the inputFloat32Array.buffer
   * (not to the inputFloat32Array.byteOffset).
   *
   * @return {PointDepthPoint.Params}
   *   Create and return a PointDepthPoint.Params according to this object's current state.
   */
  create_PointDepthPointParams( inputFloat32Array, byteOffsetBegin ) {
    let params = new PointDepthPoint.Params(
      inputFloat32Array, byteOffsetBegin,
      this.inputHeight0, this.inputWidth0,
      this.channelCount0_pointwise1Before,
      this.channelCount1_pointwise1Before,
      this.pointwise1ChannelCount, this.bPointwise1Bias, this.pointwise1ActivationId,
      this.depthwise_AvgMax_Or_ChannelMultiplier, this.depthwiseFilterHeight, this.depthwiseFilterWidth,
      this.depthwiseStridesPad, this.bDepthwiseBias, this.depthwiseActivationId,
      this.pointwise21ChannelCount, this.bPointwise21Bias, this.pointwise21ActivationId,
      this.bOutput1Requested,
      this.bKeepInputTensor
    );
    return params;
  }

  /**
   * @param {Params} blockParams
   *   The Block.Params object to be reference.
   *
   * @return {Base}
   *   Return newly created Block.Params_to_PointDepthPointParams.Xxx object according to blockParams.nConvBlockType.
   */
  static create_byBlockParams( blockParams ) {

    tf.util.assert( ( blockParams.stepCountRequested >= 2 ),
      `Block.StepParamsCreator.Base.create_byBlockParams(): `
        + `blockParams.stepCountRequested ( ${blockParams.stepCountRequested} ) must be >= 2.` );

    switch ( blockParams.nConvBlockType ) {
      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V1: // (0)
        return new Params_to_PointDepthPointParams.MobileNetV1( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V2: // (1)
        return new Params_to_PointDepthPointParams.MobileNetV2( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.MOBILE_NET_V2_THIN: // (2)
        return new Params_to_PointDepthPointParams.MobileNetV2_Thin( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2: // (3)
        return new Params_to_PointDepthPointParams.ShuffleNetV2( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_POINTWISE22: // (4)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByPointwise22( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1: // (5)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByMobileNetV1( blockParams ); break;

      case ValueDesc.ConvBlockType.Ids.SHUFFLE_NET_V2_BY_MOBILE_NET_V1_PAD_VALID: // (6)
        return new Params_to_PointDepthPointParams.ShuffleNetV2_ByMobileNetV1_padValid( blockParams ); break;

      default:
        tf.util.assert( false,
          `Block.StepParamsCreator.Base.create_byBlockParams(): `
            + `unknown this.nConvBlockType ( ${blockParams.nConvBlockType} ) value.` );
        break;
    }
  }

}

<|endoftext|>


<commit_before>(function() {
	VISAN.Plots.Scatterplot = function(options, container, dataManager, visan) {
		this._padding = 20;

		// get axes
		this._xAxis = options.xAxis;
		this._yAxis = options.yAxis;

		// render canvas
		this._stage = new Kinetic.Stage({
			container: container.get(0),
			width: options.width || 500,
			height: options.height || 500
		});

		this._shapeLayer = new Kinetic.Layer({
			clearBeforeDraw: false
		});

		this._axisLayer = new Kinetic.Layer({
			clearBeforeDraw: false
		});

		this._selectionLayer = new Kinetic.Layer();

		this._stage.add(this._shapeLayer);
		this._stage.add(this._axisLayer);
		this._stage.add(this._selectionLayer);

		var dataDimension1 = dataManager.getDimension(function(d) { return d[this._xAxis]; });
		var dataDimension2 = dataManager.getDimension(function(d) { return d[this._yAxis]; });

		this._dataManager = dataManager;

		this.draw();
	};

	VISAN.Plots.Scatterplot.renderPlotCreateOptions = function(optionsContainer) {
		optionsContainer.append($("<p />").text("Please select the X axis:")).append($("<select />").addClass("scatterplot-x").addClass("axis"));
		optionsContainer.append($("<p />").text("Please select the Y axis:")).append($("<select />").addClass("scatterplot-y").addClass("axis"));

		return function(plotOptions, optionsContainer) {
			plotOptions.xAxis = optionsContainer.find("select.scatterplot-x").val();
			plotOptions.yAxis = optionsContainer.find("select.scatterplot-y").val();
		};
	};

	VISAN.Plots.Scatterplot.prototype = {
		draw: function() {
			var padding = this._padding;

			var scXScale = new VISAN.Scale({
				range: [padding, 500 - padding],
				domain: [0, 300]
			});
			var scYScale = new VISAN.Scale({
				range: [500 - padding, padding],
				domain: [0, 300]
			});

			var context = this._shapeLayer.getContext("2d");

			new VISAN.Axis({ scale: scXScale, orientation: VISAN.AxisOrientation.BOTTOM, padding: 25 }).draw(this._axisLayer.getCanvas());
			new VISAN.Axis({ scale: scYScale, orientation: VISAN.AxisOrientation.LEFT, padding: 25 }).draw(this._axisLayer.getCanvas());

			var _ = this;
			this._dataManager.getData().forEach(function(cur) {
				if(cur._selected) {
					context.fillStyle = "#ff0000";
				} else {
					context.fillStyle = "#000000";
				}

				context.fillRect(scXScale.get(cur[_._xAxis]), scYScale.get(cur[_._yAxis]), 2,2);
			});
		}
	};
})();
<commit_msg>Added selection to Scatterplot<commit_after>(function() {
	VISAN.Plots.Scatterplot = function(options, container, stepModule, visan) {
		this._stepModule = stepModule;
		this._padding = 25;

		// get axes
		this._xAxis = options.xAxis;
		this._yAxis = options.yAxis;

		this._scXScale = new VISAN.Scale({
			range: [this._padding, 500 - this._padding],
			domain: [0, 300]
		});
		this._scYScale = new VISAN.Scale({
			range: [500 - this._padding, this._padding],
			domain: [0, 300]
		});

		// render canvas
		this._stage = new Kinetic.Stage({
			container: container.get(0),
			width: options.width || 500,
			height: options.height || 500
		});

		this._shapeLayer = new Kinetic.Layer({
			clearBeforeDraw: false
		});

		this._axisLayer = new Kinetic.Layer({
			clearBeforeDraw: false
		});

		this._selectionLayer = new Kinetic.Layer();

		this._stage.add(this._shapeLayer);
		this._stage.add(this._axisLayer);
		this._stage.add(this._selectionLayer);

		this._dataManager = stepModule._dataManager;
		this._dataDimension1 = this._dataManager.getDimension(function(d) { return d[this._xAxis]; });
		this._dataDimension2 = this._dataManager.getDimension(function(d) { return d[this._yAxis]; });

		this._selection = new Kinetic.Rect({
			x: 0,
			y: 0,
			width: 0,
			height: 0,
			fill: 'transparent',
			stroke: 'red',
			strokeWidth: 2
		});

		this._selectionLayer.add(this._selection);

		this._selecting = false;

		var stageContainer = this._stage.getContent();
		stageContainer.addEventListener("mousedown", $.proxy(this.mousedownEventHandler, this));
		stageContainer.addEventListener('mousemove', $.proxy(this.mouseMoveEventHandler, this));
		stageContainer.addEventListener('mouseup', $.proxy(this.mouseUpEventHandler, this));

		this.draw();
	};

	VISAN.Plots.Scatterplot.renderPlotCreateOptions = function(optionsContainer) {
		optionsContainer.append($("<p />").text("Please select the X axis:")).append($("<select />").addClass("scatterplot-x").addClass("axis"));
		optionsContainer.append($("<p />").text("Please select the Y axis:")).append($("<select />").addClass("scatterplot-y").addClass("axis"));

		return function(plotOptions, optionsContainer) {
			plotOptions.xAxis = optionsContainer.find("select.scatterplot-x").val();
			plotOptions.yAxis = optionsContainer.find("select.scatterplot-y").val();
		};
	};

	VISAN.Plots.Scatterplot.prototype = {
		draw: function() {
			var padding = this._padding;

			var scXScale = this._scXScale;
			var scYScale = this._scYScale;

			var context = this._shapeLayer.getContext("2d");

			new VISAN.Axis({ scale: scXScale, orientation: VISAN.AxisOrientation.BOTTOM, padding: padding }).draw(this._axisLayer.getCanvas());
			new VISAN.Axis({ scale: scYScale, orientation: VISAN.AxisOrientation.LEFT, padding: padding }).draw(this._axisLayer.getCanvas());

			var _ = this;
			this._dataManager.getData().forEach(function(cur) {
				if(cur._selected) {
					context.fillStyle = "#ff0000";
				} else {
					context.fillStyle = "#000000";
				}

				context.fillRect(scXScale.get(cur[_._xAxis]), scYScale.get(cur[_._yAxis]), 2,2);
			});
		},
		mousedownEventHandler: function(e) {
			if(!this._selecting) {
				this._selecting = true;
				this._selection.setX(e.layerX);
				this._selection.setY(e.layerY);
				this._selectionLayer.draw();
			}
		},
		mouseMoveEventHandler: function(e) {
			if(this._selecting) {
				/// TODO: this only works for drawing from top-left to bottom-right
				this._selection.setWidth(e.layerX - this._selection.getX());
				this._selection.setHeight(e.layerY - this._selection.getY());
				this._selectionLayer.draw();
			}
		},
		mouseUpEventHandler: function() {
			this._selecting = false;

			var scXScale = this._scXScale;
			var scYScale = this._scYScale;

			var xFrom = scXScale.getReverse(this._selection.getX()), xTo = scXScale.getReverse(this._selection.getX() + this._selection.getWidth());
			var yFrom = scYScale.getReverse(this._selection.getY()), yTo = scYScale.getReverse(this._selection.getY() + this._selection.getHeight());
			if(xFrom > xTo) { var temp = xFrom; xFrom = xTo; xTo = temp; }
			if(yFrom > yTo) { var temp = yFrom; yFrom = yTo; yTo = temp; }

			this._stepModule.createHighlight({
				type: "selection",
				axis1: {
					axis: this._xAxis,
					from: xFrom,
					to: xTo
				},
				axis2: {
					axis: this._yAxis,
					from: yFrom,
					to: yTo
				}
			});
			this._stepModule.refreshPlots();
		}
	};
})();
<|endoftext|>


<commit_before>/* eslint camelcase:0 */

function getOwnClasses(sort) {
  const free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer'])
  const objc_copyClassNamesForImage = new NativeFunction(Module.findExportByName(null, 'objc_copyClassNamesForImage'), 'pointer', ['pointer', 'pointer'])
  const p = Memory.alloc(Process.pointerSize)
  Memory.writeUInt(p, 0)
  const path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String()
  const pPath = Memory.allocUtf8String(path)
  const pClasses = objc_copyClassNamesForImage(pPath, p)
  const count = Memory.readUInt(p)
  const classes = new Array(count)
  for (let i = 0; i < count; i++) {
    const pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize))
    classes[i] = Memory.readUtf8String(pClassName)
  }
  free(pClasses)
  return sort ? classes.sort() : classes
}

function getGlobalClasses(sort) {
  const classes = Object.keys(ObjC.classes)
  return sort ? classes.sort() : classes
}

let ownClasses = null
let globalClasses = null

exports.ownClasses = () => {
  if (!ownClasses)
    ownClasses = getOwnClasses(true)
  return ownClasses
}

exports.classes = () => {
  if (!globalClasses)
    globalClasses = getGlobalClasses(true)

  return globalClasses
}

exports.inspect = (clazz) => {
  const proto = []
  let clz = ObjC.classes[clazz]
  if (!clz)
    throw new Error(`class ${clazz} not found`)

  for (; clz; clz = clz.$superClass)
    proto.unshift(clz.$className)

  return {
    methods: ObjC.classes[clazz].$ownMethods,
    proto,
  }
}
<commit_msg>do not include self in prototype<commit_after>/* eslint camelcase:0 */

function getOwnClasses(sort) {
  const free = new NativeFunction(Module.findExportByName(null, 'free'), 'void', ['pointer'])
  const objc_copyClassNamesForImage = new NativeFunction(Module.findExportByName(null, 'objc_copyClassNamesForImage'), 'pointer', ['pointer', 'pointer'])
  const p = Memory.alloc(Process.pointerSize)
  Memory.writeUInt(p, 0)
  const path = ObjC.classes.NSBundle.mainBundle().executablePath().UTF8String()
  const pPath = Memory.allocUtf8String(path)
  const pClasses = objc_copyClassNamesForImage(pPath, p)
  const count = Memory.readUInt(p)
  const classes = new Array(count)
  for (let i = 0; i < count; i++) {
    const pClassName = Memory.readPointer(pClasses.add(i * Process.pointerSize))
    classes[i] = Memory.readUtf8String(pClassName)
  }
  free(pClasses)
  return sort ? classes.sort() : classes
}

function getGlobalClasses(sort) {
  const classes = Object.keys(ObjC.classes)
  return sort ? classes.sort() : classes
}

let ownClasses = null
let globalClasses = null

exports.ownClasses = () => {
  if (!ownClasses)
    ownClasses = getOwnClasses(true)
  return ownClasses
}

exports.classes = () => {
  if (!globalClasses)
    globalClasses = getGlobalClasses(true)

  return globalClasses
}

exports.inspect = (clazz) => {
  const proto = []
  let clz = ObjC.classes[clazz]
  if (!clz)
    throw new Error(`class ${clazz} not found`)

  clz = clz.$superClass
  for (; clz; clz = clz.$superClass)
    proto.unshift(clz.$className)

  return {
    methods: ObjC.classes[clazz].$ownMethods,
    proto,
  }
}
<|endoftext|>


<commit_before>module.exports = function (grunt) {

  require('load-grunt-tasks')(grunt);

  grunt.initConfig({

    qunit: {
      files: ['test/index.html']
    },

    concat: {
      options: {
        separator: ';',
      },
      dist: {
        src: [
          'dependencias/helpers.js',
          'dependencias/listHelper.js',
          'node_modules/nearley/lib/nearley.js',
          'compilados/gramaticaAleatoria.js',
          'compilados/ejerciciosPilas.js',
        ],
        dest: 'compilados/ejerciciosPilas.js',
      },
    },

    copy: {
      main: {
        expand: true,
        src: 'compilados/ejerciciosPilas.js',
        dest: '../public/libs/',
        flatten: true,
        filter: 'isFile'
      },
    },

    ts: {
      default: {
        tsconfig: 'tsconfig.json'
      }
    },

    typescript: {
      base: {
        src: ['src/**/*.ts'],
        dest: 'compilados/ejerciciosPilas.js',
        options: {
          module: 'commonjs',
          target: 'es5',
          rootDir: 'src',
          experimentalDecorators: true,
          emitDecoratorMetadata: true,
          sourceMap: false,
          fullSourceMapPath: false,
          declaration: false,
          comments: true,
        }
      }
    },

    open: {
      dev: {
        path: 'src/visorEjercicios.html'
      }
    },

    watch: {
      scripts: {
        files: ['src/**'],
        tasks: ['compile'],
      }
    },

    run: {
      compilarGramaticaAleatoria: {
        cmd: 'npm',
        args: [
          'run',
          'compilarGramaticaAleatoria'
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-typescript');
  grunt.loadNpmTasks('grunt-ts');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-open');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-run');

  grunt.registerTask('copiarPilasBloques', 'copy');
  grunt.registerTask('evaluarTypeScript', 'ts');
  grunt.registerTask('compilarTypeScript', 'typescript');
  grunt.registerTask('compile', ['compilarTypeScript', 'run:compilarGramaticaAleatoria', 'concat', 'copy']);
  grunt.registerTask('default', ['compile']);

  grunt.registerTask('test', 'qunit');
};
<commit_msg>corregido Gruntfile para que utilÃice la tarea copiarEjerciciosPilas en veze copy<commit_after>module.exports = function (grunt) {

  require('load-grunt-tasks')(grunt);

  grunt.initConfig({

    qunit: {
      files: ['test/index.html']
    },

    concat: {
      options: {
        separator: ';',
      },
      dist: {
        src: [
          'dependencias/helpers.js',
          'dependencias/listHelper.js',
          'node_modules/nearley/lib/nearley.js',
          'compilados/gramaticaAleatoria.js',
          'compilados/ejerciciosPilas.js',
        ],
        dest: 'compilados/ejerciciosPilas.js',
      },
    },

    copy: {
      main: {
        expand: true,
        src: 'compilados/ejerciciosPilas.js',
        dest: '../public/libs/',
        flatten: true,
        filter: 'isFile'
      },
    },

    ts: {
      default: {
        tsconfig: 'tsconfig.json'
      }
    },

    typescript: {
      base: {
        src: ['src/**/*.ts'],
        dest: 'compilados/ejerciciosPilas.js',
        options: {
          module: 'commonjs',
          target: 'es5',
          rootDir: 'src',
          experimentalDecorators: true,
          emitDecoratorMetadata: true,
          sourceMap: false,
          fullSourceMapPath: false,
          declaration: false,
          comments: true,
        }
      }
    },

    open: {
      dev: {
        path: 'src/visorEjercicios.html'
      }
    },

    watch: {
      scripts: {
        files: ['src/**'],
        tasks: ['compile'],
      }
    },

    run: {
      compilarGramaticaAleatoria: {
        cmd: 'npm',
        args: [
          'run',
          'compilarGramaticaAleatoria'
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-typescript');
  grunt.loadNpmTasks('grunt-ts');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-open');
  grunt.loadNpmTasks('grunt-contrib-qunit');
  grunt.loadNpmTasks('grunt-run');

  grunt.registerTask('copiarEjerciciosPilas', 'copy');
  grunt.registerTask('evaluarTypeScript', 'ts');
  grunt.registerTask('compilarTypeScript', 'typescript');
  grunt.registerTask('compile', ['compilarTypeScript', 'run:compilarGramaticaAleatoria', 'concat', 'copiarEjerciciosPilas']);
  grunt.registerTask('default', ['compile']);

  grunt.registerTask('test', 'qunit');
};
<|endoftext|>