{"action":"created","comment":{"url":"https://api.github.com/repos/aframevr/aframe/pulls/comments/58264480","id":58264480,"diff_hunk":"@@ -1,112 +1,138 @@\n var registerComponent = require('../core/component').registerComponent;\n-var requestInterval = require('request-interval');\n var THREE = require('../lib/three');\n \n+var scaleDummy = new THREE.Vector3();\n+\n+/**\n+ * Raycaster component.\n+ *\n+ * Pass options to three.js Raycaster including which objects to test.\n+ * Poll for intersections.\n+ * Emit event on origin entity and on target entity on intersect.\n+ *\n+ * @member {array} intersectedEls - List of currently intersected entities.\n+ * @member {array} objects - Cached list of meshes to intersect.\n+ * @member {number} prevCheckTime - Previous time intersection was checked. To help interval.\n+ * @member {object} raycaster - three.js Raycaster.\n+ */\n module.exports.Component = registerComponent('raycaster', {\n+  schema: {\n+    far: {default: Infinity}, // Infinity.\n+    interval: {default: 100},\n+    near: {default: 0},\n+    objects: {default: ''},\n+    recursive: {default: true}\n+  },\n+\n   init: function () {\n+    this.direction = new THREE.Vector3();\n+    this.directionHelper = new THREE.Quaternion();\n+    this.intersectedEls = [];\n+    this.objects = null;\n+    this.originVec3 = new THREE.Vector3();\n+    this.prevCheckTime = null;\n     this.raycaster = new THREE.Raycaster();\n-    this.intersectedEl = null;\n+    this.updateOriginDirection();\n   },\n \n-  play: function () {\n-    this.pollForHoverIntersections();\n-  },\n+  /**\n+   * Create or update raycaster object.\n+   */\n+  update: function () {\n+    var data = this.data;\n+    var raycaster = this.raycaster;\n \n-  pause: function () {\n-    var pollInterval = this.pollInterval;\n-    if (!pollInterval) { return; }\n-    requestInterval.clear(this.pollInterval);\n-  },\n+    // Set raycaster properties.\n+    raycaster.far = data.far;\n+    raycaster.near = data.near;\n \n-  remove: function () {\n-    this.pause();\n+    this.refreshObjects();\n   },\n \n-  pollForHoverIntersections: function () {\n-    this.pollInterval = requestInterval(100, this.getIntersections.bind(this));\n-  },\n+  /**\n+   * Update list of objects to test for intersection.\n+   */\n+  refreshObjects: function () {\n+    var data = this.data;\n+    var i;\n+    var objectEls;\n \n-  getIntersections: function () {\n-    var closest = this.getClosestIntersected();\n-    if (closest) {\n-      this.handleIntersection(closest);\n+    // Push meshes onto list of objects to intersect.\n+    if (data.objects) {\n+      objectEls = this.el.closest('a-scene').querySelectorAll(data.objects);\n+      this.objects = [];\n+      for (i = 0; i < objectEls.length; i++) {\n+        this.objects.push(objectEls[i].object3D);\n+      }\n       return;\n     }\n-    // If we have no intersections other than the cursor itself,\n-    // but we still have a previously intersected element, clear it.\n-    if (this.intersectedEl) {\n-      this.clearExistingIntersection();\n-    }\n-  },\n \n-  intersect: function (objects) {\n-    var el = this.el;\n-    var raycaster = this.raycaster;\n-    var cursor = el.object3D;\n-    var parent = el.parentNode.object3D;\n-    var originPosition = new THREE.Vector3().setFromMatrixPosition(parent.matrixWorld);\n-    var cursorPosition = new THREE.Vector3().setFromMatrixPosition(cursor.matrixWorld);\n-    var direction = cursorPosition.sub(originPosition).normalize();\n-    raycaster.set(originPosition, direction);\n-    return raycaster.intersectObjects(objects, true);\n+    // If objects not defined, intersect with everything.\n+    this.objects = this.el.sceneEl.object3D.children;\n   },\n \n   /**\n-   * Returns the closest intersected object.\n-   *\n-   * @returns {Object|null}\n-   *   The closest intersected element that is not the cursor itself,\n-   *   an invisible element, or not a a-frame entity element.\n-   *   If no objects are intersected, `null` is returned.\n+   * Check for intersections and cleared intersections on an interval.\n    */\n-  getClosestIntersected: function () {\n-    var scene = this.el.sceneEl.object3D;\n-    var cursorEl = this.el;\n-    var intersectedObj;\n-    var intersectedObjs = this.intersect(scene.children);\n-    for (var i = 0; i < intersectedObjs.length; ++i) {\n-      intersectedObj = intersectedObjs[i];\n-\n-      while (intersectedObj.object.parent && intersectedObj.object.el === undefined) {\n-        intersectedObj.object = intersectedObj.object.parent;\n-      }\n+  tick: function (time) {\n+    var el = this.el;\n+    var data = this.data;\n+    var prevIntersectedEls = this.intersectedEls.slice();\n+    var intersectedEls = this.intersectedEls = [];  // Reset intersectedEls.\n+    var intersections;\n+    var prevCheckTime = this.prevCheckTime;\n+\n+    // Only check for intersection if interval time has passed.\n+    if (prevCheckTime && (time - prevCheckTime < data.interval)) { return; }\n+\n+    // Raycast.\n+    this.updateOriginDirection();\n+    intersections = this.raycaster.intersectObjects(this.objects, data.recursive);\n+\n+    // Update intersectedEls object first in case event handlers try to inspect it.\n+    intersections.forEach(function emitEvents (intersection) {\n+      intersectedEls.push(intersection.object.el);\n+    });\n \n-      // If the intersected object is the cursor itself\n-      // or the object is further than the max distance\n+    // Emit intersected on intersected entity per intersected entity.\n+    intersections.forEach(function emitEvents (intersection) {\n+      var intersectedEl = intersection.object.el;\n+      intersectedEl.emit('raycaster-intersected', {el: el, intersection: intersection});\n+    });\n \n-      if (intersectedObj.object.el === undefined) { continue; }\n-      if (intersectedObj.object.el === cursorEl) { continue; }\n-      if (!intersectedObj.object.visible) { continue; }\n-      return intersectedObj;\n+    // Emit all intersections at once on raycasting entity.\n+    if (intersections.length) {\n+      el.emit('raycaster-intersection', {\n+        els: intersections.map(function getEl (intersection) {\n+          return intersection.object.el;\n+        }),\n+        intersections: intersections\n+      });\n     }\n-    return null;\n-  },\n \n-  /**\n-   * Remembers the last intersected element\n-   */\n-  setExistingIntersection: function (el, distance) {\n-    this.intersectedEl = el;\n-    this.el.emit('intersection', { el: el, distance: distance });\n+    // Emit intersection cleared on both entities per formerly intersected entity.\n+    prevIntersectedEls.forEach(function checkStillIntersected (intersectedEl) {\n+      if (intersectedEls.indexOf(intersectedEl) !== -1) { return; }\n+      el.emit('raycaster-intersection-cleared', {el: intersectedEl});\n+      intersectedEl.emit('raycaster-intersected-cleared', {el: el});\n+    });\n   },\n \n   /**\n-   * Emits a `mouseleave` event and clears info about the last intersection.\n+   * Set origin and direction of raycaster using entity position and rotation.\n    */\n-  clearExistingIntersection: function () {\n-    var intersectedEl = this.intersectedEl;\n-    this.el.emit('intersectioncleared', { el: intersectedEl });\n-    this.intersectedEl = null;\n-  },\n+  updateOriginDirection: function () {\n+    var el = this.el;\n+    var object3D = el.object3D;\n \n-  handleIntersection: function (obj) {\n-    var el = obj.object.el;\n+    // Update matrix world.\n+    object3D.updateMatrixWorld();\n+    // Grab the position and rotation.\n+    object3D.matrixWorld.decompose(this.originVec3, this.directionHelper, scaleDummy);","path":"src/components/raycaster.js","position":205,"original_position":205,"commit_id":"095fe6247cc41bed7220363e7e07562235d370f2","original_commit_id":"095fe6247cc41bed7220363e7e07562235d370f2","user":{"login":"ngokevin","id":674727,"avatar_url":"https://avatars.githubusercontent.com/u/674727?v=3","gravatar_id":"","url":"https://api.github.com/users/ngokevin","html_url":"https://github.com/ngokevin","followers_url":"https://api.github.com/users/ngokevin/followers","following_url":"https://api.github.com/users/ngokevin/following{/other_user}","gists_url":"https://api.github.com/users/ngokevin/gists{/gist_id}","starred_url":"https://api.github.com/users/ngokevin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ngokevin/subscriptions","organizations_url":"https://api.github.com/users/ngokevin/orgs","repos_url":"https://api.github.com/users/ngokevin/repos","events_url":"https://api.github.com/users/ngokevin/events{/privacy}","received_events_url":"https://api.github.com/users/ngokevin/received_events","type":"User","site_admin":false},"body":"You could have many raycasters in the scene, and there's a likely chance they'll be overwriting each other's vectors and cause issues. `look-controls` is fine since you usually only have one of them.","created_at":"2016-04-01T20:41:01Z","updated_at":"2016-04-01T20:41:01Z","html_url":"https://github.com/aframevr/aframe/pull/1196#discussion_r58264480","pull_request_url":"https://api.github.com/repos/aframevr/aframe/pulls/1196","_links":{"self":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/comments/58264480"},"html":{"href":"https://github.com/aframevr/aframe/pull/1196#discussion_r58264480"},"pull_request":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/1196"}}},"pull_request":{"url":"https://api.github.com/repos/aframevr/aframe/pulls/1196","id":63460360,"html_url":"https://github.com/aframevr/aframe/pull/1196","diff_url":"https://github.com/aframevr/aframe/pull/1196.diff","patch_url":"https://github.com/aframevr/aframe/pull/1196.patch","issue_url":"https://api.github.com/repos/aframevr/aframe/issues/1196","number":1196,"state":"open","locked":false,"title":"improve raycaster component (fixes #829)","user":{"login":"ngokevin","id":674727,"avatar_url":"https://avatars.githubusercontent.com/u/674727?v=3","gravatar_id":"","url":"https://api.github.com/users/ngokevin","html_url":"https://github.com/ngokevin","followers_url":"https://api.github.com/users/ngokevin/followers","following_url":"https://api.github.com/users/ngokevin/following{/other_user}","gists_url":"https://api.github.com/users/ngokevin/gists{/gist_id}","starred_url":"https://api.github.com/users/ngokevin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ngokevin/subscriptions","organizations_url":"https://api.github.com/users/ngokevin/orgs","repos_url":"https://api.github.com/users/ngokevin/repos","events_url":"https://api.github.com/users/ngokevin/events{/privacy}","received_events_url":"https://api.github.com/users/ngokevin/received_events","type":"User","site_admin":false},"body":"**Description:** Improve overall cursor + raycaster components with respect to performance, configuration, and code quality. This is a stepping stone for more complex cursor/interactions later on.\r\n\r\n**[DEMO](http://ngokevin.github.io/aframe/examples/test-raycaster/)**\r\n\r\n**Changes proposed:**\r\n- Don't use requestInterval which adds an additional requestAnimationFrame call. Work off of the existing tick handler.\r\n- Generalize raycaster component for general use rather than just the cursor. Allow specifying the near, far, recursive. Thus decoupling raycaster and cursor, and having cursor be an abstraction of the raycaster.\r\n- Raycaster origin/direction can be set with position/rotation of entities\r\n- Allow passing in a list of entities (via selector) that the raycaster should intersect rather than intersecting everything\r\n- Allow passing in a list of entities to ignore\r\n- Return full intersection data in events\r\n- Move intersectioncleared/mouseleave logic to the cursor component to remove cursor/raycaster coupling\r\n- Be able to update the raycaster \r\n- Prefix event/state names with respective component (```cursor-```/```raycaster-```)\r\n- Removed `cursor.maxDistance` as it was redundant with `raycaster.far` \r\n- Fully unit test the raycaster and cursor components","created_at":"2016-03-19T06:06:29Z","updated_at":"2016-04-01T20:41:01Z","closed_at":null,"merged_at":null,"merge_commit_sha":"723491e7bee6c16b2d5aee596e2488b418c2e4dc","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/aframevr/aframe/pulls/1196/commits","review_comments_url":"https://api.github.com/repos/aframevr/aframe/pulls/1196/comments","review_comment_url":"https://api.github.com/repos/aframevr/aframe/pulls/comments{/number}","comments_url":"https://api.github.com/repos/aframevr/aframe/issues/1196/comments","statuses_url":"https://api.github.com/repos/aframevr/aframe/statuses/095fe6247cc41bed7220363e7e07562235d370f2","head":{"label":"ngokevin:raycaster","ref":"raycaster","sha":"095fe6247cc41bed7220363e7e07562235d370f2","user":{"login":"ngokevin","id":674727,"avatar_url":"https://avatars.githubusercontent.com/u/674727?v=3","gravatar_id":"","url":"https://api.github.com/users/ngokevin","html_url":"https://github.com/ngokevin","followers_url":"https://api.github.com/users/ngokevin/followers","following_url":"https://api.github.com/users/ngokevin/following{/other_user}","gists_url":"https://api.github.com/users/ngokevin/gists{/gist_id}","starred_url":"https://api.github.com/users/ngokevin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ngokevin/subscriptions","organizations_url":"https://api.github.com/users/ngokevin/orgs","repos_url":"https://api.github.com/users/ngokevin/repos","events_url":"https://api.github.com/users/ngokevin/events{/privacy}","received_events_url":"https://api.github.com/users/ngokevin/received_events","type":"User","site_admin":false},"repo":{"id":48299418,"name":"aframe","full_name":"ngokevin/aframe","owner":{"login":"ngokevin","id":674727,"avatar_url":"https://avatars.githubusercontent.com/u/674727?v=3","gravatar_id":"","url":"https://api.github.com/users/ngokevin","html_url":"https://github.com/ngokevin","followers_url":"https://api.github.com/users/ngokevin/followers","following_url":"https://api.github.com/users/ngokevin/following{/other_user}","gists_url":"https://api.github.com/users/ngokevin/gists{/gist_id}","starred_url":"https://api.github.com/users/ngokevin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ngokevin/subscriptions","organizations_url":"https://api.github.com/users/ngokevin/orgs","repos_url":"https://api.github.com/users/ngokevin/repos","events_url":"https://api.github.com/users/ngokevin/events{/privacy}","received_events_url":"https://api.github.com/users/ngokevin/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ngokevin/aframe","description":"Building Blocks for the VR Web","fork":true,"url":"https://api.github.com/repos/ngokevin/aframe","forks_url":"https://api.github.com/repos/ngokevin/aframe/forks","keys_url":"https://api.github.com/repos/ngokevin/aframe/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ngokevin/aframe/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ngokevin/aframe/teams","hooks_url":"https://api.github.com/repos/ngokevin/aframe/hooks","issue_events_url":"https://api.github.com/repos/ngokevin/aframe/issues/events{/number}","events_url":"https://api.github.com/repos/ngokevin/aframe/events","assignees_url":"https://api.github.com/repos/ngokevin/aframe/assignees{/user}","branches_url":"https://api.github.com/repos/ngokevin/aframe/branches{/branch}","tags_url":"https://api.github.com/repos/ngokevin/aframe/tags","blobs_url":"https://api.github.com/repos/ngokevin/aframe/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ngokevin/aframe/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ngokevin/aframe/git/refs{/sha}","trees_url":"https://api.github.com/repos/ngokevin/aframe/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ngokevin/aframe/statuses/{sha}","languages_url":"https://api.github.com/repos/ngokevin/aframe/languages","stargazers_url":"https://api.github.com/repos/ngokevin/aframe/stargazers","contributors_url":"https://api.github.com/repos/ngokevin/aframe/contributors","subscribers_url":"https://api.github.com/repos/ngokevin/aframe/subscribers","subscription_url":"https://api.github.com/repos/ngokevin/aframe/subscription","commits_url":"https://api.github.com/repos/ngokevin/aframe/commits{/sha}","git_commits_url":"https://api.github.com/repos/ngokevin/aframe/git/commits{/sha}","comments_url":"https://api.github.com/repos/ngokevin/aframe/comments{/number}","issue_comment_url":"https://api.github.com/repos/ngokevin/aframe/issues/comments{/number}","contents_url":"https://api.github.com/repos/ngokevin/aframe/contents/{+path}","compare_url":"https://api.github.com/repos/ngokevin/aframe/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ngokevin/aframe/merges","archive_url":"https://api.github.com/repos/ngokevin/aframe/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ngokevin/aframe/downloads","issues_url":"https://api.github.com/repos/ngokevin/aframe/issues{/number}","pulls_url":"https://api.github.com/repos/ngokevin/aframe/pulls{/number}","milestones_url":"https://api.github.com/repos/ngokevin/aframe/milestones{/number}","notifications_url":"https://api.github.com/repos/ngokevin/aframe/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ngokevin/aframe/labels{/name}","releases_url":"https://api.github.com/repos/ngokevin/aframe/releases{/id}","deployments_url":"https://api.github.com/repos/ngokevin/aframe/deployments","created_at":"2015-12-19T22:31:05Z","updated_at":"2016-03-29T22:44:31Z","pushed_at":"2016-03-31T23:38:34Z","git_url":"git://github.com/ngokevin/aframe.git","ssh_url":"git@github.com:ngokevin/aframe.git","clone_url":"https://github.com/ngokevin/aframe.git","svn_url":"https://github.com/ngokevin/aframe","homepage":"https://aframe.io/","size":97419,"stargazers_count":1,"watchers_count":1,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"aframevr:master","ref":"master","sha":"86b6580f48e714919b0b3f2acfa7e43810dd448c","user":{"login":"aframevr","id":16024092,"avatar_url":"https://avatars.githubusercontent.com/u/16024092?v=3","gravatar_id":"","url":"https://api.github.com/users/aframevr","html_url":"https://github.com/aframevr","followers_url":"https://api.github.com/users/aframevr/followers","following_url":"https://api.github.com/users/aframevr/following{/other_user}","gists_url":"https://api.github.com/users/aframevr/gists{/gist_id}","starred_url":"https://api.github.com/users/aframevr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aframevr/subscriptions","organizations_url":"https://api.github.com/users/aframevr/orgs","repos_url":"https://api.github.com/users/aframevr/repos","events_url":"https://api.github.com/users/aframevr/events{/privacy}","received_events_url":"https://api.github.com/users/aframevr/received_events","type":"Organization","site_admin":false},"repo":{"id":42488903,"name":"aframe","full_name":"aframevr/aframe","owner":{"login":"aframevr","id":16024092,"avatar_url":"https://avatars.githubusercontent.com/u/16024092?v=3","gravatar_id":"","url":"https://api.github.com/users/aframevr","html_url":"https://github.com/aframevr","followers_url":"https://api.github.com/users/aframevr/followers","following_url":"https://api.github.com/users/aframevr/following{/other_user}","gists_url":"https://api.github.com/users/aframevr/gists{/gist_id}","starred_url":"https://api.github.com/users/aframevr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aframevr/subscriptions","organizations_url":"https://api.github.com/users/aframevr/orgs","repos_url":"https://api.github.com/users/aframevr/repos","events_url":"https://api.github.com/users/aframevr/events{/privacy}","received_events_url":"https://api.github.com/users/aframevr/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/aframevr/aframe","description":"Building Blocks for the VR Web","fork":false,"url":"https://api.github.com/repos/aframevr/aframe","forks_url":"https://api.github.com/repos/aframevr/aframe/forks","keys_url":"https://api.github.com/repos/aframevr/aframe/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aframevr/aframe/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aframevr/aframe/teams","hooks_url":"https://api.github.com/repos/aframevr/aframe/hooks","issue_events_url":"https://api.github.com/repos/aframevr/aframe/issues/events{/number}","events_url":"https://api.github.com/repos/aframevr/aframe/events","assignees_url":"https://api.github.com/repos/aframevr/aframe/assignees{/user}","branches_url":"https://api.github.com/repos/aframevr/aframe/branches{/branch}","tags_url":"https://api.github.com/repos/aframevr/aframe/tags","blobs_url":"https://api.github.com/repos/aframevr/aframe/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aframevr/aframe/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aframevr/aframe/git/refs{/sha}","trees_url":"https://api.github.com/repos/aframevr/aframe/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aframevr/aframe/statuses/{sha}","languages_url":"https://api.github.com/repos/aframevr/aframe/languages","stargazers_url":"https://api.github.com/repos/aframevr/aframe/stargazers","contributors_url":"https://api.github.com/repos/aframevr/aframe/contributors","subscribers_url":"https://api.github.com/repos/aframevr/aframe/subscribers","subscription_url":"https://api.github.com/repos/aframevr/aframe/subscription","commits_url":"https://api.github.com/repos/aframevr/aframe/commits{/sha}","git_commits_url":"https://api.github.com/repos/aframevr/aframe/git/commits{/sha}","comments_url":"https://api.github.com/repos/aframevr/aframe/comments{/number}","issue_comment_url":"https://api.github.com/repos/aframevr/aframe/issues/comments{/number}","contents_url":"https://api.github.com/repos/aframevr/aframe/contents/{+path}","compare_url":"https://api.github.com/repos/aframevr/aframe/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aframevr/aframe/merges","archive_url":"https://api.github.com/repos/aframevr/aframe/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aframevr/aframe/downloads","issues_url":"https://api.github.com/repos/aframevr/aframe/issues{/number}","pulls_url":"https://api.github.com/repos/aframevr/aframe/pulls{/number}","milestones_url":"https://api.github.com/repos/aframevr/aframe/milestones{/number}","notifications_url":"https://api.github.com/repos/aframevr/aframe/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aframevr/aframe/labels{/name}","releases_url":"https://api.github.com/repos/aframevr/aframe/releases{/id}","deployments_url":"https://api.github.com/repos/aframevr/aframe/deployments","created_at":"2015-09-15T02:04:40Z","updated_at":"2016-04-01T19:40:32Z","pushed_at":"2016-04-01T20:07:54Z","git_url":"git://github.com/aframevr/aframe.git","ssh_url":"git@github.com:aframevr/aframe.git","clone_url":"https://github.com/aframevr/aframe.git","svn_url":"https://github.com/aframevr/aframe","homepage":"https://aframe.io/","size":104782,"stargazers_count":1600,"watchers_count":1600,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":221,"mirror_url":null,"open_issues_count":204,"forks":221,"open_issues":204,"watchers":1600,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/1196"},"html":{"href":"https://github.com/aframevr/aframe/pull/1196"},"issue":{"href":"https://api.github.com/repos/aframevr/aframe/issues/1196"},"comments":{"href":"https://api.github.com/repos/aframevr/aframe/issues/1196/comments"},"review_comments":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/1196/comments"},"review_comment":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/aframevr/aframe/pulls/1196/commits"},"statuses":{"href":"https://api.github.com/repos/aframevr/aframe/statuses/095fe6247cc41bed7220363e7e07562235d370f2"}}}}