/ ////////// Property list
function appendPropertyTRs (parent, plist, inverse, predicateFilter) {
      // UI.log.info('@appendPropertyTRs, 'this' is %s, dom is %s, '+ // Gives 'can't access dead object'
      //                   'thisOutline.document is %s', this, dom.location, thisOutline.document.location);
      // UI.log.info('@appendPropertyTRs, dom is now ' + this.document.location);
      // UI.log.info('@appendPropertyTRs, dom is now ' + thisOutline.document.location);
    UI.log.debug('Property list length = ' + plist.length)
    if (plist.length === 0) return ''
    var sel, j, k
    if (inverse) {
      sel = function (x) {
        return x.subject
      }
      plist = plist.sort(UI.utils.RDFComparePredicateSubject)
    } else {
      sel = function (x) {
        return x.object
      }
      plist = plist.sort(UI.utils.RDFComparePredicateObject)
    }

    var max = plist.length
    for (j = 0; j < max; j++) { // squishing together equivalent properties I think
      var s = plist[j]
          //      if (s.object == parentSubject) continue; // that we knew

        // Avoid predicates from other panes
      if (predicateFilter && !predicateFilter(s.predicate, inverse)) continue

      var tr = propertyTR(dom, s, inverse)
      parent.appendChild(tr)
      var predicateTD = tr.firstChild // we need to kludge the rowspan later

      var defaultpropview = views.defaults[s.predicate.uri]

      //   LANGUAGE PREFERENCES WAS AVAILABLE WITH FF EXTENSION - get from elsewhere?

      var dups = 0 // How many rows have the same predicate, -1?
      var langTagged = 0 // how many objects have language tags?
      var myLang = 0 // Is there one I like?

      for (k = 0;
          (k + j < max) && (plist[j + k].predicate.sameTerm(s.predicate)); k++) {
        if (k > 0 && (sel(plist[j + k]).sameTerm(sel(plist[j + k - 1])))) dups++
        if (sel(plist[j + k]).lang && outline.labeller.LanguagePreference) {
          langTagged += 1
          if (sel(plist[j + k]).lang.indexOf(outline.labeller.LanguagePreference) >= 0) myLang++
        }
      }

        /* Display only the one in the preferred language
          ONLY in the case (currently) when all the values are tagged.
          Then we treat them as alternatives. */

      if (myLang > 0 && langTagged === dups + 1) {
        for (let k = j; k <= j + dups; k++) {
          if (outline.labeller.LanguagePreference && sel(plist[k]).lang.indexOf(outline.labeller.LanguagePreference) >= 0) {
            tr.appendChild(thisOutline.outlineObjectTD(sel(plist[k]), defaultpropview, undefined, s))
            break
          }
        }
        j += dups // extra push
        continue
      }

      tr.appendChild(thisOutline.outlineObjectTD(sel(s), defaultpropview, undefined, s))

        /* Note: showNobj shows between n to 2n objects.
         * This is to prevent the case where you have a long list of objects
         * shown, and dangling at the end is '1 more' (which is easily ignored)
         * Therefore more objects are shown than hidden.
         */

      tr.showNobj = function (n) {
        var predDups = k - dups
        var show = ((2 * n) < predDups) ? n : predDups
        var showLaterArray = []
        if (predDups !== 1) {
          predicateTD.setAttribute('rowspan', (show === predDups) ? predDups : n + 1)
          var l
          if ((show < predDups) && (show === 1)) { // what case is this...
            predicateTD.setAttribute('rowspan', 2)
          }
          var displayed = 0 // The number of cells generated-1,
              // all duplicate thing removed
          for (l = 1; l < k; l++) {
                // This detects the same things
            if (!kb.canon(sel(plist[j + l])).sameTerm(kb.canon(sel(plist[j + l - 1])))) {
              displayed++
              s = plist[j + l]
              defaultpropview = views.defaults[s.predicate.uri]
              var trObj = dom.createElement('tr')
              trObj.style.colspan = '1'
              trObj.appendChild(thisOutline.outlineObjectTD(
                    sel(plist[j + l]), defaultpropview, undefined, s))
              trObj.AJAR_statement = s
              trObj.AJAR_inverse = inverse
              parent.appendChild(trObj)
              if (displayed >= show) {
                trObj.style.display = 'none'
                showLaterArray.push(trObj)
              }
            } else {
                  // ToDo: show all the data sources of this statement
              UI.log.info('there are duplicates here: %s', plist[j + l - 1])
            }
          }
              // @@a quick fix on the messing problem.
          if (show === predDups) { predicateTD.setAttribute('rowspan', displayed + 1) }
        } // end of if (predDups!==1)

        if (show < predDups) { // Add the x more <TR> here
          var moreTR = dom.createElement('tr')
          var moreTD = moreTR.appendChild(dom.createElement('td'))
          moreTD.setAttribute('style', 'margin: 0.2em; border: none; padding: 0; vertical-align: top;')
          moreTD.setAttribute('notSelectable', 'false')
          if (predDups > n) { // what is this for??
            var small = dom.createElement('a')
            moreTD.appendChild(small)

            var predToggle = (function (f) {
              return f(predicateTD, k, dups, n)
            })(function (predicateTD, k, dups, n) {
              return function (display) {
                small.innerHTML = ''
                if (display === 'none') {
                  small.appendChild(UI.utils.AJARImage(UI.icons.originalIconBase + 'tbl-more-trans.png', 'more', 'See all', dom))
                  small.appendChild(dom.createTextNode((predDups - n) + ' more...'))
                  predicateTD.setAttribute('rowspan', n + 1)
                } else {
                  small.appendChild(UI.utils.AJARImage(UI.icons.originalIconBase + 'tbl-shrink.png', '(less)', undefined, dom))
                  predicateTD.setAttribute('rowspan', predDups + 1)
                }
                for (var i = 0; i < showLaterArray.length; i++) {
                  var trObj = showLaterArray[i]
                  trObj.style.display = display
                }
              }
            }) // ???
            var current = 'none'
            var toggleObj = function (event) {
              predToggle(current)
              current = (current === 'none') ? '' : 'none'
              if (event) event.stopPropagation()
              return false // what is this for?
            }
            toggleObj()
            small.addEventListener('click', toggleObj, false)
          } // if(predDups>n)
          parent.appendChild(moreTR)
        } // if
      } // tr.showNobj

      tr.showAllobj = function () {
        tr.showNobj(k - dups)
      }

      tr.showNobj(10)

      j += k - 1 // extra push
    }
  }