/* -*- 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 }; });