GitHub PullRequestReviewCommentEvent at 2014-05-07 10:56:46
Repository: gaia
Actor: luke-chang
Payload: {"comment":{"url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments/12370494","id":12370494,"diff_hunk":"@@ -0,0 +1,633 @@\n+'use strict';\n+/* global evt */\n+\n+(function(exports) {\n+  /**\n+   * SpatialNavigator Element\n+   *\n+   * An element that you want to let SpatialNavigator can navigate to.\n+   *\n+   * It can be a DOM element, custom element (with left/top/width/height or\n+   * x/y/w/h properties), array (with first 4 elements indicates\n+   * left/top/width/height) or any other types which you can describe its\n+   * position and size by {@link SpatialNavigatorRectCalcFunc}.\n+   *\n+   * @typedef {Object} SpatialNavigatorElement\n+   */\n+\n+  /**\n+   * SpatialNavigator Configuration\n+   *\n+   * @typedef {Object} SpatialNavigatorConfig\n+   * @property {Boolean} [crossOnly=false]\n+   *           Limit the navigating direction to vertical and horizontal only.\n+   * @property {SpatialNavigatorRectCalcFunc} [rectCalcFunc=null]\n+   *           A callback function which can describe the position and size of\n+   *           a {@link SpatialNavigatorElement}.\n+   */\n+\n+  /**\n+   * SpatialNavigator Rect Calculating Function\n+   *\n+   * A callback function which can describe the position and size of a\n+   * {@link SpatialNavigatorElement}.\n+   *\n+   * @callback SpatialNavigatorRectCalcFunc\n+   * @param {SpatialNavigatorElement} element\n+   * @return {Object} An object contains \"left/top/width/height\" properties or\n+   *                  \"null\" if you prefer using default rules of\n+   *                  SpatialNavigator.\n+   */\n+\n+  /**\n+   * SpatialNavigator is a helper for simulating spatial navigation in\n+   * javascript level.\n+   *\n+   * Spatial navigation is the ability to navigate between focusable elements\n+   * within a structured document or user interface according to the spatial\n+   * location. It uses the arrow keys to navigate on the \"2D plane\" of the\n+   * interface.\n+   *\n+   * @class SpatialNavigator\n+   * @param {Array} [collection]\n+   *        An initial set of {@link SpatialNavigatorElement} for traversal.\n+   * @param {SpatialNavigatorConfig} [options] Initial configurations.\n+   */\n+  function SpatialNavigator(collection, options) {\n+    this._collection = null;\n+    this._rectCalcFunc = null;\n+    this._focus = null;\n+\n+    options = options || {};\n+    this._crossOnly = options.crossOnly || false;\n+    this.setRectCalcFunc(options.rectCalcFunc);\n+\n+    this.reset(collection);\n+  }\n+\n+  SpatialNavigator.prototype = evt({\n+    _DEBUG: false,\n+\n+    /**\n+     * Get the \"rect\" object from a {@link SpatialNavigatorElement}.\n+     *\n+     * We will try to calculate by {@link SpatialNavigatorRectCalcFunc} first.\n+     * If that function is omitted or it returns \"null\", we will fallback to\n+     * default rules.\n+     *\n+     * @access private\n+     * @param  {SpatialNavigatorElement} elem The element you want to calculate.\n+     * @return {Object} A \"rect\" object which contains all coordinate-related\n+     *                  properties.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    _getRect: function snGetRect(elem) {\n+      var rect = null;\n+\n+      if (this._rectCalcFunc) {\n+        rect = this._rectCalcFunc(elem);\n+      }\n+\n+      if (!rect) {\n+        if (elem.getBoundingClientRect) {\n+          var cr = elem.getBoundingClientRect();\n+          rect = {\n+            left: cr.left,\n+            top: cr.top,\n+            width: cr.width,\n+            height: cr.height\n+          };\n+        } else if (elem.left || elem.x) {\n+          rect = {\n+            left: parseInt(elem.left || elem.x || 0, 10),\n+            top: parseInt(elem.top || elem.y || 0, 10),\n+            width: parseInt(elem.width || elem.w || 0, 10),\n+            height: parseInt(elem.height || elem.h || 0, 10)\n+          };\n+        } else if (Array.isArray(elem) && elem.length >= 4) {\n+          rect = {\n+            left: parseInt(elem[0], 10),\n+            top: parseInt(elem[1], 10),\n+            width: parseInt(elem[2], 10),\n+            height: parseInt(elem[3], 10)\n+          };\n+        } else {\n+          rect = {\n+            left: 0,\n+            top: 0,\n+            width: 0,\n+            height: 0\n+          };\n+        }\n+      }\n+\n+      rect.element = elem;\n+      rect.right = rect.left + rect.width;\n+      rect.bottom = rect.top + rect.height;\n+      rect.center = {\n+        x: rect.left + Math.floor(rect.width / 2),\n+        y: rect.top + Math.floor(rect.height / 2)\n+      };\n+      rect.center.left = rect.center.right = rect.center.x;\n+      rect.center.top = rect.center.bottom = rect.center.y;\n+\n+      return rect;\n+    },\n+\n+    /**\n+     * Get all \"rect\" objects from the collection.\n+     *\n+     * You can pass \"exclusive_elem\" as the first argument to ignore it from\n+     * calculating (usually pass the \"target\" element here).\n+     *\n+     * @access private\n+     * @param  {SpatialNavigatorElement} [exclusive_elem]\n+     *         The element you want to ignore from calculating.\n+     * @return {Array} An array contains all \"rect\" objects.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    _getAllRectsExcept: function snGetAllRectsExcept(exclusive_elem) {\n+      var self = this;\n+      var rects = [];\n+\n+      this._collection.forEach(function(elem) {\n+        if (!exclusive_elem || exclusive_elem !== elem) {\n+          rects.push(self._getRect(elem));\n+        }\n+      });\n+\n+      return rects;\n+    },\n+\n+    /**\n+     * Partition all \"rect\" objects into nine groups based on \"target_rect\" as\n+     * below:\n+     *\n+     * <pre>\n+     *  |---+---+---|\n+     *  | 0 | 1 | 2 |\n+     *  |---+---+---|\n+     *  | 3 | 4 | 5 |\n+     *  |---+---+---|\n+     *  | 6 | 7 | 8 |\n+     *  |---+---+---|\n+     * </pre>\n+     *\n+     * (\"target_rect\" will be located in group 4)\n+     *\n+     * @access private\n+     * @param  {Array} rects The \"rect\" objects that you want to partition.\n+     * @param  {Object} target_rects The groups will be partitioned base on this\n+     *                               \"rect\" object.\n+     * @return {Array} An array with nine elements which stand for nine groups.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    _partition: function snDemarcate(rects, target_rect) {\n+      var groups = [[], [], [], [], [], [], [], [], []];\n+\n+      rects.forEach(function(rect) {\n+        var center = rect.center;\n+        var x, y, group_id;\n+\n+        if (center.x < target_rect.left) {\n+          x = 0;\n+        } else if (center.x <= target_rect.right) {\n+          x = 1;\n+        } else {\n+          x = 2;\n+        }\n+\n+        if (center.y < target_rect.top) {\n+          y = 0;\n+        } else if (center.y <= target_rect.bottom) {\n+          y = 1;\n+        } else {\n+          y = 2;\n+        }\n+\n+        group_id = y * 3 + x;\n+        groups[group_id].push(rect);\n+      });\n+\n+      return groups;\n+    },\n+\n+    /**\n+     * A function set based on \"target_rect\" for calculating priority.\n+     *\n+     * @access private\n+     * @param  {Object} target_rect The formulas will refer to this \"rect\"\n+     *                              object.\n+     * @return {Object} A object contains each functions.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    _getDistanceFunction: function snGetDistanceFunction(target_rect) {\n+      return {\n+        nearPlumbLineIsBetter: function(rect) {\n+          var d;\n+          if (rect.center.x < target_rect.center.x) {\n+            d = target_rect.center.x - rect.right;\n+          } else {\n+            d = rect.left - target_rect.center.x;\n+          }\n+          return d < 0 ? 0 : d;\n+        },\n+        nearHorizonIsBetter: function(rect) {\n+          var d;\n+          if (rect.center.y < target_rect.center.y) {\n+            d = target_rect.center.y - rect.bottom;\n+          } else {\n+            d = rect.top - target_rect.center.y;\n+          }\n+          return d < 0 ? 0 : d;\n+        },\n+        nearTargetLeftIsBetter: function(rect) {\n+          var d;\n+          if (rect.center.x < target_rect.center.x) {\n+            d = target_rect.left - rect.right;\n+          } else {\n+            d = rect.left - target_rect.left;\n+          }\n+          return d < 0 ? 0 : d;\n+        },\n+        nearTargetTopIsBetter: function(rect) {\n+          var d;\n+          if (rect.center.y < target_rect.center.y) {\n+            d = target_rect.top - rect.bottom;\n+          } else {\n+            d = rect.top - target_rect.top;\n+          }\n+          return d < 0 ? 0 : d;\n+        },\n+        topIsBetter: function(rect) {\n+          return rect.top;\n+        },\n+        bottomIsBetter: function(rect) {\n+          return -1 * rect.bottom;\n+        },\n+        leftIsBetter: function(rect) {\n+          return rect.left;\n+        },\n+        rightIsBetter: function(rect) {\n+          return -1 * rect.right;\n+        }\n+      };\n+    },\n+\n+    /**\n+     * To pick out a \"rect\" object with highest priority.\n+     *\n+     * @access private\n+     * @param  {Array} priorities An order of priority set for traversal.\n+     * @return {Object} A \"rect\" object which has the highest priority.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    _prioritize: function snPrioritize(priorities) {\n+      var dest_group = null;\n+      var distance = [];\n+\n+      priorities.some(function(p) {\n+        if (p.group.length) {\n+          dest_group = p.group;\n+          distance = p.distance;\n+          return true;\n+        }\n+        return false;\n+      });\n+\n+      if (!dest_group) {\n+        return null;\n+      }\n+\n+      dest_group.sort(function(a, b) {\n+        for (var i = 0; i < distance.length; i++) {\n+          var d = distance[i](a) - distance[i](b);\n+          if (d) {\n+            return d;\n+          }\n+        }\n+        return 0;\n+      });\n+\n+      if (this._DEBUG) {\n+        console.log(dest_group);\n+      }\n+\n+      return dest_group[0];\n+    },\n+\n+    /**\n+     * Set {@link SpatialNavigatorRectCalcFunc} for SpatialNavigator.\n+     *\n+     * @param  {SpatialNavigatorRectCalcFunc} [rectCalcFunc=null]\n+     *         A callback function that SpatialNavigator can calculate the\n+     *         position and size of any type of elements which you provide.\n+     * @memberof SpatialNavigator.prototype\n+     */\n+    setRectCalcFunc: function snSetRectCalcFunc(rectCalcFunc) {\n+      this._rectCalcFunc = rectCalcFunc || null;\n+    },\n+\n+    /**\n+     * Set or reset the collection for traversal.","path":"dev_apps/homescreen-stingray/js/spatial_navigator.js","position":332,"original_position":332,"commit_id":"b99e3dadd85e9d919f36127b93cdfdc2cab66e0c","original_commit_id":"b99e3dadd85e9d919f36127b93cdfdc2cab66e0c","user":{"login":"luke-chang","id":4920129,"avatar_url":"https://avatars.githubusercontent.com/u/4920129?","gravatar_id":"91513261a498767bb7ecd2c38c994f5a","url":"https://api.github.com/users/luke-chang","html_url":"https://github.com/luke-chang","followers_url":"https://api.github.com/users/luke-chang/followers","following_url":"https://api.github.com/users/luke-chang/following{/other_user}","gists_url":"https://api.github.com/users/luke-chang/gists{/gist_id}","starred_url":"https://api.github.com/users/luke-chang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/luke-chang/subscriptions","organizations_url":"https://api.github.com/users/luke-chang/orgs","repos_url":"https://api.github.com/users/luke-chang/repos","events_url":"https://api.github.com/users/luke-chang/events{/privacy}","received_events_url":"https://api.github.com/users/luke-chang/received_events","type":"User","site_admin":false},"body":"Agree!","created_at":"2014-05-07T10:56:46Z","updated_at":"2014-05-07T10:56:46Z","html_url":"https://github.com/mozilla-b2g/gaia/pull/19012#discussion_r12370494","pull_request_url":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/19012","_links":{"self":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/comments/12370494"},"html":{"href":"https://github.com/mozilla-b2g/gaia/pull/19012#discussion_r12370494"},"pull_request":{"href":"https://api.github.com/repos/mozilla-b2g/gaia/pulls/19012"}}}}