diff --git a/src/css/popup.css b/src/css/popup.css index 4ebde85..156019a 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -74,7 +74,7 @@ body .toolbar button.fa { body.tScopeGlobal .toolbar button.scopeRel:not(.disabled) { color: #000; } -body.tScopeLocal .toolbar button.scopeRel:not(.disabled) { +body.tScopeNarrow .toolbar button.scopeRel:not(.disabled) { color: #24c; } @@ -275,7 +275,7 @@ body #scopeCell + .dropdown-menu { body.tScopeGlobal #scopeCell { background-color: #000; } -body.tScopeLocal #scopeCell { +body.tScopeNarrow #scopeCell { background-color: #24c; } diff --git a/src/js/i18n.js b/src/js/i18n.js index f091782..0774548 100644 --- a/src/js/i18n.js +++ b/src/js/i18n.js @@ -22,19 +22,24 @@ // Helper to deal with the i18n'ing of HTML files. // jQuery must be present at this point. -$(function() { - $('[data-i18n]').each(function() { - var me = $(this); - var key = me.data('i18n'); - me.html(chrome.i18n.getMessage(key)); - }); +window.addEventListener('load', function() { + var nodeList = document.querySelectorAll('[data-i18n]'); + var i = nodeList.length; + var node; + while ( i-- ) { + node = nodeList[i]; + node.innerHTML = chrome.i18n.getMessage(node.getAttribute('data-i18n')); + } // copy text of

if any to document title - document.title = $('h1').first().text(); - + node = document.querySelector('h1'); + if ( node !== null ) { + document.title = node.textContent; + } // Tool tips - $('[data-i18n-tip]').each(function() { - var me = $(this); - var key = me.data('i18nTip'); - me.attr('data-tip', chrome.i18n.getMessage(key)); - }); + nodeList = document.querySelectorAll('[data-i18n-tip]'); + i = nodeList.length; + while ( i-- ) { + node = nodeList[i]; + node.setAttribute('data-tip', chrome.i18n.getMessage(node.getAttribute('data-i18n-tip'))); + } }); diff --git a/src/js/matrix.js b/src/js/matrix.js index 4b0eac9..5944a32 100644 --- a/src/js/matrix.js +++ b/src/js/matrix.js @@ -20,6 +20,7 @@ */ /* global µMatrix */ +/* jshint bitwise: false */ /******************************************************************************/ @@ -55,7 +56,7 @@ Matrix.GrayIndirect = Matrix.Gray | Matrix.Indirect; /******************************************************************************/ -typeBitOffsets = { +var typeBitOffsets = { '*': 0, 'cookie': 2, 'css': 4, @@ -258,6 +259,7 @@ Matrix.prototype.evaluateCellZXY = function(srcHostname, desHostname, type) { var rl = this.evaluateCellZ(srcHostname, desHostname, '*'); if ( rl === 1 ) { return Matrix.RedIndirect; } var d = desHostname; + var pos; for (;;) { pos = d.indexOf('.'); if ( pos === -1 ) { @@ -328,15 +330,9 @@ Matrix.prototype.desHostnameFromRule = function(rule) { /******************************************************************************/ -Matrix.prototype.typeFromRule = function(rule) { - return; -}; - -/******************************************************************************/ - Matrix.prototype.extractZRules = function(srcHostname, desHostname, out) { var s = srcHostname; - var rule, bitmap; + var rule, bitmap, pos; for (;;) { rule = s + ' ' + desHostname; bitmap = this.rules[rule]; @@ -358,25 +354,6 @@ Matrix.prototype.extractZRules = function(srcHostname, desHostname, out) { /******************************************************************************/ -Matrix.prototype.extractZYRules = function(srcHostname, desHostname, out) { - var d = srcHostname; - for (;;) { - this.extractZRules(srcHostname, d, out); - pos = d.indexOf('.'); - if ( pos !== -1 ) { - d = d.slice(pos + 1); - continue; - } - if ( d !== '*' ) { - d = '*'; - continue; - } - break; - } -}; - -/******************************************************************************/ - Matrix.prototype.evaluateCellZXYColor = function(srcHostname, desHostname, type) { var v = this.evaluateCellZXY(srcHostname, desHostname, type); if ( v === Matrix.RedIndirect ) { @@ -411,6 +388,7 @@ Matrix.prototype.toggleSwitch = function(srcHostname, newState) { Matrix.prototype.evaluateSwitch = function(srcHostname) { var b; var s = srcHostname; + var pos; for (;;) { b = this.switchedOn[s]; if ( b !== undefined ) { @@ -434,7 +412,7 @@ Matrix.prototype.evaluateSwitch = function(srcHostname) { Matrix.prototype.extractSwitches = function(srcHostname, out) { var s = srcHostname; - var v; + var v, pos; for (;;) { v = this.rules[s]; if ( v !== undefined ) { diff --git a/src/js/popup.js b/src/js/popup.js index 49fe070..43caba5 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -495,17 +495,16 @@ function getCollapseState(domain) { return getUserSetting('popupCollapseDomains'); } -function toggleCollapseState(element) { - var el = uDom(element); - if ( el.ancestors('#matHead.collapsible').length() > 0 ) { - toggleMainCollapseState(el); +function toggleCollapseState(elem) { + if ( elem.ancestors('#matHead.collapsible').length > 0 ) { + toggleMainCollapseState(elem); } else { - toggleSpecificCollapseState(el); + toggleSpecificCollapseState(elem); } } -function toggleMainCollapseState(element) { - var matHead = element.ancestors('#matHead.collapsible').toggleClass('collapsed'); +function toggleMainCollapseState(uelem) { + var matHead = uelem.ancestors('#matHead.collapsible').toggleClass('collapsed'); var collapsed = matHead.hasClassName('collapsed'); uDom('#matList .matSection.collapsible').toggleClass('collapsed', collapsed); setUserSetting('popupCollapseDomains', collapsed); @@ -523,10 +522,10 @@ function toggleMainCollapseState(element) { setUserSetting('popupCollapseSpecificDomains', specificCollapseStates); } -function toggleSpecificCollapseState(element) { +function toggleSpecificCollapseState(uelem) { // Remember collapse state forever, but only if it is different // from main collapse switch. - var section = element.ancestors('.matSection.collapsible').toggleClass('collapsed'); + var section = uelem.ancestors('.matSection.collapsible').toggleClass('collapsed'); var domain = section.prop('domain'); var collapsed = section.hasClassName('collapsed'); var mainCollapseState = getUserSetting('popupCollapseDomains'); @@ -546,13 +545,12 @@ function toggleSpecificCollapseState(element) { // Color changes when rules change function updateMatrixColors() { - var cells = $('.matrix .matRow.rw > .matCell'); + var cells = uDom('.matrix .matRow.rw > .matCell').removeClass(); var i = cells.length; var cell; while ( i-- ) { - cell = $(cells[i]); - cell.removeClass() - .addClass('matCell ' + getCellClass(cell.prop('hostname'), cell.prop('reqType'))); + cell = cells.node(i); + cell.className = 'matCell ' + getCellClass(cell.hostname, cell.reqType); } } @@ -573,17 +571,17 @@ function updateMatrixCounts() { // - It is not part of group 3 (blacklisted hostnames) function updateMatrixBehavior() { - matrixList = matrixList || $('#matList'); - var sections = matrixList.find('.matSection'); + matrixList = matrixList || uDom('#matList'); + var sections = matrixList.descendants('.matSection'); var i = sections.length; var section, subdomainRows, j, subdomainRow; while ( i-- ) { - section = $(sections[i]); - subdomainRows = section.children('.l2:not(.g3)'); + section = sections.unode(i); + subdomainRows = section.descendants('.l2:not(.g3)'); j = subdomainRows.length; while ( j-- ) { - subdomainRow = $(subdomainRows[j]); - subdomainRow.toggleClass('collapsible', subdomainRow.children('.gd,.rd').length === 0); + subdomainRow = subdomainRows.unode(j); + subdomainRow.toggleClass('collapsible', subdomainRow.descendants('.gd,.rd').length === 0); } section.toggleClass('collapsible', subdomainRows.filter('.collapsible').length > 0); } @@ -596,7 +594,7 @@ function updateMatrixBehavior() { function handleFilter(button, leaning) { var µm = µMatrix; // our parent cell knows who we are - var cell = button.closest('div.matCell'); + var cell = button.ancestors('div.matCell'); var type = cell.prop('reqType'); var hostname = cell.prop('hostname'); var nextAction = getNextAction(hostname, type, leaning); @@ -676,7 +674,7 @@ function getTemporaryRuleset() { if ( permanentSwitches.hasOwnProperty(k) === false ) { continue; } - if ( temporarySwitches[rule] === undefined ) { + if ( temporarySwitches[k] === undefined ) { ruleset.switchesToRemove.push(k); } } @@ -696,15 +694,15 @@ var matrixRowTemplate = null; var matrixList = null; var startMatrixUpdate = function() { - matrixList = matrixList || $('#matList'); + matrixList = matrixList || uDom('#matList'); matrixList.detach(); - var rows = matrixList.find('.matRow'); + var rows = matrixList.descendants('.matRow'); rows.detach(); matrixRowPool = matrixRowPool.concat(rows.toArray()); - var sections = matrixList.find('.matSection'); + var sections = matrixList.descendants('.matSection'); sections.detach(); matrixSectionPool = matrixSectionPool.concat(sections.toArray()); - var groups = matrixList.find('.matGroup'); + var groups = matrixList.descendants('.matGroup'); groups.detach(); matrixGroupPool = matrixGroupPool.concat(groups.toArray()); }; @@ -720,36 +718,36 @@ var endMatrixUpdate = function() { } updateMatrixBehavior(); matrixList.css('display', ''); - matrixList.appendTo($('.paneContent')); + matrixList.appendTo('.paneContent'); }; var createMatrixGroup = function() { var group = matrixGroupPool.pop(); if ( group ) { - return $(group).removeClass().addClass('matGroup'); + return uDom(group).removeClass().addClass('matGroup'); } - return $('
').addClass('matGroup'); + return uDom('
').addClass('matGroup'); }; var createMatrixSection = function() { var section = matrixSectionPool.pop(); if ( section ) { - return $(section).removeClass().addClass('matSection'); + return uDom(section).removeClass().addClass('matSection'); } - return $('
').addClass('matSection'); + return uDom('
').addClass('matSection'); }; var createMatrixRow = function() { var row = matrixRowPool.pop(); if ( row ) { row.style.visibility = ''; - row = $(row); - row.children('.matCell').removeClass().addClass('matCell'); + row = uDom(row); + row.descendants('.matCell').removeClass().addClass('matCell'); row.removeClass().addClass('matRow'); return row; } if ( matrixRowTemplate === null ) { - matrixRowTemplate = $('#templates .matRow'); + matrixRowTemplate = uDom('#templates .matRow'); } return matrixRowTemplate.clone(); }; @@ -759,7 +757,7 @@ var createMatrixRow = function() { function renderMatrixHeaderRow() { var matHead = uDom('#matHead.collapsible'); matHead.toggleClass('collapsed', getUserSetting('popupCollapseDomains')); - var cells = matHead.find('.matCell'); + var cells = matHead.descendants('.matCell'); uDom(cells.node(0)) .prop('reqType', '*') .prop('hostname', '*') @@ -845,38 +843,38 @@ function renderMatrixCellType(cell, hostname, type, stats) { } function renderMatrixCellTypes(cells, hostname, stats) { - renderMatrixCellType(cells[1], hostname, 'cookie', stats.cookie); - renderMatrixCellType(cells[2], hostname, 'css', stats.css); - renderMatrixCellType(cells[3], hostname, 'image', stats.image); - renderMatrixCellType(cells[4], hostname, 'plugin', stats.plugin); - renderMatrixCellType(cells[5], hostname, 'script', stats.script); - renderMatrixCellType(cells[6], hostname, 'xhr', stats.xhr); - renderMatrixCellType(cells[7], hostname, 'frame', stats.frame); - renderMatrixCellType(cells[8], hostname, 'other', stats.other); + renderMatrixCellType(cells.unode(1), hostname, 'cookie', stats.cookie); + renderMatrixCellType(cells.unode(2), hostname, 'css', stats.css); + renderMatrixCellType(cells.unode(3), hostname, 'image', stats.image); + renderMatrixCellType(cells.unode(4), hostname, 'plugin', stats.plugin); + renderMatrixCellType(cells.unode(5), hostname, 'script', stats.script); + renderMatrixCellType(cells.unode(6), hostname, 'xhr', stats.xhr); + renderMatrixCellType(cells.unode(7), hostname, 'frame', stats.frame); + renderMatrixCellType(cells.unode(8), hostname, 'other', stats.other); } /******************************************************************************/ function makeMatrixRowDomain(domain) { var matrixRow = createMatrixRow().addClass('rw'); - var cells = matrixRow.children('.matCell'); - renderMatrixCellDomain(cells[0], domain); + var cells = matrixRow.descendants('.matCell'); + renderMatrixCellDomain(cells.node(0), domain); renderMatrixCellTypes(cells, domain, HTTPSBPopup.matrixStats[domain].types); return matrixRow; } function makeMatrixRowSubdomain(domain, subdomain) { var matrixRow = createMatrixRow().addClass('rw'); - var cells = matrixRow.children('.matCell'); - renderMatrixCellSubdomain(cells[0], domain, subdomain); + var cells = matrixRow.descendants('.matCell'); + renderMatrixCellSubdomain(cells.node(0), domain, subdomain); renderMatrixCellTypes(cells, subdomain, HTTPSBPopup.matrixStats[subdomain].types); return matrixRow; } function makeMatrixMetaRowDomain(domain, stats) { var matrixRow = createMatrixRow().addClass('rw'); - var cells = matrixRow.children('.matCell'); - renderMatrixMetaCellDomain(cells[0], domain); + var cells = matrixRow.descendants('.matCell'); + renderMatrixMetaCellDomain(cells.node(0), domain); renderMatrixCellTypes(cells, domain, stats); return matrixRow; } @@ -893,11 +891,9 @@ function renderMatrixMetaCellType(cell, count) { function makeMatrixMetaRow(stats) { var typeStats = stats.types; - var matrixRow = uDom(createMatrixRow()[0]).addClass('ro'); - var cells = matrixRow.find('.matCell'); - var contents = uDom(cells.node(0)) - .addClass('matCell rd') - .contents(); + var matrixRow = createMatrixRow().unode(0).addClass('ro'); + var cells = matrixRow.descendants('.matCell'); + var contents = cells.unode(0).addClass('rd').contents(); contents.node(0).textContent = ' '; contents.node(1).textContent = '\u202A' + typeStats['*'].count + ' blacklisted hostname(s)'; renderMatrixMetaCellType(cells.node(1), typeStats.cookie.count); @@ -908,7 +904,7 @@ function makeMatrixMetaRow(stats) { renderMatrixMetaCellType(cells.node(6), typeStats.xhr.count); renderMatrixMetaCellType(cells.node(7), typeStats.frame.count); renderMatrixMetaCellType(cells.node(8), typeStats.other.count); - return $(matrixRow.node(0)); + return matrixRow; } /******************************************************************************/ @@ -1204,7 +1200,7 @@ function initMenuEnvironment() { var cell, key, text; while ( i-- ) { key = keys[i]; - cell = $('#matHead .matCell[data-filter-type="'+ key +'"]'); + cell = uDom('#matHead .matCell[data-filter-type="'+ key +'"]'); text = chrome.i18n.getMessage(key + 'PrettyName'); cell.text(text); prettyNames[key] = text; @@ -1242,19 +1238,14 @@ function createSiteScope() { dropDownMenuHide(); } -function getClassSuffixFromScopeKey(scopeKey) { - if ( scopeKey === '*' ) { - return 'ScopeGlobal'; +function getClassFromTargetScope() { + if ( targetScope === '*' ) { + return 'tScopeGlobal'; } - return 'ScopeLocal'; -} - -function getClassFromTemporaryScopeKey(scopeKey) { - return 't' + getClassSuffixFromScopeKey(scopeKey); -} - -function getClassFromPermanentScopeKey(scopeKey) { - return 'p' + getClassSuffixFromScopeKey(scopeKey); + if ( targetScope === targetPageDomain ) { + return 'tScopeNarrow'; + } + return 'tScopeNarrow'; } function initScopeCell() { @@ -1264,21 +1255,19 @@ function initScopeCell() { return; } // Fill in the scope menu entries - var µm = µMatrix; if ( targetPageDomain === '' || targetPageDomain === targetPageHostname ) { - $('#scopeKeyDomain').css('display', 'none'); + uDom('#scopeKeyDomain').css('display', 'none'); } else { - $('#scopeKeyDomain').text(targetPageDomain); + uDom('#scopeKeyDomain').text(targetPageDomain); } - $('#scopeKeySite').text(targetPageHostname); + uDom('#scopeKeySite').text(targetPageHostname); updateScopeCell(); } function updateScopeCell() { - var µm = µMatrix; uDom('body') - .removeClass('tScopeGlobal tScopeLocal tScopeSite') - .addClass(getClassFromTemporaryScopeKey(targetScope)) + .removeClass('tScopeGlobal tScopeNarrow') + .addClass(getClassFromTargetScope()); uDom('#scopeCell').text(targetScope.replace('*', '\u2217')); } @@ -1289,11 +1278,11 @@ function updateMtxbutton() { var masterSwitch = µm.getTemporaryMtxFiltering(targetScope); var pageStats = getPageStats(); var count = pageStats ? pageStats.requestStats.blocked.all : ''; - var button = $('#buttonMtxFiltering'); + var button = uDom('#buttonMtxFiltering'); button.toggleClass('disabled', !masterSwitch); - button.children('span.badge').text(µm.formatCount(count)); - button.attr('data-tip', button.data('tip').replace('{{count}}', count)); - $('body').toggleClass('powerOff', !masterSwitch); + button.descendants('span.badge').text(µm.formatCount(count)); + button.attr('data-tip', button.attr('data-tip').replace('{{count}}', count)); + uDom('body').toggleClass('powerOff', !masterSwitch); } function toggleMtxFiltering() { @@ -1309,12 +1298,12 @@ function toggleMtxFiltering() { function updatePersistButton() { var ruleset = getTemporaryRuleset(); - var button = $('#buttonPersist'); + var button = uDom('#buttonPersist'); button.contents() .filter(function(){return this.nodeType===3;}) - .first()[0] - .textContent = ruleset.count > 0 ? '\uf13e' : '\uf023'; - button.children('span.badge').text(ruleset.count > 0 ? ruleset.count : ''); + .first() + .text(ruleset.count > 0 ? '\uf13e' : '\uf023'); + button.descendants('span.badge').text(ruleset.count > 0 ? ruleset.count : ''); var disabled = ruleset.count === 0; button.toggleClass('disabled', disabled); uDom('#buttonRevertScope').toggleClass('disabled', disabled); @@ -1405,7 +1394,7 @@ function gotoExternalURL() { /******************************************************************************/ function dropDownMenuShow() { - $(this).next('.dropdown-menu').addClass('show'); + uDom(this).next('.dropdown-menu').addClass('show'); } function dropDownMenuHide() { @@ -1443,8 +1432,8 @@ var bindToTab = function(tabs) { // After popup menu is built, check whether there is a non-empty matrix if ( !targetPageURL ) { - $('#matHead').remove(); - $('#toolbarLeft').remove(); + uDom('#matHead').remove(); + uDom('#toolbarLeft').remove(); // https://github.com/gorhill/httpswitchboard/issues/191 uDom('#noNetTrafficPrompt').text(chrome.i18n.getMessage('matrixNoNetTrafficPrompt')); @@ -1456,55 +1445,50 @@ var bindToTab = function(tabs) { // Make menu only when popup html is fully loaded -$(function() { +uDom.onLoad(function() { chrome.tabs.query({ currentWindow: true, active: true }, bindToTab); // Below is UI stuff which is not key to make the menu, so this can // be done without having to wait for a tab to be bound to the menu. - var popup = HTTPSBPopup; - // Matrix appearance - $('body').css('font-size', getUserSetting('displayTextSize')); - $('body').toggleClass('colorblind', getUserSetting('colorBlindFriendly') === true); + uDom('body').css('font-size', getUserSetting('displayTextSize')); + uDom('body').toggleClass('colorblind', getUserSetting('colorBlindFriendly') === true); // We reuse for all cells the one and only cell hotspots. - matrixCellHotspots = $('#cellHotspots').detach(); - $('#whitelist', matrixCellHotspots) - .on('click', function() { - handleWhitelistFilter($(this)); + uDom('#whitelist').on('click', function() { + handleWhitelistFilter(uDom(this)); return false; }); - $('#blacklist', matrixCellHotspots) - .on('click', function() { - handleBlacklistFilter($(this)); + uDom('#blacklist').on('click', function() { + handleBlacklistFilter(uDom(this)); return false; }); - $('#domainOnly', matrixCellHotspots) - .on('click', function() { - toggleCollapseState(this); + uDom('#domainOnly').on('click', function() { + toggleCollapseState(uDom(this)); return false; }); - $('body') + matrixCellHotspots = uDom('#cellHotspots').detach(); + uDom('body') .on('mouseenter', '.matCell', mouseenterMatrixCellHandler) .on('mouseleave', '.matCell', mouseleaveMatrixCellHandler); - $('#scopeKeyGlobal').on('click', createGlobalScope); - $('#scopeKeyDomain').on('click', createDomainScope); - $('#scopeKeySite').on('click', createSiteScope); - $('#buttonMtxFiltering').on('click', toggleMtxFiltering); - $('#buttonPersist').on('click', persistScope); - $('#buttonRevertScope').on('click', revertScope); + uDom('#scopeKeyGlobal').on('click', createGlobalScope); + uDom('#scopeKeyDomain').on('click', createDomainScope); + uDom('#scopeKeySite').on('click', createSiteScope); + uDom('#buttonMtxFiltering').on('click', toggleMtxFiltering); + uDom('#buttonPersist').on('click', persistScope); + uDom('#buttonRevertScope').on('click', revertScope); - $('#buttonRevertAll').on('click', revertAll); - $('#buttonReload').on('click', buttonReloadHandler); - $('.extensionURL').on('click', gotoExtensionURL); - $('.externalURL').on('click', gotoExternalURL); + uDom('#buttonRevertAll').on('click', revertAll); + uDom('#buttonReload').on('click', buttonReloadHandler); + uDom('.extensionURL').on('click', gotoExtensionURL); + uDom('.externalURL').on('click', gotoExternalURL); - $('body').on('click', '.dropdown-menu-button', dropDownMenuShow); - $('body').on('click', '.dropdown-menu-capture', dropDownMenuHide); + uDom('body').on('click', '.dropdown-menu-button', dropDownMenuShow); + uDom('body').on('click', '.dropdown-menu-capture', dropDownMenuHide); - $('#matList').on('click', '.g3Meta', function() { + uDom('#matList').on('click', '.g3Meta', function() { var collapsed = uDom(this) .toggleClass('g3Collapsed') .hasClass('g3Collapsed'); diff --git a/src/js/udom.js b/src/js/udom.js index 5782e16..5bd8855 100644 --- a/src/js/udom.js +++ b/src/js/udom.js @@ -38,12 +38,15 @@ var DOMList = function() { /******************************************************************************/ -var addNodeToList = function(list, node) { - if ( node ) { - list.nodes.push(node); +Object.defineProperty( + DOMList.prototype, + 'length', + { + get: function() { + return this.nodes.length; + } } - return list; -}; +); /******************************************************************************/ @@ -78,6 +81,15 @@ DOMListFactory.onLoad = function(callback) { /******************************************************************************/ +var addNodeToList = function(list, node) { + if ( node ) { + list.nodes.push(node); + } + return list; +}; + +/******************************************************************************/ + var addNodeListToList = function(list, nodelist) { if ( nodelist ) { var n = nodelist.length; @@ -114,6 +126,8 @@ var pTagOfChildTag = { 'option': 'select' }; +// TODO: documentFragment + var addHTMLToList = function(list, html) { var matches = html.match(/^<([a-z]+)/); if ( !matches || matches.length !== 2 ) { @@ -134,6 +148,12 @@ var addHTMLToList = function(list, html) { /******************************************************************************/ +var isChildOf = function(child, parent) { + return child !== null && parent !== null && child.parentNode === parent; +}; + +/******************************************************************************/ + var isDescendantOf = function(descendant, ancestor) { while ( descendant.parentNode !== null ) { if ( descendant.parentNode === ancestor ) { @@ -146,6 +166,34 @@ var isDescendantOf = function(descendant, ancestor) { /******************************************************************************/ +var doesMatchSelector = function(node, selector) { + if ( !node ) { + return false; + } + if ( node.nodeType !== 1 ) { + return false; + } + if ( selector === undefined ) { + return true; + } + var parentNode = node.parentNode; + if ( !parentNode || !parentNode.setAttribute ) { + return false; + } + var doesMatch = false; + parentNode.setAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO', ''); + var grandpaNode = parentNode.parentNode || document; + var nl = grandpaNode.querySelectorAll('[uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO] > ' + selector); + var i = nl.length; + while ( doesMatch === false && i-- ) { + doesMatch = nl[i] === node; + } + parentNode.removeAttribute('uDom-32kXc6xEZA7o73AMB8vLbLct1RZOkeoO'); + return doesMatch; +}; + +/******************************************************************************/ + DOMList.prototype.length = function() { return this.nodes.length; }; @@ -156,6 +204,16 @@ DOMList.prototype.node = function(i) { return this.nodes[i]; }; +DOMList.prototype.unode = function(i) { + return addNodeToList(new DOMList(), this.nodes[i]); +}; + +/******************************************************************************/ + +DOMList.prototype.toArray = function() { + return this.nodes.slice(); +}; + /******************************************************************************/ DOMList.prototype.subset = function(i, l) { @@ -176,6 +234,29 @@ DOMList.prototype.first = function() { /******************************************************************************/ +DOMList.prototype.next = function(selector) { + var r = new DOMList(); + var n = this.nodes.length; + var node; + for ( var i = 0; i < n; i++ ) { + node = this.nodes[i]; + while ( node.nextSibling !== null ) { + node = node.nextSibling; + if ( node.nodeType !== 1 ) { + continue; + } + if ( doesMatchSelector(node, selector) === false ) { + continue; + } + addNodeToList(r, node); + break; + } + } + return r; +}; + +/******************************************************************************/ + DOMList.prototype.parent = function() { var r = new DOMList(); if ( this.nodes.length ) { @@ -186,21 +267,26 @@ DOMList.prototype.parent = function() { /******************************************************************************/ -DOMList.prototype.ancestors = function(selector) { +DOMList.prototype.filter = function(filter) { var r = new DOMList(); - if ( this.nodes.length === 0 ) { - return r; + var filterFunc; + if ( typeof filter === 'string' ) { + filterFunc = function() { + return doesMatchSelector(this, filter); + }; + } else if ( typeof filter === 'function' ) { + filterFunc = filter; + } else { + filterFunc = function(){ + return true; + }; } - var candidates = document.querySelectorAll(selector); - var i = candidates.length; - var j, candidate; - while ( i-- ) { - candidate = candidates[i]; - j = this.nodes.length; - while ( j-- ) { - if ( isDescendantOf(this.nodes[j], candidate) ) { - addNodeToList(r, candidate); - } + var n = this.nodes.length; + var node; + for ( var i = 0; i < n; i++ ) { + node = this.nodes[i]; + if ( filterFunc.apply(node) ) { + addNodeToList(r, node); } } return r; @@ -208,7 +294,27 @@ DOMList.prototype.ancestors = function(selector) { /******************************************************************************/ -DOMList.prototype.find = function(selector) { +// TODO: Avoid possible duplicates + +DOMList.prototype.ancestors = function(selector) { + var r = new DOMList(); + var n = this.nodes.length; + var node; + for ( var i = 0; i < n; i++ ) { + node = this.nodes[i].parentNode; + while ( node ) { + if ( doesMatchSelector(node, selector) ) { + addNodeToList(r, node); + } + node = node.parentNode; + } + } + return r; +}; + +/******************************************************************************/ + +DOMList.prototype.descendants = function(selector) { var r = new DOMList(); var n = this.nodes.length; var nl; @@ -248,17 +354,19 @@ DOMList.prototype.forEach = function(callback) { /******************************************************************************/ DOMList.prototype.remove = function() { - var n = this.nodes.length; - var c, p; - for ( var i = 0; i < n; i++ ) { - c = this.nodes[i]; - if ( p = c.parentNode ) { - p.removeChild(c); + var cn, p; + var i = this.nodes.length; + while ( i-- ) { + cn = this.nodes[i]; + if ( p = cn.parentNode ) { + p.removeChild(cn); } } return this; }; +DOMList.prototype.detach = DOMList.prototype.remove; + /******************************************************************************/ DOMList.prototype.empty = function() { @@ -304,12 +412,10 @@ DOMList.prototype.prepend = function(selector, context) { /******************************************************************************/ DOMList.prototype.appendTo = function(selector, context) { - var p = DOMListFactory(selector, context); - if ( p.length ) { - var n = this.nodes.length; - for ( var i = 0; i < n; i++ ) { - p.nodes[0].appendChild(this.nodes[i]); - } + var p = selector instanceof DOMListFactory ? selector : DOMListFactory(selector, context); + var n = p.length; + for ( var i = 0; i < n; i++ ) { + p.nodes[0].appendChild(this.nodes[i]); } return this; }; @@ -491,22 +597,10 @@ DOMList.prototype.toggleClass = function(className, targetState) { /******************************************************************************/ -var makeEventHandler = function(context, selector, callback) { +var makeEventHandler = function(selector, callback) { return function(event) { - var candidates = context.querySelectorAll(selector); - if ( !candidates.length ) { - return; - } - var node = event.target; - var i; - while ( node && node !== context ) { - i = candidates.length; - while ( i-- ) { - if ( candidates[i] === node ) { - return callback.call(node, event); - } - } - node = node.parentNode; + if ( doesMatchSelector(event.target, selector) ) { + callback.call(event.target, event); } }; }; @@ -519,7 +613,7 @@ DOMList.prototype.on = function(etype, selector, callback) { var i = this.nodes.length; while ( i-- ) { if ( selector !== undefined ) { - this.nodes[i].addEventListener(etype, makeEventHandler(this.nodes[i], selector, callback), true); + this.nodes[i].addEventListener(etype, makeEventHandler(selector, callback), true); } else { this.nodes[i].addEventListener(etype, callback); } diff --git a/src/popup.html b/src/popup.html index a180bfa..163d46e 100644 --- a/src/popup.html +++ b/src/popup.html @@ -72,7 +72,6 @@
-