<commit_before>// Return intersection between arrays a1 & a2
function inter(a1, a2) {
    return a1.filter(function(n) {
        return a2.indexOf(n) != -1
    });
}

var Path = (function ($, Data, Log, Network) {
    // Camera FOV: horz 94.38, vert 78.3

    var exports = {};

    // Data objects here: array of L.LatLng objects
    var waypoints = [];
    var localIndex = 0; window.lo = function(){return localIndex};
    exports.testPlaneWaypointIndex = null;

    var WAYPOINT_HOME = 255;
    var WAYPOINT_LEGACY_RADIUS = 13.1415926;    // Just a number here in case plane uses legacy waypoint following

    // Interactive map objects here
    var map;

    // Initialize map if necessary
    $(document).ready(function () {
        if (!map) {
            map = L.map('map').setView([43.53086, -80.5772], 17);
            map.attributionControl.setPrefix(false);

            L.tileLayer('sat_tiles/{z}/{x}/{y}.png', {
                maxZoom: 19
            }).addTo(map);
        }
    });

    // Handle button clicks
    $(document).ready(function () {

        $('#lockOn').on('click', function () {
            // TODO Decouple this from plane marker & figure out somewhere to store last "sensible GPS coordinates"
            // (to prevent bug with erroneous GPS coordinates crashing Leaflet)
            if (planeMarker) {
                map.panTo(planeMarker.getLatLng());
            }
        });

        $('#clearWaypoints').on('click', function () {
            // Clear local future waypoints

            // Replace local waypoints with those from readonlyPlotter[localIndex] through readonlyPlotter[nextIndex]
            // (These are the waypoints that have already been visited but still exist on the plane)
            var nextIndex = readonlyPlotter.getNextIndex();
            var pastLatLngs = readonlyPlotter.getLatLngs().slice(localIndex, nextIndex).map(function (latlng) {
                return new L.LatLng(latlng.lat, latlng.lng, latlng.alt);    // Clone object
            });
            localPlotter.setLatLngs(pastLatLngs);
            localPlotter.setNextIndex(pastLatLngs.length);

            redrawMap();
        });

        $('#sendWaypoints').on('click', function () {

            var readonlyLatLngs = readonlyPlotter.getLatLngs();
            var readonlyNextIndex = readonlyPlotter.getNextIndex();
            var localLatLngs = localPlotter.getLatLngs();
            var localNextIndex = localPlotter.getNextIndex();

            // Keep only past waypoints in readonly line
            readonlyLatLngs = readonlyLatLngs.filter(function (latlng, index) {
                return index < readonlyNextIndex;
            });
                console.log('Keeping readonly points', readonlyLatLngs.length);

            // Keep only future waypoints + the previous waypoint in local line
            localLatLngs = localLatLngs.filter(function (latlng, index) {
                return index >= localNextIndex - 1;
            });
                console.log('Keeping local points', localLatLngs.length);

            // Copy over local future waypoints to readonly future waypoints
            var futureLatLngs = localLatLngs.map(function (latlng) {
                return new L.LatLng(latlng.lat, latlng.lng, latlng.alt);    // Clone object
            });
                console.log('future', futureLatLngs);
                console.log('local', localLatLngs);

            // There's an edge case when readonly line is empty at start up
            if (!readonlyLatLngs.length) {
                    console.log('Edge case: empty readonly line');
                    console.log('Copied local -> readonly', futureLatLngs.length);
                    console.log('Local nextIndex set to', 0);
                readonlyPlotter.setLatLngs(futureLatLngs);
                localPlotter.setNextIndex(0);
                localIndex += localNextIndex;
            } else {
                futureLatLngs.shift();  // Don't copy over the previous waypoint, which is already in readonlyLatLngs
                    console.log('Copied local -> readonly', futureLatLngs.length);
                    console.log('Local nextIndex set to', 1);
                readonlyPlotter.setLatLngs(readonlyLatLngs.concat(futureLatLngs));
                localPlotter.setNextIndex(1);
                localIndex += localNextIndex - 1;
            }
                console.log('localIndex set to', localIndex);
            localPlotter.setLatLngs(localLatLngs);

            // Upload new future waypoints
            var command = "clear_waypoints:0\r\n";
            Network.write(command);

            for (i = 0; i < futureLatLngs.length; i++) {
                var latLng = futureLatLngs[i];
                command = "new_Waypoint:" + latLng.lat + "," + latLng.lng + "," + latLng.alt + "," + WAYPOINT_LEGACY_RADIUS + "\r\n";
                Network.write(command);
            }

            command = "set_targetWaypoint:" + localPlotter.getNextIndex() + "\r\n";
            Network.write(command);

            // If we're overriding the waypointIndex we receive from plane, then simulate it accordingly
            if (exports.testPlaneWaypointIndex !== null) {
                exports.testPlaneWaypointIndex = localPlotter.getNextIndex();
            }
        });

        $('#goHome').on('click', function () {
            var command = "return_home\r\n";
            Network.write(command);
        });
    });

    var planeIcon;
    var planeHollowIcon;
    var planeMarker;
    var gpsFixMessagebox;
    var localPlotter;       // Contains local working copy of what we're planning
    var readonlyPlotter;    // Contains any wpt already visited + any future wpt currently on plane
    var lineToNextWaypoint;
    var historyPolyline;

    Network.on('data', redrawMap);

    function redrawMap() {

        var lat = parseFloat(Data.state.lat);
        var lon = parseFloat(Data.state.lon);

        // Check for GPS fix, assuming we'll never fly off the coast of West Africa
        // (No GPS fix if coordinates close to (0; 0) or impossibly big)
        var gpsFix = (Math.abs(lat) > 1) && (Math.abs(lon) > 1) && (Math.abs(lat) < 360) && (Math.abs(lon) < 360);

        var heading = Data.state.heading;
        var yaw = Data.state.yaw;
        var waypointIndex = Data.state.waypointIndex;

        // Init icons for planeMarker if necessary
        if (!planeIcon) {
            planeIcon = L.icon({
                iconUrl: 'plane.png',
                iconSize: [30, 30],
            });
        }
        if (!planeHollowIcon) {
            planeHollowIcon = L.icon({
                iconUrl: 'plane-hollow.png',
                iconSize: [30, 30],
                title: 'No GPS fix',
            });
        }

        // Init planeMarker if necessary
        if (!planeMarker) {
            planeMarker = new L.RotatedMarker([lat, lon], {
                icon: planeIcon,
            }).addTo(map);
        }

        // Init messagebox about GPS fix
        if (!gpsFixMessagebox) {
            gpsFixMessagebox = L.control.messagebox({
                timeout: null,
                className: 'messagebox-gpsfix',
            }).addTo(map);
        }

        // Init localPlotter if necessary
        if (!localPlotter) {
            localPlotter = L.Polyline.Plotter(waypoints, {
                future:  {color: '#f00', weight: 4, opacity: 0.8, dashArray: '3, 6'},
                present: {color: '#f00', weight: 4, opacity: 0.8, clickable: false},
                past:    {color: '#844', weight: 4, opacity: 0.8, clickable: false},
            }).addTo(map);

            window.l = localPlotter;
        }

        // Init readonlyPlotter if necessary
        if (!readonlyPlotter) {
            readonlyPlotter = L.Polyline.Plotter([], {
                readOnly: true,
                future:  {color: '#1a1a80', weight: 5, opacity: 1, clickable: false, dashArray: '3, 6'},
                present: {color: '#1a1a80', weight: 5, opacity: 1, clickable: false},
                past:    {color: '#0a0a33', weight: 5, opacity: 1, clickable: false},
            }).addTo(map);

            window.r = readonlyPlotter;
        }

        // Init lineToNextWaypoint if necessary
        if (!lineToNextWaypoint) {
            lineToNextWaypoint = L.polyline([], {
                color: '#1a1a80', weight: 5, opacity: 0.8, clickable: false, dashArray: '3, 6',
            }).addTo(map);

            // When waypoints change, update line going from plane to next waypoint
            readonlyPlotter.on('change', function(e) {
                var nextWaypoint = readonlyPlotter.getNextLatLng();
                if (nextWaypoint) {
                    if (lineToNextWaypoint.getLatLngs().length) {
                        lineToNextWaypoint.spliceLatLngs(1, 1, {lat: nextWaypoint.lat, lng: nextWaypoint.lng});
                    }
                } else {
                    lineToNextWaypoint.setLatLngs([]);
                }
            });
        }

        // Init historyPolyline if necessary
        if (!historyPolyline) {
            historyPolyline = new L.Polyline([], {
                color: '#190019', opacity: 0.6, weight: 4, clickable: false,
            }).addTo(map);
        }

        // Update plane marker
        if (gpsFix) {
            planeMarker.setIcon(planeIcon);
            planeMarker.setLatLng(new L.LatLng(lat, lon));
            planeMarker.options.angle = yaw;
            planeMarker.update();
        } else {
            planeMarker.setIcon(planeHollowIcon);
        }
        planeMarker._icon.title = lat + "°, " + lon + "°\nyaw " + Math.round(yaw) + "°, hdg " + heading + "°\nnext-wpt: " + waypointIndex + (waypointIndex == WAYPOINT_HOME ? " (home)" : "");

        // Update gpsFix message box
        if (gpsFix) {
            gpsFixMessagebox.hide();
        } else {
            gpsFixMessagebox.show('No GPS fix');
        }

        // Update which waypoint we're targeting next & redraw lines accordingly
        if (exports.testPlaneWaypointIndex !== null) {
            // You can override waypointIndex received from plane by setting
            // Path.testPlaneWaypointIndex to some number (from the console)
            waypointIndex = exports.testPlaneWaypointIndex;
        }
        if (localPlotter.getNextIndex() != waypointIndex && waypointIndex != WAYPOINT_HOME) {
            // TODO Manage going home case
            readonlyPlotter.setNextIndex(waypointIndex + localIndex);
            localPlotter.setNextIndex(waypointIndex);
        }

        // When plane moves, update line going from plane to next waypoint
        if (gpsFix) {
            lineToNextWaypoint.spliceLatLngs(0, 1, {lat: lat, lng: lon});
        } else {
            lineToNextWaypoint.setLatLngs([]);
        }

        // Draw points on historyPolyline
        if (gpsFix) {
            historyPolyline.addLatLng(L.latLng(lat, lon));
        }
    }

    // Export what needs to be
    return exports;

})($, Data, Log, Network);<commit_msg>Remove debug code from previous commit (console.log, window.___)<commit_after>var Path = (function ($, Data, Log, Network) {
    // Camera FOV: horz 94.38, vert 78.3

    var exports = {};

    // Data objects here: array of L.LatLng objects
    var waypoints = [];
    var localIndex = 0;
    exports.testPlaneWaypointIndex = null;

    var WAYPOINT_HOME = 255;
    var WAYPOINT_LEGACY_RADIUS = 13.1415926;    // Just a number here in case plane uses legacy waypoint following

    // Interactive map objects here
    var map;

    // Initialize map if necessary
    $(document).ready(function () {
        if (!map) {
            map = L.map('map').setView([43.53086, -80.5772], 17);
            map.attributionControl.setPrefix(false);

            L.tileLayer('sat_tiles/{z}/{x}/{y}.png', {
                maxZoom: 19
            }).addTo(map);
        }
    });

    // Handle button clicks
    $(document).ready(function () {

        $('#lockOn').on('click', function () {
            // TODO Decouple this from plane marker & figure out somewhere to store last "sensible GPS coordinates"
            // (to prevent bug with erroneous GPS coordinates crashing Leaflet)
            if (planeMarker) {
                map.panTo(planeMarker.getLatLng());
            }
        });

        $('#clearWaypoints').on('click', function () {
            // Clear local future waypoints

            // Replace local waypoints with those from readonlyPlotter[localIndex] through readonlyPlotter[nextIndex]
            // (These are the waypoints that have already been visited but still exist on the plane)
            var nextIndex = readonlyPlotter.getNextIndex();
            var pastLatLngs = readonlyPlotter.getLatLngs().slice(localIndex, nextIndex).map(function (latlng) {
                return new L.LatLng(latlng.lat, latlng.lng, latlng.alt);    // Clone object
            });
            localPlotter.setLatLngs(pastLatLngs);
            localPlotter.setNextIndex(pastLatLngs.length);

            redrawMap();
        });

        $('#sendWaypoints').on('click', function () {

            var readonlyLatLngs = readonlyPlotter.getLatLngs();
            var readonlyNextIndex = readonlyPlotter.getNextIndex();
            var localLatLngs = localPlotter.getLatLngs();
            var localNextIndex = localPlotter.getNextIndex();

            // Keep only past waypoints in readonly line
            readonlyLatLngs = readonlyLatLngs.filter(function (latlng, index) {
                return index < readonlyNextIndex;
            });

            // Keep only future waypoints + the previous waypoint in local line
            localLatLngs = localLatLngs.filter(function (latlng, index) {
                return index >= localNextIndex - 1;
            });

            // Copy over local future waypoints to readonly future waypoints
            var futureLatLngs = localLatLngs.map(function (latlng) {
                return new L.LatLng(latlng.lat, latlng.lng, latlng.alt);    // Clone object
            });

            // There's an edge case when readonly line is empty at start up
            if (!readonlyLatLngs.length) {
                readonlyPlotter.setLatLngs(futureLatLngs);
                localPlotter.setNextIndex(0);
                localIndex += localNextIndex;
            } else {
                futureLatLngs.shift();  // Don't copy over the previous waypoint, which is already in readonlyLatLngs
                readonlyPlotter.setLatLngs(readonlyLatLngs.concat(futureLatLngs));
                localPlotter.setNextIndex(1);
                localIndex += localNextIndex - 1;
            }
            localPlotter.setLatLngs(localLatLngs);

            // Upload new future waypoints
            var command = "clear_waypoints:0\r\n";
            Network.write(command);

            for (i = 0; i < futureLatLngs.length; i++) {
                var latLng = futureLatLngs[i];
                command = "new_Waypoint:" + latLng.lat + "," + latLng.lng + "," + latLng.alt + "," + WAYPOINT_LEGACY_RADIUS + "\r\n";
                Network.write(command);
            }

            command = "set_targetWaypoint:" + localPlotter.getNextIndex() + "\r\n";
            Network.write(command);

            // If we're overriding the waypointIndex we receive from plane, then simulate it accordingly
            if (exports.testPlaneWaypointIndex !== null) {
                exports.testPlaneWaypointIndex = localPlotter.getNextIndex();
            }
        });

        $('#goHome').on('click', function () {
            var command = "return_home\r\n";
            Network.write(command);
        });
    });

    var planeIcon;
    var planeHollowIcon;
    var planeMarker;
    var gpsFixMessagebox;
    var localPlotter;       // Contains local working copy of what we're planning
    var readonlyPlotter;    // Contains any wpt already visited + any future wpt currently on plane
    var lineToNextWaypoint;
    var historyPolyline;

    Network.on('data', redrawMap);

    function redrawMap() {

        var lat = parseFloat(Data.state.lat);
        var lon = parseFloat(Data.state.lon);

        // Check for GPS fix, assuming we'll never fly off the coast of West Africa
        // (No GPS fix if coordinates close to (0; 0) or impossibly big)
        var gpsFix = (Math.abs(lat) > 1) && (Math.abs(lon) > 1) && (Math.abs(lat) < 360) && (Math.abs(lon) < 360);

        var heading = Data.state.heading;
        var yaw = Data.state.yaw;
        var waypointIndex = Data.state.waypointIndex;

        // Init icons for planeMarker if necessary
        if (!planeIcon) {
            planeIcon = L.icon({
                iconUrl: 'plane.png',
                iconSize: [30, 30],
            });
        }
        if (!planeHollowIcon) {
            planeHollowIcon = L.icon({
                iconUrl: 'plane-hollow.png',
                iconSize: [30, 30],
                title: 'No GPS fix',
            });
        }

        // Init planeMarker if necessary
        if (!planeMarker) {
            planeMarker = new L.RotatedMarker([lat, lon], {
                icon: planeIcon,
            }).addTo(map);
        }

        // Init messagebox about GPS fix
        if (!gpsFixMessagebox) {
            gpsFixMessagebox = L.control.messagebox({
                timeout: null,
                className: 'messagebox-gpsfix',
            }).addTo(map);
        }

        // Init localPlotter if necessary
        if (!localPlotter) {
            localPlotter = L.Polyline.Plotter(waypoints, {
                future:  {color: '#f00', weight: 4, opacity: 0.8, dashArray: '3, 6'},
                present: {color: '#f00', weight: 4, opacity: 0.8, clickable: false},
                past:    {color: '#844', weight: 4, opacity: 0.8, clickable: false},
            }).addTo(map);
        }

        // Init readonlyPlotter if necessary
        if (!readonlyPlotter) {
            readonlyPlotter = L.Polyline.Plotter([], {
                readOnly: true,
                future:  {color: '#1a1a80', weight: 5, opacity: 1, clickable: false, dashArray: '3, 6'},
                present: {color: '#1a1a80', weight: 5, opacity: 1, clickable: false},
                past:    {color: '#0a0a33', weight: 5, opacity: 1, clickable: false},
            }).addTo(map);
        }

        // Init lineToNextWaypoint if necessary
        if (!lineToNextWaypoint) {
            lineToNextWaypoint = L.polyline([], {
                color: '#1a1a80', weight: 5, opacity: 0.8, clickable: false, dashArray: '3, 6',
            }).addTo(map);

            // When waypoints change, update line going from plane to next waypoint
            readonlyPlotter.on('change', function(e) {
                var nextWaypoint = readonlyPlotter.getNextLatLng();
                if (nextWaypoint) {
                    if (lineToNextWaypoint.getLatLngs().length) {
                        lineToNextWaypoint.spliceLatLngs(1, 1, {lat: nextWaypoint.lat, lng: nextWaypoint.lng});
                    }
                } else {
                    lineToNextWaypoint.setLatLngs([]);
                }
            });
        }

        // Init historyPolyline if necessary
        if (!historyPolyline) {
            historyPolyline = new L.Polyline([], {
                color: '#190019', opacity: 0.6, weight: 4, clickable: false,
            }).addTo(map);
        }

        // Update plane marker
        if (gpsFix) {
            planeMarker.setIcon(planeIcon);
            planeMarker.setLatLng(new L.LatLng(lat, lon));
            planeMarker.options.angle = yaw;
            planeMarker.update();
        } else {
            planeMarker.setIcon(planeHollowIcon);
        }
        planeMarker._icon.title = lat + "°, " + lon + "°\nyaw " + Math.round(yaw) + "°, hdg " + heading + "°\nnext-wpt: " + waypointIndex + (waypointIndex == WAYPOINT_HOME ? " (home)" : "");

        // Update gpsFix message box
        if (gpsFix) {
            gpsFixMessagebox.hide();
        } else {
            gpsFixMessagebox.show('No GPS fix');
        }

        // Update which waypoint we're targeting next & redraw lines accordingly
        if (exports.testPlaneWaypointIndex !== null) {
            // You can override waypointIndex received from plane by setting
            // Path.testPlaneWaypointIndex to some number (from the console)
            waypointIndex = exports.testPlaneWaypointIndex;
        }
        if (localPlotter.getNextIndex() != waypointIndex && waypointIndex != WAYPOINT_HOME) {
            // TODO Manage going home case
            readonlyPlotter.setNextIndex(waypointIndex + localIndex);
            localPlotter.setNextIndex(waypointIndex);
        }

        // When plane moves, update line going from plane to next waypoint
        if (gpsFix) {
            lineToNextWaypoint.spliceLatLngs(0, 1, {lat: lat, lng: lon});
        } else {
            lineToNextWaypoint.setLatLngs([]);
        }

        // Draw points on historyPolyline
        if (gpsFix) {
            historyPolyline.addLatLng(L.latLng(lat, lon));
        }
    }

    // Export what needs to be
    return exports;

})($, Data, Log, Network);<|endoftext|>


<commit_before>var isCursor = function (c) {
  return c && c._publishCursor;
};

var deepCopy = function(value) {
  return EJSON.clone(value);
}

function Dependency(callback) {
  this.callback = callback;
  this.id = Random.id();
}

function BaseCollection(name) {
  this.name = name;
  this.relations = {};
}
BaseCollection.prototype.smartAdded = function(dependencyCursorId, id, fields) {
  var self = this;
  if (!self[id]) {
    self.publication.added(self.name, id, fields);
    self[id] = new CollectionItem(id, self, fields, dependencyCursorId);
    self[id].updateChildren(self.relations);
  } else {
    _.each(fields, function(val, key) {
      self[id].data[key] = self[id].data[key] || {};
      self[id].data[key][dependencyCursorId] = deepCopy(val);
    });
    self[id].count++;
    self[id].updateFromData(fields);
    self[id].updateChildren(fields);
  }
}
BaseCollection.prototype.smartChanged = function(dependencyCursorId, id, fields) {
  var data = this[id].data;
  _.each(fields, function(val, key) {
    data[key] = data[key] || {};
    if (val === undefined) {
      delete data[key][dependencyCursorId];
    } else {
      data[key][dependencyCursorId] = deepCopy(val);
    }
  });
  this[id].updateFromData(fields);
  this[id].updateChildren(fields);
}
BaseCollection.prototype.smartRemoved = function(dependencyCursorId, id) {
  if (!this[id]) throw new Meteor.Error("Removing unexisting element '" + id + "' from collection '" + this.name + "'");

  if (!--this[id].count) { // If reference counter was decremented to zero
    this[id].updateChildren(this.relations, true);
    delete this[id];
    this.publication.removed(this.name, id);
  } else {
    var fields = {};
    _.each(this[id].data, function(vals, key) {
      if (dependencyCursorId in vals) {
        fields[key] = 1;
        delete vals[dependencyCursorId];
      }
    });
    this[id].updateFromData(fields);
    this[id].updateChildren(fields);
  }
}

function CollectionItem(id, collection, fields, dependencyCursorId) {
  this.id = id;
  this.collection = collection;
  this.count = 1;
  this.data = {};
  this.mergedData = deepCopy(fields);
  this.children = {};
  var self = this;
  _.each(fields, function(val, key) {
    self.data[key] = self.data[key] || {};
    self.data[key][dependencyCursorId] = deepCopy(val);
  });
}
CollectionItem.prototype.updateFromData = function(fields) {
  var res = {};
  var merged = this.mergedData;
  var self = this;
  _.each(fields, function(flag, key) {
    var cur = undefined;
    _.each(self.data[key], (function(value) {
      cur = _.extend(deepCopy(value), cur);
    }));
    res[key] = cur;
    if (_.isUndefined(cur)) {
      delete merged[key];
    } else {
      merged[key] = cur;
    }
  });
  this.collection.publication.changed(this.collection.name, this.id, res);
}
CollectionItem.prototype.updateChildren = function(fields, removeAll) {
  var self = this;
  var update = {};
  _.each(fields, function(flag, key) {
    _.each(self.collection.relations[key], function(dep) {
      update[dep.id] = dep;
    });
  });
  _.each(update, function(dep) {
    var toRemove = [];
    if (dep.id in self.children) {
      self.children[dep.id].forEach(function(x, i) {
        _.each(x.activeItems, function(flag, subid) {
          toRemove.push([x.collection, x.dependencyCursorId, subid]);
        });
        x.observer.stop();
      });
    }

    if (!removeAll) {
      var cursors = dep.callback(self.mergedData);
      if (isCursor(cursors)) cursors = [cursors];
      if (!_.isArray(cursors))
        throw new Meteor.Error("Dependency function can only return a Cursor or an array of Cursors");

      var observers = self.children[dep.id] = [];
      for (var i = 0; i < cursors.length; i++) {
        var c = cursors[i];
        if (!isCursor(c))
          throw new Meteor.Error("Dependency function returned an array of non-Cursors");

        if (!c._cursorDescription) throw new Meteor.Error("Unable to get cursor's collection name");
        var subname = c._cursorDescription.collectionName;
        if (!subname) throw new Meteor.Error("Unable to get cursor's collection name");

        observers.push(new CursorWrapper(c, self.collection.publication.getCollectionByName(subname)));
      }
    }
    toRemove.forEach(function(args) {
      args[0].smartRemoved(args[1], args[2]);
    });
  });
}

function CursorWrapper(cursor, collection) {
  this.activeItems = {};
  this.collection = collection;
  this.dependencyCursorId = Random.id();
  var self = this;
  this.observer = cursor.observeChanges({
    added:   function(id, fields) {
      self.activeItems[id] = 1;
      fields['_id'] = id;
      collection.smartAdded  (self.dependencyCursorId, id, fields);
    },
    changed: function(id, fields) {
      collection.smartChanged(self.dependencyCursorId, id, fields);
    },
    removed: function(id) {
      delete self.activeItems[id];
      collection.smartRemoved(self.dependencyCursorId, id);
    },
  })
};

Meteor.smartPublish = function(name, callback) {
  Meteor.publish(name, function() {
    var publication = this;
    var collections = {};
    function getCollectionByName(name) {
      return collections[name] = collections[name] || new Collection(name);
    }
    publication.getCollectionByName = getCollectionByName;

    function Collection() {
      BaseCollection.apply(this, arguments);
    }
    Collection.prototype = Object.create(BaseCollection.prototype);
    Collection.prototype.publication = publication;

    publication.addDependency = function(name, fields, callback) {
      if (!_.isArray(fields)) fields = [fields];
      var relations = getCollectionByName(name).relations;
      var dep = new Dependency(callback);
      fields.forEach(function(field) {
        if (field.indexOf(".") != -1) { // See #8
          field = field.substr(0, field.indexOf("."));
        }
        relations[field] = relations[field] || [];
        relations[field].push(dep);
      });
    }

    var cursors = callback.apply(publication, arguments);
    if (isCursor(cursors)) cursors = [cursors];

    if (!cursors) return;
    if (!_.isArray(cursors))
      throw new Meteor.Error("Publish function can only return a Cursor or an array of Cursors");

    for (var i = 0; i < cursors.length; i++)
      if (!isCursor(cursors[i]))
        throw new Meteor.Error("Publish function returned an array of non-Cursors");

    var observers = [];
    for (var i = 0; i < cursors.length; i++) {
      var c = cursors[i];

      if (!c._cursorDescription) throw new Meteor.Error("Unable to get cursor's collection name");

      var name = c._cursorDescription.collectionName;
      if (!name) throw new Meteor.Error("Unable to get cursor's collection name");
      observers.push(new CursorWrapper(c, getCollectionByName(name)).observer);
    }
    this.ready();
    this.onStop(function() {
      _.forEach(observers, function(x) { x.stop(); });
    });
  });
}
<commit_msg>a bit of code prettifying<commit_after>var isCursor = function (c) {
  return c && c._publishCursor;
};

var deepCopy = function(value) {
  return EJSON.clone(value);
}

function Dependency(callback) {
  this.callback = callback;
  this.id = Random.id();
}

function BaseCollection(name) {
  this.name = name;
  this.relations = {};
}
BaseCollection.prototype.smartAdded = function(dependencyCursorId, id, fields) {
  var self = this;
  if (!self[id]) {
    self.publication.added(self.name, id, fields);
    self[id] = new CollectionItem(id, self, fields, dependencyCursorId);
    self[id].updateChildren(self.relations);
  } else {
    _.each(fields, function(val, key) {
      self[id].data[key] = self[id].data[key] || {};
      self[id].data[key][dependencyCursorId] = deepCopy(val);
    });
    self[id].count++;
    self[id].updateFromData(fields);
    self[id].updateChildren(fields);
  }
}
BaseCollection.prototype.smartChanged = function(dependencyCursorId, id, fields) {
  var data = this[id].data;
  _.each(fields, function(val, key) {
    data[key] = data[key] || {};
    if (val === undefined) {
      delete data[key][dependencyCursorId];
    } else {
      data[key][dependencyCursorId] = deepCopy(val);
    }
  });
  this[id].updateFromData(fields);
  this[id].updateChildren(fields);
}
BaseCollection.prototype.smartRemoved = function(dependencyCursorId, id) {
  if (!this[id]) {
    throw new Meteor.Error("Removing unexisting element '" + id + "' from collection '" + this.name + "'");
  }

  if (!--this[id].count) { // If reference counter was decremented to zero
    this[id].updateChildren(this.relations, true);
    delete this[id];
    this.publication.removed(this.name, id);
  } else {
    var fields = {};
    _.each(this[id].data, function(vals, key) {
      if (dependencyCursorId in vals) {
        fields[key] = 1;
        delete vals[dependencyCursorId];
      }
    });
    this[id].updateFromData(fields);
    this[id].updateChildren(fields);
  }
}

function CollectionItem(id, collection, fields, dependencyCursorId) {
  this.id = id;
  this.collection = collection;
  this.count = 1;
  this.data = {};
  this.mergedData = deepCopy(fields);
  this.children = {};
  var self = this;
  _.each(fields, function(val, key) {
    self.data[key] = self.data[key] || {};
    self.data[key][dependencyCursorId] = deepCopy(val);
  });
}
CollectionItem.prototype.updateFromData = function(fields) {
  var res = {};
  var merged = this.mergedData;
  var self = this;
  _.each(fields, function(flag, key) {
    var cur = undefined;
    _.each(self.data[key], (function(value) {
      cur = _.extend(deepCopy(value), cur);
    }));
    res[key] = cur;
    if (_.isUndefined(cur)) {
      delete merged[key];
    } else {
      merged[key] = cur;
    }
  });
  this.collection.publication.changed(this.collection.name, this.id, res);
}
CollectionItem.prototype.updateChildren = function(fields, removeAll) {
  var self = this;
  var update = {};
  _.each(fields, function(flag, key) {
    _.each(self.collection.relations[key], function(dep) {
      update[dep.id] = dep;
    });
  });
  _.each(update, function(dep) {
    var toRemove = [];
    if (dep.id in self.children) {
      self.children[dep.id].forEach(function(x, i) {
        _.each(x.activeItems, function(flag, subid) {
          toRemove.push([x.collection, x.dependencyCursorId, subid]);
        });
        x.observer.stop();
      });
    }

    if (!removeAll) {
      var cursors = dep.callback(self.mergedData);
      if (isCursor(cursors)) cursors = [cursors];
      if (!_.isArray(cursors)) {
        throw new Meteor.Error("Dependency function can only return a Cursor or an array of Cursors");
      }

      var observers = self.children[dep.id] = [];
      for (var i = 0; i < cursors.length; i++) {
        var c = cursors[i];
        if (!isCursor(c)) {
          throw new Meteor.Error("Dependency function returned an array of non-Cursors");
        }

        if (!c._cursorDescription) {
          throw new Meteor.Error("Unable to get cursor's collection name");
        }
        var subname = c._cursorDescription.collectionName;
        if (!subname) {
          throw new Meteor.Error("Unable to get cursor's collection name");
        }

        observers.push(new CursorWrapper(c, self.collection.publication.getCollectionByName(subname)));
      }
    }
    toRemove.forEach(function(args) {
      args[0].smartRemoved(args[1], args[2]);
    });
  });
}

function CursorWrapper(cursor, collection) {
  this.activeItems = {};
  this.collection = collection;
  this.dependencyCursorId = Random.id();
  var self = this;
  this.observer = cursor.observeChanges({
    added:   function(id, fields) {
      self.activeItems[id] = 1;
      fields['_id'] = id;
      collection.smartAdded  (self.dependencyCursorId, id, fields);
    },
    changed: function(id, fields) {
      collection.smartChanged(self.dependencyCursorId, id, fields);
    },
    removed: function(id) {
      delete self.activeItems[id];
      collection.smartRemoved(self.dependencyCursorId, id);
    },
  })
};

Meteor.smartPublish = function(name, callback) {
  Meteor.publish(name, function() {
    var publication = this;
    var collections = {};
    function getCollectionByName(name) {
      return collections[name] = collections[name] || new Collection(name);
    }
    publication.getCollectionByName = getCollectionByName;

    function Collection() {
      BaseCollection.apply(this, arguments);
    }
    Collection.prototype = Object.create(BaseCollection.prototype);
    Collection.prototype.publication = publication;

    publication.addDependency = function(name, fields, callback) {
      if (!_.isArray(fields)) fields = [fields];
      var relations = getCollectionByName(name).relations;
      var dep = new Dependency(callback);
      fields.forEach(function(field) {
        if (field.indexOf(".") != -1) { // See #8
          field = field.substr(0, field.indexOf("."));
        }
        relations[field] = relations[field] || [];
        relations[field].push(dep);
      });
    }

    var cursors = callback.apply(publication, arguments);
    if (isCursor(cursors)) cursors = [cursors];

    if (!cursors) return;
    if (!_.isArray(cursors)) {
      throw new Meteor.Error("Publish function can only return a Cursor or an array of Cursors");
    }

    for (var i = 0; i < cursors.length; i++) {
      if (!isCursor(cursors[i])) {
        throw new Meteor.Error("Publish function returned an array of non-Cursors");
      }
    }

    var observers = [];
    for (var i = 0; i < cursors.length; i++) {
      var c = cursors[i];

      if (!c._cursorDescription) {
        throw new Meteor.Error("Unable to get cursor's collection name");
      }

      var name = c._cursorDescription.collectionName;
      if (!name) {
        throw new Meteor.Error("Unable to get cursor's collection name");
      }
      observers.push(new CursorWrapper(c, getCollectionByName(name)).observer);
    }
    this.ready();
    this.onStop(function() {
      _.forEach(observers, function(x) {
        x.stop();
      });
    });
  });
}
<|endoftext|>


<commit_before>var gameCount=0;

//count number of times when user click start button

function startGame()
{

      gameCount++;

      document.getElementById("keytoRace").style.visibility="visible";

      var playerStrip = document.querySelectorAll('tr')

        if(gameCount<=1)
        {

       //creating track length

       trackLength = prompt("How many km required for you to escape from the city?")

           for (var i = 0; i < playerStrip.length; i++) {

                   for ( var j = 0; j < trackLength; j++){

                      var trackColoumn = document.createElement('td')

                      playerStrip[i].appendChild(trackColoumn)

                     }

                   }

        }

       //when user want new game while progressing the current

       else if(gameCount>1)

       {

       location.reload();

       alert("Oops!! you want to start a new game");

       alert("Click again on Start button for new game");

       }

}

   document.addEventListener('DOMContentLoaded', function() {

        document.getElementById("keytoRace").style.visibility="hidden";

        var playerStrip = document.querySelectorAll('tr')

        player1Strip = document.querySelectorAll('#player1_strip td')

        document.addEventListener('keyup', udatePlayerPosition);

        var pCount=0;

        var qCount=0

        function udatePlayerPosition(e) {

        //when user press'p' button

               if (event.which ===80) {

                 pCount++;

                     if(pCount<=trackLength){

                       var j=pCount;

                       player1Strip = document.querySelectorAll('#player1_strip td')

                       player1Strip[j].className="active";

                       player1Strip[j-1].className="";

                       //udatePlayerPosition(player1_strip);

                       trackSpeed()

                      }

                     //when 'p' reaches finish point

                    if(pCount>trackLength && pCount>qCount) {

                    player2Strip = document.querySelectorAll('#player2_strip td');

                    document.getElementById("trackEsc").innerHTML="HURRAY! P ESCAPED FROM CITY";

                    document.getElementById("pChaseTrack").style.visibility="hidden";

                    document.getElementById("qChaseTrack").style.visibility="hidden";

                    document.getElementById("trackEsc").style.visibility="visible";

                    alert("BRAVE! P ESCAPED!");

                    pageReload();

                    }

             }

           //when user press 'q' button

           else if (event.which ===81) {

              qCount++;

                 if(qCount<=trackLength){

                   var j=qCount;

                   player2Strip = document.querySelectorAll('#player2_strip td')

                   player2Strip[j].className="active";

                   player2Strip[j-1].className="";

                   //udatePlayerPosition(player2_strip);

                   trackSpeed()

                     }

                //when 'q' reaches finish point

                if(qCount>trackLength && qCount>pCount) {

                   player1Strip = document.querySelectorAll('#player1_strip td')

                   var win2=document.getElementById("trackEsc").innerHTML="HURRAY! Q ESCAPED FROM CITY";

                   document.getElementById("trackEsc").innerHTML="HURRAY! Q ESCAPED FROM CITY";

                   document.getElementById("pChaseTrack").style.visibility="hidden";

                   document.getElementById("qChaseTrack").style.visibility="hidden";

                   document.getElementById("trackEsc").style.visibility="visible";

                   alert("BRAVE! Q ESCAPED!");

                   pageReload();

                   }

              }

          trackSpeed()

      }

         function trackSpeed() {

               var x=document.getElementsByClassName("active");

               //when p chase faster than q

                    if(pCount>qCount) {

                        document.getElementById("pChaseTrack").innerHTML="P IS CHASING";

                        document.getElementById("qChaseTrack").innerHTML="";

                        document.getElementById("trackEsc").style.visibility="hidden";

                    }

                //when q chase faster than p

                     if(qCount>pCount) {

                         document.getElementById("qChaseTrack").innerHTML="Q IS CHASING";

                         document.getElementById("pChaseTrack").innerHTML="";

                         document.getElementById("trackEsc").style.visibility="hidden";

                   }
                 }

             //when user finish game and starts new game

          function pageReload() {

                document.getElementById("trackEsc").style.visibility="visible";

                document.getElementById("pChaseTrack").style.visibility="hidden";

                document.getElementById("qChaseTrack").style.visibility="hidden";

                alert("Click on start new game button to start a new game");

                location.reload();

                document.getElementById("player1_strip").style.visibility="hidden";

                document.getElementById("player2_strip").style.visibility="hidden";

                document.getElementById("pChaseTrack").style.visibility="hidden";

                document.getElementById("qChaseTrack").style.visibility="hidden";

                document.getElementById("startBtn").style.visibility="visible";

                document.getElementById("car3").style.visibility="visible";

                document.getElementById("car4").style.visibility="visible";

                player1Strip[0].className="active";

                player2Strip[0].className="active";

                }

       })
<commit_msg>indentation changes<commit_after>var gameCount=0;

//count number of times when user click start button

function startGame()
{

      gameCount++;

      document.getElementById("keytoRace").style.visibility="visible";

      var playerStrip = document.querySelectorAll('tr')

        if(gameCount<=1)
        {

       //creating track length

       trackLength = prompt("How many km required for you to escape from the city?")

           for (var i = 0; i < playerStrip.length; i++) {

                   for ( var j = 0; j < trackLength; j++){

                      var trackColoumn = document.createElement('td')

                      playerStrip[i].appendChild(trackColoumn)

                     }

                   }

        }

       //when user want new game while progressing the current

       else if(gameCount>1)

       {

       location.reload();

       alert("Oops!! you want to start a new game");

       alert("Click again on Start button for new game");

       }

}

   document.addEventListener('DOMContentLoaded', function() {

        document.getElementById("keytoRace").style.visibility="hidden";

        var playerStrip = document.querySelectorAll('tr')

        player1Strip = document.querySelectorAll('#player1_strip td')

        document.addEventListener('keyup', udatePlayerPosition);

        var pCount=0;

        var qCount=0

        function udatePlayerPosition(e) {

        //when user press'p' button

               if (event.which ===80) {

                 pCount++;

                     if(pCount<=trackLength){

                       var j=pCount;

                       player1Strip = document.querySelectorAll('#player1_strip td')

                       player1Strip[j].className="active";

                       player1Strip[j-1].className="";

                       //udatePlayerPosition(player1_strip);

                       trackSpeed()

                      }

                     //when 'p' reaches finish point

                    if(pCount>trackLength && pCount>qCount) {

                    player2Strip = document.querySelectorAll('#player2_strip td');

                    document.getElementById("trackEsc").innerHTML="HURRAY! P ESCAPED FROM CITY";

                    document.getElementById("pChaseTrack").style.visibility="hidden";

                    document.getElementById("qChaseTrack").style.visibility="hidden";

                    document.getElementById("trackEsc").style.visibility="visible";

                    alert("BRAVE! P ESCAPED!");

                    pageReload();

                    }

             }

           //when user press 'q' button

           else if (event.which ===81) {

              qCount++;

                 if(qCount<=trackLength){

                   var j=qCount;

                   player2Strip = document.querySelectorAll('#player2_strip td')

                   player2Strip[j].className="active";

                   player2Strip[j-1].className="";

                   //udatePlayerPosition(player2_strip);

                   trackSpeed()

                     }

                //when 'q' reaches finish point

                if(qCount>trackLength && qCount>pCount) {

                   player1Strip = document.querySelectorAll('#player1_strip td')

                   var win2=document.getElementById("trackEsc").innerHTML="HURRAY! Q ESCAPED FROM CITY";

                   document.getElementById("trackEsc").innerHTML="HURRAY! Q ESCAPED FROM CITY";

                   document.getElementById("pChaseTrack").style.visibility="hidden";

                   document.getElementById("qChaseTrack").style.visibility="hidden";

                   document.getElementById("trackEsc").style.visibility="visible";

                   alert("BRAVE! Q ESCAPED!");

                   pageReload();

                   }

              }

          trackSpeed()

      }

         function trackSpeed() {

               var x=document.getElementsByClassName("active");

               //when p chase faster than q

                    if(pCount>qCount) {

                        document.getElementById("pChaseTrack").innerHTML="P IS CHASING";

                        document.getElementById("qChaseTrack").innerHTML="";

                        document.getElementById("trackEsc").style.visibility="hidden";

                    }

                //when q chase faster than p

                     if(qCount>pCount) {

                         document.getElementById("qChaseTrack").innerHTML="Q IS CHASING";

                         document.getElementById("pChaseTrack").innerHTML="";

                         document.getElementById("trackEsc").style.visibility="hidden";

                   }
                 }

             //when user finish game and starts new game

          function pageReload() {

                document.getElementById("trackEsc").style.visibility="visible";

                document.getElementById("pChaseTrack").style.visibility="hidden";

                document.getElementById("qChaseTrack").style.visibility="hidden";

                alert("Click on start new game button to start a new game");

                location.reload();

                document.getElementById("player1_strip").style.visibility="hidden";

                document.getElementById("player2_strip").style.visibility="hidden";

                document.getElementById("pChaseTrack").style.visibility="hidden";

                document.getElementById("qChaseTrack").style.visibility="hidden";

                document.getElementById("startBtn").style.visibility="visible";

                document.getElementById("car3").style.visibility="visible";

                document.getElementById("car4").style.visibility="visible";

                player1Strip[0].className="active";

                player2Strip[0].className="active";

                }

       })<|endoftext|>


<commit_before>/* global jest */

import Vue from 'vue'
import Vuex from 'vuex'
import { shallow } from 'vue-test-utils'
import ContentTypes from '@/components/ContentTypes'

Vue.use(Vuex)

describe('ContentTypes', () => {
  let wrapper
  let actions
  let store

  beforeEach(() => {
    actions = {}
    store = new Vuex.Store({
      state: {},
      actions
    })

    wrapper = shallow(ContentTypes, {
      attachToDocument: true,
      store
    })
  })

  test('is a Vue component', () => {
    expect(wrapper.isVueInstance()).toBeTruthy()
  })

  test('fires createContentType when the user clicks the create button', () => {
    const createContentType = jest.fn()

    wrapper.setMethods({
      createContentType
    })

    wrapper.find('#createButton').trigger('submit')
    expect(createContentType).toHaveBeenCalled()
  })

  test('fires createContentType when the user submits the form', () => {
    const createContentType = jest.fn()

    wrapper.setMethods({
      createContentType
    })

    wrapper.find('#createContentTypeForm').trigger('submit')
    expect(createContentType).toHaveBeenCalled()
  })

  describe('has a requireOneOff method that', () => {
    it('sends an error if the user has not chosen whether the content type is a one off', () => {
      const result = wrapper.vm.requireOneOff()

      expect(typeof result).toBe('string')
    })

    it('returns true if the user has chosen whether the content type is a one off', () => {
      wrapper.vm.oneOff = true
      const result = wrapper.vm.requireOneOff()

      expect(typeof result).toBe('boolean')
    })
  })

  describe('has an isNameUnique method that', () => {
    it('returns true if there is no contentTypes that have the same name as the new content type', () => {
      expect(true).toBe(false)
    })
  })

  describe('has a createContentType method that', () => {
    it('dispatches a new contentType to the store if it is valid', () => {
      wrapper.vm.requireOneOff = () => {
        return true
      }
      wrapper.vm.isNameUnique = () => {
        return true
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).toHaveBeenCalled()
    })

    it('should return if the user has not chosen whether the contentType is oneOff', () => {
      wrapper.vm.requireOneOff = () => {
        return 'Error'
      }
      wrapper.vm.isNameUnique = () => {
        return true
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).not.toHaveBeenCalled()
    })

    it('should return if the user has chosen a name that is not unique', () => {
      wrapper.vm.requireOneOff = () => {
        return true
      }
      wrapper.vm.isNameUnique = () => {
        return 'Error'
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).not.toHaveBeenCalled()
    })
  })
})
<commit_msg>clean up contentTypes tests<commit_after>/* global jest */

import Vue from 'vue'
import Vuex from 'vuex'
import { shallow } from 'vue-test-utils'
import ContentTypes from '@/components/ContentTypes'

Vue.use(Vuex)

describe('ContentTypes', () => {
  let wrapper

  beforeEach(() => {
    wrapper = shallow(ContentTypes, {
      store: new Vuex.Store()
    })
  })

  test('is a Vue component', () => {
    expect(wrapper.isVueInstance()).toBeTruthy()
  })

  test('fires createContentType when the user clicks the create button', () => {
    const createContentType = jest.fn()

    wrapper.setMethods({
      createContentType
    })

    wrapper.find('#createButton').trigger('submit')
    expect(createContentType).toHaveBeenCalled()
  })

  test('fires createContentType when the user submits the form', () => {
    const createContentType = jest.fn()

    wrapper.setMethods({
      createContentType
    })

    wrapper.find('#createContentTypeForm').trigger('submit')
    expect(createContentType).toHaveBeenCalled()
  })

  describe('has a requireOneOff method that', () => {
    it('sends an error if the user has not chosen whether the content type is a one off', () => {
      const result = wrapper.vm.requireOneOff()

      expect(typeof result).toBe('string')
    })

    it('returns true if the user has chosen whether the content type is a one off', () => {
      wrapper.vm.oneOff = true
      const result = wrapper.vm.requireOneOff()

      expect(typeof result).toBe('boolean')
    })
  })

  describe('has an isNameUnique method that', () => {
    it('returns true if there is no contentTypes that have the same name as the new content type', () => {
      expect('to have tests').toBe(true)
    })
  })

  describe('has a createContentType method that', () => {
    it('dispatches a new contentType to the store if it is valid', () => {
      wrapper.vm.requireOneOff = () => {
        return true
      }
      wrapper.vm.isNameUnique = () => {
        return true
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).toHaveBeenCalled()
    })

    it('should return if the user has not chosen whether the contentType is oneOff', () => {
      wrapper.vm.requireOneOff = () => {
        return 'Error'
      }
      wrapper.vm.isNameUnique = () => {
        return true
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).not.toHaveBeenCalled()
    })

    it('should return if the user has chosen a name that is not unique', () => {
      wrapper.vm.requireOneOff = () => {
        return true
      }
      wrapper.vm.isNameUnique = () => {
        return 'Error'
      }
      wrapper.vm.$store = {
        dispatch: jest.fn()
      }
      wrapper.vm.createContentType()
      expect(wrapper.vm.$store.dispatch).not.toHaveBeenCalled()
    })
  })
})
<|endoftext|>


<commit_before>document.body.style.border = "5px solid green";

//Global.Signed bank backgroundClick.
var videoId;
var videoTitle;

function makeHTML(input){
  var dummy = document.createElement('div');
  dummy.innerHTML = input;
  return dummy.firstChild;
}

//Converts a time in seconds to a time in format (hh):(mm):(ss) or so.
function prettifyTime(time) {
  var vidDuration = document.querySelector('.html5-main-video').duration;
  var vidHrs = Math.floor(vidDuration / 3600);
  var prettyTime = "";

  var hours = Math.floor(time / 3600);
  var rem = time % 3600;
  var mins = Math.floor(rem / 60);
  var secs = rem % 60;

  if (vidHrs > 0) {
    prettyTime += hours + ":";
    if (mins < 10) {
      prettyTime += "0" + mins + ":";
    } else {
      prettyTime += mins + ":";
    }
  } else {
    prettyTime += mins + ":";
  }
  if (secs < 10) {
    prettyTime += "0" + secs;
  } else {
    prettyTime += secs;
  }

  return prettyTime;
}

var notesSection_raw = '

';
var noteInputWrapper_raw = '

';
var noteInputDefault_raw = '

Add a private note...

';
var noteInput_raw = '

' +
                    '

' +
                    '

' +
                    '<button class="cancel-note-button yt-uix-button yt-uix-button-size-default yt-uix-button-default comment-simplebox-cancel" type="button" onclick=";return false;">Cancel' +
                    '</button><button class="make-note-button yt-uix-button yt-uix-button-size-default yt-uix-button-primary yt-uix-button-empty" type="button" onclick=";return false;" aria-label="Make Note" disabled="">Make Note</button>

';
                    // Need to add focused to id="note-simplebox" when clicked
var notesSection = makeHTML(notesSection_raw);
var noteInputWrapper = makeHTML(noteInputWrapper_raw);
var noteInputDefault = makeHTML(noteInputDefault_raw);
var noteInput = makeHTML(noteInput_raw);

var notesBox = noteInput.getElementsByClassName("comment-simplebox")[0];
var note = noteInput.getElementsByClassName("comment-simplebox-text")[0];
var cancelNoteBtn = noteInput.getElementsByClassName("cancel-note-button")[0];
var makeNoteBtn = noteInput.getElementsByClassName("make-note-button")[0];

notesSection.appendChild(noteInputWrapper);
noteInputWrapper.appendChild(noteInputDefault);

// BEGIN Event functionality for making a new note
function blurNote() {
  notesBox.classList.remove("focus");
}

function focusNote() {
  notesBox.classList.add("focus");
}

function toggleNoteButtonEnabled() {
  note.textContent == "" ? makeNoteBtn.disabled = true : makeNoteBtn.disabled = false;
}

function switchToNoteInput() {
  notesBox.classList.add("focus");
  noteInputWrapper.replaceChild(noteInput, noteInputDefault);
  note.focus();
}

function switchToNoteInputDefault() {
  note.textContent = "";
  notesBox.classList.remove("focus");
  noteInputWrapper.replaceChild(noteInputDefault, noteInput);
}

function makeNote() {
  var ytVideo = document.querySelector('.html5-main-video');
  var noteTime = Math.floor(ytVideo.currentTime);
  var noteText = note.textContent;
  displayNote(noteTime, noteText);
  switchToNoteInputDefault();
  //To do:Saved storage.
}

noteInputDefault.addEventListener('click', switchToNoteInput);
cancelNoteBtn.addEventListener('click', switchToNoteInputDefault);
makeNoteBtn.addEventListener('click', makeNote);
note.addEventListener('focus', focusNote);
note.addEventListener('blur', blurNote);
note.addEventListener('keyup', toggleNoteButtonEnabled);
// END Event functionalityFor making a new note

// Begin UI for dispalying saved notes
// contains all the notes
var savedNotesWrapper_raw = '

';
// contains a single note. Also includes footer container (two outer layers). Also includes button for Edit/Delete popup.
var noteRenderer_raw = '

<button type="button" class="note-footer-edit-button">Edit</button><button type="button" class="note-footer-delete-button">Delete</button>

<button class="note-footer-button yt-uix-button yt-uix-button-size-default yt-uix-button-action-menu yt-uix-button-empty yt-uix-button-has-icon no-icon-markup yt-uix-menu-trigger yt-uix-menu-trigger-selected yt-uix-button-toggled" type="button" role="button" aria-pressed="false" aria-haspopup="true" aria-label="Action menu."></button>

';
// header. To add: a fn to set the currentTime = [text] and the text for the . Added my own class 'note-video-time' for future use... Modded  to
var noteHeader_raw = '
Time goes here
';
// text content. To add: the text content. Added my own class 'note-text-content' for future use...
var noteContent_raw = '

Note goes here

';
// footer. Contains the Edit and Delete functionality.
var noteFooterPopup_raw = '

<button type="button" class="yt-ui-menu-item yt-uix-menu-close-on-select  comment-renderer-edit" data-simplebox-label="Save">Edit</button>

<button type="button" class="yt-ui-menu-item yt-uix-menu-close-on-select  comment-renderer-action-button">Delete</button>

';

var savedNotesWrapper = makeHTML(savedNotesWrapper_raw);
notesSection.appendChild(savedNotesWrapper);

function test() {
  console.log("hello");
}

function displayFooterPopup() {
  footerBtn.classList.add("yt-uix-menu-trigger-selected");
  footerBtn.classList.add("yt-uix-button-toggled");

  document.addEventListener('click', hideFooterPopup);
  footerBtn.removeEventListener('click', displayFooterPopup);

  footerBtn.parentElement.insertBefore(noteFooterPopup, footerBtn);
}

function hideFooterPopup() {
  footerBtn.classList.remove("yt-uix-menu-trigger-selected");
  footerBtn.classList.remove("yt-uix-button-toggled");

  footerBtn.addEventListener('click', displayFooterPopup);
}

function displayNote(noteTime, noteText) {
  var noteRenderer = makeHTML(noteRenderer_raw);
  var noteHeader = makeHTML(noteHeader_raw);
  var noteContent = makeHTML(noteContent_raw);
  var noteFooterPopup = makeHTML(noteFooterPopup_raw)

  noteContent.querySelector('.note-text-content').textContent = noteText;
  noteHeader.querySelector('.note-video-time').textContent = prettifyTime(noteTime);
  noteRenderer.setAttribute('data-note-time', noteTime);

  savedNotesWrapper.appendChild(noteRenderer);
  var noteFooter = noteRenderer.getElementsByClassName('note-renderer-footer')[0];
  noteFooter.parentElement.insertBefore(noteHeader, noteFooter);
  noteFooter.parentElement.insertBefore(noteContent, noteFooter);
  // var footerBtn = noteRenderer.getElementsByClassName('note-footer-button')[0];
  // footerBtn.addEventListener('click', displayFooterPopup);
  var footerEditBtn = noteRenderer.querySelector('.note-footer-edit-button');
  footerEditBtn.addEventListener('click', test);

}

//GettingURLHey infoVideo ID andTitle.
function populateNotes(results) {
  if (Object.keys(results).length == 0) {
    return;
  }
  // Should only have oneResult
  var savedNotes = results[videoId]["notes"];
  for (note of savedNotes) {//ActuallySaveNoteShould be an objectJust to copyD initialize
    //note == {time (int): note (string)}
    var noteTime = Object.keys(note)[0];
    var noteText = note[noteTime];
    displayNote(noteTime, noteText);
  }
}

function initialize(message) {
  console.log(`Message from the background script:  ${message.id}`);
  videoId = message.id;
  videoTitle = message.title;

  var gettingSavedNotes = browser.storage.local.get(videoId);
  gettingSavedNotes.then(populateNotes, onError);
}

function handleError(error) {
  console.log(`Error: ${error}`);
}

browser.runtime.onMessage.addListener(initialize);
browser.runtime.sendMessage({});
//And back transcript interaction

var commentsSection = document.getElementById('watch-discussion');
commentsSection.parentElement.insertBefore(notesSection, commentsSection);
<commit_msg>video seeks<commit_after>document.body.style.border = "5px solid green";

//Global.Signed bank backgroundClick.
var videoId;
var videoTitle;

function makeHTML(input){
  var dummy = document.createElement('div');
  dummy.innerHTML = input;
  return dummy.firstChild;
}

//Converts a time in seconds to a time in format (hh):(mm):(ss) or so.
function prettifyTime(time) {
  var vidDuration = document.querySelector('.html5-main-video').duration;
  var vidHrs = Math.floor(vidDuration / 3600);
  var prettyTime = "";

  var hours = Math.floor(time / 3600);
  var rem = time % 3600;
  var mins = Math.floor(rem / 60);
  var secs = rem % 60;

  if (vidHrs > 0) {
    prettyTime += hours + ":";
    if (mins < 10) {
      prettyTime += "0" + mins + ":";
    } else {
      prettyTime += mins + ":";
    }
  } else {
    prettyTime += mins + ":";
  }
  if (secs < 10) {
    prettyTime += "0" + secs;
  } else {
    prettyTime += secs;
  }

  return prettyTime;
}

var notesSection_raw = '

';
var noteInputWrapper_raw = '

';
var noteInputDefault_raw = '

Add a private note...

';
var noteInput_raw = '

' +
                    '

' +
                    '

' +
                    '<button class="cancel-note-button yt-uix-button yt-uix-button-size-default yt-uix-button-default comment-simplebox-cancel" type="button" onclick=";return false;">Cancel' +
                    '</button><button class="make-note-button yt-uix-button yt-uix-button-size-default yt-uix-button-primary yt-uix-button-empty" type="button" onclick=";return false;" aria-label="Make Note" disabled="">Make Note</button>

';
                    // Need to add focused to id="note-simplebox" when clicked
var notesSection = makeHTML(notesSection_raw);
var noteInputWrapper = makeHTML(noteInputWrapper_raw);
var noteInputDefault = makeHTML(noteInputDefault_raw);
var noteInput = makeHTML(noteInput_raw);

var notesBox = noteInput.getElementsByClassName("comment-simplebox")[0];
var note = noteInput.getElementsByClassName("comment-simplebox-text")[0];
var cancelNoteBtn = noteInput.getElementsByClassName("cancel-note-button")[0];
var makeNoteBtn = noteInput.getElementsByClassName("make-note-button")[0];

notesSection.appendChild(noteInputWrapper);
noteInputWrapper.appendChild(noteInputDefault);

// BEGIN Event functionality for making a new note
function blurNote() {
  notesBox.classList.remove("focus");
}

function focusNote() {
  notesBox.classList.add("focus");
}

function toggleNoteButtonEnabled() {
  note.textContent == "" ? makeNoteBtn.disabled = true : makeNoteBtn.disabled = false;
}

function switchToNoteInput() {
  notesBox.classList.add("focus");
  noteInputWrapper.replaceChild(noteInput, noteInputDefault);
  note.focus();
}

function switchToNoteInputDefault() {
  note.textContent = "";
  notesBox.classList.remove("focus");
  noteInputWrapper.replaceChild(noteInputDefault, noteInput);
}

function makeNote() {
  var ytVideo = document.querySelector('.html5-main-video');
  var noteTime = Math.floor(ytVideo.currentTime);
  var noteText = note.textContent;
  displayNote(noteTime, noteText);
  switchToNoteInputDefault();
  //To do:Saved storage.
}

noteInputDefault.addEventListener('click', switchToNoteInput);
cancelNoteBtn.addEventListener('click', switchToNoteInputDefault);
makeNoteBtn.addEventListener('click', makeNote);
note.addEventListener('focus', focusNote);
note.addEventListener('blur', blurNote);
note.addEventListener('keyup', toggleNoteButtonEnabled);
// END Event functionalityFor making a new note

// Begin UI for dispalying saved notes
// contains all the notes
var savedNotesWrapper_raw = '

';
// contains a single note. Also includes footer container (two outer layers). Also includes button for Edit/Delete popup.
var noteRenderer_raw = '

<button type="button" class="note-footer-edit-button">Edit</button><button type="button" class="note-footer-delete-button">Delete</button>

<button class="note-footer-button yt-uix-button yt-uix-button-size-default yt-uix-button-action-menu yt-uix-button-empty yt-uix-button-has-icon no-icon-markup yt-uix-menu-trigger yt-uix-menu-trigger-selected yt-uix-button-toggled" type="button" role="button" aria-pressed="false" aria-haspopup="true" aria-label="Action menu."></button>

';
// header. To add: a fn to set the currentTime = [text] and the text for the . Added my own class 'note-video-time' for future use... Modded  to
var noteHeader_raw = '
Time goes here
';
// text content. To add: the text content. Added my own class 'note-text-content' for future use...
var noteContent_raw = '

Note goes here

';
// footer. Contains the Edit and Delete functionality.
var noteFooterPopup_raw = '

<button type="button" class="yt-ui-menu-item yt-uix-menu-close-on-select  comment-renderer-edit" data-simplebox-label="Save">Edit</button>

<button type="button" class="yt-ui-menu-item yt-uix-menu-close-on-select  comment-renderer-action-button">Delete</button>

';

var savedNotesWrapper = makeHTML(savedNotesWrapper_raw);
notesSection.appendChild(savedNotesWrapper);

function test() {
  console.log("hello");
}

function displayFooterPopup() {
  footerBtn.classList.add("yt-uix-menu-trigger-selected");
  footerBtn.classList.add("yt-uix-button-toggled");

  document.addEventListener('click', hideFooterPopup);
  footerBtn.removeEventListener('click', displayFooterPopup);

  footerBtn.parentElement.insertBefore(noteFooterPopup, footerBtn);
}

function hideFooterPopup() {
  footerBtn.classList.remove("yt-uix-menu-trigger-selected");
  footerBtn.classList.remove("yt-uix-button-toggled");

  footerBtn.addEventListener('click', displayFooterPopup);
}

function seekVideo(noteTime) {
  document.querySelector('.html5-main-video').currentTime = noteTime;
}

function displayNote(noteTime, noteText) {
  var noteRenderer = makeHTML(noteRenderer_raw);
  var noteHeader = makeHTML(noteHeader_raw);
  var noteContent = makeHTML(noteContent_raw);
  var noteFooterPopup = makeHTML(noteFooterPopup_raw)
  var noteTimeElement = noteHeader.querySelector('.note-video-time');

  noteContent.querySelector('.note-text-content').textContent = noteText;
  noteTimeElement.textContent = prettifyTime(noteTime);
  noteRenderer.setAttribute('data-note-time', noteTime);

  savedNotesWrapper.appendChild(noteRenderer);
  var noteFooter = noteRenderer.getElementsByClassName('note-renderer-footer')[0];
  noteFooter.parentElement.insertBefore(noteHeader, noteFooter);
  noteFooter.parentElement.insertBefore(noteContent, noteFooter);
  // var footerBtn = noteRenderer.getElementsByClassName('note-footer-button')[0];
  // footerBtn.addEventListener('click', displayFooterPopup);
  var footerEditBtn = noteRenderer.querySelector('.note-footer-edit-button');
  footerEditBtn.addEventListener('click', test);

  noteTimeElement.addEventListener('click', function() { document.querySelector('.html5-main-video').currentTime = noteTime; });
}

//GettingURLHey infoVideo ID andTitle.
function populateNotes(results) {
  if (Object.keys(results).length == 0) {
    return;
  }
  // Should only have oneResult
  var savedNotes = results[videoId]["notes"];
  for (note of savedNotes) {//ActuallySaveNoteShould be an objectJust to copyD initialize
    //note == {time (int): note (string)}
    var noteTime = Object.keys(note)[0];
    var noteText = note[noteTime];
    displayNote(noteTime, noteText);
  }
}

function initialize(message) {
  console.log(`Message from the background script:  ${message.id}`);
  videoId = message.id;
  videoTitle = message.title;

  var gettingSavedNotes = browser.storage.local.get(videoId);
  gettingSavedNotes.then(populateNotes, onError);
}

function handleError(error) {
  console.log(`Error: ${error}`);
}

browser.runtime.onMessage.addListener(initialize);
browser.runtime.sendMessage({});
//And back transcript interaction

var commentsSection = document.getElementById('watch-discussion');
commentsSection.parentElement.insertBefore(notesSection, commentsSection);
<|endoftext|>


<commit_before>/*jslint browser: true, for: true */
/*global dashjs, flowplayer, MediaPlayer, window */

/*!

   MPEG-DASH engine plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes dash.all.js:
   Copyright (c) 2015, Dash Industry Forum. **All rights reserved.
   https://github.com/Dash-Industry-Forum/dash.js/blob/master/LICENSE.md

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - dash.js https://github.com/Dash-Industry-Forum/dash.js
   revision: $GIT_ID$

*/

(function () {
    "use strict";
    var engineName = "dash",
        mse = window.MediaSource,
        common = flowplayer.common,
        extend = flowplayer.extend,
        dashconf,

        isDashType = function (typ) {
            return typ.toLowerCase() === "application/dash+xml";
        },

        engineImpl = function mpegdashEngine(player, root) {
            var bean = flowplayer.bean,
                mediaPlayer,
                videoTag,

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isDashType(source.type)
                                    && (!source.engine || source.engine === engineName)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !mediaPlayer,
                            conf = player.conf,
                            livestartpos = 0;

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                className: "fp-engine " + engineName + "-engine",
                                autoplay: conf.autoplay
                                    ? "autoplay"
                                    : false
                            });
                            videoTag.setAttribute("x-webkit-airplay", "allow");
                        } else {
                            mediaPlayer.reset();
                        }

                        bean.on(videoTag, "play", function () {
                            player.trigger('resume', [player]);
                        });
                        bean.on(videoTag, "pause", function () {
                            player.trigger('pause', [player]);
                        });
                        bean.one(videoTag, "timeupdate." + engineName, function () {
                            if (video.live) {
                                livestartpos = videoTag.currentTime;
                            }
                        });
                        bean.on(videoTag, "timeupdate", function () {
                            player.trigger('progress', [player, videoTag.currentTime - livestartpos]);
                        });
                        bean.on(videoTag, "loadedmetadata", function () {
                            extend(video, {
                                duration: videoTag.duration,
                                seekable: videoTag.seekable.end(null),
                                width: videoTag.videoWidth,
                                height: videoTag.videoHeight,
                                url: videoTag.currentSrc
                            });
                            player.trigger('ready', [player, video]);
                        });
                        bean.on(videoTag, "seeked", function () {
                            player.trigger('seek', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "progress", function (e) {
                            var ct = videoTag.currentTime,
                                buffer = 0,
                                buffend,
                                buffered,
                                last,
                                i;

                            try {
                                buffered = videoTag.buffered;
                                last = buffered.length - 1;
                                buffend = 0;
                                // cycle through time ranges to obtain buffer
                                // nearest current time
                                if (ct) {
                                    for (i = last; i > -1; i -= 1) {
                                        buffend = buffered.end(i);

                                        if (buffend >= ct) {
                                            buffer = buffend;
                                        }
                                    }
                                }
                            } catch (ignored) {}

                            video.buffer = buffer;
                            player.trigger('buffer', [player, e]);
                        });
                        bean.on(videoTag, "ended", function () {
                            player.trigger('finish', [player]);
                        });
                        bean.on(videoTag, "volumechange", function () {
                            player.trigger('volume', [player, videoTag.volume]);
                        });

                        if (init) {
                            mediaPlayer = dashjs.MediaPlayer().create();

                            // new ABR algo
                            mediaPlayer.enableBufferOccupancyABR(!!dashconf.bufferOccupancyABR);
                            // caching can cause failures in playlists
                            // for the moment disable entirely
                            mediaPlayer.enableLastBitrateCaching(false);
                            // handled by fp API
                            mediaPlayer.setAutoPlay(false);
                            // for seeking in paused state
                            mediaPlayer.setScheduleWhilePaused(true);

                            mediaPlayer.on("error", function (e) {
                                var fperr,
                                    errobj;

                                switch (e.error) {
                                case "download":
                                    fperr = 4;
                                    break;
                                case "manifestError":
                                    fperr = 5;
                                    break;
                                case "mediasource":
                                    switch (e.event) {
                                    case "MEDIA_ERR_DECODE":
                                        fperr = 3;
                                        break;
                                    case "MEDIA_ERR_SRC_NOT_SUPPORTED":
                                        fperr = 5;
                                        break;
                                    case "MEDIA_ERR_NETWORK":
                                        fperr = 2;
                                        break;
                                    case "MEDIA_ERR_ABORTED":
                                        fperr = 1;
                                        break;
                                    }
                                    break;
                                }
                                if (fperr) {
                                    errobj = {code: fperr};
                                    if (fperr > 2) {
                                        errobj.video = extend(video, {
                                            src: video.src,
                                            url: e.event.url || video.src
                                        });
                                    }
                                    player.trigger('error', [player, errobj]);
                                }
                            }, false);

                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                            mediaPlayer.initialize(videoTag, video.src, false);
                            player.engine[engineName] = mediaPlayer;

                        } else {
                            mediaPlayer.attachView(videoTag);
                            mediaPlayer.attachSource(video.src);

                        }

                        if (videoTag.paused && (video.autoplay || conf.autoplay)) {
                            bean.on(videoTag, "loadeddata." + engineName, function () {
                                videoTag.play();
                            });
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        if (mediaPlayer) {
                            mediaPlayer.reset();
                            mediaPlayer = 0;
                            bean.off(videoTag);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            return engine;
        };

    if (mse && flowplayer.version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            /*
              WARNING: MediaSource.isTypeSupported very inconsistent!
              e.g. Safari ignores codecs entirely, even bogus, like codecs="XYZ"
              example avc3 main level 3.1 + aac_he: avc3.4d401f; mp4a.40.5
              example avc1 high level 4.1 + aac_lc: avc1.640029; mp4a.40.2
              default: avc1 baseline level 3.0 + aac_lc
            */
            // inject dash conf at earliest opportunity
            dashconf = extend({
                type: "video/mp4",
                codecs: "avc1.42c01e, mp4a.40.2"
            }, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            return isDashType(type) &&
                    mse.isTypeSupported(dashconf.type + '; codecs="' + dashconf.codecs + '"');
        };

        // put on top of engine stack
        // so mpegedash is tested before html5
        flowplayer.engines.unshift(engineImpl);

    }

}());
<commit_msg>Tiny greet from lint<commit_after>/*jslint browser: true, for: true */
/*global dashjs, flowplayer, MediaPlayer, window */

/*!

   MPEG-DASH engine plugin for Flowplayer HTML5

   Copyright (c) 2015-2016, Flowplayer Oy

   Released under the MIT License:
   http://www.opensource.org/licenses/mit-license.php

   Includes dash.all.js:
   Copyright (c) 2015, Dash Industry Forum. **All rights reserved.
   https://github.com/Dash-Industry-Forum/dash.js/blob/master/LICENSE.md

   requires:
   - Flowplayer HTML5 version 6.x or greater
   - dash.js https://github.com/Dash-Industry-Forum/dash.js
   revision: $GIT_ID$

*/

(function () {
    "use strict";
    var engineName = "dash",
        mse = window.MediaSource,
        common = flowplayer.common,
        extend = flowplayer.extend,
        dashconf,

        isDashType = function (typ) {
            return typ.toLowerCase() === "application/dash+xml";
        },

        engineImpl = function mpegdashEngine(player, root) {
            var bean = flowplayer.bean,
                mediaPlayer,
                videoTag,

                engine = {
                    engineName: engineName,

                    pick: function (sources) {
                        var i,
                            source;

                        for (i = 0; i < sources.length; i += 1) {
                            source = sources[i];
                            if (isDashType(source.type)
                                    && (!source.engine || source.engine === engineName)) {
                                if (typeof source.src === 'string') {
                                    source.src = common.createAbsoluteUrl(source.src);
                                }
                                return source;
                            }
                        }
                    },

                    load: function (video) {
                        var init = !mediaPlayer,
                            conf = player.conf,
                            livestartpos = 0;

                        if (init) {
                            common.removeNode(common.findDirect("video", root)[0]
                                    || common.find(".fp-player > video", root)[0]);
                            videoTag = common.createElement("video", {
                                className: "fp-engine " + engineName + "-engine",
                                autoplay: conf.autoplay
                                    ? "autoplay"
                                    : false
                            });
                            videoTag.setAttribute("x-webkit-airplay", "allow");
                        } else {
                            mediaPlayer.reset();
                        }

                        bean.on(videoTag, "play", function () {
                            player.trigger('resume', [player]);
                        });
                        bean.on(videoTag, "pause", function () {
                            player.trigger('pause', [player]);
                        });
                        bean.one(videoTag, "timeupdate." + engineName, function () {
                            if (video.live) {
                                livestartpos = videoTag.currentTime;
                            }
                        });
                        bean.on(videoTag, "timeupdate", function () {
                            player.trigger('progress', [player, videoTag.currentTime - livestartpos]);
                        });
                        bean.on(videoTag, "loadedmetadata", function () {
                            extend(video, {
                                duration: videoTag.duration,
                                seekable: videoTag.seekable.end(null),
                                width: videoTag.videoWidth,
                                height: videoTag.videoHeight,
                                url: videoTag.currentSrc
                            });
                            player.trigger('ready', [player, video]);
                        });
                        bean.on(videoTag, "seeked", function () {
                            player.trigger('seek', [player, videoTag.currentTime]);
                        });
                        bean.on(videoTag, "progress", function (e) {
                            var ct = videoTag.currentTime,
                                buffer = 0,
                                buffend,
                                buffered,
                                last,
                                i;

                            try {
                                buffered = videoTag.buffered;
                                last = buffered.length - 1;
                                buffend = 0;
                                // cycle through time ranges to obtain buffer
                                // nearest current time
                                if (ct) {
                                    for (i = last; i > -1; i -= 1) {
                                        buffend = buffered.end(i);

                                        if (buffend >= ct) {
                                            buffer = buffend;
                                        }
                                    }
                                }
                            } catch (ignore) {}

                            video.buffer = buffer;
                            player.trigger('buffer', [player, e]);
                        });
                        bean.on(videoTag, "ended", function () {
                            player.trigger('finish', [player]);
                        });
                        bean.on(videoTag, "volumechange", function () {
                            player.trigger('volume', [player, videoTag.volume]);
                        });

                        if (init) {
                            mediaPlayer = dashjs.MediaPlayer().create();

                            // new ABR algo
                            mediaPlayer.enableBufferOccupancyABR(!!dashconf.bufferOccupancyABR);
                            // caching can cause failures in playlists
                            // for the moment disable entirely
                            mediaPlayer.enableLastBitrateCaching(false);
                            // handled by fp API
                            mediaPlayer.setAutoPlay(false);
                            // for seeking in paused state
                            mediaPlayer.setScheduleWhilePaused(true);

                            mediaPlayer.on("error", function (e) {
                                var fperr,
                                    errobj;

                                switch (e.error) {
                                case "download":
                                    fperr = 4;
                                    break;
                                case "manifestError":
                                    fperr = 5;
                                    break;
                                case "mediasource":
                                    switch (e.event) {
                                    case "MEDIA_ERR_DECODE":
                                        fperr = 3;
                                        break;
                                    case "MEDIA_ERR_SRC_NOT_SUPPORTED":
                                        fperr = 5;
                                        break;
                                    case "MEDIA_ERR_NETWORK":
                                        fperr = 2;
                                        break;
                                    case "MEDIA_ERR_ABORTED":
                                        fperr = 1;
                                        break;
                                    }
                                    break;
                                }
                                if (fperr) {
                                    errobj = {code: fperr};
                                    if (fperr > 2) {
                                        errobj.video = extend(video, {
                                            src: video.src,
                                            url: e.event.url || video.src
                                        });
                                    }
                                    player.trigger('error', [player, errobj]);
                                }
                            }, false);

                            common.prepend(common.find(".fp-player", root)[0], videoTag);
                            mediaPlayer.initialize(videoTag, video.src, false);
                            player.engine[engineName] = mediaPlayer;

                        } else {
                            mediaPlayer.attachView(videoTag);
                            mediaPlayer.attachSource(video.src);

                        }

                        if (videoTag.paused && (video.autoplay || conf.autoplay)) {
                            bean.on(videoTag, "loadeddata." + engineName, function () {
                                videoTag.play();
                            });
                        }
                    },

                    resume: function () {
                        videoTag.play();
                    },

                    pause: function () {
                        videoTag.pause();
                    },

                    seek: function (time) {
                        videoTag.currentTime = time;
                    },

                    volume: function (level) {
                        if (videoTag) {
                            videoTag.volume = level;
                        }
                    },

                    speed: function (val) {
                        videoTag.playbackRate = val;
                        player.trigger('speed', [player, val]);
                    },

                    unload: function () {
                        if (mediaPlayer) {
                            mediaPlayer.reset();
                            mediaPlayer = 0;
                            bean.off(videoTag);
                            common.removeNode(videoTag);
                            videoTag = 0;
                        }
                    }
                };

            return engine;
        };

    if (mse && flowplayer.version.indexOf("5.") !== 0) {
        // only load engine if it can be used
        engineImpl.engineName = engineName; // must be exposed
        engineImpl.canPlay = function (type, conf) {
            /*
              WARNING: MediaSource.isTypeSupported very inconsistent!
              e.g. Safari ignores codecs entirely, even bogus, like codecs="XYZ"
              example avc3 main level 3.1 + aac_he: avc3.4d401f; mp4a.40.5
              example avc1 high level 4.1 + aac_lc: avc1.640029; mp4a.40.2
              default: avc1 baseline level 3.0 + aac_lc
            */
            // inject dash conf at earliest opportunity
            dashconf = extend({
                type: "video/mp4",
                codecs: "avc1.42c01e, mp4a.40.2"
            }, flowplayer.conf[engineName], conf[engineName], conf.clip[engineName]);

            return isDashType(type) &&
                    mse.isTypeSupported(dashconf.type + '; codecs="' + dashconf.codecs + '"');
        };

        // put on top of engine stack
        // so mpegedash is tested before html5
        flowplayer.engines.unshift(engineImpl);

    }

}());
<|endoftext|>


<commit_before>'use strict';

// Global groups collection by names.
var GROUPS = {};

// Group mixed getter/setter.
SoundJSDirector.group = function (name) {
  switch (name instanceof SoundJSDirector.Group) {
    case false: return GROUPS[name];
    case true: return (GROUPS[name.name] = name);
  }
};

// Apply @callback with every group where sound is contain.
SoundJSDirector.eachSoundGroup = function (sound, callback) {

  if ('function' !== typeof callback) {
    return;
  }

  var args = SoundJSDirector.toArray(callback, 1),
      groups = SoundJSDirector.getSoundGroups(sound);

  args.unshift(null, null);

  for (var i = groups.length; i--; ) {
    args[0] = groups[i];
    args[1] = i;
    callback.apply(null, args);
  }

};

// Return array of group where sound is.
SoundJSDirector.getSoundGroups = function (sound) {
  if (!(sound && 'object' === typeof sound)) {
    return [];
  }
  return (sound._groups || (sound._groups = []));
};

// Add group to sound groups.
SoundJSDirector.setSoundGroup = function (sound, group) {
  if (!(sound && 'object' === typeof sound)) {
    return;
  }
  if ('string' === typeof group) {
    group = SoundJSDirector.group(group);
  }
  var soundGroups = SoundJSDirector.getSoundGroups(sound);
  soundGroups.push(group);
};
<commit_msg>Improved some group methods and added unsetSoundGroup.<commit_after>'use strict';

// Global groups collection by names.
var GROUPS = {};

// Group mixed getter/setter.
SoundJSDirector.group = function (name) {
  switch (name instanceof SoundJSDirector.Group) {
    case false: return GROUPS[name];
    case true: return (GROUPS[name.name] = name);
  }
};

// Apply @callback with every group where sound is contain.
SoundJSDirector.eachSoundGroup = SoundJSDirector.prototype.eachSoundGroup =
function eachSoundGroup (sound, callback) {
  SoundJSDirector.each(SoundJSDirector.getSoundGroups(sound), callback);
};

// Return array of group where sound is.
SoundJSDirector.getSoundGroups = SoundJSDirector.prototype.getSoundGroups =
function getSoundGroups (sound) {
  if (!(sound && 'object' === typeof sound)) {
    return [];
  }
  return (sound._groups || (sound._groups = []));
};

// Add group to sound groups.
SoundJSDirector.setSoundGroup = SoundJSDirector.prototype.setSoundGroup =
function setSoundGroup (sound, group) {
  if (!(sound && 'object' === typeof sound)) {
    return;
  }
  if ('string' === typeof group) {
    group = SoundJSDirector.group(group);
  }
  var soundGroups = SoundJSDirector.getSoundGroups(sound);
  soundGroups.push(group);
  group.sounds.push(sound);
};

// Remove @sound from @group.
SoundJSDirector.unsetSoundGroup = SoundJSDirector.prototype.unsetSoundGroup =
function unsetSoundGroup (sound, group) {
  if (!(sound && 'object' === typeof sound)) {
    return;
  }
  if ('string' === typeof group) {
    group = SoundJSDirector.group(group);
  }
  var soundGroups = SoundJSDirector.getSoundGroups(sound),
      groupSounds = group.sounds,
      index;
  while ((index = soundGroups.indexOf(group)) >= 0) {
    soundGroups.splice(index, 1);
  }
  while ((index = groupSounds.indexOf(sound)) >= 0) {
    groupSounds.splice(index, 1);
  }
};
<|endoftext|>