[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/webroot/rsrc/externals/javelin/lib/ -> Vector.js (source)

   1  /**
   2   * @requires javelin-install
   3   *           javelin-event
   4   * @provides javelin-vector
   5   *
   6   * @javelin-installs JX.$V
   7   *
   8   * @javelin
   9   */
  10  
  11  
  12  /**
  13   * Convenience function that returns a @{class:JX.Vector} instance. This allows
  14   * you to concisely write things like:
  15   *
  16   *  JX.$V(x, y).add(10, 10);                // Explicit coordinates.
  17   *  JX.$V(node).add(50, 50).setDim(node);   // Position of a node.
  18   *
  19   * @param number|Node         If a node, returns the node's position vector.
  20   *                            If numeric, the x-coordinate for the new vector.
  21   * @param number?             The y-coordinate for the new vector.
  22   * @return @{class:JX.Vector} New vector.
  23   */
  24  JX.$V = function(x, y) {
  25    return new JX.Vector(x, y);
  26  };
  27  
  28  
  29  /**
  30   * Query and update positions and dimensions of nodes (and other things) within
  31   * within a document. Each vector has two elements, 'x' and 'y', which usually
  32   * represent width/height ('dimension vector') or left/top ('position vector').
  33   *
  34   * Vectors are used to manage the sizes and positions of elements, events,
  35   * the document, and the viewport (the visible section of the document, i.e.
  36   * how much of the page the user can actually see in their browser window).
  37   * Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,
  38   * 'x' and 'y'. You can read and manipulate these directly:
  39   *
  40   *   // Give the user information about elements when they click on them.
  41   *   JX.Stratcom.listen(
  42   *     'click',
  43   *     null,
  44   *     function(e) {
  45   *       var p = new JX.Vector(e);
  46   *       var d = JX.Vector.getDim(e.getTarget());
  47   *
  48   *       alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +
  49   *             'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');
  50   *     });
  51   *
  52   * You can also update positions and dimensions using vectors:
  53   *
  54   *   // When the user clicks on something, make it 10px wider and 10px taller.
  55   *   JX.Stratcom.listen(
  56   *     'click',
  57   *     null,
  58   *     function(e) {
  59   *       var target = e.getTarget();
  60   *       JX.$V(target).add(10, 10).setDim(target);
  61   *     });
  62   *
  63   * Additionally, vectors can be used to query document and viewport information:
  64   *
  65   *   var v = JX.Vector.getViewport(); // Viewport (window) width and height.
  66   *   var d = JX.Vector.getDocument(); // Document width and height.
  67   *   var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);
  68   *   alert('You can currently see ' + visible_area + ' % of the document.');
  69   *
  70   * The function @{function:JX.$V} provides convenience construction of common
  71   * vectors.
  72   *
  73   * @task query  Querying Positions and Dimensions
  74   * @task update Changing Positions and Dimensions
  75   * @task manip  Manipulating Vectors
  76   */
  77  JX.install('Vector', {
  78  
  79    /**
  80     * Construct a vector, either from explicit coordinates or from a node
  81     * or event. You can pass two Numbers to construct an explicit vector:
  82     *
  83     *   var p = new JX.Vector(35, 42);
  84     *
  85     * Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly
  86     * construct a vector:
  87     *
  88     *   var q = new JX.Vector(some_event);
  89     *   var r = new JX.Vector(some_node);
  90     *
  91     * These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or
  92     * Node.
  93     *
  94     * For convenience, @{function:JX.$V} constructs a new vector so you don't
  95     * need to use the 'new' keyword. That is, these are equivalent:
  96     *
  97     *   var s = new JX.Vector(x, y);
  98     *   var t = JX.$V(x, y);
  99     *
 100     * Methods like @{method:getScroll}, @{method:getViewport} and
 101     * @{method:getDocument} also create new vectors.
 102     *
 103     * Once you have a vector, you can manipulate it with add():
 104     *
 105     *   var u = JX.$V(35, 42);
 106     *   var v = u.add(5, -12); // v = <40, 30>
 107     *
 108     * @param wild      'x' component of the vector, or a @{class:JX.Event}, or a
 109     *                  Node.
 110     * @param Number?   If providing an 'x' component, the 'y' component of the
 111     *                  vector.
 112     * @return @{class:JX.Vector} Specified vector.
 113     * @task query
 114     */
 115    construct : function(x, y) {
 116      if (typeof y == 'undefined') {
 117        return JX.Vector.getPos(x);
 118      }
 119  
 120      this.x = (x === null) ? null : parseFloat(x);
 121      this.y = (y === null) ? null : parseFloat(y);
 122    },
 123  
 124    members : {
 125      x : null,
 126      y : null,
 127  
 128      /**
 129       * Move a node around by setting the position of a Node to the vector's
 130       * coordinates. For instance, if you want to move an element to the top left
 131       * corner of the document, you could do this (assuming it has 'position:
 132       * absolute'):
 133       *
 134       *   JX.$V(0, 0).setPos(node);
 135       *
 136       * @param Node Node to move.
 137       * @return this
 138       * @task update
 139       */
 140      setPos : function(node) {
 141        node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
 142        node.style.top  = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
 143        return this;
 144      },
 145  
 146      /**
 147       * Change the size of a node by setting its dimensions to the vector's
 148       * coordinates. For instance, if you want to change an element to be 100px
 149       * by 100px:
 150       *
 151       *   JX.$V(100, 100).setDim(node);
 152       *
 153       * Or if you want to expand a node's dimensions by 50px:
 154       *
 155       *   JX.$V(node).add(50, 50).setDim(node);
 156       *
 157       * @param Node Node to resize.
 158       * @return this
 159       * @task update
 160       */
 161      setDim : function(node) {
 162        node.style.width =
 163          (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
 164        node.style.height =
 165          (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
 166        return this;
 167      },
 168  
 169      /**
 170       * Change a vector's x and y coordinates by adding numbers to them, or
 171       * adding the coordinates of another vector. For example:
 172       *
 173       *   var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>
 174       *
 175       * You can also add another vector:
 176       *
 177       *   var q = JX.$V(777, 999);
 178       *   var r = JX.$V(1000, 2000);
 179       *   var s = q.add(r); // s = <1777, 2999>
 180       *
 181       * Note that this method returns a new vector. It does not modify the
 182       * 'this' vector.
 183       *
 184       * @param wild      Value to add to the vector's x component, or another
 185       *                  vector.
 186       * @param Number?   Value to add to the vector's y component.
 187       * @return @{class:JX.Vector} New vector, with summed components.
 188       * @task manip
 189       */
 190      add : function(x, y) {
 191        if (x instanceof JX.Vector) {
 192          y = x.y;
 193          x = x.x;
 194        }
 195        return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));
 196      }
 197    },
 198  
 199    statics : {
 200      _viewport: null,
 201  
 202      /**
 203       * Determine where in a document an element is (or where an event, like
 204       * a click, occurred) by building a new vector containing the position of a
 205       * Node or @{class:JX.Event}. The 'x' component of the vector will
 206       * correspond to the pixel offset of the argument relative to the left edge
 207       * of the document, and the 'y' component will correspond to the pixel
 208       * offset of the argument relative to the top edge of the document. Note
 209       * that all vectors are generated in document coordinates, so the scroll
 210       * position does not affect them.
 211       *
 212       * See also @{method:getDim}, used to determine an element's dimensions.
 213       *
 214       * @param  Node|@{class:JX.Event}  Node or event to determine the position
 215       *                                 of.
 216       * @return @{class:JX.Vector}      New vector with the argument's position.
 217       * @task query
 218       */
 219      getPos : function(node) {
 220        JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());
 221  
 222        if (node.getBoundingClientRect) {
 223          var rect;
 224          try {
 225            rect = node.getBoundingClientRect();
 226          } catch (e) {
 227            rect = { top : 0, left : 0 };
 228          }
 229          return new JX.Vector(
 230            rect.left + window.pageXOffset,
 231            rect.top + window.pageYOffset);
 232        }
 233  
 234        if (('pageX' in node) || ('clientX' in node)) {
 235          var c = JX.Vector._viewport;
 236          return new JX.Vector(
 237            node.pageX || (node.clientX + c.scrollLeft),
 238            node.pageY || (node.clientY + c.scrollTop)
 239          );
 240        }
 241  
 242        var x = 0;
 243        var y = 0;
 244        do {
 245          var offsetParent = node.offsetParent;
 246          var scrollLeft = 0;
 247          var scrollTop = 0;
 248          if (offsetParent && offsetParent != document.body) {
 249            scrollLeft = offsetParent.scrollLeft;
 250            scrollTop = offsetParent.scrollTop;
 251          }
 252          x += (node.offsetLeft - scrollLeft);
 253          y += (node.offsetTop - scrollTop);
 254          node = offsetParent;
 255        } while (node && node != document.body);
 256  
 257        return new JX.Vector(x, y);
 258      },
 259  
 260      /**
 261       * Determine the width and height of a node by building a new vector with
 262       * dimension information. The 'x' component of the vector will correspond
 263       * to the element's width in pixels, and the 'y' component will correspond
 264       * to its height in pixels.
 265       *
 266       * See also @{method:getPos}, used to determine an element's position.
 267       *
 268       * @param  Node      Node to determine the display size of.
 269       * @return @{JX.$V}  New vector with the node's dimensions.
 270       * @task query
 271       */
 272      getDim : function(node) {
 273        return new JX.Vector(node.offsetWidth, node.offsetHeight);
 274      },
 275  
 276      /**
 277       * Determine the current scroll position by building a new vector where
 278       * the 'x' component corresponds to how many pixels the user has scrolled
 279       * from the left edge of the document, and the 'y' component corresponds to
 280       * how many pixels the user has scrolled from the top edge of the document.
 281       *
 282       * See also @{method:getViewport}, used to determine the size of the
 283       * viewport.
 284       *
 285       * @return @{JX.$V}  New vector with the document scroll position.
 286       * @task query
 287       */
 288      getScroll : function() {
 289        // We can't use JX.Vector._viewport here because there's diversity between
 290        // browsers with respect to where position/dimension and scroll position
 291        // information is stored.
 292        var b = document.body;
 293        var e = document.documentElement;
 294        return new JX.Vector(
 295          window.pageXOffset || b.scrollLeft || e.scrollLeft,
 296          window.pageYOffset || b.scrollTop || e.scrollTop
 297        );
 298      },
 299  
 300  
 301      /**
 302       * Get the aggregate scroll offsets for a node and all of its parents.
 303       *
 304       * Note that this excludes scroll at the document level, because it does
 305       * not normally impact operations in document coordinates, which everything
 306       * on this class returns. Use @{method:getScroll} to get the document scroll
 307       * position.
 308       *
 309       * @param   Node        Node to determine offsets for.
 310       * @return  JX.Vector   New vector with aggregate scroll offsets.
 311       */
 312      getAggregateScrollForNode: function(node) {
 313        var x = 0;
 314        var y = 0;
 315  
 316        do {
 317          if (node == document.body || node == document.documentElement) {
 318            break;
 319          }
 320  
 321          x += node.scrollLeft || 0;
 322          y += node.scrollTop || 0;
 323          node = node.parentNode;
 324        } while (node);
 325  
 326        return new JX.$V(x, y);
 327      },
 328  
 329      /**
 330       * Determine the size of the viewport (basically, the browser window) by
 331       * building a new vector where the 'x' component corresponds to the width
 332       * of the viewport in pixels and the 'y' component corresponds to the height
 333       * of the viewport in pixels.
 334       *
 335       * See also @{method:getScroll}, used to determine the position of the
 336       * viewport, and @{method:getDocument}, used to determine the size of the
 337       * entire document.
 338       *
 339       * @return @{class:JX.Vector}  New vector with the viewport dimensions.
 340       * @task query
 341       */
 342      getViewport : function() {
 343        var c = JX.Vector._viewport;
 344        return new JX.Vector(
 345          window.innerWidth || c.clientWidth || 0,
 346          window.innerHeight || c.clientHeight || 0
 347        );
 348      },
 349  
 350      /**
 351       * Determine the size of the document, including any area outside the
 352       * current viewport which the user would need to scroll in order to see, by
 353       * building a new vector where the 'x' component corresponds to the document
 354       * width in pixels and the 'y' component corresponds to the document height
 355       * in pixels.
 356       *
 357       * @return @{class:JX.Vector} New vector with the document dimensions.
 358       * @task query
 359       */
 360      getDocument : function() {
 361        var c = JX.Vector._viewport;
 362        return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);
 363      }
 364    },
 365  
 366    /**
 367     * On initialization, the browser-dependent viewport root is determined and
 368     * stored.
 369     *
 370     * In ##__DEV__##, @{class:JX.Vector} installs a toString() method so
 371     * vectors print in a debuggable way:
 372     *
 373     *   <23, 92>
 374     *
 375     * This string representation of vectors is not available in a production
 376     * context.
 377     *
 378     * @return void
 379     */
 380    initialize : function() {
 381      JX.Vector._viewport = document.documentElement || document.body;
 382  
 383      if (__DEV__) {
 384        JX.Vector.prototype.toString = function() {
 385          return '<' + this.x + ', ' + this.y + '>';
 386        };
 387      }
 388    }
 389  
 390  });


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1