diff options
Diffstat (limited to 'devtools/client/shared/components/reps/grip-map.js')
-rw-r--r-- | devtools/client/shared/components/reps/grip-map.js | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/devtools/client/shared/components/reps/grip-map.js b/devtools/client/shared/components/reps/grip-map.js new file mode 100644 index 000000000..df673d005 --- /dev/null +++ b/devtools/client/shared/components/reps/grip-map.js @@ -0,0 +1,193 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; +// Make this available to both AMD and CJS environments +define(function (require, exports, module) { + // Dependencies + const React = require("devtools/client/shared/vendor/react"); + const { createFactories, isGrip } = require("./rep-utils"); + const { Caption } = createFactories(require("./caption")); + const { PropRep } = createFactories(require("./prop-rep")); + + // Shortcuts + const { span } = React.DOM; + /** + * Renders an map. A map is represented by a list of its + * entries enclosed in curly brackets. + */ + const GripMap = React.createClass({ + displayName: "GripMap", + + propTypes: { + object: React.PropTypes.object, + mode: React.PropTypes.string, + }, + + getTitle: function (object) { + let title = object && object.class ? object.class : "Map"; + if (this.props.objectLink) { + return this.props.objectLink({ + object: object + }, title); + } + return title; + }, + + safeEntriesIterator: function (object, max) { + max = (typeof max === "undefined") ? 3 : max; + try { + return this.entriesIterator(object, max); + } catch (err) { + console.error(err); + } + return []; + }, + + entriesIterator: function (object, max) { + // Entry filter. Show only interesting entries to the user. + let isInterestingEntry = this.props.isInterestingEntry || ((type, value) => { + return ( + type == "boolean" || + type == "number" || + (type == "string" && value.length != 0) + ); + }); + + let mapEntries = object.preview && object.preview.entries + ? object.preview.entries : []; + + let indexes = this.getEntriesIndexes(mapEntries, max, isInterestingEntry); + if (indexes.length < max && indexes.length < mapEntries.length) { + // There are not enough entries yet, so we add uninteresting entries. + indexes = indexes.concat( + this.getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => { + return !isInterestingEntry(t, value, name); + }) + ); + } + + let entries = this.getEntries(mapEntries, indexes); + if (entries.length < mapEntries.length) { + // There are some undisplayed entries. Then display "more…". + let objectLink = this.props.objectLink || span; + + entries.push(Caption({ + key: "more", + object: objectLink({ + object: object + }, `${mapEntries.length - max} more…`) + })); + } + + return entries; + }, + + /** + * Get entries ordered by index. + * + * @param {Array} entries Entries array. + * @param {Array} indexes Indexes of entries. + * @return {Array} Array of PropRep. + */ + getEntries: function (entries, indexes) { + // Make indexes ordered by ascending. + indexes.sort(function (a, b) { + return a - b; + }); + + return indexes.map((index, i) => { + let [key, entryValue] = entries[index]; + let value = entryValue.value !== undefined ? entryValue.value : entryValue; + + return PropRep({ + // key, + name: key, + equal: ": ", + object: value, + // Do not add a trailing comma on the last entry + // if there won't be a "more..." item. + delim: (i < indexes.length - 1 || indexes.length < entries.length) ? ", " : "", + mode: "tiny", + objectLink: this.props.objectLink, + }); + }); + }, + + /** + * Get the indexes of entries in the map. + * + * @param {Array} entries Entries array. + * @param {Number} max The maximum length of indexes array. + * @param {Function} filter Filter the entry you want. + * @return {Array} Indexes of filtered entries in the map. + */ + getEntriesIndexes: function (entries, max, filter) { + return entries + .reduce((indexes, [key, entry], i) => { + if (indexes.length < max) { + let value = (entry && entry.value !== undefined) ? entry.value : entry; + // Type is specified in grip's "class" field and for primitive + // values use typeof. + let type = (value && value.class ? value.class : typeof value).toLowerCase(); + + if (filter(type, value, key)) { + indexes.push(i); + } + } + + return indexes; + }, []); + }, + + render: function () { + let object = this.props.object; + let props = this.safeEntriesIterator(object, + (this.props.mode == "long") ? 100 : 3); + + let objectLink = this.props.objectLink || span; + if (this.props.mode == "tiny") { + return ( + span({className: "objectBox objectBox-object"}, + this.getTitle(object), + objectLink({ + className: "objectLeftBrace", + object: object + }, "") + ) + ); + } + + return ( + span({className: "objectBox objectBox-object"}, + this.getTitle(object), + objectLink({ + className: "objectLeftBrace", + object: object + }, " { "), + props, + objectLink({ + className: "objectRightBrace", + object: object + }, " }") + ) + ); + }, + }); + + function supportsObject(grip, type) { + if (!isGrip(grip)) { + return false; + } + return (grip.preview && grip.preview.kind == "MapLike"); + } + + // Exports from this module + exports.GripMap = { + rep: GripMap, + supportsObject: supportsObject + }; +}); |