MediaWiki:Common.js: Difference between revisions

No edit summary
No edit summary
Line 1,267: Line 1,267:
     try { JSON.parse(readLS(LS_TEEKA_KEY) || '[]').forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }
     try { JSON.parse(readLS(LS_TEEKA_KEY) || '[]').forEach(function (s) { selectedTeekaSlugs[s] = true; }); } catch (e) { }
     var teekaIndex = null;
     var teekaIndex = null;
     var availableDocs = [];
     var availableList = []; // [{slug, title, subItems: [{slug, title}, ...]}]
     var checkboxesBySlug = {};
     var checkboxesBySlug = {};
     var readyCallbacks = [];
     var readyCallbacks = [];
Line 1,301: Line 1,301:
     // matched by real href rather than any assumed list markup, since
     // matched by real href rather than any assumed list markup, since
     // that markup isn't something this script controls.
     // that markup isn't something this script controls.
    // CHANGED: checkbox is now wrapped in a .gr-teeka-cb-wrap span for
    // reliable vertical centering (confirmed directly - screenshot -
    // that a bare checkbox relying on vertical-align alone rendered
    // oversized/misaligned), and the enclosing <li> (if any) gets
    // .gr-teeka-list-item so its bullet can be removed via CSS, since a
    // checkbox is now the line's own visual marker.
     function augmentExistingTeekaLinks(subpageTitles, docs) {
     function augmentExistingTeekaLinks(subpageTitles, docs) {
         var slugByPath = {};
         var slugByPath = {};
Line 1,317: Line 1,323:
             var slug = slugByPath[path];
             var slug = slugByPath[path];
             if (!slug || this.closest('.gr-injected-teeka-group')) return;
             if (!slug || this.closest('.gr-injected-teeka-group')) return;
            var li = this.closest('li');
            if (li) li.classList.add('gr-teeka-list-item');
            var $wrap = $('<span>').addClass('gr-teeka-cb-wrap');
             var $cb = $('<input>').attr({ type: 'checkbox' }).addClass('gr-teeka-inline-checkbox').prop('checked', !!selectedTeekaSlugs[slug]);
             var $cb = $('<input>').attr({ type: 'checkbox' }).addClass('gr-teeka-inline-checkbox').prop('checked', !!selectedTeekaSlugs[slug]);
             $(this).before($cb, ' ');
            $wrap.append($cb);
             $(this).before($wrap);
             registerCheckbox(slug, $cb);
             registerCheckbox(slug, $cb);
             $cb.on('change', function () { toggleSlug(slug, $cb.is(':checked')); });
             $cb.on('change', function () { toggleSlug(slug, $cb.is(':checked')); });
Line 1,376: Line 1,386:
                         return !!p.doc;
                         return !!p.doc;
                     });
                     });
                 var docs = pairs.map(function (p) { return p.doc; });
                 var topDocs = pairs.map(function (p) { return p.doc; });
                 teekaIndex = buildTeekaInMoolaIndex(docs);
 
                availableDocs = docs;
                // NEW: sub-commentaries (tippanis) - a teeka doc declares
                var augmented = augmentExistingTeekaLinks(pairs.map(function (p) { return p.title; }), docs);
                // its own children via doc.related (an array of slugs,
                if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));
                // matching the same field this session's own generator
                else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point
                // already writes into new JSON docs). Only the ASK here
                if (Object.keys(selectedTeekaSlugs).length) renderInterleave();
                // is to show these as sub-categories in the options list
                isReady = true;
                // and gate their visibility on the parent being checked
                readyCallbacks.forEach(function (cb) { cb(docs.map(function (d) { return { slug: d.doc.slug || d._title, title: d.doc.title || d._title }; })); });
                // - so this only needs one more level of fetching, not
                if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });
                // unbounded recursion.
                var relatedSlugs = [];
                topDocs.forEach(function (d) { (d.doc.related || []).forEach(function (s) { relatedSlugs.push(s); }); });
                 var uniqueRelated = relatedSlugs.filter(function (s, i) { return relatedSlugs.indexOf(s) === i; });
 
                function finishInit(subDocsBySlug) {
                    var allDocs = topDocs.slice();
                    Object.keys(subDocsBySlug).forEach(function (slug) { if (subDocsBySlug[slug]) allDocs.push(subDocsBySlug[slug]); });
                    teekaIndex = buildTeekaInMoolaIndex(allDocs);
                    availableList = topDocs.map(function (d) {
                        var subItems = (d.doc.related || [])
                            .map(function (s) { return subDocsBySlug[s]; })
                            .filter(Boolean)
                            .map(function (sd) { return { slug: sd.doc.slug || sd._title, title: sd.doc.title || sd._title }; });
                        return { slug: d.doc.slug || d._title, title: d.doc.title || d._title, subItems: subItems };
                    });
                    var augmented = augmentExistingTeekaLinks(pairs.map(function (p) { return p.title; }), topDocs);
                    if (augmented.length) observeInlineVisibility(augmented.map(function (a) { return a.el; }));
                    else inlineListVisible = false; // no inline checkboxes exist - toolbar control should always show as the only access point
                    if (Object.keys(selectedTeekaSlugs).length) renderInterleave();
                    isReady = true;
                    readyCallbacks.forEach(function (cb) { cb(availableList); });
                    if (inlineListVisible !== null) visibilityCallbacks.forEach(function (cb) { cb(inlineListVisible); });
                }
 
                if (!uniqueRelated.length) { finishInit({}); return; }
                $.when.apply($, uniqueRelated.map(fetchTeekaJson)).then(function () {
                    var subFetched = arguments.length === 1 ? [arguments[0]] : Array.prototype.slice.call(arguments);
                    var subDocsBySlug = {};
                    uniqueRelated.forEach(function (slug, i) {
                        if (!subFetched[i]) console.warn('[grTeekaInterleave] Could not load sub-commentary JSON:' + slug);
                        subDocsBySlug[slug] = subFetched[i];
                    });
                    finishInit(subDocsBySlug);
                });
             });
             });
         });
         });
Line 1,391: Line 1,435:


     window.grTeekaInterleave = {
     window.grTeekaInterleave = {
         onReady: function (cb) { if (isReady) cb(availableDocs.map(function (d) { return { slug: d.doc.slug || d._title, title: d.doc.title || d._title }; })); else readyCallbacks.push(cb); },
         onReady: function (cb) { if (isReady) cb(availableList); else readyCallbacks.push(cb); },
         onChange: function (cb) { changeCallbacks.push(cb); },
         onChange: function (cb) { changeCallbacks.push(cb); },
         onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },
         onVisibilityChange: function (cb) { visibilityCallbacks.push(cb); if (inlineListVisible !== null) cb(inlineListVisible); },