grip.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. "use strict";
  6. // Make this available to both AMD and CJS environments
  7. define(function (require, exports, module) {
  8. // ReactJS
  9. const React = require("devtools/client/shared/vendor/react");
  10. // Dependencies
  11. const { createFactories, isGrip } = require("./rep-utils");
  12. const { Caption } = createFactories(require("./caption"));
  13. const { PropRep } = createFactories(require("./prop-rep"));
  14. // Shortcuts
  15. const { span } = React.DOM;
  16. /**
  17. * Renders generic grip. Grip is client representation
  18. * of remote JS object and is used as an input object
  19. * for this rep component.
  20. */
  21. const GripRep = React.createClass({
  22. displayName: "Grip",
  23. propTypes: {
  24. object: React.PropTypes.object.isRequired,
  25. mode: React.PropTypes.string,
  26. isInterestingProp: React.PropTypes.func
  27. },
  28. getTitle: function (object) {
  29. if (this.props.objectLink) {
  30. return this.props.objectLink({
  31. object: object
  32. }, object.class);
  33. }
  34. return object.class || "Object";
  35. },
  36. safePropIterator: function (object, max) {
  37. max = (typeof max === "undefined") ? 3 : max;
  38. try {
  39. return this.propIterator(object, max);
  40. } catch (err) {
  41. console.error(err);
  42. }
  43. return [];
  44. },
  45. propIterator: function (object, max) {
  46. if (object.preview && Object.keys(object.preview).includes("wrappedValue")) {
  47. const { Rep } = createFactories(require("./rep"));
  48. return [Rep({
  49. object: object.preview.wrappedValue,
  50. mode: this.props.mode || "tiny",
  51. defaultRep: Grip,
  52. })];
  53. }
  54. // Property filter. Show only interesting properties to the user.
  55. let isInterestingProp = this.props.isInterestingProp || ((type, value) => {
  56. return (
  57. type == "boolean" ||
  58. type == "number" ||
  59. (type == "string" && value.length != 0)
  60. );
  61. });
  62. let properties = object.preview
  63. ? object.preview.ownProperties
  64. : {};
  65. let propertiesLength = object.preview && object.preview.ownPropertiesLength
  66. ? object.preview.ownPropertiesLength
  67. : object.ownPropertyLength;
  68. if (object.preview && object.preview.safeGetterValues) {
  69. properties = Object.assign({}, properties, object.preview.safeGetterValues);
  70. propertiesLength += Object.keys(object.preview.safeGetterValues).length;
  71. }
  72. let indexes = this.getPropIndexes(properties, max, isInterestingProp);
  73. if (indexes.length < max && indexes.length < propertiesLength) {
  74. // There are not enough props yet. Then add uninteresting props to display them.
  75. indexes = indexes.concat(
  76. this.getPropIndexes(properties, max - indexes.length, (t, value, name) => {
  77. return !isInterestingProp(t, value, name);
  78. })
  79. );
  80. }
  81. const truncate = Object.keys(properties).length > max;
  82. let props = this.getProps(properties, indexes, truncate);
  83. if (truncate) {
  84. // There are some undisplayed props. Then display "more...".
  85. let objectLink = this.props.objectLink || span;
  86. props.push(Caption({
  87. object: objectLink({
  88. object: object
  89. }, `${object.ownPropertyLength - max} more…`)
  90. }));
  91. }
  92. return props;
  93. },
  94. /**
  95. * Get props ordered by index.
  96. *
  97. * @param {Object} properties Props object.
  98. * @param {Array} indexes Indexes of props.
  99. * @param {Boolean} truncate true if the grip will be truncated.
  100. * @return {Array} Props.
  101. */
  102. getProps: function (properties, indexes, truncate) {
  103. let props = [];
  104. // Make indexes ordered by ascending.
  105. indexes.sort(function (a, b) {
  106. return a - b;
  107. });
  108. indexes.forEach((i) => {
  109. let name = Object.keys(properties)[i];
  110. let value = this.getPropValue(properties[name]);
  111. props.push(PropRep(Object.assign({}, this.props, {
  112. mode: "tiny",
  113. name: name,
  114. object: value,
  115. equal: ": ",
  116. delim: i !== indexes.length - 1 || truncate ? ", " : "",
  117. defaultRep: Grip
  118. })));
  119. });
  120. return props;
  121. },
  122. /**
  123. * Get the indexes of props in the object.
  124. *
  125. * @param {Object} properties Props object.
  126. * @param {Number} max The maximum length of indexes array.
  127. * @param {Function} filter Filter the props you want.
  128. * @return {Array} Indexes of interesting props in the object.
  129. */
  130. getPropIndexes: function (properties, max, filter) {
  131. let indexes = [];
  132. try {
  133. let i = 0;
  134. for (let name in properties) {
  135. if (indexes.length >= max) {
  136. return indexes;
  137. }
  138. // Type is specified in grip's "class" field and for primitive
  139. // values use typeof.
  140. let value = this.getPropValue(properties[name]);
  141. let type = (value.class || typeof value);
  142. type = type.toLowerCase();
  143. if (filter(type, value, name)) {
  144. indexes.push(i);
  145. }
  146. i++;
  147. }
  148. } catch (err) {
  149. console.error(err);
  150. }
  151. return indexes;
  152. },
  153. /**
  154. * Get the actual value of a property.
  155. *
  156. * @param {Object} property
  157. * @return {Object} Value of the property.
  158. */
  159. getPropValue: function (property) {
  160. let value = property;
  161. if (typeof property === "object") {
  162. let keys = Object.keys(property);
  163. if (keys.includes("value")) {
  164. value = property.value;
  165. } else if (keys.includes("getterValue")) {
  166. value = property.getterValue;
  167. }
  168. }
  169. return value;
  170. },
  171. render: function () {
  172. let object = this.props.object;
  173. let props = this.safePropIterator(object,
  174. (this.props.mode == "long") ? 100 : 3);
  175. let objectLink = this.props.objectLink || span;
  176. if (this.props.mode == "tiny") {
  177. return (
  178. span({className: "objectBox objectBox-object"},
  179. this.getTitle(object),
  180. objectLink({
  181. className: "objectLeftBrace",
  182. object: object
  183. }, "")
  184. )
  185. );
  186. }
  187. return (
  188. span({className: "objectBox objectBox-object"},
  189. this.getTitle(object),
  190. objectLink({
  191. className: "objectLeftBrace",
  192. object: object
  193. }, " { "),
  194. ...props,
  195. objectLink({
  196. className: "objectRightBrace",
  197. object: object
  198. }, " }")
  199. )
  200. );
  201. },
  202. });
  203. // Registration
  204. function supportsObject(object, type) {
  205. if (!isGrip(object)) {
  206. return false;
  207. }
  208. return (object.preview && object.preview.ownProperties);
  209. }
  210. let Grip = {
  211. rep: GripRep,
  212. supportsObject: supportsObject
  213. };
  214. // Exports from this module
  215. exports.Grip = Grip;
  216. });