mirror of
https://github.com/iconify/iconify.git
synced 2024-11-09 23:00:56 +00:00
Rebuild observer in SVG framework. Now it supports multiple root nodes
This commit is contained in:
parent
0266760091
commit
7645047431
14
packages/browser-tests/package-lock.json
generated
14
packages/browser-tests/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@iconify/iconify-browser-tests",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.0.0-beta.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -11,9 +11,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@iconify/core": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-PIPWM7Z0R8CClnl9kx8c3G8qe4gF+WGvro6IOcYRLw5YsVt0JyagGh5MgTHH8yfhMWlpAgiiROm65tJpKHctQw==",
|
||||
"version": "1.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-VqdW1vu7uWBI4QhOijVbT4vxDUKLjGNOye7SbJxvB2XQpJIQe9ZKnNqIyX1fO1olSV+Zf4Y1CQhWjw/sOxW+sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cyberalien/redundancy": "^1.0.0",
|
||||
@ -22,9 +22,9 @@
|
||||
}
|
||||
},
|
||||
"@iconify/iconify": {
|
||||
"version": "2.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-s+R3tKl+5+21Foj/JtODPtoAZmxCBt+NLTS0oTwDaibl8VFkomoPDuMPdT+twSVD7iJG81+MqERr0zTk8HyFfg==",
|
||||
"version": "2.0.0-beta.4",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/iconify/-/iconify-2.0.0-beta.4.tgz",
|
||||
"integrity": "sha512-/uUDM3D2rYWa5JmfC9DIfoznGbodBwwsMcpMOyG5xx5vgMZzfJYrgBWA1kCT+ec5NJ3bcH2etZBzkLOVS3sJAA==",
|
||||
"dev": true
|
||||
},
|
||||
"@iconify/types": {
|
||||
|
@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"description": "Browser tests for @iconify/iconify package",
|
||||
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.0.0-beta.4",
|
||||
"license": "(Apache-2.0 OR GPL-2.0)",
|
||||
"bugs": "https://github.com/iconify/iconify/issues",
|
||||
"homepage": "https://iconify.design/",
|
||||
@ -19,8 +19,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cyberalien/redundancy": "^1.0.0",
|
||||
"@iconify/core": "^1.0.0-beta.2",
|
||||
"@iconify/iconify": "^2.0.0-beta.3",
|
||||
"@iconify/core": "^1.0.0-beta.3",
|
||||
"@iconify/iconify": "^2.0.0-beta.4",
|
||||
"@iconify/types": "^1.0.3",
|
||||
"@rollup/plugin-buble": "^0.21.1",
|
||||
"@rollup/plugin-commonjs": "^11.0.2",
|
||||
|
@ -1,11 +1,10 @@
|
||||
import mocha from 'mocha';
|
||||
import chai from 'chai';
|
||||
|
||||
import { getNode } from './node';
|
||||
import { setRoot } from '@iconify/iconify/lib/modules/root';
|
||||
import { getNode, setRoot } from './node';
|
||||
import { listRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import {
|
||||
initObserver,
|
||||
isObserverPaused,
|
||||
pauseObserver,
|
||||
} from '@iconify/iconify/lib/modules/observer';
|
||||
|
||||
@ -16,28 +15,40 @@ describe('Testing observer creation', () => {
|
||||
const node = getNode('observer-creation');
|
||||
setRoot(node);
|
||||
|
||||
// Get node
|
||||
const list = listRootNodes();
|
||||
expect(list.length).to.be.equal(1);
|
||||
|
||||
const item = list[0];
|
||||
expect(item.node).to.be.equal(node);
|
||||
expect(item.observer).to.be.equal(void 0);
|
||||
|
||||
// Do test
|
||||
let counter = 0;
|
||||
|
||||
node.innerHTML = '<div></div><ul><li>test</li><li>test2</li></ul>';
|
||||
initObserver((root) => {
|
||||
expect(root).to.be.equal(node);
|
||||
expect(root.node).to.be.equal(node);
|
||||
|
||||
counter++;
|
||||
|
||||
// Should be called only once
|
||||
expect(counter).to.be.equal(1);
|
||||
|
||||
expect(isObserverPaused()).to.be.equal(false);
|
||||
// Check if observer is paused
|
||||
expect(item.observer).to.not.be.equal(void 0);
|
||||
expect(item.observer.paused).to.be.equal(0);
|
||||
|
||||
// Pause observer
|
||||
pauseObserver();
|
||||
expect(isObserverPaused()).to.be.equal(true);
|
||||
expect(item.observer.paused).to.be.equal(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
// Add few nodes to trigger observer
|
||||
expect(isObserverPaused()).to.be.equal(false);
|
||||
expect(item.observer).to.not.be.equal(void 0);
|
||||
expect(item.observer.paused).to.be.equal(0);
|
||||
node.querySelector('div').innerHTML =
|
||||
'<span class="test">Some text</span><i>!</i>';
|
||||
});
|
||||
|
@ -1,14 +1,12 @@
|
||||
import mocha from 'mocha';
|
||||
import chai from 'chai';
|
||||
|
||||
import { getNode } from './node';
|
||||
import { getNode, setRoot } from './node';
|
||||
import { elementFinderProperty } from '@iconify/iconify/lib/modules/element';
|
||||
import { setRoot } from '@iconify/iconify/lib/modules/root';
|
||||
import {
|
||||
initObserver,
|
||||
pauseObserver,
|
||||
resumeObserver,
|
||||
isObserverPaused,
|
||||
} from '@iconify/iconify/lib/modules/observer';
|
||||
|
||||
const expect = chai.expect;
|
||||
@ -24,7 +22,7 @@ describe('Testing observer with DOM manipulation', () => {
|
||||
node.innerHTML =
|
||||
'<div></div><ul><li>test</li><li>test2</li></ul><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" focusable="false" role="img" width="1em" height="1em" style="-ms-transform: rotate(360deg); -webkit-transform: rotate(360deg); transform: rotate(360deg); vertical-align: -0.125em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24" data-icon="mdi-home" data-inline="false" class="iconify"><path d="M10 20v-6h4v6h5v-8h3L12 3L2 12h3v8h5z" fill="currentColor"></path></svg>';
|
||||
initObserver((root) => {
|
||||
expect(root).to.be.equal(node);
|
||||
expect(root.node).to.be.equal(node);
|
||||
expect(waitingCallback).to.be.equal(true);
|
||||
|
||||
counter++;
|
||||
@ -108,7 +106,6 @@ describe('Testing observer with DOM manipulation', () => {
|
||||
});
|
||||
|
||||
// Add few nodes to trigger observer
|
||||
expect(isObserverPaused()).to.be.equal(false);
|
||||
node.querySelector('div').innerHTML =
|
||||
'<span class="test">Some text</span><i>!</i>';
|
||||
});
|
||||
|
@ -1,13 +1,14 @@
|
||||
import mocha from 'mocha';
|
||||
import chai from 'chai';
|
||||
|
||||
import { getNode } from './node';
|
||||
import { getNode, setRoot } from './node';
|
||||
import { addFinder } from '@iconify/iconify/lib/modules/finder';
|
||||
import { finder as iconifyFinder } from '@iconify/iconify/lib/finders/iconify';
|
||||
import { finder as iconifyIconFinder } from '@iconify/iconify/lib/finders/iconify-icon';
|
||||
import { getStorage, addIconSet } from '@iconify/core/lib/storage';
|
||||
import { setRoot, getRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { scanDOM } from '@iconify/iconify/lib/modules/scanner';
|
||||
import { listRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { scanDOM, scanElement } from '@iconify/iconify/lib/modules/scanner';
|
||||
import { removeObservedNode } from '@iconify/iconify/lib/modules/observer';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
@ -43,10 +44,12 @@ describe('Scanning DOM', () => {
|
||||
});
|
||||
|
||||
// Sanity check before running tests
|
||||
expect(getRootNodes()).to.be.eql([]);
|
||||
expect(listRootNodes()).to.be.eql([]);
|
||||
|
||||
it('Scan DOM with preloaded icons', () => {
|
||||
const node = getNode('scan-dom');
|
||||
setRoot(node);
|
||||
|
||||
node.innerHTML =
|
||||
'<div><p>Testing scanning DOM</p><ul>' +
|
||||
'<li>Valid icons:' +
|
||||
@ -60,49 +63,66 @@ describe('Scanning DOM', () => {
|
||||
'</ul></div>';
|
||||
|
||||
// Scan node
|
||||
setRoot(node);
|
||||
scanDOM();
|
||||
|
||||
// Find elements
|
||||
const elements = node.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(4);
|
||||
|
||||
// Make sure tempoary node was not added as root, but new root node was
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: node,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
// Check root nodes list
|
||||
const nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(node);
|
||||
});
|
||||
|
||||
it('Scan DOM with unattached root', () => {
|
||||
const fakeNode = getNode('scan-dom');
|
||||
setRoot(fakeNode);
|
||||
|
||||
const node = document.createElement('div');
|
||||
|
||||
node.innerHTML = '<span class="iconify" data-icon="mdi:home"></span>';
|
||||
|
||||
// Get old root nodes. It should not be empty because of previous test(s)
|
||||
const oldRoot = getRootNodes();
|
||||
// Check root nodes list
|
||||
let nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeNode);
|
||||
|
||||
// Scan node
|
||||
scanDOM(node);
|
||||
scanElement(node);
|
||||
|
||||
// Find elements
|
||||
const elements = node.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(1);
|
||||
|
||||
// Make sure tempoary node was not added as root
|
||||
expect(getRootNodes()).to.be.eql(oldRoot);
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeNode);
|
||||
});
|
||||
|
||||
it('Scan DOM with icon as root', () => {
|
||||
const fakeNode = getNode('scan-dom');
|
||||
setRoot(fakeNode);
|
||||
|
||||
const node = document.createElement('span');
|
||||
node.setAttribute('data-icon', 'mdi:home');
|
||||
|
||||
// Check root nodes list
|
||||
let nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeNode);
|
||||
|
||||
// Scan node
|
||||
scanDOM(node);
|
||||
scanElement(node);
|
||||
|
||||
// Check node
|
||||
expect(node.tagName).to.be.equal('SPAN');
|
||||
expect(node.innerHTML).to.be.equal('');
|
||||
|
||||
// Make sure tempoary node was not added as root
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeNode);
|
||||
});
|
||||
});
|
||||
|
@ -1,14 +1,18 @@
|
||||
import mocha from 'mocha';
|
||||
import chai from 'chai';
|
||||
|
||||
import { getNode } from './node';
|
||||
import { getNode, setRoot } from './node';
|
||||
import { addFinder } from '@iconify/iconify/lib/modules/finder';
|
||||
import { finder as iconifyFinder } from '@iconify/iconify/lib/finders/iconify';
|
||||
import { finder as iconifyIconFinder } from '@iconify/iconify/lib/finders/iconify-icon';
|
||||
import { getStorage, addIconSet } from '@iconify/core/lib/storage';
|
||||
import { setRoot, getRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { listRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { scanDOM } from '@iconify/iconify/lib/modules/scanner';
|
||||
import { initObserver } from '@iconify/iconify/lib/modules/observer';
|
||||
import {
|
||||
initObserver,
|
||||
observeNode,
|
||||
removeObservedNode,
|
||||
} from '@iconify/iconify/lib/modules/observer';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
@ -45,30 +49,38 @@ describe('Observe DOM', () => {
|
||||
|
||||
it('Basic test', (done) => {
|
||||
const node = getNode('observe-dom');
|
||||
const ignoredNode = getNode('observe-dom');
|
||||
|
||||
// Set root and init observer
|
||||
setRoot(node);
|
||||
initObserver(scanDOM);
|
||||
|
||||
// Test getRootNodes
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: node,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
// Test listRootNodes
|
||||
const nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(node);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Set HTML
|
||||
node.innerHTML =
|
||||
'<p>Testing observing DOM</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
ignoredNode.innerHTML =
|
||||
'<p>This node should be ignored</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
|
||||
// Test nodes
|
||||
setTimeout(() => {
|
||||
// Find elements
|
||||
const elements = node.querySelectorAll('svg.iconify');
|
||||
let elements = node.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(1);
|
||||
|
||||
elements = ignoredNode.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(
|
||||
0,
|
||||
'Looks like document.body is observed!'
|
||||
);
|
||||
|
||||
// Test for "home" icon contents
|
||||
expect(node.innerHTML.indexOf('20v-6h4v6h5v')).to.not.be.equal(-1);
|
||||
|
||||
@ -114,4 +126,112 @@ describe('Observe DOM', () => {
|
||||
}, 100);
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('Adding node to observe', (done) => {
|
||||
const baseNode = getNode('observe-dom');
|
||||
const node = getNode('observe-dom');
|
||||
|
||||
// Set root and init observer
|
||||
setRoot(baseNode);
|
||||
initObserver(scanDOM);
|
||||
|
||||
// Test listRootNodes
|
||||
let nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(baseNode);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Observe another node
|
||||
observeNode(node);
|
||||
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(2);
|
||||
expect(nodes[0].node).to.be.equal(baseNode);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
expect(nodes[1].node).to.be.equal(node);
|
||||
expect(nodes[1].temporary).to.be.equal(false);
|
||||
|
||||
// Set HTML
|
||||
baseNode.innerHTML =
|
||||
'<p>Testing observing 2 nodes (1)</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
node.innerHTML =
|
||||
'<p>Testing observing 2 nodes (2)</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
|
||||
// Test nodes
|
||||
setTimeout(() => {
|
||||
// Find elements
|
||||
let elements = node.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(1);
|
||||
|
||||
elements = baseNode.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(1);
|
||||
|
||||
// Test for "home" icon contents
|
||||
expect(node.innerHTML.indexOf('20v-6h4v6h5v')).to.not.be.equal(-1);
|
||||
expect(baseNode.innerHTML.indexOf('20v-6h4v6h5v')).to.not.be.equal(
|
||||
-1
|
||||
);
|
||||
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
it('Stop observing node', (done) => {
|
||||
const baseNode = getNode('observe-dom');
|
||||
const node = getNode('observe-dom');
|
||||
|
||||
// Set root and init observer
|
||||
setRoot(baseNode);
|
||||
initObserver(scanDOM);
|
||||
|
||||
// Test listRootNodes
|
||||
let nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(baseNode);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Observe another node
|
||||
observeNode(node);
|
||||
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(2);
|
||||
expect(nodes[0].node).to.be.equal(baseNode);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
expect(nodes[1].node).to.be.equal(node);
|
||||
expect(nodes[1].temporary).to.be.equal(false);
|
||||
|
||||
// Stop observing baseNode
|
||||
removeObservedNode(baseNode);
|
||||
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(node);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Set HTML
|
||||
baseNode.innerHTML =
|
||||
'<p>Testing observing 2 nodes (1)</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
node.innerHTML =
|
||||
'<p>Testing observing 2 nodes (2)</p>' +
|
||||
'<span class="iconify" data-icon="mdi:home"></span>';
|
||||
|
||||
// Test nodes
|
||||
setTimeout(() => {
|
||||
// Find elements
|
||||
let elements = node.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(1);
|
||||
|
||||
elements = baseNode.querySelectorAll('svg.iconify');
|
||||
expect(elements.length).to.be.equal(0);
|
||||
|
||||
// Test for "home" icon contents
|
||||
expect(node.innerHTML.indexOf('20v-6h4v6h5v')).to.not.be.equal(-1);
|
||||
expect(baseNode.innerHTML.indexOf('20v-6h4v6h5v')).to.be.equal(-1);
|
||||
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import mocha from 'mocha';
|
||||
import chai from 'chai';
|
||||
|
||||
import { getNode } from './node';
|
||||
import { getNode, setRoot } from './node';
|
||||
import { addFinder } from '@iconify/iconify/lib/modules/finder';
|
||||
import { FakeData, setFakeData, prepareQuery, sendQuery } from './fake-api';
|
||||
import { API } from '@iconify/core/lib/api/';
|
||||
@ -10,8 +10,8 @@ import { setAPIConfig } from '@iconify/core/lib/api/config';
|
||||
import { coreModules } from '@iconify/core/lib/modules';
|
||||
import { finder as iconifyFinder } from '@iconify/iconify/lib/finders/iconify';
|
||||
import { finder as iconifyIconFinder } from '@iconify/iconify/lib/finders/iconify-icon';
|
||||
import { setRoot, getRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { scanDOM } from '@iconify/iconify/lib/modules/scanner';
|
||||
import { listRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { scanDOM, scanElement } from '@iconify/iconify/lib/modules/scanner';
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
@ -86,6 +86,7 @@ describe('Scanning DOM with API', () => {
|
||||
setFakeData(provider, prefix2, data2);
|
||||
|
||||
const node = getNode('scan-dom');
|
||||
|
||||
node.innerHTML =
|
||||
'<div><p>Testing scanning DOM with API</p><ul>' +
|
||||
'<li>Inline icons:' +
|
||||
@ -118,13 +119,11 @@ describe('Scanning DOM with API', () => {
|
||||
setRoot(node);
|
||||
scanDOM();
|
||||
|
||||
// Test getRootNodes
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: node,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
// Test listRootNodes
|
||||
const nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(node);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// First API response should have loaded
|
||||
setTimeout(() => {
|
||||
@ -253,13 +252,11 @@ describe('Scanning DOM with API', () => {
|
||||
setRoot(node);
|
||||
scanDOM();
|
||||
|
||||
// Test getRootNodes
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: node,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
// Test listRootNodes
|
||||
const nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(node);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Make sure no icons were rendered yet
|
||||
const elements = node.querySelectorAll('svg.iconify');
|
||||
@ -447,27 +444,22 @@ describe('Scanning DOM with API', () => {
|
||||
|
||||
// Set root node, test nodes list
|
||||
setRoot(fakeRoot);
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: fakeRoot,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
|
||||
// Test listRootNodes
|
||||
let nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeRoot);
|
||||
expect(nodes[0].temporary).to.be.equal(false);
|
||||
|
||||
// Scan different node
|
||||
scanDOM(node);
|
||||
scanElement(node);
|
||||
|
||||
// Test nodes list
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: fakeRoot,
|
||||
temporary: false,
|
||||
},
|
||||
{
|
||||
node: node,
|
||||
temporary: true,
|
||||
},
|
||||
]);
|
||||
// Test listRootNodes
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(2);
|
||||
expect(nodes[0].node).to.be.equal(fakeRoot);
|
||||
expect(nodes[1].node).to.be.equal(node);
|
||||
expect(nodes[1].temporary).to.be.equal(true);
|
||||
|
||||
// API response should have loaded
|
||||
setTimeout(() => {
|
||||
@ -478,12 +470,9 @@ describe('Scanning DOM with API', () => {
|
||||
);
|
||||
|
||||
// Test nodes list: temporary node should have been removed
|
||||
expect(getRootNodes()).to.be.eql([
|
||||
{
|
||||
node: fakeRoot,
|
||||
temporary: false,
|
||||
},
|
||||
]);
|
||||
nodes = listRootNodes();
|
||||
expect(nodes.length).to.be.equal(1);
|
||||
expect(nodes[0].node).to.be.equal(fakeRoot);
|
||||
|
||||
// Done
|
||||
done();
|
||||
|
@ -9,10 +9,16 @@ const expect = chai.expect;
|
||||
const selector =
|
||||
'span.iconify, i.iconify, span.iconify-inline, i.iconify-inline';
|
||||
|
||||
const node = getNode('iconify-api');
|
||||
Iconify.setRoot(node);
|
||||
// Do not observe document.body!
|
||||
Iconify.stopObserving(document.body);
|
||||
|
||||
node.innerHTML =
|
||||
// Create node to observe
|
||||
const observedNode = getNode('iconify-api');
|
||||
const ignoredNode = getNode('iconify-api');
|
||||
|
||||
Iconify.observe(observedNode);
|
||||
|
||||
observedNode.innerHTML =
|
||||
'<div><p>Testing Iconify with API</p><ul>' +
|
||||
'<li>Inline icons:' +
|
||||
' <span class="iconify-inline" data-icon="mdi:home" style="color: red; box-shadow: 0 0 2px black;"></span>' +
|
||||
@ -24,13 +30,23 @@ node.innerHTML =
|
||||
'</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
describe('Testing Iconify object', () => {
|
||||
ignoredNode.innerHTML =
|
||||
'<div>This node should not have icons! <span class="iconify-inline" data-icon="mdi:home" style="color: red; box-shadow: 0 0 2px black;"></span>';
|
||||
|
||||
describe('Testing Iconify object with API', () => {
|
||||
it('Rendering icons with API', () => {
|
||||
// Icons should have been replaced by now
|
||||
let list = node.querySelectorAll(selector);
|
||||
let list = observedNode.querySelectorAll(selector);
|
||||
expect(list.length).to.be.equal(0);
|
||||
|
||||
list = node.querySelectorAll('svg.iconify');
|
||||
list = observedNode.querySelectorAll('svg.iconify');
|
||||
expect(list.length).to.be.equal(4);
|
||||
|
||||
// Icons in ignored node should not have been replaced
|
||||
list = ignoredNode.querySelectorAll(selector);
|
||||
expect(list.length).to.be.equal(1);
|
||||
|
||||
list = ignoredNode.querySelectorAll('svg.iconify');
|
||||
expect(list.length).to.be.equal(0);
|
||||
});
|
||||
});
|
||||
|
@ -12,8 +12,11 @@ const selector =
|
||||
const node1 = getNode('iconify-basic');
|
||||
const node2 = getNode('iconify-basic');
|
||||
|
||||
// Do not observe document.body!
|
||||
Iconify.stopObserving(document.body);
|
||||
|
||||
// Set root node
|
||||
Iconify.setRoot(node1);
|
||||
Iconify.observe(node1);
|
||||
|
||||
describe('Testing Iconify object', () => {
|
||||
const prefix = 'invalid-' + Date.now();
|
||||
|
@ -12,8 +12,11 @@ const selector =
|
||||
const node1 = getNode('iconify-basic');
|
||||
const node2 = getNode('iconify-basic');
|
||||
|
||||
// Do not observe document.body!
|
||||
Iconify.stopObserving(document.body);
|
||||
|
||||
// Set root node
|
||||
Iconify.setRoot(node1);
|
||||
Iconify.observe(node1);
|
||||
|
||||
describe('Testing Iconify object (without API)', () => {
|
||||
const prefix = 'invalid-' + Date.now();
|
||||
|
@ -1,3 +1,7 @@
|
||||
import { addRootNode, listRootNodes } from '@iconify/iconify/lib/modules/root';
|
||||
import { removeObservedNode } from '@iconify/iconify/lib/modules/observer';
|
||||
import { ObservedNode } from '@iconify/iconify/lib/modules/observed-node';
|
||||
|
||||
let counter = 0;
|
||||
|
||||
/**
|
||||
@ -12,3 +16,15 @@ export function getNode(prefix = 'test') {
|
||||
document.getElementById('debug').appendChild(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set root node, remove old nodes
|
||||
*/
|
||||
export function setRoot(node: HTMLElement): ObservedNode {
|
||||
listRootNodes().forEach((node) => {
|
||||
if (typeof node.node !== 'function') {
|
||||
removeObservedNode(node.node);
|
||||
}
|
||||
});
|
||||
return addRootNode(node);
|
||||
}
|
||||
|
2
packages/core/package-lock.json
generated
2
packages/core/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@iconify/core",
|
||||
"version": "1.0.0-beta.2",
|
||||
"version": "1.0.0-beta.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "@iconify/core",
|
||||
"description": "Reusable files used by multiple Iconify packages",
|
||||
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
|
||||
"version": "1.0.0-beta.2",
|
||||
"version": "1.0.0-beta.3",
|
||||
"license": "(Apache-2.0 OR GPL-2.0)",
|
||||
"bugs": "https://github.com/iconify/iconify/issues",
|
||||
"homepage": "https://iconify.design/",
|
||||
|
@ -21,13 +21,13 @@ Iconify SVG framework is designed to be as easy to use as possible.
|
||||
Add this line to your page to load Iconify SVG framework (you can add it to `<head>` section of the page or before `</body>`):
|
||||
|
||||
```html
|
||||
<script src="https://code.iconify.design/2/2.0.0-beta.3/iconify.min.js"></script>
|
||||
<script src="https://code.iconify.design/2/2.0.0-beta.4/iconify.min.js"></script>
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/@iconify/iconify@2.0.0-beta.3/dist/iconify.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@iconify/iconify@2.0.0-beta.4/dist/iconify.min.js"></script>
|
||||
```
|
||||
|
||||
or, if you are building a project with something like WebPack or Rollup, you can include the script by installing `@iconify/iconify` as a dependency and importing it in your project:
|
||||
|
8
packages/iconify/package-lock.json
generated
8
packages/iconify/package-lock.json
generated
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@iconify/iconify",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.0.0-beta.4",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@ -31,9 +31,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@iconify/core": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0-beta.2.tgz",
|
||||
"integrity": "sha512-PIPWM7Z0R8CClnl9kx8c3G8qe4gF+WGvro6IOcYRLw5YsVt0JyagGh5MgTHH8yfhMWlpAgiiROm65tJpKHctQw==",
|
||||
"version": "1.0.0-beta.3",
|
||||
"resolved": "https://registry.npmjs.org/@iconify/core/-/core-1.0.0-beta.3.tgz",
|
||||
"integrity": "sha512-VqdW1vu7uWBI4QhOijVbT4vxDUKLjGNOye7SbJxvB2XQpJIQe9ZKnNqIyX1fO1olSV+Zf4Y1CQhWjw/sOxW+sQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@cyberalien/redundancy": "^1.0.0",
|
||||
|
@ -2,7 +2,7 @@
|
||||
"name": "@iconify/iconify",
|
||||
"description": "Unified SVG framework with over 50,000 icons to choose from",
|
||||
"author": "Vjacheslav Trushkin <cyberalien@gmail.com> (https://iconify.design)",
|
||||
"version": "2.0.0-beta.3",
|
||||
"version": "2.0.0-beta.4",
|
||||
"license": "(Apache-2.0 OR GPL-2.0)",
|
||||
"main": "./dist/iconify.min.js",
|
||||
"types": "./dist/iconify.d.ts",
|
||||
@ -24,7 +24,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cyberalien/redundancy": "^1.0.0",
|
||||
"@iconify/core": "^1.0.0-beta.2",
|
||||
"@iconify/core": "^1.0.0-beta.3",
|
||||
"@iconify/types": "^1.0.3",
|
||||
"@microsoft/api-extractor": "^7.9.2",
|
||||
"@rollup/plugin-buble": "^0.21.3",
|
||||
|
@ -25,13 +25,16 @@ import {
|
||||
initObserver,
|
||||
pauseObserver,
|
||||
resumeObserver,
|
||||
observeNode,
|
||||
removeObservedNode,
|
||||
} from './modules/observer';
|
||||
import { scanDOM } from './modules/scanner';
|
||||
import { scanDOM, scanElement } from './modules/scanner';
|
||||
|
||||
// Finders
|
||||
import { addFinder } from './modules/finder';
|
||||
import { finder as iconifyFinder } from './finders/iconify';
|
||||
import { setRoot } from './modules/root';
|
||||
import { findRootNode, addRootNode, addBodyNode } from './modules/root';
|
||||
import { onReady } from './modules/ready';
|
||||
// import { finder as iconifyIconFinder } from './finders/iconify-icon';
|
||||
|
||||
/**
|
||||
@ -194,23 +197,28 @@ export interface IconifyGlobal {
|
||||
/**
|
||||
* Scan DOM
|
||||
*/
|
||||
scanDOM: typeof scanDOM;
|
||||
scan: (root?: HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* Set root node
|
||||
* Add root node
|
||||
*/
|
||||
setRoot: (root: HTMLElement) => void;
|
||||
observe: (root: HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* Remove root node
|
||||
*/
|
||||
stopObserving: (root: HTMLElement) => void;
|
||||
|
||||
/* Observer */
|
||||
/**
|
||||
* Pause observer
|
||||
*/
|
||||
pauseObserver: typeof pauseObserver;
|
||||
pauseObserver: (root?: HTMLElement) => void;
|
||||
|
||||
/**
|
||||
* Resume observer
|
||||
*/
|
||||
resumeObserver: typeof resumeObserver;
|
||||
resumeObserver: (root?: HTMLElement) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,30 +304,56 @@ export const IconifyCommon: IconifyGlobal = {
|
||||
replaceIDs,
|
||||
|
||||
// Scan DOM
|
||||
scanDOM,
|
||||
scan: (root?: HTMLElement) => {
|
||||
if (root) {
|
||||
scanElement(root);
|
||||
} else {
|
||||
scanDOM();
|
||||
}
|
||||
},
|
||||
|
||||
// Set root node
|
||||
setRoot: (root: HTMLElement) => {
|
||||
setRoot(root);
|
||||
// Add root node
|
||||
observe: (root: HTMLElement) => {
|
||||
observeNode(root);
|
||||
},
|
||||
|
||||
// Restart observer
|
||||
initObserver(scanDOM);
|
||||
|
||||
// Scan DOM on next tick
|
||||
setTimeout(scanDOM);
|
||||
// Remove root node
|
||||
stopObserving: (root: HTMLElement) => {
|
||||
removeObservedNode(root);
|
||||
},
|
||||
|
||||
// Pause observer
|
||||
pauseObserver,
|
||||
pauseObserver: (root?: HTMLElement) => {
|
||||
if (root) {
|
||||
const node = findRootNode(root);
|
||||
if (node) {
|
||||
pauseObserver(node);
|
||||
}
|
||||
} else {
|
||||
pauseObserver();
|
||||
}
|
||||
},
|
||||
|
||||
// Resume observer
|
||||
resumeObserver,
|
||||
resumeObserver: (root?: HTMLElement) => {
|
||||
if (root) {
|
||||
const node = findRootNode(root);
|
||||
if (node) {
|
||||
resumeObserver(node);
|
||||
}
|
||||
} else {
|
||||
resumeObserver();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise stuff
|
||||
*/
|
||||
if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
||||
// Add document.body node
|
||||
addBodyNode();
|
||||
|
||||
// Add finder modules
|
||||
// addFinder(iconifyIconFinder);
|
||||
addFinder(iconifyFinder);
|
||||
@ -362,7 +396,7 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
|
||||
|
||||
// Load observer and scan DOM on next tick
|
||||
setTimeout(() => {
|
||||
scanDOM();
|
||||
initObserver(scanDOM);
|
||||
scanDOM();
|
||||
});
|
||||
}
|
||||
|
27
packages/iconify/src/modules/observed-node.ts
Normal file
27
packages/iconify/src/modules/observed-node.ts
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* Callback
|
||||
*/
|
||||
export type GetHTMLElement = () => HTMLElement | null;
|
||||
|
||||
/**
|
||||
* Observed node type
|
||||
*/
|
||||
export interface ObservedNode {
|
||||
// Node
|
||||
node: HTMLElement | GetHTMLElement;
|
||||
|
||||
// True if node should be removed when all placeholders have been replaced
|
||||
temporary?: boolean;
|
||||
|
||||
// Observer data
|
||||
observer?: {
|
||||
// MutationObserver instance
|
||||
instance?: MutationObserver;
|
||||
|
||||
// Number instead of boolean to allow multiple pause/resume calls. Observer is resumed only when pause reaches 0
|
||||
paused: number;
|
||||
|
||||
// Timer for pending scan
|
||||
pendingScan?: number;
|
||||
};
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
import { elementFinderProperty, IconifyElement } from './element';
|
||||
import { getRoot } from './root';
|
||||
|
||||
/**
|
||||
* MutationObserver instance, null until DOM is ready
|
||||
*/
|
||||
let instance: MutationObserver | null = null;
|
||||
import { ObservedNode } from './observed-node';
|
||||
import {
|
||||
listRootNodes,
|
||||
addRootNode,
|
||||
findRootNode,
|
||||
removeRootNode,
|
||||
} from './root';
|
||||
import { onReady } from './ready';
|
||||
|
||||
/**
|
||||
* Observer callback function
|
||||
*/
|
||||
export type ObserverCallback = (root: HTMLElement) => void;
|
||||
export type ObserverCallback = (item: ObservedNode) => void;
|
||||
|
||||
/**
|
||||
* Callback
|
||||
@ -25,32 +27,20 @@ const observerParams: MutationObserverInit = {
|
||||
attributes: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* Pause. Number instead of boolean to allow multiple pause/resume calls. Observer is resumed only when pause reaches 0
|
||||
*/
|
||||
let paused = 0;
|
||||
|
||||
/**
|
||||
* Scan is pending when observer is resumed
|
||||
*/
|
||||
let scanPending = false;
|
||||
|
||||
/**
|
||||
* Scan is already queued
|
||||
*/
|
||||
let scanQueued = false;
|
||||
|
||||
/**
|
||||
* Queue DOM scan
|
||||
*/
|
||||
function queueScan(): void {
|
||||
if (!scanQueued) {
|
||||
scanQueued = true;
|
||||
setTimeout(() => {
|
||||
scanQueued = false;
|
||||
scanPending = false;
|
||||
function queueScan(node: ObservedNode): void {
|
||||
if (!node.observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = node.observer;
|
||||
if (!observer.pendingScan) {
|
||||
observer.pendingScan = setTimeout(() => {
|
||||
delete observer.pendingScan;
|
||||
if (callback) {
|
||||
callback(getRoot());
|
||||
callback(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -59,8 +49,13 @@ function queueScan(): void {
|
||||
/**
|
||||
* Check mutations for added nodes
|
||||
*/
|
||||
function checkMutations(mutations: MutationRecord[]): void {
|
||||
if (!scanPending) {
|
||||
function checkMutations(node: ObservedNode, mutations: MutationRecord[]): void {
|
||||
if (!node.observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = node.observer;
|
||||
if (!observer.pendingScan) {
|
||||
for (let i = 0; i < mutations.length; i++) {
|
||||
const item = mutations[i];
|
||||
if (
|
||||
@ -71,9 +66,8 @@ function checkMutations(mutations: MutationRecord[]): void {
|
||||
(item.target as IconifyElement)[elementFinderProperty] !==
|
||||
void 0)
|
||||
) {
|
||||
scanPending = true;
|
||||
if (!paused) {
|
||||
queueScan();
|
||||
if (!observer.paused) {
|
||||
queueScan(node);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -84,108 +78,173 @@ function checkMutations(mutations: MutationRecord[]): void {
|
||||
/**
|
||||
* Start/resume observer
|
||||
*/
|
||||
function observe(): void {
|
||||
if (instance) {
|
||||
instance.observe(getRoot(), observerParams);
|
||||
}
|
||||
function observe(node: ObservedNode, root: HTMLElement): void {
|
||||
node.observer.instance.observe(root, observerParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start mutation observer
|
||||
*/
|
||||
function startObserver(): void {
|
||||
if (instance !== null) {
|
||||
function startObserver(node: ObservedNode): void {
|
||||
let observer = node.observer;
|
||||
if (observer && observer.instance) {
|
||||
// Already started
|
||||
return;
|
||||
}
|
||||
|
||||
scanPending = true;
|
||||
instance = new MutationObserver(checkMutations);
|
||||
observe();
|
||||
if (!paused) {
|
||||
queueScan();
|
||||
const root = typeof node.node === 'function' ? node.node() : node.node;
|
||||
if (!root) {
|
||||
// document.body is not available yet
|
||||
return;
|
||||
}
|
||||
|
||||
if (!observer) {
|
||||
observer = {
|
||||
paused: 0,
|
||||
};
|
||||
node.observer = observer;
|
||||
}
|
||||
|
||||
// Create new instance, observe
|
||||
observer.instance = new MutationObserver(checkMutations.bind(null, node));
|
||||
observe(node, root);
|
||||
|
||||
// Scan immediately
|
||||
if (!observer.paused) {
|
||||
queueScan(node);
|
||||
}
|
||||
}
|
||||
|
||||
// Fake interface to test old IE properties
|
||||
interface OldIEElement extends HTMLElement {
|
||||
doScroll?: boolean;
|
||||
/**
|
||||
* Start all observers
|
||||
*/
|
||||
function startObservers(): void {
|
||||
listRootNodes().forEach(startObserver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Export module
|
||||
* Stop observer
|
||||
*/
|
||||
function stopObserver(node: ObservedNode): void {
|
||||
if (!node.observer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = node.observer;
|
||||
|
||||
// Stop scan
|
||||
if (observer.pendingScan) {
|
||||
clearTimeout(observer.pendingScan);
|
||||
delete observer.pendingScan;
|
||||
}
|
||||
|
||||
// Disconnect observer
|
||||
if (observer.instance) {
|
||||
observer.instance.disconnect();
|
||||
delete observer.instance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start observer when DOM is ready
|
||||
*/
|
||||
export function initObserver(cb: ObserverCallback): void {
|
||||
callback = cb;
|
||||
let isRestart = callback !== void 0;
|
||||
|
||||
if (instance && !paused) {
|
||||
// Restart observer
|
||||
instance.disconnect();
|
||||
observe();
|
||||
if (callback !== cb) {
|
||||
// Change callback and stop all pending observers
|
||||
callback = cb;
|
||||
if (isRestart) {
|
||||
listRootNodes().forEach(stopObserver);
|
||||
}
|
||||
}
|
||||
|
||||
if (isRestart) {
|
||||
// Restart instances
|
||||
startObservers();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
const doc = document;
|
||||
if (
|
||||
doc.readyState === 'complete' ||
|
||||
(doc.readyState !== 'loading' &&
|
||||
!(doc.documentElement as OldIEElement).doScroll)
|
||||
) {
|
||||
startObserver();
|
||||
} else {
|
||||
doc.addEventListener('DOMContentLoaded', startObserver);
|
||||
window.addEventListener('load', startObserver);
|
||||
}
|
||||
});
|
||||
// Start observers when document is ready
|
||||
onReady(startObservers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause observer
|
||||
*/
|
||||
export function pauseObserver(root?: HTMLElement): void {
|
||||
if (root && getRoot() !== root) {
|
||||
// Invalid root node
|
||||
export function pauseObserver(node?: ObservedNode): void {
|
||||
(node ? [node] : listRootNodes()).forEach((node) => {
|
||||
if (!node.observer) {
|
||||
node.observer = {
|
||||
paused: 1,
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
paused++;
|
||||
if (paused > 1 || instance === null) {
|
||||
const observer = node.observer;
|
||||
observer.paused++;
|
||||
if (observer.paused > 1 || !observer.instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check pending records, stop observer
|
||||
checkMutations(instance.takeRecords());
|
||||
// Disconnect observer
|
||||
const instance = observer.instance;
|
||||
// checkMutations(node, instance.takeRecords());
|
||||
instance.disconnect();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume observer
|
||||
*/
|
||||
export function resumeObserver(root?: HTMLElement): void {
|
||||
if (root && getRoot() !== root) {
|
||||
// Invalid root node
|
||||
export function resumeObserver(observer?: ObservedNode): void {
|
||||
(observer ? [observer] : listRootNodes()).forEach((node) => {
|
||||
if (!node.observer) {
|
||||
// Start observer
|
||||
startObserver(node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!paused) {
|
||||
return;
|
||||
}
|
||||
paused--;
|
||||
const observer = node.observer;
|
||||
if (observer.paused) {
|
||||
observer.paused--;
|
||||
|
||||
if (!paused && instance) {
|
||||
observe();
|
||||
if (scanPending) {
|
||||
queueScan();
|
||||
if (!observer.paused) {
|
||||
// Start / resume
|
||||
const root =
|
||||
typeof node.node === 'function' ? node.node() : node.node;
|
||||
|
||||
if (!root) {
|
||||
return;
|
||||
} else if (observer.instance) {
|
||||
observe(node, root);
|
||||
} else {
|
||||
startObserver(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if observer is paused
|
||||
* Observe node
|
||||
*/
|
||||
export function isObserverPaused(): boolean {
|
||||
return paused > 0;
|
||||
export function observeNode(
|
||||
root: HTMLElement,
|
||||
autoRemove = false
|
||||
): ObservedNode {
|
||||
const node = addRootNode(root, autoRemove);
|
||||
startObserver(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove observed node
|
||||
*/
|
||||
export function removeObservedNode(root: HTMLElement): void {
|
||||
const node = findRootNode(root);
|
||||
if (node) {
|
||||
stopObserver(node);
|
||||
removeRootNode(root);
|
||||
}
|
||||
}
|
||||
|
21
packages/iconify/src/modules/ready.ts
Normal file
21
packages/iconify/src/modules/ready.ts
Normal file
@ -0,0 +1,21 @@
|
||||
// Fake interface to test old IE properties
|
||||
interface OldIEElement extends HTMLElement {
|
||||
doScroll?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute function when DOM is ready
|
||||
*/
|
||||
export function onReady(callback): void {
|
||||
const doc = document;
|
||||
if (
|
||||
doc.readyState === 'complete' ||
|
||||
(doc.readyState !== 'loading' &&
|
||||
!(doc.documentElement as OldIEElement).doScroll)
|
||||
) {
|
||||
callback();
|
||||
} else {
|
||||
doc.addEventListener('DOMContentLoaded', callback);
|
||||
window.addEventListener('load', callback);
|
||||
}
|
||||
}
|
@ -1,80 +1,77 @@
|
||||
// Default root element
|
||||
let root: HTMLElement;
|
||||
|
||||
// Interface for extra root nodes
|
||||
export interface ExtraRootNode {
|
||||
node: HTMLElement;
|
||||
temporary: boolean; // True if node should be removed when all placeholders have been replaced
|
||||
}
|
||||
|
||||
// Additional root elements
|
||||
let customRoot: ExtraRootNode[] = [];
|
||||
import { ObservedNode } from './observed-node';
|
||||
|
||||
/**
|
||||
* Get root element
|
||||
* List of root nodes
|
||||
*/
|
||||
export function getRoot(): HTMLElement {
|
||||
return root ? root : (document.querySelector('body') as HTMLElement);
|
||||
}
|
||||
let nodes: ObservedNode[] = [];
|
||||
|
||||
/**
|
||||
* Set root element
|
||||
* Find node
|
||||
*/
|
||||
export function setRoot(node: HTMLElement): void {
|
||||
root = node;
|
||||
export function findRootNode(node: HTMLElement): ObservedNode | undefined {
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const item = nodes[i];
|
||||
let root = typeof item.node === 'function' ? item.node() : item.node;
|
||||
if (root === node) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add extra root node
|
||||
*/
|
||||
export function addRoot(node: HTMLElement, autoRemove = false): ExtraRootNode {
|
||||
if (root === node) {
|
||||
return {
|
||||
export function addRootNode(
|
||||
root: HTMLElement,
|
||||
autoRemove = false
|
||||
): ObservedNode {
|
||||
let node = findRootNode(root);
|
||||
if (node) {
|
||||
// Node already exist: switch type if needed
|
||||
if (node.temporary) {
|
||||
node.temporary = autoRemove;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
// Create item, add it to list, start observer
|
||||
node = {
|
||||
node: root,
|
||||
temporary: false,
|
||||
};
|
||||
}
|
||||
|
||||
for (let i = 0; i < customRoot.length; i++) {
|
||||
const item = customRoot[i];
|
||||
if (item.node === node) {
|
||||
// Found matching node
|
||||
if (!autoRemove && item.temporary) {
|
||||
// Change to permanent
|
||||
item.temporary = false;
|
||||
}
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// Add item
|
||||
const item = {
|
||||
node,
|
||||
temporary: autoRemove,
|
||||
};
|
||||
customRoot.push(item);
|
||||
nodes.push(node);
|
||||
|
||||
return item;
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add document.body node
|
||||
*/
|
||||
export function addBodyNode(): ObservedNode {
|
||||
if (document.body) {
|
||||
return addRootNode(document.body);
|
||||
}
|
||||
nodes.push({
|
||||
node: () => {
|
||||
return document.body;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove root node
|
||||
*/
|
||||
export function removeRoot(node: HTMLElement): void {
|
||||
customRoot = customRoot.filter((item) => item.node !== node);
|
||||
export function removeRootNode(root: HTMLElement): void {
|
||||
nodes = nodes.filter((node) => {
|
||||
const element =
|
||||
typeof node.node === 'function' ? node.node() : node.node;
|
||||
return root !== element;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all root nodes
|
||||
* Get list of root nodes
|
||||
*/
|
||||
export function getRootNodes(): ExtraRootNode[] {
|
||||
return (root
|
||||
? [
|
||||
{
|
||||
node: root,
|
||||
temporary: false,
|
||||
},
|
||||
]
|
||||
: []
|
||||
).concat(customRoot);
|
||||
export function listRootNodes(): ObservedNode[] {
|
||||
return nodes;
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import { FullIconifyIcon } from '@iconify/core/lib/icon';
|
||||
import { findPlaceholders } from './finder';
|
||||
import { IconifyElementData, elementDataProperty } from './element';
|
||||
import { renderIcon } from './render';
|
||||
import { pauseObserver, resumeObserver } from './observer';
|
||||
import { ObservedNode } from './observed-node';
|
||||
import {
|
||||
getRoot,
|
||||
getRootNodes,
|
||||
addRoot,
|
||||
removeRoot,
|
||||
ExtraRootNode,
|
||||
} from './root';
|
||||
pauseObserver,
|
||||
resumeObserver,
|
||||
removeObservedNode,
|
||||
observeNode,
|
||||
} from './observer';
|
||||
import { findRootNode, addRootNode, listRootNodes } from './root';
|
||||
|
||||
/**
|
||||
* Flag to avoid scanning DOM too often
|
||||
@ -51,10 +51,29 @@ const compareIcons = (
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Scan node for placeholders
|
||||
*/
|
||||
export function scanElement(root: HTMLElement): void {
|
||||
// Add temporary node
|
||||
let node = findRootNode(root);
|
||||
if (!node) {
|
||||
scanDOM(
|
||||
{
|
||||
node: root,
|
||||
temporary: true,
|
||||
},
|
||||
true
|
||||
);
|
||||
} else {
|
||||
scanDOM(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan DOM for placeholders
|
||||
*/
|
||||
export function scanDOM(customRoot?: HTMLElement): void {
|
||||
export function scanDOM(node?: ObservedNode, addTempNode = false): void {
|
||||
scanQueued = false;
|
||||
|
||||
// List of icons to load: [provider][prefix][name] = boolean
|
||||
@ -63,18 +82,9 @@ export function scanDOM(customRoot?: HTMLElement): void {
|
||||
Record<string, Record<string, boolean>>
|
||||
> = Object.create(null);
|
||||
|
||||
// Add temporary root node
|
||||
let customRootItem: ExtraRootNode;
|
||||
if (customRoot) {
|
||||
customRootItem = addRoot(customRoot, true);
|
||||
}
|
||||
|
||||
// Get root node and placeholders
|
||||
const rootNodes: ExtraRootNode[] = customRoot
|
||||
? [customRootItem]
|
||||
: getRootNodes();
|
||||
rootNodes.forEach((rootItem) => {
|
||||
const root = rootItem.node;
|
||||
// Get placeholders
|
||||
(node ? [node] : listRootNodes()).forEach((node) => {
|
||||
const root = typeof node.node === 'function' ? node.node() : node.node;
|
||||
|
||||
if (!root || !root.querySelectorAll) {
|
||||
return;
|
||||
@ -121,9 +131,9 @@ export function scanDOM(customRoot?: HTMLElement): void {
|
||||
// Check icon
|
||||
const storage = getStorage(provider, prefix);
|
||||
if (storage.icons[name] !== void 0) {
|
||||
// Icon exists - replace placeholder
|
||||
if (!paused && !rootItem.temporary) {
|
||||
pauseObserver(root);
|
||||
// Icon exists - pause observer before replacing placeholder
|
||||
if (!paused && node.observer) {
|
||||
pauseObserver(node);
|
||||
paused = true;
|
||||
}
|
||||
|
||||
@ -178,13 +188,16 @@ export function scanDOM(customRoot?: HTMLElement): void {
|
||||
hasPlaceholders = true;
|
||||
});
|
||||
|
||||
// Remove temporay node
|
||||
if (rootItem.temporary && !hasPlaceholders) {
|
||||
removeRoot(root);
|
||||
}
|
||||
|
||||
if (paused && !rootItem.temporary) {
|
||||
resumeObserver(root);
|
||||
// Node stuff
|
||||
if (node.temporary && !hasPlaceholders) {
|
||||
// Remove temporary node
|
||||
removeObservedNode(root);
|
||||
} else if (addTempNode && hasPlaceholders) {
|
||||
// Add new temporary node
|
||||
observeNode(root, true);
|
||||
} else if (paused && node.observer) {
|
||||
// Resume observer
|
||||
resumeObserver(node);
|
||||
}
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user