|
1 | 1 | import { booleanHtmlAttributes } from '../../shared/constants'
|
2 |
| -import { toArray, includes } from '../../utils/array' |
3 |
| -import { isArray } from '../../utils/is-type' |
| 2 | +import { includes } from '../../utils/array' |
| 3 | + |
| 4 | +// keep a local map of attribute values |
| 5 | +// instead of adding it to the html |
| 6 | +export const attributeMap = {} |
4 | 7 |
|
5 | 8 | /**
|
6 | 9 | * Updates the document's html tag attributes
|
7 | 10 | *
|
8 | 11 | * @param {Object} attrs - the new document html attributes
|
9 | 12 | * @param {HTMLElement} tag - the HTMLElement tag to update with new attrs
|
10 | 13 | */
|
11 |
| -export default function updateAttribute ({ attribute } = {}, attrs, tag) { |
| 14 | +export default function updateAttribute (appId, { attribute } = {}, type, attrs, tag) { |
12 | 15 | const vueMetaAttrString = tag.getAttribute(attribute)
|
13 |
| - const vueMetaAttrs = vueMetaAttrString ? vueMetaAttrString.split(',') : [] |
14 |
| - const toRemove = toArray(vueMetaAttrs) |
| 16 | + if (vueMetaAttrString) { |
| 17 | + attributeMap[type] = JSON.parse(decodeURI(vueMetaAttrString)) |
| 18 | + tag.removeAttribute(attribute) |
| 19 | + } |
15 | 20 |
|
16 |
| - const keepIndexes = [] |
17 |
| - for (const attr in attrs) { |
18 |
| - if (attrs.hasOwnProperty(attr)) { |
19 |
| - const value = includes(booleanHtmlAttributes, attr) |
20 |
| - ? '' |
21 |
| - : isArray(attrs[attr]) ? attrs[attr].join(' ') : attrs[attr] |
| 21 | + let data = attributeMap[type] || {} |
| 22 | + |
| 23 | + const toUpdate = [] |
22 | 24 |
|
23 |
| - tag.setAttribute(attr, value || '') |
| 25 | + // remove attributes from the map |
| 26 | + // which have been removed for this appId |
| 27 | + for (const attr in data) { |
| 28 | + if (data[attr] && appId in data[attr]) { |
| 29 | + toUpdate.push(attr) |
24 | 30 |
|
25 |
| - if (!includes(vueMetaAttrs, attr)) { |
26 |
| - vueMetaAttrs.push(attr) |
| 31 | + if (!attrs[attr]) { |
| 32 | + delete data[attr][appId] |
27 | 33 | }
|
| 34 | + } |
| 35 | + } |
| 36 | + |
| 37 | + for (const attr in attrs) { |
| 38 | + const attrData = data[attr] |
| 39 | + |
| 40 | + if (!attrData || attrData[appId] !== attrs[attr]) { |
| 41 | + toUpdate.push(attr) |
28 | 42 |
|
29 |
| - // filter below wont ever check -1 |
30 |
| - keepIndexes.push(toRemove.indexOf(attr)) |
| 43 | + if (attrs[attr]) { |
| 44 | + data[attr] = data[attr] || {} |
| 45 | + data[attr][appId] = attrs[attr] |
| 46 | + } else { |
| 47 | + delete data[attr][appId] |
| 48 | + } |
31 | 49 | }
|
32 | 50 | }
|
33 | 51 |
|
34 |
| - const removedAttributesCount = toRemove |
35 |
| - .filter((el, index) => !includes(keepIndexes, index)) |
36 |
| - .reduce((acc, attr) => { |
37 |
| - tag.removeAttribute(attr) |
38 |
| - return acc + 1 |
39 |
| - }, 0) |
| 52 | + for (const attr of toUpdate) { |
| 53 | + const attrData = data[attr] |
40 | 54 |
|
41 |
| - if (vueMetaAttrs.length === removedAttributesCount) { |
42 |
| - tag.removeAttribute(attribute) |
43 |
| - } else { |
44 |
| - tag.setAttribute(attribute, (vueMetaAttrs.sort()).join(',')) |
| 55 | + const attrValues = [] |
| 56 | + for (const appId in attrData) { |
| 57 | + Array.prototype.push.apply(attrValues, [].concat(attrData[appId])) |
| 58 | + } |
| 59 | + |
| 60 | + if (attrValues.length) { |
| 61 | + const attrValue = includes(booleanHtmlAttributes, attr) && attrValues.some(Boolean) |
| 62 | + ? '' |
| 63 | + : attrValues.filter(Boolean).join(' ') |
| 64 | + |
| 65 | + tag.setAttribute(attr, attrValue) |
| 66 | + } else { |
| 67 | + tag.removeAttribute(attr) |
| 68 | + } |
45 | 69 | }
|
| 70 | + |
| 71 | + attributeMap[type] = data |
46 | 72 | }
|
0 commit comments