aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--css/redirector.css87
-rw-r--r--js/organizemode.js42
-rw-r--r--js/redirect.js48
-rw-r--r--js/redirectorpage.js194
-rw-r--r--js/util.js8
-rw-r--r--redirector.html25
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
diff --git a/js/util.js b/js/util.js
index a751497..4249acc 100644
--- a/js/util.js
+++ b/js/util.js
@@ -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>