Source: volume/slot.js

// Generated by CoffeeScript 1.12.7

/**
 * Volume files
 * @module
 */
'use strict';
var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
  hasProp = {}.hasOwnProperty,
  slice = [].slice,
  indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };

app.controller('volume/slot', [
  '$scope', '$location', '$sce', '$timeout', 'constantService', 'modelService', 'displayService', 'messageService', 'tooltipService', 'styleService', 'storageService', 'Offset', 'Segment', 'uploadService', 'routerService', 'slot', 'edit', function($scope, $location, $sce, $timeout, constants, models, display, messages, tooltips, styles, storage, Offset, Segment, uploads, router, slot, editing) {
    var $flow, Asset, Comment, Excerpt, Record, Tag, TagName, TimeBar, TimePoint, TimeSegment, asset, assetId, autoplay, blank, byId, byPosition, comment, done, ev, finite, fn, fullRange, getSelection, playerDropHeightStyle, playerHeight, playerImgHeightStyle, playerMinHeight, playerPDFHeightStyle, playerVideoHeightStyle, playrange, records, ref, ref1, resetRange, ruler, searchLocation, seekOffset, setPlayerHeight, stayDirty, tag, tagId, tagToggle, target, unchoose, updatePlayerHeight, updateRange, updateSelection, video, videoEvents, viewportMinHeight;
    display.title = slot.displayName;
    $scope.flowOptions = uploads.flowOptions();
    $scope.slot = slot;
    $scope.volume = slot.volume;
    $scope.editing = editing;
    $scope.form = {};
    $scope.authorized = models.Login.isAuthorized();
    $flow = $scope.$flow;
    console.log(slot);

    /**
     * Represents a point on the timeline, which can be expressed as "o" (offset time in ms), "x" (pixel X position in client coordinates), or "p" (fractional position on the timeline, may escape [0,1])
     * @interface slot/TimePoint
     */
    TimePoint = (function() {
      var tl;

      function TimePoint(v, t) {
        var x;
        if (v instanceof TimePoint) {
          for (t in v) {
            x = v[t];
            if (t.startsWith('_')) {
              this[t] = x;
            }
          }
        } else {
          this['_' + (t != null ? t : 'o')] = v;
        }
      }

      tl = document.getElementById('slot-timeline');

      Object.defineProperties(TimePoint.prototype, {
        o: {
          get: function() {
            var p;
            if ('_o' in this) {
              return this._o;
            } else {
              p = this.p;
              return this._o = Math.round(ruler.range.l + p * (ruler.range.u - ruler.range.l));
            }
          },
          set: function(o) {
            if (o === this._o) {
              return;
            }
            this._o = o;
            delete this._p;
            delete this._x;
          }
        },
        p: {
          get: function() {
            var o, tlr, x;
            if ('_p' in this) {
              return this._p;
            } else if ('_o' in this) {
              o = this._o;
              return this._p = (o - ruler.range.base) / (ruler.range.u - ruler.range.l);
            } else if ('_x' in this) {
              x = this._x;
              tlr = tl.getBoundingClientRect();
              return this._p = (x - tlr.left) / tlr.width;
            }
          },
          set: function(p) {
            if (p === this._p) {
              return;
            }
            this._p = p;
            delete this._o;
            delete this._x;
          }
        },
        x: {
          get: function() {
            var p, tlr;
            if ('_x' in this) {
              return this._x;
            } else {
              p = this.p;
              tlr = tl.getBoundingClientRect();
              return this._x = tlr.left + p * tlr.width;
            }
          },
          set: function(x) {
            if (x === this._x) {
              return;
            }
            this._x = x;
            delete this._o;
            delete this._p;
          }
        }
      });

      TimePoint.prototype.reset = function() {
        if ('_o' in this) {
          delete this._p;
          delete this._x;
        } else if ('_x' in this) {
          delete this._p;
        }
      };

      TimePoint.prototype.clip = function() {
        var p;
        p = this.p;
        if (p > 1) {
          this.p = 2e308;
        }
        if (p < 0) {
          this.p = -2e308;
        }
        return this;
      };

      TimePoint.prototype.defined = function() {
        return isFinite(this.o);
      };

      TimePoint.prototype.minus = function(t) {
        var r;
        r = new TimePoint();
        if ('_o' in this) {
          r.o = this._o - t.o;
        }
        if ('_p' in this) {
          r.p = this._p - t.p;
        }
        if ('_x' in this) {
          r.x = this._x - t.x;
        }
        return r;
      };

      TimePoint.prototype.seek = function() {
        var o;
        if (isFinite(o = this.o)) {
          seekOffset(o);
          if (!(ruler.selection.contains(o) || ruler.selection.u === o)) {
            ruler.selection = new TimeSegment(null);
            updateSelection();
          }
        }
      };

      TimePoint.prototype.style = function() {
        var p, style;
        style = {};
        p = this.p;
        if (p >= 0 && p <= 1) {
          style.left = 100 * p + '%';
        }
        return style;
      };

      return TimePoint;

    })();

    /**
     * Elaboration on Segment that uses lt and ut TimePoints
     * @interface slot/TimeSegment
     */
    TimeSegment = (function(superClass) {
      extend(TimeSegment, superClass);

      function TimeSegment() {
        return TimeSegment.__super__.constructor.apply(this, arguments);
      }

      TimeSegment.prototype.init = function(a, u) {
        if (arguments.length >= 2) {
          this.lt = new TimePoint(a);
          return this.ut = new TimePoint(u);
        } else if (a instanceof TimeSegment) {
          this.lt = new TimePoint(a.lt);
          return this.ut = new TimePoint(a.ut);
        } else if (a instanceof TimePoint) {
          this.lt = new TimePoint(a);
          return this.ut = new TimePoint(a);
        } else {
          this.lt = new TimePoint();
          this.ut = new TimePoint();
          return TimeSegment.__super__.init.apply(this, arguments);
        }
      };

      Object.defineProperties(TimeSegment.prototype, {
        l: {
          get: function() {
            return this.lt.o;
          },
          set: function(o) {
            this.lt.o = o;
          }
        },
        u: {
          get: function() {
            return this.ut.o;
          },
          set: function(o) {
            this.ut.o = o;
          }
        },
        size: {
          get: function() {
            return this.ut.minus(this.lt);
          }
        }
      });

      TimeSegment.prototype.contains = function(t) {
        if (t instanceof TimePoint) {
          t = t.o;
        }
        return TimeSegment.__super__.contains.call(this, t);
      };

      TimeSegment.prototype.reset = function() {
        this.lt.reset();
        return this.ut.reset();
      };

      TimeSegment.prototype.style = function() {
        var l, r, style;
        style = {};
        l = this.lt.p;
        r = this.ut.p;
        if (l < 0) {
          style.left = '0px';
          style['border-top-left-radius'] = '0px';
          style['border-bottom-left-radius'] = '0px';
          if (r < 0) {
            style.border = '#f26363 3px solid';
          } else {
            style['border-left'] = '0px';
          }
        } else if (l < 1) {
          style.left = 100 * l + '%';
        }
        if (r > 1) {
          style.right = '0px';
          style['border-top-right-radius'] = '0px';
          style['border-bottom-right-radius'] = '0px';
          if (l > 1) {
            style.border = '#f26363 3px solid';
          } else {
            style['border-right'] = '0px';
          }
        } else if (r > 0) {
          style.right = 100 * (1 - r) + '%';
        }
        return style;
      };

      TimeSegment.prototype.select = function(event) {
        if (typeof $scope.editing === 'string') {
          return false;
        }
        ruler.selection = new TimeSegment(this.full || !ruler.range.overlaps(this) ? null : this);
        if (isFinite(this.l) && !this.contains(ruler.position)) {
          seekOffset(this.l);
        } else if (this.full) {
          seekOffset(void 0);
        }
        updateSelection();
        return event != null ? event.stopPropagation() : void 0;
      };

      return TimeSegment;

    })(Segment);

    /**
     * Set the global state
     * @interface slot/searchLocation
     */
    target = $location.search();
    video = void 0;
    blank = void 0;
    fullRange = new Segment(0, 0);
    ruler = $scope.ruler = {
      range: 'range' in target ? new Segment(target.range) : fullRange,
      selection: new TimeSegment('select' in target ? target.select : null),
      position: new TimePoint(Offset.parse(target.pos)),
      zoomed: 'range' in target
    };
    playrange = void 0;
    searchLocation = function(url) {
      var ref, ref1;
      return url.search('asset', void 0).search('record', void 0).search(((ref = $scope.current) != null ? ref.type : void 0) || '', (ref1 = $scope.current) != null ? ref1.id : void 0).search('select', !ruler.selection.empty ? ruler.selection.format() : void 0).search('range', ruler.zoomed ? ruler.range.format() : void 0);
    };
    $scope.toggleEdit = function() {
      return searchLocation($location.url(editing ? slot.route() : slot.editRoute()));
    };
    byId = function(a, b) {
      return a.id - b.id;
    };
    byPosition = function(a, b) {
      return a.l - b.l;
    };
    finite = function() {
      var args;
      args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
      return args.find(isFinite);
    };
    resetRange = function() {
      var c, j, k, len, len1, ref, t;
      ref = [$scope.assets, records, $scope.tags];
      for (j = 0, len = ref.length; j < len; j++) {
        c = ref[j];
        for (k = 0, len1 = c.length; k < len1; k++) {
          t = c[k];
          t.reset();
        }
      }
      ruler.selection.reset();
    };
    updateRange = function() {
      var c, j, k, l, len, len1, ref, t, u;
      l = 2e308;
      u = -2e308;
      ref = [$scope.assets, records];
      for (j = 0, len = ref.length; j < len; j++) {
        c = ref[j];
        for (k = 0, len1 = c.length; k < len1; k++) {
          t = c[k];
          if (isFinite(t.l)) {
            if (t.l < l) {
              l = t.l;
            }
            if (t.l > u) {
              u = t.l;
            }
          }
          if (isFinite(t.u)) {
            if (t.u < l) {
              l = t.u;
            }
            if (t.u > u) {
              u = t.u;
            }
          }
        }
      }
      fullRange.l = finite(slot.segment.l, l, 0);
      fullRange.u = finite(slot.segment.u, u, 0);
      resetRange();
    };

    /**
     * Video/playback controls
     * @interface slot/updatePosition
     */
    $scope.updatePosition = function() {
      var ref, seg;
      seg = ($scope.editing === 'asset' ? $scope.current : (ref = $scope.asset) != null ? ref.segment : void 0);
      if (seg && video && seg.contains(ruler.position.o) && seg.lBounded) {
        video[0].currentTime = (ruler.position.o - seg.l) / 1000;
      }
    };
    seekOffset = function(o) {
      ruler.position.o = Math.round(o);
      $scope.updatePosition();
    };
    $scope.play = function() {
      if (video) {
        video[0].play();
      } else {
        $scope.playing = 1;
      }
    };
    $scope.pause = function() {
      if (video) {
        video[0].pause();
      } else {
        $scope.playing = 0;
      }
    };
    videoEvents = {
      pause: function() {
        $scope.playing = 0;
      },
      playing: function() {
        $scope.playing = 1;
      },
      ratechange: function() {
        $scope.playing = video[0].playbackRate;
      },
      timeupdate: function() {
        var o;
        if ($scope.asset && isFinite($scope.asset.segment.l)) {
          o = Math.round(1000 * video[0].currentTime);
          if ($scope.editing === 'asset' && $scope.asset === $scope.current.asset) {
            $scope.current.setPosition(ruler.position.o - o);
          } else {
            ruler.position.o = $scope.asset.segment.l + o;
            if ((playrange != null ? playrange.uBounded : void 0) && ruler.position.o >= playrange.u) {
              video[0].pause();
              seekOffset(playrange.l);
            }
          }
        }
      },
      ended: function() {
        var a, i, j, len, p, ref;
        $scope.playing = 0;
        if (!($scope.asset && isFinite(p = $scope.asset.segment.u))) {
          return;
        }
        ref = $scope.assets;
        for (j = 0, len = ref.length; j < len; j++) {
          i = ref[j];
          if (!(i.asset && i.asset.duration && !i.asset.pending && i.asset.checkPermission(constants.permission.VIEW) && i.lBounded && i.ut.o > p)) {
            continue;
          }
          a = i;
          break;
        }
        if (!a) {
          return;
        }
        a.choose(true);
      }
    };
    for (ev in videoEvents) {
      fn = videoEvents[ev];
      videoEvents[ev] = $scope.$lift(fn);
    }
    autoplay = void 0;
    this.deregisterVideo = function(v) {
      if (video !== v) {
        return;
      }
      video = void 0;
      v.off(videoEvents);
    };
    this.registerVideo = function(v) {
      if (video) {
        this.deregisterVideo(video);
      }
      video = v;
      seekOffset(ruler.position.o);
      v.on(videoEvents);
      if (autoplay) {
        autoplay = false;
        video[0].play();
      }
    };

    /**
     * Video player display
     * @interface slot/setPlayerHeight
     */
    playerMinHeight = 200;
    viewportMinHeight = 120;
    playerHeight = parseInt(storage.getString('player-height'), 10) || 400;
    if (!(playerHeight >= playerMinHeight)) {
      playerHeight = playerMinHeight;
    }
    playerImgHeightStyle = void 0;
    playerVideoHeightStyle = void 0;
    playerDropHeightStyle = void 0;
    playerPDFHeightStyle = void 0;
    updatePlayerHeight = function() {
      $timeout(function() {
        var d, player, viewer;
        player = document.getElementById('player-scroll');
        if (!player) {
          return;
        }
        viewer = document.getElementById('player-viewport');
        d = player.offsetTop + playerHeight - viewer.offsetTop - 10;
        if (d < viewportMinHeight) {
          d = viewportMinHeight;
        }
        playerImgHeightStyle = styles.set('.player-viewport .asset-display img{max-height:' + d + 'px}', playerImgHeightStyle);
        playerVideoHeightStyle = styles.set('.player-viewport .asset-display video{height:' + d + 'px}', playerVideoHeightStyle);
        playerDropHeightStyle = styles.set('.file-drop{height:' + (d - 30) + 'px}', playerDropHeightStyle);
        playerPDFHeightStyle = styles.set('.player-viewport .asset-display object{height:' + (d - 10) + 'px}', playerPDFHeightStyle);
      });
    };
    setPlayerHeight = function() {
      storage.setString('player-height', playerHeight);
      $scope.playerHeight = playerHeight;
      updatePlayerHeight();
    };
    setPlayerHeight();
    $scope.updatePlayerHeight = updatePlayerHeight;
    $scope.resizePlayer = function(down, up) {
      var bar, frame, h, player;
      frame = document.getElementById('slot-player');
      frame.style.overflow = 'visible';
      player = document.getElementById('player-scroll');
      bar = down.currentTarget;
      h = Math.max(player.offsetHeight + up.clientY - down.clientY, playerMinHeight);
      if (up.type === 'mousemove') {
        bar.style.top = h - player.offsetHeight + 'px';
        bar.style.borderTop = "2px solid #f26363";
      } else {
        frame.style.overflow = 'hidden';
        bar.style.top = '0px';
        bar.style.borderTop = "none";
        playerHeight = h;
        setPlayerHeight();
      }
    };

    /**
     * Track management
     * @interface slot/TimeBar
     */
    stayDirty = function(global) {
      if (global || editing && $scope.current && $scope.form.edit && ($scope.current.asset || $scope.current.record) && ($scope.current.dirty = $scope.form.edit.$dirty)) {
        return !confirm(constants.message('navigation.confirmation'));
      }
    };
    TimeBar = (function(superClass) {
      extend(TimeBar, superClass);

      function TimeBar() {
        return TimeBar.__super__.constructor.apply(this, arguments);
      }

      TimeBar.prototype.choose = function(ap) {
        if (stayDirty()) {
          return false;
        }
        $scope.current = this;
        if (!this || this.type === 'asset') {
          $scope.asset = this != null ? this.asset : void 0;
        }
        searchLocation($location.replace());
        delete target.asset;
        delete target.record;
        if ($scope.form.edit) {
          if (this != null ? this.dirty : void 0) {
            $scope.form.edit.$setDirty();
          } else {
            $scope.form.edit.$setPristine();
          }
        }
        $scope.playing = 0;
        updateSelection();
        updatePlayerHeight();
        autoplay = ap;
        return true;
      };

      TimeBar.prototype.click = function(event) {
        if (typeof $scope.editing === 'string') {
          return false;
        }
        if (!this || $scope.current === this) {
          new TimePoint(event.clientX, 'x').seek();
        } else {
          this.choose();
        }
      };


      /**
       * Generic function that takes in a time, then will determine if it's
       * close enough to do a premiere-esque "snap" feature to the nearest
       * object.
       * @interface slot/TimeBar/snapping
       */

      TimeBar.prototype.snapping = function(pos) {
        var i, j, len, listOfAllPlacements, min, ref;
        listOfAllPlacements = [];
        ref = $scope.assets.concat(records);
        for (j = 0, len = ref.length; j < len; j++) {
          i = ref[j];
          if (!(i !== this)) {
            continue;
          }
          if (i.lt.defined()) {
            listOfAllPlacements.push(i.lt);
          }
          if (i.ut.defined()) {
            listOfAllPlacements.push(i.ut);
          }
        }
        if (ruler.position.defined()) {
          listOfAllPlacements.push(ruler.position);
        }
        if (!listOfAllPlacements.length) {
          return pos;
        }
        min = _.min(listOfAllPlacements, function(i) {
          return Math.abs(pos.x - i.x);
        });
        if (Math.abs(pos.x - min.x) <= 10) {
          return min;
        } else {
          return pos;
        }
      };

      return TimeBar;

    })(TimeSegment);
    unchoose = TimeBar.prototype.choose.bind(void 0);
    $scope.click = TimeBar.prototype.click.bind(void 0);

    /**
     * Selection (temporal) management
     * @interface slot/setSelectionEnd
     */
    $scope.setSelectionEnd = function(u) {
      var pos, sel;
      pos = ruler.position.o;
      if (u === void 0) {
        ruler.selection = new TimeSegment(pos);
      } else {
        sel = ruler.selection;
        if (sel.empty) {
          sel = slot.segment;
        }
        ruler.selection = u ? new TimeSegment(Math.min(sel.l, pos), pos) : new TimeSegment(pos, Math.max(sel.u, pos));
      }
      updateSelection();
    };
    $scope.dragSelection = function(down, up, c) {
      var endPos, startPos;
      if (typeof $scope.editing === 'string' || c && $scope.current !== c) {
        return false;
      }
      startPos = down.position != null ? down.position : down.position = new TimePoint(down.clientX, 'x');
      endPos = new TimePoint(up.clientX, 'x');
      endPos.clip();
      ruler.selection = startPos.x < endPos.x ? new TimeSegment(startPos, endPos) : startPos.x > endPos.x ? new TimeSegment(endPos, startPos) : startPos.x === endPos.x ? new TimeSegment(startPos) : new TimeSegment(null);
      if (up.type !== 'mousemove') {
        updateSelection();
      }
    };
    $scope.zoom = function(seg) {
      if (seg) {
        ruler.range = new Segment((seg.lBounded ? seg.l : fullRange.l), (seg.uBounded ? seg.u : fullRange.u));
        ruler.zoomed = true;
      } else {
        ruler.range = fullRange;
        ruler.zoomed = false;
      }
      resetRange();
      searchLocation($location.replace());
    };
    $scope.updateSelection = updateSelection = function() {
      var c, executed, j, k, len, len1, ref, ref1, ref2, ref3, t;
      if (editing) {
        if (typeof $scope.editing === 'string') {
          return false;
        }
        $scope.editing = true;
        if ((ref = $scope.current) != null ? ref.excerpts : void 0) {
          $scope.current.updateExcerpt();
        }
        executed = false;
        if (!executed) {
          executed = true;
          window.dataLayer.push({
            'event': 'gtm.drag'
          });
        }
        return;
      }
      playrange = (ref1 = $scope.asset) != null ? ref1.segment.intersect(ruler.selection) : void 0;
      ref2 = $scope.tags;
      for (j = 0, len = ref2.length; j < len; j++) {
        t = ref2[j];
        t.update();
      }
      ref3 = $scope.comments;
      for (k = 0, len1 = ref3.length; k < len1; k++) {
        c = ref3[k];
        c.update();
      }
    };
    getSelection = function() {
      if (ruler.selection.empty) {
        return new TimeSegment(ruler.position.defined() ? ruler.position : void 0);
      } else {
        return ruler.selection;
      }
    };

    /**
     * Track implementations
     * @interface slot/Asset
     */
    Asset = (function(superClass) {
      extend(Asset, superClass);

      function Asset(asset) {
        var e;
        this.excerpts = (asset != null ? asset.excerpts : void 0) ? (function() {
          var j, len, ref, results;
          ref = asset.excerpts;
          results = [];
          for (j = 0, len = ref.length; j < len; j++) {
            e = ref[j];
            results.push(new Excerpt(e));
          }
          return results;
        })() : [];
        this.setAsset(asset);
        return;
      }

      Asset.prototype.type = 'asset';

      Asset.prototype.reset = function() {
        var e, j, len, ref;
        Asset.__super__.reset.call(this);
        ref = this.excerpts;
        for (j = 0, len = ref.length; j < len; j++) {
          e = ref[j];
          e.reset();
        }
      };

      Asset.prototype.setAsset = function(asset1) {
        this.asset = asset1;
        this.fillData();
        if (this.asset) {
          this.init(this.asset.segment);
          if (this.asset.id == target.asset) {
            this.choose();
          }
          if ($scope.current === this) {
            $scope.asset = this.asset;
          }
          this.updateExcerpt();
        } else {
          this.init(void 0);
        }
      };

      Asset.prototype.fillData = function() {
        this.data = this.asset ? {
          name: this.asset.name,
          classification: this.asset.classification
        } : {};
      };

      Object.defineProperty(Asset.prototype, 'id', {
        get: function() {
          var ref;
          return (ref = this.asset) != null ? ref.id : void 0;
        }
      });

      Object.defineProperty(Asset.prototype, 'name', {
        get: function() {
          var ref, ref1, ref2, ref3, ref4;
          if (!(this.file || this.asset)) {
            return constants.message('asset.add');
          }
          return (ref = (ref1 = (ref2 = (ref3 = this.asset) != null ? ref3.name : void 0) != null ? ref2 : this.data.name) != null ? ref1 : (ref4 = this.file) != null ? ref4.file.name : void 0) != null ? ref : constants.message('file');
        }
      });

      Asset.prototype.removed = function() {
        if (this.asset || this.file) {
          return;
        }
        if (this === $scope.current) {
          unchoose();
        }
        if (this === blank) {
          blank = void 0;
        }
        $scope.assets.remove(this);
      };

      Asset.prototype.remove = function() {
        messages.clear(this);
        if (this.pending) {
          return;
        }
        if (!confirm(constants.message('asset.remove.confirm'))) {
          return;
        }
        if (this.file) {
          this.file.cancel();
          delete this.file;
          return this.removed();
        }
        if (!this.asset) {
          return this.removed();
        }
        this.asset.remove().then((function(_this) {
          return function(asset) {
            uploads.removedAsset = asset;
            messages.add({
              type: 'green',
              body: constants.message('asset.remove.success', _this.name),
              owner: _this
            });
            delete _this.asset;
            return _this.removed();
          };
        })(this), (function(_this) {
          return function(res) {
            messages.addError({
              type: 'red',
              body: constants.message('asset.remove.error', _this.name),
              report: res,
              owner: _this
            });
          };
        })(this));
      };

      Asset.prototype.save = function() {
        var file, ref;
        if (this.pending) {
          return;
        }
        this.pending = 1;
        messages.clear(this);
        if ((ref = this.file) != null ? ref.isComplete() : void 0) {
          file = this.file;
        }
        (file ? (this.data.upload = file.uniqueIdentifier, this.asset ? this.asset.replace(this.data) : slot.createAsset(this.data)) : this.asset.save(this.data)).then((function(_this) {
          return function(asset) {
            var first;
            delete _this.pending;
            first = !_this.asset;
            _this.setAsset(asset);
            if (file) {
              if (asset.creation == null) {
                asset.creation = {
                  date: Date.now(),
                  name: file.file.name
                };
              }
              file.cancel();
              delete _this.file;
              delete _this.progress;
            }
            delete _this.dirty;
            if (_this === $scope.current) {
              $scope.form.edit.$setPristine();
            }
            updateRange();
            Asset.sort();
          };
        })(this), (function(_this) {
          return function(res) {
            delete _this.pending;
            messages.addError({
              type: 'red',
              body: constants.message('asset.update.error', _this.name),
              report: res,
              owner: _this
            });
            if (file) {
              file.cancel();
              delete _this.file;
              delete _this.progress;
              delete _this.data.upload;
            }
          };
        })(this));
      };

      Asset.prototype.upload = function(file) {
        if (this === blank) {
          blank = void 0;
        }
        messages.clear(this);
        if (this.file) {
          return;
        }
        this.file = file;
        this.progress = 0;
        file.store = this;
        uploads.upload(slot.volume, file).then((function(_this) {
          return function() {
            var base;
            (base = _this.data).name || (base.name = file.name);
          };
        })(this), (function(_this) {
          return function(res) {
            delete _this.file;
            delete _this.progress;
            if (!blank) {
              blank = _this;
            } else if ($.isEmptyObject(_this.data)) {
              _this.removed();
            }
          };
        })(this));
      };

      Asset.prototype.error = function(message) {
        messages.addError({
          type: 'red',
          body: constants.message('asset.upload.error', this.name, message || 'unknown error'),
          owner: this
        });
        this.file.cancel();
        delete this.file;
        delete this.progress;
      };

      Asset.prototype.rePosition = function() {
        $scope.editing = 'asset';
      };

      Asset.prototype.updatePosition = function() {
        this.u = this.l + (this.asset.duration || 0);
      };

      Asset.prototype.setPosition = function(p) {
        if (p instanceof TimePoint) {
          this.lt = p;
        } else {
          this.l = p;
        }
        this.updatePosition();
        $scope.form.position.$setDirty();
      };

      Asset.prototype.finishPosition = function() {
        $scope.form.position.$setPristine();
        $scope.editing = true;
        this.init(this.asset.segment);
      };

      Asset.prototype.savePosition = function() {
        var ref, shift;
        messages.clear(this);
        shift = (ref = this.asset) != null ? ref.segment.base : void 0;
        return this.asset.save({
          container: slot.id,
          position: Math.floor(this.l)
        }).then((function(_this) {
          return function(asset) {
            var e, j, len, ref1;
            _this.asset = asset;
            shift -= _this.asset.segment.base;
            if (isFinite(shift) && shift) {
              ref1 = _this.excerpts;
              for (j = 0, len = ref1.length; j < len; j++) {
                e = ref1[j];
                e.excerpt.segment.l -= shift;
                e.excerpt.segment.u -= shift;
                e.l -= shift;
                e.u -= shift;
              }
            }
            updateRange();
            Asset.sort();
            _this.finishPosition();
            return _this.updateExcerpt();
          };
        })(this), (function(_this) {
          return function(res) {
            _this.finishPosition();
            messages.addError({
              type: 'red',
              body: constants.message('asset.update.error', _this.name),
              report: res,
              owner: _this
            });
          };
        })(this));
      };

      Asset.prototype.dragMove = function(down, up) {
        var offset, pos;
        offset = down.offset != null ? down.offset : down.offset = new TimePoint(down.clientX, 'x').minus(this.lt);
        pos = new TimePoint(up.clientX, 'x').minus(offset);
        if (!pos.defined()) {
          return;
        }
        pos = this.snapping(pos);
        this.setPosition(pos);
        if (up.type !== 'mousemove') {
          $scope.updatePosition();
        }
      };

      Asset.prototype.updateExcerpt = function() {
        var e, seg;
        this.excerpt = void 0;
        if (!(this.asset && this.excerpts)) {
          return;
        }
        seg = this.full ? this.asset.assumedSegment : getSelection().intersect(this);
        if (!this.asset || !seg) {
          return;
        }
        e = this.excerpts.find(function(e) {
          return seg.overlaps(e);
        });
        this.excerpt = !e ? this.overlaps(seg) ? {
          target: this.asset.inSegment(seg),
          on: false,
          release: '',
          full: seg.contains(this.asset.assumedSegment)
        } : void 0 : e.equals(seg) ? {
          current: e,
          target: e.excerpt,
          on: true,
          release: e.excerpt.excerpt + '',
          full: e.excerpt.full
        } : null;
      };

      Asset.prototype.editExcerpt = function() {
        this.updateExcerpt();
        if (this.excerpt.full) {
          this.saveExcerpt(this.excerpt.on ? null : true);
        } else {
          $scope.editing = 'excerpt';
        }
      };

      Asset.prototype.excerptOptions = function() {
        var c, i, j, l, len, r, ref, ref1;
        r = ((ref = this.asset.classification) != null ? ref : this.asset.container.release) || 0;
        l = {
          "true": constants.message('release.UNRELEASED.select') + ' (' + constants.message('release.' + constants.release[r] + '.title') + ')'
        };
        ref1 = constants.release;
        for (i = j = 0, len = ref1.length; j < len; i = ++j) {
          c = ref1[i];
          if (i > r) {
            l[i] = constants.message('release.' + c + '.title') + ': ' + constants.message('release.' + c + '.select');
          }
        }
        if (!(this.excerpt.release in l)) {
          l[this.excerpt.release] = constants.message('release.prompt');
        }
        return l;
      };

      Asset.prototype.saveExcerpt = function(value) {
        $scope.editing = true;
        messages.clear(this);
        if (value === void 0 || value === '') {
          return;
        }
        if (value === 'true') {
          value = true;
        }
        if ((this.asset.classification == null) && value !== true && value > (slot.release || 0) && !confirm(constants.message('release.excerpt.warning'))) {
          return;
        }
        this.excerpt.target.setExcerpt(value).then((function(_this) {
          return function(excerpt) {
            _this.excerpts.remove(_this.excerpt.current);
            if ('excerpt' in excerpt) {
              _this.excerpts.push(new Excerpt(excerpt));
            }
            _this.updateExcerpt();
          };
        })(this), (function(_this) {
          return function(res) {
            messages.addError({
              type: 'red',
              body: constants.message('asset.update.error', _this.name),
              report: res,
              owner: _this
            });
          };
        })(this));
      };

      Asset.prototype.canRestore = function() {
        var ref;
        if (editing && this === blank && ((ref = uploads.removedAsset) != null ? ref.volume.id : void 0) === slot.volume.id) {
          return uploads.removedAsset != null;
        }
      };

      Asset.prototype.restore = function() {
        messages.clear(this);
        uploads.removedAsset.link(slot).then((function(_this) {
          return function(a) {
            uploads.removedAsset = void 0;
            _this.setAsset(a);
            if (_this === blank) {
              blank = void 0;
            }
          };
        })(this), function(res) {
          messages.addError({
            type: 'red',
            body: constants.message('asset.update.error', '[removed file]'),
            report: res,
            owner: this
          });
        });
      };

      Asset.sort = function() {
        if (!$scope.assets) {
          return;
        }
        $scope.assets.sort(function(a, b) {
          if (a.asset && b.asset) {
            return isFinite(b.asset.segment.l) - isFinite(a.asset.segment.l) || a.asset.segment.l - b.asset.segment.l || a.asset.segment.u - b.asset.segment.u || a.id - b.id;
          } else {
            return !a.asset - !b.asset || !a.file - !b.file;
          }
        });
      };

      return Asset;

    })(TimeBar);

    /**
     * Excerpt in slot
     * @interface slot/Excerpt
     */
    Excerpt = (function(superClass) {
      extend(Excerpt, superClass);

      function Excerpt(e) {
        Excerpt.__super__.constructor.call(this, e.segment);
        this.excerpt = e;
        return;
      }

      return Excerpt;

    })(TimeBar);
    $scope.addBlank = function() {
      if (!blank) {
        $scope.assets.push(blank = new Asset());
      }
      blank.choose();
      return blank;
    };
    $scope.$on('$viewContentLoaded', function() {
      if (target.file === 'open') {
        $scope.addBlank();
      }
    });
    $scope.fileAdded = function(file) {
      var ref;
      $flow = file.flowObj;
      if (editing) {
        (!((ref = $scope.current) != null ? ref.file : void 0) && $scope.current || $scope.addBlank()).upload(file);
      }
    };
    $scope.fileSuccess = function(file) {
      file.store.progress = 1;
      return file.store.save();
    };
    $scope.fileProgress = function(file) {
      return file.store.progress = file.progress();
    };
    $scope.fileError = function(file, message) {
      return file.store.error(message);
    };

    /**
     * Record in slot
     * @interface slot/Record
     */
    Record = (function(superClass) {
      extend(Record, superClass);

      function Record(r) {
        var f, j, len, ref;
        this.rec = r;
        this.record = r.record || slot.volume.records[r.id];
        ref = ['age'];
        for (j = 0, len = ref.length; j < len; j++) {
          f = ref[j];
          if (f in r) {
            this[f] = r[f];
          }
        }
        Record.__super__.constructor.call(this, r.segment);
        if (editing) {
          this.fillData();
        }
        return;
      }

      Record.prototype.type = 'record';

      Record.prototype.fillData = function() {
        this.data = {
          measures: angular.extend({}, this.record.measures),
          add: ''
        };
        if (editing) {
          this.sortMetrics();
        }
      };

      Record.prototype.sortMetrics = function() {
        var keys;
        keys = _.keys(this.data.measures);
        this.sortedMetrics = keys.sort(function(a, b) {
          return a - b;
        });
      };

      Object.defineProperty(Record.prototype, 'id', {
        get: function() {
          return this.rec.id;
        }
      });

      Record.prototype.remove = function() {
        messages.clear(this);
        return slot.removeRecord(this.record, this).then((function(_this) {
          return function(r) {
            if (!r) {
              return;
            }
            records.remove(_this);
            if ($scope.current === _this) {
              unchoose();
            }
            Record.place();
          };
        })(this), function(res) {
          messages.addError({
            type: 'red',
            body: 'Unable to remove',
            report: res,
            owner: this
          });
        });
      };

      Record.prototype.metrics = function() {
        var m;
        return ((function() {
          var results;
          results = [];
          for (m in this.record.measures) {
            results.push(constants.metric[m]);
          }
          return results;
        }).call(this)).sort(byId);
      };

      Record.prototype.rePosition = function() {
        $scope.editing = 'record';
      };

      Record.prototype.updatePosition = function(u) {
        $scope.form.position[u ? 'position-lower' : 'position-upper'].$validate();
      };

      Record.prototype.finishPosition = function() {
        $scope.editing = true;
        this.init(this.rec.segment);
        $scope.form.position.$setPristine();
      };

      Record.prototype.savePosition = function() {
        messages.clear(this);
        return slot.moveRecord(this.record, this.rec.segment, this).then((function(_this) {
          return function(r) {
            if (r && _this.empty) {
              records.remove(_this);
              if (_this === $scope.current) {
                unchoose();
              }
            }
            _this.finishPosition();
            if (r) {
              Record.place();
              updateRange();
            }
          };
        })(this), (function(_this) {
          return function(res) {
            messages.addError({
              type: 'red',
              body: 'Error saving record',
              report: res,
              owner: _this
            });
          };
        })(this));
      };

      Record.prototype.drag = function(event, which) {
        var x;
        x = new TimePoint(event.clientX, 'x');
        this[which ? 'ut' : 'lt'] = which && x.p > 1 ? new TimePoint(2e308) : !which && x.p < 0 ? new TimePoint(-2e308) : this.snapping(x);
        if (this.empty) {
          if (which) {
            this.lt = this.ut;
          } else {
            this.ut = this.lt;
          }
        }
        if (event.type !== 'mousemove') {
          $scope.form.position.$setDirty();
        }
      };

      Record.place = function() {
        var i, j, k, len, len1, len2, n, o, overlaps, r, ref, t;
        t = [];
        overlaps = function(rr) {
          return rr.record.id !== r.record.id && r.overlaps(rr);
        };
        ref = records.sort(function(a, b) {
          return a.record.category - b.record.category || a.rec.id - b.rec.id;
        });
        for (j = 0, len = ref.length; j < len; j++) {
          r = ref[j];
          for (i = k = 0, len1 = t.length; k < len1; i = ++k) {
            o = t[i];
            if (!(o[0].record.category !== r.record.category || o.some(overlaps))) {
              break;
            }
          }
          if (!(i in t)) {
            t[i] = [];
          }
          t[i].push(r);
          if (r.id == target.record) {
            r.choose();
          }
        }
        for (n = 0, len2 = t.length; n < len2; n++) {
          r = t[n];
          r.sort(byPosition);
        }
        $scope.records = t;
      };

      return Record;

    })(TimeBar);
    $scope.positionBackgroundStyle = function(l, i) {
      if (l[i].lt.p > 1 || l[i].ut.p < 0) {
        return {
          visibility: "hidden"
        };
      } else {
        return new TimeSegment(l[i].l, i + 1 in l ? l[i + 1].l : 2e308).style();
      }
    };
    $scope.setCategory = function(c) {
      var r, ref, ri, rs;
      if (c != null) {
        rs = {};
        ref = $scope.addRecord.records;
        for (ri in ref) {
          r = ref[ri];
          if (r.category == c) {
            rs[ri] = r.displayName;
          }
        }
        $scope.addRecord.options = rs;
      } else {
        $scope.addRecord.options = void 0;
      }
    };
    $scope.addRecord = function(r) {
      var j, len, rc, ref, ri, rs, seg, sr;
      seg = getSelection();
      if (r === void 0) {
        $scope.editing = 'addrecord';
        rs = {};
        ref = slot.volume.records;
        for (ri in ref) {
          r = ref[ri];
          rs[ri] = r;
        }
        for (j = 0, len = records.length; j < len; j++) {
          sr = records[j];
          if (sr.record.id in rs && sr.overlaps(seg)) {
            delete rs[sr.record.id];
          }
        }
        $scope.addRecord.records = rs;
        rc = {};
        for (ri in rs) {
          r = rs[ri];
          rc[r.category] = null;
        }
        $scope.addRecord.categories = rc;
        $scope.addRecord.select = null;
        $scope.setCategory($scope.addRecord.category);
      } else {
        $scope.addRecord.records = void 0;
        if (r == null) {
          $scope.editing = true;
          return;
        }
        slot.addRecord(slot.volume.records[r], seg).then(function(r) {
          $scope.editing = true;
          r = new Record(r);
          records.push(r);
          Record.place();
          r.choose();
        }, function(res) {
          $scope.editing = true;
          messages.addError({
            body: 'Error adding record',
            report: res,
            owner: $scope
          });
        });
      }
    };

    /**
     * TagName in slot
     * @interface slot/TagName
     */
    TagName = (function(superClass) {
      extend(TagName, superClass);

      function TagName(name) {
        this.id = name;
        TagName.__super__.constructor.call(this);
        return;
      }

      TagName.prototype.save = function(vote) {
        var seg, tag;
        seg = getSelection();
        tag = this;
        return slot.setTag(this.id, vote, editing, seg).then(function(data) {
          var ref, ref1;
          if (!(tag instanceof Tag)) {
            tag = $scope.tags.find(function(t) {
              return t.id === data.id;
            });
            if (!tag) {
              tag = new Tag(data);
              tag.toggle(true);
              $scope.tags.push(tag);
            }
          }
          tag.fillData(data);
          if ((editing ? (ref = tag.keyword) != null ? ref.length : void 0 : (ref1 = tag.coverage) != null ? ref1.length : void 0)) {
            tag.update();
          } else {
            $scope.tags.remove(tag);
          }
          tooltips.clear();
        }, (function(_this) {
          return function(res) {
            messages.addError({
              body: constants.message('tags.vote.error', {
                sce: $sce.HTML
              }, _this.id),
              report: res,
              owner: $scope
            });
          };
        })(this));
      };

      return TagName;

    })(TimeBar);
    $scope.vote = function(name, vote) {
      return new TagName(name).save(vote);
    };
    tagToggle = (ref = (ref1 = storage.getString('tag-toggle')) != null ? ref1.split("\n") : void 0) != null ? ref : [];

    /**
     * Tag in slot
     * @interface slot/Tag
     */
    Tag = (function(superClass) {
      extend(Tag, superClass);

      function Tag(t) {
        var ref2;
        Tag.__super__.constructor.call(this, t.id);
        this.active = (ref2 = t.id, indexOf.call(tagToggle, ref2) >= 0) || t.id === target.tag;
        this.fillData(t);
        return;
      }

      Tag.prototype.type = 'tag';

      Tag.prototype.reset = function() {
        var f, j, k, len, len1, ref2, ref3, t;
        ref2 = (editing ? ['keyword'] : ['coverage', 'vote', 'keyword']);
        for (j = 0, len = ref2.length; j < len; j++) {
          f = ref2[j];
          ref3 = this[f];
          for (k = 0, len1 = ref3.length; k < len1; k++) {
            t = ref3[k];
            t.reset();
          }
        }
      };

      Tag.prototype.fillData = function(t) {
        var f, j, k, len, len1, ref2, ref3, s;
        this.weight = t.weight;
        ref2 = (editing ? ['keyword'] : ['coverage', 'vote', 'keyword']);
        for (j = 0, len = ref2.length; j < len; j++) {
          f = ref2[j];
          this[f] = [];
          if (t[f]) {
            ref3 = t[f];
            for (k = 0, len1 = ref3.length; k < len1; k++) {
              s = ref3[k];
              this[f].push(new TimeSegment(s));
            }
          }
        }
      };

      Tag.prototype.toggle = function(act) {
        if (this.active = act != null ? act : !this.active) {
          tagToggle.push(this.id);
        } else {
          tagToggle.remove(this.id);
        }
        return storage.setString('tag-toggle', tagToggle.join("\n"));
      };

      Tag.prototype.update = function() {
        var j, len, ref2, s, sel, state;
        state = false;
        sel = getSelection();
        ref2 = (editing ? this.keyword : this.vote);
        for (j = 0, len = ref2.length; j < len; j++) {
          s = ref2[j];
          if (sel.contains(s)) {
            state = true;
          } else if (sel.overlaps(s)) {
            state = void 0;
            break;
          }
        }
        return this.state = state;
      };

      return Tag;

    })(TagName);

    /**
     * Comment in slot
     * @interface slot/Comment
     */
    Comment = (function(superClass) {
      extend(Comment, superClass);

      function Comment(c) {
        this.comment = c;
        Comment.__super__.constructor.call(this, c.segment);
      }

      Comment.prototype.type = 'comment';

      Comment.prototype.update = function() {
        this.classes = [];
        if (this.comment.parents) {
          this.classes.push('depth-' + Math.min(this.comment.parents.length, 5));
        }
        if (!(ruler.selection.empty || ruler.selection.overlaps(this) || this === $scope.replyTo)) {
          return this.classes.push('notselected');
        }
      };

      Comment.prototype.setReply = function(event) {
        var ref2, ref3;
        if ((ref2 = $scope.form.comment) != null) {
          ref2.text = '';
        }
        if ((ref3 = $scope.form.comment) != null) {
          ref3.reply = '';
        }
        $scope.commentReply = event ? (this.select(event), this) : void 0;
        return updateSelection();
      };

      return Comment;

    })(TimeBar);
    $scope.addComment = function(message, replyTo) {
      return slot.postComment({
        text: message
      }, getSelection(), replyTo != null ? replyTo.comment.id : void 0).then(function() {
        return slot.getSlot(slot.segment, ['comments']).then(function(res) {
          var comment, j, len, ref2;
          $scope.form.comment.text = $scope.form.reply = '';
          $scope.comments = (function() {
            var j, len, ref2, results;
            ref2 = res.comments;
            results = [];
            for (j = 0, len = ref2.length; j < len; j++) {
              comment = ref2[j];
              results.push(new Comment(comment));
            }
            return results;
          })();
          ref2 = $scope.comments;
          for (j = 0, len = ref2.length; j < len; j++) {
            comment = ref2[j];
            comment.update();
          }
        }, function(res) {
          return messages.addError({
            body: constants.message('comments.update.error'),
            report: res
          });
        });
      }, function(e) {
        return messages.addError({
          body: constants.message('comments.add.error'),
          report: e
        });
      });
    };
    $scope.tags = (function() {
      var ref2, ref3, ref4, results;
      ref2 = slot.tags;
      results = [];
      for (tagId in ref2) {
        tag = ref2[tagId];
        if ((editing ? (ref3 = tag.keyword) != null ? ref3.length : void 0 : (ref4 = tag.coverage) != null ? ref4.length : void 0)) {
          results.push(new Tag(tag));
        }
      }
      return results;
    })();
    $scope.comments = (function() {
      var j, len, ref2, results;
      ref2 = slot.comments;
      results = [];
      for (j = 0, len = ref2.length; j < len; j++) {
        comment = ref2[j];
        results.push(new Comment(comment));
      }
      return results;
    })();
    $scope.assets = (function() {
      var ref2, results;
      ref2 = slot.assets;
      results = [];
      for (assetId in ref2) {
        asset = ref2[assetId];
        results.push(new Asset(asset));
      }
      return results;
    })();
    Asset.sort();
    records = slot.records.map(function(r) {
      return new Record(r);
    });
    $scope.playing = 0;
    Record.place();
    updateRange();
    updateSelection();
    if (editing) {
      done = $scope.$on('$locationChangeStart', function(event, url) {
        var uploading;
        if (url.includes(slot.editRoute())) {
          return;
        }
        if ($flow) {
          uploading = $flow.isUploading();
          $flow.pause();
        }
        if (stayDirty(uploading)) {
          if (uploading) {
            $flow.resume();
          }
          display.cancelRouteChange(event);
        } else {
          done();
        }
      });
    }
    $scope.$on('$destroy', function() {
      if ($flow != null) {
        $flow.cancel();
      }
    });
  }
]);

//# sourceMappingURL=slot.js.map