:lipstick: Fix lines.

:lipstick: Fix lines.

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";
import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace config = "http://apps.28.io/config";

declare variable $local:expected as object :=
    {
      "byconcept" : [ "1001000 - Statement - Consolidated Statement of Income", "1005000 - Statement - Consolidated Statement of Equity", "2404402 - Disclosure - Summarized Financial Data - Chevron U.S.A. Inc. (Details)", "2405402 - Disclosure - Summarized Financial Data - Chevron Transport Corporation (Details)", "2410402 - Disclosure - Operating Segments and Geographic Data (Details)", "2427402 - Disclosure - Earnings Per Share (Details)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF INCOME", "1005000 - Statement - CONSOLIDATED STATEMENTS OF SHAREOWNERS' EQUITY", "1002000 - Statement - CONSOLIDATED STATEMENTS OF OPERATIONS", "1004000 - Statement - CONSOLIDATED STATEMENTS OF CHANGES IN EQUITY", "2436402 - Disclosure - Basic and Diluted Earnings Per Share (Detail)", "1001000 - Statement - Consolidated Income Statements", "1001000 - Statement - Consolidated Statements Of Income (Audited)", "1002000 - Statement - Consolidated Statements Of Comprehensive Income (Audited)", "2415402 - Disclosure - Quarterly Financial Data (Unaudited) (Quarterly Financial Data) (Details)", "000100 - Statement - Consolidated Statement Of Income", "041101 - Disclosure - Earnings Per Share (Details)", "041702 - Disclosure - Disclosures About Segments And Related Information (Schedule Of Segments And Related Information) (Details)", "1003 - Statement - Consolidated Statements of Income", "1008 - Statement - Consolidated Statements of Changes in Equity", "1118 - Disclosure - Schedule of Quarterly Financial Information (Detail)", "1001000 - Statement - Consolidated Statements of Income", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Cash Flows", "1005000 - Statement - Consolidated Statements of Stockholders' Equity", "1003 - Statement - Income Statements", "1004 - Statement - Comprehensive Income Statements", "1008 - Statement - Cash Flows Statements", "1009 - Statement - Stockholders' Equity Statements", "1055 - Disclosure - Basic and Diluted Earnings Per Share (Detail)", "1121 - Disclosure - Quarterly Information (Unaudited) (Detail)", "1002000 - Statement - Consolidated Statements of Income", "1003000 - Statement - Consolidated Statements of Comprehensive Income", "1005000 - Statement - Consolidated Statements of Changes in Stockholders' Equity", "1006000 - Statement - Consolidated Statements of Cash Flows", "2429403 - Disclosure - Earnings Per Share (Details)", "2438402 - Disclosure - International Operations (Details)", "2440402 - Disclosure - Parent Company - Statements of Income (Details)", "2440404 - Disclosure - Parent Company - Statements of Cash Flows (Details2)", "1001000 - Statement - Consolidated Statements of Income", "1002000 - Statement - Consolidated Statements Of Earnings", "1003000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Stockholders' Equity", "1005000 - Statement - Consolidated Statements Of Cash Flows", "2415402 - Disclosure - Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Operations", "1004000 - Statement - Consolidated Statement of Cash Flows", "2404402 - Disclosure - Earnings Per Share (Details)", "2410403 - Disclosure - Equity (Changes in Noncontrolling Interests) (Details)", "2420404 - Disclosure - Segment Financial Data (Selected Quarterly Financial Data) (Details)", "2421402 - Disclosure - Selected Quarterly Financial Data - Unaudited (Details)", "1002000 - Statement - Consolidated Statements of Earnings", "1004000 - Statement - Consolidated Statements of Equity", "2419402 - Disclosure - Selected Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Income", "1002000 - Statement - Consolidated Statement of Comprehensive Income", "1004000 - Statement - Consolidated Statement of Equity", "2420402 - Disclosure - Earnings Per Share - Calculations of Earnings Per Share (Detail)", "1001000 - Statement - Consolidated Statements Of Operations", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements Of Cash Flows", "2401402 - Disclosure - Summary Of Business Segment Data (Details)", "2405403 - Disclosure - Earnings Per Share (Schedule Of Weighted-Average Number Of Shares Outstanding Used To Compute Earnings Per Share) (Details)", "2424403 - Disclosure - Quarterly Financial Data (Schedule Of Quarterly Financial Data) (Details)", "1003000 - Statement - Consolidated Statements of Operations", "1003000 - Statement - Consolidated Statements of Operations", "1005000 - Statement - Consolidated Statements of Changes in Shareholders' Equity", "2429403 - Disclosure - Quarterly Financial Data Quarterly Financial Data (Unaudited) (Details)", "2430405 - Disclosure - Schedule I Condensed Income Statement (Details)", "000100 - Statement - Consolidated Statements Of Income", "000400 - Statement - Consolidated Statements Of Changes In Stockholders' Equity", "041801 - Disclosure - Quarterly Financial Information (Unaudited) (Quarterly Financial Results) (Details)", "0110 - Statement - Consolidated Statements of Earnings", "0120 - Statement - Consolidated Statements of Comprehensive Income", "0140 - Statement - Consolidated Statements of Changes in Shareholders' Equity", "0150 - Statement - Consolidated Statements of Cash Flows", "0625 - Disclosure - Business Segments (Details)", "0630 - Disclosure - Parent Company (Details)", "06302 - Disclosure - Parent Company (Details 2)", "100020 - Statement - Statement of Earnings", "100030 - Statement - Statement of Comprehensive Income", "100040 - Statement - Statement of Changes in Shareowners' Equity", "100060 - Statement - Statement of Cash Flows", "401500 - Disclosure - Shareowners' Equity (Details)", "402800 - Disclosure - Quarterly Information (unaudited) (Details)", "1001000 - Statement - Consolidated Statements Of Income", "1001000 - Statement - Consolidated Statements Of Income", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Cash Flows", "1005000 - Statement - Consolidated Statements of Shareholders' Equity", "1003000 - Statement - Consolidated Statements Of Operations", "1004000 - Statement - Consolidated Statements of Comprehensive Income", "1005000 - Statement - Consolidated Statements Of Cash Flows", "1006000 - Statement - Consolidated Statements Of Equity", "2420402 - Disclosure - Net Income per Share (Calculation Of Basic And Diluted Net Income Per Share) (Detail)", "010020 - Statement - Consolidated Statements of Income", "010035 - Statement - Consolidated Statements of Comprehensive Income", "010060 - Statement - Consolidated Statements of Cash Flows", "010080 - Statement - Consolidated Statements of Shareholders' Equity", "042600 - Disclosure - Parent Company (Details)", "042620 - Disclosure - Parent Company (Details 2)", "042700 - Disclosure - Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Income", "1002000 - Statement - Consolidated Statement of Comprehensive Income", "1004000 - Statement - Consolidated Statement of Cash Flows", "1005000 - Statement - Consolidated Statement of Shareholders Equity", "2414402 - Disclosure - Quarterly Results (Detail)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF INCOME", "2475402 - Disclosure - Quarterly Financial Summary (Detail)", "000011 - Statement - Consolidated Statement of Income", "004010 - Disclosure - Significant Accounting Policies (Details)", "004170 - Disclosure - Quarterly Data (Unaudited) (Details)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF EARNINGS", "2418402 - Disclosure - EARNINGS PER SHARE (DETAILS)", "2447402 - Disclosure - QUARTERLY RESULTS (UNAUDITED) (DETAILS)" ]
    };

declare %an:nondeterministic function local:test-acceptance-date($expected as string, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as string? := $request[2].Archives[].AcceptanceDatetime
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-components($expected as integer, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].Archives[].Components[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:sequential function local:test-components-result($expected as array, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as array := [ $request[2].Archives[].Components[].NetworkLabel ]
    let $status as integer := $request[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-generic-components($expected as integer, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].Components[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

local:check({
    cocacola: local:test-components(94, {
        ticker:"ko",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    cocacolaincome: local:test-components(1, {
        ticker:"ko",
        networkIdentifier:"http://www.thecocacolacompany.com/role/ConsolidatedStatementsOfIncome",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    byconcept: local:test-components-result($local:expected.byconcept, {
        tag:"DOW30",
        concept:"us-gaap:NetIncomeLoss",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    byfyfp: local:test-components(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        networkIdentifier:"http://www.thecoca-colacompany.com/role/CondensedConsolidatedBalanceSheets"
    }),
    byfyfplowercase: local:test-components(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"q2",
        networkIdentifier:"http://www.thecoca-colacompany.com/role/CondensedConsolidatedBalanceSheets"
    }),
    acceptance: local:test-acceptance-date("2014-07-30T15:44:58Z", {
        aid:"0000021344-14-000029"
    }),
    generic: local:test-generic-components(1, {
      profile-name: "generic",
      aid: "0000021344-14-000008",
      role: "http://www.thecocacolacompany.com/role/DocumentAndEntityInformationDocument"
    }),
    generic-aid: local:test-generic-components(95, {
      profile-name: "generic",
      aid: "0000021344-14-000008"
    }),
    example1: test:invoke-and-assert-deep-equal(
      "components",
      {aid:"0000021344-13-000039"},
      function($b as item*) as item* { [ trim($b.Archives[].Components[], ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure", "ValidationErrors")) ] },
      test:get-expected-result("secxbrl/components-expected1.jq"),
      { NoArrayOrder: true }
    ),
    example2: test:invoke-and-assert-deep-equal(
      "components",
      {
        ticker:"ko",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
      },
      function($b as item*) as item* {
        [ trim(
          $b.Archives[].Components[],
          ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure", "ValidationErrors")
        ) ]
      },
      test:get-expected-result("secxbrl/components-expected2.jq"),
      { NoArrayOrder: true }
    ),
    url: test:invoke-and-assert-deep-equal(
      "components",
      {aid:"0000021344-13-000039"},
      function($b as item*) as item* {
        project(
          $b.Archives[].Components[]
            [$$.NetworkIdentifier = "http://www.thecoca-colacompany.com/role/DocumentAndEntityInformation"],
          ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure")
        )
      },
      {
        "FactTable" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/facttable-for-component.jq?token="||$config:test-token||"&aid=0000021344-13-000039&format=&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec",
        "SpreadSheet" : "http://rendering.secxbrl.info/#?url=http%3A%2F%2F"||request:server-name()||"%3A"||request:server-port()||"%2Fv1%2F_queries%2Fpublic%2Fapi%2Fspreadsheet-for-component.jq%3Ftoken%3D"||$config:test-token||"%26aid%3D0000021344-13-000039%26format%3D%26role%3Dhttp%253A%252F%252Fwww.thecoca-colacompany.com%252Frole%252FDocumentAndEntityInformation%26profile-name%3Dsec",
      "ReportElements" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/report-elements.jq?token=foobar&aid=0000021344-13-000039&format=&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec",
      "ModelStructure" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/modelstructure-for-component.jq?token="||$config:test-token||"&aid=0000021344-13-000039&format=json&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec"
      },
      { NoArrayOrder: true }
    )
})

import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace test = "http://apps.28.io/test";
import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace config = "http://apps.28.io/config";

declare variable $local:expected as object :=
    {
      "byconcept" : [ "1001000 - Statement - Consolidated Statement of Income", "1005000 - Statement - Consolidated Statement of Equity", "2404402 - Disclosure - Summarized Financial Data - Chevron U.S.A. Inc. (Details)", "2405402 - Disclosure - Summarized Financial Data - Chevron Transport Corporation (Details)", "2410402 - Disclosure - Operating Segments and Geographic Data (Details)", "2427402 - Disclosure - Earnings Per Share (Details)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF INCOME", "1005000 - Statement - CONSOLIDATED STATEMENTS OF SHAREOWNERS' EQUITY", "1002000 - Statement - CONSOLIDATED STATEMENTS OF OPERATIONS", "1004000 - Statement - CONSOLIDATED STATEMENTS OF CHANGES IN EQUITY", "2436402 - Disclosure - Basic and Diluted Earnings Per Share (Detail)", "1001000 - Statement - Consolidated Income Statements", "1001000 - Statement - Consolidated Statements Of Income (Audited)", "1002000 - Statement - Consolidated Statements Of Comprehensive Income (Audited)", "2415402 - Disclosure - Quarterly Financial Data (Unaudited) (Quarterly Financial Data) (Details)", "000100 - Statement - Consolidated Statement Of Income", "041101 - Disclosure - Earnings Per Share (Details)", "041702 - Disclosure - Disclosures About Segments And Related Information (Schedule Of Segments And Related Information) (Details)", "1003 - Statement - Consolidated Statements of Income", "1008 - Statement - Consolidated Statements of Changes in Equity", "1118 - Disclosure - Schedule of Quarterly Financial Information (Detail)", "1001000 - Statement - Consolidated Statements of Income", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Cash Flows", "1005000 - Statement - Consolidated Statements of Stockholders' Equity", "1003 - Statement - Income Statements", "1004 - Statement - Comprehensive Income Statements", "1008 - Statement - Cash Flows Statements", "1009 - Statement - Stockholders' Equity Statements", "1055 - Disclosure - Basic and Diluted Earnings Per Share (Detail)", "1121 - Disclosure - Quarterly Information (Unaudited) (Detail)", "1002000 - Statement - Consolidated Statements of Income", "1003000 - Statement - Consolidated Statements of Comprehensive Income", "1005000 - Statement - Consolidated Statements of Changes in Stockholders' Equity", "1006000 - Statement - Consolidated Statements of Cash Flows", "2429403 - Disclosure - Earnings Per Share (Details)", "2438402 - Disclosure - International Operations (Details)", "2440402 - Disclosure - Parent Company - Statements of Income (Details)", "2440404 - Disclosure - Parent Company - Statements of Cash Flows (Details2)", "1001000 - Statement - Consolidated Statements of Income", "1002000 - Statement - Consolidated Statements Of Earnings", "1003000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Stockholders' Equity", "1005000 - Statement - Consolidated Statements Of Cash Flows", "2415402 - Disclosure - Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Operations", "1004000 - Statement - Consolidated Statement of Cash Flows", "2404402 - Disclosure - Earnings Per Share (Details)", "2410403 - Disclosure - Equity (Changes in Noncontrolling Interests) (Details)", "2420404 - Disclosure - Segment Financial Data (Selected Quarterly Financial Data) (Details)", "2421402 - Disclosure - Selected Quarterly Financial Data - Unaudited (Details)", "1002000 - Statement - Consolidated Statements of Earnings", "1004000 - Statement - Consolidated Statements of Equity", "2419402 - Disclosure - Selected Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Income", "1002000 - Statement - Consolidated Statement of Comprehensive Income", "1004000 - Statement - Consolidated Statement of Equity", "2420402 - Disclosure - Earnings Per Share - Calculations of Earnings Per Share (Detail)", "1001000 - Statement - Consolidated Statements Of Operations", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements Of Cash Flows", "2401402 - Disclosure - Summary Of Business Segment Data (Details)", "2405403 - Disclosure - Earnings Per Share (Schedule Of Weighted-Average Number Of Shares Outstanding Used To Compute Earnings Per Share) (Details)", "2424403 - Disclosure - Quarterly Financial Data (Schedule Of Quarterly Financial Data) (Details)", "1003000 - Statement - Consolidated Statements of Operations", "1003000 - Statement - Consolidated Statements of Operations", "1005000 - Statement - Consolidated Statements of Changes in Shareholders' Equity", "2429403 - Disclosure - Quarterly Financial Data Quarterly Financial Data (Unaudited) (Details)", "2430405 - Disclosure - Schedule I Condensed Income Statement (Details)", "000100 - Statement - Consolidated Statements Of Income", "000400 - Statement - Consolidated Statements Of Changes In Stockholders' Equity", "041801 - Disclosure - Quarterly Financial Information (Unaudited) (Quarterly Financial Results) (Details)", "0110 - Statement - Consolidated Statements of Earnings", "0120 - Statement - Consolidated Statements of Comprehensive Income", "0140 - Statement - Consolidated Statements of Changes in Shareholders' Equity", "0150 - Statement - Consolidated Statements of Cash Flows", "0625 - Disclosure - Business Segments (Details)", "0630 - Disclosure - Parent Company (Details)", "06302 - Disclosure - Parent Company (Details 2)", "100020 - Statement - Statement of Earnings", "100030 - Statement - Statement of Comprehensive Income", "100040 - Statement - Statement of Changes in Shareowners' Equity", "100060 - Statement - Statement of Cash Flows", "401500 - Disclosure - Shareowners' Equity (Details)", "402800 - Disclosure - Quarterly Information (unaudited) (Details)", "1001000 - Statement - Consolidated Statements Of Income", "1001000 - Statement - Consolidated Statements Of Income", "1002000 - Statement - Consolidated Statements of Comprehensive Income", "1004000 - Statement - Consolidated Statements of Cash Flows", "1005000 - Statement - Consolidated Statements of Shareholders' Equity", "1003000 - Statement - Consolidated Statements Of Operations", "1004000 - Statement - Consolidated Statements of Comprehensive Income", "1005000 - Statement - Consolidated Statements Of Cash Flows", "1006000 - Statement - Consolidated Statements Of Equity", "2420402 - Disclosure - Net Income per Share (Calculation Of Basic And Diluted Net Income Per Share) (Detail)", "010020 - Statement - Consolidated Statements of Income", "010035 - Statement - Consolidated Statements of Comprehensive Income", "010060 - Statement - Consolidated Statements of Cash Flows", "010080 - Statement - Consolidated Statements of Shareholders' Equity", "042600 - Disclosure - Parent Company (Details)", "042620 - Disclosure - Parent Company (Details 2)", "042700 - Disclosure - Quarterly Financial Data (Unaudited) (Details)", "1001000 - Statement - Consolidated Statement of Income", "1002000 - Statement - Consolidated Statement of Comprehensive Income", "1004000 - Statement - Consolidated Statement of Cash Flows", "1005000 - Statement - Consolidated Statement of Shareholders Equity", "2414402 - Disclosure - Quarterly Results (Detail)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF INCOME", "2475402 - Disclosure - Quarterly Financial Summary (Detail)", "000011 - Statement - Consolidated Statement of Income", "004010 - Disclosure - Significant Accounting Policies (Details)", "004170 - Disclosure - Quarterly Data (Unaudited) (Details)", "1001000 - Statement - CONSOLIDATED STATEMENTS OF EARNINGS", "2418402 - Disclosure - EARNINGS PER SHARE (DETAILS)", "2447402 - Disclosure - QUARTERLY RESULTS (UNAUDITED) (DETAILS)" ]
    };

declare %an:nondeterministic function local:test-acceptance-date($expected as string, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as string? := $request[2].Archives[].AcceptanceDatetime
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-components($expected as integer, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].Archives[].Components[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:sequential function local:test-components-result($expected as array, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as array := [ $request[2].Archives[].Components[].NetworkLabel ]
    let $status as integer := $request[1]
    return test:assert-eq-array($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:nondeterministic function local:test-generic-components($expected as integer, $params as object) as item
{
    let $endpoint := "components"
    let $request := test:invoke($endpoint, $params)
    let $actual as integer := count($request[2].Components[])
    let $status as integer := $request[1]
    return test:assert-eq($expected, $actual, $status, test:url($endpoint, $params))
};

declare %an:sequential function local:check($o as object) as object
{
    if (not(every $k in (keys($o) ! $o.$$) satisfies ($k instance of boolean and $k)))
    then {
            response:status-code(500);
            $o
    } else
            $o
};

local:check({
    cocacola: local:test-components(94, {
        ticker:"ko",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    cocacolaincome: local:test-components(1, {
        ticker:"ko",
        networkIdentifier:"http://www.thecocacolacompany.com/role/ConsolidatedStatementsOfIncome",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    byconcept: local:test-components-result($local:expected.byconcept, {
        tag:"DOW30",
        concept:"us-gaap:NetIncomeLoss",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
    }),
    byfyfp: local:test-components(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"Q1",
        networkIdentifier:"http://www.thecoca-colacompany.com/role/CondensedConsolidatedBalanceSheets"
    }),
    byfyfplowercase: local:test-components(1, {
        ticker:"ko",
        fiscalYear:"2012",
        fiscalPeriod:"q2",
        networkIdentifier:"http://www.thecoca-colacompany.com/role/CondensedConsolidatedBalanceSheets"
    }),
    acceptance: local:test-acceptance-date("2014-07-30T15:44:58Z", {
        aid:"0000021344-14-000029"
    }),
    generic: local:test-generic-components(1, {
      profile-name: "generic",
      aid: "0000021344-14-000008",
      role: "http://www.thecocacolacompany.com/role/DocumentAndEntityInformationDocument"
    }),
    generic-aid: local:test-generic-components(95, {
      profile-name: "generic",
      aid: "0000021344-14-000008"
    }),
    example1: test:invoke-and-assert-deep-equal(
      "components",
      {aid:"0000021344-13-000039"},
      function($b as item*) as item* { [ trim($b.Archives[].Components[], ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure", "ValidationErrors")) ] },
      test:get-expected-result("secxbrl/components-expected1.jq"),
      { NoArrayOrder: true }
    ),
    example2: test:invoke-and-assert-deep-equal(
      "components",
      {
        ticker:"ko",
        fiscalYear: "2013",
        fiscalPeriod: "FY"
      },
      function($b as item*) as item* { [ trim($b.Archives[].Components[], ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure", "ValidationErrors")) ] },
      test:get-expected-result("secxbrl/components-expected2.jq"),
      { NoArrayOrder: true }
    ),
    url: test:invoke-and-assert-deep-equal(
      "components",
      {aid:"0000021344-13-000039"},
      function($b as item*) as item* { project($b.Archives[].Components[][$$.NetworkIdentifier = "http://www.thecoca-colacompany.com/role/DocumentAndEntityInformation"], ("FactTable", "SpreadSheet", "ReportElements", "ModelStructure")) },
      {
        "FactTable" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/facttable-for-component.jq?token="||$config:test-token||"&aid=0000021344-13-000039&format=&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec",
        "SpreadSheet" : "http://rendering.secxbrl.info/#?url=http%3A%2F%2F"||request:server-name()||"%3A"||request:server-port()||"%2Fv1%2F_queries%2Fpublic%2Fapi%2Fspreadsheet-for-component.jq%3Ftoken%3D"||$config:test-token||"%26aid%3D0000021344-13-000039%26format%3D%26role%3Dhttp%253A%252F%252Fwww.thecoca-colacompany.com%252Frole%252FDocumentAndEntityInformation%26profile-name%3Dsec",
      "ReportElements" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/report-elements.jq?token=foobar&aid=0000021344-13-000039&format=&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec",
      "ModelStructure" : "http://"||request:server-name()||":"||request:server-port()||"/v1/_queries/public/api/modelstructure-for-component.jq?token="||$config:test-token||"&aid=0000021344-13-000039&format=json&role=http%3A%2F%2Fwww.thecoca-colacompany.com%2Frole%2FDocumentAndEntityInformation&profile-name=sec"
      },
      { NoArrayOrder: true }
    )
})


Update patientfilter.jq

Update patientfilter.jq

# Require jq > 1.4
# Takes open.fda.gov drug event JSON and transforms into compact flat linear records suitable for Perl recs-tocsv

# start from the .results array
|.results

# remove .patient.drug array
|map(del(.patient.drug))

# remove .patient.reaction array
|map(del(.patient.reaction))

# flattens .patient object and renames child keys with patient_ prefix
|map(with_entries(select(.key != "patient")) +
       (.patient|with_entries(.key = "patient_" + .key)))

# flattens all the objects and renames child keys
|map(with_entries(select(.key != "primarysource" and
                         .key != "sender" and
                         .key != "receiver" and
                         .key != "patient_summary" and
                         .key != "reportduplicate" and
                         .key != "patient_patientdeath")) +
       (.primarysource//{primarysource:{"NA":"NA"}}|with_entries(.key = "primarysource_" + .key)) +
       (.sender//{sender:{"NA":"NA"}}|with_entries(.key = "sender_" + .key)) +
       (.receiver//{receiver:{"NA":"NA"}}|with_entries(.key = "receiver_" + .key)) +
       (.patient_summary//{patient_summary:{"NA":"NA"}}|with_entries(.key = "patient_summary_" + .key)) +
       (.report_duplicate//{report_duplicate:{"NA":"NA"}}|with_entries(.key = "report_duplicate_" + .key))+
       (.patient_patientdeath//{patient_patientdeath:{"NA":"NA"}}|with_entries(.key = "patient_patientdeath_" + .key)))

|map(del(.primarysource_primarysource))
|map(del(.sender_sender))
|map(del(.receiver_receiver))
|map(del(.report_duplicate_report_duplicate))
|map(del(.patient_summary_patient_summary))
|map(del(.patient_patientdeath_patient_patientdeath))

# grabs all unique keys and spits out CSV
|["safetyreportid",
"authoritynumb",
"companynumb",
"duplicate",
"fulfillexpeditecriteria",
"occurcountry",
"patient_patientagegroup",
"patient_patientonsetage",
"patient_patientonsetageunit",
"patient_patientsex",
"patient_patientweight",
"patient_summary_narrativeincludeclinical",
"primarysource_literaturereference",
"primarysource_qualification",
"primarysource_reportercountry",
"primarysourcecountry",
"receiptdate",
"receiptdateformat",
"receivedate",
"receivedateformat",
"receiver_receiverorganization",
"receiver_receivertype",
"reporttype",
"safetyreportversion",
"sender_senderorganization",
"sender_sendertype",
"serious",
"seriousnesscongenitalanomali",
"seriousnessdeath",
"seriousnessdisabling",
"seriousnesshospitalization",
"seriousnesslifethreatening",
"seriousnessother",
"transmissiondate",
"transmissiondateformat"] as $keys
|$keys, (.[]|[.[$keys[]]])|@csv

# Require jq > 1.4
# Takes open.fda.gov drug event JSON and transforms into compact flat linear records suitable for Perl recs-tocsv
.[]
# start from the .results array
|.results

# remove .patient.drug array
|map(del(.patient.drug))

# remove .patient.reaction array
|map(del(.patient.reaction))

# flattens .patient object and renames child keys with patient_ prefix
|map(with_entries(select(.key != "patient")) +
       (.patient|with_entries(.key = "patient_" + .key)))

# flattens all the objects and renames child keys
|map(with_entries(select(.key != "primarysource" and
                         .key != "sender" and
                         .key != "receiver" and
                         .key != "patient_summary" and
                         .key != "reportduplicate" and
                         .key != "patient_patientdeath")) +
       (.primarysource//{primarysource:{"NA":"NA"}}|with_entries(.key = "primarysource_" + .key)) +
       (.sender//{sender:{"NA":"NA"}}|with_entries(.key = "sender_" + .key)) +
       (.receiver//{receiver:{"NA":"NA"}}|with_entries(.key = "receiver_" + .key)) +
       (.patient_summary//{patient_summary:{"NA":"NA"}}|with_entries(.key = "patient_summary_" + .key)) +
       (.report_duplicate//{report_duplicate:{"NA":"NA"}}|with_entries(.key = "report_duplicate_" + .key))+
       (.patient_patientdeath//{patient_patientdeath:{"NA":"NA"}}|with_entries(.key = "patient_patientdeath_" + .key)))

|map(del(.primarysource_primarysource))
|map(del(.sender_sender))
|map(del(.receiver_receiver))
|map(del(.report_duplicate_report_duplicate))
|map(del(.patient_summary_patient_summary))
|map(del(.patient_patientdeath_patient_patientdeath))

# grabs all unique keys and spits out CSV
|["safetyreportid",
"authoritynumb",
"companynumb",
"duplicate",
"fulfillexpeditecriteria",
"occurcountry",
"patient_patientagegroup",
"patient_patientonsetage",
"patient_patientonsetageunit",
"patient_patientsex",
"patient_patientweight",
"patient_summary_narrativeincludeclinical",
"primarysource_literaturereference",
"primarysource_qualification",
"primarysource_reportercountry",
"primarysourcecountry",
"receiptdate",
"receiptdateformat",
"receivedate",
"receivedateformat",
"receiver_receiverorganization",
"receiver_receivertype",
"reporttype",
"safetyreportversion",
"sender_senderorganization",
"sender_sendertype",
"serious",
"seriousnesscongenitalanomali",
"seriousnessdeath",
"seriousnessdisabling",
"seriousnesshospitalization",
"seriousnesslifethreatening",
"seriousnessother",
"transmissiondate",
"transmissiondateformat"] as $keys
|$keys, (.[]|[.[$keys[]]])|@csv


Fix TotalAssets regression

Fix TotalAssets regression

import module namespace facts = "http://xbrl.io/modules/bizql/facts";
import module namespace entities = "http://xbrl.io/modules/bizql/entities";
import module namespace archives = "http://xbrl.io/modules/bizql/archives";
import module namespace companies = "http://xbrl.io/modules/bizql/profiles/sec/companies";
import module namespace sec-fiscal = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core";
import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace csv = "http://zorba.io/modules/json-csv";
import module namespace session = "http://apps.28.io/session";

declare function local:to-csv($o as object*) as string
{
    string-join(
        csv:serialize(
            for $o in $o
            let $a := $o.Aspects
            return {|
                for $k in keys($a)
                return { $k : $a.$k },
                { "Unit" :  $o."Unit" },
                { "Value" : $o.Value },
                { "Decimals" : "INF" },
                { "NumReports" : $o.NumReports }
            |},
            { serialize-null-as : "" }
        )
    )
};

declare function local:to-xml($o as object*)
{
    (
    session:comment("xml"),
    <FactTable NetworkIdentifier="http://xbrl.io/TotalForConceptGroupedByFiscalPeriodForAllEntities"
            TableName="xbrl:TotalForConceptGroupedByFiscalPeriodForAllEntities">{

        for $o in $o
        let $a := $o.Aspects
        return
            <Fact>{
                (<Aspects>{
                    for $k in keys($a)
                    return
                        <Aspect>
                            <Name>{$k}</Name>
                            <Value>{$a.$k}</Value>
                        </Aspect>
                }</Aspects>,
                <Unit>{$o.Unit}</Unit>,
                <Type>{$o.Value}</Type>,
                <Value>{$o.Value}</Value>,
                <Decimals>INF</Decimals>,
                <NumReports>{$o.NumReports}</NumReports>,
                if (exists($o.Debug))
                then
                    <Debug>{
                        for $a in $o.Debug[]
                        return <InstanceURL value="{$a.Value}">{$a.InstanceURL}</InstanceURL>
                    }</Debug>
                else ()
                )
            }</Fact>
    }</FactTable>)
};

let $format  := lower-case(request:param-values("format")[1])
let $periods := let $period := upper-case(request:param-values("fiscalPeriod", "FY"))
                return
                    if ($period = ("Q1", "Q2", "Q3", "FY", "ALL"))
                    then distinct-values($period)
                    else error(xs:QName("local:INVALID-PERIOD"),
                               $period || ": fiscalPeriod value must be one of Q1, Q2, Q3, FY")
let $years   := let $years := request:param-values("fiscalYear", "ALL")
                return
                    if ($years = "ALL")
                    then $sec-fiscal:ALL_FISCAL_YEARS
                    else $years ! $$ cast as integer
let $concept := request:param-values("concept", "us-gaap:Assets")[1]
let $map     := request:param-values("map")[1]
let $tags    := request:param-values("tag")
let $debug   := request:param-values("debug")

let $json-result :=
    let $facts :=
        for $fact in sec-fiscal:facts-for-entities-and-concepts-and-fiscal-periods-and-years(
            (if (exists($tags))
                then companies:companies-for-tags(upper-case($tags))
                else entities:entities()
            )[$$.Profiles.SEC.IsTrust eq false],
            $concept,
            if ($periods = "ALL") then ("Q1", "Q2", "Q3", "FY") else $periods,
            $years,
            {|
                if (exists($map))
                then { "concept-maps" : $map }
                else ()
            |}
        )
        group by $fyf := sec-fiscal:fiscal-year($fact),
                 $fpf := sec-fiscal:fiscal-period($fact),
                 $entity:= facts:entity-for-fact($fact)
        return ($fact[$$.Aspects."xbrl:Unit" eq "iso4217:USD"], $fact)[1]
    for $fact in $facts
    group by $fyf := sec-fiscal:fiscal-year($fact),
             $fpf := sec-fiscal:fiscal-period($fact),
             $unit := $fact.Aspects."xbrl:Unit"
    order by $fyf
    return {|
        {
            Aspects : {
                "xbrl:Entity" : "bizql:AllReportingEntitiesMember",
                "dei:LegalEntityAxis" : "sec:DefaultLegalEntity",
                "xbrl:Concept" : $concept,
                "bizql:FiscalYear" : $fyf,
                "bizql:FiscalPeriod" : $fpf
            }
        },
        { "Value" : sum($fact.Value[$$ ne null]) },
        { "Type"  : "NumericValue" },
        { "Unit" : $unit },
        { "Decimals"  : "INF" },
        { "NumReports" : count(distinct-values($fact.Archive)) },

        if (exists($debug) and $debug)
        then
            {
                "Debug" : [ $fact !
                            {
                                "InstanceURL" : archives:archives(distinct-values($$.Archive)).InstanceURL,
                                "Value" : $$.Value,
                                "Decimals" : $$.Decimals
                            } ]
            }
        else ()
    |}
return
    switch ($format)
    case "xml" return {
        response:serialization-parameters({"omit-xml-declaration" : false, indent : true });
        local:to-xml($json-result)
    }
    case "text" case "csv" return {
        response:content-type("text/csv");
        response:header("Content-Disposition", "attachment; filename=Total-" || $concept || ".csv");
        local:to-csv($json-result)
    }
    case "excel" return {
        response:content-type("application/vnd.ms-excel");
        response:header("Content-Disposition", "attachment; filename=Total-" || $concept || ".csv");
        local:to-csv($json-result)
    }
    default return {
        response:content-type("application/json");
        response:serialization-parameters({"indent" : true});
        {|
            { NetworkIdentifier : "http://xbrl.io/TotalForConceptGroupedByFiscalPeriodForAllEntities" },
            { TableName : "xbrl:TotalForConceptGroupedByFiscalPeriodForAllEntities" },
            { FactTable : [ $json-result ] },
            session:comment("json")
        |}
    }

import module namespace facts = "http://xbrl.io/modules/bizql/facts";
import module namespace entities = "http://xbrl.io/modules/bizql/entities";
import module namespace archives = "http://xbrl.io/modules/bizql/archives";
import module namespace companies = "http://xbrl.io/modules/bizql/profiles/sec/companies";
import module namespace sec-fiscal = "http://xbrl.io/modules/bizql/profiles/sec/fiscal/core";
import module namespace request = "http://www.28msec.com/modules/http-request";
import module namespace response = "http://www.28msec.com/modules/http-response";
import module namespace csv = "http://zorba.io/modules/json-csv";
import module namespace session = "http://apps.28.io/session";

declare function local:to-csv($o as object*) as string
{
    string-join(
        csv:serialize(
            for $o in $o
            let $a := $o.Aspects
            return {|
                for $k in keys($a)
                return { $k : $a.$k },
                { "Unit" :  $o."Unit" },
                { "Value" : $o.Value },
                { "Decimals" : "INF" },
                { "NumReports" : $o.NumReports }
            |},
            { serialize-null-as : "" }
        )
    )
};

declare function local:to-xml($o as object*)
{
    (
    session:comment("xml"),
    <FactTable NetworkIdentifier="http://xbrl.io/TotalForConceptGroupedByFiscalPeriodForAllEntities"
            TableName="xbrl:TotalForConceptGroupedByFiscalPeriodForAllEntities">{

        for $o in $o
        let $a := $o.Aspects
        return
            <Fact>{
                (<Aspects>{
                    for $k in keys($a)
                    return
                        <Aspect>
                            <Name>{$k}</Name>
                            <Value>{$a.$k}</Value>
                        </Aspect>
                }</Aspects>,
                <Unit>{$o.Unit}</Unit>,
                <Type>{$o.Value}</Type>,
                <Value>{$o.Value}</Value>,
                <Decimals>INF</Decimals>,
                <NumReports>{$o.NumReports}</NumReports>,
                if (exists($o.Debug))
                then
                    <Debug>{
                        for $a in $o.Debug[]
                        return <InstanceURL value="{$a.Value}">{$a.InstanceURL}</InstanceURL>
                    }</Debug>
                else ()
                )
            }</Fact>
    }</FactTable>)
};

let $format  := lower-case(request:param-values("format")[1])
let $periods := let $period := upper-case(request:param-values("fiscalPeriod", "FY"))
                return
                    if ($period = ("Q1", "Q2", "Q3", "FY", "ALL"))
                    then distinct-values($period)
                    else error(xs:QName("local:INVALID-PERIOD"),
                               $period || ": fiscalPeriod value must be one of Q1, Q2, Q3, FY")
let $years   := let $years := request:param-values("fiscalYear", "ALL")
                return
                    if ($years = "ALL")
                    then ()
                    else $years ! $$ cast as integer
let $concept := request:param-values("concept", "us-gaap:Assets")[1]
let $map     := request:param-values("map")[1]
let $tags    := request:param-values("tag")
let $debug   := request:param-values("debug")

let $json-result :=
    let $facts :=
        for $fact in sec-fiscal:facts-for-entities-and-concepts-and-fiscal-periods-and-years(
            (if (exists($tags))
                then companies:companies-for-tags(upper-case($tags))
                else entities:entities()
            )[$$.Profiles.SEC.IsTrust eq false],
            $concept,
            if ($periods = "ALL") then ("Q1", "Q2", "Q3", "FY") else $periods,
            $years,
            {|
                if (exists($map))
                then { "concept-maps" : $map }
                else ()
            |}
        )
        group by $fyf := sec-fiscal:fiscal-year($fact),
                 $fpf := sec-fiscal:fiscal-period($fact),
                 $entity:= facts:entity-for-fact($fact)
        return ($fact[$$.Aspects."xbrl:Unit" eq "iso4217:USD"], $fact)[1]
    for $fact in $facts
    group by $fyf := sec-fiscal:fiscal-year($fact),
             $fpf := sec-fiscal:fiscal-period($fact),
             $unit := $fact.Aspects."xbrl:Unit"
    order by $fyf
    return {|
        {
            Aspects : {
                "xbrl:Entity" : "bizql:AllReportingEntitiesMember",
                "dei:LegalEntityAxis" : "sec:DefaultLegalEntity",
                "xbrl:Concept" : $concept,
                "bizql:FiscalYear" : $fyf,
                "bizql:FiscalPeriod" : $fpf
            }
        },
        { "Value" : sum($fact.Value[$$ ne null]) },
        { "Type"  : "NumericValue" },
        { "Unit" : $unit },
        { "Decimals"  : "INF" },
        { "NumReports" : count(distinct-values($fact.Archive)) },

        if (exists($debug) and $debug)
        then
            {
                "Debug" : [ $fact !
                            {
                                "InstanceURL" : archives:archives(distinct-values($$.Archive)).InstanceURL,
                                "Value" : $$.Value,
                                "Decimals" : $$.Decimals
                            } ]
            }
        else ()
    |}
return
    switch ($format)
    case "xml" return {
        response:serialization-parameters({"omit-xml-declaration" : false, indent : true });
        local:to-xml($json-result)
    }
    case "text" case "csv" return {
        response:content-type("text/csv");
        response:header("Content-Disposition", "attachment; filename=Total-" || $concept || ".csv");
        local:to-csv($json-result)
    }
    case "excel" return {
        response:content-type("application/vnd.ms-excel");
        response:header("Content-Disposition", "attachment; filename=Total-" || $concept || ".csv");
        local:to-csv($json-result)
    }
    default return {
        response:content-type("application/json");
        response:serialization-parameters({"indent" : true});
        {|
            { NetworkIdentifier : "http://xbrl.io/TotalForConceptGroupedByFiscalPeriodForAllEntities" },
            { TableName : "xbrl:TotalForConceptGroupedByFiscalPeriodForAllEntities" },
            { FactTable : [ $json-result ] },
            session:comment("json")
        |}
    }


Add merge/1 and merge_pages/0.

Add merge/1 and merge_pages/0.

These functions aren't yet used, but they will later make for a more
robust way of adding information to pages than using the `setpath/2` etc
functions.

#!/usr/bin/env jq
# This is a collection of filters for `jq`, intended to manipulate JSON objects
# into JSON representing a website index, for further processing in a template.

# Convert "truthy" value to actual boolean.
def bool:
    (. == {} or . == [] or . == false or . == null or . == 0) | not
;

# Determine whether an index entry was manually entered into the table of
# contents "base".
def is_manual:
    .type == "hyperlink"
;

# Determine whether a page should be uploaded and included in the table of
# contents: it should either be marked as `publish`, or be *not* marked as
# `draft` and have children that *are* marked as `publish`.
def is_publication:
    (.meta.publish|bool)
    or
    ((.meta.draft|bool|not) and ((.contents // [])|any(is_publication or is_manual))
    )
;

# Group the values of properties of an array of objects. For example, [{"a":1,
# "b":2}, {"a":3}] turns into {"a":[1, 3], "b":[3]}. This can be used to
# partition an array; example: [1,2,3] | map({(.%2|tostring):.}) | group
def group:
    reduce ([.[] | to_entries[]] | group_by(.key))[] as $group
    ( {}
    ; .[ $group[0].key ] |= (. // []) + [ $group[].value ]
    )
;

# Insert a child element at a particular path.
# Like `setpath/2`, but instead of making objects like `{"a":{"b":{…}}}`, this
# makes objects like `{"contents":[{"name":"a", "contents":[{"name":"b",…}]}]}`
def insert($path; $child):
    if ($path | length) > 0 then
        if ([.contents[]? | select(.name == $path[0])] | length) > 0 then
            (.contents[] | select(.name == $path[0])) |= (. | insert($path[1:]; $child))
        else
            (.contents |= (. // []) + [ {} | .name = $path[0] | insert($path[1:]; $child)])
        end
    else
        . * $child
    end
;

# Merge two values. If the values are both objects, the values at equal indices
# will be merged; if the values are both arrays, the arrays are concatenated;
# if the values are some other type, we only check if the values do not
# conflict.
def merge(other):
    if (. | type) == "object" and (other | type) == "object" then
        (. | to_entries) + (other | to_entries)
        | group_by(.key)
        | map({ "key": (.[0].key), "value" : (reduce .[1:][].value as $x (.[0].value; merge($x)))})
        | from_entries
    elif (. | type) == "array" and (other | type) == "array" then
        . + other
    elif . == other then
        .
    else
        error("trying to merge incompatible objects")
    end
;

# Merge an array of pages by grouping them by path and then merging all the
# pages with the same path.
def merge_pages:
    group_by(.path) | map(reduce .[] as $x ({}; merge($x)))
;

# Merge together `filetree.json` and `*.meta.json` files, with behaviour
# depending on the filename of the object. Use with the `--null-input` switch.
def process_files:
    reduce inputs as $input
        (   {}
        ;   ($input | input_filename) as $f |
            if ($f | endswith(".meta.json")) then
                insert
                ( $f | ltrimstr($prefix) | rtrimstr(".meta.json") | split("/")
                ; {"meta": $input} )
            else
                reduce $input[] as $entry
                ( .
                ;   insert
                    ( $entry.path | split("/")
                    ; $entry )
                )
            end
        )
;

# Any page gets a link to its HTML.
def add_links:
    if (has("path") and (.path | endswith(".md"))) then
        .link = (.path | rtrimstr(".md") | . + ".html")
    else
        .contents[]? |= add_links
    end
;

# Front matter is the page to be associated with the enclosing directory rather
# than itself.
def add_frontmatter:
    if has("contents") then
        ( .contents | map({(.name=="index.md" or .meta.frontmatter|tostring):.}) | group) as {$true, $false}
        | .frontmatter = $true[0]
        | .contents = ($false + $true[1:])
        | (.contents[]? |= add_frontmatter)
    else
        .
    end
;

# If marked as such, drafts can be excluded from being uploaded and included
# in the table of contents.
def add_drafts:
    if has("contents") then
        ( .contents | map({(is_publication or is_manual|tostring):.}) | group) as {$true, $false}
        | .contents = ($true // [])
        | .drafts = ($false // [])
        | (.contents[]? |= add_drafts)
    else
        .
    end
;

# A resource is anything that is neither a directory nor a page with metadata.
def add_resources:
    if has("contents") then
        ( .contents | map({(has("contents") or has("meta") or is_manual|tostring):.}) | group) as {$true, $false}
        | .contents = ($true // [])
        | .resources = ($false // [])
        | (.contents[]? |= add_resources)
    else
        .
    end;

# Combines the given stream of JSON objects by merging them, and performs the
# given operations to turn it into a proper index.
def index:
    process_files
    | add_links
    | add_frontmatter
    | add_drafts
    | add_resources
;

#!/usr/bin/env jq
# This is a collection of filters for `jq`, intended to manipulate JSON objects
# into JSON representing a website index, for further processing in a template.

# Convert "truthy" value to actual boolean.
def bool:
    (. == {} or . == [] or . == false or . == null or . == 0) | not
;

# Determine whether an index entry was manually entered into the table of
# contents "base".
def is_manual:
    .type == "hyperlink"
;

# Determine whether a page should be uploaded and included in the table of
# contents: it should either be marked as `publish`, or be *not* marked as
# `draft` and have children that *are* marked as `publish`.
def is_publication:
    (.meta.publish|bool)
    or
    ((.meta.draft|bool|not) and ((.contents // [])|any(is_publication or is_manual))
    )
;

# Group the values of properties of an array of objects. For example, [{"a":1,
# "b":2}, {"a":3}] turns into {"a":[1, 3], "b":[3]}. This can be used to
# partition an array; example: [1,2,3] | map({(.%2|tostring):.}) | group
def group:
    reduce ([.[] | to_entries[]] | group_by(.key))[] as $group
    ( {}
    ; .[ $group[0].key ] |= (. // []) + [ $group[].value ]
    )
;

# Insert a child element at a particular path.
# Like `setpath/2`, but instead of making objects like `{"a":{"b":{…}}}`, this
# makes objects like `{"contents":[{"name":"a", "contents":[{"name":"b",…}]}]}`
def insert($path; $child):
    if ($path | length) > 0 then
        if ([.contents[]? | select(.name == $path[0])] | length) > 0 then
            (.contents[] | select(.name == $path[0])) |= (. | insert($path[1:]; $child))
        else
            (.contents |= (. // []) + [ {} | .name = $path[0] | insert($path[1:]; $child)])
        end
    else
        . * $child
    end
;

# Merge together `filetree.json` and `*.meta.json` files, with behaviour
# depending on the filename of the object. Use with the `--null-input` switch.
def process_files:
    reduce inputs as $input
        (   {}
        ;   ($input | input_filename) as $f |
            if ($f | endswith(".meta.json")) then
                insert
                ( $f | ltrimstr($prefix) | rtrimstr(".meta.json") | split("/")
                ; {"meta": $input} )
            else
                reduce $input[] as $entry
                ( .
                ;   insert
                    ( $entry.path | split("/")
                    ; $entry )
                )
            end
        )
;

# Any page gets a link to its HTML.
def add_links:
    if (has("path") and (.path | endswith(".md"))) then
        .link = (.path | rtrimstr(".md") | . + ".html")
    else
        .contents[]? |= add_links
    end
;

# Front matter is the page to be associated with the enclosing directory rather
# than itself.
def add_frontmatter:
    if has("contents") then
        ( .contents | map({(.name=="index.md" or .meta.frontmatter|tostring):.}) | group) as {$true, $false}
        | .frontmatter = $true[0]
        | .contents = ($false + $true[1:])
        | (.contents[]? |= add_frontmatter)
    else
        .
    end
;

# If marked as such, drafts can be excluded from being uploaded and included
# in the table of contents.
def add_drafts:
    if has("contents") then
        ( .contents | map({(is_publication or is_manual|tostring):.}) | group) as {$true, $false}
        | .contents = ($true // [])
        | .drafts = ($false // [])
        | (.contents[]? |= add_drafts)
    else
        .
    end
;

# A resource is anything that is neither a directory nor a page with metadata.
def add_resources:
    if has("contents") then
        ( .contents | map({(has("contents") or has("meta") or is_manual|tostring):.}) | group) as {$true, $false}
        | .contents = ($true // [])
        | .resources = ($false // [])
        | (.contents[]? |= add_resources)
    else
        .
    end;

# Combines the given stream of JSON objects by merging them, and performs the
# given operations to turn it into a proper index.
def index:
    process_files
    | add_links
    | add_frontmatter
    | add_drafts
    | add_resources
;