<commit_before>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview Code for the viewport.
 */
base.require('base.events');

base.exportTo('tracing', function() {

  /**
   * The TimelineViewport manages the transform used for navigating
   * within the timeline. It is a simple transform:
   *   x' = (x+pan) * scale
   *
   * The timeline code tries to avoid directly accessing this transform,
   * instead using this class to do conversion between world and viewspace,
   * as well as the math for centering the viewport in various interesting
   * ways.
   *
   * @constructor
   * @extends {base.EventTarget}
   */
  function TimelineViewport(parentEl) {
    this.parentEl_ = parentEl;
    this.modelTrackContainer_ = null;
    this.scaleX_ = 1;
    this.panX_ = 0;
    this.panY_ = 0;
    this.gridTimebase_ = 0;
    this.gridStep_ = 1000 / 60;
    this.gridEnabled_ = false;
    this.hasCalledSetupFunction_ = false;

    this.onResize_ = this.onResize_.bind(this);
    this.onModelTrackControllerScroll_ =
        this.onModelTrackControllerScroll_.bind(this);

    // The following code uses an interval to detect when the parent element
    // is attached to the document. That is a trigger to run the setup function
    // and install a resize listener.
    this.checkForAttachInterval_ = setInterval(
        this.checkForAttach_.bind(this), 250);

    this.markers = [];
    this.majorMarkPositions = [];
  }

  TimelineViewport.prototype = {
    __proto__: base.EventTarget.prototype,

    /**
     * Allows initialization of the viewport when the viewport's parent element
     * has been attached to the document and given a size.
     * @param {Function} fn Function to call when the viewport can be safely
     * initialized.
     */
    setWhenPossible: function(fn) {
      this.pendingSetFunction_ = fn;
    },

    /**
     * @return {boolean} Whether the current timeline is attached to the
     * document.
     */
    get isAttachedToDocument_() {
      var cur = this.parentEl_;
      // Allow not providing a parent element, used by tests.
      if (cur === undefined)
        return;
      while (cur.parentNode)
        cur = cur.parentNode;
      return cur == this.parentEl_.ownerDocument;
    },

    onResize_: function() {
      this.dispatchChangeEvent();
    },

    /**
     * Checks whether the parentNode is attached to the document.
     * When it is, it installs the iframe-based resize detection hook
     * and then runs the pendingSetFunction_, if present.
     */
    checkForAttach_: function() {
      if (!this.isAttachedToDocument_ || this.clientWidth == 0)
        return;

      if (!this.iframe_) {
        this.iframe_ = document.createElement('iframe');
        this.iframe_.style.cssText =
            'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
        this.parentEl_.appendChild(this.iframe_);

        this.iframe_.contentWindow.addEventListener('resize', this.onResize_);
      }

      var curSize = this.parentEl_.clientWidth + 'x' +
          this.parentEl_.clientHeight;
      if (this.pendingSetFunction_) {
        this.lastSize_ = curSize;
        try {
          this.pendingSetFunction_();
        } catch (ex) {
          console.log('While running setWhenPossible:',
              ex.message ? ex.message + '\n' + ex.stack : ex.stack);
        }
        this.pendingSetFunction_ = undefined;
      }

      window.clearInterval(this.checkForAttachInterval_);
      this.checkForAttachInterval_ = undefined;
    },

    /**
     * Fires the change event on this viewport. Used to notify listeners
     * to redraw when the underlying model has been mutated.
     */
    dispatchChangeEvent: function() {
      base.dispatchSimpleEvent(this, 'change');
    },

    dispatchMarkersChangeEvent_: function() {
      base.dispatchSimpleEvent(this, 'markersChange');
    },

    detach: function() {
      if (this.checkForAttachInterval_) {
        window.clearInterval(this.checkForAttachInterval_);
        this.checkForAttachInterval_ = undefined;
      }
      if (this.iframe_) {
        this.iframe_.removeEventListener('resize', this.onResize_);
        this.parentEl_.removeChild(this.iframe_);
      }
    },

    getStateInViewCoordinates: function() {
      return {
        panX: this.xWorldVectorToView(this.panX),
        panY: this.panY,
        scaleX: this.scaleX
      };
    },

    setStateInViewCoordinates: function(state) {
      this.panX = this.xViewVectorToWorld(state.panX);
      this.panY = state.panY;
    },

    onModelTrackControllerScroll_: function(e) {
      this.panY_ = this.modelTrackContainer_.scrollTop;
    },

    set modelTrackContainer(m) {

      if (this.modelTrackContainer_)
        this.modelTrackContainer_.removeEventListener('scroll',
            this.onModelTrackControllerScroll_);

      this.modelTrackContainer_ = m;
      this.modelTrackContainer_.addEventListener('scroll',
          this.onModelTrackControllerScroll_);
    },

    get scaleX() {
      return this.scaleX_;
    },
    set scaleX(s) {
      var changed = this.scaleX_ != s;
      if (changed) {
        this.scaleX_ = s;
        this.dispatchChangeEvent();
      }
    },

    get panX() {
      return this.panX_;
    },
    set panX(p) {
      var changed = this.panX_ != p;
      if (changed) {
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    get panY() {
      return this.panY_;
    },
    set panY(p) {
      this.panY_ = p;
      this.modelTrackContainer_.scrollTop = p;
    },

    setPanAndScale: function(p, s) {
      var changed = this.scaleX_ != s || this.panX_ != p;
      if (changed) {
        this.scaleX_ = s;
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    xWorldToView: function(x) {
      return (x + this.panX_) * this.scaleX_;
    },

    xWorldVectorToView: function(x) {
      return x * this.scaleX_;
    },

    xViewToWorld: function(x) {
      return (x / this.scaleX_) - this.panX_;
    },

    xViewVectorToWorld: function(x) {
      return x / this.scaleX_;
    },

    xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
      if (typeof viewX == 'string') {
        if (viewX == 'left') {
          viewX = 0;
        } else if (viewX == 'center') {
          viewX = viewWidth / 2;
        } else if (viewX == 'right') {
          viewX = viewWidth - 1;
        } else {
          throw new Error('unrecognized string for viewPos. left|center|right');
        }
      }
      this.panX = (viewX / this.scaleX_) - worldX;
    },

    xPanWorldBoundsIntoView: function(worldMin, worldMax, viewWidth) {
      if (this.xWorldToView(worldMin) < 0)
        this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
      else if (this.xWorldToView(worldMax) > viewWidth)
        this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
    },

    xSetWorldBounds: function(worldMin, worldMax, viewWidth) {
      var worldWidth = worldMax - worldMin;
      var scaleX = viewWidth / worldWidth;
      var panX = -worldMin;
      this.setPanAndScale(panX, scaleX);
    },

    get gridEnabled() {
      return this.gridEnabled_;
    },

    set gridEnabled(enabled) {
      if (this.gridEnabled_ == enabled)
        return;

      this.gridEnabled_ = enabled && true;
      this.dispatchChangeEvent();
    },

    get gridTimebase() {
      return this.gridTimebase_;
    },

    set gridTimebase(timebase) {
      if (this.gridTimebase_ == timebase)
        return;
      this.gridTimebase_ = timebase;
      this.dispatchChangeEvent();
    },

    get gridStep() {
      return this.gridStep_;
    },

    applyTransformToCanvas: function(ctx) {
      ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
    },

    createMarker: function(positionWorld) {
      return new ViewportMarker(this, positionWorld);
    },

    addMarker: function(marker) {
      if (this.markers.indexOf(marker) >= 0)
        return false;
      this.markers.push(marker);
      this.dispatchChangeEvent();
      this.dispatchMarkersChangeEvent_();
      return marker;
    },

    removeMarker: function(marker) {
      for (var i = 0; i < this.markers.length; ++i) {
        if (this.markers[i] === marker) {
          this.markers.splice(i, 1);
          this.dispatchChangeEvent();
          this.dispatchMarkersChangeEvent_();
          return true;
        }
      }
    },

    findMarkerNear: function(positionWorld, nearnessInViewPixels) {
      // Converts pixels into distance in world.
      var nearnessThresholdWorld = this.xViewVectorToWorld(
          nearnessInViewPixels);
      for (var i = 0; i < this.markers.length; ++i) {
        if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
            nearnessThresholdWorld) {
          var marker = this.markers[i];
          return marker;
        }
      }
      return undefined;
    },

    drawGrid: function(ctx, viewLWorld, viewRWorld) {
      ctx.beginPath();
      for (var idx in this.majorMarkPositions) {
        var x = this.majorMarkPositions[idx];
        ctx.moveTo(x, 0);
        ctx.lineTo(x, ctx.canvas.height);
      }
      ctx.strokeStyle = '#ddd';
      ctx.stroke();
    },

    drawGridLines: function(ctx, viewLWorld, viewRWorld) {
      if (!this.gridEnabled)
        return;

      var x = this.gridTimebase;

      ctx.beginPath();
      while (x < viewRWorld) {
        if (x >= viewLWorld) {
          // Do conversion to viewspace here rather than on
          // x to avoid precision issues.
          var vx = this.xWorldToView(x);
          ctx.moveTo(vx, 0);
          ctx.lineTo(vx, ctx.canvas.height);
        }
        x += this.gridStep;
      }
      ctx.strokeStyle = 'rgba(255,0,0,0.25)';
      ctx.stroke();
    },

    drawMarkerArrows: function(ctx, viewLWorld, viewRWorld, drawHeight) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
                                      ctx.canvas.height, drawHeight, this);
      }
    },

    drawMarkerLines: function(ctx, viewLWorld, viewRWorld) {
      // Dim the area left and right of the markers if there are 2.
      if (this.markers.length === 2) {
        var posWorld0 = this.markers[0].positionWorld;
        var posWorld1 = this.markers[1].positionWorld;

        var markerLWorld = Math.min(posWorld0, posWorld1);
        var markerRWorld = Math.max(posWorld0, posWorld1);

        var markerLView = this.xWorldToView(markerLWorld);
        var markerRView = this.xWorldToView(markerRWorld);
        var viewL = this.xWorldToView(viewLWorld);
        var viewR = this.xWorldToView(viewRWorld);

        ctx.fillStyle = 'rgb(0,0,0)';
        ctx.globalAlpha = 0.2;

        if (markerLWorld > viewLWorld)
          ctx.fillRect(viewL, 0, markerLView, ctx.canvas.height);
        if (markerRWorld < viewRWorld)
          ctx.fillRect(markerRView, 0, viewR, ctx.canvas.height);

        ctx.globalAlpha = 1.0;
      }

      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawLine(ctx, viewLWorld, viewRWorld,
            ctx.canvas.height, this);
      }
    }
  };

  /**
   * Represents a marked position in the world, at a viewport level.
   * @constructor
   */
  function ViewportMarker(vp, positionWorld) {
    this.viewport_ = vp;
    this.positionWorld_ = positionWorld;
    this.selected_ = false;
  }

  ViewportMarker.prototype = {
    get positionWorld() {
      return this.positionWorld_;
    },

    set positionWorld(positionWorld) {
      this.positionWorld_ = positionWorld;
      this.viewport_.dispatchChangeEvent();
    },

    get positionView() {
      return this.viewport_.xWorldToView(this.positionWorld);
    },

    set selected(selected) {
      if (this.selected === selected)
        return;
      this.selected_ = selected;
      this.viewport_.dispatchChangeEvent();
    },

    get selected() {
      return this.selected_;
    },

    get color() {
      if (this.selected)
        return 'rgb(255,0,0)';
      return 'rgb(0,0,0)';
    },

    drawTriangle_: function(ctx, viewLWorld, viewRWorld,
                            canvasH, rulerHeight, vp) {
      ctx.beginPath();

      var ts = this.positionWorld_;
      if (ts < viewLWorld || ts > viewRWorld)
        return;

      var viewX = vp.xWorldToView(ts);
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX - 3, rulerHeight / 2);
      ctx.lineTo(viewX + 3, rulerHeight / 2);
      ctx.lineTo(viewX, rulerHeight);
      ctx.closePath();
      ctx.fillStyle = this.color;
      ctx.fill();

      if (rulerHeight === canvasH)
        return;

      // Draw line from bottom of triangle to the bottom of our canvas.
      ctx.beginPath();
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX, canvasH);
      ctx.closePath();
      ctx.strokeStyle = this.color;
      ctx.stroke();
    },

    drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
      ctx.beginPath();
      var ts = this.positionWorld_;
      if (ts >= viewLWorld && ts < viewRWorld) {
        var viewX = vp.xWorldToView(ts);
        ctx.moveTo(viewX, 0);
        ctx.lineTo(viewX, canvasH);
      }
      ctx.strokeStyle = this.color;
      ctx.stroke();
    }
  };

  return {
    TimelineViewport: TimelineViewport,
    ViewportMarker: ViewportMarker
  };
});
<commit_msg>Fix marker line drawn twice in ruler track<commit_after>// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

'use strict';

/**
 * @fileoverview Code for the viewport.
 */
base.require('base.events');

base.exportTo('tracing', function() {

  /**
   * The TimelineViewport manages the transform used for navigating
   * within the timeline. It is a simple transform:
   *   x' = (x+pan) * scale
   *
   * The timeline code tries to avoid directly accessing this transform,
   * instead using this class to do conversion between world and viewspace,
   * as well as the math for centering the viewport in various interesting
   * ways.
   *
   * @constructor
   * @extends {base.EventTarget}
   */
  function TimelineViewport(parentEl) {
    this.parentEl_ = parentEl;
    this.modelTrackContainer_ = null;
    this.scaleX_ = 1;
    this.panX_ = 0;
    this.panY_ = 0;
    this.gridTimebase_ = 0;
    this.gridStep_ = 1000 / 60;
    this.gridEnabled_ = false;
    this.hasCalledSetupFunction_ = false;

    this.onResize_ = this.onResize_.bind(this);
    this.onModelTrackControllerScroll_ =
        this.onModelTrackControllerScroll_.bind(this);

    // The following code uses an interval to detect when the parent element
    // is attached to the document. That is a trigger to run the setup function
    // and install a resize listener.
    this.checkForAttachInterval_ = setInterval(
        this.checkForAttach_.bind(this), 250);

    this.markers = [];
    this.majorMarkPositions = [];
  }

  TimelineViewport.prototype = {
    __proto__: base.EventTarget.prototype,

    /**
     * Allows initialization of the viewport when the viewport's parent element
     * has been attached to the document and given a size.
     * @param {Function} fn Function to call when the viewport can be safely
     * initialized.
     */
    setWhenPossible: function(fn) {
      this.pendingSetFunction_ = fn;
    },

    /**
     * @return {boolean} Whether the current timeline is attached to the
     * document.
     */
    get isAttachedToDocument_() {
      var cur = this.parentEl_;
      // Allow not providing a parent element, used by tests.
      if (cur === undefined)
        return;
      while (cur.parentNode)
        cur = cur.parentNode;
      return cur == this.parentEl_.ownerDocument;
    },

    onResize_: function() {
      this.dispatchChangeEvent();
    },

    /**
     * Checks whether the parentNode is attached to the document.
     * When it is, it installs the iframe-based resize detection hook
     * and then runs the pendingSetFunction_, if present.
     */
    checkForAttach_: function() {
      if (!this.isAttachedToDocument_ || this.clientWidth == 0)
        return;

      if (!this.iframe_) {
        this.iframe_ = document.createElement('iframe');
        this.iframe_.style.cssText =
            'position:absolute;width:100%;height:0;border:0;visibility:hidden;';
        this.parentEl_.appendChild(this.iframe_);

        this.iframe_.contentWindow.addEventListener('resize', this.onResize_);
      }

      var curSize = this.parentEl_.clientWidth + 'x' +
          this.parentEl_.clientHeight;
      if (this.pendingSetFunction_) {
        this.lastSize_ = curSize;
        try {
          this.pendingSetFunction_();
        } catch (ex) {
          console.log('While running setWhenPossible:',
              ex.message ? ex.message + '\n' + ex.stack : ex.stack);
        }
        this.pendingSetFunction_ = undefined;
      }

      window.clearInterval(this.checkForAttachInterval_);
      this.checkForAttachInterval_ = undefined;
    },

    /**
     * Fires the change event on this viewport. Used to notify listeners
     * to redraw when the underlying model has been mutated.
     */
    dispatchChangeEvent: function() {
      base.dispatchSimpleEvent(this, 'change');
    },

    dispatchMarkersChangeEvent_: function() {
      base.dispatchSimpleEvent(this, 'markersChange');
    },

    detach: function() {
      if (this.checkForAttachInterval_) {
        window.clearInterval(this.checkForAttachInterval_);
        this.checkForAttachInterval_ = undefined;
      }
      if (this.iframe_) {
        this.iframe_.removeEventListener('resize', this.onResize_);
        this.parentEl_.removeChild(this.iframe_);
      }
    },

    getStateInViewCoordinates: function() {
      return {
        panX: this.xWorldVectorToView(this.panX),
        panY: this.panY,
        scaleX: this.scaleX
      };
    },

    setStateInViewCoordinates: function(state) {
      this.panX = this.xViewVectorToWorld(state.panX);
      this.panY = state.panY;
    },

    onModelTrackControllerScroll_: function(e) {
      this.panY_ = this.modelTrackContainer_.scrollTop;
    },

    set modelTrackContainer(m) {

      if (this.modelTrackContainer_)
        this.modelTrackContainer_.removeEventListener('scroll',
            this.onModelTrackControllerScroll_);

      this.modelTrackContainer_ = m;
      this.modelTrackContainer_.addEventListener('scroll',
          this.onModelTrackControllerScroll_);
    },

    get scaleX() {
      return this.scaleX_;
    },
    set scaleX(s) {
      var changed = this.scaleX_ != s;
      if (changed) {
        this.scaleX_ = s;
        this.dispatchChangeEvent();
      }
    },

    get panX() {
      return this.panX_;
    },
    set panX(p) {
      var changed = this.panX_ != p;
      if (changed) {
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    get panY() {
      return this.panY_;
    },
    set panY(p) {
      this.panY_ = p;
      this.modelTrackContainer_.scrollTop = p;
    },

    setPanAndScale: function(p, s) {
      var changed = this.scaleX_ != s || this.panX_ != p;
      if (changed) {
        this.scaleX_ = s;
        this.panX_ = p;
        this.dispatchChangeEvent();
      }
    },

    xWorldToView: function(x) {
      return (x + this.panX_) * this.scaleX_;
    },

    xWorldVectorToView: function(x) {
      return x * this.scaleX_;
    },

    xViewToWorld: function(x) {
      return (x / this.scaleX_) - this.panX_;
    },

    xViewVectorToWorld: function(x) {
      return x / this.scaleX_;
    },

    xPanWorldPosToViewPos: function(worldX, viewX, viewWidth) {
      if (typeof viewX == 'string') {
        if (viewX == 'left') {
          viewX = 0;
        } else if (viewX == 'center') {
          viewX = viewWidth / 2;
        } else if (viewX == 'right') {
          viewX = viewWidth - 1;
        } else {
          throw new Error('unrecognized string for viewPos. left|center|right');
        }
      }
      this.panX = (viewX / this.scaleX_) - worldX;
    },

    xPanWorldBoundsIntoView: function(worldMin, worldMax, viewWidth) {
      if (this.xWorldToView(worldMin) < 0)
        this.xPanWorldPosToViewPos(worldMin, 'left', viewWidth);
      else if (this.xWorldToView(worldMax) > viewWidth)
        this.xPanWorldPosToViewPos(worldMax, 'right', viewWidth);
    },

    xSetWorldBounds: function(worldMin, worldMax, viewWidth) {
      var worldWidth = worldMax - worldMin;
      var scaleX = viewWidth / worldWidth;
      var panX = -worldMin;
      this.setPanAndScale(panX, scaleX);
    },

    get gridEnabled() {
      return this.gridEnabled_;
    },

    set gridEnabled(enabled) {
      if (this.gridEnabled_ == enabled)
        return;

      this.gridEnabled_ = enabled && true;
      this.dispatchChangeEvent();
    },

    get gridTimebase() {
      return this.gridTimebase_;
    },

    set gridTimebase(timebase) {
      if (this.gridTimebase_ == timebase)
        return;
      this.gridTimebase_ = timebase;
      this.dispatchChangeEvent();
    },

    get gridStep() {
      return this.gridStep_;
    },

    applyTransformToCanvas: function(ctx) {
      ctx.transform(this.scaleX_, 0, 0, 1, this.panX_ * this.scaleX_, 0);
    },

    createMarker: function(positionWorld) {
      return new ViewportMarker(this, positionWorld);
    },

    addMarker: function(marker) {
      if (this.markers.indexOf(marker) >= 0)
        return false;
      this.markers.push(marker);
      this.dispatchChangeEvent();
      this.dispatchMarkersChangeEvent_();
      return marker;
    },

    removeMarker: function(marker) {
      for (var i = 0; i < this.markers.length; ++i) {
        if (this.markers[i] === marker) {
          this.markers.splice(i, 1);
          this.dispatchChangeEvent();
          this.dispatchMarkersChangeEvent_();
          return true;
        }
      }
    },

    findMarkerNear: function(positionWorld, nearnessInViewPixels) {
      // Converts pixels into distance in world.
      var nearnessThresholdWorld = this.xViewVectorToWorld(
          nearnessInViewPixels);
      for (var i = 0; i < this.markers.length; ++i) {
        if (Math.abs(this.markers[i].positionWorld - positionWorld) <=
            nearnessThresholdWorld) {
          var marker = this.markers[i];
          return marker;
        }
      }
      return undefined;
    },

    drawGrid: function(ctx, viewLWorld, viewRWorld) {
      ctx.beginPath();
      for (var idx in this.majorMarkPositions) {
        var x = this.majorMarkPositions[idx];
        ctx.moveTo(x, 0);
        ctx.lineTo(x, ctx.canvas.height);
      }
      ctx.strokeStyle = '#ddd';
      ctx.stroke();
    },

    drawGridLines: function(ctx, viewLWorld, viewRWorld) {
      if (!this.gridEnabled)
        return;

      var x = this.gridTimebase;

      ctx.beginPath();
      while (x < viewRWorld) {
        if (x >= viewLWorld) {
          // Do conversion to viewspace here rather than on
          // x to avoid precision issues.
          var vx = this.xWorldToView(x);
          ctx.moveTo(vx, 0);
          ctx.lineTo(vx, ctx.canvas.height);
        }
        x += this.gridStep;
      }
      ctx.strokeStyle = 'rgba(255,0,0,0.25)';
      ctx.stroke();
    },

    drawMarkerArrows: function(ctx, viewLWorld, viewRWorld, drawHeight) {
      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawTriangle_(ctx, viewLWorld, viewRWorld,
                                      ctx.canvas.height, drawHeight, this);
      }
    },

    drawMarkerLines: function(ctx, viewLWorld, viewRWorld) {
      // Dim the area left and right of the markers if there are 2.
      if (this.markers.length === 2) {
        var posWorld0 = this.markers[0].positionWorld;
        var posWorld1 = this.markers[1].positionWorld;

        var markerLWorld = Math.min(posWorld0, posWorld1);
        var markerRWorld = Math.max(posWorld0, posWorld1);

        var markerLView = this.xWorldToView(markerLWorld);
        var markerRView = this.xWorldToView(markerRWorld);
        var viewL = this.xWorldToView(viewLWorld);
        var viewR = this.xWorldToView(viewRWorld);

        ctx.fillStyle = 'rgb(0,0,0)';
        ctx.globalAlpha = 0.2;

        if (markerLWorld > viewLWorld)
          ctx.fillRect(viewL, 0, markerLView, ctx.canvas.height);
        if (markerRWorld < viewRWorld)
          ctx.fillRect(markerRView, 0, viewR, ctx.canvas.height);

        ctx.globalAlpha = 1.0;
      }

      for (var i = 0; i < this.markers.length; ++i) {
        this.markers[i].drawLine(ctx, viewLWorld, viewRWorld,
            ctx.canvas.height, this);
      }
    }
  };

  /**
   * Represents a marked position in the world, at a viewport level.
   * @constructor
   */
  function ViewportMarker(vp, positionWorld) {
    this.viewport_ = vp;
    this.positionWorld_ = positionWorld;
    this.selected_ = false;
  }

  ViewportMarker.prototype = {
    get positionWorld() {
      return this.positionWorld_;
    },

    set positionWorld(positionWorld) {
      this.positionWorld_ = positionWorld;
      this.viewport_.dispatchChangeEvent();
    },

    get positionView() {
      return this.viewport_.xWorldToView(this.positionWorld);
    },

    set selected(selected) {
      if (this.selected === selected)
        return;
      this.selected_ = selected;
      this.viewport_.dispatchChangeEvent();
    },

    get selected() {
      return this.selected_;
    },

    get color() {
      if (this.selected)
        return 'rgb(255,0,0)';
      return 'rgb(0,0,0)';
    },

    drawTriangle_: function(ctx, viewLWorld, viewRWorld,
                            canvasH, rulerHeight, vp) {
      ctx.beginPath();

      var ts = this.positionWorld_;
      if (ts < viewLWorld || ts > viewRWorld)
        return;

      var viewX = vp.xWorldToView(ts);
      ctx.moveTo(viewX, rulerHeight);
      ctx.lineTo(viewX - 3, rulerHeight / 2);
      ctx.lineTo(viewX + 3, rulerHeight / 2);
      ctx.lineTo(viewX, rulerHeight);
      ctx.closePath();
      ctx.fillStyle = this.color;
      ctx.fill();
    },

    drawLine: function(ctx, viewLWorld, viewRWorld, canvasH, vp) {
      ctx.beginPath();
      var ts = this.positionWorld_;
      if (ts >= viewLWorld && ts < viewRWorld) {
        var viewX = vp.xWorldToView(ts);
        ctx.moveTo(viewX, 0);
        ctx.lineTo(viewX, canvasH);
      }
      ctx.strokeStyle = this.color;
      ctx.stroke();
    }
  };

  return {
    TimelineViewport: TimelineViewport,
    ViewportMarker: ViewportMarker
  };
});
<|endoftext|>


<commit_before>require("sugar");
fs = require("fs");
Handlebars = require("handlebars");

//
// The base Bugsnag NotificationPlugin class
// Extend this class to create your own Bugsnag notification plugins:
//
//   NotificationPlugin = require "../../notification-plugin.js"
//   class MyPlugin extends NotificationPlugin
//     @receiveEvent = (config, event) ->
//       ...
//   module.exports = MyPlugin
//
// All notification plugins must override the receiveEvent function to perform
// the notification. This method is fired when a new event is triggered.
//
// See https://github.com/bugsnag/bugsnag-notification-plugins/ for full docs
//

var NotificationPlugin = (function () {
  function NotificationPlugin() {}

  // Load templates
  NotificationPlugin.markdownTemplate = Handlebars.compile(fs.readFileSync(__dirname + "/templates/error.md", "utf8"));
  NotificationPlugin.textTemplate = Handlebars.compile(fs.readFileSync(__dirname + "/templates/error.txt", "utf8"));

  // Fired when a new event is triggered for notification
  // Plugins MUST override this method
  NotificationPlugin.receiveEvent = function (config, event, callback) {
    throw new Error("Plugins must override receiveEvent");
  };

  // Utility methods for generating notification content
  NotificationPlugin.stacktraceLineString = function (stacktraceLine) {
    return stacktraceLine.file + ":" + stacktraceLine.lineNumber + " - " + stacktraceLine.method
  };

  NotificationPlugin.firstStacktraceLine = function (stacktrace) {
    for(var i=0; i<stacktrace.length; i++) {
      var line = stacktrace[i];
      if(line.inProject) {
        return this.stacktraceLineString(line);
      }
    }

    return this.stacktraceLineString(stacktrace[0]);
  };

  NotificationPlugin.title = function (event) {
    return event.error.exceptionClass + " in " + event.error.context;
  };

  NotificationPlugin.markdownBody = function (event) {
    return this.markdownTemplate(event);
  };

  NotificationPlugin.textBody = function (event) {
    return this.textTemplate(event);
  };

  // Utility methods for http requests
  NotificationPlugin.request = require("superagent");

  // Fire a test event to your notification plugin (do not override)
  NotificationPlugin.fireTestEvent = function (config, callback) {
    var event = {
      error: {
        exceptionClass: "ExampleException",
        message: "Something really bad happened",
        context: "home#example",
        appVersion: "1.0.0",
        releaseStage: "production",
        occurrences: 42,
        firstReceived: new Date(),
        usersAffected: 20,
        url: "http://bugsnag.com/errors/example/events/example",
        stacktrace: [{
          file: "app/controllers/home_controller.rb",
          lineNumber: 123,
          method: "example",
          inProject: true
        }, {
          file: "app/controllers/other_controller.rb",
          lineNumber: 12,
          method: "broken",
          inProject: true
        }]
      },
      project: {
        name: "Example.com",
        url: "http://bugsnag.com/projects/example"
      },
      trigger: {
        type: "firstException",
        message: "New exception"
      }
    };

    this.receiveEvent(config, event, callback);
  };

  // Configuration validation methods (do not override)
  NotificationPlugin.validateConfig = function (config, pluginConfigFile) {
    var fs = require("fs");
    var pluginConfig = JSON.parse(fs.readFileSync(pluginConfigFile, "ascii"));
    if(pluginConfig.fields) {
      pluginConfig.fields.each(function (option) {
        var configValue = config[option.name];

        // Validate all non-optional config fields are present
        if (!(configValue !== undefined || option.optional || (option.type == "boolean" && option.defaultValue !== undefined))) {
          throw new Error("ConfigurationError: Required configuration option '" + option.name + "' is missing");
        }

        // Validate fields with allowed values
        if (configValue !== undefined && option.allowedValues && option.allowedValues.none(configValue)) {
          throw new Error("ConfigurationError: Invalid value for '" + option.name + "'");
        }

        // Fill in default values
        if(configValue == undefined && option.defaultValue !== undefined) {
          config[option.name] = option.defaultValue;
        }
      });
    }
  };

  return NotificationPlugin;
})();
module.exports = NotificationPlugin;

// If running plugins from the command line, allow them to fire test events
if (module.parent && module.parent.parent === null) {
  var path = require("path");
  var argv = require("optimist").argv;

  // Parse command line flags
  var flags = Object.keys(argv).exclude("_", "$0");
  var config = {};
  flags.each(function (flag) {
    if(argv[flag] != null && argv[flag] != "") {
      config[flag] = argv[flag];
    }
  });

  // Validate configuration
  try {
    NotificationPlugin.validateConfig(config, path.dirname(module.parent.filename) + "/config.json");
  } catch (err) {
    return console.error(err.message);
  }

  // Fire a test event
  var plugin = require(module.parent.filename);
  plugin.fireTestEvent(config, function (err, data) {
    if(err) {
      console.error("Error firing notification\n", err.stack);
    } else {
      console.log("Fired test event successfully\n", data);
    }
  });
}<commit_msg>Remove text template for now<commit_after>require("sugar");
fs = require("fs");
Handlebars = require("handlebars");

//
// The base Bugsnag NotificationPlugin class
// Extend this class to create your own Bugsnag notification plugins:
//
//   NotificationPlugin = require "../../notification-plugin.js"
//   class MyPlugin extends NotificationPlugin
//     @receiveEvent = (config, event) ->
//       ...
//   module.exports = MyPlugin
//
// All notification plugins must override the receiveEvent function to perform
// the notification. This method is fired when a new event is triggered.
//
// See https://github.com/bugsnag/bugsnag-notification-plugins/ for full docs
//

var NotificationPlugin = (function () {
  function NotificationPlugin() {}

  // Load templates
  NotificationPlugin.markdownTemplate = Handlebars.compile(fs.readFileSync(__dirname + "/templates/error.md", "utf8"));

  // Fired when a new event is triggered for notification
  // Plugins MUST override this method
  NotificationPlugin.receiveEvent = function (config, event, callback) {
    throw new Error("Plugins must override receiveEvent");
  };

  // Utility methods for generating notification content
  NotificationPlugin.stacktraceLineString = function (stacktraceLine) {
    return stacktraceLine.file + ":" + stacktraceLine.lineNumber + " - " + stacktraceLine.method
  };

  NotificationPlugin.firstStacktraceLine = function (stacktrace) {
    for(var i=0; i<stacktrace.length; i++) {
      var line = stacktrace[i];
      if(line.inProject) {
        return this.stacktraceLineString(line);
      }
    }

    return this.stacktraceLineString(stacktrace[0]);
  };

  NotificationPlugin.title = function (event) {
    return event.error.exceptionClass + " in " + event.error.context;
  };

  NotificationPlugin.markdownBody = function (event) {
    return this.markdownTemplate(event);
  };

  NotificationPlugin.textBody = function (event) {
    return this.textTemplate(event);
  };

  // Utility methods for http requests
  NotificationPlugin.request = require("superagent");

  // Fire a test event to your notification plugin (do not override)
  NotificationPlugin.fireTestEvent = function (config, callback) {
    var event = {
      error: {
        exceptionClass: "ExampleException",
        message: "Something really bad happened",
        context: "home#example",
        appVersion: "1.0.0",
        releaseStage: "production",
        occurrences: 42,
        firstReceived: new Date(),
        usersAffected: 20,
        url: "http://bugsnag.com/errors/example/events/example",
        stacktrace: [{
          file: "app/controllers/home_controller.rb",
          lineNumber: 123,
          method: "example",
          inProject: true
        }, {
          file: "app/controllers/other_controller.rb",
          lineNumber: 12,
          method: "broken",
          inProject: true
        }]
      },
      project: {
        name: "Example.com",
        url: "http://bugsnag.com/projects/example"
      },
      trigger: {
        type: "firstException",
        message: "New exception"
      }
    };

    this.receiveEvent(config, event, callback);
  };

  // Configuration validation methods (do not override)
  NotificationPlugin.validateConfig = function (config, pluginConfigFile) {
    var fs = require("fs");
    var pluginConfig = JSON.parse(fs.readFileSync(pluginConfigFile, "ascii"));
    if(pluginConfig.fields) {
      pluginConfig.fields.each(function (option) {
        var configValue = config[option.name];

        // Validate all non-optional config fields are present
        if (!(configValue !== undefined || option.optional || (option.type == "boolean" && option.defaultValue !== undefined))) {
          throw new Error("ConfigurationError: Required configuration option '" + option.name + "' is missing");
        }

        // Validate fields with allowed values
        if (configValue !== undefined && option.allowedValues && option.allowedValues.none(configValue)) {
          throw new Error("ConfigurationError: Invalid value for '" + option.name + "'");
        }

        // Fill in default values
        if(configValue == undefined && option.defaultValue !== undefined) {
          config[option.name] = option.defaultValue;
        }
      });
    }
  };

  return NotificationPlugin;
})();
module.exports = NotificationPlugin;

// If running plugins from the command line, allow them to fire test events
if (module.parent && module.parent.parent === null) {
  var path = require("path");
  var argv = require("optimist").argv;

  // Parse command line flags
  var flags = Object.keys(argv).exclude("_", "$0");
  var config = {};
  flags.each(function (flag) {
    if(argv[flag] != null && argv[flag] != "") {
      config[flag] = argv[flag];
    }
  });

  // Validate configuration
  try {
    NotificationPlugin.validateConfig(config, path.dirname(module.parent.filename) + "/config.json");
  } catch (err) {
    return console.error(err.message);
  }

  // Fire a test event
  var plugin = require(module.parent.filename);
  plugin.fireTestEvent(config, function (err, data) {
    if(err) {
      console.error("Error firing notification\n", err.stack);
    } else {
      console.log("Fired test event successfully\n", data);
    }
  });
}<|endoftext|>


<commit_before>var _ = require('alloy/underscore')._,
	db;

function S4() {
   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};

function guid() {
   return (S4()+S4()+'-'+S4()+'-'+S4()+'-'+S4()+'-'+S4()+S4()+S4());
};

function InitAdapter(config) {
	if (!db) {
		if (Ti.Platform.osname === 'mobileweb' || typeof Ti.Database === 'undefined') {
			throw 'No support for Titanium.Database in MobileWeb environment.';
		}
		else {
			db = Ti.Database.open('_alloy_');
		}
		module.exports.db = db;

		// create the migration table in case it doesn't exist
		db.execute('CREATE TABLE IF NOT EXISTS migrations (latest TEXT, model TEXT)');
	}
	return {};
}

function GetMigrationFor(table) {
	var mid;
	// get the latest migratino
	var rs = db.execute('SELECT latest FROM migrations where model = ?', table);
	if (rs.isValidRow()) {
		mid = rs.field(0);
	}
	rs.close();
	return mid;
}

function SQLiteMigrateDB() {
	//TODO: normalize columns at compile time - https://jira.appcelerator.org/browse/ALOY-222
	this.column = function(name)
	{
		switch(name)
		{
			case 'string':
			case 'varchar':
			case 'text':
			{
				return 'TEXT';
			}
			case 'int':
			case 'tinyint':
			case 'smallint':
			case 'bigint':
			case 'integer':
			{
				return 'INTEGER';
			}
			case 'double':
			case 'float':
			case 'real':
			{
				return 'REAL';
			}
			case 'blob':
			{
				return 'BLOB';
			}
			case 'decimal':
			case 'number':
			case 'date':
			case 'datetime':
			case 'boolean':
			{
				return 'NUMERIC';
			}
			case 'null':
			{
				return 'NULL';
			}
		}
		return 'TEXT';
	};

	this.createTable = function(config) {
		Ti.API.info('create table migration called for '+config.adapter.collection_name);

		var self = this,
			columns = [];

		for (var k in config.columns) {
			columns.push(k+" "+self.column(config.columns[k]));
		}

		var sql = 'CREATE TABLE IF NOT EXISTS '+config.adapter.collection_name+' ( '+columns.join(',')+',id' + ' )';
		Ti.API.info(sql);

		db.execute(sql);
	};

	this.dropTable = function(name) {
		Ti.API.info('drop table migration called for '+name);
		db.execute('DROP TABLE IF EXISTS '+name);
	};
}

function Sync(model, method, opts) {
	var table =  model.config.adapter.collection_name;
	var columns = model.config.columns;
	var resp = null;

	switch (method) {

		case 'create':
			var names = [];
			var values = [];
			var q = [];
			for (var k in columns) {
				names.push(k);
				values.push(model.get(k));
				q.push('?');
			}
			var id = guid();
			var sql = 'INSERT INTO '+table+' ('+names.join(',')+',id) VALUES ('+q.join(',')+',?)';
			values.push(id);
			db.execute(sql, values);
			model.id = id;
			resp = model.toJSON();
			break;

		case 'read':
			var sql = 'SELECT * FROM '+table;
			var rs = db.execute(sql);
			var len = 0;
			var values = [];
			while(rs.isValidRow())
			{
				var o = {};
                var fc = 0;

                // Pre-3.0.0, Android supported fieldCount as a property, iOS
                // supported it as a function. Post-3.0.0, both are a property.
				fc = _.isFunction(rs.fieldCount) ? rs.fieldCount() : rs.fieldCount;

				_.times(fc,function(c){
					var fn = rs.fieldName(c);
					o[fn] = rs.fieldByName(fn);
				});
				values.push(o);
				len++;
				rs.next();
			}
			rs.close();
			model.length = len;
			len === 1 ? resp = values[0] : resp = values;
			break;

		case 'update':
			var names = [];
			var values = [];
			var q = [];
			for (var k in columns)
			{
				names.push(k+'=?');
				values.push(model.get(k));
				q.push('?');
			}
			var sql = 'UPDATE '+table+' SET '+names.join(',')+' WHERE id=?';

		    var e = sql +','+values.join(',')+','+model.id;
		    values.push(model.id);
			db.execute(sql,values);
			resp = model.toJSON();
			break;

		case 'delete':
			var sql = 'DELETE FROM '+table+' WHERE id=?';
			db.execute(sql, model.id);
			model.id = null;
			resp = model.toJSON();
			break;
	}

    // process success/error handlers, if present
	if (resp) {
        _.isFunction(opts.success) && opts.success(resp);
        method === "read" && model.trigger("fetch");
    } else {
    	_.isFunction(opts.error) && opts.error("Record not found");
    }

}

function GetMigrationForCached(t,m) {
	if (m[t]) {
		return m[t];
	}
	var v = GetMigrationFor(t);
	if (v) {
		m[t] = v;
	}
	return v;
}

function Migrate(migrations, config) {
	var prev;
	var sqlMigration = new SQLiteMigrateDB;
	var migrationIds = {}; // cache for latest mid by model name

	db.execute('BEGIN;');

	if (migrations.length) {
		// iterate through all our migrations and call up/down and the last migration should
		// have the up called but never the down -- the migrations come in pre sorted from
		// oldest to newest based on timestamp
		_.each(migrations,function(migration) {
			var mctx = {};
			migration(mctx);
			var mid = GetMigrationForCached(mctx.name,migrationIds);
			Ti.API.info('mid = '+mid+', name = '+mctx.name);
			if (!mid || mctx.id > mid) {
				Ti.API.info('Migration starting to '+mctx.id+' for '+mctx.name);
				if (prev && _.isFunction(prev.down)) {
					prev.down(sqlMigration);
				}
				if (_.isFunction(mctx.up)) {
					mctx.down(sqlMigration);
					mctx.up(sqlMigration);
				}
				prev = mctx;
			}
			else {
				Ti.API.info('skipping migration '+mctx.id+', already performed');
				prev = null;
			}
		});

		if (prev && prev.id) {
			db.execute('DELETE FROM migrations where model = ?', prev.name);
			db.execute('INSERT INTO migrations VALUES (?,?)', prev.id,prev.name);
		}
	} else {
		sqlMigration.createTable(config);
	}

	db.execute('COMMIT;');
}

module.exports.sync = Sync;

module.exports.beforeModelCreate = function(config) {
	config = config || {};

    InitAdapter(config);

	return config;
};

module.exports.afterModelCreate = function(Model) {
	Model = Model || {};

	Model.prototype.config.Model = Model; // needed for fetch operations to initialize the collection from persistent store

	Migrate(Model.migrations, Model.prototype.config);

	return Model;
};

<commit_msg>refactored sql adapter "create" sync to use idAttribute<commit_after>var _ = require('alloy/underscore')._,
	db;

function S4() {
   return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
};

function guid() {
   return (S4()+S4()+'-'+S4()+'-'+S4()+'-'+S4()+'-'+S4()+S4()+S4());
};

function InitAdapter(config) {
	if (!db) {
		if (Ti.Platform.osname === 'mobileweb' || typeof Ti.Database === 'undefined') {
			throw 'No support for Titanium.Database in MobileWeb environment.';
		}
		else {
			db = Ti.Database.open('_alloy_');
		}
		module.exports.db = db;

		// create the migration table in case it doesn't exist
		db.execute('CREATE TABLE IF NOT EXISTS migrations (latest TEXT, model TEXT)');
	}
	return {};
}

function GetMigrationFor(table) {
	var mid;
	// get the latest migratino
	var rs = db.execute('SELECT latest FROM migrations where model = ?', table);
	if (rs.isValidRow()) {
		mid = rs.field(0);
	}
	rs.close();
	return mid;
}

function SQLiteMigrateDB() {
	//TODO: normalize columns at compile time - https://jira.appcelerator.org/browse/ALOY-222
	this.column = function(name)
	{
		switch(name)
		{
			case 'string':
			case 'varchar':
			case 'text':
			{
				return 'TEXT';
			}
			case 'int':
			case 'tinyint':
			case 'smallint':
			case 'bigint':
			case 'integer':
			{
				return 'INTEGER';
			}
			case 'double':
			case 'float':
			case 'real':
			{
				return 'REAL';
			}
			case 'blob':
			{
				return 'BLOB';
			}
			case 'decimal':
			case 'number':
			case 'date':
			case 'datetime':
			case 'boolean':
			{
				return 'NUMERIC';
			}
			case 'null':
			{
				return 'NULL';
			}
		}
		return 'TEXT';
	};

	this.createTable = function(config) {
		Ti.API.info('create table migration called for '+config.adapter.collection_name);

		var self = this,
			columns = [];

		for (var k in config.columns) {
			columns.push(k+" "+self.column(config.columns[k]));
		}

		var sql = 'CREATE TABLE IF NOT EXISTS '+config.adapter.collection_name+' ( '+columns.join(',')+',id' + ' )';
		Ti.API.info(sql);

		db.execute(sql);
	};

	this.dropTable = function(name) {
		Ti.API.info('drop table migration called for '+name);
		db.execute('DROP TABLE IF EXISTS '+name);
	};
}

function Sync(model, method, opts) {
	var table =  model.config.adapter.collection_name,
		columns = model.config.columns,
		resp = null;

	switch (method) {
		case 'create':
			resp = (function(){
				// Use idAttribute to account for something other then "id"
				// being used for the model's id.
				if (!model.id) {
	                model.id = guid();
	                model.set(model.idAttribute, model.id);
	            }

	            // Create arrays for insert query
				var names = [], values = [], q = [];
				for (var k in columns) {
					names.push(k);
					values.push(model.get(k));
					q.push('?');
				}

				// Assemble create query
				var sql = "INSERT INTO " + table + " (" + names.join(",") + ",id) VALUES (" + q.join(",") + ",?)";

				// add id as the last value to the query
	            values.push(model.id);

	            // execute the query and return the response
	            db.execute(sql, values);
	            return model.toJSON();
			})();
			break;
		case 'read':
			var sql = 'SELECT * FROM '+table;
			var rs = db.execute(sql);
			var len = 0;
			var values = [];
			while(rs.isValidRow())
			{
				var o = {};
                var fc = 0;

                // Pre-3.0.0, Android supported fieldCount as a property, iOS
                // supported it as a function. Post-3.0.0, both are a property.
				fc = _.isFunction(rs.fieldCount) ? rs.fieldCount() : rs.fieldCount;

				_.times(fc,function(c){
					var fn = rs.fieldName(c);
					o[fn] = rs.fieldByName(fn);
				});
				values.push(o);
				len++;
				rs.next();
			}
			rs.close();
			model.length = len;
			len === 1 ? resp = values[0] : resp = values;
			break;

		case 'update':
			var names = [];
			var values = [];
			var q = [];
			for (var k in columns)
			{
				names.push(k+'=?');
				values.push(model.get(k));
				q.push('?');
			}
			var sql = 'UPDATE '+table+' SET '+names.join(',')+' WHERE id=?';

		    var e = sql +','+values.join(',')+','+model.id;
		    values.push(model.id);
			db.execute(sql,values);
			resp = model.toJSON();
			break;

		case 'delete':
			var sql = 'DELETE FROM '+table+' WHERE id=?';
			db.execute(sql, model.id);
			model.id = null;
			resp = model.toJSON();
			break;
	}

    // process success/error handlers, if present
	if (resp) {
        _.isFunction(opts.success) && opts.success(resp);
        method === "read" && model.trigger("fetch");
    } else {
    	_.isFunction(opts.error) && opts.error("Record not found");
    }

}

function GetMigrationForCached(t,m) {
	if (m[t]) {
		return m[t];
	}
	var v = GetMigrationFor(t);
	if (v) {
		m[t] = v;
	}
	return v;
}

function Migrate(migrations, config) {
	var prev;
	var sqlMigration = new SQLiteMigrateDB;
	var migrationIds = {}; // cache for latest mid by model name

	db.execute('BEGIN;');

	if (migrations.length) {
		// iterate through all our migrations and call up/down and the last migration should
		// have the up called but never the down -- the migrations come in pre sorted from
		// oldest to newest based on timestamp
		_.each(migrations,function(migration) {
			var mctx = {};
			migration(mctx);
			var mid = GetMigrationForCached(mctx.name,migrationIds);
			Ti.API.info('mid = '+mid+', name = '+mctx.name);
			if (!mid || mctx.id > mid) {
				Ti.API.info('Migration starting to '+mctx.id+' for '+mctx.name);
				if (prev && _.isFunction(prev.down)) {
					prev.down(sqlMigration);
				}
				if (_.isFunction(mctx.up)) {
					mctx.down(sqlMigration);
					mctx.up(sqlMigration);
				}
				prev = mctx;
			}
			else {
				Ti.API.info('skipping migration '+mctx.id+', already performed');
				prev = null;
			}
		});

		if (prev && prev.id) {
			db.execute('DELETE FROM migrations where model = ?', prev.name);
			db.execute('INSERT INTO migrations VALUES (?,?)', prev.id,prev.name);
		}
	} else {
		sqlMigration.createTable(config);
	}

	db.execute('COMMIT;');
}

module.exports.sync = Sync;

module.exports.beforeModelCreate = function(config) {
	config = config || {};

    InitAdapter(config);

	return config;
};

module.exports.afterModelCreate = function(Model) {
	Model = Model || {};

	Model.prototype.config.Model = Model; // needed for fetch operations to initialize the collection from persistent store

	Migrate(Model.migrations, Model.prototype.config);

	return Model;
};

<|endoftext|>


<commit_before>/**
 * Write a function `deepCopy(obj)` which takes an object as argument and returns a
 * deep copy of the same object. Don't use JSON.stringify/JSON.parse methods.
 * if the object contains array with holes, those should be preserved, ie: [1,,3]
 */

// test > deepCopy
// # deepCopy({a:1, b: 2}) === {a:1,b:2} (return deep copy)
// # deepCopy({a:1, b: {b:2, c:3}}) === {a:1,b:{b:2,c:3}} (return deep copy with nested objects)
// # deepCopy({a:1, b: {b:2, c:3}, d: null, e: [1,2,3, [3, ,5]]}) === {a:1,b:{b:2,c:3},d:null,e:[1,2,3,[3,,5]]} (return deep copy with holes arrays)
exports.deepCopy = function(obj) {
  var copy = Array.isArray(obj) ? [] : {};

  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    if (typeof obj[key] === 'string' || typeof obj[key] === 'number' || typeof obj[key] === 'boolean' || obj[key] === undefined || obj[key] === null) {
      copy[key] = obj[key];
    } else {
      copy[key] = exports.deepCopy(obj[key]);
    }
  }
  return copy;
};
<commit_msg>small refactor<commit_after>/**
 * Write a function `deepCopy(obj)` which takes an object as argument and returns a
 * deep copy of the same object. Don't use JSON.stringify/JSON.parse methods.
 * if the object contains array with holes, those should be preserved, ie: [1,,3]
 */

// test > deepCopy
// # deepCopy({a:1, b: 2}) === {a:1,b:2} (return deep copy)
// # deepCopy({a:1, b: {b:2, c:3}}) === {a:1,b:{b:2,c:3}} (return deep copy with nested objects)
// # deepCopy({a:1, b: {b:2, c:3}, d: null, e: [1,2,3, [3, ,5]]}) === {a:1,b:{b:2,c:3},d:null,e:[1,2,3,[3,,5]]} (return deep copy with holes arrays)
exports.deepCopy = function(obj) {
  var copy = Array.isArray(obj) ? [] : {};

  var keys = Object.keys(obj);
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    if (obj[key] === undefined || obj[key] === null || typeof obj[key] !== 'object') {
      copy[key] = obj[key];
    } else {
      copy[key] = exports.deepCopy(obj[key]);
    }
  }
  return copy;
};
<|endoftext|>


<commit_before>import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining";

export default function ({ types: t }) {
  function optional(path, key, replacementPath, needsContext = false, loose = false) {
    const { scope } = path;
    const optionals = [path.node];

    let objectPath = path.get(key);
    while (objectPath.isMemberExpression()) {
      const { node } = objectPath;
      if (node.optional) {
        optionals.push(node);
      }

      objectPath = objectPath.get("object");
    }

    for (let i = optionals.length - 1; i >= 0; i--) {
      const node = optionals[i];
      node.optional = false;

      const replaceKey = i == 0 ? key : "object";
      const atCall = needsContext && i == 0;

      const chain = node[replaceKey];

      let ref;
      let check;
      if (loose && atCall) {
        // If we are using a loose transform (avoiding a Function#call) and we are at the call,
        // we can avoid a needless memoize.
        check = ref = chain;
      } else {
        ref = scope.maybeGenerateMemoised(chain);
        if (ref) {
          check = t.assignmentExpression("=", ref, chain);
          node[replaceKey] = ref;
        } else {
          check = ref = chain;
        }
      }

      // Ensure call expressions have the proper `this`
      // `foo.bar()` has context `foo`.
      if (atCall && t.isMemberExpression(chain)) {
        if (loose) {
          // To avoid a Function#call, we can instead re-grab the property from the context object.
          // `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
          node.callee = chain;
        } else {
          // Otherwise, we need to memoize the context object, and change the call into a Function#call.
          // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)`
          const { object } = chain;
          let context = scope.maybeGenerateMemoised(object);
          if (context) {
            chain.object = t.assignmentExpression("=", context, object);
          } else {
            context = object;
          }

          node.arguments.unshift(context);
          node.callee = t.memberExpression(node.callee, t.identifier("call"));
        }
      }

      replacementPath.replaceWith(t.conditionalExpression(
        t.binaryExpression("==", check, t.nullLiteral()),
        scope.buildUndefinedNode(),
        replacementPath.node
      ));

      replacementPath = replacementPath.get("alternate");
    }
  }

  return {
    inherits: syntaxOptionalChaining,

    visitor: {
      MemberExpression(path) {
        if (!path.node.optional) {
          return;
        }

        const replace = path.find((path) => {
          const { parentPath } = path;

          if (path.key == "left" && parentPath.isAssignmentExpression()) {
            return false;
          }
          if (path.key == "object" && parentPath.isMemberExpression()) {
            return false;
          }
          if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) {
            return false;
          }
          if (path.key == "argument" && parentPath.isUnaryExpression({ operator: "delete" })) {
            return false;
          }

          return true;
        });

        optional(path, "object", replace);
      },

      "NewExpression|CallExpression"(path) {
        if (!path.node.optional) {
          return;
        }

        optional(path, "callee", path, path.isCallExpression(), this.opts.loose);
      },
    },
  };
}
<commit_msg>Consider any unary expression<commit_after>import syntaxOptionalChaining from "babel-plugin-syntax-optional-chaining";

export default function ({ types: t }) {
  function optional(path, key, replacementPath, needsContext = false, loose = false) {
    const { scope } = path;
    const optionals = [path.node];

    let objectPath = path.get(key);
    while (objectPath.isMemberExpression()) {
      const { node } = objectPath;
      if (node.optional) {
        optionals.push(node);
      }

      objectPath = objectPath.get("object");
    }

    for (let i = optionals.length - 1; i >= 0; i--) {
      const node = optionals[i];
      node.optional = false;

      const replaceKey = i == 0 ? key : "object";
      const atCall = needsContext && i == 0;

      const chain = node[replaceKey];

      let ref;
      let check;
      if (loose && atCall) {
        // If we are using a loose transform (avoiding a Function#call) and we are at the call,
        // we can avoid a needless memoize.
        check = ref = chain;
      } else {
        ref = scope.maybeGenerateMemoised(chain);
        if (ref) {
          check = t.assignmentExpression("=", ref, chain);
          node[replaceKey] = ref;
        } else {
          check = ref = chain;
        }
      }

      // Ensure call expressions have the proper `this`
      // `foo.bar()` has context `foo`.
      if (atCall && t.isMemberExpression(chain)) {
        if (loose) {
          // To avoid a Function#call, we can instead re-grab the property from the context object.
          // `a.?b.?()` translates roughly to `_a.b != null && _a.b()`
          node.callee = chain;
        } else {
          // Otherwise, we need to memoize the context object, and change the call into a Function#call.
          // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)`
          const { object } = chain;
          let context = scope.maybeGenerateMemoised(object);
          if (context) {
            chain.object = t.assignmentExpression("=", context, object);
          } else {
            context = object;
          }

          node.arguments.unshift(context);
          node.callee = t.memberExpression(node.callee, t.identifier("call"));
        }
      }

      replacementPath.replaceWith(t.conditionalExpression(
        t.binaryExpression("==", check, t.nullLiteral()),
        scope.buildUndefinedNode(),
        replacementPath.node
      ));

      replacementPath = replacementPath.get("alternate");
    }
  }

  return {
    inherits: syntaxOptionalChaining,

    visitor: {
      MemberExpression(path) {
        if (!path.node.optional) {
          return;
        }

        const replace = path.find((path) => {
          const { parentPath } = path;

          if (path.key == "left" && parentPath.isAssignmentExpression()) {
            return false;
          }
          if (path.key == "object" && parentPath.isMemberExpression()) {
            return false;
          }
          if (path.key == "callee" && (parentPath.isCallExpression() || parentPath.isNewExpression())) {
            return false;
          }
          if (path.key == "argument" && parentPath.isUnaryExpression()) {
            return false;
          }

          return true;
        });

        optional(path, "object", replace);
      },

      "NewExpression|CallExpression"(path) {
        if (!path.node.optional) {
          return;
        }

        optional(path, "callee", path, path.isCallExpression(), this.opts.loose);
      },
    },
  };
}
<|endoftext|>


<commit_before>
'use strict';

angular.module('mean.icu.ui.displayby', [])
.directive('icuDisplayBy', function() {
    function controller($scope, $state, context,SettingServices,SearchService,EntityService, $rootScope, $stateParams,$location, $window,NotifyingService,UsersService, TasksService,ProjectsService,DiscussionsService,OfficesService,TemplateDocsService, OfficeDocumentsService) {
        $scope.statusList = SettingServices.getStatusList();
        $scope.activeList = SettingServices.getActiveStatusList();
        $scope.archiveList = SettingServices.getNonActiveStatusList();
        $scope.userFilterList = SettingServices.getUserFilter();

        $rootScope.$on('changeStatus',function(){
          $scope.AllStatus = $scope.statusList[$scope.filteringData.issue];
        });

        function arrayUnique(array) {
            var a = array.concat();
            for(var i=0; i<a.length; ++i) {
                for(var j=i+1; j<a.length; ++j) {
                    if(a[i] === a[j])
                        a.splice(j--, 1);
                }
            }

            return a;
        }
        $scope.currentType = 'All';

        $scope.clearDateRange = function(){
            SearchService.filterDateOption = null;
            $scope.datePicker.date = {startDate: null, endDate: null};
        };

        $scope.removeTags = function(text){
            return text ? String(text).replace(/<[^>]+>/gm, '') : '';
        };

        $scope.clearDueDate = function(){
          $scope.dueDate = null;
          SearchService.filteringByDueDate = null;
        };

        $scope.clearUpdatedDate = function(){
            $scope.updatedDate = null;
            SearchService.filteringByUpdated = null;
        };

        //clearing default date filter
        $scope.clearDateRange();
        $scope.clearDueDate();
        $scope.clearUpdatedDate();

        $scope.turnOffRecycle = function () {
            var query = SearchService.getQuery();
            $scope.recycled = false;
            $scope.isRecycled = false;
            $state.go('main.search', {reload: true, query: query});
        };

        $scope.tmpStatus = [];

          $scope.statusListChange = function(type){
            var index;
            if($scope.currentType == 'active'){
                if ($scope.activeList.indexOf(type) < 0)
                $scope.activeList.push(type);
                  else {
                      index = $scope.activeList.indexOf(type);
                      $scope.activeList.splice(index , 1);
                  }
            }
            else if($scope.currentType == 'nonactive'){
                // archiveList.push(type);
                if ($scope.archiveList.indexOf(type) < 0)
                $scope.archiveList.push(type);
                else {
                   index = $scope.archiveList.indexOf(type);
                   $scope.archiveList.splice(index , 1);
                }
            }
            else {
                $scope.currentType = 'all';
                if ($scope.userFilterList.indexOf(type) < 0)
                    $scope.userFilterList.push(type);
                else {
                   index = $scope.userFilterList.indexOf(type);
                    $scope.userFilterList.splice(index , 1);
                }
            }
        };

        $scope.isActive = function(type, status){
            if ( status !== 'active' && status !== 'nonactive' && $scope.tmpStatus.length && $scope.tmpStatus.indexOf(type)> -1)
                return true;
            if (status !== 'active' && status !== 'nonactive' && status == type)
                return true;
            else {
                if($scope.currentType == 'all') {
                    if ($scope.userFilterList.indexOf(type) > -1)
                        return true;
                }
                if($scope.currentType == 'active'){
                    if ($scope.activeList.indexOf(type) >-1)
                        return true;
                }
                if($scope.currentType == 'nonactive'){
                    if ($scope.archiveList.indexOf(type) >-1)
                    return true;
                }
            }
            return false;
        }

        $scope.AllStatus = [];

        $scope.showM = function(type){
            $scope.currentType = type;
            if (type == 'empty'){
              $scope.AllStatus = [];
              $scope.activeList = SettingServices.getActiveStatusList();
              $scope.archiveList = SettingServices.getNonActiveStatusList();
              $scope.userFilterList = SettingServices.getUserFilter();
            }
            else if ($location.search().type)
              $scope.AllStatus = $scope.statusList[$scope.filteringData.issue];
            else $scope.AllStatus =  arrayUnique($scope.activeList.concat($scope.archiveList));
            $scope.tmpStatus = [];
        }

        $scope.typeClicked = false;

        NotifyingService.subscribe('editionData', function () {
            TasksService.getAll(0,2500,'created').then(function (data) {
                $scope.tasks = data.data || data;
            });

            ProjectsService.getAll(0,2500,'created').then(function (data) {
                $scope.projects = data.data || data;
            });

            DiscussionsService.getAll(0,2500,'created').then(function (data) {
                $scope.discussions = data.data || data;
            });

            OfficeDocumentsService.getAll(0,2500,'created').then(function (data) {
                $scope.documents = data.data || data;
            });

            OfficesService.getAll(0,2500,'created').then(function (data) {
                $scope.offices = data.data || data;
            });

            TemplateDocsService.getAll(0,2500,'created').then(function (data) {
                $scope.templateDocs = data.data || data;
            });

            UsersService.getAll(0,2500,'created').then(function (data) {
                $scope.people = data.data || data;
            });
            $scope.createLists();
        }, $scope);

        $scope.createLists();

        $scope.focus = false;
        $scope.changeFocus = function(){
            $scope.focus = !$scope.focus;
        };

        $scope.myFilter = function (item) {
            if(item.office.title && $scope.officesList.office.title)
            {
                return item.office.title === $scope.officesList.office.title;
            }
            else
            {
                return false;
            }

        };

        $scope.clearSearchType = function(){

            $location.search('type', null);
            $window.location.reload();
        }

        $scope.getLinkUrl = function(){
            return $state.href('main.' + $scope.activeTab.state ,{'officeDocuments':undefined});
        };

        //$scope.temp = {};
        $scope.changeOrder = function () {
            //$scope.temp.office = $scope.officesList.office;

            // if($scope.officesList.office.title != "custom"){
            //     $scope.sorting.isReverse = !$scope.sorting.isReverse;
            // }

            /*Made By OHAD - Needed for reversing sort*/
            //$state.go($state.current.name, { sort: $scope.officesList.office.title });
        };

        $scope.goToParentState = function(){

        };

        $scope.typesList = [{
            name: 'new',
             color:'ff4081'
        },{
            name:  'received',
            color:'37afef'
        },{
            name:  'in-progress',
            color:'f69679'
         },{
            name:  'sent',
            color:'359123'
        },{
            name:  'done',
            color:'757575'
        }];

        $scope.typeSelected = '';

        $scope.singularItemName = {
            tasks: "task",
            projects: "project",
            discussions: "discussion",
            officeDocuments: "officeDocument",
            offices: "office",
            folders: "folder",
            watchers: "watcher"
        };

        $scope.allItems = {
            tasks: $scope.tasks,
            projects: $scope.projects,
            discussions: $scope.discussions,
            officeDocuments: $scope.officeDocuments,
            offices: $scope.offices,
            folders: $scope.folders
        };

        // Reverse list in sideline
        $scope.projectsList = $scope.projectsList.slice();
        $scope.projectsList.reverse();
        $scope.allItems.projects = $scope.allItems.projects.slice();
        $scope.allItems.projects.reverse();
        $scope.allItems.discussions = $scope.allItems.discussions.slice();
        $scope.allItems.discussions.reverse();
        $scope.discussions = $scope.discussions.slice();
        $scope.discussions.reverse();

        $scope.context = context;

        $window.onbeforeunload = function(){
            localStorage.removeItem("type");
        };

        $scope.reset = function(main){
             EntityService.setEntityFolderValue(undefined, undefined);
            localStorage.removeItem("type");
            $scope.typeSelected = null;
        };

        $scope.changeState = function(state){
            $state.go(state, {
                //officeDocuments: undefined,
                status:undefined,
                activeTab: $scope.item
            }, {reload: true});
        };

        //$scope.typeSelected = localStorage.getItem("type");

        $scope.switchToType = function (type) {

            if(context.entityName == "folder"){
                $state.go('main.' + context.main + '.byentity', {
                    entity: context.entityName,
                    entityId: context.entityId,
                    status: type.name
                });

            }else{
                $state.go('main.' + context.main + '.all', {
                    status : type.name
                  },{reload:true});
            }

            $scope.typeSelected = type.name;

             EntityService.setActiveStatusFilterValue(type.name);
        //     OfficeDocumentsService.getAll(0,25,EntityService.getSortFilterValue().field, EntityService.getSortFilterValue().order,type.name).then(function (results) {
        //     $scope.officeDocuments=[];
        //     results.data.forEach(function(result){
        //         var flag = false;
        //         if(context.entityName='folder'){
        //             if(result.status==type.name&&result.folder&&result.folder._id==context.entityId){
        //                 flag = true;
        //             }
        //         }
        //         else{
        //             if(result.status==type.name){
        //                 flag= true;
        //             }
        //         }
        //         if(flag){
        //             $scope.officeDocuments.push(result);
        //         }

        //     });

        //  /**
        //  if (context.entityName == 'folder') {
        //      $scope.officeDocuments = $scope.officeDocuments.filter(function (officeDocument) {
        //          return officeDocument.status == type.name && officeDocument.folder && officeDocument.folder._id == context.entityId;
        //      });
        //  } else {

        //      $scope.officeDocuments = $scope.officeDocuments.filter(function (officeDocument) {
        //          return officeDocument.status == type.name;
        //      });
        //  }
        //  */

        //  localStorage.setItem("type", type.name);

        //  $state.go($state.current, {
        //      officeDocuments: $scope.officeDocuments,
        //      activeTab: $scope.item
        //  }, { reload: true });
        //     });

         /**
         var temp=[];
         $scope.officeDocuments.forEach(function(d){
             temp.push(d);
         });
         temp = temp.filter(function(officeDocument){
             if(context.entityName=='folder'){
                 return officeDocument.status == type.name &&officeDocument.folder&& officeDocument.folder._id==context.entityId ;
             }
             else{
                 return officeDocument.status == type.name;
             }
          });
           /*if(context.entityName == 'all'){
             $state.go('main.' + context.main + '.all', {},{reload: true});
         }else if (context.entityName == 'folder'){
             $state.go('main.' + context.main + '.byentity', {
                 entityId: context.entityId,
                 entity: 'folder',
                 officeDocuments: temp
             },{reload: true});
         }*/
     };
        $scope.switchTo = function (entityName, id) {
            EntityService.setEntityFolderValue(entityName, id);

                $state.go('main.' + context.main + '.byentity', {
                entity: entityName,
                entityId: id,
                status:undefined
            });
            // If we are switching between entities, then shrink the display limit again
            if (!$scope.visible[entityName]) {
                $scope.displayLimit.reset();
            }

        };

        $scope.switchToAll = function (entityName, id) {
            $state.go('main.' + context.main + '.all.details.activities', {
                id: id,
                entity: entityName,
            });
        };

        $scope.visible = {
            project: true,
            discussion: true,
            user: true,
            officeDocument: true,
            office: true,
            folder: true
        };

        $scope.visible[$scope.context.entityName] = true;

        $scope.GoToFolders = function() {
            $state.go('main.folders.all');
        };

        $scope.GoToOffices = function() {
            $state.go('main.offices.all');
        };

        $scope.GoToTemplateDocs = function() {
            $state.go('main.templateDocs.all');
        };

        $scope.GoToAdminSettings = function() {
            $state.go('main.adminSettings');
        }
    }

        function link($scope, $element, context) {
            $scope.showMore = function(limit, entityName) {
                if (($scope.displayLimit[entityName] + 4) >= limit) {
                    $scope.displayLimit[entityName] = limit;
                } else {
                    $scope.displayLimit[entityName]  += 4;

                }
            };

            $scope.collapse = function(entityName) {
                $scope.displayLimit[entityName] = $scope.displayLimit.default[entityName];
            };
        }

    return {
        restrict: 'A',
        templateUrl: '/icu/components/display-by/display-by.html',
        controller: controller,
        link: link
    };
});
<commit_msg>Fixd ui in pagination officeDocument<commit_after>
'use strict';

angular.module('mean.icu.ui.displayby', [])
.directive('icuDisplayBy', function() {
    function controller($scope, $state, context,SettingServices,SearchService,EntityService, $rootScope, $stateParams,$location, $window,NotifyingService,UsersService, TasksService,ProjectsService,DiscussionsService,OfficesService,TemplateDocsService, OfficeDocumentsService) {
        $scope.statusList = SettingServices.getStatusList();
        $scope.activeList = SettingServices.getActiveStatusList();
        $scope.archiveList = SettingServices.getNonActiveStatusList();
        $scope.userFilterList = SettingServices.getUserFilter();

        $rootScope.$on('changeStatus',function(){
          $scope.AllStatus = $scope.statusList[$scope.filteringData.issue];
        });

        function arrayUnique(array) {
            var a = array.concat();
            for(var i=0; i<a.length; ++i) {
                for(var j=i+1; j<a.length; ++j) {
                    if(a[i] === a[j])
                        a.splice(j--, 1);
                }
            }

            return a;
        }
        $scope.currentType = 'All';

        $scope.clearDateRange = function(){
            SearchService.filterDateOption = null;
            $scope.datePicker.date = {startDate: null, endDate: null};
        };

        $scope.removeTags = function(text){
            return text ? String(text).replace(/<[^>]+>/gm, '') : '';
        };

        $scope.clearDueDate = function(){
          $scope.dueDate = null;
          SearchService.filteringByDueDate = null;
        };

        $scope.clearUpdatedDate = function(){
            $scope.updatedDate = null;
            SearchService.filteringByUpdated = null;
        };

        //clearing default date filter
        $scope.clearDateRange();
        $scope.clearDueDate();
        $scope.clearUpdatedDate();

        $scope.turnOffRecycle = function () {
            var query = SearchService.getQuery();
            $scope.recycled = false;
            $scope.isRecycled = false;
            $state.go('main.search', {reload: true, query: query});
        };

        $scope.tmpStatus = [];

          $scope.statusListChange = function(type){
            var index;
            if($scope.currentType == 'active'){
                if ($scope.activeList.indexOf(type) < 0)
                $scope.activeList.push(type);
                  else {
                      index = $scope.activeList.indexOf(type);
                      $scope.activeList.splice(index , 1);
                  }
            }
            else if($scope.currentType == 'nonactive'){
                // archiveList.push(type);
                if ($scope.archiveList.indexOf(type) < 0)
                $scope.archiveList.push(type);
                else {
                   index = $scope.archiveList.indexOf(type);
                   $scope.archiveList.splice(index , 1);
                }
            }
            else {
                $scope.currentType = 'all';
                if ($scope.userFilterList.indexOf(type) < 0)
                    $scope.userFilterList.push(type);
                else {
                   index = $scope.userFilterList.indexOf(type);
                    $scope.userFilterList.splice(index , 1);
                }
            }
        };

        $scope.isActive = function(type, status){
            if ( status !== 'active' && status !== 'nonactive' && $scope.tmpStatus.length && $scope.tmpStatus.indexOf(type)> -1)
                return true;
            if (status !== 'active' && status !== 'nonactive' && status == type)
                return true;
            else {
                if($scope.currentType == 'all') {
                    if ($scope.userFilterList.indexOf(type) > -1)
                        return true;
                }
                if($scope.currentType == 'active'){
                    if ($scope.activeList.indexOf(type) >-1)
                        return true;
                }
                if($scope.currentType == 'nonactive'){
                    if ($scope.archiveList.indexOf(type) >-1)
                    return true;
                }
            }
            return false;
        }

        $scope.AllStatus = [];

        $scope.showM = function(type){
            $scope.currentType = type;
            if (type == 'empty'){
              $scope.AllStatus = [];
              $scope.activeList = SettingServices.getActiveStatusList();
              $scope.archiveList = SettingServices.getNonActiveStatusList();
              $scope.userFilterList = SettingServices.getUserFilter();
            }
            else if ($location.search().type)
              $scope.AllStatus = $scope.statusList[$scope.filteringData.issue];
            else $scope.AllStatus =  arrayUnique($scope.activeList.concat($scope.archiveList));
            $scope.tmpStatus = [];
        }

        $scope.typeClicked = false;

        NotifyingService.subscribe('editionData', function () {
            TasksService.getAll(0,2500,'created').then(function (data) {
                $scope.tasks = data.data || data;
            });

            ProjectsService.getAll(0,2500,'created').then(function (data) {
                $scope.projects = data.data || data;
            });

            DiscussionsService.getAll(0,2500,'created').then(function (data) {
                $scope.discussions = data.data || data;
            });

            OfficeDocumentsService.getAll(0,2500,'created').then(function (data) {
                $scope.documents = data.data || data;
            });

            OfficesService.getAll(0,2500,'created').then(function (data) {
                $scope.offices = data.data || data;
            });

            TemplateDocsService.getAll(0,2500,'created').then(function (data) {
                $scope.templateDocs = data.data || data;
            });

            UsersService.getAll(0,2500,'created').then(function (data) {
                $scope.people = data.data || data;
            });
            $scope.createLists();
        }, $scope);

        $scope.createLists();

        $scope.focus = false;
        $scope.changeFocus = function(){
            $scope.focus = !$scope.focus;
        };

        $scope.myFilter = function (item) {
            if(item.office.title && $scope.officesList.office.title)
            {
                return item.office.title === $scope.officesList.office.title;
            }
            else
            {
                return false;
            }

        };

        $scope.clearSearchType = function(){

            $location.search('type', null);
            $window.location.reload();
        }

        $scope.getLinkUrl = function(){
            return $state.href('main.' + $scope.activeTab.state ,{'officeDocuments':undefined});
        };

        //$scope.temp = {};
        $scope.changeOrder = function () {
            //$scope.temp.office = $scope.officesList.office;

            // if($scope.officesList.office.title != "custom"){
            //     $scope.sorting.isReverse = !$scope.sorting.isReverse;
            // }

            /*Made By OHAD - Needed for reversing sort*/
            //$state.go($state.current.name, { sort: $scope.officesList.office.title });
        };

        $scope.goToParentState = function(){

        };

        $scope.typesList = [{
            name: 'new',
             color:'ff4081'
        },{
            name:  'received',
            color:'37afef'
        },{
            name:  'in-progress',
            color:'f69679'
         },{
            name:  'sent',
            color:'359123'
        },{
            name:  'done',
            color:'757575'
        }];

        $scope.typeSelected = $state.current.params.status;

        $scope.singularItemName = {
            tasks: "task",
            projects: "project",
            discussions: "discussion",
            officeDocuments: "officeDocument",
            offices: "office",
            folders: "folder",
            watchers: "watcher"
        };

        $scope.allItems = {
            tasks: $scope.tasks,
            projects: $scope.projects,
            discussions: $scope.discussions,
            officeDocuments: $scope.officeDocuments,
            offices: $scope.offices,
            folders: $scope.folders
        };

        // Reverse list in sideline
        $scope.projectsList = $scope.projectsList.slice();
        $scope.projectsList.reverse();
        $scope.allItems.projects = $scope.allItems.projects.slice();
        $scope.allItems.projects.reverse();
        $scope.allItems.discussions = $scope.allItems.discussions.slice();
        $scope.allItems.discussions.reverse();
        $scope.discussions = $scope.discussions.slice();
        $scope.discussions.reverse();

        $scope.context = context;

        $window.onbeforeunload = function(){
            localStorage.removeItem("type");
        };

        // $scope.reset = function(main){
        //      EntityService.setEntityFolderValue(undefined, undefined);
        //     localStorage.removeItem("type");
        //     //$scope.typeSelected = null;
        // };

        $scope.changeState = function(state){
            $state.go(state, {
                //officeDocuments: undefined,
                status:undefined,
                activeTab: $scope.item
            }, {reload: true});
        };

        //$scope.typeSelected = localStorage.getItem("type");

        $scope.switchToType = function (type) {

            if(context.entityName == "folder"){
                $state.go('main.' + context.main + '.byentity', {
                    entity: context.entityName,
                    entityId: context.entityId,
                    status: type.name
                });
                $scope.typeSelected = type.name;
            }else{
                $state.go('main.' + context.main + '.all', {
                    status : type.name
                  },{reload:true});
            }

             EntityService.setActiveStatusFilterValue(type.name);
        //     OfficeDocumentsService.getAll(0,25,EntityService.getSortFilterValue().field, EntityService.getSortFilterValue().order,type.name).then(function (results) {
        //     $scope.officeDocuments=[];
        //     results.data.forEach(function(result){
        //         var flag = false;
        //         if(context.entityName='folder'){
        //             if(result.status==type.name&&result.folder&&result.folder._id==context.entityId){
        //                 flag = true;
        //             }
        //         }
        //         else{
        //             if(result.status==type.name){
        //                 flag= true;
        //             }
        //         }
        //         if(flag){
        //             $scope.officeDocuments.push(result);
        //         }

        //     });

        //  /**
        //  if (context.entityName == 'folder') {
        //      $scope.officeDocuments = $scope.officeDocuments.filter(function (officeDocument) {
        //          return officeDocument.status == type.name && officeDocument.folder && officeDocument.folder._id == context.entityId;
        //      });
        //  } else {

        //      $scope.officeDocuments = $scope.officeDocuments.filter(function (officeDocument) {
        //          return officeDocument.status == type.name;
        //      });
        //  }
        //  */

        //  localStorage.setItem("type", type.name);

        //  $state.go($state.current, {
        //      officeDocuments: $scope.officeDocuments,
        //      activeTab: $scope.item
        //  }, { reload: true });
        //     });

         /**
         var temp=[];
         $scope.officeDocuments.forEach(function(d){
             temp.push(d);
         });
         temp = temp.filter(function(officeDocument){
             if(context.entityName=='folder'){
                 return officeDocument.status == type.name &&officeDocument.folder&& officeDocument.folder._id==context.entityId ;
             }
             else{
                 return officeDocument.status == type.name;
             }
          });
           /*if(context.entityName == 'all'){
             $state.go('main.' + context.main + '.all', {},{reload: true});
         }else if (context.entityName == 'folder'){
             $state.go('main.' + context.main + '.byentity', {
                 entityId: context.entityId,
                 entity: 'folder',
                 officeDocuments: temp
             },{reload: true});
         }*/
     };
        $scope.switchTo = function (entityName, id) {
            EntityService.setEntityFolderValue(entityName, id);

                $state.go('main.' + context.main + '.byentity', {
                entity: entityName,
                entityId: id,
                status:undefined
            });

            $scope.typeSelected = '';

            // If we are switching between entities, then shrink the display limit again
            if (!$scope.visible[entityName]) {
                $scope.displayLimit.reset();
            }

        };

        $scope.switchToAll = function (entityName, id) {
            $state.go('main.' + context.main + '.all.details.activities', {
                id: id,
                entity: entityName,
            });
        };

        $scope.visible = {
            project: true,
            discussion: true,
            user: true,
            officeDocument: true,
            office: true,
            folder: true
        };

        $scope.visible[$scope.context.entityName] = true;

        $scope.GoToFolders = function() {
            $state.go('main.folders.all');
        };

        $scope.GoToOffices = function() {
            $state.go('main.offices.all');
        };

        $scope.GoToTemplateDocs = function() {
            $state.go('main.templateDocs.all');
        };

        $scope.GoToAdminSettings = function() {
            $state.go('main.adminSettings');
        }
    }

        function link($scope, $element, context) {
            $scope.showMore = function(limit, entityName) {
                if (($scope.displayLimit[entityName] + 4) >= limit) {
                    $scope.displayLimit[entityName] = limit;
                } else {
                    $scope.displayLimit[entityName]  += 4;

                }
            };

            $scope.collapse = function(entityName) {
                $scope.displayLimit[entityName] = $scope.displayLimit.default[entityName];
            };
        }

    return {
        restrict: 'A',
        templateUrl: '/icu/components/display-by/display-by.html',
        controller: controller,
        link: link
    };
});
<|endoftext|>


<commit_before>'use strict';

angular.module('mean.icu.ui.entity', []).service('EntityService', function (ApiUri, $http, PaginationService, WarningsService, ActivitiesService, MeanSocket, SettingServices) {
    var activeStatusFilterValue = "default";
    var SortFilterValue = {
                             field:"created",
                             order: 1
                          };
    var entityFolderValue = {}

    var entityTypes = ['projects', 'tasks', 'discussions', 'updates', 'offices', 'folders', 'officeDocuments'];

    function getByEntityId(type, id) {
        var entityType = entityTypes.find(function (elem) {
            return elem.includes(type);
        });

        if (entityType == null) return Promise.resolve(null);
        return $http.get(ApiUri + '/' + entityType + '/' + id).then(function (result) {
        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function isActiveStatusAvailable() {
        return SettingServices.activeStatusConfigured == null ? false : true ;
    }

    function getActiveStatusFilterValue() {
        return activeStatusFilterValue ;
    }

    function setActiveStatusFilterValue(_activeStatusFilterValue) {
        activeStatusFilterValue = _activeStatusFilterValue ;
    }

    function setSortFilterValue(_sortFilterValue) {
       SortFilterValue = _sortFilterValue
    }

    function getSortFilterValue() {
        return SortFilterValue;
    }

    function getEntityActivityStatus(filterValue, entityType, entityStatus) {
        return SettingServices.getIsActiveStatus(filterValue, entityType, entityStatus);
    }

    function setEntityFolderValue(_entity, _id) {
       entityTypes.entity = _entity;
       entityTypes.id = _id;
    }

    function getEntityFolderValue() {
        return entityTypes;
    }

    var activeToggleList = [{
        title: 'Active',
        value: 'active'
    }, {
        title: 'Archived',
        value: 'nonactive'
    }, {
        title: 'All',
        value: 'all'
    }];

    function recycle(type,id) {
        return $http.patch(ApiUri + '/' + type +  '/' + id + '/recycle').then(function (result) {

        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function recycleRestore(type,id) {
        return $http.patch(ApiUri + '/' + type +  '/' + id + '/recycle_restore').then(function (result) {

        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function getRecycleBin(type) {
    	return $http.get(ApiUri + '/' + type + '/get_recycle_bin').then(function (result) {
    		WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }
    function getSearchAll(type) {
    	return $http.get(ApiUri  + '/get_search_all').then(function (result) {
    		WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    return {
        getByEntityId: getByEntityId,
        isActiveStatusAvailable, isActiveStatusAvailable,
        getActiveStatusFilterValue: getActiveStatusFilterValue,
        setActiveStatusFilterValue: setActiveStatusFilterValue,
        getSortFilterValue: getSortFilterValue,
        setSortFilterValue: setSortFilterValue,
        activeStatusFilterValue: activeStatusFilterValue,
        getEntityActivityStatus: getEntityActivityStatus,
        activeToggleList: activeToggleList,
        setEntityFolderValue: setEntityFolderValue,
        getEntityFolderValue: getEntityFolderValue,
        recycle: recycle,
        recycleRestore: recycleRestore,
        getRecycleBin: getRecycleBin

    };
}) ;
<commit_msg>Duplicated documents inside folers fixed<commit_after>'use strict';

angular.module('mean.icu.ui.entity', []).service('EntityService', function (ApiUri, $http, $stateParams, PaginationService, WarningsService, ActivitiesService, MeanSocket, SettingServices) {
    var activeStatusFilterValue = "default";
    var SortFilterValue = {
                             field:"created",
                             order: 1
                          };
    var entityFolderValue = {}
    if($stateParams.entity == "folder"){
        entityFolderValue = $stateParams.entityId;
    }

    var entityTypes = ['projects', 'tasks', 'discussions', 'updates', 'offices', 'folders', 'officeDocuments'];

    function getByEntityId(type, id) {
        var entityType = entityTypes.find(function (elem) {
            return elem.includes(type);
        });

        if (entityType == null) return Promise.resolve(null);
        return $http.get(ApiUri + '/' + entityType + '/' + id).then(function (result) {
        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function isActiveStatusAvailable() {
        return SettingServices.activeStatusConfigured == null ? false : true ;
    }

    function getActiveStatusFilterValue() {
        return activeStatusFilterValue ;
    }

    function setActiveStatusFilterValue(_activeStatusFilterValue) {
        activeStatusFilterValue = _activeStatusFilterValue ;
    }

    function setSortFilterValue(_sortFilterValue) {
       SortFilterValue = _sortFilterValue
    }

    function getSortFilterValue() {
        return SortFilterValue;
    }

    function getEntityActivityStatus(filterValue, entityType, entityStatus) {
        return SettingServices.getIsActiveStatus(filterValue, entityType, entityStatus);
    }

    function setEntityFolderValue(_entity, _id) {
        entityFolderValue.entity = _entity;
        entityFolderValue.id = _id;
    }

    function getEntityFolderValue() {
        return entityFolderValue;
    }

    var activeToggleList = [{
        title: 'Active',
        value: 'active'
    }, {
        title: 'Archived',
        value: 'nonactive'
    }, {
        title: 'All',
        value: 'all'
    }];

    function recycle(type,id) {
        return $http.patch(ApiUri + '/' + type +  '/' + id + '/recycle').then(function (result) {

        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function recycleRestore(type,id) {
        return $http.patch(ApiUri + '/' + type +  '/' + id + '/recycle_restore').then(function (result) {

        	WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    function getRecycleBin(type) {
    	return $http.get(ApiUri + '/' + type + '/get_recycle_bin').then(function (result) {
    		WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }
    function getSearchAll(type) {
    	return $http.get(ApiUri  + '/get_search_all').then(function (result) {
    		WarningsService.setWarning(result.headers().warning);
            return result.data;
        });
    }

    return {
        getByEntityId: getByEntityId,
        isActiveStatusAvailable, isActiveStatusAvailable,
        getActiveStatusFilterValue: getActiveStatusFilterValue,
        setActiveStatusFilterValue: setActiveStatusFilterValue,
        getSortFilterValue: getSortFilterValue,
        setSortFilterValue: setSortFilterValue,
        activeStatusFilterValue: activeStatusFilterValue,
        getEntityActivityStatus: getEntityActivityStatus,
        activeToggleList: activeToggleList,
        setEntityFolderValue: setEntityFolderValue,
        getEntityFolderValue: getEntityFolderValue,
        recycle: recycle,
        recycleRestore: recycleRestore,
        getRecycleBin: getRecycleBin

    };
}) ;
<|endoftext|>