Widget: DCTList: Difference between revisions

From LINKS Community Center
Jump to: navigation, search
Eschmidt (talk | contribs)
No edit summary
Eschmidt (talk | contribs)
No edit summary
Line 1: Line 1:
<noinclude>Development verstion of the DCT List.<br><span style="color: red; font-weight: bold;">Not ready for production!</span></noinclude>
<noinclude>Development version of the Compass.<br><strong style="color:red;">Not ready for production!</strong>
</noinclude>
<includeonly>
<includeonly>
    <link href="/resources/assets/tabulator.min.css" rel="stylesheet">
    <script type="text/javascript" src="/resources/assets/tabulator.min.js"></script>
     <style>
     <style>
         #dct-list-wrapper {
         :root {
             font-family: 'Open Sans';
            --compass-color: #C31980;
             margin-top: 4em;
             --handbook-color: #C35C19;
             --safe-color: #80C319;
         }
         }


         #dct-list-wrapper h1,
         .plus.icon {
        #dct-list-wrapper h2,
            color: currentColor;
        #dct-list-wrapper h3,
            display: inline-block;
        #dct-list-wrapper h4 {
             position: relative;
             font-family: 'Raleway';
             top: -.3em;
             font-weight: 300;
             margin-left: .5em;
             letter-spacing: .06em;
         }
         }


         #dct-list-wrapper h1,
         .plus.icon::before {
        #dct-filters h2 {
             content: '';
            display: flex;
             position: absolute;
            align-items: center;
             width: 1em;
        }
             height: 1px;
 
             background-color: currentColor;
        #dct-intro {
            font-size: larger;
            margin-bottom: 4em;
        }
 
        #dct-list-wrapper h1 {
             color: var(--links-blue);
        }
        #dct-list-wrapper h1 svg {
             height: 2.5em;
             width: 2.5em;
             fill: var(--links-blue);
             margin-right: .5em;
         }
         }


         #filter-bar { position: relative; margin: 1em 0 2em 0; }
         .plus.icon::after {
        #dct-filters {
            content: '';
             position: absolute;
             position: absolute;
            top: 0;
             width: 1em;
            right: 0;
             height: 1px;
            z-index: 100;
             background-color: currentColor;
            padding: 2em;
             transform: rotate(90deg);
             width: 45vw;
             transition: all 0.4s ease;
             background: #fff;
            border: 1px solid var(--links-blue);
             clip-path: inset(0 0 100% 100%);
             box-shadow: -10px 10px 10px 5px rgb(0 0 0 / 10%);
             transition: all 400ms ease-in-out;
        }
        #dct-filters.open {
            clip-path: inset(0 0 -50px -50px);
        }
 
        #close-filter-button {
            display: block;
            cursor: pointer;
            font-size: 2.5em;
            line-height: .7em;
            margin-top: -.2em;
            font-weight: 100;
            color: var(--links-blue);
         }
         }


         #dct-filters h2 svg {
         h2.opened .plus.icon::after,
            height: 1.5em;
        h3.opened .plus.icon::after {
            width: 1.5em;
             transform: rotate(0);
            margin-right: .5em;
             margin-left: -.2em;
         }
         }


         .large-button {
         #cmp-container {
            border: 1px solid var(--links-blue);
            font-size: 1.5em;
             font-family: 'Open Sans';
             font-family: 'Open Sans';
             font-weight: 100;
             font-size: 120%;
            margin-bottom: 2em;
            padding: 0.3em 0.8em;
            color: var(--links-blue);
            background: transparent;
            font-variant: small-caps;
            display: inline-block;
            transition: all 200ms ease-in-out;
         }
         }


         .large-button:hover {
         #cmp-container h1,
             background-color: var(--links-blue);
        #cmp-container h2,
             color: #fff;
        #cmp-container h3 {
             font-family: 'Raleway';
            font-weight: 300;
            letter-spacing: .06em;
             position: relative;
         }
         }


         #dct-list-wrapper h2 {
         #cmp-container h1 {
             margin-bottom: 1em;
             letter-spacing: .06em;
         }
         }


         .filter-button-wrapper {
         #cmp-container h2 {
             margin-bottom: 1em;
             margin-top: 2em;
            padding-bottom: 5px;
            color: var(--compass-color);
            cursor: pointer;
         }
         }


         .filter-wrapper button {
         #cmp-container h2::before {
             border: 0 none;
             content: '';
             color: var(--links-blue);
            display: block;
             background-color: transparent;
            position: absolute;
             font-variant: small-caps;
            left: 0;
             font-size: 1.2em;
            bottom: 0;
             text-decoration: underline;
             width: 100%;
             padding: 0;
            height: 1px;
             background-color: currentColor;
             letter-spacing: .06em;
             transition: all 0.4s ease;
             transform: scaleX(0);
             transform-origin: bottom left;
         }
         }


         .filter-wrapper .filter-container { display: none; }
         #cmp-container h2.opened::before {
        .filter-wrapper.open .filter-container { display: block; }
             transform: scaleX(1);
 
        .filter-wrapper h4 {
             display: flex;
            justify-content: space-between;
            align-items: flex-start;
         }
         }


         .filter-wrapper .filter-toggle svg {
         #cmp-container h3 {
             width: 1.5em;
             font-weight: 1.4em;
            height: 1.5em;
            fill: var(--links-blue);
             cursor: pointer;
             cursor: pointer;
         }
         }


         .filter-wrapper.open .filter-toggle {
         .subtheme {
             transform: rotate(180deg);
             margin-bottom: 2em;
         }
         }


         .filter-content {
         .question {
             font-size: 1.2em;
             font-size: 1.2em;
             margin-bottom: 4em;
             padding-left: 2em;
             display: grid;
             cursor: pointer;
             grid-template-columns: repeat(auto-fit, minmax(14em, 1fr));
             margin-bottom: 1em;
            gap: 1em 1em;
            align-items: start;
         }
         }


         .filter-group-header {
         .question::before {
            grid-column: 1/-1;
             content: "→ ";
            margin-bottom: -.5em;
            margin-top: 1em;
            font-family: 'Raleway';
            font-weight: 200;
             letter-spacing: .06em;
         }
         }


         .filter-group-start {
         .answer {
             grid-column-start: 1;
             margin: 1.4em;
            font-size: 1.2rem;
         }
         }


         .filter-content input[type="checkbox"] {
         .subtheme,
             margin-right: .5em;
        .question,
        .answer {
             display: none;
         }
         }


         .filter-content img {
         .subtheme.opened,
            width: 2em;
        .question.opened,
            height: 2em;
        .answer.opened {
             margin: 0 .5em 0 0;
             display: block;
         }
         }


         .subfunc-filter-block {
         .answer-wrapper {
             font-size: smaller;
             display: grid;
             padding-left: 1em;
            grid-template-columns: 1fr 1fr;
             gap: 1em;
         }
         }


         #dct-tabulator.tabulator {
         .choice {
             border: 0 none;
             display: flex;
             background-color: transparent;
             flex-flow: row nowrap;
             font-size: 1.2em;
             justify-content: flex-start;
            align-items: flex-start;
         }
         }


         #dct-tabulator.tabulator .tabulator-header {
         .choice .button {
             border-bottom: 4px double var(--links-blue);
             flex: 0 0 8.2em;
             background-color: transparent;
            white-space: nowrap;
            transition: all 250ms;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 1em;
             border: 1px solid currentColor;
         }
         }


         #dct-tabulator.tabulator .tabulator-header .tabulator-col {
         .choice .desc {
             border-right: 0 none;
            font-size: 75%;
             background: transparent;
             padding-left: 1em;
             transition: all 250ms;
         }
         }


         #dct-tabulator.tabulator .tabulator-header .tabulator-col-content {
         .choice:hover .desc {
             padding: .5em 0;
             font-weight: 600;
         }
         }


         #dct-tabulator.tabulator .tabulator-header .tabulator-col-sorter {
         .choice.tech {
             right: 1em;
             color: var(--links-blue);
         }
         }


         #dct-tabulator.tabulator .tabulator-row {
         .choice.guide {
             background-color: transparent;
             color: var(--links-orange);
         }
         }


         #dct-tabulator.tabulator .tabulator-cell {
         .choice.case {
             border-right: 0 none;
             color: var(--links-cyan);
            border-top: 1px solid var(--links-blue);
            padding: .5em 0;
         }
         }


         .data-source-img,
         .choice.handbook {
        .func-img {
             color: var(--handbook-color);
             width: 1.2rem;
            height: 1.2rem;
            margin-right: .4rem;
            margin-bottom: .4rem;
            filter: grayscale(1);
        }
        .func-img {
            width: 1.5rem;
            height: 1.5rem;
         }
         }


         #dct-tabulator img.unselected {
         .choice.safe {
             opacity: .15;
             color: var(--safe-color);
         }
         }


         .sources-collapse {
         .choice.tech:hover .button {
             display: inline-block;
             background-color: var(--links-blue);
            color: #fff;
         }
         }


         .sources-collapse-toggle {
         .choice.guide:hover .button {
             display: inline-block;
             background-color: var(--links-orange);
            width: 2em;
             color: #fff;
             height: 2em;
         }
         }


         #dct-tabulator .tabulator-row .tabulator-responsive-collapse {
         .choice.case:hover .button {
             padding: 0 .5em 2em 0;
             background-color: var(--links-cyan);
             border: 0 none;
             color: #fff;
         }
         }


         #filter-summary { margin-right: 1em; }
         .choice.handbook:hover .button {
 
            background-color: var(--handbook-color);
        #filter-summary table tr td { vertical-align: top; }
             color: #fff;
        #filter-summary table tr td:first-of-type { padding-right: 10px; }
       
        #dct-tabulator .tabulator-row .tabulator-responsive-collapse table {
             font-size: smaller;
         }
         }


         #filter-summary table tr td strong,
         .choice.safe:hover .button {
        #dct-tabulator .tabulator-row .tabulator-responsive-collapse table tr td strong {
            background-color: var(--safe-color);
             font-weight: 300;
             color: #fff;
         }
         }
     </style>
     </style>


     <script>
     <div id="cmp-container">
         'use strict';
         <div class="theme">
            <h2>Engaging Citizens</h2>
            <div class="subtheme">
                <h3>Collecting and Analysing Information from SMCS</h3>
                <div class="question" data-aspect="Social">
                    Whom do you want to include in your collection and analysis of information, and from whom do you
                    want to collect and analyse information?


        const FUNC_KEY = 'functions';
                    <div class="answer">
        const DESC_KEY = 'description';
                        <div class="answer-wrapper">
                            <div class="choice case" id="q1-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Collecting and Analysing Information from SMCS”. Also the
                                    question, which vulnerable groups (disabled, migrants, minorities,…) were
                                    specifically involved in social media and crowdsourcing activities is answered.
                                </div>
                            </div>
                            <div class="choice guide" id="q1-guide">
                                <div class="button">Guidelines</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How to collect and analyse information from citizens?


        /** @typedef {Map<string, { [DESC_KEY]: string, [FUNC_KEY]: string[] }>} FuncData */
                    <div class="answer">
                        <div class="answer-wrapper">
                            <div class="choice tech" id="q2-tech">
                                <div class="button">Technologies</div>
                                <div class="desc">
                                    It allows filtering for software that covers the functions Search &
                                    Monitor and Analysis of information from social media.
                                </div>
                            </div>
                            <div class="choice guide" id="q2-guide">
                                <div class="button">Guidelines</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice case" id="q2-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Collecting and Analysing Information from SMCS”. Also the
                                    question, which specific functionality SMCS has been used for and how (e.g.
                                    gathering of information) is answered.
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="subtheme">
                <h3>Mobilising Citizens</h3>
                <div class="question" data-aspect="Social">
                    Who do you want to mobilise, and from whom would you want mobility-affiliated information?


        /** @type FuncData */
                    <div class="answer">
        const functionsData = new Map();    // Data on functions and function categories
                        <div class="answer-wrapper">
                            <div class="choice case" id="q3-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Mobilising Citizens”.
                                </div>
                            </div>
                            <div class="choice handbook" id="q3-handbook">
                                <div class="button">Citizens Handbook</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How can you mobilise the citizens?


        /**
                    <div class="answer">
        * @typedef {Object} DCT
                        <div class="answer-wrapper">
        * @property {string} name
                            <div class="choice tech" id="q4-tech">
        * @property {string} url
                                <div class="button">Technologies</div>
        * @property {string[]} dataSources
                                <div class="desc">
        * @property {string[]} businessModel
                                    It allows filtering for software that covers the function Post &
        * @property {FuncData} functions
                                    Schedule, which aims at publishing information on social media.
        * @property {string} usedByDmo
                                </div>
        * @property {string} logo
                            </div>
        */
                            <div class="choice guide" id="q4-guide">
                                <div class="button">Guidelines</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice case" id="q4-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Mobilising Citizens”.
                                </div>
                            </div>
                            <div class="choice safe" id="q4-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="subtheme">
                <h3>Mobilising Volunteers</h3>
                <div class="question" data-aspect="Social">
                    Who do you want to mobilise (interested in volunteering), and from whom in such volunteering
                    positions would you want mobility-affiliated information?


        let table;  // Tabulator instance
                    <div class="answer">
                        <div class="answer-wrapper">
                            <div class="choice guide" id="q5-guide">
                                <div class="button">Guidelines</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice case" id="q5-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Mobilising Volunteers”.
                                </div>
                            </div>
                            <div class="choice handbook" id="q5-handbook">
                                <div class="button">Citizens Handbook</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice safe" id="q5-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How can you mobilise the volunteers?


        // This defines how sources are grouped in the filter.
                    <div class="answer">
        // Any source not listed here will be added to the last group entitled "More platforms".
                        <div class="answer-wrapper">
        const sourcesLayout = [
                            <div class="choice tech" id="q6-tech">
            {
                                <div class="button">Technologies</div>
                title: 'General',
                                <div class="desc">
                sources: ['Crowd', 'Web']
                                    It allows filtering for software that covers the function Post &
            },
                                    Schedule, which aims at publishing information on social media.
            {
                                </div>
                title: 'Platforms',
                            </div>
                 sources: ['Facebook', 'Twitter', 'Instagram', 'YouTube']
                            <div class="choice guide" id="q6-guide">
             }
                                <div class="button">Guidelines</div>
         ];
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice case" id="q6-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Mobilising Volunteers”.
                                </div>
                            </div>
                            <div class="choice safe" id="q6-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                 </div>
             </div>
         </div>


         // This object defines the sort order of function categories and matches them to icons.
         <div class="theme">
        const fnImages = {
             <h2>Improving Communication</h2>
             'Search and Monitor':  '/index.php/Special:FilePath/File:Func_search.svg',
             <div class="subtheme">
             'Post and Schedule':    '/index.php/Special:FilePath/File:Func_post.svg',
                <h3>Making Information Accessible</h3>
            'Analysis':            '/index.php/Special:FilePath/File:Func_analysis.svg',
                <div class="question" data-aspect="Social">
            'Metrics':              '/index.php/Special:FilePath/File:Func_metrics.svg',
                    Who do you want to access your information, and from whom do you want to access information?
            'Report':              '/index.php/Special:FilePath/File:Func_report.svg',
            'Collaboration':        '/index.php/Special:FilePath/File:Func_collaboration.svg',
            'Interoperability':    '/index.php/Special:FilePath/File:Func_interoperability.svg',
            'Meta':                '/index.php/Special:FilePath/File:Func_meta.svg',
        };


        // Helpers
                    <div class="answer">
        const escapeAttr = text => text ? text.replace(/\W/g, '-') : text;
                        <div class="answer-wrapper">
        const getFilePath = title => title ? '/index.php/Special:FilePath/' + title : title;
                            <div class="choice case" id="q7-case">
        const getQueryUrl = query => '/api.php?action=ask&format=json&query=' + encodeURIComponent(query);
                                <div class="button">Use Cases</div>
        const removePrefix = str => str.substring(str.indexOf(':') + 1);
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Making Information Accessible”.
                                </div>
                            </div>
                            <div class="choice handbook" id="q7-handbook">
                                <div class="button">Citizens Handbook</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice safe" id="q7-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How can you make your information accessible?


        // Fetches platform / data source information.
                    <div class="answer">
        async function getSources() {
                        <div class="answer-wrapper">
             const IMG_KEY = 'IMAGE';
                            <div class="choice tech" id="q8-tech">
             const sourceResponse = await fetch(
                                <div class="button">Technologies</div>
                 getQueryUrl('[[Category:Social media platform]]|?' + IMG_KEY)
                                <div class="desc">
            ).then(response => response.json());
                                    It allows filtering for software that covers the function Post &
                                    Schedule, which aims at publishing information on social media.
                                </div>
                            </div>
                            <div class="choice case" id="q8-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Making Information Accessible”.
                                </div>
                            </div>
                            <div class="choice handbook" id="q8-handbook">
                                <div class="button">Citizens Handbook</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice safe" id="q8-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
             </div>
             <div class="subtheme">
                 <h3>Targeting Communication</h3>
                <div class="question" data-aspect="Social">
                    Who do you want to target with your communication plan?


            return Object.keys(sourceResponse.query.results).map(platform => {
                    <div class="answer">
                const img = sourceResponse.query.results[platform].printouts[IMG_KEY][0];
                        <div class="answer-wrapper">
                return {
                            <div class="choice case" id="q9-case">
                    name: platform,
                                <div class="button">Use Cases</div>
                     image: img ? getFilePath(img.fulltext) : undefined
                                <div class="desc">
                 };
                                    It provides helpful information from social media and crowdsourcing
            });
                                    examples around the theme “Targeting Communication”.
        }
                                </div>
                            </div>
                            <div class="choice handbook" id="q9-handbook">
                                <div class="button">Citizens Handbook</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                            <div class="choice safe" id="q9-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                     </div>
                 </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How can you target your communication?


        // Fetches DCTs and their functions.
                    <div class="answer">
        async function getDcts() {
                        <div class="answer-wrapper">
            const functionsQuery = '[[Category:Function_category]]'
                            <div class="choice tech" id="q10-tech">
                                + '|?-Subproperty_of=' + FUNC_KEY
                                <div class="button">Technologies</div>
                                + '|?Has_description=' + DESC_KEY;
                                <div class="desc">
            const functionsQueryResponse = await fetch(getQueryUrl(functionsQuery)).then(response => response.json());
                                    It allows filtering for software that covers the function Post &
       
                                    Schedule, which aims at publishing information on social media. It is also possible
            const sortOrder = Object.keys(fnImages);
                                    to filter by different platforms.
            Object.entries(functionsQueryResponse.query.results)
                                </div>
                .map(([key, value]) => ([removePrefix(key), value]))
                            </div>
                .sort(([keyA,], [keyB,]) => sortOrder.indexOf(keyA) - sortOrder.indexOf(keyB))
                            <div class="choice guide" id="q10-guide">
                .forEach(([categoryName, results]) => {
                                <div class="button">Guidelines</div>
                    functionsData.set(
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                        categoryName,
                                    consectetur, adipisci velit...</div>
                        {
                            </div>
                             [DESC_KEY]: results.printouts[DESC_KEY][0],
                            <div class="choice case" id="q10-case">
                            [FUNC_KEY]: results.printouts[FUNC_KEY].map(func => removePrefix(func.fulltext))
                                <div class="button">Use Cases</div>
                         }
                                <div class="desc">
                     );
                                    It provides helpful information from social media and crowdsourcing
                 });
                                    examples around the theme “Targeting Communication”.
                                </div>
                            </div>
                             <div class="choice safe" id="q10-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                         </div>
                     </div>
                </div>
            </div>
            <div class="subtheme">
                 <h3>Ensuring Credible Information</h3>
                <div class="question" data-aspect="Social">
                    Who do you want to share credible information with, and from whom do you receive credible
                    information?


            // TODO: Remove properties that are not relevant for the filter? (e.g. 'Supported content types')
                    <div class="answer">
            const allFunctions = Array.from(functionsData.values(), entry => entry[FUNC_KEY]).flat();
                        <div class="answer-wrapper">
                            <div class="choice case" id="q11-case">
                                <div class="button">Use Cases</div>
                                <div class="desc">
                                    It provides helpful information from social media and crowdsourcing
                                    examples around the theme “Ensuring Credible Information”.
                                </div>
                            </div>
                            <div class="choice safe" id="q11-safe">
                                <div class="button">Feel Safe</div>
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                                    consectetur, adipisci velit...</div>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="question" data-aspect="Socio-/Technical">
                    How can you ensure credible information exchange?


            const DATASRC_PROP = 'Data Sources';
                    <div class="answer">
            const IMG_PROP = 'Image';
                        <div class="answer-wrapper">
            const BUSINESS_PROP = 'Business model';
                            <div class="choice tech" id="q12-tech">
            const DMO_PROP = 'Used by Practitioners';
                                <div class="button">Technologies</div>
            const dctQuery = '[[Category:Disaster Community Technology]]'
                                <div class="desc">
                          + '[[Is Archived::No]]'
                                    There are no mature technologies on the market yet that can examine
                          + '|limit=500'
                                    information for credibility.
                          + '|?' + IMG_PROP
                                </div>
                          + '|?' + DATASRC_PROP
                            </div>
                          + '|?' + BUSINESS_PROP
                            <div class="choice guide" id="q12-guide">
                          + '|?' + DMO_PROP
                                <div class="button">Guidelines</div>
                          + '|?' + allFunctions.join('|?');
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
 
                                    consectetur, adipisci velit...</div>
            const dctResponse = await fetch(getQueryUrl(dctQuery)).then(response => response.json());
                            </div>
 
                            <div class="choice case" id="q12-case">
            const dctList = Object.keys(dctResponse.query.results).map(dctName => {
                                <div class="button">Use Cases</div>
                const dctResult = dctResponse.query.results[dctName];
                                <div class="desc">
 
                                    It provides helpful information from social media and crowdsourcing
                /** @type {DCT} */
                                    examples around the theme “Ensuring Credible Information”.
                const dct = {};
                                </div>
                dct.name = dctName;
                            </div>
                dct.url = dctResult.fullurl;
                            <div class="choice safe" id="q12-safe">
                dct.dataSources = dctResult.printouts[DATASRC_PROP].map(source => source.fulltext).sort();
                                <div class="button">Feel Safe</div>
                dct.businessModel = dctResult.printouts[BUSINESS_PROP].map(bModel => bModel.fulltext);
                                <div class="desc">Neque porro quisquam est qui dolorem ipsum quia dolor sit amet,
                dct.logo = dctResult.printouts[IMG_PROP][0] ? getFilePath(dctResult.printouts[IMG_PROP][0].fulltext) : undefined;
                                    consectetur, adipisci velit...</div>
                dct.usedByDmo = dctResult.printouts[DMO_PROP][0] ? dctResult.printouts[DMO_PROP][0].fulltext : 'Unknown';
                            </div>
 
                         </div>
                dct.functions = new Map();
                     </div>
                functionsData.forEach((categoryData, funcCategory) => {
                 </div>
                    const confirmedFunctions = categoryData[FUNC_KEY]
             </div>
                        .filter(func => dctResult.printouts[func][0] && dctResult.printouts[func][0].fulltext.toLowerCase() === 'yes');
         </div>
 
    </div>
                    if (confirmedFunctions.length > 0) {
    <script>
                         dct.functions.set(funcCategory, { [DESC_KEY]: categoryData[DESC_KEY], [FUNC_KEY]: confirmedFunctions });
         // Attach plus icon to the title of expandable blocks.
                     }
        document.querySelectorAll('.theme > h2, .subtheme > h3').forEach(el => {
                 });
            const icon = document.createElement('div');
 
            icon.classList.add('plus', 'icon');
                return dct;
            el.appendChild(icon);
             });
        });
 
            return dctList;
         }
 
        /**
        * @param {DCT} dct
        */
         function dctFilter(dct, filterState) {
            // If filtering property is empty, don't apply the filter (set the check to true).
            // Passing an empty object (as with applyFilters(true)) should result in an unfiltered table.
 
            let functionsCheck = true;
            if (filterState.functions) {
                // If filterState has a category but subfunctions array is empty, we only care about the category.
                const emptyCategories = [], nonemptyCategories = [];
                Array.from(filterState.functions).forEach(([key, subs]) => {
                    if (subs.length > 0) { nonemptyCategories.push(key); } else { emptyCategories.push(key); }
                });
               
                // Empty categories should only be checked by their name, nonempty - ONLY by subfunctions.
                const checkEmpty = emptyCategories.some(cat => dct.functions.has(cat));
                const checkNonempty = nonemptyCategories.some(cat => {
                    const selectedSubs = filterState.functions.get(cat);
                    const dctCat = dct.functions.get(cat);
                    return dctCat && dctCat[FUNC_KEY] && dctCat[FUNC_KEY].some(sub => selectedSubs.includes(sub));
                });


                 functionsCheck = checkEmpty || checkNonempty;
        // Set up toggles for expandable blocks.
        document.getElementById('cmp-container').addEventListener('click', event => {
            if (event.target.tagName === 'H2') {
                event.target.classList.toggle('opened');
                 event.target.closest('.theme').querySelectorAll('.subtheme').forEach(el => el.classList.toggle('opened'));
             }
             }
 
             if (event.target.tagName === 'H3') {
             // const functionsCheck = filterState.functions
                 event.target.classList.toggle('opened');
            //    ? dctFunctions.some(func => filterState.functions.includes(func))
                 event.target.closest('.subtheme').querySelectorAll('.question').forEach(el => el.classList.toggle('opened'));
            //    : true;
            const sourcesCheck = filterState.dataSources
                 ? dct.dataSources.some(source => filterState.dataSources.includes(source))
                : true;
            const businessModelCheck = filterState.businessModel
                 ? dct.businessModel.some(bm => filterState.businessModel.includes(bm))
                : true;
            const dmUseCheck = filterState.usedByDmo
                ? filterState.usedByDmo.includes(dct.usedByDmo)
                : true;
            return sourcesCheck && functionsCheck && businessModelCheck && dmUseCheck;
        }
 
        function applyFilters(clear) {
            if (!table) return;
 
            // If clear=true, pass empty object to the filter to disable it.
            if (clear) {
                table.setFilter(dctFilter, {});
                return;
             }
             }
 
             if (event.target.classList.contains('question')) {
            /** @type {Partial<Omit<DCT, 'usedByDmo'> & { usedByDmo: string[]>}} */
                 event.target.querySelector('.answer')?.classList.toggle('opened');
            const filterState = {};
 
             if (
                // Don't pass the filter on if everything is selected.
                document.querySelectorAll('#functions-filter input[type="checkbox"]').length !==
                document.querySelectorAll('#functions-filter input[type="checkbox"]:checked').length
            ) {  
                 const functionFilterBlocks = document.querySelectorAll('#functions-filter .func-filter-block');
                const funcOpts = new Map();
                functionFilterBlocks.forEach(block => {
                    const cat = block.querySelector('input.func-cat');
                    if (cat.checked) {
                        funcOpts.set(
                            cat.value,
                            Array.from(block.querySelectorAll('.subfunc-filter-block input[type="checkbox"]:checked'))
                                .map(box => box.value)
                        );
                    }
                });
                filterState.functions = funcOpts;
 
                // TEMPORARY! Restores original functionality. Delete after fixing the 'dataFiltered' hook.
                // filterState.functions = Array.from(funcOpts).flat(2);
             }
             }
        });


            const sourceOptions = Array.from(document.querySelectorAll('#data-source-filter input[type="checkbox"]'));
        const search = 'Search and Monitor',
             const selectedSources = sourceOptions.filter(checkbox => checkbox.checked).map(checkbox => checkbox.value);
             post = 'Post and Schedule',
             filterState.dataSources = selectedSources.length === sourceOptions.length ? undefined : selectedSources;
             analysis = 'Analysis',
 
             metrics = 'Metrics',
             const bmOptions = Array.from(document.querySelectorAll('#business-model-filter input[type="checkbox"]'));
             report = 'Report',
             const selectedBModels = bmOptions.filter(checkbox => checkbox.checked).map(checkbox => checkbox.value);
             collab = 'Collaboration',
            filterState.businessModel = selectedBModels.length === bmOptions.length ? undefined : selectedBModels;
             interop = 'Interoperability',
 
             meta = 'Meta';
             const dmUseOptions = Array.from(document.querySelectorAll('#dm-use-filter input[type="checkbox"]'));
            const selectedDmUseOptions = dmUseOptions.filter(checkbox => checkbox.checked).map(checkbox => checkbox.value);
            filterState.usedByDmo = selectedDmUseOptions.length === dmUseOptions.length ? undefined : selectedDmUseOptions;
 
            table.setFilter(dctFilter, filterState);
        }
 
        // Load data and build page.
        Promise.all([getSources(), getDcts()]).then(data => {
             const [dataSources, dcts] = data;
 
            // Set up functions filter
            let funcFilterHtml = '';
             Array.from(functionsData).forEach(([fnCat, fnInfo], index) => {
                const identifier = 'func-filter-' + escapeAttr(fnCat);
                funcFilterHtml +=
                    `<div class="func-filter-block">
                        <div>
                            <input type="checkbox" checked id="${identifier}" value="${fnCat}" class="func-cat">
                            <label for="${identifier}"><img src="${fnImages[fnCat]}"> ${fnCat}</label>
                        </div>`;


                // add subfunctions
        // Click actions.
                if (index < 4) {
        const actions = {
                    funcFilterHtml += '<div class="subfunc-filter-block">';
            'q2-tech': {
                     for (const func of fnInfo.functions) {
                filter: {
                         const subfuncId = 'subfunc-filter-' + escapeAttr(func);
                     functions: {
                         funcFilterHtml +=
                         [search]: true,
                            `<div>
                        [analysis]: true,
                                <input type="checkbox" checked id="${subfuncId}" value="${func}">
                        [post]: false,
                                <label for="${subfuncId}">${func}</label>
                         [metrics]: false,
                            </div>`;
                        [report]: false,
                        [collab]: false,
                        [interop]: false,
                        [meta]: false,
                     }
                     }
                    funcFilterHtml += '</div>';
                 }
                 }
                funcFilterHtml += '</div>';
             },
             });
             'q4-tech': {
             document.getElementById('functions-filter').innerHTML = funcFilterHtml;
                filter: {
 
                    functions: {
            // Set up sources filter
                        [search]: false,
            const groupedSources = [];
                        [analysis]: false,
            const sourcesCopy = Array.from(dataSources);
                        [post]: true,
            for (const layoutGroup of sourcesLayout) {
                        [metrics]: false,
                const group = [];
                        [report]: false,
                for (const source of layoutGroup.sources) {
                        [collab]: false,
                    let idx = sourcesCopy.findIndex(src => src.name === source);
                        [interop]: false,
                    if (idx !== -1) { group.push(sourcesCopy.splice(idx, 1)[0]); }
                        [meta]: false,
                    }
                 }
                 }
                if (group.length > 0) { groupedSources.push({ title: layoutGroup.title, sources: group }); }
             },
             }
             'q6-tech': {
            groupedSources.push({ title: 'More platforms', sources: sourcesCopy });
                 filter: {
 
                     functions: {
            let dataSourceFilterHtml = '';
                         [search]: false,
             groupedSources.forEach(group => {
                         [analysis]: false,
                dataSourceFilterHtml += '<div class="filter-group-header">' + group.title + '</div>';
                         [post]: true,
                dataSourceFilterHtml += group.sources.reduce((acc, curr, idx) => {
                         [metrics]: false,
                    const identifier = escapeAttr(curr.name);
                         [report]: false,
                    return acc +
                         [collab]: false,
                        '<div ' + (idx === 0 ? ' class="filter-group-start">' : '>') +
                         [interop]: false,
                        '<input type="checkbox" id="filter-' + identifier + '" value="' + curr.name + '" checked>' +
                         [meta]: false,
                        '<label for="filter-' + identifier + '"><img src="' + curr.image + '"> ' + curr.name + '</label></div>'
                 }, '');
            });
            document.getElementById('data-source-filter').innerHTML = dataSourceFilterHtml;
 
            // TODO: Fetch from server?
            const FREE_KEY = 'Freeware', FREE_PLAN_KEY = 'Free edition';
            const bModels = [FREE_KEY, FREE_PLAN_KEY, 'Paid subscription', 'Other'];
            let bModelFilterHtml = bModels.reduce((acc, curr) => {
                const identifier = escapeAttr(curr);
                return acc
                     + '<div><input type="checkbox" checked id="bm-filter-' + identifier
                    + '" value="' + curr + '">'
                    + '<label for="bm-filter-' + identifier + '">' + curr
                    + '</label></div>'
            }, '');
            document.getElementById('business-model-filter').innerHTML = bModelFilterHtml;
 
            const dmUse = ['Yes', 'Yes with Use Case', 'Unknown'];
            let dmUseFilterHtml = dmUse.reduce((acc, curr) => {
                const identifier = escapeAttr(curr);
                return acc
                    + '<div><input type="checkbox" checked id="dm-use-filter-' + identifier
                    + '" value="' + curr + '">'
                    + '<label for="dm-use-filter-' + identifier + '">' + curr + '</label></div>'
            }, '');
            document.getElementById('dm-use-filter').innerHTML = dmUseFilterHtml;
 
            // Set up table.
            const tabulator = new Tabulator("#dct-tabulator", {
                layout: 'fitColumns',
                responsiveLayout: 'collapse',
                columns: [
                    {
                         title: 'Name',
                         field: 'name',
                         minWidth: 300, // required for responsiveness when using fitColumns
                         formatter: function (cell) {
                            /** @type {DCT} */
                            const dct = cell.getData();
                            let out = '<a href="' + dct.url + '">' + dct.name + '</a><br>';
                            if (dct.businessModel.includes(FREE_KEY)) {
                                out += '<small><span class="badge badge-success">' + FREE_KEY + '</span></small> ';
                            }
                            if (dct.businessModel.includes(FREE_PLAN_KEY)) {
                                out += '<small><span class="badge badge-success">' + FREE_PLAN_KEY + '</span></small> ';
                            }
                            if (dct.usedByDmo.toLowerCase() === 'yes') {
                                out += '<small><span class="badge badge-danger">Used by practitioners</span></small> ';
                            }
                            if (dct.usedByDmo.toLowerCase() === 'yes with use case') {
                                out += '<small><span class="badge badge-danger">Used by practitioners</span></small>';
                                out += ' <small><span class="badge badge-warning">Use case</span></small>'
                            }
                            return out;
                        }
                    },
                    {
                        title: 'Functions',
                         field: 'functions',
                         minWidth: 300, // required for responsiveness when using fitColumns
                         cssClass: 'functions-cell',
                         formatter: function (cell) {
                            return Array.from(cell.getValue().keys())
                                .map(fn => `<img class="func-img" src="${fnImages[fn]}" data-value="${fn}" alt="${fn}" title="${fn}">`)
                                .join('');
                        }
                    },
                    {
                        title: 'Supported Platforms',
                        field: 'dataSources',
                        minWidth: 300, // required for responsiveness when using fitColumns
                        cssClass: 'data-sources-cell',
                        formatter: function (cell) {
                            const val = cell.getValue();
                            let out = '<div>';
 
                            groupedSources.forEach((group, gIndex) => {
                                // if (gIndex === groupedSources.length - 1) { out += '<div class="sources-collapse">'; }
                                out += group.sources.reduce((prev, curr) => {
                                    const idx = val.findIndex(src => src === curr.name);
                                    if (idx === -1) {
                                        return prev;
                                    } else {
                                        return curr.image
                                            ? prev + '<img class="data-source-img" data-value="' + curr.name
                                            + '" src="' + curr.image
                                            + '" alt="' + curr.name
                                            + '" title="' + curr.name + '">'
                                            : prev + ' ' + curr.name;
                                    }
                                }, '');
                                if (gIndex === groupedSources.length - 1) {
                                    // out += '<span class="sources-collapse-toggle"></span>';
                                    // out += '</div>';
                                }
                            });
                            return out + '</div>';
                        }
                     }
                     }
                 ]
                 }
             });
             },
 
             'q8-tech': {
             tabulator.on('tableBuilt', () => {
                 filter: {
                 tabulator.setData(dcts);
                     functions: {
                table = tabulator;
                         [search]: false,
 
                         [analysis]: false,
                // Set up the table if parameter was passed.
                         [post]: true,
                const params = new URLSearchParams(window.location.search);
                        [metrics]: false,
                const encoded = params.get('do');
                         [report]: false,
 
                         [collab]: false,
                if (encoded) {
                         [interop]: false,
                     const action = JSON.parse(decodeURIComponent(atob(encoded)));
                         [meta]: false,
 
                    const filter = action.filter;
                    if (filter) {
                         // Functions filter
                         const functions = filter.functions;
                         if (functions) {
                            Object.keys(functions).forEach(fnCat => {
                                document.getElementById('func-filter-' + escapeAttr(fnCat))
                                    .closest('.func-filter-block')
                                    .querySelectorAll('input[type="checkbox"]').forEach(box => box.checked = functions[fnCat]);
                            });
                         }
 
                         // Further filters
                         // ...
 
                         applyFilters();
                     }
                     }
                    // Further actions (e.g. open filter panel, etc.)
                    // ...
                 }
                 }
             });
             },
 
             'q10-tech': {
             tabulator.on('dataFiltered', (filters, rows) => {
                 filter: {
                const summary = document.getElementById('filter-summary');
                     functions: {
                const filter = filters[0];
                         [search]: false,
 
                         [analysis]: false,
                // Set result counter
                        [post]: true,
                document.getElementById('result-count').textContent = rows.length;
                         [metrics]: false,
               
                        [report]: false,
                // Exit if filter object/type doesn't exist (happens after Tabulator's own filter reset).
                        [collab]: false,
                if (!(filter && filter.type)) { summary.textContent = 'No filter. Showing all results.'; return; }
                         [interop]: false,
 
                         [meta]: false,
                 // Update filter text.
                if (
                    !filter.type.functions &&
                    !filter.type.dataSources &&
                    !filter.type.businessModel &&
                    !filter.type.usedByDmo
                ) { summary.textContent = 'No filter. Showing all results.'; }
                else {
                     let summaryHtml = '<table>';
                    if (filter.type.functions) {
                         summaryHtml += '<tr><td><strong>Functions</strong></td><td>';
                         if (filter.type.functions.size === 0) {
                            summaryHtml += 'none';
                         } else {
                            for (const [cat, subs] of filter.type.functions) {
                                summaryHtml += cat;
                                summaryHtml += subs.length > 0 ? ' <small>(' + subs.join(', ') + ')</small>' : '';
                                summaryHtml += ', ';
                            }
                            summaryHtml = summaryHtml.substring(0, summaryHtml.length - 2);
                         }
                         summaryHtml  += '</td></tr>';
                     }
                     }
                    if (filter.type.dataSources) {
                        summaryHtml += '<tr><td><strong>Platforms</strong></td><td>'
                            + (filter.type.dataSources.length > 0 ? filter.type.dataSources.join(', ') : 'none')
                            + '</td></tr>';
                    }
                    if (filter.type.businessModel) {
                        summaryHtml += '<tr><td><strong>License model</strong></td><td>'
                            + (filter.type.businessModel.length > 0 ? filter.type.businessModel.join(', ') : 'none')
                            + '</td></tr>';
                    }
                    if (filter.type.usedByDmo) {
                        summaryHtml += '<tr><td><strong>Used by practitioners</strong></td><td>'
                            + (filter.type.usedByDmo.length > 0 ? filter.type.usedByDmo.join(', ') : 'none')
                            + '</td></tr>';
                    }
                    summaryHtml += '</table>';
                    summary.innerHTML = summaryHtml;
                 }
                 }
            }
        };


                const markImages = () => {
        const handler = event => {
                    const selectedSources = filter.type.dataSources;
            const answerId = event.currentTarget.id;
                    const selectedFunctions = Array.from(filter.type.functions.keys());
            const act = actions[answerId];


                    // Mark data source images
            if (!actions) return;
                    document.querySelectorAll('.data-sources-cell .data-source-img, .tabulator-responsive-collapse .data-source-img')
                        .forEach(img => {
                            if (!selectedSources || selectedSources.includes(img.dataset.value)) { img.classList.remove('unselected'); }
                            else { img.classList.add('unselected'); }
                        });


                    // Mark functions images
            if (answerId.indexOf('tech') !== -1) {
                    document.querySelectorAll('.functions-cell .func-img').forEach(img => {
                // Action takes us to Tech Library
                        if (!selectedFunctions || selectedFunctions.includes(img.dataset.value)) { img.classList.remove('unselected'); }
                const enc = btoa(encodeURIComponent(JSON.stringify(act)));
                        else { img.classList.add('unselected'); }
                window.location.assign('/index.php/List_of_Disaster_Community_Technologies?do=' + enc);
                    });
            }


                    tabulator.off('renderComplete', markImages);
             if (answerId.indexOf('safe') !== -1) {
                }
                 // Action takes us to Feel Safe
                tabulator.on('renderComplete', markImages); // TODO: Prevent this from running if corresponding filters are not active.
                 window.location.assign('/index.php/Feel_Safe');
             });
             }
 
            // Listen for changes in filter checkbox state.
            document.getElementById('functions-filter').addEventListener('change', event => {
                const filterBlock = event.target.closest('.func-filter-block');
                const category = filterBlock.querySelector('input.func-cat');
                const subfunctions = filterBlock.querySelectorAll('.subfunc-filter-block input[type="checkbox"]');
 
                if (event.target === category) {
                    // Selecting/deselecting the category checks/unchecks all subfunctions.
                    subfunctions.forEach(checkbox => checkbox.checked = category.checked);
                } else {
                    // If no subfunctions are selected, deactivate the category. Activate otherwise.
                    const checkedSubs = Array.from(subfunctions).filter(sub => sub.checked).length;
                    if (checkedSubs > 0) { category.checked = true; } else { category.checked = false; }
                }
 
                applyFilters();
            }, { passive: true });
            document.getElementById('data-source-filter').addEventListener('change', event => {
                applyFilters();
            }, { passive: true });
            document.getElementById('business-model-filter').addEventListener('change', event => {
                applyFilters();
            }, { passive: true });
            document.getElementById('dm-use-filter').addEventListener('change', event => {
                applyFilters();
            }, { passive: true });
 
            // Listen for clicks on filter toggles
            document.querySelectorAll('.filter-wrapper .filter-toggle').forEach(el => {
                 const wrapper = el.closest('.filter-wrapper');
                el.addEventListener('click', event => void wrapper.classList.toggle('open'));
            });
 
            // Fix bug where the table is truncated to zero height despite having visible rows.
            tabulator.on('renderComplete', function() {
                 // TODO: Check the bugfix for a possible infinite event loop.
                // This will help detect it, in case it happens.
                // console.log('Table height bugfix: render complete.');
               
                try {
                    const holderHeight = tabulator.rowManager.element.offsetHeight;
                    const tableHeight = tabulator.rowManager.tableElement.offsetHeight;
                    if (
                        holderHeight < tableHeight ||                          // table is truncated vertically (including zero-height)
                        holderHeight - tableHeight > window.screen.availHeight  // table is more than a screen longer than content
                    ) {
                        tabulator.redraw();
                    }
                } catch (ignore) { }
            });
             // End bugfix
 
        });
 
        function selectAll(context) {
            document.querySelectorAll(context + ' input[type="checkbox"]').forEach(checkbox => checkbox.checked = true);
            applyFilters();
         }
         }


         function deselectAll(context) {
         document.querySelectorAll('.choice').forEach(el => el.addEventListener('click', handler, { passive: true }));
            document.querySelectorAll(context + ' input[type="checkbox"]').forEach(checkbox => checkbox.checked = false);
       
            applyFilters();
        }
 
        function clearFilters() {
            document.querySelectorAll('#dct-filters input[type="checkbox').forEach(checkbox => checkbox.checked = checkbox.defaultChecked);
            applyFilters(true);
        }
 
        function toggleFilter() {
            document.getElementById('dct-filters').classList.toggle('open');
        }
 
        // Close filter pane when clicked outside of it.
        document.body.addEventListener('click', event => {
            const filterPane = document.getElementById('dct-filters');
            if (
                filterPane.classList.contains('open') &&
                !filterPane.contains(event.target) &&
                event.target !== document.querySelector('#filter-bar .large-button')
            ) { filterPane.classList.remove('open'); }
        }, { passive: true });
     </script>
     </script>
    <!-- Icon definitons -->
    <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" overflow="hidden" style="display:none;">
        <defs>
            <symbol id="chevron-down" viewBox="0 0 96 96">
                <clipPath id="chev">
                    <path d="M592 312h96v96h-96z" />
                </clipPath>
                <g clip-path="url(#chev)" transform="translate(-592 -312)">
                    <path d="m640 370.586-25.293-25.293-1.414 1.414L640 373.414l26.707-26.707-1.414-1.414L640 370.586Z" />
                </g>
            </symbol>
        </defs>
    </svg>
    <div id="dct-list-wrapper">
        <h1>
            <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" overflow="hidden" viewBox="0 0 96 96">
                <defs>
                    <clipPath id="a">
                        <path d="M65 334h96v96H65z" />
                    </clipPath>
                </defs>
                <g clip-path="url(#a)" transform="translate(-65 -334)">
                    <path d="m142 395.981.004-35.993H84.001v36.01Zm-55.999-33.993h54.003L140 393.981l-53.999.017Z" />
                    <path
                        d="M81.001 358.987a2.002 2.002 0 0 1 2-2h60.003a2.002 2.002 0 0 1 2 2V400h2v-41.013a4.003 4.003 0 0 0-4-4H83.001a4.003 4.003 0 0 0-4 4V400h2ZM118.003 402.981v2h-10.001v-2H67v1.999a5.006 5.006 0 0 0 5 5.001h82.004a5.006 5.006 0 0 0 5.001-5.001v-1.999Zm36.001 5H72a3.004 3.004 0 0 1-3-3h37.002a1.934 1.934 0 0 0 2 2h10.001a1.934 1.934 0 0 0 2-2h37.002a3.005 3.005 0 0 1-3.001 3Z" />
                    <path
                        d="M113 365c-7.18 0-13 5.82-13 13s5.82 13 13 13 13-5.82 13-13c-.008-7.176-5.824-12.992-13-13Zm10.949 12h-3.518a14.167 14.167 0 0 0-4.176-9.508 11.023 11.023 0 0 1 7.694 9.508ZM112 368.226V377h-4.426c.254-3.771 1.929-7.06 4.426-8.774ZM112 379v8.775c-2.494-1.717-4.17-5.017-4.425-8.775Zm2 8.774V379h4.425c-.255 3.754-1.932 7.056-4.425 8.774ZM114 377v-8.773c2.492 1.718 4.17 5.019 4.425 8.773Zm-4.273-9.502a14.124 14.124 0 0 0-4.158 9.502h-3.518a11.02 11.02 0 0 1 7.676-9.502ZM102.051 379h3.518a14.157 14.157 0 0 0 4.173 9.507 11.022 11.022 0 0 1-7.691-9.507Zm14.205 9.508a14.168 14.168 0 0 0 4.175-9.508h3.518a11.023 11.023 0 0 1-7.693 9.508Z" />
                </g>
            </svg>
            <div>
                <div>Technologies</div>
                <div style="font-size:small; letter-spacing:.03em; margin-left: .6em;">Social Media and Crowdsourcing Library</div>
            </div>
        </h1>
        <div id="dct-intro">
            <p>This page provides an&nbsp;overview of&nbsp;various Technologies related to Social Media and
                Crowdsourcing. You can use the&nbsp;filters to&nbsp;identify
                relevant technologies according to your needs and then click on the name of a&nbsp;tool to&nbsp;get
                further information.</p>
        </div>
        <div id="filter-bar">
            <div style="display: flex; justify-content: space-between;">
                <div style="flex: 1 1;">
                    <h2 style="margin-bottom: 1rem;">Applied Filters</h2>
                    <div id="filter-summary">No filter. Showing all results.</div>
                </div>
                <div style="flex: 0 0;">
                    <button class="large-button" type="button" onclick="toggleFilter()">Filters</button>
                </div>
            </div>
            <h2 style="margin-top: 2.5rem; margin-bottom: 0;">Results: <span id="result-count"></span></h2>
       
            <div id="dct-filters">
                <h2 style="display: flex; justify-content: space-between;">
                    <div>
                        <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" overflow="hidden" viewBox="0 0 96 96">
                            <defs>
                                <clipPath id="b">
                                    <path d="M592 312h96v96h-96z" />
                                </clipPath>
                            </defs>
                            <g clip-path="url(#b)" transform="translate(-592 -312)">
                                <path
                                d="M636 356.012v38.011l8-8v-30.011L674 326h-68Zm6.588-1.412-.588.584v30.008l-4 4v-34.008l-.585-.586L610.828 328h58.348Z" />
                            </g>
                        </svg>
                        <span>Filters</span>
                    </div>
                    <a onclick="toggleFilter()" id="close-filter-button">&times;</a>
                </h2>
                <div style="text-align: center;">
                    <button class="large-button" type="button" onclick="clearFilters()">Clear Filters</button>
                </div>
                <div class="filter-wrapper">
                    <h4>Functions <span class="filter-toggle"><svg><use href="#chevron-down"/></svg></span></h4>
                    <div class="filter-container">
                        <div class="filter-button-wrapper">
                            <button type="button" onclick="selectAll('#functions-filter')">Select all</button> |
                            <button type="button" onclick="deselectAll('#functions-filter')">Clear all</button>
                        </div>
                        <div class="filter-content" id="functions-filter"></div>
                    </div>
                </div>
                <div class="filter-wrapper">
                    <h4>Supported Platforms <span class="filter-toggle"><svg><use href="#chevron-down"/></svg></span></h4>
                    <div class="filter-container">
                        <div class="filter-button-wrapper">
                            <button type="button" onclick="selectAll('#data-source-filter')">Select all</button> |
                            <button type="button" onclick="deselectAll('#data-source-filter')">Clear all</button>
                        </div>
                        <div class="filter-content" id="data-source-filter"></div>
                    </div>
                </div>
                <div class="filter-wrapper">
                    <h4>License Model <span class="filter-toggle"><svg><use href="#chevron-down"/></svg></span></h4>
                    <div class="filter-container">
                        <div class="filter-button-wrapper">
                            <button type="button" onclick="selectAll('#business-model-filter')">Select all</button> |
                            <button type="button" onclick="deselectAll('#business-model-filter')">Clear all</button>
                        </div>
                        <div class="filter-content" id="business-model-filter"></div>
                    </div>
                </div>
                <div class="filter-wrapper">
                    <h4>Used by Practitioners <span class="filter-toggle"><svg><use href="#chevron-down"/></svg></span></h4>
                    <div class="filter-container">
                        <div class="filter-button-wrapper">
                            <button type="button" onclick="selectAll('#dm-use-filter')">Select all</button> |
                            <button type="button" onclick="deselectAll('#dm-use-filter')">Clear all</button>
                        </div>
                        <div class="filter-content" id="dm-use-filter"></div>
                    </div>
                </div>
            </div>
        </div>
        <div id="dct-tabulator"></div>
    </div>
</includeonly>
</includeonly>

Revision as of 15:02, 24 November 2022

Development version of the Compass.
Not ready for production!