A `category` property for the Attribute class?

The osascript (AppleScript and JavaScript) interface to Tinderbox 8 is already excellent, and must have taken an enormous amount of work to develop and test.

If any further refinement of it were considered, a category property for the Attribute class would be my main request.

It would be very useful for scripts of various kinds to let the user pick one or more attributes from a named subset, as in the Inspector categorisation which we can use in the GUI:

a category property on attributes would make it very easy and fast to do this.

2 Likes

In the meanwhile, a JSON dictionary containing two sub-dictionaries:

  • Categories: Grouped lists of attributes, each keyed by the name of the category label used in the Tinderbox 8 inspector.
  • Attributes: Key-value pairs: attribute : category.

The former should be useful (particularly in JS scripts, though potentially also in AS) which need to put up menus of attribute categories, leading to shorter lists of attributes.

The latter should be useful for checking which category a given attribute belongs to.

The JSON below could either be directly embedded in a script, or read from a json file at run-time, and parsed with the standard JSON.parse() function.

{
  "Categories": {
    "AI": [
      "NLNames",
      "NLOrganizations",
      "NLPlaces"
    ],
    "Agent": [
      "AgentAction",
      "AgentCaseSensitive",
      "AgentPriority",
      "AgentQuery",
      "CleanupAction"
    ],
    "Appearance": [
      "AccentColor",
      "Badge",
      "BadgeMonochrome",
      "BadgeSize",
      "Border",
      "BorderBevel",
      "BorderColor",
      "BorderDash",
      "CaptionAlignment",
      "CaptionColor",
      "CaptionFont",
      "CaptionOpacity",
      "CaptionSize",
      "Color",
      "Color2",
      "Flags",
      "Opacity",
      "Pattern",
      "PlotColor",
      "Shadow",
      "ShadowBlur",
      "ShadowColor",
      "ShadowDistance",
      "Shape"
    ],
    "Composites": [
      "Associates",
      "IsComposite",
      "IsMultiple",
      "NeverComposite",
      "OnJoin",
      "Role"
    ],
    "Events": [
      "DueDate",
      "EndDate",
      "StartDate",
      "TimelineAliases",
      "TimelineBand",
      "TimelineBandLabelColor",
      "TimelineBandLabelOpacity",
      "TimelineBandLabels",
      "TimelineColor",
      "TimelineDescendants",
      "TimelineEnd",
      "TimelineEndAttribute",
      "TimelineGridColor",
      "TimelineMarker",
      "TimelineScaleColor",
      "TimelineScaleColor2",
      "TimelineStart",
      "TimelineStartAttribute"
    ],
    "General": [
      "AdornmentCount",
      "Aliases",
      "Caption",
      "Checked",
      "ChildCount",
      "ClusterTerms",
      "Container",
      "Created",
      "Creator",
      "DescendantCount",
      "DisplayExpression",
      "DisplayExpressionDisabled",
      "DisplayName",
      "Edict",
      "EdictDisabled",
      "File",
      "HoverExpression",
      "HoverImage",
      "HoverOpacity",
      "ID",
      "InboundLinkCount",
      "IsAdornment",
      "IsAlias",
      "IsPrototype",
      "Modified",
      "Name",
      "OnAdd",
      "OnRemove",
      "OutboundLinkCount",
      "OutlineDepth",
      "OutlineOrder",
      "Path",
      "PlainLinkCount",
      "Private",
      "Prototype",
      "PrototypeBequeathsChildren",
      "ReadCount",
      "Rule",
      "RuleDisabled",
      "Searchable",
      "SelectionCount",
      "SiblingOrder",
      "Subtitle",
      "TextLinkCount",
      "User",
      "WebLinkCount"
    ],
    "Grid": [
      "GridColor",
      "GridColumns",
      "GridLabelFont",
      "GridLabelSize",
      "GridLabels",
      "GridOpacity",
      "GridRows"
    ],
    "HTML": [
      "HTMLBoldEnd",
      "HTMLBoldStart",
      "HTMLCloud1End",
      "HTMLCloud1Start",
      "HTMLCloud2End",
      "HTMLCloud2Start",
      "HTMLCloud3End",
      "HTMLCloud3Start",
      "HTMLCloud4End",
      "HTMLCloud4Start",
      "HTMLCloud5End",
      "HTMLCloud5Start",
      "HTMLDontExport",
      "HTMLEntities",
      "HTMLExportAfter",
      "HTMLExportBefore",
      "HTMLExportChildren",
      "HTMLExportCommand",
      "HTMLExportExtension",
      "HTMLExportFileName",
      "HTMLExportFileNameSpacer",
      "HTMLExportPath",
      "HTMLExportTemplate",
      "HTMLFileNameLowerCase",
      "HTMLFileNameMaxLength",
      "HTMLFirstParagraphEnd",
      "HTMLFirstParagraphStart",
      "HTMLFont",
      "HTMLFontSize",
      "HTMLImageEnd",
      "HTMLImageStart",
      "HTMLIndentedParagraphEnd",
      "HTMLIndentedParagraphStart",
      "HTMLItalicEnd",
      "HTMLItalicStart",
      "HTMLLinkExtension",
      "HTMLListEnd",
      "HTMLListItemEnd",
      "HTMLListItemStart",
      "HTMLListStart",
      "HTMLMarkDown",
      "HTMLMarkupText",
      "HTMLOrderedListEnd",
      "HTMLOrderedListItemStart",
      "HTMLOrderedListStart",
      "HTMLOrderedsListItemEnd",
      "HTMLOverwriteImages",
      "HTMLParagraphEnd",
      "HTMLParagraphStart",
      "HTMLPreviewCommand",
      "HTMLQuoteHTML",
      "HTMLStrikeEnd",
      "HTMLStrikeStart",
      "HTMLUnderlineEnd",
      "HTMLUnderlineStart",
      "IsTemplate"
    ],
    "Iris": [
      "IrisAngle",
      "IrisRadius"
    ],
    "Map": [
      "AdornmentFont",
      "Base",
      "Bend",
      "Direction",
      "Fill",
      "FillOpacity",
      "Height",
      "HoverFont",
      "InteriorScale",
      "LeafBase",
      "LeafBend",
      "LeafDirection",
      "LeafTip",
      "Lock",
      "MapBackgroundAccentColor",
      "MapBackgroundColor",
      "MapBackgroundColor2",
      "MapBackgroundFill",
      "MapBackgroundFillOpacity",
      "MapBackgroundPattern",
      "MapBackgroundShadow",
      "MapBodyTextColor",
      "MapBodyTextSize",
      "MapScrollX",
      "MapScrollY",
      "MapTextSize",
      "NameAlignment",
      "NameBold",
      "NameColor",
      "NameFont",
      "NameLeading",
      "NameStrike",
      "PlotBackgroundColor",
      "PlotBackgroundOpacity",
      "PlotColorList",
      "Sticky",
      "SubtitleColor",
      "SubtitleOpacity",
      "SubtitleSize",
      "TableExpression",
      "TableHeading",
      "Tip",
      "TitleHeight",
      "TitleOpacity",
      "Width",
      "Xpos",
      "Ypos"
    ],
    "Net": [
      "AutoFetch",
      "AutoFetchCommand",
      "LastFetched",
      "NoteURL",
      "RSSChannelTemplate",
      "RSSItemLimit",
      "RSSItemTemplate",
      "RawData",
      "URL",
      "ViewInBrowser"
    ],
    "Outline": [
      "OutlineBackgroundColor",
      "OutlineColorSwatch",
      "OutlineTextSize",
      "PrototypeHighlightColor",
      "Separator"
    ],
    "People": [
      "AIM",
      "Address",
      "City",
      "Country",
      "District",
      "Email",
      "FormattedAddress",
      "FullName",
      "GeocodedAddress",
      "Latitude",
      "Longitude",
      "Organization",
      "PostalCode",
      "State",
      "Telephone",
      "Twitter"
    ],
    "References": [
      "Abstract",
      "AccessDate",
      "ArticleTitle",
      "Author2",
      "Author3",
      "Author4",
      "Authors",
      "BookTitle",
      "CallNumber",
      "DOI",
      "Edition",
      "ISBN",
      "Issue",
      "Journal",
      "Pages",
      "PublicationCity",
      "PublicationYear",
      "Publisher",
      "RefFormat",
      "RefKeywords",
      "RefType",
      "ReferenceRIS",
      "ReferenceTitle",
      "ReferenceURL",
      "SourceCreated",
      "SourceModified",
      "Tags",
      "Volume"
    ],
    "Sandbox": [
      "MyBoolean",
      "MyColor",
      "MyDate",
      "MyInterval",
      "MyList",
      "MyNumber",
      "MySet",
      "MyString"
    ],
    "Scrivener": [
      "ScrivenerID",
      "ScrivenerKeywords",
      "ScrivenerLabel",
      "ScrivenerLabelID",
      "ScrivenerNote",
      "ScrivenerStatus",
      "ScrivenerStatusID",
      "ScrivenerType"
    ],
    "Sorting": [
      "Sort",
      "SortAlso",
      "SortAlsoTransform",
      "SortBackward",
      "SortBackwardAlso",
      "SortTransform"
    ],
    "Storyspace": [
      "BeforeVisit",
      "ChosenWord",
      "Deck",
      "OnVisit",
      "Requirements",
      "ResetAction",
      "Visits"
    ],
    "TextFormat": [
      "HideKeyAttributes",
      "KeyAttributeDateFormat",
      "KeyAttributeFont",
      "KeyAttributeFontSize",
      "KeyAttributes",
      "LeftMargin",
      "LineSpacing",
      "NoSpelling",
      "ParagraphSpacing",
      "RightMargin",
      "ShowTitle",
      "SmartQuotes",
      "Tabs",
      "TextAlign",
      "TextBackgroundColor",
      "TextColor",
      "TextFont",
      "TextFontSize",
      "TextPaneRatio",
      "TextPaneWidth",
      "TextSidebar",
      "TextWindowHeight",
      "TextWindowWidth",
      "TitleBackgroundColor",
      "TitleFont",
      "TitleForegroundColor"
    ],
    "Textual": [
      "ImageCount",
      "ReadOnly",
      "Text",
      "TextExportTemplate",
      "TextLength",
      "WordCount"
    ],
    "Watch": [
      "DEVONthinkGroup",
      "DEVONthinkLabel",
      "EvernoteNotebook",
      "NotesFolder",
      "NotesID",
      "NotesModified",
      "SimplenoteKey",
      "SimplenoteModified",
      "SimplenoteSync",
      "SimplenoteTags",
      "SimplenoteVersion",
      "SourceURL",
      "UUID",
      "WatchFolder"
    ],
    "Weblog": [
      "WeblogPostID",
      "mt_allow_comments",
      "mt_allow_pings",
      "mt_convert_breaks",
      "mt_keywords"
    ]
  },
  "Attributes": {
    "Abstract": "References",
    "AccentColor": "Appearance",
    "AccessDate": "References",
    "Address": "People",
    "AdornmentCount": "General",
    "AdornmentFont": "Map",
    "AgentAction": "Agent",
    "AgentCaseSensitive": "Agent",
    "AgentPriority": "Agent",
    "AgentQuery": "Agent",
    "AIM": "People",
    "Aliases": "General",
    "ArticleTitle": "References",
    "Associates": "Composites",
    "Author2": "References",
    "Author3": "References",
    "Author4": "References",
    "Authors": "References",
    "AutoFetch": "Net",
    "AutoFetchCommand": "Net",
    "Badge": "Appearance",
    "BadgeMonochrome": "Appearance",
    "BadgeSize": "Appearance",
    "Base": "Map",
    "BeforeVisit": "Storyspace",
    "Bend": "Map",
    "BookTitle": "References",
    "Border": "Appearance",
    "BorderBevel": "Appearance",
    "BorderColor": "Appearance",
    "BorderDash": "Appearance",
    "CallNumber": "References",
    "Caption": "General",
    "CaptionAlignment": "Appearance",
    "CaptionColor": "Appearance",
    "CaptionFont": "Appearance",
    "CaptionOpacity": "Appearance",
    "CaptionSize": "Appearance",
    "Checked": "General",
    "ChildCount": "General",
    "ChosenWord": "Storyspace",
    "City": "People",
    "CleanupAction": "Agent",
    "ClusterTerms": "General",
    "Color": "Appearance",
    "Color2": "Appearance",
    "Container": "General",
    "Country": "People",
    "Created": "General",
    "Creator": "General",
    "Deck": "Storyspace",
    "DescendantCount": "General",
    "DEVONthinkGroup": "Watch",
    "DEVONthinkLabel": "Watch",
    "Direction": "Map",
    "DisplayExpression": "General",
    "DisplayExpressionDisabled": "General",
    "DisplayName": "General",
    "District": "People",
    "DOI": "References",
    "DueDate": "Events",
    "Edict": "General",
    "EdictDisabled": "General",
    "Edition": "References",
    "Email": "People",
    "EndDate": "Events",
    "EvernoteNotebook": "Watch",
    "File": "General",
    "Fill": "Map",
    "FillOpacity": "Map",
    "Flags": "Appearance",
    "FormattedAddress": "People",
    "FullName": "People",
    "GeocodedAddress": "People",
    "GridColor": "Grid",
    "GridColumns": "Grid",
    "GridLabelFont": "Grid",
    "GridLabels": "Grid",
    "GridLabelSize": "Grid",
    "GridOpacity": "Grid",
    "GridRows": "Grid",
    "Height": "Map",
    "HideKeyAttributes": "TextFormat",
    "HoverExpression": "General",
    "HoverFont": "Map",
    "HoverImage": "General",
    "HoverOpacity": "General",
    "HTMLBoldEnd": "HTML",
    "HTMLBoldStart": "HTML",
    "HTMLCloud1End": "HTML",
    "HTMLCloud1Start": "HTML",
    "HTMLCloud2End": "HTML",
    "HTMLCloud2Start": "HTML",
    "HTMLCloud3End": "HTML",
    "HTMLCloud3Start": "HTML",
    "HTMLCloud4End": "HTML",
    "HTMLCloud4Start": "HTML",
    "HTMLCloud5End": "HTML",
    "HTMLCloud5Start": "HTML",
    "HTMLDontExport": "HTML",
    "HTMLEntities": "HTML",
    "HTMLExportAfter": "HTML",
    "HTMLExportBefore": "HTML",
    "HTMLExportChildren": "HTML",
    "HTMLExportCommand": "HTML",
    "HTMLExportExtension": "HTML",
    "HTMLExportFileName": "HTML",
    "HTMLExportFileNameSpacer": "HTML",
    "HTMLExportPath": "HTML",
    "HTMLExportTemplate": "HTML",
    "HTMLFileNameLowerCase": "HTML",
    "HTMLFileNameMaxLength": "HTML",
    "HTMLFirstParagraphEnd": "HTML",
    "HTMLFirstParagraphStart": "HTML",
    "HTMLFont": "HTML",
    "HTMLFontSize": "HTML",
    "HTMLImageEnd": "HTML",
    "HTMLImageStart": "HTML",
    "HTMLIndentedParagraphEnd": "HTML",
    "HTMLIndentedParagraphStart": "HTML",
    "HTMLItalicEnd": "HTML",
    "HTMLItalicStart": "HTML",
    "HTMLLinkExtension": "HTML",
    "HTMLListEnd": "HTML",
    "HTMLListItemEnd": "HTML",
    "HTMLListItemStart": "HTML",
    "HTMLListStart": "HTML",
    "HTMLMarkDown": "HTML",
    "HTMLMarkupText": "HTML",
    "HTMLOrderedListEnd": "HTML",
    "HTMLOrderedListItemStart": "HTML",
    "HTMLOrderedListStart": "HTML",
    "HTMLOrderedsListItemEnd": "HTML",
    "HTMLOverwriteImages": "HTML",
    "HTMLParagraphEnd": "HTML",
    "HTMLParagraphStart": "HTML",
    "HTMLPreviewCommand": "HTML",
    "HTMLQuoteHTML": "HTML",
    "HTMLStrikeEnd": "HTML",
    "HTMLStrikeStart": "HTML",
    "HTMLUnderlineEnd": "HTML",
    "HTMLUnderlineStart": "HTML",
    "ID": "General",
    "ImageCount": "Textual",
    "InboundLinkCount": "General",
    "InteriorScale": "Map",
    "IrisAngle": "Iris",
    "IrisRadius": "Iris",
    "IsAdornment": "General",
    "IsAlias": "General",
    "ISBN": "References",
    "IsComposite": "Composites",
    "IsMultiple": "Composites",
    "IsPrototype": "General",
    "Issue": "References",
    "IsTemplate": "HTML",
    "Journal": "References",
    "KeyAttributeDateFormat": "TextFormat",
    "KeyAttributeFont": "TextFormat",
    "KeyAttributeFontSize": "TextFormat",
    "KeyAttributes": "TextFormat",
    "LastFetched": "Net",
    "Latitude": "People",
    "LeafBase": "Map",
    "LeafBend": "Map",
    "LeafDirection": "Map",
    "LeafTip": "Map",
    "LeftMargin": "TextFormat",
    "LineSpacing": "TextFormat",
    "Lock": "Map",
    "Longitude": "People",
    "MapBackgroundAccentColor": "Map",
    "MapBackgroundColor": "Map",
    "MapBackgroundColor2": "Map",
    "MapBackgroundFill": "Map",
    "MapBackgroundFillOpacity": "Map",
    "MapBackgroundPattern": "Map",
    "MapBackgroundShadow": "Map",
    "MapBodyTextColor": "Map",
    "MapBodyTextSize": "Map",
    "MapScrollX": "Map",
    "MapScrollY": "Map",
    "MapTextSize": "Map",
    "Modified": "General",
    "mt_allow_comments": "Weblog",
    "mt_allow_pings": "Weblog",
    "mt_convert_breaks": "Weblog",
    "mt_keywords": "Weblog",
    "MyBoolean": "Sandbox",
    "MyColor": "Sandbox",
    "MyDate": "Sandbox",
    "MyInterval": "Sandbox",
    "MyList": "Sandbox",
    "MyNumber": "Sandbox",
    "MySet": "Sandbox",
    "MyString": "Sandbox",
    "Name": "General",
    "NameAlignment": "Map",
    "NameBold": "Map",
    "NameColor": "Map",
    "NameFont": "Map",
    "NameLeading": "Map",
    "NameStrike": "Map",
    "NeverComposite": "Composites",
    "NLNames": "AI",
    "NLOrganizations": "AI",
    "NLPlaces": "AI",
    "NoSpelling": "TextFormat",
    "NotesFolder": "Watch",
    "NotesID": "Watch",
    "NotesModified": "Watch",
    "NoteURL": "Net",
    "OnAdd": "General",
    "OnJoin": "Composites",
    "OnRemove": "General",
    "OnVisit": "Storyspace",
    "Opacity": "Appearance",
    "Organization": "People",
    "OutboundLinkCount": "General",
    "OutlineBackgroundColor": "Outline",
    "OutlineColorSwatch": "Outline",
    "OutlineDepth": "General",
    "OutlineOrder": "General",
    "OutlineTextSize": "Outline",
    "Pages": "References",
    "ParagraphSpacing": "TextFormat",
    "Path": "General",
    "Pattern": "Appearance",
    "PlainLinkCount": "General",
    "PlotBackgroundColor": "Map",
    "PlotBackgroundOpacity": "Map",
    "PlotColor": "Appearance",
    "PlotColorList": "Map",
    "PostalCode": "People",
    "Private": "General",
    "Prototype": "General",
    "PrototypeBequeathsChildren": "General",
    "PrototypeHighlightColor": "Outline",
    "PublicationCity": "References",
    "PublicationYear": "References",
    "Publisher": "References",
    "RawData": "Net",
    "ReadCount": "General",
    "ReadOnly": "Textual",
    "ReferenceRIS": "References",
    "ReferenceTitle": "References",
    "ReferenceURL": "References",
    "RefFormat": "References",
    "RefKeywords": "References",
    "RefType": "References",
    "Requirements": "Storyspace",
    "ResetAction": "Storyspace",
    "RightMargin": "TextFormat",
    "Role": "Composites",
    "RSSChannelTemplate": "Net",
    "RSSItemLimit": "Net",
    "RSSItemTemplate": "Net",
    "Rule": "General",
    "RuleDisabled": "General",
    "ScrivenerID": "Scrivener",
    "ScrivenerKeywords": "Scrivener",
    "ScrivenerLabel": "Scrivener",
    "ScrivenerLabelID": "Scrivener",
    "ScrivenerNote": "Scrivener",
    "ScrivenerStatus": "Scrivener",
    "ScrivenerStatusID": "Scrivener",
    "ScrivenerType": "Scrivener",
    "Searchable": "General",
    "SelectionCount": "General",
    "Separator": "Outline",
    "Shadow": "Appearance",
    "ShadowBlur": "Appearance",
    "ShadowColor": "Appearance",
    "ShadowDistance": "Appearance",
    "Shape": "Appearance",
    "ShowTitle": "TextFormat",
    "SiblingOrder": "General",
    "SimplenoteKey": "Watch",
    "SimplenoteModified": "Watch",
    "SimplenoteSync": "Watch",
    "SimplenoteTags": "Watch",
    "SimplenoteVersion": "Watch",
    "SmartQuotes": "TextFormat",
    "Sort": "Sorting",
    "SortAlso": "Sorting",
    "SortAlsoTransform": "Sorting",
    "SortBackward": "Sorting",
    "SortBackwardAlso": "Sorting",
    "SortTransform": "Sorting",
    "SourceCreated": "References",
    "SourceModified": "References",
    "SourceURL": "Watch",
    "StartDate": "Events",
    "State": "People",
    "Sticky": "Map",
    "Subtitle": "General",
    "SubtitleColor": "Map",
    "SubtitleOpacity": "Map",
    "SubtitleSize": "Map",
    "TableExpression": "Map",
    "TableHeading": "Map",
    "Tabs": "TextFormat",
    "Tags": "References",
    "Telephone": "People",
    "Text": "Textual",
    "TextAlign": "TextFormat",
    "TextBackgroundColor": "TextFormat",
    "TextColor": "TextFormat",
    "TextExportTemplate": "Textual",
    "TextFont": "TextFormat",
    "TextFontSize": "TextFormat",
    "TextLength": "Textual",
    "TextLinkCount": "General",
    "TextPaneRatio": "TextFormat",
    "TextPaneWidth": "TextFormat",
    "TextSidebar": "TextFormat",
    "TextWindowHeight": "TextFormat",
    "TextWindowWidth": "TextFormat",
    "TimelineAliases": "Events",
    "TimelineBand": "Events",
    "TimelineBandLabelColor": "Events",
    "TimelineBandLabelOpacity": "Events",
    "TimelineBandLabels": "Events",
    "TimelineColor": "Events",
    "TimelineDescendants": "Events",
    "TimelineEnd": "Events",
    "TimelineEndAttribute": "Events",
    "TimelineGridColor": "Events",
    "TimelineMarker": "Events",
    "TimelineScaleColor": "Events",
    "TimelineScaleColor2": "Events",
    "TimelineStart": "Events",
    "TimelineStartAttribute": "Events",
    "Tip": "Map",
    "TitleBackgroundColor": "TextFormat",
    "TitleFont": "TextFormat",
    "TitleForegroundColor": "TextFormat",
    "TitleHeight": "Map",
    "TitleOpacity": "Map",
    "Twitter": "People",
    "URL": "Net",
    "User": "General",
    "UUID": "Watch",
    "ViewInBrowser": "Net",
    "Visits": "Storyspace",
    "Volume": "References",
    "WatchFolder": "Watch",
    "WebLinkCount": "General",
    "WeblogPostID": "Weblog",
    "Width": "Map",
    "WordCount": "Textual",
    "Xpos": "Map",
    "Ypos": "Map"
  }
}

Both dictionaries in the JSON listed in the preceding post are generated by the following script, which takes c. 3 seconds to check (against an automatically generated fresh new document) there there are no missing system attributes or unrecognised attribute names, before placing the the full JSON () in the clipboard.

(() => {
    'use strict';

    ObjC.import('AppKit');

    // Rob Trew 2019
    // Ver 0.01

    // For Tinderbox 8 (osascript) JavaScript for for Automation

    // Generate and validate a JSON array containing two dictionaries
    // - Attribute lists keyed by their categories
    // - Attribute : Category key-value pairs

    // If the JSON checks against a freshly generated empty document
    // (No missing system attributes, no unrecognised attribute names)
    // then the JSON is copied to the clipboard.

    // If gaps or oddities are found, nothing is copied, but lists of
    // missing and/or unrecognised attribute names are printed to console.

    // main :: IO ()
    const main = () => {

        // Main dictionary.
        // The second (attribute:category) is automatically generated
        // from this one.

        // Both will be checked against an automatically created new
        // Tinderbox 8 document before any JSON is copied to the clipboard.

        // Attribute lists keyed by category names:
        const dctGroups = {
            'AI': [
                'NLNames',
                'NLOrganizations',
                'NLPlaces'
            ],
            'Agent': [
                'AgentAction',
                'AgentCaseSensitive',
                'AgentPriority',
                'AgentQuery',
                'CleanupAction'
            ],
            'Appearance': [
                'AccentColor',
                'Badge',
                'BadgeMonochrome',
                'BadgeSize',
                'Border',
                'BorderBevel',
                'BorderColor',
                'BorderDash',
                'CaptionAlignment',
                'CaptionColor',
                'CaptionFont',
                'CaptionOpacity',
                'CaptionSize',
                'Color',
                'Color2',
                'Flags',
                'Opacity',
                'Pattern',
                'PlotColor',
                'Shadow',
                'ShadowBlur',
                'ShadowColor',
                'ShadowDistance',
                'Shape'
            ],
            'Composites': [
                'Associates',
                'IsComposite',
                'IsMultiple',
                'NeverComposite',
                'OnJoin',
                'Role'
            ],
            'Events': [
                'DueDate',
                'EndDate',
                'StartDate',
                'TimelineAliases',
                'TimelineBand',
                'TimelineBandLabelColor',
                'TimelineBandLabelOpacity',
                'TimelineBandLabels',
                'TimelineColor',
                'TimelineDescendants',
                'TimelineEnd',
                'TimelineEndAttribute',
                'TimelineGridColor',
                'TimelineMarker',
                'TimelineScaleColor',
                'TimelineScaleColor2',
                'TimelineStart',
                'TimelineStartAttribute'
            ],
            'General': [
                'AdornmentCount',
                'Aliases',
                'Caption',
                'Checked',
                'ChildCount',
                'ClusterTerms',
                'Container',
                'Created',
                'Creator',
                'DescendantCount',
                'DisplayExpression',
                'DisplayExpressionDisabled',
                'DisplayName',
                'Edict',
                'EdictDisabled',
                'File',
                'HoverExpression',
                'HoverImage',
                'HoverOpacity',
                'ID',
                'InboundLinkCount',
                'IsAdornment',
                'IsAlias',
                'IsPrototype',
                'Modified',
                'Name',
                'OnAdd',
                'OnRemove',
                'OutboundLinkCount',
                'OutlineDepth',
                'OutlineOrder',
                'Path',
                'PlainLinkCount',
                'Private',
                'Prototype',
                'PrototypeBequeathsChildren',
                'ReadCount',
                'Rule',
                'RuleDisabled',
                'Searchable',
                'SelectionCount',
                'SiblingOrder',
                'Subtitle',
                'TextLinkCount',
                'User',
                'WebLinkCount'
            ],
            'Grid': [
                'GridColor',
                'GridColumns',
                'GridLabelFont',
                'GridLabelSize',
                'GridLabels',
                'GridOpacity',
                'GridRows'
            ],
            'HTML': [
                "HTMLBoldEnd",
                "HTMLBoldStart",
                'HTMLCloud1End',
                'HTMLCloud1Start',
                'HTMLCloud2End',
                'HTMLCloud2Start',
                'HTMLCloud3End',
                'HTMLCloud3Start',
                'HTMLCloud4End',
                'HTMLCloud4Start',
                'HTMLCloud5End',
                'HTMLCloud5Start',
                'HTMLDontExport',
                'HTMLEntities',
                'HTMLExportAfter',
                'HTMLExportBefore',
                'HTMLExportChildren',
                'HTMLExportCommand',
                'HTMLExportExtension',
                'HTMLExportFileName',
                'HTMLExportFileNameSpacer',
                'HTMLExportPath',
                'HTMLExportTemplate',
                'HTMLFileNameLowerCase',
                'HTMLFileNameMaxLength',
                'HTMLFirstParagraphEnd',
                'HTMLFirstParagraphStart',
                'HTMLFont',
                'HTMLFontSize',
                'HTMLImageEnd',
                'HTMLImageStart',
                'HTMLIndentedParagraphEnd',
                'HTMLIndentedParagraphStart',
                'HTMLItalicEnd',
                'HTMLItalicStart',
                'HTMLLinkExtension',
                'HTMLListEnd',
                'HTMLListItemEnd',
                'HTMLListItemStart',
                'HTMLListStart',
                'HTMLMarkDown',
                'HTMLMarkupText',
                'HTMLOrderedListEnd',
                'HTMLOrderedListItemStart',
                'HTMLOrderedListStart',
                'HTMLOrderedsListItemEnd',
                'HTMLOverwriteImages',
                'HTMLParagraphEnd',
                'HTMLParagraphStart',
                'HTMLPreviewCommand',
                'HTMLQuoteHTML',
                'HTMLStrikeEnd',
                'HTMLStrikeStart',
                'HTMLUnderlineEnd',
                'HTMLUnderlineStart',
                'IsTemplate'
            ],
            'Iris': [
                'IrisAngle',
                'IrisRadius'
            ],
            'Map': [
                'AdornmentFont',
                'Base',
                'Bend',
                'Direction',
                'Fill',
                'FillOpacity',
                'Height',
                'HoverFont',
                'InteriorScale',
                'LeafBase',
                'LeafBend',
                'LeafDirection',
                'LeafTip',
                'Lock',
                'MapBackgroundAccentColor',
                'MapBackgroundColor',
                'MapBackgroundColor2',
                'MapBackgroundFill',
                'MapBackgroundFillOpacity',
                'MapBackgroundPattern',
                'MapBackgroundShadow',
                'MapBodyTextColor',
                'MapBodyTextSize',
                'MapScrollX',
                'MapScrollY',
                'MapTextSize',
                'NameAlignment',
                'NameBold',
                'NameColor',
                'NameFont',
                'NameLeading',
                'NameStrike',
                'PlotBackgroundColor',
                'PlotBackgroundOpacity',
                'PlotColorList',
                'Sticky',
                'SubtitleColor',
                'SubtitleOpacity',
                'SubtitleSize',
                'TableExpression',
                'TableHeading',
                'Tip',
                'TitleHeight',
                'TitleOpacity',
                'Width',
                'Xpos',
                'Ypos'
            ],
            'Net': [
                'AutoFetch',
                'AutoFetchCommand',
                'LastFetched',
                'NoteURL',
                'RSSChannelTemplate',
                'RSSItemLimit',
                'RSSItemTemplate',
                'RawData',
                'URL',
                'ViewInBrowser'
            ],
            'Outline': [
                'OutlineBackgroundColor',
                'OutlineColorSwatch',
                'OutlineTextSize',
                "PrototypeHighlightColor",
                'Separator'
            ],
            'People': [
                'AIM',
                'Address',
                'City',
                'Country',
                'District',
                'Email',
                'FormattedAddress',
                'FullName',
                'GeocodedAddress',
                'Latitude',
                'Longitude',
                'Organization',
                'PostalCode',
                'State',
                'Telephone',
                'Twitter'
            ],
            'References': [
                "Abstract",
                'AccessDate',
                'ArticleTitle',
                'Author2',
                'Author3',
                'Author4',
                'Authors',
                'BookTitle',
                'CallNumber',
                'DOI',
                'Edition',
                'ISBN',
                'Issue',
                'Journal',
                'Pages',
                'PublicationCity',
                'PublicationYear',
                'Publisher',
                'RefFormat',
                'RefKeywords',
                'RefType',
                'ReferenceRIS',
                'ReferenceTitle',
                'ReferenceURL',
                'SourceCreated',
                'SourceModified',
                'Tags',
                'Volume'
            ],
            'Sandbox': [
                'MyBoolean',
                'MyColor',
                'MyDate',
                'MyInterval',
                'MyList',
                'MyNumber',
                'MySet',
                'MyString'
            ],
            'Scrivener': [
                'ScrivenerID',
                'ScrivenerKeywords',
                'ScrivenerLabel',
                'ScrivenerLabelID',
                'ScrivenerNote',
                'ScrivenerStatus',
                'ScrivenerStatusID',
                'ScrivenerType'
            ],
            'Sorting': [
                'Sort',
                'SortAlso',
                'SortAlsoTransform',
                'SortBackward',
                'SortBackwardAlso',
                'SortTransform'
            ],
            'Storyspace': [
                'BeforeVisit',
                'ChosenWord',
                'Deck',
                'OnVisit',
                'Requirements',
                'ResetAction',
                'Visits'
            ],
            'TextFormat': [
                'HideKeyAttributes',
                'KeyAttributeDateFormat',
                'KeyAttributeFont',
                'KeyAttributeFontSize',
                'KeyAttributes',
                'LeftMargin',
                'LineSpacing',
                'NoSpelling',
                'ParagraphSpacing',
                'RightMargin',
                'ShowTitle',
                'SmartQuotes',
                'Tabs',
                'TextAlign',
                'TextBackgroundColor',
                'TextColor',
                'TextFont',
                'TextFontSize',
                'TextPaneRatio',
                'TextPaneWidth',
                'TextSidebar',
                'TextWindowHeight',
                'TextWindowWidth',
                'TitleBackgroundColor',
                'TitleFont',
                'TitleForegroundColor'
            ],
            'Textual': [
                'ImageCount',
                'ReadOnly',
                'Text',
                'TextExportTemplate',
                'TextLength',
                'WordCount'
            ],
            'Watch': [
                'DEVONthinkGroup',
                'DEVONthinkLabel',
                'EvernoteNotebook',
                'NotesFolder',
                'NotesID',
                'NotesModified',
                'SimplenoteKey',
                'SimplenoteModified',
                'SimplenoteSync',
                'SimplenoteTags',
                'SimplenoteVersion',
                'SourceURL',
                'UUID',
                'WatchFolder'
            ],
            'Weblog': [
                'WeblogPostID',
                'mt_allow_comments',
                'mt_allow_pings',
                'mt_convert_breaks',
                'mt_keywords'
            ]
        };

        return either(
            x => 'Missing attributes, or unrecognised attribute names.',
            x => {
                const strJSON = JSON.stringify({
                        Categories: dctGroups,
                        Attributes: sortedDict(x)
                    },
                    null, 2
                );
                return (
                    copyText(strJSON),
                    strJSON
                );
            },
            bindLR(
                noMissingAttributesLR(dctGroups),
                noSurplusAttributesLR
            )
        );
    };

    // TINDERBOX ------------------------------------------

    // noMissingAttributesLR :: Dict {String :: [String]} ->
    //          Either [String] Dict {String :: String}
    const noMissingAttributesLR = dctGroups => {
        const
            tbx = Application('Tinderbox 8'),
            ds = tbx.documents,
            // new doc object constructed, not yet added.
            doc = tbx.Document(),
            dctAttrs = leafDictFromListsDict(dctGroups)
        return (
            ds.push(doc), // Added to top of document collection.
            bindLR(
                0 < tbx.documents.length ? (
                    Right(tbx.documents.at(0))
                ) : Left('No new document open in Tinderbox 8'),
                d => {
                    const gaps = filter(
                        k => !Boolean(dctAttrs[k]),
                        d.attributes.name()
                    );
                    return (
                        showLog('Missing attributes: ', gaps),
                        0 < length.gaps ? Left(
                            'Missing System attributes: ' +
                            unwords(gaps)
                        ) : Right(dctAttrs)
                    );
                }
            )
        );
    };

    // noSurplusAttributesLR :: Dict {String :: [String]} ->
    //          Either String [String]
    const noSurplusAttributesLR = dctAttrs => {
        const
            tbx = Application('Tinderbox 8'),
            ds = tbx.documents,
            // new doc object constructed, not yet added.
            doc = tbx.Document();
        return (
            ds.push(doc), // Added to top of document collection.
            bindLR(
                0 < tbx.documents.length ? (
                    Right(tbx.documents.at(0))
                ) : Left('No new document open in Tinderbox 8'),
                d => {
                    const
                        attribs = d.attributes,
                        unknowns = filter(
                            k => !attribs.byName(k).exists(),
                            keys(dctAttrs)
                        );
                    return (
                        showLog('Unknown attribute names: ', unknowns),
                        0 < unknowns.length ? Left(
                            'Unknown attributes: ' +
                            unwords(unknowns)
                        ) : Right(dctAttrs)
                    );
                }
            )
        );
    };

    // JXA ------------------------------------------------

    // String copied to general pasteboard
    // copyText :: String -> IO Bool
    const copyText = s => {
        const pb = $.NSPasteboard.generalPasteboard;
        return (
            pb.clearContents,
            pb.setStringForType(
                $(s),
                $.NSPasteboardTypeString
            )
        );
    };

    // JS BASICS ------------------------------------------

    // leafDictFromListsDict :: Dict -> Dict
    const leafDictFromListsDict = dctLists =>
        // Dictionary of list names mutiply keyed by each of
        // their members,
        // derived from a dictionary of lists keyed by list names.
        foldl(
            (a, k) => Object.assign(
                a,
                foldl(
                    (b, s) => Object.assign(
                        b, {
                            [s]: k
                        }
                    ), {},
                    dctLists[k]
                )
            ), {},
            keys(dctLists)
        );

    // sortedDict :: Dict -> Dict
    const sortedDict = dct =>
        // Dictionary with keys in lexical sort order.
        foldl(
            (a, k) => Object.assign(
                a, {
                    [k]: dct[k]
                }
            ), {},
            sortBy(comparing(toUpper), keys(dct))
        );


    // GENERIC FUNCTIONS ----------------------------------
    // https://github.com/RobTrew/prelude-jxa

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });

    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });

    // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
    const bindLR = (m, mf) =>
        undefined !== m.Left ? (
            m
        ) : mf(m.Right);

    // comparing :: (a -> b) -> (a -> a -> Ordering)
    const comparing = f =>
        (x, y) => {
            const
                a = f(x),
                b = f(y);
            return a < b ? -1 : (a > b ? 1 : 0);
        };

    // either :: (a -> c) -> (b -> c) -> Either a b -> c
    const either = (fl, fr, e) =>
        'Either' === e.type ? (
            undefined !== e.Left ? (
                fl(e.Left)
            ) : fr(e.Right)
        ) : undefined;

    // filter :: (a -> Bool) -> [a] -> [a]
    const filter = (f, xs) => xs.filter(f);

    // foldl :: (a -> b -> a) -> a -> [b] -> a
    const foldl = (f, a, xs) => xs.reduce(f, a);

    // keys :: Dict -> [String]
    const keys = Object.keys;

    // Returns Infinity over objects without finite length.
    // This enables zip and zipWith to choose the shorter
    // argument when one is non-finite, like cycle, repeat etc

    // length :: [a] -> Int
    const length = xs =>
        (Array.isArray(xs) || 'string' === typeof xs) ? (
            xs.length
        ) : Infinity;

    // showLog :: a -> IO ()
    const showLog = (...args) =>
        console.log(
            args
            .map(JSON.stringify)
            .join(' -> ')
        );

    // sortBy :: (a -> a -> Ordering) -> [a] -> [a]
    const sortBy = (f, xs) =>
        xs.slice()
        .sort(f);

    // toUpper :: String -> String
    const toUpper = s => s.toLocaleUpperCase();

    // unwords :: [String] -> String
    const unwords = xs => xs.join(' ');

    // MAIN ---
    return main();
})();