diff options
author | Einar Egilsson <einar@einaregilsson.com> | 2020-02-04 22:37:30 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-04 22:37:30 +0000 |
commit | 7a1c186b14fae578940f319a338f22b962316a41 (patch) | |
tree | d0fec15912e3a03e3487c7fe1e82840bb8c49b80 | |
parent | 5a52a13850607443d8f76ecad92e498a66d13166 (diff) | |
parent | e160789614b80a86133b820e1419c9457c5bf3e2 (diff) |
Merge pull request #184 from ncolletti/nav-btns
added move to First and Last buttons
-rw-r--r-- | css/redirector.css | 87 | ||||
-rw-r--r-- | js/organizemode.js | 42 | ||||
-rw-r--r-- | js/redirect.js | 48 | ||||
-rw-r--r-- | js/redirectorpage.js | 194 | ||||
-rw-r--r-- | js/util.js | 8 | ||||
-rw-r--r-- | redirector.html | 25 |
6 files changed, 351 insertions, 53 deletions
diff --git a/css/redirector.css b/css/redirector.css index 34ecaa3..71c7d74 100644 --- a/css/redirector.css +++ b/css/redirector.css @@ -114,6 +114,10 @@ input[type="radio"] { border:solid 1px green; } +.btn.blue.active { + color: white; + background:rgb(21,90,233); +} #redirect-row-template { display: none; @@ -235,10 +239,14 @@ button span { color:red !important; } -.move-up-btn, .move-down-btn { +/* nav btns */ +.move-up-btn, .move-down-btn, .move-downbottom-btn, .move-uptop-btn { width:45px !important; } +.move-downbottom-btn, .move-uptop-btn { + height:25px !important; +} .redirect-row label { display:inline-block; @@ -258,6 +266,65 @@ a.disabled:hover, button[disabled]:hover { background:white !important; } +/* Toggle Grouping Checkbox */ +.toggle-container { + display: block; + position: absolute; + top: 8%; + right: 5%; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.toggle-container input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +.checkmark { + position: absolute; + height: 15px; + width: 15px; + background-color: #eee; +} + +.toggle-container:hover input ~ .checkmark { + background-color: #ccc; +} + +.toggle-container input:checked ~ .checkMarked { + background-color: #2196F3; +} + +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +.toggle-container input:checked ~ .checkMarked:after { + display: block; +} + +.toggle-container .checkMarked:after { + left: 4px; + top: -1px; + width: 4px; + height: 9px; + border: solid white; + border-width: 0 3px 3px 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} + /* Popup form for deleting redirects */ #delete-redirect-form { @@ -312,8 +379,8 @@ a.disabled:hover, button[disabled]:hover { } .blur { - -webkit-filter:blur(3px); - filter:blur(3px); + -webkit-filter:blur(3px); + filter:blur(3px); } #edit-redirect-form { @@ -353,7 +420,7 @@ a.disabled:hover, button[disabled]:hover { .input-cell { padding-top:1px; -} +} .form-grid div input[type='text'] { width:510px; @@ -404,7 +471,7 @@ a.disabled:hover, button[disabled]:hover { } ::-moz-placeholder { /* Firefox 19+ */ - color: #c0c0c0; + color: #c0c0c0; } .advanced { @@ -509,10 +576,10 @@ footer small a:hover { .redirect-row:nth-child(odd) { background: rgb(31,32,35); - } + } .redirect-row:nth-child(even) { background: rgb(41,42,45); - } + } .btn { background-color: rgb(32,33,36); @@ -527,6 +594,10 @@ footer small a:hover { color: rgb(53,180,75); } + .toggle { + background-color: #ccc; + } + #message-box.success { background-color: rgb(53,203,75);; } @@ -590,7 +661,7 @@ footer small a:hover { } .example-result-error { - color:rgb(252,87,84) !important; + color:rgb(252,87,84) !important; } #edit-redirect-form input { diff --git a/js/organizemode.js b/js/organizemode.js new file mode 100644 index 0000000..800a53f --- /dev/null +++ b/js/organizemode.js @@ -0,0 +1,42 @@ + +function displayOrganizeModeMessage() { + if(el('#message-box').classList.contains('visible')) { + hideMessage(); + } else { + showMessage("Use ⟱ to move a redirect to the bottom, ⟰ to move to the top, and use the checkboxes to select multiple redirects and move them together.", true) + } +} + +function organizeModeToggle(ev) { + ev.preventDefault(); + let organizeModes = ['.groupings', '.arrows'] + for (let mode of organizeModes) { + let organizeModeElms = document.querySelectorAll(mode); + for (i = 0; i < organizeModeElms.length; ++i) { + let elm = organizeModeElms[i]; + let isHidden = ''; + if(mode === '.arrows') { + // targeting parent span for arrows + elm = elm.parentElement; + } + isHidden = elm.classList.contains('hidden'); + isHidden ? elm.classList.remove('hidden') : elm.classList.add('hidden'); + } + } + + // let buttonText = el('#organize-mode').textContent; + // buttonText.includes('Show') ? el('#organize-mode').textContent = 'Hide Organize' : el('#organize-mode').textContent = 'Show Organize'; + + let buttonClasses = el('#organize-mode').classList; + console.log('NSC: organizeModeToggle -> buttonClasses', buttonClasses); + !buttonClasses.contains('active') ? el('#organize-mode').classList.add('active') : el('#organize-mode').classList.remove('active'); + + displayOrganizeModeMessage(); +} + + +function setupOrganizeModeToggleEventListener() { + el('#organize-mode').addEventListener('click', organizeModeToggle); +} + +setupOrganizeModeToggleEventListener();
\ No newline at end of file diff --git a/js/redirect.js b/js/redirect.js index 4ad57d5..b902da2 100644 --- a/js/redirect.js +++ b/js/redirect.js @@ -27,7 +27,7 @@ Redirect.requestTypes = { Redirect.prototype = { - + //attributes description : '', exampleUrl : '', @@ -40,7 +40,8 @@ Redirect.prototype = { patternType : '', processMatches : 'noProcessing', disabled : false, - + grouped: false, + compile : function() { var incPattern = this._preparePattern(this.includePattern); @@ -65,7 +66,7 @@ Redirect.prototype = { && this.processMatches == redirect.processMatches && this.appliesTo.toString() == redirect.appliesTo.toString(); }, - + toObject : function() { return { description : this.description, @@ -79,6 +80,7 @@ Redirect.prototype = { patternType : this.patternType, processMatches : this.processMatches, disabled : this.disabled, + grouped: this.grouped, appliesTo : this.appliesTo.slice(0) }; }, @@ -87,10 +89,10 @@ Redirect.prototype = { if (!this._rxInclude) { this.compile(); } - var result = { - isMatch : false, - isExcludeMatch : false, - isDisabledMatch : false, + var result = { + isMatch : false, + isExcludeMatch : false, + isDisabledMatch : false, redirectTo : '', toString : function() { return JSON.stringify(this); } }; @@ -106,11 +108,11 @@ Redirect.prototype = { result.redirectTo = redirectTo; } } - return result; + return result; }, - + //Updates the .exampleResult field or the .error - //field depending on if the example url and patterns match + //field depending on if the example url and patterns match //and make a good redirect updateExampleResult : function() { @@ -171,25 +173,25 @@ Redirect.prototype = { isRegex: function() { return this.patternType == Redirect.REGEX; }, - + isWildcard : function() { - return this.patternType == Redirect.WILDCARD; + return this.patternType == Redirect.WILDCARD; }, test : function() { - return this.getMatch(this.exampleUrl); + return this.getMatch(this.exampleUrl); }, - //Private functions below + //Private functions below _rxInclude : null, _rxExclude : null, - + _preparePattern : function(pattern) { if (!pattern) { return null; } if (this.patternType == Redirect.REGEX) { - return pattern; + return pattern; } else { //Convert wildcard to regex pattern var converted = '^'; for (var i = 0; i < pattern.length; i++) { @@ -206,7 +208,7 @@ Redirect.prototype = { return converted; } }, - + _init : function(o) { o = o || {}; this.description = o.description || ''; @@ -240,7 +242,7 @@ Redirect.prototype = { get appliesToText() { return this.appliesTo.map(type => Redirect.requestTypes[type]).join(', '); }, - + get processMatchesExampleText() { let examples = { noProcessing : 'Use matches as they are', @@ -256,11 +258,11 @@ Redirect.prototype = { toString : function() { return JSON.stringify(this.toObject(), null, 2); }, - + _includeMatch : function(url) { if (!this._rxInclude) { return null; - } + } var matches = this._rxInclude.exec(url); if (!matches) { return null; @@ -285,12 +287,12 @@ Redirect.prototype = { this._rxInclude.lastIndex = 0; return resultUrl; }, - + _excludeMatch : function(url) { if (!this._rxExclude) { - return false; + return false; } - var shouldExclude = this._rxExclude.test(url); + var shouldExclude = this._rxExclude.test(url); this._rxExclude.lastIndex = 0; return shouldExclude; } diff --git a/js/redirectorpage.js b/js/redirectorpage.js index f82b0b7..959b73c 100644 --- a/js/redirectorpage.js +++ b/js/redirectorpage.js @@ -23,15 +23,15 @@ function saveChanges() { } }); } - + function toggleSyncSetting() { chrome.runtime.sendMessage({type:"toggle-sync", isSyncEnabled: !options.isSyncEnabled}, function(response) { if(response.message === "sync-enabled"){ options.isSyncEnabled = true; - showMessage('Sync is enabled!',true); + showMessage('Sync is enabled!', true); } else if(response.message === "sync-disabled"){ options.isSyncEnabled = false; - showMessage('Sync is disabled - local storage will be used!',true); + showMessage('Sync is disabled - local storage will be used!', true); } else if(response.message.indexOf("Sync Not Possible")>-1){ options.isSyncEnabled = false; chrome.storage.local.set({isSyncEnabled: $s.isSyncEnabled}, function(){ @@ -60,16 +60,16 @@ function renderRedirects() { } function renderSingleRedirect(node, redirect, index) { - + //Add extra props to help with rendering... if (index === 0) { redirect.$first = true; } if (index === REDIRECTS.length - 1) { redirect.$last = true; - } + } redirect.$index = index; - + dataBind(node, redirect); node.setAttribute('data-index', index); @@ -77,6 +77,12 @@ function renderSingleRedirect(node, redirect, index) { btn.setAttribute('data-index', index); } + let checkmark = node.querySelectorAll('.checkmark'); + + if(checkmark.length == 1) { + checkmark[0].setAttribute('data-index', index); + } + //Remove extra props... delete redirect.$first; delete redirect.$last; @@ -117,19 +123,170 @@ function toggleDisabled(index) { saveChanges(); } +// function handleGroupedMove(index) { +// console.log(`NSC`, arguments); +// let grouping = REDIRECTS.filter(row => row.grouped); +// let size = grouping.length; +// console.log(grouping) +// } + +// function wrap(node, elm) { +// node.parentNode.insertBefore(elm, node); +// // node.previousElementSibling.appendChild(node); +// } + +function swap(node1, node2) { + const afterNode2 = node2.nextElementSibling; + const parent = node2.parentNode; + node1.replaceWith(node2); + parent.insertBefore(node1, afterNode2); +} + +function groupedMoveDown(group) { + // let grouping = REDIRECTS.map((row, i) => { return { row, index: i}}) + // .filter(result => result.row.grouped) + // .sort((a, b) => b.index - a.index); + for(let rule of group) { + // swap positions in dom + let elm = document.querySelector("[data-index='" + (rule.index).toString() + "']"); + let prev = document.querySelector("[data-index='" + (rule.index + group.length).toString() + "']"); + swap(elm,prev); + } + + for(let rule of group) { + // swap positions in array + rule.row.grouping = false; + let prevRedir = REDIRECTS[rule.index + group.length]; + REDIRECTS[rule.index + group.length] = REDIRECTS[rule.index]; + REDIRECTS[rule.index] = prevRedir; + // REDIRECTS[rule.index].grouped = false; + } +} + +function isGroupAdjacent(grouping) { + let distances = []; + for(let i = grouping.length - 1; i >= 0; i--) { + + if(i != 0) { + distances.push(grouping[i].index - grouping[i - 1].index); + + } + + } + return distances.every(distance => distance === 1); +} + +function groupedMoveUp(group) { + + + console.log(group); + + // working config - sort of + // for(let rule of group) { + // // swap positions in dom + // let elm = document.querySelector("[data-index='" + (rule.index).toString() + "']"); + // let prev = document.querySelector("[data-index='" + (rule.index - group.length).toString() + "']"); + // elm.childNodes[7].childNodes[3].classList.remove("checkMarked"); + // console.log(`NSC: groupedMoveUp -> elm`, elm); + // prev.childNodes[7].childNodes[3].classList.remove("checkMarked"); + // console.log(`NSC: groupedMoveUp -> prev`, prev); + // swap(elm,prev); + // } + + // for(let rule of group) { + // // swap positions in array + // rule.row.grouping = false; + // let prevRedir = REDIRECTS[rule.index - group.length]; + // REDIRECTS[rule.index - group.length] = REDIRECTS[rule.index]; + // REDIRECTS[rule.index] = prevRedir; + // // REDIRECTS[rule.index].grouped = false; + // } + + // only set the below to 1 if groupings are not next to each other. + var jumpLength = 1; + + if(isGroupAdjacent(group)) { + console.log('adjacent') + jumpLength = group.length; + } + + + + + for(let rule of group) { + console.log(`NSC: groupedMoveUp -> rule`, rule); + // swap positions in dom + let elm = document.querySelector("[data-index='" + (rule.index).toString() + "']"); + console.log(`NSC: groupedMoveUp -> elm`, elm); + let prev = document.querySelector("[data-index='" + (rule.index - jumpLength).toString() + "']"); + console.log(`NSC: groupedMoveUp -> prev`, prev); + elm.childNodes[7].childNodes[3].classList.remove("checkMarked"); + prev.childNodes[7].childNodes[3].classList.remove("checkMarked"); + if(jumpLength > 1) { + swap(elm,prev); + } + + } + for(let rule of group) { + // swap positions in array + rule.row.grouping = false; + let prevRedir = REDIRECTS[rule.index - jumpLength]; + REDIRECTS[rule.index - jumpLength] = REDIRECTS[rule.index]; + REDIRECTS[rule.index] = prevRedir; + } +} function moveUp(index) { - let prev = REDIRECTS[index-1]; - REDIRECTS[index-1] = REDIRECTS[index]; - REDIRECTS[index] = prev; + let grouping = REDIRECTS.map((row, i) => { return { row, index: i}}) + .filter(result => result.row.grouped) + .sort((a, b) => a.index - b.index); + + if(grouping.length > 1) { + // many + console.log('many') + + groupedMoveUp(grouping); + + } else { + // one + let prev = REDIRECTS[index-1]; + REDIRECTS[index-1] = REDIRECTS[index]; + REDIRECTS[index] = prev; + } + updateBindings(); saveChanges(); } function moveDown(index) { - let next = REDIRECTS[index+1]; - REDIRECTS[index+1] = REDIRECTS[index]; - REDIRECTS[index] = next; + let grouping = REDIRECTS.map((row, i) => { return { row, index: i}}) + .filter(result => result.row.grouped) + .sort((a, b) => a.index - b.index); + + if(grouping.length > 1) { + // many + groupedMoveDown(grouping); + } else { + // one + let next = REDIRECTS[index+1]; + REDIRECTS[index+1] = REDIRECTS[index]; + REDIRECTS[index] = next; + } + + updateBindings(); + saveChanges(); +} + +function moveUpTop(index) { + let top = REDIRECTS[0]; + move(REDIRECTS, index, top); + updateBindings(); + saveChanges(); +} + +function moveDownBottom(index) { + let bottom = REDIRECTS.length - 1; + move(REDIRECTS, index, bottom); updateBindings(); saveChanges(); } @@ -138,7 +295,7 @@ function moveDown(index) { function pageLoad() { template = el('#redirect-row-template'); template.parentNode.removeChild(template); - + //Need to proxy this through the background page, because Firefox gives us dead objects //nonsense when accessing chrome.storage directly. chrome.runtime.sendMessage({type: "get-redirects"}, function(response) { @@ -169,7 +326,7 @@ function pageLoad() { )); } renderRedirects(); - }); + }); chrome.storage.local.get({isSyncEnabled:false}, function(obj){ options.isSyncEnabled = obj.isSyncEnabled; @@ -186,6 +343,11 @@ function pageLoad() { el('#storage-sync-option input').addEventListener('click', toggleSyncSetting); el('.redirect-rows').addEventListener('click', function(ev) { + // apply checkMarked class for Grouping + if(ev.target.type == 'checkbox') { + ev.target.nextElementSibling.classList.add("checkMarked"); + } + let action = ev.target.getAttribute('data-action'); //We clone and re-use nodes all the time, so instead of attaching and removing event handlers endlessly we just put @@ -214,4 +376,8 @@ let mql = window.matchMedia('(prefers-color-scheme:dark)'); mql.onchange = updateFavicon; updateFavicon(mql); +function toggleGrouping(index) { + REDIRECTS[index].grouped = !REDIRECTS[index].grouped; +} + pageLoad();
\ No newline at end of file @@ -36,12 +36,12 @@ function dataBind(el, dataObject) { } } for (let tag of el.querySelectorAll('[data-show]')) { - let shouldShow = boolValue(tag.getAttribute('data-show')); + let shouldShow = boolValue(tag.getAttribute('data-show')); tag.style.display = shouldShow ? '' : 'none'; } for (let tag of el.querySelectorAll('[data-disabled]')) { let isDisabled = boolValue(tag.getAttribute('data-disabled')); - + if (isDisabled) { tag.classList.add('disabled'); tag.setAttribute('disabled', 'disabled'); @@ -82,6 +82,10 @@ function showForm(selector, dataObject) { show(selector); } +function move(arr, from, to) { + arr.splice(to, 0, arr.splice(from, 1)[0]); +} + function hideForm(selector) { hide('#cover'); hide(selector); diff --git a/redirector.html b/redirector.html index af11b6b..d2fbf9c 100644 --- a/redirector.html +++ b/redirector.html @@ -10,7 +10,7 @@ <body> <div id="cover"> </div> - + <!-- Confirmation form for deleting redirects --> <div id="delete-redirect-form"> <h3>Are you sure you want to delete this redirect?</h3> @@ -39,7 +39,7 @@ <button id="cancel-delete" class="btn grey large">No, don't delete it</button> </div> </div> - + <!-- Form for creating and editing redirects --> <div id="edit-redirect-form"> @@ -124,20 +124,21 @@ <div id="blur-wrapper"> - + <h1>REDIRECTOR</h1> <h5>Go where <em>YOU</em> want!</h5> <div id="menu"> <a id="create-new-redirect" class="btn blue large">Create new redirect</a> - + <!-- Importing/Exporting of redirects --> <span> <input type="file" id="import-file" accept=".rjson,.json,.txt" /> <label for="import-file" class="btn blue large">Import</label> <a class="btn blue large" id="export-link" download="Redirector.json">Export</a> + <button class="btn blue large" id="organize-mode">Organize</button> </span> - + <a class="btn blue large" href="help.html" target="_blank">Help</a> </div> @@ -177,10 +178,21 @@ <button class="btn medium blue" data-action="toggleDisabled"><span data-show="disabled">Enable</span><span data-show="!disabled">Disable</span></button> <button class="btn medium green" data-action="editRedirect">Edit</button> <button class="btn medium red" data-action="confirmDeleteRedirect">Delete</button> + <span class="hidden"> + <button class="btn medium grey move-uptop-btn arrows" data-action="moveUpTop" data-disabled="$first">⟰</button> + </span> + <button class="btn medium grey move-up-btn" data-action="moveUp" data-disabled="$first">▲</button> <button class="btn medium grey move-down-btn" data-action="moveDown" data-disabled="$last">▼</button> + <span class="hidden"> + <button class="btn medium grey move-downbottom-btn arrows" data-action="moveDownBottom" data-disabled="$last">⟱</button> + </span> <button class="btn medium grey" data-action="duplicateRedirect">Duplicate</button> </div> + <label class="toggle-container"> + <input type="checkbox"> + <span class="checkmark groupings hidden" data-action="toggleGrouping"></span> + </label> </div> </div> <label id="storage-sync-option"><input type="checkbox" /> Enable Storage Sync</label> @@ -189,7 +201,7 @@ <footer> <small>Redirector is created by <a target="_blank" href="http://einaregilsson.com">Einar Egilsson</a></small> </footer> - + </div> <script src="js/stub.js"></script> @@ -198,5 +210,6 @@ <script src="js/redirectorpage.js"></script> <script src="js/editredirect.js"></script> <script src="js/importexport.js"></script> + <script src="js/organizemode.js"></script> </body> </html> |