PageSourceSearch

https://sergisproject.github.io/sergis-author/javascripts/author-table.js

js sergisproject.github.io collected 2026-10-03 09:55:57 UTC 11,523 bytes, 297 lines download raw bytes

1/*
2    The SerGIS Project - sergis-author
3
4    Copyright (c) 2015, SerGIS Project Contributors. All rights reserved.
5    Use of this source code is governed by the MIT License, which can be found
6    in the LICENSE.txt file.
7*/
8
9// This file handles the drawing of the main table.
10
11AUTHOR.TABLE = {
12    /* generateTable */
13    /* setCurrentPrompt */
14    /* setExpandAllPrompts */
15};
16
17(function () {
18    /**
19     * The current promptIndex that we have "open" (if expandAllPrompts==false).
20     */
21    var currentPromptIndex = null;
22    
23    /**
24     * Whether all prompts should be expanded.
25     */
26    var expandAllPrompts = false;
27    
28    /**
29     * Whether it's our first time auto-scrolling to the prompt we're at (in
30     * which case, we don't want to scroll, because the user just opened the
31     * Author).
32     */
33    var firstTimeScrolling = true;
34    
35    /**
36     * The columns of the table.
37     * Each column has a title string and a generator function.
38     * The generator function is passed 2 arguments:
39     * (1) the prompt index, and (2) `game.jsondata.promptList[promptIndex]`.
40     * The generator function should return either one td element or an array
41     * of td elements.
42     */
43    var TABLE_COLUMNS = [
44        {
45            title: "",
46            generator: AUTHOR.TABLE_COLUMNS.generateTitleColumn
47        },
48        {
49            title: _("Map"),
50            generator: AUTHOR.TABLE_COLUMNS.generateMapColumn
51        },
52        {
53            title: _("Content"),
54            generator: AUTHOR.TABLE_COLUMNS.generateContentColumn
55        },
56        {
57            title: _("Choices"),
58            generator: AUTHOR.TABLE_COLUMNS.generateChoicesColumn
59        },
60        {
61            title: _("Actions"),
62            generator: AUTHOR.TABLE_COLUMNS.generateActionsColumn
63        }
64    ];
65    
66    /**
67     * Set whether all prompts should be expanded.
68     */
69    AUTHOR.TABLE.setExpandAllPrompts = function (yesorno) {
70        expandAllPrompts = yesorno;
71        currentPromptIndex = null;
72        checkCurrentPrompt();
73    };
74    
75    /**
76     * Set the current prompt.
77     */
78    AUTHOR.TABLE.setCurrentPrompt = function (promptIndex) {
79        currentPromptIndex = promptIndex;
80        // Re-draw the table
81        AUTHOR.TABLE.generateTable();
82    };
83    
84    /**
85     * Update the table based on currentPrompt and/or expandAllRows, then make
86     * sure scrolling is all set up well.
87     */
88    function checkCurrentPrompt() {
89        var expandAllPromptsHere = game.jsondata.promptList.length <= 1 ? true : expandAllPrompts;
90        forClass("prompt-row-minimal", function (elem) {
91            var isCurrentPromptIndex = currentPromptIndex !== null && elem.getAttribute("data-promptIndex") == currentPromptIndex.toString();
92            if (expandAllPromptsHere || isCurrentPromptIndex) {
93                // The prompt is expanded, so hide the minimal row
94                elem.className += " prompt-row-hidden";
95            } else {
96                // The prompt is minimized, so show the minimal row
97                elem.className = removeFromString(elem.className, "prompt-row-hidden");
98            }
99        });
100        var finalElem;
101        forClass("prompt-row-full", function (elem) {
102            var isCurrentPromptIndex = currentPromptIndex !== null && elem.getAttribute("data-promptIndex") == currentPromptIndex.toString();
103            if (expandAllPromptsHere || isCurrentPromptIndex) {
104                // The prompt is expanded, so show the full row
105                elem.className = removeFromString(elem.className, "prompt-row-hidden");
106                // Is it the current prompt index?
107                if (elem.className.indexOf("toprow") != -1 && isCurrentPromptIndex) {
108                    finalElem = elem;
109                }
110            } else {
111                // The prompt is minimized, so hide the full row
112                elem.className += " prompt-row-hidden";
113            }
114        });
115        
116        // Check scrolling
117        checkScroll();
118        
119        // Scroll the current prompt into view, if applicable
120        if (finalElem) {
121            if (firstTimeScrolling) {
122                firstTimeScrolling = false;
123            } else {
124                setTimeout(function () {
125                    var bodyRect = document.body.getBoundingClientRect(),
126                        elemRect = finalElem.getBoundingClientRect(),
127                        offset   = elemRect.top - bodyRect.top;
128                    window.scrollTo(0, offset - 60);
129                }, 2);
130            }
131        }
132    }
133    
134    
135    /**
136     * Make the prompt table.
137     *
138     * @param {number} [newCurrentPromptIndex] - A new current prompt index.
139     * @param {number} [scrollToPromptIndex] - A prompt index to scroll to.
140     */
141    AUTHOR.TABLE.generateTable = function (newCurrentPromptIndex) {
142        // Get rid of the old table
143        byId("promptContainer").innerHTML = "";
144        
145        // Create the new table and the table header with all the column titles
146        var table = create("table").create("thead").create("tr", /*Children:*/ TABLE_COLUMNS.map(function (column) {
147            return create("th", {
148                text: column.title
149            });
150        }));
151        
152        // Make the table body
153        var tbody = create("tbody");
154        
155        // Generate table row(s) for each prompt
156        game.jsondata.promptList.forEach(function (promptItem, promptIndex) {
157            // First, make the minimal row
158            tbody.appendChild(AUTHOR.TABLE_COLUMNS.generateMinimalRow(promptIndex, promptItem));
159            
160            // 2D array: first dimension is rows, second dimension is columns (td elements)
161            // This allows for some columns that require multple rows for a prompt
162            // (that are still part of this one "row", i.e. prompt)
163            var rows = [[]];
164            // Generate each table column for this row
165            TABLE_COLUMNS.forEach(function (column, column_index) {
166                var tds = column.generator(promptIndex, promptItem);
167                if (!Array.isArray(tds)) {
168                    // It's just one column, so put it in the first row
169                    rows[0].push(tds);
170                } else {
171                    // This column spans multiple rows
172                    tds.forEach(function (td, row_index) {
173                        // Make the new row if it doesn't exist
174                        if (!rows[row_index]) {
175                            rows[row_index] = [];
176                            // Fill in empty space in the row up to this column
177                            // (Fill it in with `null`s so we can recognize it later)
178                            for (var i = 0; i < column_index; i++) {
179                                rows[row_index].push(null);
180                            }
181                        }
182                        // Add our column to the row
183                        rows[row_index].push(td);
184                    });
185                }
186            });
187            
188            // Add the row(s) to the table
189            rows.forEach(function (row, row_index) {
190                var tds = [];
191                row.forEach(function (td, column_index) {
192                    if (td) {
193                        // See how many rows after this one in this column are null
194                        // For any of those, we must "rowspan" over them
195                        var rowspan = 1, rowi = row_index;
196                        while (++rowi < rows.length && rows[rowi][column_index] === null) {
197                            rowspan++;
198                        }
199                        // Set the rowspan on our td and add it to the list
200                        td.setAttribute("rowspan", "" + rowspan);
201                        tds.push(td);
202                    }
203                });
204                // Add this row to the table
205                tbody.appendChild(create("tr", {
206                    className: "prompt-row-full "  + (row_index === 0 ? "toprow" : "prompt-row-hidden"),
207                    "data-promptIndex": promptIndex
208                }, tds));
209            });
210        });
211        
212        // Make spacer row (based on # of columns, i.e. # of titles)
213        tbody.appendChild(create("tr", /*Children:*/ TABLE_COLUMNS.map(function (column) {
214            // Make a spacer column for this column
215            return create("td", {
216                text: " ",
217                className: "prompt-container-header-cell-spacer"
218            });
219        })));
220        
221        // Stick it all in there
222        table.appendChild(tbody);
223        byId("promptContainer").appendChild(table);
224        
225        if (typeof newCurrentPromptIndex == "number") {
226            // Select a new current prompt
227            currentPromptIndex = newCurrentPromptIndex;
228        }
229        // Make sure current prompt is selected and scrolling is good
230        checkCurrentPrompt();
231    };
232    
233    
234    
235    /** Whether the browser supports "window.pageXOffset" (for checkScroll). */
236    var supportPageOffset = window.pageXOffset !== undefined;
237    /** Whether the browser's in "Standards Mode" (opposed to "Quirks Mode"). */
238    var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
239    
240    /** Helper function to set the proper width on the table columns. */
241    function setHeaderWidths(tbl, thead, auto) {
242        // Make sure the header cells stay the same width
243        thead.style.width = auto ? "auto" : thead.offsetWidth + "px";
244        var kids = tbl.getElementsByTagName("th");
245        var kidSpacers = tbl.getElementsByClassName("prompt-container-header-cell-spacer");
246        for (var j = 0; j < kids.length; j++) {
247            kidSpacers[j].style.minWidth = kids[j].style.width = auto ? "auto" : kids[j].offsetWidth + "px";
248        }
249    }
250    
251    /**
252     * Check the scrolling and set up the table header accordingly.
253     */
254    function checkScroll() {
255        var tbl = byId("promptContainer").childNodes[0];
256        if (tbl) {
257            // Find the <thead>
258            var thead;
259            for (var i = 0; i < tbl.childNodes.length; i++) {
260                if (tbl.childNodes[i].nodeName.toLowerCase() == "thead") {
261                    thead = tbl.childNodes[i];
262                    break;
263                }
264            }
265            
266            // Find the amount that the window is scrolled
267            var scrollY = supportPageOffset ? window.pageYOffset :
268                isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop;
269            
270            // Find out how far the table is from the top of the page
271            var elem = tbl;
272            var offsetY = elem.offsetTop;
273            while ((elem = elem.offsetParent)) {
274                offsetY += elem.offsetTop || 0;
275            }
276            
277            // Reset any and all spacing
278            tbl.className = removeFromString(tbl.className, "scrollFixed");
279            setHeaderWidths(tbl, thead, true);
280            
281            // Check if we should be .scrollFixed
282            if (scrollY >= offsetY) {
283                setHeaderWidths(tbl, thead);
284                // And, we should be .scrollFixed; let's add the class
285                tbl.className += " scrollFixed";
286            }
287        }
288    }
289
290    // Set up the scrolling
291    window.addEventListener("load", function (event) {
292        window.addEventListener("scroll", function (event) {
293            checkScroll();
294        }, false);
295        checkScroll();
296    }, false);
297})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.