UNPKG

2.23 kBJavaScriptView Raw
1/**
2 * @fileoverview
3 * Sort attribute values.
4 *
5 * This optimizes for repetition-based compression (such as GZip).
6 * @example
7 * <div class="qux quux foo bar"></div>
8 */
9
10'use strict'
11
12var array = require('x-is-array')
13var visit = require('unist-util-visit')
14var is = require('hast-util-is-element')
15var attributes = require('./schema')
16
17module.exports = sortAttributeValues
18
19var own = {}.hasOwnProperty
20
21function sortAttributeValues() {
22 return transform
23}
24
25function transform(tree) {
26 var counts = {}
27 var queues = []
28
29 visit(tree, 'element', visitor)
30
31 flush(optimize())
32
33 function visitor(node) {
34 var props = node.properties
35 var prop
36 var value
37
38 for (prop in props) {
39 value = props[prop]
40
41 if (
42 own.call(attributes, prop) &&
43 is(node, attributes[prop]) &&
44 array(value)
45 ) {
46 add(prop, value)
47 }
48 }
49 }
50
51 function add(prop, values) {
52 var cache = counts[prop] || (counts[prop] = {known: []})
53 var length = values.length
54 var index = -1
55 var value
56
57 while (++index < length) {
58 value = safe(values[index])
59
60 if (value in cache) {
61 cache[value]++
62 } else {
63 cache[value] = 1
64 cache.known.push(values[index])
65 }
66 }
67
68 queues.push([values, prop])
69 }
70
71 function optimize() {
72 var caches = {}
73 var prop
74 var values
75
76 for (prop in counts) {
77 values = counts[prop]
78 caches[prop] = values.known.sort(sort)
79 }
80
81 return caches
82
83 function sort(a, b) {
84 return values[safe(b)] - values[safe(a)] || compare(a, b, 0)
85 }
86 }
87
88 function flush(caches) {
89 var length = queues.length
90 var index = -1
91 var queue
92 var cache
93
94 while (++index < length) {
95 queue = queues[index]
96 cache = caches[queue[1]]
97 queue[0].sort(sorter)
98 }
99
100 function sorter(a, b) {
101 return cache.indexOf(a) - cache.indexOf(b)
102 }
103 }
104}
105
106function safe(value) {
107 return '$' + value
108}
109
110// This would create an infinite loop if `a` and `b` could be equal, but the
111// list we operate on only has unique values.
112function compare(a, b, index) {
113 return (
114 (a.charCodeAt(index) || 0) - (b.charCodeAt(index) || 0) ||
115 compare(a, b, index + 1)
116 )
117}