├── images ├── userjs-tool.png ├── toggle-readonly-textarea-box.png └── example-append-overrides-onto-template-to-form-userjs.png ├── css ├── fontawesome │ ├── circle.svg │ ├── windows.svg │ ├── desktop.svg │ ├── apple-whole.svg │ ├── android.svg │ ├── triangle-exclamation.svg │ ├── vial.svg │ ├── helmet-safety.svg │ ├── question.svg │ ├── clipboard.svg │ ├── power-off.svg │ ├── comment.svg │ ├── wrench.svg │ ├── hashtag.svg │ ├── eye-slash.svg │ ├── gear.svg │ ├── firefox-browser.svg │ ├── linux.svg │ └── LICENSE.txt ├── userjs-tool-af-mode.css ├── userjs-tool-userjs-table-view.css ├── userjs-tool.css └── userjs-tool-themes.css ├── .gitignore ├── LICENSE ├── arkenfox-gui.html ├── README.md ├── js ├── userjs-tool-userjs-append.js ├── userjs-tool-userjs-reduce.js ├── jsdiff-modified.js ├── download.js ├── userjs-tool-collect-mode.js ├── userjs-tool-userjs-to-value-groups.js ├── userjs-tool-common.js ├── userjs-tool-af-mode.js ├── userjs-tool-file-loading.js ├── userjs-tool-userjs-viewer.js └── userjs-tool-start-up.js ├── ChangeLog └── userjs-tool-aboutconfig-functions.js /images/userjs-tool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/HEAD/images/userjs-tool.png -------------------------------------------------------------------------------- /images/toggle-readonly-textarea-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/HEAD/images/toggle-readonly-textarea-box.png -------------------------------------------------------------------------------- /images/example-append-overrides-onto-template-to-form-userjs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/HEAD/images/example-append-overrides-onto-template-to-form-userjs.png -------------------------------------------------------------------------------- /css/fontawesome/circle.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/windows.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/desktop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/apple-whole.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/android.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/triangle-exclamation.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/vial.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/helmet-safety.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/clipboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/power-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/comment.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/wrench.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.class 3 | *.com 4 | *.dll 5 | *.exe 6 | *.o 7 | *.so 8 | 9 | # BlueJ files 10 | *.ctxt 11 | 12 | # Mobile Tools for Java (J2ME) 13 | .mtj.tmp/ 14 | 15 | # Package Files 16 | *.jar 17 | *.war 18 | *.nar 19 | *.ear 20 | *.zip 21 | *.tar.gz 22 | *.rar 23 | *.7z 24 | *.dmg 25 | *.gz 26 | *.iso 27 | *.tar 28 | *.zip 29 | 30 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 31 | hs_err_pid* 32 | 33 | # Logs and databases 34 | *.log 35 | **/*.log* 36 | *.sql 37 | *.sqlite 38 | 39 | # OS generated files 40 | .DS_Store 41 | .DS_Store? 42 | ._* 43 | .Spotlight-V100 44 | .Trashes 45 | ehthumbs.db 46 | Thumbs.db 47 | 48 | # https://gist.github.com/octocat/9257657 49 | *~ 50 | **/*~ 51 | *# 52 | *.bak 53 | *.swp 54 | *.swo 55 | *.orig 56 | desktop.ini 57 | 58 | # IDE (eclipse / intellij) files 59 | .classpath 60 | .project 61 | .settings 62 | #.idea 63 | /.idea/workspace.xml 64 | /.idea/tasks.xml 65 | .metadata 66 | *.iml 67 | *.ipr 68 | -------------------------------------------------------------------------------- /css/fontawesome/hashtag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 icpantsparti 4 | Copyright (c) 2022 icpantsparti and icpantsparti2 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /css/fontawesome/eye-slash.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/userjs-tool-af-mode.css: -------------------------------------------------------------------------------- 1 | /* 2 | Name : userjs-tool-af-mode.css 3 | Project : https://github.com/icpantsparti2/firefox-user.js-tool 4 | On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 5 | License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 6 | Version : 2022.04.07 7 | */ 8 | 9 | #tview_about_div { 10 | display: none; 11 | position: fixed; 12 | top: 6em; 13 | left: 25vw; 14 | width: 50vw; 15 | min-width: 50vw; 16 | max-width: 50vw; 17 | /* height: 50vh; */ 18 | min-height: 0vh; 19 | max-height: 50vh; 20 | padding: 20px; 21 | overflow: auto; 22 | white-space: normal; 23 | background-color: inherit; 24 | } 25 | 26 | #tview_search_input { 27 | width: 25vw; 28 | min-width: 4em; 29 | } 30 | 31 | /* these classes will be changed with js */ 32 | .afmode2block, .afmode2flex { 33 | display: none; 34 | } 35 | 36 | .afmode_button { 37 | width: 8em; 38 | } 39 | 40 | @media screen and (max-width: 669px) { 41 | .afmode_button_text { 42 | display: block; 43 | } 44 | .afmode_button { 45 | width: 2.5em; 46 | } 47 | .button_text { 48 | display: none; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /css/fontawesome/gear.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fontawesome/firefox-browser.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /arkenfox-gui.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | arkenfox gui 36 | 37 | 38 | 39 |
40 | Hi! This requires JavaScript for dynamically rendering the HTML interface.
41 |
42 | If you see this message perhaps JavaScript is off, or controlled by an extension. 43 |
44 | 45 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | -------------------------------------------------------------------------------- /css/fontawesome/linux.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### userjs-tool.html 2 | 3 | > This is a continuation of the project by the same maintainer (previously under @icpantsparti) 4 | 5 | Interactive view, compare, and more for Firefox user.js (eg arkenfox/user.js) + about:config functions 6 | 7 | * [Open userjs-tool.html on-line (github.io)](https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html) 8 | 9 | View the current arkenfox user.js* in a table (github.io): 10 | 11 | * https://icpantsparti2.github.io/firefox-user.js-tool/arkenfox-gui.html 12 | * https://arkenfox.github.io/gui 13 | * https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?at 14 | 15 | [Functions to get more out of about:config (eg: find, filter, list, save to file, etc)](https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/userjs-tool-aboutconfig-functions.js) 16 | 17 | > > > \*Note: the excellent `arkenfox user.js` is by other developers at:
18 | > > > [arkenfox user.js home](https://github.com/arkenfox/user.js) / [arkenfox user.js issues](https://github.com/arkenfox/user.js/issues?q=sort%3Aupdated-desc) / [arkenfox user.js wiki](https://github.com/arkenfox/user.js/wiki) 19 | 20 | ---- 21 | 22 |
Introduction
23 | 24 | Display a Mozilla Firefox user.js settings file contents in your Firefox browser, with: 25 | * highlighting, links, themes*, re-size, wrap, about:config links/regex/groups 26 | * expanding sections, and index to go to sections (with compatible user.js projects) 27 | * compare preferences in two user.js, in a table format with order/layout options and bold cell border around differences 28 | * actions including: user-overrides.js* append* (with comment-out*), point and click overrides collector, skeleton, prefs.js cleaner*, group by values 29 | * load/save, drag/drop, or copy/paste user.js files (can load from some on-line URLs too) 30 | * functions for find (filter/list)/reset/set on about:config Web Console (Firefox/forks/Thunderbird/SeaMonkey) 31 | * This is coded in HTML/CSS/JavaScript with no cross domain dependency 32 | * open [userjs-tool.html on-line](https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html) or save for off-line use 33 | 34 | (*arkenfox/user.js inspired. Please visit [arkenfox/user.js](https://github.com/arkenfox/user.js) and read their info on [arkenfox/user.js/wiki](https://github.com/arkenfox/user.js/wiki). They also have nice scripts for append/clean/troubleshoot.) 35 | 36 | This started as an over the top experiment for learning some HTML/CSS/JavaScript (first released 2019.01.02, compare added 2020.02.22). This is a viewer/tool, and not an editor/installer. 37 | 38 | Disclaimer: Use with care at your own risk, and verify any results 39 | 40 | ---- 41 | 42 |
43 | 44 |
(Optional) How to save and open userjs-tool.html off-line
45 | 46 | * Click the Code button on this repo and Download ZIP (https://github.com/icpantsparti2/firefox-user.js-tool/archive/refs/heads/master.zip) 47 | * Open the saved `userjs-tool.html` file with your Firefox browser 48 | (you can drag and drop it from your Downloads folder into a new tab) 49 | * Bookmark it for easy access 50 | * Remember to check here for updates 51 | 52 | ---- 53 | 54 |
55 | 56 |
Other Info 57 | 58 | * The `userjs-tool-aboutconfig-functions.js` file is also embeded in `userjs-tool.html` (view with the [a:c Functions] button). 59 | 60 | * You can do these (and more) from the interface, or by using URL parameters: 61 | 62 | * [View the current arkenfox user.js (github.io)](https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?av) 63 | 64 | * [View the current arkenfox user.js in a table (github.io)](https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?at) 65 | 66 | * Load and view a user.js URL: [https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?action=view1&load1=%68ttps://raw.githubusercontent.com/arkenfox/user.js/master/user.js](https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?action=view1&load1=%68ttps://raw.githubusercontent.com/arkenfox/user.js/master/user.js) 67 | 68 | ---- 69 | 70 |
71 | 72 | Preview 73 | 74 | 75 | -------------------------------------------------------------------------------- /js/userjs-tool-userjs-append.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-userjs-append.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.06 6 | 7 | // ************************************* 8 | // userjsAppend 9 | // ************************************* 10 | 11 | function userjsAppend(input_box_name, input_box_name_2, output_box_name) { 12 | 13 | var input_box = document.getElementById(input_box_name); 14 | var input_box_2 = document.getElementById(input_box_name_2); 15 | var output_box = document.getElementById(output_box_name); 16 | // convert in-block comments to in-line (improves inactive pref recognition) 17 | var append_content = 18 | amendCodeComments(input_box_2.value, true) 19 | .replace(/(\r\n|\r)/g,'\n').split("\n"); 20 | 21 | var x = new RegExp("^[ \t]*\/\/\/\/ --- add-override-comment ---[ \t]*$", "mi"); 22 | var override_tagging_on = x.test(input_box_2.value); 23 | output_box.value = ''; 24 | updateDateTimeStampVariable(); 25 | output_box.value += 26 | '// ' + date_time_stamp 27 | + " appended user-overrides.js to user-template.js\n\n" 28 | + input_box.value.replace(/[\r\n]*$/, "") 29 | + "\n\n\n\n\n" 30 | + "/*** (overrides) __________ " 31 | + "(start of overrides) __________ ***/\n" 32 | + 'user_pref("_user.js.parrot", "(overrides) (start of overrides)");' 33 | + "\n\n\n\n\n"; 34 | 35 | var i = 0; 36 | for (i in append_content) { 37 | var line = append_content[i]; 38 | var prefName; 39 | 40 | // (after amendCodeComments) change '// /*...*/' to '/*...*/' 41 | x = new RegExp("^\\/\\/ ([ \t]*\\/\\*.*\\*\\/[ \t]*)$", "gm"); 42 | line = line.replace(x, "$1"); 43 | 44 | // overrides: comment-out 45 | x = new RegExp("^[ \t]*\/\/\/\/ --- comment-out --- '([^']+)'.*$"); 46 | if (x.test(line)) { 47 | // override-out comment pref in the output 48 | prefName = line.replace(x, "$1"); 49 | x = new RegExp("user_pref[ \t]*\\([ \t]*[\"']" 50 | + RegExp.escape(prefName) + "[\"']", "gm"); 51 | output_box.value = output_box.value.replace(x, "\/\/\/\/COMMENT-OUT: $&"); 52 | // prevent duplicated ////COMMENT-OUT: 53 | x = new RegExp("\/\/\/\/COMMENT-OUT: (\/\/\/\/COMMENT-OUT: " 54 | + "user_pref[ \t]*\\([ \t]*[\"']" 55 | + RegExp.escape(prefName) + "[\"'])", "gm"); 56 | output_box.value = output_box.value.replace(x, "$1"); 57 | } 58 | 59 | // if override tagging is required, for active overrides user_pref 60 | // prefix any already occurring same name user_pref with ////OVERRIDE: 61 | x = new RegExp("^[ \t]*user_pref", "i"); 62 | if ( (override_tagging_on) && (x.test(line)) ) { 63 | // get prefname 64 | x = new RegExp("^(.*user_pref)" // $1 user_pref //user_pref etc 65 | + "([ \t]*\\([ \t]*[\"'])" // $2 (" (' 66 | + "([^\"']+)" // $3 prefname 67 | + "([\"'][ \t]*,[ \t]*)" // $4 ", ', 68 | + "(.*)" // $5 prefvalue "prefvalue" 'prefvalue' 69 | + "([ \t]*\\)[ \t]*;)" // $6 ); 70 | + "(.*)$", "gi"); // $7 afters/comment 71 | prefName = line.replace(x,"$3"); 72 | // override comment pref in the output 73 | if (prefName != "_user.js.parrot") { 74 | x = new RegExp("user_pref[ \t]*\\([ \t]*[\"']" 75 | + RegExp.escape(prefName) + "[\"']", "gm"); 76 | output_box.value = output_box.value.replace(x, "\/\/\/\/OVERRIDE: $&"); 77 | // prevent duplicated ////OVERRIDE: 78 | x = new RegExp("\/\/\/\/OVERRIDE: (\/\/\/\/OVERRIDE: " 79 | + "user_pref[ \t]*\\([ \t]*[\"']" 80 | + RegExp.escape(prefName) + "[\"'])", "gm"); 81 | output_box.value = output_box.value.replace(x, "$1"); 82 | } 83 | } 84 | 85 | // append line 86 | output_box.value += line + "\n"; 87 | } 88 | append_content = []; 89 | 90 | output_box.value += "\n\n\n\n/*** (overrides) (end of overrides) ***/\n"; 91 | output_box.value += 92 | 'user_pref("_user.js.parrot", "(overrides) (end of overrides)' 93 | + ': SUCCESS");' + "\n"; 94 | } /* end function userjsAppend */ 95 | -------------------------------------------------------------------------------- /js/userjs-tool-userjs-reduce.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-userjs-reduce.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.06 6 | 7 | // ************************************* 8 | // userjsReduce 9 | // ************************************* 10 | 11 | function userjsReduce(input_box_name, output_box_name, skeleton) { 12 | 13 | var input_box = document.getElementById(input_box_name); 14 | var output_box = document.getElementById(output_box_name); 15 | output_box.value = ''; 16 | 17 | // convert in-block comments to in-line (improves inactive pref recognition) 18 | var input_content = amendCodeComments(input_box.value, true) 19 | .replace(/(\r\n|\r)/g,'\n').split("\n"); 20 | 21 | var section_number_tag = "0000"; 22 | var section_number_tag_9999_prefix = ""; 23 | var userjs_type = ""; 24 | updateDateTimeStampVariable(); 25 | if (skeleton) { 26 | output_box.value += "// " + date_time_stamp + " skeleton\n\n"; 27 | output_box.value += "/*** (overrides) (TOP) / Introduction ***/"; 28 | } 29 | else { 30 | output_box.value += "// " + date_time_stamp + " reduced\n"; 31 | } 32 | 33 | var i = 0; 34 | for (i in input_content) { 35 | var line = input_content[i]; 36 | var x="",r=""; 37 | 38 | if (skeleton) { 39 | if (line == "/*** (overrides) (TOP) / Introduction ***/") { continue; } 40 | } 41 | 42 | userjs_type = detectUserjsType(line, userjs_type, i); 43 | 44 | // pick up the arkenfox/user.js section number 45 | if (userjs_type == "arkenfox") { 46 | if ( 47 | new RegExp("^\/\/ /[*/]+ +(START|END|[0-9][^:]+):").test(line) 48 | || new RegExp("^\/\/ /[*/]+ +\\[SECTION [^:]+:").test(line) 49 | ) { 50 | section_number_tag = line; 51 | x = new RegExp("^(?:\/\/ )/[*/]+ +\\[SECTION ([^\\]]+)\\]:.*$"); 52 | section_number_tag = section_number_tag.replace(x, "$1"); 53 | x = new RegExp("^(?:\/\/ )/[*/]+ +([^:]+):.*$"); 54 | section_number_tag = section_number_tag.replace(x, "$1"); 55 | if (/^9999/.test(section_number_tag)) { 56 | section_number_tag_9999_prefix = '9999:'; 57 | } 58 | else if (/^END/.test(section_number_tag)) { 59 | section_number_tag_9999_prefix = ''; 60 | } 61 | } 62 | } 63 | 64 | if (skeleton) { 65 | // prefix arkenfox/user.js parrot value 66 | x = new RegExp("^([ \t/*]*user_pref[ \t]*" 67 | + "\\([ \t]*[\"']_user\.js\.parrot[\"'][ \t]*,[ \t]*[\"'])" 68 | + "(.*)$"); 69 | if (new RegExp("\\(overrides\\)").test(line) == false) { 70 | line = line.replace(x, "$1(overrides) $2"); 71 | } 72 | } 73 | 74 | // prefix user_pref line with a temporary ##PREF## identifier 75 | line = line.replace("\/\/user_pref", "\/\/ user_pref"); 76 | line = line.replace("\/\*user_pref", "\/* user_pref"); 77 | x = new RegExp( 78 | "^[ \t]*" 79 | + "(user_pref|\/[\/\*].*user_pref)" 80 | + "[ \t]*" + "(\\()" 81 | + "[ \t]*" + "([\"'][^\"']+[\"'])" 82 | + "[ \t]*,[ \t]*" + "(.*)" 83 | + "[ \t]*" + "(\\))" 84 | + "[ \t]*" + "(;).*$"); 85 | if (skeleton) { 86 | // comment the pref // 87 | line = line.replace(x, "##PREF##\/\/ $1$2$3, $4$5$6"); 88 | } 89 | else { 90 | line = line.replace(x, "##PREF##$1$2$3, $4$5$6"); 91 | } 92 | 93 | // keep section_heading or pref lines 94 | if ( detectSectionHeading(line, userjs_type, i) == true ) { 95 | line = tidySectionHeading(line, true); 96 | if (skeleton) { 97 | // output heading line (with '(overrides)' inserted) 98 | line = line.replace(new RegExp("^\\(overrides\\) "), ""); 99 | output_box.value += "\n\n/*** (overrides) " + line + " ***/"; 100 | } 101 | else { 102 | output_box.value += "\n\n/*** " + line + " ***/"; 103 | } 104 | } 105 | else if (/^##PREF##/.test(line)) { 106 | // remove the prefix 107 | line = line.replace(new RegExp("^##PREF##"), ""); 108 | 109 | if (skeleton) { 110 | // un-comment the arkenfox/user.js parrot 111 | x = new RegExp("^//[ \t/*]*(user_pref[ \t]*" 112 | + "\\([ \t]*[\"']_user\.js\.parrot[\"'][ \t]*,[ \t]*[\"'].*)$"); 113 | line = line.replace(x, "$1"); 114 | } 115 | 116 | // output user_pref line (and add section number if arkenfox) 117 | output_box.value += "\n" + line; 118 | if (userjs_type == "arkenfox") { 119 | output_box.value += 120 | " // " + section_number_tag_9999_prefix + section_number_tag; 121 | } 122 | } 123 | 124 | } /* end for (i in input_content) */ 125 | input_content = []; 126 | 127 | output_box.value += "\n"; 128 | } /* end function userjsReduce */ 129 | -------------------------------------------------------------------------------- /js/jsdiff-modified.js: -------------------------------------------------------------------------------- 1 | // 11 | 12 | /* 13 | * Javascript Diff Algorithm 14 | * By John Resig (http://ejohn.org/) 15 | * Modified by Chu Alan "sprite" 16 | * 17 | * Released under the MIT license. 18 | * 19 | * More Info: 20 | * http://ejohn.org/projects/javascript-diff-algorithm/ 21 | */ 22 | 23 | function escape(s) { 24 | var n = s; 25 | // n = n.replace(/&/g, "&"); 26 | // n = n.replace(//g, ">"); 28 | // n = n.replace(/"/g, """); 29 | 30 | return n; 31 | } 32 | 33 | function diffString( o, n ) { 34 | o = o.replace(/\s+$/, ''); 35 | n = n.replace(/\s+$/, ''); 36 | 37 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); 38 | var str = ""; 39 | 40 | var oSpace = o.match(/\s+/g); 41 | if (oSpace == null) { 42 | oSpace = ["\n"]; 43 | } else { 44 | oSpace.push("\n"); 45 | } 46 | var nSpace = n.match(/\s+/g); 47 | if (nSpace == null) { 48 | nSpace = ["\n"]; 49 | } else { 50 | nSpace.push("\n"); 51 | } 52 | 53 | if (out.n.length == 0) { 54 | for (var i = 0; i < out.o.length; i++) { 55 | str += '' + escape(out.o[i]) + oSpace[i] + ""; 56 | } 57 | } else { 58 | if (out.n[0].text == null) { 59 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 60 | str += '' + escape(out.o[n]) + oSpace[n] + ""; 61 | } 62 | } 63 | 64 | for ( var i = 0; i < out.n.length; i++ ) { 65 | if (out.n[i].text == null) { 66 | str += '' + escape(out.n[i]) + nSpace[i] + ""; 67 | } else { 68 | var pre = ""; 69 | 70 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) { 71 | pre += '' + escape(out.o[n]) + oSpace[n] + ""; 72 | } 73 | str += " " + out.n[i].text + nSpace[i] + pre; 74 | } 75 | } 76 | } 77 | 78 | return str; 79 | } 80 | 81 | function randomColor() { 82 | return "rgb(" + (Math.random() * 100) + "%, " + 83 | (Math.random() * 100) + "%, " + 84 | (Math.random() * 100) + "%)"; 85 | } 86 | function diffString2( o, n ) { 87 | o = o.replace(/\s+$/, ''); 88 | n = n.replace(/\s+$/, ''); 89 | 90 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) ); 91 | 92 | var oSpace = o.match(/\s+/g); 93 | if (oSpace == null) { 94 | oSpace = ["\n"]; 95 | } else { 96 | oSpace.push("\n"); 97 | } 98 | var nSpace = n.match(/\s+/g); 99 | if (nSpace == null) { 100 | nSpace = ["\n"]; 101 | } else { 102 | nSpace.push("\n"); 103 | } 104 | 105 | var os = ""; 106 | var colors = new Array(); 107 | for (var i = 0; i < out.o.length; i++) { 108 | colors[i] = randomColor(); 109 | 110 | if (out.o[i].text != null) { 111 | os += '' + 112 | escape(out.o[i].text) + oSpace[i] + ""; 113 | } else { 114 | os += "" + escape(out.o[i]) + oSpace[i] + ""; 115 | } 116 | } 117 | 118 | var ns = ""; 119 | for (var i = 0; i < out.n.length; i++) { 120 | if (out.n[i].text != null) { 121 | ns += '' + 122 | escape(out.n[i].text) + nSpace[i] + ""; 123 | } else { 124 | ns += "" + escape(out.n[i]) + nSpace[i] + ""; 125 | } 126 | } 127 | 128 | return { o : os , n : ns }; 129 | } 130 | 131 | function diff( o, n ) { 132 | var ns = new Object(); 133 | var os = new Object(); 134 | 135 | for ( var i = 0; i < n.length; i++ ) { 136 | if ( ns[ n[i] ] == null ) 137 | ns[ n[i] ] = { rows: new Array(), o: null }; 138 | ns[ n[i] ].rows.push( i ); 139 | } 140 | 141 | for ( var i = 0; i < o.length; i++ ) { 142 | if ( os[ o[i] ] == null ) 143 | os[ o[i] ] = { rows: new Array(), n: null }; 144 | os[ o[i] ].rows.push( i ); 145 | } 146 | 147 | for ( var i in ns ) { 148 | if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) { 149 | n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] }; 150 | o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] }; 151 | } 152 | } 153 | 154 | for ( var i = 0; i < n.length - 1; i++ ) { 155 | if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null && 156 | n[i+1] == o[ n[i].row + 1 ] ) { 157 | n[i+1] = { text: n[i+1], row: n[i].row + 1 }; 158 | o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 }; 159 | } 160 | } 161 | 162 | for ( var i = n.length - 1; i > 0; i-- ) { 163 | if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null && 164 | n[i-1] == o[ n[i].row - 1 ] ) { 165 | n[i-1] = { text: n[i-1], row: n[i].row - 1 }; 166 | o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 }; 167 | } 168 | } 169 | 170 | return { o: o, n: n }; 171 | } 172 | -------------------------------------------------------------------------------- /js/download.js: -------------------------------------------------------------------------------- 1 | // 10 | 11 | //download.js v4.2, by dandavis; 2008-2016. [CCBY2] see http://danml.com/download.html for tests/usage 12 | // v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime 13 | // v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs 14 | // v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling. 15 | // v4 adds AMD/UMD, commonJS, and plain browser support 16 | // v4.1 adds url download capability via solo URL argument (same domain/CORS only) 17 | // v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors 18 | // https://github.com/rndme/download 19 | 20 | (function (root, factory) { 21 | if (typeof define === 'function' && define.amd) { 22 | // AMD. Register as an anonymous module. 23 | define([], factory); 24 | } else if (typeof exports === 'object') { 25 | // Node. Does not work with strict CommonJS, but 26 | // only CommonJS-like environments that support module.exports, 27 | // like Node. 28 | module.exports = factory(); 29 | } else { 30 | // Browser globals (root is window) 31 | root.download = factory(); 32 | } 33 | }(this, function () { 34 | 35 | return function download(data, strFileName, strMimeType) { 36 | 37 | var self = window, // this script is only for browsers anyway... 38 | defaultMime = "application/octet-stream", // this default mime also triggers iframe downloads 39 | mimeType = strMimeType || defaultMime, 40 | payload = data, 41 | url = !strFileName && !strMimeType && payload, 42 | anchor = document.createElement("a"), 43 | toString = function(a){return String(a);}, 44 | myBlob = (self.Blob || self.MozBlob || self.WebKitBlob || toString), 45 | fileName = strFileName || "download", 46 | blob, 47 | reader; 48 | myBlob= myBlob.call ? myBlob.bind(self) : Blob ; 49 | 50 | if(String(this)==="true"){ //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback 51 | payload=[payload, mimeType]; 52 | mimeType=payload[0]; 53 | payload=payload[1]; 54 | } 55 | 56 | 57 | if(url && url.length< 2048){ // if no filename and no mime, assume a url was passed as the only argument 58 | fileName = url.split("/").pop().split("?")[0]; 59 | anchor.href = url; // assign href prop to temp anchor 60 | if(anchor.href.indexOf(url) !== -1){ // if the browser determines that it's a potentially valid url path: 61 | var ajax=new XMLHttpRequest(); 62 | ajax.open( "GET", url, true); 63 | ajax.responseType = 'blob'; 64 | ajax.onload= function(e){ 65 | download(e.target.response, fileName, defaultMime); 66 | }; 67 | setTimeout(function(){ ajax.send();}, 0); // allows setting custom ajax headers using the return: 68 | return ajax; 69 | } // end if valid url? 70 | } // end if url? 71 | 72 | 73 | //go ahead and download dataURLs right away 74 | if(/^data\:[\w+\-]+\/[\w+\-]+[,;]/.test(payload)){ 75 | 76 | if(payload.length > (1024*1024*1.999) && myBlob !== toString ){ 77 | payload=dataUrlToBlob(payload); 78 | mimeType=payload.type || defaultMime; 79 | }else{ 80 | return navigator.msSaveBlob ? // IE10 can't do a[download], only Blobs: 81 | navigator.msSaveBlob(dataUrlToBlob(payload), fileName) : 82 | saver(payload) ; // everyone else can save dataURLs un-processed 83 | } 84 | 85 | }//end if dataURL passed? 86 | 87 | blob = payload instanceof myBlob ? 88 | payload : 89 | new myBlob([payload], {type: mimeType}) ; 90 | 91 | 92 | function dataUrlToBlob(strUrl) { 93 | var parts= strUrl.split(/[:;,]/), 94 | type= parts[1], 95 | decoder= parts[2] == "base64" ? atob : decodeURIComponent, 96 | binData= decoder( parts.pop() ), 97 | mx= binData.length, 98 | i= 0, 99 | uiArr= new Uint8Array(mx); 100 | 101 | for(i;i summary { font-weight: bold; } 293 | details > summary:hover { cursor: pointer; } 294 | abbr { cursor: help; } 295 | /* details[open] > summary { text-decoration: underline; } */ 296 | del { font-weight: bold; } 297 | ins { text-decoration: none; } 298 | .jump { 299 | color: #FF0000 !important; /*Red*/ 300 | background-color: #FFFF00 !important; /*Yellow*/ 301 | } 302 | 303 | .vcentertext { 304 | display: flex; 305 | justify-content: center; 306 | align-content: center; 307 | flex-direction: column; 308 | } 309 | -------------------------------------------------------------------------------- /js/userjs-tool-userjs-to-value-groups.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-userjs-to-value-groups.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.06 6 | 7 | // ************************************* 8 | // userjsToValueGroups 9 | // ************************************* 10 | 11 | function userjsToValueGroups(input_box_name, output_box_name) { 12 | var input_box = document.getElementById(input_box_name); 13 | var output_box = document.getElementById(output_box_name); 14 | output_box.value = ''; 15 | var prefArray = []; 16 | 17 | // each id in valueGroups gets unique incrementing index 18 | var valueGroups = { 19 | 'bfa': { 'id': 0, 'count': 0, 'name': "active boolean false" }, 20 | 'bta': { 'id': 0, 'count': 0, 'name': "active boolean true" }, 21 | 'i0a': { 'id': 0, 'count': 0, 'name': "active integer 0" }, 22 | 'i1a': { 'id': 0, 'count': 0, 'name': "active integer 1" }, 23 | 'i2a': { 'id': 0, 'count': 0, 'name': "active integer 2" }, 24 | // 'i3a': { 'id': 0, 'count': 0, 'name': "active integer 3" }, 25 | 'ixa': { 'id': 0, 'count': 0, 'name': "active integer other" }, 26 | 'sea': { 'id': 0, 'count': 0, 'name': "active string empty" }, 27 | 'sxa': { 'id': 0, 'count': 0, 'name': "active string other" }, 28 | 'zza': { 'id': 0, 'count': 0, 'name': "active invalid" }, 29 | 'bfi': { 'id': 0, 'count': 0, 'name': "inactive boolean false" }, 30 | 'bti': { 'id': 0, 'count': 0, 'name': "inactive boolean true" }, 31 | 'i0i': { 'id': 0, 'count': 0, 'name': "inactive integer 0" }, 32 | 'i1i': { 'id': 0, 'count': 0, 'name': "inactive integer 1" }, 33 | 'i2i': { 'id': 0, 'count': 0, 'name': "inactive integer 2" }, 34 | // 'i3i': { 'id': 0, 'count': 0, 'name': "inactive integer 3" }, 35 | 'ixi': { 'id': 0, 'count': 0, 'name': "inactive integer other" }, 36 | 'sei': { 'id': 0, 'count': 0, 'name': "inactive string empty" }, 37 | 'sxi': { 'id': 0, 'count': 0, 'name': "inactive string other" }, 38 | 'zzi': { 'id': 0, 'count': 0, 'name': "inactive invalid" } 39 | } 40 | var idinc=1; 41 | Object.keys(valueGroups).forEach(function(key){ 42 | valueGroups[key].id = idinc++; 43 | }); 44 | 45 | // add dividers to prefArray 46 | Object.keys(valueGroups).forEach(function(key){ 47 | if (valueGroups[key].id > 0) { 48 | prefArray.push( { 49 | 'group': valueGroups[key].id, 50 | 'name': "", 'state': "-", 'value': "-", 'line': "", 51 | 'hidden': false, 'count': 0 52 | } ); 53 | } 54 | }); 55 | 56 | var user_pref_regex = new RegExp( 57 | "^([ \t]*user_pref|.*//.*user_pref)" // $1 user_pref //user_pref etc 58 | + "([ \t]*\\([ \t]*[\"'])" // $2 (" (' 59 | + "([^\"']+)" // $3 prefname 60 | + "([\"'][ \t]*,[ \t]*)" // $4 ", ', 61 | + "(.*)" // $5 prefvalue "prefvalue" 'prefvalue' 62 | + "([ \t]*\\)[ \t]*;)" // $6 ); 63 | + "(.*)$", "gi" // $7 afters/comment 64 | ); 65 | 66 | var line, prefState, prefName, prefValue, prefComment, found, ic = 0; 67 | 68 | // convert in-block comments to in-line (improves inactive pref recognition) 69 | var input_content = amendCodeComments(input_box.value, true) 70 | .replace(/(\r\n|\r)/g,'\n').split("\n"); 71 | 72 | // read user_pref into prefArray 73 | for (ic in input_content) { 74 | line = input_content[ic]; 75 | if (user_pref_regex.test(line)) { 76 | prefState = line.replace(user_pref_regex, "$1"); 77 | prefName = line.replace(user_pref_regex, "$3"); 78 | prefValue = line.replace(user_pref_regex, "$5"); 79 | prefComment = line.replace(user_pref_regex, "$7"); 80 | if (new RegExp("^.*[/][/*].*user_pref", "i").test(prefState)) { 81 | prefState = "//"; 82 | } 83 | else { 84 | prefState = ""; 85 | } 86 | // check if already in the results array 87 | found=-1; 88 | for (var j = 0, len = prefArray.length; j < len; j++) { 89 | if (prefArray[j].name == prefName) { 90 | found = j; 91 | break; 92 | } 93 | } 94 | // update or add to the results array 95 | if (found > -1) { 96 | // new pref replaces previous if new=active or previous=inactive 97 | if ( (prefState == '') || (prefArray[found].state != '') ) { 98 | prefArray[found].state = prefState; 99 | prefArray[found].value = prefValue; 100 | prefArray[found].line = line; 101 | if (/hidden/i.test(prefComment)) { 102 | prefArray[found].hidden = true; 103 | } 104 | // and append ////{#count: previous} 105 | // prefArray[found].line = 106 | // line + ' ////{#' + prefArray[found].count + ': ' 107 | // + prefArray[found].line 108 | // .replace(/^(.*)user_pref\("[^"]+", *(.*)\);(.*)/, "$1$2$3") 109 | // + '}'; 110 | } 111 | prefArray[found].count ++; 112 | } 113 | else { 114 | // add 115 | prefArray.push( { 116 | 'group': 0, 117 | 'name': prefName, 118 | 'state': prefState, 119 | 'value': prefValue, 120 | 'line': line, 121 | 'hidden': (/hidden/i.test(prefComment) ? true : false), 122 | 'count': 1, 123 | } ); 124 | } 125 | } 126 | } 127 | input_content = []; 128 | 129 | // allocate value groups (and add count to line end if >1) 130 | var groupBase = "", groupSuffix = ""; 131 | for (var i = 0, len = prefArray.length; i < len; i++) { 132 | if (prefArray[i].state != "-") { 133 | if (prefArray[i].state == '//') { groupSuffix = "i" } 134 | else { groupSuffix = "a" } 135 | if (prefArray[i].value == "false") { groupBase = "bf"; } 136 | else if (prefArray[i].value == "true") { groupBase = "bt"; } 137 | else if (prefArray[i].value == "0") { groupBase = "i0"; } 138 | else if (prefArray[i].value == "1") { groupBase = "i1"; } 139 | else if (prefArray[i].value == "2") { groupBase = "i2"; } 140 | // else if (prefArray[i].value == "3") { groupBase = "i3"; } 141 | else if (new RegExp("^[0-9.+-]+$").test(prefArray[i].value)) { 142 | groupBase = "ix"; // integer other 143 | } 144 | else if (new RegExp("^[\"'][\"']$").test(prefArray[i].value)) { 145 | groupBase = "se"; // string empty 146 | } 147 | else if (new RegExp("^[\"'].*[\"']$").test(prefArray[i].value)) { 148 | groupBase = "sx"; // string other 149 | } 150 | else { 151 | groupBase = "zz"; // invalid 152 | } 153 | valueGroups[groupBase + groupSuffix].count++; 154 | prefArray[i].group = valueGroups[groupBase + groupSuffix].id; 155 | } 156 | // add count and hidden for multiple occurance 157 | if (prefArray[i].count > 1) { 158 | prefArray[i].line += ' //// (x' + prefArray[i].count 159 | + (prefArray[i].hidden ? " [HIDDEN PREF]?" : "") + ')'; 160 | } 161 | } 162 | 163 | // sort 164 | prefArray.sort((a, b) => a.name.localeCompare(b.name)); 165 | prefArray.sort((a, b) => a.group - b.group); 166 | 167 | // output 168 | updateDateTimeStampVariable(); 169 | var activeTitleNeeded = true, inactiveTitleNeeded = true; 170 | for (var i = 0, len = prefArray.length; i < len; i++) { 171 | if (prefArray[i].state == "-") { 172 | // output title 173 | Object.keys(valueGroups).forEach(function(key){ 174 | // find the valueGroups that matches the title 175 | if (prefArray[i].group == valueGroups[key].id) { 176 | // output active/inactive heading (if not already done) 177 | if ( (activeTitleNeeded) 178 | && (/^active/.test(valueGroups[key].name)) ) 179 | { 180 | output_box.value = 181 | "/*** __________ " + date_time_stamp + " active user_pref" 182 | + " grouped by values (no repeats) __________ ***/\n"; 183 | activeTitleNeeded = false; 184 | } 185 | if ( (inactiveTitleNeeded) 186 | && (/^inactive/.test(valueGroups[key].name)) ) 187 | { 188 | output_box.value += 189 | "\n/*** __________ " + date_time_stamp + " inactive user_pref" 190 | + " grouped by values (no repeats) __________ ***/\n"; 191 | inactiveTitleNeeded = false; 192 | } 193 | // output title 194 | output_box.value += "\n/*** " + valueGroups[key].name 195 | + " (" + valueGroups[key].count + ") ***/\n" 196 | } 197 | }); 198 | } 199 | else { 200 | // output user_pref 201 | output_box.value += prefArray[i].line +"\n" 202 | } 203 | } 204 | 205 | } /* end function userjsToValueGroups */ 206 | -------------------------------------------------------------------------------- /js/userjs-tool-common.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-common.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.07 6 | 7 | // ************************************* 8 | // returnDateTime 9 | // ************************************* 10 | 11 | function returnDateTime() { 12 | var d = new Date(); 13 | // var months = ["Jan","Feb","Mar","Apr","May","Jun", 14 | // "Jul","Aug","Sep","Oct","Nov","Dec"]; 15 | // var days = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; 16 | // Date.now() Get the time. ECMAScript 5. 17 | return d.getFullYear() 18 | // + months[d.getMonth()] 19 | + ((d.getMonth()+1) < 10 ? "0" : "") + (d.getMonth()+1) 20 | + (d.getDate() < 10 ? "0" : "") + d.getDate() + "_" 21 | // + days[d.getDay()] 22 | + (d.getHours() < 10 ? "0" : "") + d.getHours() 23 | + (d.getMinutes() < 10 ? "0" : "") + d.getMinutes() 24 | + (d.getSeconds() < 10 ? "0" : "") + d.getSeconds(); 25 | // + "." + getMilliseconds(); 26 | } 27 | 28 | // ************************************* 29 | // RegExp.escape 30 | // ************************************* 31 | 32 | // function to escape a variable for use in regex 33 | // https://stackoverflow.com/questions/6318710/javascript-equivalent-of-perls-q-e-or-quotemeta 34 | 35 | RegExp.escape = function(text) { 36 | return (text+'').replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); 37 | } 38 | 39 | // ************************************* 40 | // b64EncodeUnicode/b64DecodeUnicode 41 | // ************************************* 42 | 43 | // functions to base64 encode/decode (and cope with Unicode characters) 44 | // https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_Unicode_Problem 45 | 46 | function b64EncodeUnicode(str) { 47 | // first we use encodeURIComponent to get percent-encoded UTF-8, 48 | // then we convert the percent encodings into raw bytes which 49 | // can be fed into btoa. 50 | return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, 51 | function toSolidBytes(match, p1) { 52 | return String.fromCharCode('0x' + p1); 53 | })); 54 | } 55 | 56 | function b64DecodeUnicode(str) { 57 | // Going backwards: from bytestream, to percent-encoding, to original string. 58 | return decodeURIComponent(atob(str).split('').map(function(c) { 59 | return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); 60 | }).join('')); 61 | } 62 | 63 | // ************************************* 64 | // computedStyle 65 | // ************************************* 66 | 67 | // function to get element style values 68 | // https://stackoverflow.com/questions/6134471/using-elements-that-are-added-to-an-array-with-document-getelementbyidid/6134501#6134501 69 | // var element = document.getElementById('Img3'); 70 | // alert(computedStyle(element,'width')); 71 | 72 | var computedStyle = function (el,style) { 73 | var cs; 74 | if (typeof el.currentStyle != 'undefined') { 75 | cs = el.currentStyle; 76 | } 77 | else { 78 | cs = document.defaultView.getComputedStyle(el,null); 79 | } 80 | return cs[style]; 81 | } 82 | 83 | // ************************************* 84 | // escapeHtml 85 | // ************************************* 86 | 87 | // function to replace & < > " ' characters with HTML escape codes 88 | // action = decode+quotes|encode+quotes|decode|encode 89 | // (blank or anything else = encode) 90 | // based on code from: 91 | // https://stackoverflow.com/questions/1787322/htmlspecialchars-equivalent-in-javascript/4835406 92 | 93 | function escapeHtml(text, action) { 94 | if (action == "decode+quotes") { 95 | // decode (+ quotes) 96 | var map = { '&': '&', '<': '<', '>': '>', 97 | '"': '"', ''': "'" }; 98 | return text.replace(/&|<|>|"|'/g, 99 | function(m) { return map[m]; }); 100 | } 101 | else if (action == "encode+quotes") { 102 | // encode (+ quotes) 103 | var map = { '&': '&', '<': '<', '>': '>', 104 | '"': '"', "'": ''' }; 105 | return text.replace(/[&<>"']/g, function(m) { return map[m]; }); 106 | } 107 | else if (action == "decode") { 108 | // decode 109 | var map = { '&': '&', '<': '<', '>': '>' }; 110 | return text.replace(/&|<|>/g, 111 | function(m) { return map[m]; }); 112 | } 113 | else { 114 | // encode 115 | var map = { '&': '&', '<': '<', '>': '>' }; 116 | return text.replace(/[&<>]/g, function(m) { return map[m]; }); 117 | } 118 | } 119 | 120 | // ************************************* 121 | // changeClass 122 | // ************************************* 123 | 124 | // based on code from: 125 | // https://stackoverflow.com/questions/195951/change-an-elements-css-class-with-javascript 126 | 127 | function changeClass(object,oldClass,newClass) 128 | { 129 | if (oldClass == "") { 130 | if ( ! /(?:^|\s)newClass(?!\S)/.test(object.className) ) { 131 | object.className += " "+newClass; 132 | } 133 | } 134 | else { 135 | var regExp = new RegExp('(?:^|\\s)' + oldClass + '(?!\\S)', 'ig'); 136 | object.className = object.className.replace( regExp , newClass ); 137 | } 138 | } 139 | 140 | // ************************************* 141 | // getURLVariable 142 | // ************************************* 143 | 144 | /* process URL variables */ 145 | // https://stackoverflow.com/questions/831030/how-to-get-get-request-parameters-in-javascript 146 | 147 | function getURLVariable(name){ 148 | // window.location.search.substring(1) 149 | if (name=(new RegExp('[?&]' 150 | + encodeURIComponent(name) + '=([^&#]*)')).exec(location.search)) 151 | return decodeURIComponent(name[1]) 152 | } 153 | 154 | // ************************************* 155 | // amendCodeComments 156 | // ************************************* 157 | 158 | /* 159 | function to either: 160 | (1) remove javascript comments, or 161 | (2) convert in-block comments to in-line 162 | based on removeCodeComments function from: 163 | https://stackoverflow.com/questions/5989315/regex-for-match-replacing-javascript-comments-both-multiline-and-inline#52630274 164 | and modified to add: convertInBlockToInLine (optional) asteriskPosition (fix) 165 | */ 166 | 167 | function amendCodeComments(code="", convertInBlockToInLine=false) { 168 | var inQuoteChar = null; 169 | var inBlockComment = false; 170 | var asteriskPosition = null; 171 | var inLineComment = false; 172 | var inRegexLiteral = false; 173 | var newCode = ''; 174 | for (var i=0,j=code.length;i asteriskPosition 210 | ) { 211 | inBlockComment = false; 212 | asteriskPosition = null; 213 | if (convertInBlockToInLine && code[i] !== '\n') { 214 | newCode += '\n'; 215 | } 216 | } 217 | if (inLineComment && code[i] === '\n') { 218 | inLineComment = false; 219 | } 220 | } 221 | if ((!inBlockComment && !inLineComment) || convertInBlockToInLine) { 222 | newCode += code[i]; 223 | } 224 | if (convertInBlockToInLine && inBlockComment && code[i] === '\n') { 225 | newCode += '// '; 226 | } 227 | } 228 | return newCode; 229 | } /* end function amendCodeComments */ 230 | 231 | // ************************************* 232 | // indexSelectAction 233 | // ************************************* 234 | 235 | function indexSelectAction(element) { 236 | // expand, focus, and scroll to the chosen section 237 | var id = element.value; 238 | // make the select element show first choice (button title) 239 | element.selectedIndex = 0; 240 | element.blur(); 241 | if (id) { 242 | if ( 243 | /(compare_div|tableview_div)/ 244 | .test(document.getElementById("view_area").innerHTML) 245 | ) { 246 | /* for compare or tableview */ 247 | document.getElementById("tview_slider").value = id; 248 | if (id == 0) { 249 | scroll(0,0); 250 | } 251 | else { 252 | document.getElementById(id).scrollIntoView(); 253 | } 254 | } 255 | else { 256 | var e = document.getElementById(id); 257 | if (e) { 258 | e.nextElementSibling.style.display = "block"; 259 | section_focus = e; 260 | refocusSection(); 261 | } 262 | else if (id == "(TOP) / Introduction") { 263 | scroll(0,0); 264 | } 265 | } 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /css/userjs-tool-themes.css: -------------------------------------------------------------------------------- 1 | /* 2 | Name : userjs-tool-themes.css 3 | Project : https://github.com/icpantsparti2/firefox-user.js-tool 4 | On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 5 | License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 6 | Version : 2022.04.09 7 | */ 8 | 9 | /* ************************************* */ 10 | /* color themes */ 11 | /* ************************************* */ 12 | 13 | /* default theme has same values as dark theme */ 14 | /* color codes: https://www.w3schools.com/colors/colors_groups.asp */ 15 | 16 | 17 | /* default theme */ 18 | .body_default, .view_area_default, .panels_default 19 | { background-color: #161b22; /*(black)*/ 20 | color: #b3b3b3; /*(gray)*/ } 21 | .controls_default { background-color: #555555; /*DimGray*/ 22 | color: #C0C0C0; /*Silver*/ } 23 | .controls_default:focus { outline: 2px auto #FF0000; /*Red*/ } 24 | .controls_default:hover { background-color: #DAA520; /*GoldenRod*/ 25 | color: #000000; /*Black*/ } 26 | .borders_default { box-shadow: 3px 3px 4px #FFFFFF; /*White*/ 27 | border: 1px solid #A9A9A9; /*DarkGray*/ 28 | border-width: 1px 1px 0px 1px; } 29 | .view_area_default .pref { color: #FFFFFF; /*White*/ } 30 | .body_default a { color: #ffbf80; /*(brown)*/ } 31 | .view_area_default .false { color: #FF6347; /*Tomato*/ } 32 | .view_area_default .true { color: #00FF00; /*Lime*/ } 33 | .view_area_default .integer { color: #FFFF00; /*Yellow*/ } 34 | .view_area_default .string { color: #DDA0DD; /*Plum*/ } 35 | .view_area_default .http { color: #99ccff; /*(blue)*/ } 36 | .view_area_default .warn { background-color: #FF3333; /*(Red)*/ 37 | color: #FFFFFF; /*White*/ } 38 | .view_area_default .hid { color: #C6F3C6; /*(Green)*/ } 39 | .view_area_default .ref { color: #98E498; /*(Green)*/ } 40 | 41 | 42 | /* dark theme */ 43 | .body_dark, .view_area_dark, .panels_dark 44 | { background-color: #161b22; /*(black)*/ 45 | color: #b3b3b3; /*(gray)*/ } 46 | .controls_dark { background-color: #555555; /*DimGray*/ 47 | color: #C0C0C0; /*Silver*/ } 48 | .controls_dark:focus { outline: 2px auto #FF0000; /*Red*/ } 49 | .controls_dark:hover { background-color: #DAA520; /*GoldenRod*/ 50 | color: #000000; /*Black*/ } 51 | .borders_dark { box-shadow: 3px 3px 4px #FFFFFF; /*White*/ 52 | border: 1px solid #A9A9A9; /*DarkGray*/ 53 | border-width: 1px 1px 0px 1px; } 54 | .view_area_dark .pref { color: #FFFFFF; /*White*/ } 55 | .body_dark a { color: #ffbf80; /*(brown)*/ } 56 | .view_area_dark .false { color: #FF6347; /*Tomato*/ } 57 | .view_area_dark .true { color: #00FF00; /*Lime*/ } 58 | .view_area_dark .integer { color: #FFFF00; /*Yellow*/ } 59 | .view_area_dark .string { color: #DDA0DD; /*Plum*/ } 60 | .view_area_dark .http { color: #99ccff; /*(blue)*/ } 61 | .view_area_dark .warn { background-color: #FF3333; /*(Red)*/ 62 | color: #FFFFFF; /*White*/ } 63 | .view_area_dark .hid { color: #C6F3C6; /*(Green)*/ } 64 | .view_area_dark .ref { color: #98E498; /*(Green)*/ } 65 | 66 | 67 | /* light theme */ 68 | .body_light, .view_area_light, .panels_light 69 | { background-color: #FFFFFF; /*White*/ 70 | color: #808080; /*Gray*/ } 71 | .controls_light { background-color: #C0C0C0; /*Silver*/ 72 | color: #000000; /*Black*/ } 73 | .controls_light:focus { outline: 2px auto #FF0000; /*Red*/ } 74 | .controls_light:hover { background-color: #DAA520; /*GoldenRod*/ 75 | color: #000000; /*Black*/ } 76 | .borders_light { box-shadow: 3px 3px 4px #000000; /*Black*/ 77 | border: 1px solid #000000; /*Black*/ 78 | border-width: 1px 1px 0px 1px; } 79 | .view_area_light .pref { color: #000000; /*Black*/ } 80 | .body_light a { color: #D2691E; /*Chocolate*/ } 81 | .view_area_light .false { color: #FF6347; /*Tomato*/ } 82 | .view_area_light .true { color: #32CD32; /*LimeGreen*/ } 83 | .view_area_light .integer { color: #9932CC; /*DarkOrchid*/ } 84 | .view_area_light .string { color: #DDA0DD; /*Plum*/ } 85 | .view_area_light .http { color: #1982D1; /*(Blue)*/ } 86 | .view_area_light .warn { background-color: #FF3333; /*(Red)*/ 87 | color: #FFFFFF; /*White*/ } 88 | .view_area_light .hid { color: #598033; /*(Green)*/ } 89 | .view_area_light .ref { color: #A53C00; /*(Brown)*/ } 90 | 91 | 92 | /* mono theme */ 93 | .body_mono, .view_area_mono, .panels_mono 94 | { background-color: #FFFFFF; /*White*/ 95 | color: #000000; /*Black*/ } 96 | .controls_mono { background-color: #FFFFFF; /*White*/ 97 | color: #000000; /*Black*/ } 98 | .controls_mono:focus { outline: 2px auto #FF0000; /*Red*/ } 99 | .controls_mono:hover { background-color: #000000; /*Black*/ 100 | color: #FFFFFF; /*White*/ } 101 | .borders_mono { box-shadow: 3px 3px 4px #000000; /*Black*/ 102 | border: 1px solid #000000; /*Black*/ 103 | border-width: 1px 1px 0px 1px; } 104 | .view_area_mono .pref { color: #000000; /*Black*/ } 105 | .body_mono a { color: #696969; /*DimGray*/ } 106 | .view_area_mono .false { color: #000000; /*Black*/ } 107 | .view_area_mono .true { color: #000000; /*Black*/ } 108 | .view_area_mono .integer { color: #000000; /*Black*/ } 109 | .view_area_mono .string { color: #000000; /*Black*/ } 110 | .view_area_mono .http { color: #696969; /*DimGray*/ } 111 | .view_area_mono .warn { background-color: #000000; /*Black*/ 112 | color: #FFFFFF; /*White*/ } 113 | .view_area_mono .hid { color: #000000; /*Black*/ } 114 | .view_area_mono .ref { color: #000000; /*Black*/ } 115 | 116 | 117 | /* inverse theme */ 118 | .body_inverse, .view_area_inverse, .panels_inverse 119 | { background-color: #000000; /*Black*/ 120 | color: #FFFFFF; /*White*/ } 121 | .controls_inverse { background-color: #000000; /*Black*/ 122 | color: #FFFFFF; /*White*/ } 123 | .controls_inverse:focus { outline: 2px auto #FF0000; /*Red*/ } 124 | .controls_inverse:hover { background-color: #FFFFFF; /*White*/ 125 | color: #000000; /*Black*/ } 126 | .borders_inverse { box-shadow: 3px 3px 4px #FFFFFF; /*White*/ 127 | border: 1px solid #FFFFFF; /*White*/ 128 | border-width: 1px 1px 0px 1px; } 129 | .view_area_inverse .pref { color: #FFFFFF; /*White*/ } 130 | .body_inverse a { color: #D3D3D3; /*LightGray*/ } 131 | .view_area_inverse .false { color: #FFFFFF; /*White*/ } 132 | .view_area_inverse .true { color: #FFFFFF; /*White*/ } 133 | .view_area_inverse .integer { color: #FFFFFF; /*White*/ } 134 | .view_area_inverse .string { color: #FFFFFF; /*White*/ } 135 | .view_area_inverse .http { color: #D3D3D3; /*LightGray*/ } 136 | .view_area_inverse .warn { background-color: #FFFFFF; /*White*/ 137 | color: #000000; /*Black*/ } 138 | .view_area_inverse .hid { color: #FFFFFF; /*White*/ } 139 | .view_area_inverse .ref { color: #FFFFFF; /*White*/ } 140 | 141 | 142 | /* arkenfox theme */ 143 | .body_arkenfox, .view_area_arkenfox, .panels_arkenfox 144 | { background-color: #161b22; 145 | color: #b3b3b3; } 146 | .controls_arkenfox { background-color: #161b22; 147 | color: #b3b3b3; } 148 | .controls_arkenfox:focus { outline: 2px auto #FF0000; } 149 | .controls_arkenfox:hover { background-color: #dcdc8c; 150 | color: #000000; } 151 | .borders_arkenfox { /*box-shadow: 3px 3px 4px #808080;*/ 152 | border: 1px solid #b3b3b3; 153 | border-width: 1px 1px 1px 1px; } 154 | .view_area_arkenfox .pref { color: #ffffff; } /* section header */ 155 | .body_arkenfox a { color: #b3b3b3; } /* prefname */ 156 | .view_area_arkenfox .false { color: #f07878; } 157 | .view_area_arkenfox .true { color: #78f078; } 158 | .view_area_arkenfox .integer { color: #f0f078; } 159 | .view_area_arkenfox .string { color: #a078f0; } 160 | .view_area_arkenfox .http { color: #78a0f0; } /* a http */ 161 | .view_area_arkenfox .warn { background-color: #ff4f4f; 162 | color: #FFFFFF; } 163 | .view_area_arkenfox .hid { color: #b3b3b3; } 164 | .view_area_arkenfox .ref { color: #f0c878; } /* id+desc */ 165 | -------------------------------------------------------------------------------- /js/userjs-tool-af-mode.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-af-mode.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2024.06.09 6 | 7 | //////////////////////////////////////// 8 | // userjsTableViewWhenArkenfoxRepoMode 9 | // (used by both userjs-tool.html and arkenfoxGUIStart() calls this) 10 | //////////////////////////////////////// 11 | 12 | function userjsTableViewWhenArkenfoxRepoMode() { 13 | 14 | var theme = document.body.className.replace( /(^| *)[^_]+_/ , ''); 15 | 16 | // hide some elements 17 | var e = document.getElementsByClassName("afmode2none"); 18 | for (var i = 0, j = e.length; i < j; i++) { 19 | e[i].style.display="none"; 20 | } 21 | // reveal some elements 22 | var e = document.getElementsByClassName("afmode2block"); 23 | for (var i = 0, j = e.length; i < j; i++) { 24 | e[i].style.display="block"; 25 | } 26 | var e = document.getElementsByClassName("afmode2flex"); 27 | for (var i = 0, j = e.length; i < j; i++) { 28 | e[i].style.display="flex"; 29 | } 30 | 31 | // add another index button (as original is hidden) 32 | document.getElementById("tview_collapse_button").insertAdjacentHTML("beforebegin", 33 | '
'); 38 | 39 | // make index button have same content as original 40 | document.getElementById("tview_index_select").innerHTML = 41 | document.getElementById("index_select").innerHTML; 42 | 43 | document.getElementById("tview_index_select").options[0].innerHTML= 44 | ' ☛  Index' 45 | 46 | // place "About" text into element 47 | if (!!(document.getElementById("about_textarea"))) { 48 | document.getElementById("tview_about_inner_div").innerHTML = 49 | document.getElementById("about_textarea").value; 50 | } 51 | else { 52 | // if no text just put the arkenfox url 53 | document.getElementById("tview_about_inner_div").innerHTML = 54 | 'https://github.com/arkenfox/user.js'; 57 | } 58 | 59 | // hide the search option under the filter button 60 | var e = document.getElementById("tview_filter_select_search_option"); 61 | e.disabled=true; 62 | e.hidden=true; 63 | 64 | changeClass(document.getElementById("tview_filter_select"),"","afmode_button"); 65 | 66 | 67 | // event listeners 68 | 69 | // [Index] button 70 | document.getElementById("tview_index_select").addEventListener("change", function() { 71 | indexSelectAction(this); 72 | }); 73 | 74 | // [Search] box 75 | document.getElementById("tview_search_input").addEventListener("keyup", function() { 76 | userjsTableViewTagFilter(null,this.value); 77 | }); 78 | 79 | document.getElementById("tview_search_clear_button").style.borderTop = "0px"; 80 | document.getElementById("tview_search_clear_button").style.borderRight = "0px"; 81 | document.getElementById("tview_search_clear_button").style.borderBottom = "0px"; 82 | 83 | // search clear button 84 | document.getElementById("tview_search_clear_button").addEventListener("click", function() { 85 | if (!(document.getElementById("tview_search_input").value=="")) { 86 | userjsTableViewTagFilter(null,""); 87 | } 88 | }); 89 | 90 | // [About] button (toggle) 91 | document.getElementById("tview_about_button").addEventListener("click", function() { 92 | if (document.getElementById("tview_about_div").style.display=="block") { 93 | document.getElementById("tview_about_div").style.display="none"; 94 | } 95 | else { 96 | document.getElementById("tview_about_div").style.display="block"; 97 | } 98 | }); 99 | 100 | // top bar opaque and re-size 101 | var e = document.getElementById("tview_buttons_div"); 102 | e.style.backgroundColor="#000000"; 103 | e.style.border="1px solid #b3b3b3"; 104 | e.style.borderWidth="0px 0px 1px 0px"; 105 | e.style.top="0"; 106 | e.style.width="100%"; 107 | e.style.padding="0.9em 0"; 108 | e.style.height="4em"; 109 | e.style.maxWidth = "1400px"; 110 | e.style.margin = "0 auto"; 111 | e.style.zIndex = "100"; 112 | 113 | // max/min width 114 | document.body.style.maxWidth = "1400px"; 115 | document.body.style.margin = "0 auto"; 116 | document.body.style.float = "none"; 117 | document.getElementById("table_tview").style.minWidth="600px"; 118 | 119 | // inactive pref background 120 | var e = document.getElementsByClassName("tr_tview_inactive"); 121 | for (var i = 0, j = e.length; i < j; i++) { 122 | e[i].style.backgroundColor="#313131"; 123 | } 124 | 125 | // adjust offsets (for preventing content hidden behind top bar) 126 | if (!!(document.getElementById("body_offset"))) { 127 | document.getElementById("body_offset").innerHTML=""; 128 | } 129 | document.getElementById("tableview_offset").innerHTML=""; 130 | document.getElementById("tableview_div").style.marginTop="7em"; 131 | var e = document.getElementsByClassName("anchor"); 132 | for (var i = 0, j = e.length; i < j; i++) { 133 | e[i].style.marginTop="-7em"; 134 | e[i].style.paddingBottom="7em"; 135 | } 136 | 137 | // add some button margins 138 | document.getElementById("tview_index_select").style.marginLeft="2em"; 139 | for (const id of [ "tview_index_select", "tview_expand_collapse_button", 140 | "tview_filter_select", "tview_about_button" ] ) 141 | { 142 | document.getElementById(id).style.marginRight="1em"; 143 | } 144 | for (const id of [ "tview_version_div", "tview_about_button" ] ) 145 | { 146 | document.getElementById(id).style.marginLeft="1em"; 147 | } 148 | 149 | // disable the prefname about:config search formatted links 150 | var e = document.getElementsByClassName("td_tview_name"); 151 | for (var i = 0, j = e.length; i < j; i++) { 152 | var e2 = e[i].getElementsByTagName("a"); 153 | for (var i2 = 0, j2 = e2.length; i2 < j2; i2++) { 154 | e2[i2].removeAttribute("href"); 155 | } 156 | } 157 | 158 | } /* end function userjsTableViewWhenArkenfoxRepoMode */ 159 | 160 | 161 | /////////////////////////////////////// 162 | // userjstoolWhenArkenfoxRepoMode 163 | // (used by userjs-tool.html) 164 | /////////////////////////////////////// 165 | 166 | function userjstoolWhenArkenfoxRepoMode() { 167 | if (getURLVariable("afmode")=="off") { 168 | arkenfoxRepoMode=""; 169 | } 170 | else if (getURLVariable("afmode")=="demo" 171 | || getURLVariable("afmode")=="test" 172 | ) { 173 | arkenfoxRepoMode=getURLVariable("afmode"); 174 | } 175 | else if ( 176 | /^https:\/\/arkenfox\.github\.io\//.test(location) 177 | || (getURLVariable("afmode")=="on") 178 | ) { 179 | arkenfoxRepoMode="on"; 180 | } 181 | } /* end function userjstoolWhenArkenfoxRepoMode */ 182 | 183 | 184 | //////////////////////////////////////////////// 185 | // arkenfoxGUIStart 186 | // (used by arkenfox-gui.html) 187 | // (aka index.html on arkenfox gui repo) 188 | //////////////////////////////////////////////// 189 | 190 | function arkenfoxGUIStart(text_box="") { 191 | 192 | var txtbx = document.getElementById(text_box); 193 | 194 | document.body.style.fontSize = "70%"; 195 | 196 | // must have these ids or it breaks 197 | txtbx.insertAdjacentHTML("afterend", 198 | '' 199 | +'
'); 200 | 201 | var timeout = 0; 202 | 203 | if ( (txtbx.value=="") 204 | || (/^\?(a|b)($|&)/.test(location.search)) 205 | || (/^(\?|.*&)v=[^&]+($|&)/.test(location.search)) 206 | ) { 207 | // fetch file from main repo instead 208 | txtbx.value=""; 209 | var url="https://raw.githubusercontent.com/arkenfox/user.js/master/user.js"; 210 | if (/^(\?|.*&)v=[^&]+($|&)/.test(location.search)) { 211 | // eg "?v=115.1", then in url replace "/master/" with "/115.1/" 212 | // https://github.com/arkenfox/user.js/tags 213 | // eg https://github.com/arkenfox/user.js/tree/115.1 214 | url=url.replace(/\/master\//,`/${ 215 | location.search.replace(/^(\?|.*&)v=([^&]+)($|&.*$)/,"$2")}/`); 216 | } 217 | var msg = "\nTroubleshooting: Check connection?" 218 | + " Blocked by an extension (eg uMatrix XHR)?" 219 | + " Site allows fetch? Valid URL/file?\n" 220 | + url; 221 | fetch(url) 222 | .then(function(response) { 223 | if (response.ok) { 224 | return response.text() 225 | } 226 | else { 227 | throw Error(response.status); 228 | } 229 | }) 230 | .then(function(text) { 231 | txtbx.value = text; 232 | }) 233 | .catch(function(err) { 234 | alert("File fetch error:\n" + err.message + msg); 235 | }); 236 | timeout = 1000; 237 | } 238 | 239 | setTimeout(function(){ 240 | userjsTableView(text_box,getURLVariable("t"),getURLVariable("s")); 241 | userjsTableViewWhenArkenfoxRepoMode(); 242 | }, timeout); 243 | 244 | // provide the user.js from main as base64 245 | if (/^\?(b)($|&)/.test(location.search)) { 246 | setTimeout(function(){ 247 | document.getElementById("tableview_div").insertAdjacentHTML("afterbegin", 248 | ''); 253 | }, 1000); 254 | } 255 | 256 | // list the urls in the hardcoded user.js 257 | if (/^\?(u)($|&)/.test(location.search)) { 258 | setTimeout(function(){ 259 | // https://javascript.tutorialink.com/extract-urls-from-paragraph-or-block-of-text-using-a-regular-expression/ 260 | var urlRegex = /((mailto:|ftp:\/\/|https?:\/\/)\S+?)[^\s]+/ig; 261 | document.getElementById("tableview_div").insertAdjacentHTML("afterbegin", 262 | ''); 266 | }, 500); 267 | } 268 | 269 | } 270 | -------------------------------------------------------------------------------- /js/userjs-tool-file-loading.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-file-loading.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.06 6 | 7 | // ************************************* 8 | // downloadFile 9 | // ************************************* 10 | 11 | // based on code from: 12 | // https://stackoverflow.com/questions/196498/how-do-i-load-the-contents-of-a-text-file-into-a-javascript-variable 13 | // https://stackoverflow.com/questions/6348207/making-a-paragraph-in-html-contain-a-text-from-a-file 14 | // https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Fetching_data 15 | // https://cameronnokes.com/blog/4-common-mistakes-front-end-developers-make-when-using-fetch/ 16 | 17 | // browser security policies prevent same origin local file:// loading 18 | 19 | function downloadFile(url, text_box){ 20 | var e = document.getElementById(text_box); 21 | var msg = "\nTroubleshooting: Check connection?" 22 | + " Blocked by an extension (eg uMatrix XHR)?" 23 | + " Site allows fetch? Valid URL/file?" 24 | + " Visit the URL and copy/paste the content instead?\n" 25 | + url; 26 | clearTextAreaContents(text_box); 27 | toggleTextAreaReadOnly(text_box, true, false); 28 | fetch(url) 29 | .then(function(response) { 30 | if (response.ok) { 31 | return response.text() 32 | } 33 | else { 34 | throw Error(response.status); 35 | } 36 | }) 37 | .then(function(text) { 38 | e.value = text; 39 | }) 40 | .catch(function(err) { 41 | if (location.protocol == "file:") { 42 | console.error('// local (file:) fetch failed - firefox flip "security.fileuri.strict_origin_policy" (and change it back afterwards)'); 43 | } 44 | alert("File fetch error:\n" + err.message + msg); 45 | }); 46 | } // end function downloadFile 47 | 48 | // ************************************* 49 | // loadButtonAction 50 | // ************************************* 51 | 52 | function loadButtonAction(select, input, text_box, filenameforsave){ 53 | var url = select.value; 54 | select.selectedIndex = 0; 55 | if (url == "LOCAL") { 56 | // trigger the (hidden) local file load selector (does not work for Fennec) 57 | if ( 58 | document.getElementById("collect_button").style.textDecoration == "line-through" 59 | && (text_box == "box_1_template" || text_box == "box_2_overrides") 60 | ) { 61 | return; 62 | } 63 | document.getElementById(input).click(); 64 | } 65 | else if (url == "BUTTON") { 66 | // show the Browse button (Fennec workaround) 67 | for (const id of [ "template", "overrides", "userjs", "other" ]) 68 | { 69 | if (document.getElementById("load_" + id + "_input").style.display === "block") { 70 | document.getElementById("load_" + id + "_input").style.display = "none"; 71 | document.getElementById("loadsave_" + id + "_select").options[2].textContent = 72 | document.getElementById("loadsave_" + id + "_select").options[2].textContent 73 | .replace(/\u{25A3}/u, "\u25A2"); 74 | } 75 | else { 76 | document.getElementById("load_" + id + "_input").style.display = "block"; 77 | document.getElementById("loadsave_" + id + "_select").options[2].textContent = 78 | document.getElementById("loadsave_" + id + "_select").options[2].textContent 79 | .replace(/\u{25A2}/u, "\u25A3"); 80 | } 81 | } 82 | } 83 | else if (url == "SAVE") { 84 | download(document.getElementById(text_box).value, 85 | filenameforsave, "application/javascript"); 86 | } 87 | else if (/^.+$/.test(url)) { 88 | if ( 89 | document.getElementById("collect_button").style.textDecoration == "line-through" 90 | && (text_box == "box_1_template" || text_box == "box_2_overrides") 91 | ) { 92 | return; 93 | } 94 | if (url == "URL") { 95 | url = ""; 96 | url = prompt("Please input URL of file to load\n" 97 | + "(Only works if the site allows this)\n" 98 | + "(eg arkenfox /master/ /vNN/ or /vNN.0-beta/ /NN.0/)", 99 | "https://raw.githubusercontent.com/arkenfox/user.js/master/user.js"); 100 | } 101 | if (url != null && url != "") { 102 | downloadFile(url, text_box); 103 | setTimeout(function(){ 104 | toggleTextAreaReadOnly(text_box, true); 105 | e.selectionStart = 0; 106 | e.selectionEnd = 0; 107 | e.focus(); 108 | }, 1000); 109 | } 110 | } 111 | } // end function loadButtonAction 112 | 113 | // ************************************* 114 | // loadLocalFile / drag and drop 115 | // ************************************* 116 | 117 | // based on code from: 118 | // https://www.html5rocks.com/en/tutorials/file/dndfiles/ 119 | // https://stackoverflow.com/questions/16215771/how-open-select-file-dialog-via-js/16215950 120 | // https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop 121 | function loadLocalFile(ev, text_box){ 122 | // get files from Browse selection or drag and drop 123 | var files = []; 124 | if (typeof ev == 'string') { 125 | var files = document.getElementById(ev).files; 126 | } 127 | else { 128 | // dropHandler(ev) 129 | // Prevent default behavior (Prevent file from being opened) 130 | ev.preventDefault(); 131 | if (ev.dataTransfer.items) { 132 | // Use DataTransferItemList interface to access the file(s) 133 | for (var i = 0; i < ev.dataTransfer.items.length; i++) { 134 | // If dropped items aren't files, reject them 135 | if (ev.dataTransfer.items[i].kind === 'file') { 136 | files.push(ev.dataTransfer.items[i].getAsFile()); 137 | } 138 | } 139 | } else { 140 | // Use DataTransfer interface to access the file(s) 141 | for (var i = 0; i < ev.dataTransfer.files.length; i++) { 142 | files.push(ev.dataTransfer.files[i]); 143 | } 144 | } 145 | } 146 | 147 | // determine which box to load the selected files 148 | 149 | var fe, ft, fo, fu, fx; // filename for each box (fe is current box) 150 | 151 | if (!files.length) { 152 | // no files selected 153 | return; 154 | } 155 | else if (files.length > 4) { 156 | alert("Too many files selected. (" + files.length + " > maximum 4)"); 157 | return; 158 | } 159 | else if (files.length == 1) { 160 | // one file selected (load into current box) 161 | fe=files[0]; 162 | } 163 | else { 164 | // multiple files selected (load into corresponding boxes) 165 | // loop through the file list 166 | var matched = "", noMatch = ""; 167 | for (var i=0, f; f=files[i]; i++) { 168 | if ( (/^user\.js.*$/.test(f.name)) && (!fu) ) { 169 | if (!fu) { fu=f; } else { noMatch += "\n?: " + f.name; } 170 | } 171 | else if ( (/^user-overrides.*\.js$/.test(f.name)) && (!fo) ) { 172 | if (!fo) { fo=f; } else { noMatch += "\n?: " + f.name; } 173 | } 174 | else if ( (/user-template.*\.js$/.test(f.name)) && (!ft) ) { 175 | if (!ft) { ft=f; } else { noMatch += "\n?: " + f.name; } 176 | } 177 | else if ( (/^.*\.js$/.test(f.name)) && (!fx) ) { 178 | if (!fx) { fx=f; } else { noMatch += "\n?: " + f.name; } 179 | } 180 | else { 181 | noMatch += "\n?: " + f.name; 182 | } 183 | } 184 | matched = "\n1: " 185 | if (ft) { matched += ft.name ; } 186 | matched += "\n2: " 187 | if (fo) { matched += fo.name ; } 188 | matched += "\n3: " 189 | if (fu) { matched += fu.name ; } 190 | matched += "\n4: " 191 | if (fx) { matched += fx.name ; } 192 | if (noMatch) { 193 | noMatch = "\n\nNot matched to a box:" + noMatch; 194 | } 195 | if (!confirm("Load into boxes?" + matched + noMatch)) { 196 | return; 197 | } 198 | } 199 | 200 | // separate reads and variables (to avoid delayed loads into wrong box) 201 | 202 | if (fe) { 203 | var e = document.getElementById(text_box); 204 | var reader_e = new FileReader(); 205 | // If we use onloadend, we need to check the readyState. 206 | reader_e.onloadend = function(evt) { 207 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 208 | // if (typeof evt.target.result == 'string') { 209 | e.value = evt.target.result; 210 | // } 211 | // else { 212 | // let utf8decoder = new TextDecoder('utf-8'); 213 | // e.value = utf8decoder.decode(decomp(evt.target.result)); 214 | // } 215 | // e.select(); 216 | toggleTextAreaReadOnly(text_box, true); 217 | e.selectionStart = 0; 218 | e.selectionEnd = 0; 219 | e.focus(); 220 | } 221 | }; 222 | reader_e.readAsText(fe); 223 | // reader_e.readAsArrayBuffer(fe); 224 | // reader_e.readAsBinaryString(fe); 225 | } 226 | 227 | if (ft) { 228 | var t = document.getElementById("box_1_template"); 229 | var reader_t = new FileReader(); 230 | // If we use onloadend, we need to check the readyState. 231 | reader_t.onloadend = function(evt) { 232 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 233 | t.value = evt.target.result; 234 | toggleTextAreaReadOnly("box_1_template", true); 235 | // t.select(); 236 | t.selectionStart = 0; 237 | t.selectionEnd = 0; 238 | t.focus(); 239 | } 240 | }; 241 | reader_t.readAsText(ft); 242 | } 243 | 244 | if (fo) { 245 | var o = document.getElementById("box_2_overrides"); 246 | var reader_o = new FileReader(); 247 | // If we use onloadend, we need to check the readyState. 248 | reader_o.onloadend = function(evt) { 249 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 250 | o.value = evt.target.result; 251 | toggleTextAreaReadOnly("box_2_overrides", true); 252 | // o.select(); 253 | o.selectionStart = 0; 254 | o.selectionEnd = 0; 255 | o.focus(); 256 | } 257 | }; 258 | reader_o.readAsText(fo); 259 | } 260 | 261 | if (fu) { 262 | var u = document.getElementById("box_3_userjs"); 263 | var reader_u = new FileReader(); 264 | // If we use onloadend, we need to check the readyState. 265 | reader_u.onloadend = function(evt) { 266 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 267 | u.value = evt.target.result; 268 | toggleTextAreaReadOnly("box_3_userjs", true); 269 | // u.select(); 270 | u.selectionStart = 0; 271 | u.selectionEnd = 0; 272 | u.focus(); 273 | } 274 | }; 275 | reader_u.readAsText(fu); 276 | } 277 | 278 | if (fx) { 279 | var x = document.getElementById("box_4_other"); 280 | var reader_x = new FileReader(); 281 | // If we use onloadend, we need to check the readyState. 282 | reader_x.onloadend = function(evt) { 283 | if (evt.target.readyState == FileReader.DONE) { // DONE == 2 284 | x.value = evt.target.result; 285 | toggleTextAreaReadOnly("box_4_other", true); 286 | // t.select(); 287 | x.selectionStart = 0; 288 | x.selectionEnd = 0; 289 | x.focus(); 290 | } 291 | }; 292 | reader_x.readAsText(fx); 293 | } 294 | 295 | } // end function loadLocalFile 296 | 297 | function dragOverHandler(ev) { 298 | // Prevent default behavior (Prevent file from being opened) 299 | ev.preventDefault(); 300 | } 301 | 302 | function dropHandler1(ev) { 303 | if ( 304 | document.getElementById("collect_button").style.textDecoration != "line-through" 305 | ) { 306 | loadLocalFile(ev, "box_1_template"); 307 | } 308 | } 309 | 310 | function dropHandler2(ev) { 311 | if ( 312 | document.getElementById("collect_button").style.textDecoration != "line-through" 313 | ) { 314 | loadLocalFile(ev, "box_2_overrides"); 315 | } 316 | } 317 | 318 | function dropHandler3(ev) { loadLocalFile(ev, "box_3_userjs"); } 319 | 320 | function dropHandler4(ev) { loadLocalFile(ev, "box_4_other"); } 321 | -------------------------------------------------------------------------------- /js/userjs-tool-userjs-viewer.js: -------------------------------------------------------------------------------- 1 | // Name : userjs-tool-userjs-viewer.js 2 | // Project : https://github.com/icpantsparti2/firefox-user.js-tool 3 | // On-line : https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html 4 | // License (MIT): https://raw.githubusercontent.com/icpantsparti2/firefox-user.js-tool/master/LICENSE 5 | // Version : 2022.04.06 6 | 7 | // ************************************* 8 | // userjsViewer 9 | // ************************************* 10 | 11 | /* build and display userjs as HTML */ 12 | 13 | function userjsViewer(text_box_name, convert_code_comments) { 14 | 15 | // update date of hidden heading (H1/H3/DL tags for bookmarks import compatibility) 16 | updateDateTimeStampVariable(); 17 | 18 | document.getElementById("hiddendate").innerHTML = 19 | document.getElementById("hiddendate").innerHTML 20 | .replace(/(--userjs_)[0-9_]+/, "$1" + date_time_stamp); 21 | 22 | // get content and clear input and page 23 | document.getElementById("view_area").innerHTML = ""; 24 | var content; 25 | 26 | // if View+ style is selected in the menu 27 | if (typeof convert_code_comments !== "boolean") { 28 | convert_code_comments = toggleViewPlusOnView("status"); 29 | } 30 | 31 | // convert in-block comments to in-line 32 | // (improves inactive pref recognition) 33 | if (convert_code_comments) { 34 | content = 35 | amendCodeComments(document.getElementById(text_box_name).value, true) 36 | .replace(/(\r\n|\r)/g,'\n').split("\n"); 37 | } 38 | else { 39 | content = document.getElementById(text_box_name).value 40 | .replace(/(\r\n|\r)/g,'\n').split("\n"); 41 | } 42 | 43 | var theme = document.body.className.replace( /(^| *)[^_]+_/ , ''); 44 | 45 | // variables for holding HTML that we build from the content 46 | var index_select_html = '' 47 | + ' \n'; 48 | var groups_container_html = ''; 49 | var content_html = ''; 50 | 51 | var section_heading = '(TOP) / Introduction'; 52 | // if there is only 1 section we will not collapse the viewer 53 | var section_count = 1; 54 | var userjs_type = ""; 55 | var group_user_pref_list = ''; 56 | var previousLineWasBlank = true, currentLineWasBlank; 57 | 58 | // some common HTML used for each section_heading 59 | 60 | function appendSectionStartHtml() { 61 | index_select_html += ' \n'; 62 | groups_container_html += '
\n\n\n'; // end section 78 | group_user_pref_list = ''; 79 | } 80 | 81 | /* work through each line of the user.js content */ 82 | // detect headings, user_pref, values, and URLs 83 | // insert color class styles and section blocks 84 | // by appending code to: index_select_html groups_container_html content_html 85 | 86 | appendSectionStartHtml(); 87 | var i = 0; 88 | for (i in content) { 89 | var line = content[i]; 90 | 91 | if (/^[ \t]*$/.test(line)) { 92 | currentLineWasBlank = true; 93 | } 94 | else { 95 | currentLineWasBlank = false; 96 | } 97 | 98 | userjs_type = detectUserjsType(line,userjs_type,i); 99 | 100 | // on section_heading detection: end previous section, start new section 101 | if ( detectSectionHeading(line,userjs_type,i) == true ) { 102 | if (!previousLineWasBlank) { content_html += "\n" } 103 | previousLineWasBlank = true; 104 | appendSectionEndHtml(); 105 | section_heading = tidySectionHeading(line); 106 | appendSectionStartHtml(); 107 | section_count += 1; 108 | } 109 | 110 | // HTML code injection prevention 111 | // replace & < > characters with HTML escape codes 112 | line = escapeHtml(line); 113 | 114 | // on user_pref lines add span and class tags 115 | // (for syntax highlighting pretty colors) 116 | var x,r; 117 | var line_userpref_part = ""; 118 | 119 | x = new RegExp("^(.*user_pref)" // $1 user_pref //user_pref etc 120 | + "([ \t]*\\([ \t]*[\"'])" // $2 (" (' 121 | + "([^\"']+)" // $3 prefname 122 | + "([\"'][ \t]*,[ \t]*)" // $4 ", ', 123 | + "(.*)" // $5 prefvalue "prefvalue" 'prefvalue' 124 | + "([ \t]*\\)[ \t]*;)" // $6 ); 125 | + "(.*)$", "gi"); // $7 afters/comment 126 | 127 | if (x.test(line)) { 128 | 129 | // separate line into: line_userpref_part) user_pref statement line) afters/comment 130 | line_userpref_part = line.replace(x,"$1$2$3$4$5$6"); 131 | line = line.replace(x,"$7"); 132 | 133 | 134 | // add some HTML (tag and class for colors) 135 | 136 | // a/href for: prefname (turn into an about:config link) 137 | x = new RegExp("^(.*user_pref[ \t]*\\([ \t]*[\"'])" 138 | + "([^\"']+)([\"'].*)$", "i"); 139 | r = "$2"; 140 | var pref_escaped = line_userpref_part.replace(x, r).replace(/([*.+])/g, "\\$1"); 141 | r = "$1$2$3"; 143 | line_userpref_part = line_userpref_part.replace(x, r); 144 | // add prefname to groups 145 | group_user_pref_list += pref_escaped + "|"; 146 | 147 | // span/class for: //user_pref /*user_pref user_pref 148 | if (new RegExp("([/][/*]+[ \t]*user_pref)", "i").test(line_userpref_part)) { 149 | // for: "// user_pref" "/* user_pref" 150 | x = new RegExp("([/][/*]+[ \t]*user_pref)", "i"); 151 | r = '$1'; 152 | line_userpref_part = line_userpref_part.replace(x, r); 153 | } 154 | else { 155 | // for: "user_pref" (without // or /* immediately before) 156 | //x = new RegExp("([ /*\t]*user_pref)", "i"); 157 | x = new RegExp("(user_pref)", "i"); 158 | r = '$1'; 159 | line_userpref_part = line_userpref_part.replace(x, r); 160 | } 161 | 162 | // span/class for: false (prefvalue) 163 | if (new RegExp(",[ \t]*false[ \t]*\\);", "i").test(line_userpref_part)) { 164 | x = new RegExp("(,[ \t]*)(false)([ \t]*\\);)", "i"); 165 | r = '$1$2$3'; 166 | line_userpref_part = line_userpref_part.replace(x, r); 167 | } 168 | // span/class for: true (prefvalue) 169 | else if (new RegExp(",[ \t]*true[ \t]*\\);", "i").test(line_userpref_part)) { 170 | x = new RegExp("(,[ \t]*)(true)([ \t]*\\);)", "i"); 171 | r = '$1$2$3'; 172 | line_userpref_part = line_userpref_part.replace(x, r); 173 | } 174 | // span/class for: integer (prefvalue) 175 | else if (new RegExp(",[ \t]*[0-9.+-]+[ \t]*\\);", "i").test(line_userpref_part)) { 176 | x = new RegExp("(,[ \t]*)([0-9.+-]+)([ \t]*\\);)", "i"); 177 | r = '$1$2$3'; 178 | line_userpref_part = line_userpref_part.replace(x, r); 179 | } 180 | // span/class for: "string" 'string' (prefvalue) 181 | else { 182 | x = new RegExp("(,[ \t]*)([\"']*.*[\"']*)([ \t]*\\);)", "i"); 183 | r = '$1$2$3'; 184 | line_userpref_part = line_userpref_part.replace(x, r); 185 | } 186 | 187 | // span/class for: ////OVERRIDE: ////COMMENT-OUT: 188 | x = new RegExp("\/\/\/\/(OVERRIDE|COMMENT-OUT):", "gi"); 189 | r = '$&'; 190 | line_userpref_part = line_userpref_part.replace(x, r); 191 | 192 | } 193 | 194 | // a/href for: HTTP/HTTPS URLs (turn into a link) 195 | x = new RegExp("(https?:[/][/][^ \"']+[^\\)\\], \"'.;])", "gi"); 196 | r = '' 197 | + '$1'; 198 | line = line.replace(x, r); 199 | 200 | // span/class for: [WARNING] 201 | x = new RegExp("(\\[WARNING\\]|\\[WARNING.)", "gi"); 202 | r = '$1'; 203 | line = line.replace(x, r); 204 | 205 | // span/class for: (hidden pref) [HIDDEN PREF] 206 | x = new RegExp("([\\[\\(]hidden pref[^\\)\\]]*[\\)\\]])", "gi"); 207 | r = '$1'; 208 | line = line.replace(x, r); 209 | 210 | // span/class for: [SETUP] [SETUP-...] 211 | x = new RegExp("(\\[SETUP\\]|\\[SETUP-[^\\]]+\\]|\/\/[ \t]NOTICE-DISABLED:)", "gi"); 212 | r = '$1'; 213 | line = line.replace(x, r); 214 | 215 | // span/class for: arkenfox ref 216 | if (line_userpref_part == "" && userjs_type == "arkenfox") { 217 | 218 | // for: /* 1234: /*** 1234: 219 | // ref in bold and color whole line 220 | x = new RegExp("^((?:\/\/ )?[/*]+ )([0-9][0-9][0-9][0-9][^:]*)(:)(.*?)([ /*]*)?$", "gi"); 221 | r = ""; 222 | if (convert_code_comments) { 223 | // add extra newline (if the replace matches) 224 | if (!previousLineWasBlank) { r = "\n" } 225 | if (x.test(line)) { previousLineWasBlank = true; } 226 | } 227 | r += '$1$2$3$4$5'; 228 | line = line.replace(x, r); 229 | 230 | // for: /*** [SECTION 1234]: 231 | // ref in bold and color whole line 232 | x = new RegExp("^((?:\/\/ )?[/*]+ )(\\[SECTION [0-9][0-9][0-9][0-9][^\\]]*\\])(:)(.*?)([ /*]*)?$", "gi"); 233 | r = '$1$2$3$4$5'; 234 | line = line.replace(x, r); 235 | 236 | // for: /* /** /*** 237 | if ( (convert_code_comments) && (!previousLineWasBlank) ) { 238 | x = new RegExp("^((?:\/\/ )?\\/\\*.*)$", "gi"); 239 | r = "\n$1"; 240 | line = line.replace(x, r); 241 | x = new RegExp("^((?:\/\/ )?\\/\\/ FF[0-9.+]+)$", "gi"); 242 | r = "\n$1"; 243 | line = line.replace(x, r); 244 | } 245 | 246 | } 247 | else if (line_userpref_part == "" && userjs_type == "pyllyukko") { 248 | // for: * SECTION: and // PREF: 249 | x = new RegExp("^((?:\/\/ )?[ *\t]*)((?:SECTION|PREF):.*)([ *\t]*)$", "gi"); 250 | // color 251 | r = '$1$2$3'; 252 | line = line.replace(x, r); 253 | } 254 | 255 | // add to HTML code 256 | content_html += line_userpref_part + line + '\n'; 257 | 258 | previousLineWasBlank = currentLineWasBlank; 259 | 260 | } /* end for (i in content) */ 261 | content = []; 262 | 263 | appendSectionEndHtml(); 264 | 265 | 266 | /* update HTML page with the new content */ 267 | content_html += '


\n'; 268 | index_select_html += '\n'; 269 | document.getElementById("index_select").innerHTML = index_select_html; 270 | 271 | // also remove first group if unused 272 | document.getElementById("groups_container").innerHTML = 273 | groups_container_html.replace(new RegExp('' 276 | + '-\\(TOP\\) \\/ Introduction<\\/A>
'), ""); 277 | 278 | document.getElementById("view_area").innerHTML = content_html; 279 | index_select_html = null; 280 | groups_container_html = null; 281 | content_html = null; 282 | 283 | 284 | /* actions following the new content addition */ 285 | 286 | /* add listener for expand/collapse individual sections (multiple buttons) */ 287 | var e = document.getElementsByClassName("heading_buttons"); 288 | for (var i = 0, j = e.length; i elements) 40 | - hide the section jump slider 41 | * 2022.04.01: 42 | + add arkenfox repo mode 43 | if userjs-tool.html is run from a copy.html on an arkenfox repo: 44 | auto loads a file on that same repo called user.js 45 | and displays in a table, with theme/style and hide parts of UI 46 | (with arkenfox theme using color values from the arkenfox TZP project) 47 | + add "afmode=" URL parameter (for demo/etc of the mode mentioned above) 48 | (you would not usually need to specify this as the arkenfox repo mode is automatic) 49 | ?afmode=off default when html not opened from an arkenfox repo 50 | ?afmode=on default if html opened from an arkenfox repo 51 | ?afmode=demo to demo from this repo (loads main arkenfox repo user.js) 52 | https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?afmode=demo 53 | ?afmode=test similar to demo - but some UI not hidden 54 | + table view: add an t= URL parameter for filter by tag 55 | + table view: update URL box when using the filter button 56 | ie to show the t= or s= parameter 57 | * make default theme background darker 58 | 59 | 60 | 61 | 62 | version 2022.03.20 userjs-tool.html 63 | * table view: improve code for comments that appear after the pref 64 | (the indented: " //") (re "prefComment+") 65 | (the code added in v2022.03.15 also distorted some tag counts - fixed) 66 | * table view: improve Search (find text) 67 | * rename Find Text to Search (and add an icon) 68 | + show counts of text matched (alongside the option) 69 | + add hidden columns to the table: so that columns are separated 70 | with a space character when we search the row innerText 71 | (this improves the RegExp matching eg \b word boundaries etc) 72 | + allow search_text to be passed to the functions 73 | + add an s= URL parameter, then when auto loading and auto displaying 74 | user.js in a table a search filter can be applied 75 | eg https://icpantsparti2.github.io/firefox-user.js-tool/userjs-tool.html?at&s=ocsp 76 | TODO: add some examples to the help section for this 77 | 78 | 79 | 80 | 81 | versions 2022.03.18 2022.03.17 2022.03.16.2 2022.03.16 userjs-tool.html 82 | * 2022.03.18: 83 | + table view: added a find text option under the [Filter] button 84 | * 2022.03.17: 85 | * table view: issue: some inactive prefs were still showing when 86 | hide inactive selected (re display of arkenfox section 9000) 87 | solution: corrected an object key name (re code added in v2022.03.15) 88 | * 2022.03.16.2: 89 | * do not display empty table view 90 | re: when html location is under "https://arkenfox.github.io/..." 91 | and user.js file does not fetch (an alert is already shown) 92 | * 2022.03.16: edit some references in [Links] section 93 | * remove: arkenfox-clear-deprecated.js 94 | * remove: arkenfox-clear-RFP-alternatives.js 95 | * rename: arkenfox-clear-removed.js to arkenfox-cleanup.js 96 | 97 | 98 | 99 | 100 | version 2022.03.15 userjs-tool.html 101 | 102 | + if html location is under "https://arkenfox.github.io/..." 103 | (eg https://arkenfox.github.io/foo/bar.html) then: 104 | * auto load and table view: "https://arkenfox.github.io/user.js/user.js" 105 | (github.io pages will need to be enabled on both repos) 106 | * use arkenfox theme (which has the same values as dark theme for now...) 107 | 108 | + table view: comments that appear after the pref (indented: " //") 109 | now included in description (instead of looking like a sub heading below) 110 | (eg arkenfox section 9000) 111 | 112 | * table view: added tag indicators on end of heading rows 113 | (eg arkenfox sections 4500 and 8000 have [WARNING]) 114 | 115 | + table view: added hard coded tags: Y=WHY T=TEST 116 | (previously these got the default * indicator used for tags not hard coded) 117 | 118 | * removed the underlines of when
open 119 | 120 | * table view: enable double clicking of the [+] button 121 | this expands descriptions and leaves section headers collapsed 122 | (a first/single click still expands all descriptions as before) 123 | 124 | * table view: changed word wrap (try not to break mid word) 125 | 126 | * [Groups]: updated references to some shortcut keys: 127 | Firefox changed the shortcut hint key for "Copy Link" (was "a" now "L") 128 | ie when you right click a link and the context menu appears 129 | 130 | * [a:c Functions] now version 2022.03.15 131 | 132 | 133 | ---------------------------------------- 134 | 135 | 136 | version 2022.03.15 userjs-tool-aboutconfig-functions.js 137 | * same code as version 2021.03.11 138 | * updated some URLs and comments 139 | 140 | version 2022.03.12 (2021.03.11 forked) 141 | * forked from @icpantsparti to @icpantsparti2 142 | * the only modifications are user name and version number 143 | 144 | 145 | ---------------------------------------- 146 | 147 | 148 | version 2021.03.11 149 | 150 | updated userjs-tool-aboutconfig-functions.js and minor changes 151 | 152 | * updated [a:c Functions] userjs-tool-aboutconfig-functions.js 153 | + ujtFindPref options: asdefault, nodefault, fileout 154 | optionally save results to desktop file (fileout: true) 155 | + ujtFindPref style 0 and 1: now updates the about:config display 156 | insert result into the about:config search box and filter 157 | reduce text size and spacing to see more 158 | + use Services.prefs.getComplexValue to look up "chrome:..." prefs 159 | + ujtFindPref style: 5=user_pref@default 160 | + ujtFindPref style 2 and 5: show pref default values 161 | (note: policies.json/etc can alter defaults) 162 | 163 | * userjsTableView: 164 | detected tags (those not hardcoded) now show * indicator in Info column 165 | under [Filter] button: 166 | detected tags now show above the individual SETUP and FF tags (arkenfox) 167 | grouped SETUP... and FF... tags (arkenfox) now hint "(listed below)" 168 | tag sort made case insensitive 169 | make arkenfox "_user.js.parrot" pref show first value 170 | 171 | + userjsTableView/userjsTableViewTagFilter: 172 | make a first link in [Groups] to reflect whats shown in the filtered table 173 | 174 | * userjsViewer/userjsCompare/userjsTableView: 175 | remove first [Groups] link if empty 176 | 177 | * userjsCompare/userjsCompareLauncher/handleActionURLparameter: 178 | remove space from all layout variable values re below 179 | improve action=compare URL params (eg action=compare:1:4:az:2column) 180 | 181 | * loadLocalFile: use readAsText instead of readAsBinaryString 182 | 183 | * links_panel: 184 | + add links for arkenfox wiki and issues 185 | * tidy up auto-loading links 186 | + add "Auto-load about:config Groups" (about:config search patterns) 187 | 188 | * rename "Get started info" to "Hints", tidy up and add more hints 189 | 190 | + two action buttons, shortcuts for: [View arkenfox] + [Load arkenfox] 191 | 192 | 193 | 194 | 195 | version 2020.09.15 (alpha/experimental) 196 | * updated hyperlinks and references 197 | re "https://github.com/ghacksuserjs/ghacks-user.js" 198 | has moved to "https://github.com/arkenfox/user.js" 199 | 200 | 201 | 202 | 203 | version 2020.03.21 (alpha/experimental) 204 | * added a [