├── .vscode └── settings.json ├── LICENSE ├── Readme.md ├── autojs-scripts └── yiban-punch-card │ ├── .vscode │ └── settings.json │ ├── main.js │ ├── project.json │ └── readme.md └── tampermonkey-scripts ├── deep-l ├── .vscode │ └── launch.json ├── cli.html ├── fast.html ├── index.js ├── panel.html ├── readme - 副本.md ├── readme.assets │ ├── image-20200414101247741.png │ ├── image-20200414101306521.png │ ├── image-20200414101341671.png │ ├── image-20200414101423124.png │ ├── image-20200414101648590.png │ └── image-20200414101659748.png ├── readme.md └── test.js ├── fast-git-code ├── index.js └── readme.md ├── jianshu-dir-tree ├── Readme.assets │ ├── GIF.gif │ ├── image-20200405213921183.png │ ├── image-20200405214022718.png │ └── 微信截图_20200405213621.png ├── Readme.md └── index.js └── jianshu-search-pro ├── index.js ├── readme.assets ├── GIF.gif ├── image-20200408234130843.png ├── image-20200408235758979.png └── image-20200409112703717.png └── readme.md /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.snippetSuggestions": "bottom", 3 | "editor.suggest.snippetsPreventQuickSuggestions": false 4 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # mini-tools 2 | 3 | > mini-tools是本人在闲暇之余开发的小的插件集合地,虽然名字是`mini-tools`,其实它并不`mini`, 在mini-tools中你可以找到`VSCode`、`Tampermonkey`、`Chrome`的好玩好用的插件。欢迎大家来Star~ 4 | 5 | ## 快速通道 6 | 7 | ### [1-fast-git-code(Tampermonkey)](./tampermonkey-scripts/fast-git-code/) 8 | 9 | > 一个快速查看Git源码的Tampermonkey插件 10 | 11 | ### [2-简书目录树(Tampermonkey)](./tampermonkey-scripts/jianshu-dir-tree/) 12 | 13 | > 一个生成简书目录的Tampermonkey插件 14 | 15 | ### [3-简书搜索强化(Tampermonkey)](./tampermonkey-scripts/jianshu-search-pro/) 16 | 17 | > 一个增强简书自带搜索功能的Tampermonkey插件 18 | 19 | ### [4-DeepL深度翻译器(tampermonkey)](./tampermonkey-scripts/deep-l/) 20 | 21 | > 为程序员量身定做的翻译神器 22 | 23 | ## 关于 24 | 25 | WM/2020.4 -------------------------------------------------------------------------------- /autojs-scripts/yiban-punch-card/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.snippetSuggestions": "bottom", 3 | "editor.suggest.snippetsPreventQuickSuggestions": false 4 | } -------------------------------------------------------------------------------- /autojs-scripts/yiban-punch-card/main.js: -------------------------------------------------------------------------------- 1 | toast("Start Yiban Script"); 2 | 3 | app.launchApp("易班"); 4 | 5 | while (!click("跳过")); 6 | 7 | while (!click("易班校本化")); 8 | 9 | while (!click("任务")); 10 | 11 | while (!click("未反馈")); 12 | sleep(300); 13 | 14 | while (!className("android.widget.Button").findOne().click()); 15 | sleep(300); 16 | 17 | setFormItemMany("家庭住址所在省/市/旗县", [6, 5, 1]); 18 | 19 | setFormItemMany("目前所在省/市/旗县", [6, 5, 1]); 20 | 21 | setFormItemText("今日体温", "36"); 22 | 23 | setFormItemBool("近期有无国(区)外出行史", false); 24 | 25 | setFormItemBool("有无疫情严重地区接触史", false); 26 | 27 | setFormItemBool("是否接触过疑似或确诊患者", false); 28 | 29 | setFormItemBool("是否获悉学院疫情防控通知", true); 30 | 31 | setFormItemBool("家人是否有确诊或疑似病例", false); 32 | 33 | setFormItemText("本人联系方式", "15804854160"); 34 | 35 | swipe(500, 1500, 500, 1000, 500); 36 | sleep(300); 37 | setFormItemMultiText("其他说明", "无"); 38 | 39 | function setFormItemBool(label, val) { 40 | while (!click(label)); 41 | sleep(500); 42 | selectWithOne(val ? 0 : 1); 43 | sleep(500); 44 | while (!click("确定")); 45 | } 46 | 47 | function setFormItemMany(label, selects) { 48 | while (!click(label)); 49 | sleep(500); 50 | selectWithMany(selects); 51 | sleep(500); 52 | while (!click("确定")); 53 | } 54 | 55 | function setFormItemText(label, val) { 56 | formItemValue(label).setText(val); 57 | } 58 | 59 | function setFormItemMultiText(label, val) { 60 | const labelIndex = formItem(label).indexInParent(); 61 | className("android.widget.EditText") 62 | .indexInParent(labelIndex + 1) 63 | .depth(8) 64 | .findOne() 65 | .setText(val); 66 | } 67 | 68 | function selectWithMany([c1, c2, c3]) { 69 | try { 70 | swipe(181, 2000, 181, 2000 - c1 * 93, c1 * 500); // 1442=2000-6*93, 3000=6*500 71 | sleep(100); 72 | swipe(554, 2000, 554, 2000 - c2 * 93, c2 * 500); 73 | sleep(100); 74 | swipe(954, 2000, 954, 2000 - c3 * 93, c3 * 500); 75 | } catch (e) {} 76 | } 77 | 78 | function selectWithOne(c1) { 79 | try { 80 | swipe(181, 2000, 181, 2000 - c1 * 93, c1 * 500); // 1442=2000-6*93, 3000=6*500 81 | } catch (e) {} 82 | } 83 | 84 | function formItem(label) { 85 | return className("android.widget.TextView").text(label).findOne().parent(); 86 | } 87 | 88 | function formItemValue(label) { 89 | return className("android.widget.TextView") 90 | .text(label) 91 | .findOne() 92 | .parent() 93 | .parent() 94 | .children()[1] 95 | .children()[0]; 96 | } 97 | -------------------------------------------------------------------------------- /autojs-scripts/yiban-punch-card/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiban-punch-card", 3 | "main": "main.js", 4 | "ignore": [ 5 | "build" 6 | ], 7 | "packageName": "com.yiban_punch_card", 8 | "versionName": "1.0.0", 9 | "versionCode": 1 10 | } -------------------------------------------------------------------------------- /autojs-scripts/yiban-punch-card/readme.md: -------------------------------------------------------------------------------- 1 | ## 易班打卡脚本 2 | >- 测试机型:Redme K30S 3 | 4 | 需要下载AutoJs才能执行 5 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "测试程序", 11 | "skipFiles": [ 12 | "/**" 13 | ], 14 | "program": "${workspaceFolder}\\test.js" 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/cli.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Document 7 | 8 | 9 | 10 | 11 | 225 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/fast.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/index.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name deepL 3 | // @name:zh 深度翻译器 4 | // @name:en deepL 5 | // @namespace https://github.com/WumaCoder/mini-tools.git 6 | // @homepageURL https://github.com/WumaCoder/mini-tools.git 7 | // @version 1.0.6 8 | // @description 基于deepL、google、youdao开发的第三方翻译插件,可以实现页面的智能自动翻译,最有特色的是他可以添加忽略翻译 9 | // @description:en A third-party translation plugin based on deepL Translations 10 | // @author WumaCoder 11 | // @match *://*/* 12 | // @grant GM_setValue 13 | // @grant GM_getValue 14 | // @grant GM_registerMenuCommand 15 | // @grant GM_unregisterMenuCommand 16 | // @grant GM_openInTab 17 | // @grant GM_xmlhttpRequest 18 | // @grant GM_notification 19 | // @grant GM_listValues 20 | // @grant GM_deleteValue 21 | // @connect deepl.com 22 | // @connect cdn.jsdelivr.net 23 | // @connect cdn.bootcss.com 24 | // @connect translate.google.cn 25 | // @connect fanyi.youdao.com 26 | // @connect fanyi.baidu.com 27 | // @license GPL-3.0-only 28 | // @require https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js 29 | // @require https://cdn.jsdelivr.net/npm/jquery.md5@1.0.2/index.min.js 30 | // ==/UserScript== 31 | 32 | (function () { 33 | 34 | //----------引用------------------ 35 | /* 36 | * @name menuCommand.js 37 | * @version 0.0.1 38 | * @author Blaze 39 | * @date 2019/9/21 14:22 40 | * 来自 HTML5视频播放器增强脚本 41 | */ 42 | const monkeyMenu = { 43 | on (title, fn, accessKey) { 44 | return window.GM_registerMenuCommand && window.GM_registerMenuCommand(title, fn, accessKey) 45 | }, 46 | off (id) { 47 | return window.GM_unregisterMenuCommand && window.GM_unregisterMenuCommand(id) 48 | }, 49 | /* 切换类型的菜单功能 */ 50 | switch (title, fn, defVal) { 51 | const t = this; 52 | t.on(title, fn); 53 | } 54 | }; 55 | 56 | //---以下这些代码来自https://github.com/zyufstudio/TM/tree/master/webTranslate 57 | /** 58 | * 字符串模板格式化 59 | * @param {string} formatStr - 字符串模板 60 | * @returns {string} 格式化后的字符串 61 | * @example 62 | * StringFormat("ab{0}c{1}ed",1,"q") output "ab1cqed" 63 | */ 64 | function StringFormat (formatStr) { 65 | var args = arguments; 66 | return formatStr.replace(/\{(\d+)\}/g, function (m, i) { 67 | i = parseInt(i); 68 | return args[i + 1]; 69 | }); 70 | } 71 | /** 72 | * 日期格式化 73 | * @param {Date} date - 日期 74 | * @param {string} formatStr - 格式化模板 75 | * @returns {string} 格式化日期后的字符串 76 | * @example 77 | * DateFormat(new Date(),"yyyy-MM-dd") output "2020-03-23" 78 | * @example 79 | * DateFormat(new Date(),"yyyy/MM/dd hh:mm:ss") output "2020/03/23 10:30:05" 80 | */ 81 | function DateFormat (date, formatStr) { 82 | var o = { 83 | "M+": date.getMonth() + 1, //月份 84 | "d+": date.getDate(), //日 85 | "h+": date.getHours(), //小时 86 | "m+": date.getMinutes(), //分 87 | "s+": date.getSeconds(), //秒 88 | "q+": Math.floor((date.getMonth() + 3) / 3), //季度 89 | "S": date.getMilliseconds() //毫秒 90 | }; 91 | if (/(y+)/.test(formatStr)) { 92 | formatStr = formatStr.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); 93 | } 94 | for (var k in o) { 95 | if (new RegExp("(" + k + ")").test(formatStr)) { 96 | formatStr = formatStr.replace( 97 | RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 98 | } 99 | } 100 | return formatStr; 101 | } 102 | /** 103 | * 生成Guid 104 | * @param {boolean} hasLine - guid字符串是否包含短横线 105 | * @returns {string} guid 106 | * @example 107 | * Guid(false) output "b72f78a6cb88362c0784cb82afae450b" 108 | * @example 109 | * Guid(true) output "67b25d43-4cfa-3edb-40d7-89961ce7f388" 110 | */ 111 | function Guid (hasLine) { 112 | var guid = ""; 113 | function S4 () { 114 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 115 | } 116 | if (hasLine) { 117 | guid = (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); 118 | } 119 | else { 120 | guid = (S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4()); 121 | } 122 | return guid; 123 | } 124 | 125 | function ObjectToQueryString (object) { 126 | var querystring = Object.keys(object).map(function (key) { 127 | return encodeURIComponent(key) + '=' + encodeURIComponent(object[key]) 128 | }).join('&'); 129 | return querystring; 130 | } 131 | 132 | //谷歌翻译 133 | var googleTrans = { 134 | code: "ge", 135 | codeText: "谷歌", 136 | defaultOrigLang: "en", //默认源语言 137 | defaultTargetLang: "zh-CN", //默认目标语言 138 | langList: { "auto": "自动检测", "zh-CN": "中文简体", "zh-TW": "中文繁体", "en": "英文", "ja": "日文", "ko": "韩文", "fr": "法文", "es": "西班牙文", "pt": "葡萄牙文", "it": "意大利文", "ru": "俄文", "vi": "越南文", "de": "德文", "ar": "阿拉伯文", "id": "印尼文" }, 139 | Execute: function (h_onloadfn) { 140 | var h_url = ""; 141 | var googleTransApi = StringFormat("https://translate.google.cn/translate_a/single?client=gtx&dt=t&dj=1&sl={1}&tl={0}&hl=zh-CN", Trans.transTargetLang, Trans.transOrigLang); 142 | h_url = googleTransApi + "&q=" + encodeURIComponent(Trans.transText); 143 | 144 | GM_xmlhttpRequest({ 145 | method: "GET", 146 | url: h_url, 147 | onload: function (r) { 148 | console.log(r) 149 | setTimeout(function () { 150 | var data = JSON.parse(r.responseText); 151 | var trans = [], origs = [], src = ""; 152 | for (var i = 0; i < data.sentences.length; i++) { 153 | var getransCont = data.sentences[i]; 154 | trans.push(getransCont.trans); 155 | origs.push(getransCont.orig); 156 | } 157 | src = data.src; 158 | Trans.transResult.trans = trans; 159 | Trans.transResult.orig = origs; 160 | Trans.transResult.origLang = src; 161 | h_onloadfn(); 162 | }, 300); 163 | }, 164 | onerror: function (e) { 165 | console.error(e); 166 | } 167 | }); 168 | }, 169 | }; 170 | 171 | //有道翻译 172 | var youdaoTrans = { 173 | code: "yd", 174 | codeText: "有道", 175 | defaultOrigLang: "en", //默认源语言 176 | defaultTargetLang: "ZH-CHS", //默认目标语言 177 | langList: { "AUTO": "自动检测", "zh-CHS": "中文简体", "en": "英文", "ja": "日文", "ko": "韩文", "fr": "法文", "es": "西班牙文", "pt": "葡萄牙文", "it": "意大利文", "ru": "俄文", "vi": "越南文", "de": "德文", "ar": "阿拉伯文", "id": "印尼文" }, 178 | Execute: function (h_onloadfn) { 179 | var h_url = "", 180 | h_headers = {}, 181 | h_data = ""; 182 | 183 | var youdaoTransApi = "http://fanyi.youdao.com/translate_o?client=fanyideskweb&keyfrom=fanyi.web&version=2.1&doctype=json"; 184 | var tempsalt = "" + (new Date).getTime() + parseInt(10 * Math.random(), 10); 185 | var tempsign = $.md5("fanyideskweb" + Trans.transText + tempsalt + "Nw(nmmbP%A-r6U3EUn]Aj"); 186 | h_url = youdaoTransApi; 187 | h_headers = { 188 | "Content-Type": "application/x-www-form-urlencoded", 189 | "Referer": "http://fanyi.youdao.com/" 190 | }; 191 | h_data = StringFormat("from={0}&to={1}&salt={2}&sign={3}&i={4}", Trans.transOrigLang, Trans.transTargetLang, tempsalt, tempsign, Trans.transText); 192 | 193 | GM_xmlhttpRequest({ 194 | method: "POST", 195 | url: h_url, 196 | headers: h_headers, 197 | data: h_data, 198 | onload: function (r) { 199 | console.log(r) 200 | setTimeout(function () { 201 | var data = JSON.parse(r.responseText); 202 | var trans = [], 203 | origs = [], 204 | src = ""; 205 | if (data.errorCode == 0) { 206 | for (var j = 0; j < data.translateResult.length; j++) { 207 | var ydTransCont = data.translateResult[j]; 208 | var ydtgt = ""; 209 | var ydsrc = ""; 210 | for (var k = 0; k < ydTransCont.length; k++) { 211 | var ydcont = ydTransCont[k]; 212 | ydtgt += ydcont.tgt; 213 | ydsrc += ydcont.src; 214 | } 215 | trans.push(ydtgt); 216 | origs.push(ydsrc); 217 | } 218 | src = data.type; 219 | Trans.transResult.trans = trans; 220 | Trans.transResult.orig = origs; 221 | Trans.transResult.origLang = src.split("2")[0]; 222 | 223 | } 224 | h_onloadfn(); 225 | }, 300); 226 | }, 227 | onerror: function (e) { 228 | console.error(e); 229 | } 230 | }); 231 | }, 232 | }; 233 | 234 | function a (r) { 235 | if (Array.isArray(r)) { 236 | for (var o = 0, t = Array(r.length); o < r.length; o++) 237 | t[o] = r[o]; 238 | return t 239 | } 240 | return Array.from(r) 241 | } 242 | 243 | function n (r, o) { 244 | for (var t = 0; t < o.length - 2; t += 3) { 245 | var a = o.charAt(t + 2); 246 | a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a), 247 | a = "+" === o.charAt(t + 1) ? r >>> a : r << a, 248 | r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a; 249 | } 250 | return r 251 | } 252 | 253 | function e (r, gtk) { 254 | var i = null; 255 | var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); 256 | if (null === o) { 257 | var t = r.length; 258 | t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10)); 259 | } else { 260 | for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++) 261 | "" !== e[C] && f.push.apply(f, a(e[C].split(""))), 262 | C !== h - 1 && f.push(o[C]); 263 | var g = f.length; 264 | g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join("")); 265 | } 266 | var u = void 0 267 | ; 268 | u = null !== i ? i : (i = gtk || "") || ""; 269 | for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { 270 | var A = r.charCodeAt(v); 271 | 128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), 272 | S[c++] = A >> 18 | 240, 273 | S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, 274 | S[c++] = A >> 6 & 63 | 128), 275 | S[c++] = 63 & A | 128); 276 | } 277 | for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), b = 0; b < S.length; b++) 278 | p += S[b], 279 | p = n(p, F); 280 | return p = n(p, D), 281 | p ^= s, 282 | 0 > p && (p = (2147483647 & p) + 2147483648), 283 | p %= 1e6, 284 | p.toString() + "." + (p ^ m) 285 | } 286 | 287 | /** 288 | * @param {string} word 289 | * @param {string} gtk 290 | * @return {string} 291 | */ 292 | var calcSign = function (word, gtk) { 293 | return e(word, gtk); 294 | }; 295 | 296 | //获取gtk和token 297 | function GetToken () { 298 | GM_xmlhttpRequest({ 299 | method: "GET", 300 | url: "https://fanyi.baidu.com/", 301 | timeout: 5000, 302 | onload: function (r) { 303 | var gtkMatch = /window\.gtk = '(.*?)'/.exec(r.responseText); 304 | var commonTokenMatch = /token: '(.*?)',/.exec(r.responseText); 305 | if (!gtkMatch) { 306 | console.log("获取gtk失败!!!"); 307 | } 308 | if (!commonTokenMatch) { 309 | console.log("获取token失败!!!"); 310 | } 311 | var newGtk = gtkMatch[1]; 312 | var newCommonToken = commonTokenMatch[1]; 313 | 314 | if (typeof newGtk !== 'undefined') { 315 | baiduTrans.gtk = newGtk; 316 | } 317 | if (typeof newCommonToken !== 'undefined') { 318 | baiduTrans.token = newCommonToken; 319 | } 320 | }, 321 | onerror: function (e) { 322 | console.error(e); 323 | } 324 | }); 325 | } 326 | 327 | //百度翻译 328 | var baiduTrans = { 329 | code: "bd", 330 | codeText: "百度", 331 | gtk: "", 332 | token: "", 333 | defaultOrigLang: "en", //默认源语言 334 | defaultTargetLang: "zh", //默认目标语言 335 | langList: { "auto": "自动检测", "zh": "中文", "cht": "繁体中文", "en": "英语", "jp": "日语", "kor": "韩语", "fra": "法语", "spa": "西班牙语", "pt": "葡萄牙语", "it": "意大利语", "ru": "俄语", "vie": "越南语", "de": "德语", "ara": "阿拉伯语" }, 336 | Execute: function (h_onloadfn) { 337 | if (Trans.transOrigLang == "auto") 338 | this.AutoTrans(h_onloadfn); 339 | else 340 | this.ExecTrans(h_onloadfn); 341 | 342 | }, 343 | AutoTrans: function (h_onloadfn) { 344 | var self = this; 345 | var datas = { 346 | query: Trans.transText 347 | }; 348 | GM_xmlhttpRequest({ 349 | method: "POST", 350 | headers: { 351 | "referer": 'https://fanyi.baidu.com', 352 | "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8', 353 | }, 354 | url: "https://fanyi.baidu.com/langdetect", 355 | data: ObjectToQueryString(datas), 356 | onload: function (r) { 357 | var data = JSON.parse(r.responseText); 358 | if (data.error === 0) { 359 | Trans.transOrigLang = data.lan; 360 | self.ExecTrans(h_onloadfn); 361 | } 362 | }, 363 | onerror: function (e) { 364 | console.error(e); 365 | } 366 | }); 367 | }, 368 | ExecTrans: function (h_onloadfn) { 369 | var tempSign = calcSign(Trans.transText, this.gtk); 370 | var datas = { 371 | from: Trans.transOrigLang, 372 | to: Trans.transTargetLang, 373 | query: Trans.transText, 374 | transtype: "translang", 375 | simple_means_flag: 3, 376 | sign: tempSign, 377 | token: this.token 378 | }; 379 | GM_xmlhttpRequest({ 380 | method: "POST", 381 | headers: { 382 | "referer": 'https://fanyi.baidu.com', 383 | "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8', 384 | //"User-Agent": window.navigator.userAgent, 385 | }, 386 | url: "https://fanyi.baidu.com/v2transapi", 387 | data: ObjectToQueryString(datas), 388 | onload: function (r) { 389 | setTimeout(function () { 390 | var result = JSON.parse(r.responseText); 391 | var trans_result = result.trans_result; 392 | var transDatas = trans_result.data; 393 | 394 | var trans = [], origs = [], src = ""; 395 | for (var i = 0; i < transDatas.length; i++) { 396 | var getransCont = transDatas[i]; 397 | trans.push(getransCont.dst); 398 | origs.push(getransCont.src); 399 | } 400 | src = trans_result.from; 401 | Trans.transResult.trans = trans; 402 | Trans.transResult.orig = origs; 403 | Trans.transResult.origLang = src; 404 | h_onloadfn(); 405 | }, 500); 406 | }, 407 | onerror: function (e) { 408 | console.error(e); 409 | } 410 | }); 411 | }, 412 | init: function () { 413 | GetToken(); 414 | } 415 | }; 416 | 417 | var Trans = { 418 | transEngineList: {}, //翻译引擎实例列表 419 | transEngine: "", //当前翻译引擎。ge(谷歌)/yd(有道) 420 | transEngineObj: {}, //当前翻译引擎实例 421 | transTargetLang: "", //目标语言。 422 | transOrigLang: "", //源语言 423 | transType: "text", //翻译类型。word(划词翻译)/text(输入文本翻译)/page(整页翻译) 424 | transText: "", //被翻译内容 425 | transResult: { //当前翻译内容 426 | //译文 427 | trans: [], 428 | //原文 429 | orig: [], 430 | //原文语言 431 | origLang: "" 432 | }, 433 | Execute: function (h_onloadfn) { 434 | this.transResult.trans = []; 435 | this.transResult.orig = []; 436 | this.transResult.origLang = ""; 437 | this.transEngineObj.Execute(h_onloadfn); 438 | }, 439 | GetLangList: function () { 440 | var langList = {}; 441 | langList = this.transEngineObj.langList; 442 | return langList; 443 | }, 444 | Update: function () { 445 | this.transResult.trans = []; 446 | this.transResult.orig = []; 447 | this.transResult.origLang = ""; 448 | this.transEngineObj = this.transEngineList[this.transEngine]; 449 | this.transTargetLang = this.transEngineObj.defaultTargetLang; 450 | this.transOrigLang = this.transEngineObj.defaultOrigLang; 451 | }, 452 | Clear: function () { 453 | this.transEngine = ""; //当前翻译引擎。ge(谷歌)/yd(有道) 454 | this.transTargetLang = ""; //目标语言。 455 | this.transOrigLang = ""; //源语言 456 | this.transText = ""; //被翻译内容 457 | this.transResult.trans = []; 458 | this.transResult.orig = []; 459 | this.transResult.origLang = ""; 460 | }, 461 | //注册翻译引擎接口并执行翻译引擎的初始化接口 462 | RegisterEngine: function () { 463 | /** 464 | * 翻译引擎必须提供以下接口 465 | code:"", //代号 466 | codeText:"", //代号描述 467 | defaultOrigLang:"", //默认源语言 468 | defaultTargetLang:"", //默认目标语言 469 | langList: {}, //支持翻译语言列表 470 | Execute: function (h_onloadfn) {}, //执行翻译 471 | init:function(){}, //可选,初始化接口,在脚本创建时立即执行 472 | */ 473 | var transEngineListObj = {}; 474 | transEngineListObj[googleTrans.code] = googleTrans; 475 | transEngineListObj[youdaoTrans.code] = youdaoTrans; 476 | transEngineListObj[baiduTrans.code] = baiduTrans; 477 | this.transEngineList = transEngineListObj; 478 | for (var key in this.transEngineList) { 479 | if (this.transEngineList.hasOwnProperty(key) && this.transEngineList[key].hasOwnProperty("init")) { 480 | this.transEngineList[key].init(); 481 | } 482 | } 483 | } 484 | }; 485 | 486 | //-------------初始化-------------- 487 | let store = null; 488 | let config = null; 489 | function initData () { 490 | store = new Store({ 491 | /** 492 | * 默认配置项 493 | * isAuto:Boolean = false 是否自动翻译 494 | * transEngine:String = 'ge' 翻译引擎 495 | * transOrigLang:String = '自动' 源语言 496 | * transTargetLang:String = '中文简体' 目标语言 497 | * ignoreWork:String[] 忽略的单词 498 | * allowUrl:String[] 允许自动翻译的URL 499 | * ignoreUrl:String[] 忽略自动翻译的URL(优先) 500 | * ignoreElement:String[] 忽略的元素(这里填的是定位这个元素的CSS选择器的格式) 501 | * replaceWork:Object[] 翻译之前替换的单词,Object:{match:RegExp|String,value:String}match是匹配,value是替换的值 502 | */ 503 | config: { 504 | isAuto: false, 505 | transEngine: 'ge', 506 | transOrigLang: '自动', 507 | transTargetLang: '中文简体', 508 | ignoreWork: [ 509 | 'Express', 510 | 'Fastify', 511 | 'Nest', 512 | 'node', 513 | 'angular', 514 | 'yarn' 515 | ], 516 | allowUrl:[ 517 | 'github.com', 518 | 'nestjs.com', 519 | 'apollographql.com' 520 | ], 521 | ignoreUrl: [ 522 | 'www.runoob.com', 523 | ], 524 | ignoreElement: [ 525 | 'code', 526 | '.cli', 527 | 'style', 528 | 'script', 529 | '.repository-topics-container', 530 | '.file-navigation', 531 | '#js-repo-pjax-container > div.container-lg.clearfix.new-discussion-timeline.px-3 > div > div.Box.mb-3.Box--condensed', 532 | '.highlight>pre', 533 | '#js-repo-pjax-container > div.pagehead.repohead.hx_repohead.readability-menu.bg-gray-light.pb-0.pt-3 > div', 534 | '.gatsby-highlight', 535 | '.language-typescript', 536 | '.language-javascript', 537 | '.language-bash', 538 | '.language-shell' 539 | ], 540 | replaceWork: [] 541 | } 542 | }); 543 | config = store.config; 544 | } 545 | 546 | /** 547 | * 初始化自定义样式 548 | */ 549 | function initStyle () { 550 | const style = document.createElement("style"); 551 | style.innerText = ` 552 | 553 | `; 554 | document.body.appendChild(style); 555 | } 556 | 557 | let isExec = false; 558 | /** 559 | * 初始化事件 560 | */ 561 | function initEvent () { 562 | cliUI.on('conf set isAuto', '配置是否自动翻译,格式: conf set isAuto <0|1> ,0是关闭 1是开启', ([v]) => { 563 | config.isAuto = v * 1; 564 | console.log(config.isAuto) 565 | store.save(); 566 | }); 567 | cliUI.on('conf set transEngine', '配置翻译引擎,格式: conf set transEngine ,ge是谷歌', ([v]) => { 568 | config.transEngine = v; 569 | console.log(config.transEngine); 570 | store.save(); 571 | }); 572 | cliUI.on('conf set transOrigLang', '配置翻译源语言,格式: conf set transOrigLang <自动>', ([v]) => { 573 | config.transOrigLang = v; 574 | console.log(config.transOrigLang); 575 | store.save(); 576 | }); 577 | cliUI.on('conf set transTargetLang', '配置目标语言,格式: conf set transTargetLang <中文简体>', ([v]) => { 578 | config.transTargetLang = v; 579 | console.log(config.transTargetLang); 580 | store.save(); 581 | }); 582 | cliUI.on('conf set ignoreWork add', '添加忽略翻译的单词,格式: conf set ignoreWork add <单词>', ([v]) => { 583 | config.ignoreWork.push(v); 584 | console.log(config.ignoreWork); 585 | store.save(); 586 | }); 587 | cliUI.on('conf set ignoreWork del', '删除忽略翻译的单词,格式: conf set ignoreWork del <单词>', ([v]) => { 588 | const index = config.ignoreWork.findIndex(item => item.match == v); 589 | config.ignoreWork.splice(index, 1); 590 | console.log(config.ignoreWork); 591 | store.save(); 592 | }); 593 | cliUI.on('conf set ignoreElement add', '添加忽略翻译的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement add <元素选择器>', ([v]) => { 594 | config.ignoreElement.push(v); 595 | console.log(config.ignoreElement); 596 | store.save(); 597 | }); 598 | cliUI.on('conf set ignoreElement del', '删除忽略翻译的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement del <元素选择器>', ([v]) => { 599 | const index = config.ignoreElement.findIndex(item => item.match == v); 600 | config.ignoreElement.splice(index, 1); 601 | console.log(config.ignoreElement); 602 | store.save(); 603 | }); 604 | cliUI.on('conf set allowUrl add', '添加允许的自动翻译的URL,格式: conf set allowUrl add ', ([v]) => { 605 | if(!config.allowUrl){ 606 | config.allowUrl = []; 607 | } 608 | config.allowUrl.push(v); 609 | console.log(config.allowUrl); 610 | store.save(); 611 | }); 612 | cliUI.on('conf set allowUrl del', '删除允许自动翻译的URL,格式: conf set allowUrl del ', ([v]) => { 613 | const index = config.allowUrl.findIndex(item => item.match == v); 614 | config.allowUrl.splice(index, 1); 615 | console.log(config.allowUrl); 616 | store.save(); 617 | }); 618 | cliUI.on('conf set ignoreUrl add', '添加忽略自动翻译的URL,格式: conf set ignoreUrl add ', ([v]) => { 619 | config.ignoreUrl.push(v); 620 | console.log(config.ignoreUrl); 621 | store.save(); 622 | }); 623 | cliUI.on('conf set ignoreUrl del', '删除忽略自动翻译的URL,格式: conf set ignoreUrl del ', ([v]) => { 624 | const index = config.ignoreUrl.findIndex(item => item.match == v); 625 | config.ignoreUrl.splice(index, 1); 626 | console.log(config.ignoreUrl); 627 | store.save(); 628 | }); 629 | cliUI.on('conf set replaceWork add', '添加翻译之前替换的单词,格式: conf set replaceWork add <匹配单词/替换单词>', ([v]) => { 630 | const arr = v.split('/'); 631 | config.replaceWork.push({ 632 | match: arr[0], 633 | value: arr[1] 634 | }); 635 | console.log(config.replaceWork); 636 | store.save(); 637 | 638 | }); 639 | cliUI.on('conf set replaceWork del', '删除翻译之前替换的单词,格式: conf set replaceWork del <匹配单词>', ([v]) => { 640 | const index = config.replaceWork.findIndex(item => item.match == v); 641 | config.replaceWork.splice(index, 1); 642 | console.log(config.replaceWork); 643 | store.save(); 644 | }); 645 | cliUI.on('conf get all', '获取所有的配置', ([v]) => { 646 | console.log(config); 647 | }); 648 | cliUI.on('conf init', '复位配置', ([v]) => { 649 | store.remove('config'); 650 | initData(); 651 | console.log(config); 652 | }); 653 | floatPart.button.onclick = () => { 654 | if (isExec) switchText(); 655 | else exec(); 656 | } 657 | 658 | document.body.addEventListener('mouseup', (e) => { 659 | const path = e.path; 660 | if (path.length && config.isAuto && isFanyi()) { 661 | for (let i = 0; i < path.length; i++) { 662 | const item = path[i]; 663 | if (item.nodeName === 'A') { 664 | setTimeout(exec, 1000) 665 | break; 666 | } 667 | } 668 | } 669 | }); 670 | } 671 | 672 | /** 673 | * 初始化菜单 674 | */ 675 | function initMenu () { 676 | monkeyMenu.on('打开命令', function () { 677 | cliUI.show(); 678 | }); 679 | monkeyMenu.on('翻译页面', async function () { 680 | exec(); 681 | }); 682 | monkeyMenu.on('切换原文/翻译', function () { 683 | switchText(); 684 | }); 685 | monkeyMenu.on('更新脚本', function () { 686 | window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/400334-deepl', { 687 | active: true, 688 | insert: true, 689 | setParent: true 690 | }); 691 | }); 692 | monkeyMenu.on('关于', function () { 693 | window.GM_openInTab('https://github.com/WumaCoder/mini-tools', { 694 | active: true, 695 | insert: true, 696 | setParent: true 697 | }); 698 | }); 699 | monkeyMenu.on('反馈', function () { 700 | window.GM_openInTab('https://github.com/WumaCoder/mini-tools/issues', { 701 | active: true, 702 | insert: true, 703 | setParent: true 704 | }); 705 | }); 706 | } 707 | let cliUI = null; 708 | let floatPart = null; 709 | function initView () { 710 | cliUI = new CLI(); 711 | floatPart = new FloatPart(); 712 | } 713 | 714 | /** 715 | * 节流函数 716 | * @param {Function} func 回调 717 | * @param {Number} wait 时间 718 | * @return {Function} 返回是执行函数 719 | */ 720 | function throttle (func, wait) { 721 | let previous = 0; 722 | return function () { 723 | let now = Date.now(); 724 | let context = this; 725 | let args = arguments; 726 | if (now - previous > wait) { 727 | func.apply(context, args); 728 | previous = now; 729 | } 730 | } 731 | } 732 | 733 | //----------------工具函数------------------ 734 | /** 735 | * 防抖 736 | * @param {*} fn 737 | * @param {*} wait 738 | * @return {Function} 返回是执行函数 739 | */ 740 | function debounce (fn, wait) { 741 | let timeout = null; 742 | return function () { 743 | if (timeout !== null) clearTimeout(timeout); 744 | timeout = setTimeout(fn, wait); 745 | } 746 | } 747 | 748 | /** 749 | * 将字符串转化为doument 750 | */ 751 | function toDocument (str = "") { 752 | str = str.replace(/\r\n\f/igm, ''); 753 | const dom = document.createElement('DOM'); 754 | dom.innerHTML = str; 755 | return dom; 756 | } 757 | 758 | 759 | /** 760 | * 深度筛选DOM树 761 | * @param {Element | Node} node 一个节点 762 | */ 763 | const resRules = [ //所有规则都为true才可以通过 764 | (el) => { 765 | return typeof el.nodeValue === 'string'; 766 | }, 767 | (el) => { 768 | return Boolean(el.nodeValue.replace(/[\W]+/igm, '')); 769 | }, 770 | ]; 771 | /** 772 | * 深度遍历body,检查出有文本的元素 773 | */ 774 | let nodeList = []; //存储有文本的节点 775 | let _nodeList = [];//这个和上面一样,但是这个不会被清空 776 | let textCount = 0; //计数器 777 | let maxTextCount = 100; 778 | function deepFilterList (node) { 779 | const { ignoreElement } = config; 780 | switch (node.nodeName) { 781 | case '#text': 782 | for (let i = 0; i < resRules.length; i++) { 783 | const res = resRules[i]; 784 | if (!res(node)) { 785 | return; 786 | } 787 | } 788 | if (!node['data-ok'] && ( textCount===0 || maxTextCount > (textCount+node.nodeValue.length))) { 789 | textCount+=node.nodeValue.length; 790 | nodeList.push(node); 791 | } 792 | default: 793 | for (let i = 0; i < node.childNodes.length; i++) { 794 | const el = node.childNodes[i]; 795 | let f = false; 796 | for (let j = 0; j < ignoreElement.length; j++) { 797 | const item = ignoreElement[j]; 798 | const temp = document.querySelector(item); 799 | 800 | if (temp && temp.nodeName == el.nodeName && temp.className == el.className) { 801 | f = true; 802 | break; 803 | } 804 | } 805 | if (f) { 806 | continue; 807 | } 808 | deepFilterList(el); 809 | } 810 | break; 811 | } 812 | } 813 | 814 | function transformToDLJobs (nodeList = []) { 815 | const tempList = []; 816 | for (let i = 0; i < nodeList.length; i++) { 817 | const item = nodeList[i]; 818 | const preItem = i !== 0 ? [nodeList[i - 1].nodeValue] : []; 819 | const nextItem = i !== nodeList.length - 1 ? [nodeList[i + 1].nodeValue] : []; 820 | tempList.push({ 821 | "kind": "default", 822 | "raw_en_sentence": item.nodeValue, 823 | "raw_en_context_before": preItem, 824 | "raw_en_context_after": nextItem, 825 | "preferred_num_beams": 1 826 | }); 827 | } 828 | return tempList; 829 | } 830 | 831 | // 将nodeList内的英文转化为字符串 832 | function transformToString (nodeList) { 833 | let str = "" 834 | for (let i = 0; i < nodeList.length; i++) { 835 | const item = nodeList[i]; 836 | str += item.nodeValue.trim(); 837 | str += ". _SPT_ " 838 | } 839 | return str; 840 | } 841 | //上面的反向 842 | function reTransformToString (arr) { 843 | const str = arr.map(item => item.replace(/[。 ]+$/igm, ' ')).join(''); 844 | return reTransformIgnoreWork(str).split('_SPT_'); 845 | } 846 | /** 847 | * 为字符串添加忽略标记 848 | * @param {*} str 字符串 849 | */ 850 | function transformIgnoreWork (str) { 851 | const { ignoreWork, replaceWork } = config; 852 | replaceWork.forEach(item => { 853 | str = str.replace(new RegExp(item.match,'gm'), item.value); 854 | }); 855 | ignoreWork.forEach((item, index) => { 856 | str = str.replace(new RegExp(item, 'igm'), '_' + index) 857 | }); 858 | return str; 859 | } 860 | 861 | /** 862 | * 将反义反转 863 | * @param {*} str 字符串 864 | */ 865 | function reTransformIgnoreWork (str) { 866 | const { ignoreWork } = config; 867 | ignoreWork.forEach((item, index) => { 868 | str = str.replace(new RegExp('_' + index, 'gm'), item) 869 | }); 870 | return str; 871 | } 872 | 873 | /** 874 | * 最后的操作 875 | * @param {*} arr dom 876 | * @param {*} arr2 fanyi 877 | */ 878 | function replaceNodeValue (arr, arr2) { 879 | for (let i = 0; i < arr.length; i++) { 880 | const item = arr[i]; 881 | item['data-text'] = item.nodeValue; 882 | item['data-ok'] = 1; 883 | _nodeList.push(item); 884 | item.nodeValue = arr2[i]; 885 | } 886 | } 887 | /** 888 | * 原文切换 889 | * @param {*} arr nodeList 890 | */ 891 | function switchText () { 892 | let arr = _nodeList; 893 | 894 | for (let i = 0; i < arr.length; i++) { 895 | const item = arr[i]; 896 | const temp = item['data-text']; 897 | item['data-text'] = item.nodeValue; 898 | item.nodeValue = temp; 899 | } 900 | } 901 | let execing = false; 902 | async function exec () { 903 | if(execing){ 904 | console.log('翻译中'); 905 | return; 906 | } 907 | execing = true; 908 | while (true) { 909 | nodeList = []; 910 | textCount = 0; 911 | deepFilterList(document.body); 912 | if (nodeList.length === 0) { 913 | break; 914 | } 915 | let str = transformToString(nodeList); 916 | str = transformIgnoreWork(str); 917 | Trans.RegisterEngine(); 918 | Trans.transEngine = config.transEngine; 919 | Trans.Update(); 920 | Trans.transText = str;//'Updating vue-cli-plugin-apollo will also update both Apollo Client and its configuration for you!'; 921 | await new Promise(resolve => { 922 | Trans.Execute(() => { 923 | const arr2 = reTransformToString(Trans.transResult.trans); 924 | replaceNodeValue(nodeList, arr2); 925 | resolve(); 926 | }); 927 | }); 928 | await new Promise(resolve => { 929 | setTimeout(resolve, 500); 930 | }) 931 | } 932 | isExec = true; 933 | execing = false; 934 | } 935 | 936 | function isFanyi () { 937 | // deepFilterList(document.body); 938 | let f = false; 939 | for (let i = 0; i < config.allowUrl.length; i++) { 940 | if (window.location.href.match(new RegExp(config.allowUrl[i], 'igm'))) 941 | f=true; 942 | } 943 | for (let i = 0; i < config.ignoreUrl.length; i++) { 944 | if (window.location.href.match(new RegExp(config.ignoreUrl[i], 'igm'))) 945 | f=false; 946 | } 947 | return f; 948 | } 949 | //------------------操作类-------------------------- 950 | /** 951 | * 永久保存的数据类 952 | */ 953 | class Store { 954 | constructor(obj) { 955 | this._keys = Object.keys(obj); 956 | this._keys.forEach(key => { 957 | obj[key] = GM_getValue(key, obj[key]); 958 | }); 959 | Object.assign(this, obj); 960 | } 961 | save () { 962 | this._keys.forEach(key => { 963 | GM_setValue(key, this[key]); 964 | }) 965 | } 966 | remove (key) { 967 | GM_deleteValue(key); 968 | } 969 | } 970 | class CLI { 971 | constructor() { 972 | this.cmds = []; 973 | this.initStyle(); 974 | this.initView(); 975 | this.initEvent(); 976 | this.hide(); 977 | } 978 | initStyle () { 979 | const style = document.createElement('style'); 980 | style.innerText = `.cli{ 981 | position: fixed; 982 | top: 0; 983 | width: 50vw; 984 | margin: 0 25vw; 985 | background: #fff; 986 | border: 1px solid gainsboro; 987 | border-top: 0; 988 | padding: 10px; 989 | box-shadow: #ddd 0 0 5px 5px; 990 | font-family:'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 991 | user-select: none; 992 | z-index:1000000000000000; 993 | } 994 | .cli-top{ 995 | width: 100%; 996 | } 997 | .cli-input{ 998 | display: block; 999 | height: 40px; 1000 | width: 98%; 1001 | font-size: 25px; 1002 | padding:0 5px; 1003 | font-family:'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; 1004 | } 1005 | .cli-tip{ 1006 | color: grey; 1007 | margin-top: 5px; 1008 | } 1009 | .cli-list{ 1010 | border-top:1px solid #ddd; 1011 | margin-top: 10px; 1012 | max-height:500px; 1013 | overflow-y:auto; 1014 | } 1015 | .cli-item{ 1016 | font-size: 20px; 1017 | border-bottom: 1px solid #ddd; 1018 | padding: 5px 10px; 1019 | } 1020 | .cli-item:hover{ 1021 | background-color: #ddd; 1022 | } 1023 | .cli-info{ 1024 | font-size: 14px; 1025 | color: gray; 1026 | }`; 1027 | document.body.appendChild(style) 1028 | } 1029 | 1030 | initView () { 1031 | this.main = document.createElement('DIV'); 1032 | this.main.innerHTML = ` 1033 |
1034 |
1035 | 1036 |
1037 |
1038 |
1039 |
1040 | Please press [ESC] to exit, or press [Enter] to execute the command. Please go to the console to see the output. 1041 |
1042 |
1043 |
1044 |
1045 | ` 1046 | document.body.appendChild(this.main); 1047 | this.list = this.main.querySelector('.cli-list'); 1048 | this.input = this.main.querySelector('.cli-input'); 1049 | } 1050 | 1051 | initEvent () { 1052 | this.input.addEventListener('input', () => { 1053 | const value = this.input.value; 1054 | const list = this.matchCmds(value); 1055 | if (list.length === 1) { 1056 | this.matchCmd = list[0]; 1057 | } 1058 | this.clearMatchList(); 1059 | this.addItems(list); 1060 | }); 1061 | this.input.addEventListener('keypress', (e) => { 1062 | if (e.key === 'Enter') { 1063 | this.matchCmd = this.matchCmds(this.input.value)[0] || null; 1064 | if (!this.matchCmd) { 1065 | this.clearMatchList(); 1066 | this.addItems([{ orgCmd: '没有匹配到命令', info: '暂无该命令' }]); 1067 | return; 1068 | } 1069 | const cmdStr = this.input.value.substr(1); 1070 | const matchCmdStr = this.matchCmd.cmd.join(' '); 1071 | this.matchCmd.callback(cmdStr.replace(matchCmdStr + ' ', '').split(/ +/igm)); 1072 | this.input.value = '>'; 1073 | } 1074 | }); 1075 | document.addEventListener('keyup', (e) => { 1076 | if (e.key === 'Escape') { 1077 | this.hide(); 1078 | } else if (e.key === 'Tab') { 1079 | if (this.matchCmd) 1080 | this.input.value = '>' + this.matchCmd.orgCmd; 1081 | } 1082 | }); 1083 | this.input.focus(); 1084 | this.input.setSelectionRange(1, 1); 1085 | } 1086 | 1087 | matchCmds (ucmd) { 1088 | if (!ucmd.match(/>/igm)) { 1089 | return false; 1090 | } 1091 | if (!ucmd.match(/\w/igm)) { 1092 | return false; 1093 | } 1094 | ucmd = ucmd.substr(1); 1095 | const { cmd } = this.parser(ucmd); 1096 | const str = cmd.join(''); 1097 | return this.cmds.filter(item => { 1098 | const str2 = item.cmd.join(''); 1099 | if (str.length > str2.length) { 1100 | return str.match(new RegExp(str2, 'igm')) 1101 | } else { 1102 | return str2.match(new RegExp(str, 'igm')) 1103 | } 1104 | }) 1105 | } 1106 | 1107 | parser (orgCmd) { 1108 | const tokens = orgCmd.split(/ +/igm); 1109 | const args = tokens.filter(item => item.match(/^\$\w+/igm)); 1110 | const cmd = tokens.filter(item => item.match(/^\w+/igm)); 1111 | return { tokens, args, cmd }; 1112 | } 1113 | 1114 | on (orgCmd, info, callback) { 1115 | const { cmd, args } = this.parser(orgCmd); 1116 | const ret = { 1117 | orgCmd, 1118 | cmd, 1119 | args, 1120 | info, 1121 | callback 1122 | }; 1123 | this.cmds.push(ret); 1124 | if (ret) this.addItems([ret]); 1125 | } 1126 | 1127 | createItem (orgCmd, info) { 1128 | const div = document.createElement('DIV'); 1129 | div.className = 'cli-item'; 1130 | div.innerHTML = `
1131 | ${orgCmd} 1132 |
1133 |
1134 | ${info} 1135 | 1136 |
`; 1137 | div.addEventListener('click', () => { 1138 | this.input.value = '>' + orgCmd; 1139 | this.input.focus() 1140 | }); 1141 | return div; 1142 | } 1143 | 1144 | addItems (items = []) { 1145 | items && items.forEach(item => { 1146 | this.list.appendChild(this.createItem(item.orgCmd, item.info)); 1147 | }); 1148 | } 1149 | 1150 | clearMatchList () { 1151 | for (let i = this.list.childNodes.length - 1; i >= 0; i--) { 1152 | const node = this.list.childNodes[i]; 1153 | node.remove(); 1154 | } 1155 | } 1156 | 1157 | show () { 1158 | this.main.style.display = 'block'; 1159 | } 1160 | 1161 | hide () { 1162 | this.main.style.display = 'none'; 1163 | } 1164 | } 1165 | 1166 | class FloatPart { 1167 | constructor() { 1168 | this.initStyle(); 1169 | this.initView(); 1170 | } 1171 | initStyle () { 1172 | const style = document.createElement('style'); 1173 | style.innerText = ` 1174 | .fast { 1175 | position: fixed; 1176 | width: 100px; 1177 | height: 60px; 1178 | background: #F46F52; 1179 | box-shadow: #ddd 0 0 10px; 1180 | border: #ddd 1px solid; 1181 | bottom: -40px; 1182 | left: 40px; 1183 | line-height: 50px; 1184 | text-align: center; 1185 | border-radius: 10px 10px 0 0; 1186 | padding-top: 10px; 1187 | transition: 0.36s all; 1188 | z-index:1000; 1189 | } 1190 | 1191 | .fast:hover { 1192 | bottom: 0px; 1193 | } 1194 | 1195 | .fast-button { 1196 | border: 1px #fff solid; 1197 | padding: 4px 8px; 1198 | color: #fff; 1199 | user-select: none; 1200 | text-decoration: none; 1201 | }`; 1202 | document.body.appendChild(style) 1203 | } 1204 | 1205 | initView () { 1206 | this.main = document.createElement('DIV'); 1207 | this.main.className = 'fast'; 1208 | this.button = document.createElement('A'); 1209 | this.button.className = 'fast-button'; 1210 | this.button.href = 'javascript:void(0)'; 1211 | this.button.innerText = '翻译/切换' 1212 | this.main.appendChild(this.button) 1213 | document.body.appendChild(this.main); 1214 | } 1215 | } 1216 | /** 1217 | * 入口 1218 | */ 1219 | async function main () { 1220 | initData(); 1221 | initStyle(); 1222 | initMenu(); 1223 | initView(); 1224 | initEvent(); 1225 | maxTextCount = config.transEngine == 'yd' ? 100 : 1000; 1226 | 1227 | if (config.isAuto && isFanyi()) { 1228 | exec(); 1229 | } 1230 | console.log(config) 1231 | } 1232 | main(); 1233 | //--------------------- 1234 | // Your code here... 1235 | })(); 1236 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/panel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 26 | 28 | 31 | 34 | 37 | 38 | 39 | 40 |
41 |
42 | 49 | 54 |
55 | 63 |
64 |
66 |
67 |
68 | 69 |
70 | 71 |
72 |
73 |
75 |
76 |
77 | 78 | 82 |
83 |
84 | 85 | 89 |
90 |
91 | 92 | 97 |
98 |
99 | 100 | 104 |
105 |
106 | 107 | 108 |
109 |
110 | 111 | 112 |
113 |
114 | 115 | 116 |
117 | 118 |
119 |
120 |
121 |
122 | 123 | 124 | 133 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme - 副本.md: -------------------------------------------------------------------------------- 1 | ## DeepL深度翻译器 2 | 3 | > DeepL一个程序员的翻译神器,可能有人要问,市面上这么多的翻译插件这么多,我为毛要用你的。 4 | > 5 | > 首先这个翻译神器可以实现多个翻译引擎的翻译,目前支持有道和google,下一步我会把最近特别流行的翻译软件DeepL加入(其实最开始就想要加,奈何DeepL的反扒太牛逼了)。 6 | 7 | ### 开始 8 | 9 | - **[GitHub]( https://github.com/WumaCoder/mini-tools.git)** 10 | - **[作者码云](https://gitee.com/wuma/mini-tools)** 11 | - **[作者简书](https://www.jianshu.com/u/c5090bf9d2f3)** 12 | - **[安装脚本](https://greasyfork.org/zh-CN/scripts/400334-deepl)** 13 | 14 | ### 特征 15 | 16 | - *多引擎* 17 | - *页面翻译* 18 | - *自动翻译(这里包括自动翻译页面,自动翻译ajax更新的信息)* 19 | - *忽略单词(这里的忽略表示跳过,下同)* 20 | - *忽略元素* 21 | - *忽略域名* 22 | - *替换指定的单词* 23 | - *命令窗口* 24 | - *可配置* 25 | - *无服务器* 26 | 27 | ### 使用 28 | 29 | #### 页面翻译 30 | 31 | 安装好后打开一个英文的网站,具体安装点击上方的安装脚本 32 | 33 | ![image-20200414101306521](https://user-gold-cdn.xitu.io/2020/4/14/171768b7a650d8d9?w=1896&h=903&f=png&s=235291) 34 | 35 | 然后点击翻译页面 36 | 37 | ![image-20200414101341671](https://user-gold-cdn.xitu.io/2020/4/14/171768b8e6793aed?w=403&h=858&f=png&s=75261) 38 | 39 | ok翻译完成 40 | 41 | ![image-20200414101423124](https://user-gold-cdn.xitu.io/2020/4/14/171768c1d292a139?w=1904&h=899&f=png&s=188791) 42 | 43 | #### 显示原文 44 | 45 | 如果我们想显示原文,只需要点击`切换原文/翻译`按钮 46 | 47 | ![image-20200414101648590](https://user-gold-cdn.xitu.io/2020/4/14/171768c29e25c020?w=400&h=823&f=png&s=65749) 48 | 49 | 效果如下 50 | 51 | ![image-20200414101659748](https://user-gold-cdn.xitu.io/2020/4/14/171768c37fdd0f69?w=1920&h=908&f=png&s=228775) 52 | 53 | #### 使用命令 54 | 55 | 点击打开命令的功能即可。 56 | 57 | 具体命令如下: 58 | 59 | - 'conf set isAuto', '配置是否自动翻译,格式: conf set isAuto <0|1> ,0是关闭 1是开启' 60 | - 'conf set transEngine', '配置翻译引擎,格式: conf set transEngine ,ge是谷歌' 61 | - 'conf set transOrigLang', '配置翻译源语言,格式: conf set transOrigLang <自动>' 62 | - 'conf set transTargetLang', '配置目标语言,格式: conf set transTargetLang <中文简体>' 63 | - 'conf set ignoreWork add', '添加忽略的单词,格式: conf set ignoreWork add <单词>' 64 | - 'conf set ignoreWork del', '删除忽略的单词,格式: conf set ignoreWork del <单词>' 65 | - 'conf set ignoreElement add', '添加忽略的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement add <元素选择器> 66 | - 'conf set ignoreElement del', '删除忽略的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement del <元素选择器>' 67 | - 'conf set ignoreUrl add', '添加忽略的翻译的域名,格式: conf set ignoreUrl add ' 68 | - 'conf set ignoreUrl del', '删除忽略翻译的域名,格式: conf set ignoreUrl del ' 69 | - 'conf set replaceWork add', '添加翻译之前替换的单词,格式: conf set replaceWork add <匹配单词/替换单词>' 70 | - 'conf set replaceWork del', '删除翻译之前替换的单词,格式: conf set replaceWork del <匹配单词>' 71 | - 'conf get all', '获取所有的配置' 72 | - 'conf init', '复位配置' 73 | 74 | > 注意:参数位置的`<>`这对尖括号表示必须要填的,正在在写的时候不需要加尖括号,并且参数中不能存在空格。输入命令后按击Enter执行 75 | > 76 | > 如: conf set isAuto 1 表示开启自动翻译 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101247741.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101247741.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101306521.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101306521.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101341671.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101341671.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101423124.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101423124.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101648590.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101648590.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.assets/image-20200414101659748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/deep-l/readme.assets/image-20200414101659748.png -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/readme.md: -------------------------------------------------------------------------------- 1 | ## DeepL深度翻译器 2 | 3 | > DeepL一个程序员的翻译神器,可能有人要问,市面上这么多的翻译插件这么多,我为毛要用你的。 4 | > 5 | > 首先这个翻译神器可以实现多个翻译引擎的翻译,目前支持有道和google,下一步我会把最近特别流行的翻译软件DeepL加入(其实最开始就想要加,奈何DeepL的反扒太牛逼了)。 6 | 7 | ### 开始 8 | 9 | - **[GitHub]( https://github.com/WumaCoder/mini-tools.git)** 10 | - **[作者码云](https://gitee.com/wuma/mini-tools)** 11 | - **[作者简书](https://www.jianshu.com/u/c5090bf9d2f3)** 12 | - **[安装脚本](https://greasyfork.org/zh-CN/scripts/400334-deepl)** 13 | 14 | ### 特征 15 | 16 | - *多引擎* 17 | - *页面翻译* 18 | - *自动翻译(这里包括自动翻译页面,自动翻译ajax更新的信息)* 19 | - *忽略单词(这里的忽略表示跳过,下同)* 20 | - *忽略元素* 21 | - *忽略域名* 22 | - *替换指定的单词* 23 | - *命令窗口* 24 | - *可配置* 25 | - *无服务器* 26 | 27 | ### 使用 28 | 29 | #### 页面翻译 30 | 31 | 安装好后打开一个英文的网站,具体安装点击上方的安装脚本 32 | 33 | ![image-20200414101306521](readme.assets/image-20200414101306521.png) 34 | 35 | 然后点击翻译页面 36 | 37 | ![image-20200414101341671](readme.assets/image-20200414101341671.png) 38 | 39 | ok翻译完成 40 | 41 | ![image-20200414101423124](readme.assets/image-20200414101423124.png) 42 | 43 | #### 显示原文 44 | 45 | 如果我们想显示原文,只需要点击`切换原文/翻译`按钮 46 | 47 | ![image-20200414101648590](readme.assets/image-20200414101648590.png) 48 | 49 | 效果如下 50 | 51 | ![image-20200414101659748](readme.assets/image-20200414101659748.png) 52 | 53 | #### 使用命令 54 | 55 | 点击打开命令的功能即可。 56 | 57 | 具体命令如下: 58 | 59 | - 'conf set isAuto', '配置是否自动翻译,格式: conf set isAuto <0|1> ,0是关闭 1是开启' 60 | - 'conf set transEngine', '配置翻译引擎,格式: conf set transEngine ,ge是谷歌' 61 | - 'conf set transOrigLang', '配置翻译源语言,格式: conf set transOrigLang <自动>' 62 | - 'conf set transTargetLang', '配置目标语言,格式: conf set transTargetLang <中文简体>' 63 | - 'conf set ignoreWork add', '添加忽略的单词,格式: conf set ignoreWork add <单词>' 64 | - 'conf set ignoreWork del', '删除忽略的单词,格式: conf set ignoreWork del <单词>' 65 | - 'conf set ignoreElement add', '添加忽略的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement add <元素选择器> 66 | - 'conf set ignoreElement del', '删除忽略的元素(这里填的是定位这个元素的CSS选择器的格式),格式: conf set ignoreElement del <元素选择器>' 67 | - 'conf set ignoreUrl add', '添加忽略的翻译的域名,格式: conf set ignoreUrl add ' 68 | - 'conf set ignoreUrl del', '删除忽略翻译的域名,格式: conf set ignoreUrl del ' 69 | - 'conf set replaceWork add', '添加翻译之前替换的单词,格式: conf set replaceWork add <匹配单词/替换单词>' 70 | - 'conf set replaceWork del', '删除翻译之前替换的单词,格式: conf set replaceWork del <匹配单词>' 71 | - 'conf get all', '获取所有的配置' 72 | - 'conf init', '复位配置' 73 | 74 | > 注意:参数位置的`<>`这对尖括号表示必须要填的,正在在写的时候不需要加尖括号,并且参数中不能存在空格。输入命令后按击Enter执行 75 | > 76 | > 如: conf set isAuto 1 表示开启自动翻译 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /tampermonkey-scripts/deep-l/test.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 网页翻译助手 3 | // @version 1.2.1 4 | // @namespace https://github.com/zyufstudio/TM/tree/master/webTranslate 5 | // @description 支持划词翻译,输入文本翻译,谷歌整页翻译。可以自行选择谷歌翻译,有道字典翻译和百度翻译。 6 | // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAENklEQVRoQ+2ZTVITQRTH/28qyNIJFxCqnGwNJzCcwHACcelkIZxAPAG4IFkaTyCegHgC4jZjVfACzLBEUvOs7jCx57unZ6JSRZZJ9+v3e+/1++gQHviHHrj+eAT41x4s9YA99F5qK3nb+h4c7QTa6xtYmAuwNfLeg3Fc5QxmBGy1doO3O1dV9tVZmwmwdeYdg/DeTDB/vnY7B2Z7q+/KBhh6XF3UcgeDr3y3s2O6v+q+FIB9NutaRJdVBanrQ+bdYNCZ1pGhuzcNMPJ6FuNCV0DWuhA4ClzntI4M3b1rARBhRERjXSWK1oUhfy3y5loAmlBclcHAue86+1lyHwTAfXb4cD1wUmm9EgAzfhLoPLT4fGUNRpeAPgH6Bc/ARcw89Qed3eRWLQAGbhg4LrqY9sjrUYgxEZ7FDmH6GAPWUZ7JJvBh0ijXrpPStxRAKs/c00mLAiKewcyLmn0yt63Nha/yGgGEoP3Afb4KGSEYT+5eWxbZQniUJUT9INAFEeT3RXGr4wSxpj30JqoXKgMw8M13nV50oH02OyDQSUxJWX55zKB+8vu8uP1rAKr17dF8m8LFZUr5Em1Cau2ozZ09/NEn8Dum1puypq+2B1SXbQ1nY4Be61rvTxjxOPy1cRS12e2z2SURdcHITIuq/FoA4vL6rrOK56QwBn/n241eXv/fHs6mBHpRFViEHVsb+8I79QAYgT9w2pECSWFlFlxZuiqBSAz3YVcLQJxbFEJyeGEcwMJyAmO+UVPtltKSJzOZyiTuhAX+woyv/sDpNxZCMkUqKVReYl5MCXiaZdSQ+U0w6MgGLt2S59eD9tC7IKCn7s/zukkanfiusxdLo0SnaYi4gnkTXXLYiQqfLJa3re3kfaodQkkvSOuKQra5OLB4WbBCC5PgrTNZQZ7MbXqymKdqAnAjwZXsU5aRGgGQsQ7e02olpPJ3FzJNJj4ixonwSkIz71qgvpi7RYPoD5ztrLBsBCASHDIfBoPOx7yksmwl8ClLeXnHgW/ENAXxO2GUyENF42ejAEsl+Eq204DSTnOXiHqipS7LmOFtq02bi/OovykbPRsHKFNQ4/dTkXoj68ui9WtjL78Y1mzmNBQyWiKqOETPT3hWBPE/egBRwRLZjDbvJqLdyIP4LwFCwl6UdssgzAAaeNgqjKtEFxqDAGKF0whAHK72MUZBXrApOSRFxXGZnfhKfVc1B6j1uFuGrD8nGwNIL6wBIq/nyUOuBbDqbUbeaiYus230uxWil/U8X9RWZ7cSszmBVm2GVjeqq2TZuqwRtAqAeECwiD6p5/xVgGhQSTd1PAVRyd9QvK1a/r6Xio24kdzS/8jKLF30e3voBXkDUFW5WRObkLFWAOOXjASdvPzU6mY9w6wVYFmk/nSfla0O3IAxYat1mPeGtFaAqgqbrH8EMLFak3sePdCkNU1k/QadtchPhjx3/AAAAABJRU5ErkJggg== 7 | // @author Johnny Li 8 | // @license MIT 9 | // @match *://*/* 10 | // @grant GM_info 11 | // @grant GM_xmlhttpRequest 12 | // @grant GM_addStyle 13 | // @grant GM_registerMenuCommand 14 | // @grant GM_setValue 15 | // @grant GM_getValue 16 | // @connect cdn.jsdelivr.net 17 | // @connect cdn.bootcss.com 18 | // @connect translate.google.cn 19 | // @connect fanyi.youdao.com 20 | // @connect fanyi.baidu.com 21 | // @require https://cdn.bootcss.com/jquery/1.11.1/jquery.min.js 22 | // @require https://cdn.jsdelivr.net/npm/jquery.md5@1.0.2/index.min.js 23 | // @require https://cdn.jsdelivr.net/gh/zyufstudio/jQuery@master/jPopBox/dist/jPopBox.min.js 24 | // ==/UserScript== 25 | 26 | 27 | //文件使用Rollup+Gulp编译而成,如需查看源码请转到GitHub项目。 28 | 29 | (function () { 30 | 'use strict'; 31 | 32 | /** 33 | * 字符串模板格式化 34 | * @param {string} formatStr - 字符串模板 35 | * @returns {string} 格式化后的字符串 36 | * @example 37 | * StringFormat("ab{0}c{1}ed",1,"q") output "ab1cqed" 38 | */ 39 | function StringFormat (formatStr) { 40 | var args = arguments; 41 | return formatStr.replace(/\{(\d+)\}/g, function (m, i) { 42 | i = parseInt(i); 43 | return args[i + 1]; 44 | }); 45 | } 46 | /** 47 | * 日期格式化 48 | * @param {Date} date - 日期 49 | * @param {string} formatStr - 格式化模板 50 | * @returns {string} 格式化日期后的字符串 51 | * @example 52 | * DateFormat(new Date(),"yyyy-MM-dd") output "2020-03-23" 53 | * @example 54 | * DateFormat(new Date(),"yyyy/MM/dd hh:mm:ss") output "2020/03/23 10:30:05" 55 | */ 56 | function DateFormat (date, formatStr) { 57 | var o = { 58 | "M+": date.getMonth() + 1, //月份 59 | "d+": date.getDate(), //日 60 | "h+": date.getHours(), //小时 61 | "m+": date.getMinutes(), //分 62 | "s+": date.getSeconds(), //秒 63 | "q+": Math.floor((date.getMonth() + 3) / 3), //季度 64 | "S": date.getMilliseconds() //毫秒 65 | }; 66 | if (/(y+)/.test(formatStr)) { 67 | formatStr = formatStr.replace(RegExp.$1, (date.getFullYear() + "").substr(4 - RegExp.$1.length)); 68 | } 69 | for (var k in o) { 70 | if (new RegExp("(" + k + ")").test(formatStr)) { 71 | formatStr = formatStr.replace( 72 | RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 73 | } 74 | } 75 | return formatStr; 76 | } 77 | /** 78 | * 生成Guid 79 | * @param {boolean} hasLine - guid字符串是否包含短横线 80 | * @returns {string} guid 81 | * @example 82 | * Guid(false) output "b72f78a6cb88362c0784cb82afae450b" 83 | * @example 84 | * Guid(true) output "67b25d43-4cfa-3edb-40d7-89961ce7f388" 85 | */ 86 | function Guid (hasLine) { 87 | var guid = ""; 88 | function S4 () { 89 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 90 | } 91 | if (hasLine) { 92 | guid = (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); 93 | } 94 | else { 95 | guid = (S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4()); 96 | } 97 | return guid; 98 | } 99 | /** 100 | * 清除dom元素默认事件 101 | * @param {object} e - dom元素 102 | */ 103 | function ClearBubble (e) { 104 | if (e.stopPropagation) { 105 | e.stopPropagation(); 106 | } else { 107 | e.cancelBubble = true; 108 | } 109 | if (e.preventDefault) { 110 | e.preventDefault(); 111 | } else { 112 | e.returnValue = false; 113 | } 114 | } 115 | function ObjectToQueryString (object) { 116 | var querystring = Object.keys(object).map(function (key) { 117 | return encodeURIComponent(key) + '=' + encodeURIComponent(object[key]) 118 | }).join('&'); 119 | return querystring; 120 | } 121 | 122 | /** 123 | * 配置参数 124 | */ 125 | var options = { 126 | //默认翻译引擎 127 | defaulttransengine: "yd" 128 | }; 129 | /** 130 | * 获取配置参数 131 | */ 132 | function GetSettingOptions () { 133 | var optionsJson = GM_getValue("webtranslate-options") || ""; 134 | if (optionsJson != "") { 135 | var optionsData = JSON.parse(optionsJson); 136 | for (var key in options) { 137 | if (options.hasOwnProperty(key) && optionsData.hasOwnProperty(key)) { 138 | options[key] = optionsData[key]; 139 | } 140 | } 141 | } 142 | return options; 143 | } 144 | /** 145 | * 设置配置参数 146 | */ 147 | function SetSettingOptions () { 148 | var optionsJson = JSON.stringify(options); 149 | GM_setValue("webtranslate-options", optionsJson); 150 | } 151 | 152 | //谷歌翻译 153 | var googleTrans = { 154 | code: "ge", 155 | codeText: "谷歌", 156 | defaultOrigLang: "auto", //默认源语言 157 | defaultTargetLang: "zh-CN", //默认目标语言 158 | langList: { "auto": "自动检测", "zh-CN": "中文简体", "zh-TW": "中文繁体", "en": "英文", "ja": "日文", "ko": "韩文", "fr": "法文", "es": "西班牙文", "pt": "葡萄牙文", "it": "意大利文", "ru": "俄文", "vi": "越南文", "de": "德文", "ar": "阿拉伯文", "id": "印尼文" }, 159 | Execute: function (h_onloadfn) { 160 | var h_url = ""; 161 | var googleTransApi = StringFormat("https://translate.google.cn/translate_a/single?client=gtx&dt=t&dj=1&sl={1}&tl={0}&hl=zh-CN", Trans.transTargetLang, Trans.transOrigLang); 162 | h_url = googleTransApi + "&q=" + encodeURIComponent(Trans.transText); 163 | 164 | GM_xmlhttpRequest({ 165 | method: "GET", 166 | url: h_url, 167 | onload: function (r) { 168 | setTimeout(function () { 169 | var data = JSON.parse(r.responseText); 170 | var trans = [], origs = [], src = ""; 171 | for (var i = 0; i < data.sentences.length; i++) { 172 | var getransCont = data.sentences[i]; 173 | trans.push(getransCont.trans); 174 | origs.push(getransCont.orig); 175 | } 176 | src = data.src; 177 | Trans.transResult.trans = trans; 178 | Trans.transResult.orig = origs; 179 | Trans.transResult.origLang = src; 180 | h_onloadfn(); 181 | }, 300); 182 | }, 183 | onerror: function (e) { 184 | console.error(e); 185 | } 186 | }); 187 | }, 188 | }; 189 | 190 | //有道翻译 191 | var youdaoTrans = { 192 | code: "yd", 193 | codeText: "有道", 194 | defaultOrigLang: "AUTO", //默认源语言 195 | defaultTargetLang: "ZH-CHS", //默认目标语言 196 | langList: { "AUTO": "自动检测", "zh-CHS": "中文", "en": "英文", "ja": "日文", "ko": "韩文", "fr": "法文", "es": "西班牙文", "pt": "葡萄牙文", "it": "意大利文", "ru": "俄文", "vi": "越南文", "de": "德文", "ar": "阿拉伯文", "id": "印尼文" }, 197 | Execute: function (h_onloadfn) { 198 | var h_url = "", 199 | h_headers = {}, 200 | h_data = ""; 201 | 202 | var youdaoTransApi = "http://fanyi.youdao.com/translate_o?client=fanyideskweb&keyfrom=fanyi.web&version=2.1&doctype=json"; 203 | var tempsalt = "" + (new Date).getTime() + parseInt(10 * Math.random(), 10); 204 | var tempsign = $.md5("fanyideskweb" + Trans.transText + tempsalt + "Nw(nmmbP%A-r6U3EUn]Aj"); 205 | h_url = youdaoTransApi; 206 | h_headers = { 207 | "Content-Type": "application/x-www-form-urlencoded", 208 | "Referer": "http://fanyi.youdao.com/" 209 | }; 210 | h_data = StringFormat("from={0}&to={1}&salt={2}&sign={3}&i={4}", Trans.transOrigLang, Trans.transTargetLang, tempsalt, tempsign, Trans.transText); 211 | 212 | GM_xmlhttpRequest({ 213 | method: "POST", 214 | url: h_url, 215 | headers: h_headers, 216 | data: h_data, 217 | onload: function (r) { 218 | setTimeout(function () { 219 | var data = JSON.parse(r.responseText); 220 | var trans = [], 221 | origs = [], 222 | src = ""; 223 | if (data.errorCode == 0) { 224 | for (var j = 0; j < data.translateResult.length; j++) { 225 | var ydTransCont = data.translateResult[j]; 226 | var ydtgt = ""; 227 | var ydsrc = ""; 228 | for (var k = 0; k < ydTransCont.length; k++) { 229 | var ydcont = ydTransCont[k]; 230 | ydtgt += ydcont.tgt; 231 | ydsrc += ydcont.src; 232 | } 233 | trans.push(ydtgt); 234 | origs.push(ydsrc); 235 | } 236 | src = data.type; 237 | Trans.transResult.trans = trans; 238 | Trans.transResult.orig = origs; 239 | Trans.transResult.origLang = src.split("2")[0]; 240 | 241 | } 242 | h_onloadfn(); 243 | }, 300); 244 | }, 245 | onerror: function (e) { 246 | console.error(e); 247 | } 248 | }); 249 | }, 250 | }; 251 | 252 | function a (r) { 253 | if (Array.isArray(r)) { 254 | for (var o = 0, t = Array(r.length); o < r.length; o++) 255 | t[o] = r[o]; 256 | return t 257 | } 258 | return Array.from(r) 259 | } 260 | 261 | function n (r, o) { 262 | for (var t = 0; t < o.length - 2; t += 3) { 263 | var a = o.charAt(t + 2); 264 | a = a >= "a" ? a.charCodeAt(0) - 87 : Number(a), 265 | a = "+" === o.charAt(t + 1) ? r >>> a : r << a, 266 | r = "+" === o.charAt(t) ? r + a & 4294967295 : r ^ a; 267 | } 268 | return r 269 | } 270 | 271 | function e (r, gtk) { 272 | var i = null; 273 | var o = r.match(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g); 274 | if (null === o) { 275 | var t = r.length; 276 | t > 30 && (r = "" + r.substr(0, 10) + r.substr(Math.floor(t / 2) - 5, 10) + r.substr(-10, 10)); 277 | } else { 278 | for (var e = r.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/), C = 0, h = e.length, f = []; h > C; C++) 279 | "" !== e[C] && f.push.apply(f, a(e[C].split(""))), 280 | C !== h - 1 && f.push(o[C]); 281 | var g = f.length; 282 | g > 30 && (r = f.slice(0, 10).join("") + f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) + 5).join("") + f.slice(-10).join("")); 283 | } 284 | var u = void 0 285 | ; 286 | u = null !== i ? i : (i = gtk || "") || ""; 287 | for (var d = u.split("."), m = Number(d[0]) || 0, s = Number(d[1]) || 0, S = [], c = 0, v = 0; v < r.length; v++) { 288 | var A = r.charCodeAt(v); 289 | 128 > A ? S[c++] = A : (2048 > A ? S[c++] = A >> 6 | 192 : (55296 === (64512 & A) && v + 1 < r.length && 56320 === (64512 & r.charCodeAt(v + 1)) ? (A = 65536 + ((1023 & A) << 10) + (1023 & r.charCodeAt(++v)), 290 | S[c++] = A >> 18 | 240, 291 | S[c++] = A >> 12 & 63 | 128) : S[c++] = A >> 12 | 224, 292 | S[c++] = A >> 6 & 63 | 128), 293 | S[c++] = 63 & A | 128); 294 | } 295 | for (var p = m, F = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(97) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(54)), D = "" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(51) + ("" + String.fromCharCode(94) + String.fromCharCode(43) + String.fromCharCode(98)) + ("" + String.fromCharCode(43) + String.fromCharCode(45) + String.fromCharCode(102)), b = 0; b < S.length; b++) 296 | p += S[b], 297 | p = n(p, F); 298 | return p = n(p, D), 299 | p ^= s, 300 | 0 > p && (p = (2147483647 & p) + 2147483648), 301 | p %= 1e6, 302 | p.toString() + "." + (p ^ m) 303 | } 304 | 305 | /** 306 | * @param {string} word 307 | * @param {string} gtk 308 | * @return {string} 309 | */ 310 | var calcSign = function (word, gtk) { 311 | return e(word, gtk); 312 | }; 313 | 314 | //获取gtk和token 315 | function GetToken () { 316 | GM_xmlhttpRequest({ 317 | method: "GET", 318 | url: "https://fanyi.baidu.com/", 319 | timeout: 5000, 320 | onload: function (r) { 321 | var gtkMatch = /window\.gtk = '(.*?)'/.exec(r.responseText); 322 | var commonTokenMatch = /token: '(.*?)',/.exec(r.responseText); 323 | if (!gtkMatch) { 324 | console.log("获取gtk失败!!!"); 325 | } 326 | if (!commonTokenMatch) { 327 | console.log("获取token失败!!!"); 328 | } 329 | var newGtk = gtkMatch[1]; 330 | var newCommonToken = commonTokenMatch[1]; 331 | 332 | if (typeof newGtk !== 'undefined') { 333 | baiduTrans.gtk = newGtk; 334 | } 335 | if (typeof newCommonToken !== 'undefined') { 336 | baiduTrans.token = newCommonToken; 337 | } 338 | }, 339 | onerror: function (e) { 340 | console.error(e); 341 | } 342 | }); 343 | } 344 | 345 | //百度翻译 346 | var baiduTrans = { 347 | code: "bd", 348 | codeText: "百度", 349 | gtk: "", 350 | token: "", 351 | defaultOrigLang: "auto", //默认源语言 352 | defaultTargetLang: "zh", //默认目标语言 353 | langList: { "auto": "自动检测", "zh": "中文", "cht": "繁体中文", "en": "英语", "jp": "日语", "kor": "韩语", "fra": "法语", "spa": "西班牙语", "pt": "葡萄牙语", "it": "意大利语", "ru": "俄语", "vie": "越南语", "de": "德语", "ara": "阿拉伯语" }, 354 | Execute: function (h_onloadfn) { 355 | if (Trans.transOrigLang == "auto") 356 | this.AutoTrans(h_onloadfn); 357 | else 358 | this.ExecTrans(h_onloadfn); 359 | 360 | }, 361 | AutoTrans: function (h_onloadfn) { 362 | var self = this; 363 | var datas = { 364 | query: Trans.transText 365 | }; 366 | GM_xmlhttpRequest({ 367 | method: "POST", 368 | headers: { 369 | "referer": 'https://fanyi.baidu.com', 370 | "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8', 371 | }, 372 | url: "https://fanyi.baidu.com/langdetect", 373 | data: ObjectToQueryString(datas), 374 | onload: function (r) { 375 | var data = JSON.parse(r.responseText); 376 | if (data.error === 0) { 377 | Trans.transOrigLang = data.lan; 378 | self.ExecTrans(h_onloadfn); 379 | } 380 | }, 381 | onerror: function (e) { 382 | console.error(e); 383 | } 384 | }); 385 | }, 386 | ExecTrans: function (h_onloadfn) { 387 | var tempSign = calcSign(Trans.transText, this.gtk); 388 | var datas = { 389 | from: Trans.transOrigLang, 390 | to: Trans.transTargetLang, 391 | query: Trans.transText, 392 | transtype: "translang", 393 | simple_means_flag: 3, 394 | sign: tempSign, 395 | token: this.token 396 | }; 397 | GM_xmlhttpRequest({ 398 | method: "POST", 399 | headers: { 400 | "referer": 'https://fanyi.baidu.com', 401 | "Content-Type": 'application/x-www-form-urlencoded; charset=UTF-8', 402 | //"User-Agent": window.navigator.userAgent, 403 | }, 404 | url: "https://fanyi.baidu.com/v2transapi", 405 | data: ObjectToQueryString(datas), 406 | onload: function (r) { 407 | setTimeout(function () { 408 | var result = JSON.parse(r.responseText); 409 | var trans_result = result.trans_result; 410 | var transDatas = trans_result.data; 411 | 412 | var trans = [], origs = [], src = ""; 413 | for (var i = 0; i < transDatas.length; i++) { 414 | var getransCont = transDatas[i]; 415 | trans.push(getransCont.dst); 416 | origs.push(getransCont.src); 417 | } 418 | src = trans_result.from; 419 | Trans.transResult.trans = trans; 420 | Trans.transResult.orig = origs; 421 | Trans.transResult.origLang = src; 422 | h_onloadfn(); 423 | }, 300); 424 | }, 425 | onerror: function (e) { 426 | console.error(e); 427 | } 428 | }); 429 | }, 430 | init: function () { 431 | GetToken(); 432 | } 433 | }; 434 | 435 | var Trans = { 436 | transEngineList: {}, //翻译引擎实例列表 437 | transEngine: "", //当前翻译引擎。ge(谷歌)/yd(有道) 438 | transEngineObj: {}, //当前翻译引擎实例 439 | transTargetLang: "", //目标语言。 440 | transOrigLang: "", //源语言 441 | transType: "word", //翻译类型。word(划词翻译)/text(输入文本翻译)/page(整页翻译) 442 | transText: "", //被翻译内容 443 | transResult: { //当前翻译内容 444 | //译文 445 | trans: [], 446 | //原文 447 | orig: [], 448 | //原文语言 449 | origLang: "" 450 | }, 451 | Execute: function (h_onloadfn) { 452 | this.transResult.trans = []; 453 | this.transResult.orig = []; 454 | this.transResult.origLang = ""; 455 | this.transEngineObj.Execute(h_onloadfn); 456 | }, 457 | GetLangList: function () { 458 | var langList = {}; 459 | langList = this.transEngineObj.langList; 460 | return langList; 461 | }, 462 | Update: function () { 463 | this.transResult.trans = []; 464 | this.transResult.orig = []; 465 | this.transResult.origLang = ""; 466 | this.transEngineObj = this.transEngineList[this.transEngine]; 467 | this.transTargetLang = this.transEngineObj.defaultTargetLang; 468 | this.transOrigLang = this.transEngineObj.defaultOrigLang; 469 | }, 470 | Clear: function () { 471 | this.transEngine = ""; //当前翻译引擎。ge(谷歌)/yd(有道) 472 | this.transTargetLang = ""; //目标语言。 473 | this.transOrigLang = ""; //源语言 474 | this.transText = ""; //被翻译内容 475 | this.transResult.trans = []; 476 | this.transResult.orig = []; 477 | this.transResult.origLang = ""; 478 | }, 479 | //注册翻译引擎接口并执行翻译引擎的初始化接口 480 | RegisterEngine: function () { 481 | /** 482 | * 翻译引擎必须提供以下接口 483 | code:"", //代号 484 | codeText:"", //代号描述 485 | defaultOrigLang:"", //默认源语言 486 | defaultTargetLang:"", //默认目标语言 487 | langList: {}, //支持翻译语言列表 488 | Execute: function (h_onloadfn) {}, //执行翻译 489 | init:function(){}, //可选,初始化接口,在脚本创建时立即执行 490 | */ 491 | var transEngineListObj = {}; 492 | transEngineListObj[googleTrans.code] = googleTrans; 493 | transEngineListObj[youdaoTrans.code] = youdaoTrans; 494 | transEngineListObj[baiduTrans.code] = baiduTrans; 495 | this.transEngineList = transEngineListObj; 496 | for (var key in this.transEngineList) { 497 | if (this.transEngineList.hasOwnProperty(key) && this.transEngineList[key].hasOwnProperty("init")) { 498 | this.transEngineList[key].init(); 499 | } 500 | } 501 | } 502 | }; 503 | 504 | //面板 505 | var Panel = { 506 | popBoxEl: {}, 507 | randomCode: "", 508 | Create: function (title, placement, isShowArrow, content, shownFn) { 509 | var self = this; 510 | $(self.popBoxEl).jPopBox({ 511 | title: title, 512 | className: 'JPopBox-tip-white', 513 | placement: placement, 514 | trigger: 'none', 515 | isTipHover: true, 516 | isShowArrow: isShowArrow, 517 | content: function () { 518 | return StringFormat('
{1}
', self.randomCode, content); 519 | } 520 | }); 521 | $(self.popBoxEl).on("shown.jPopBox", function () { 522 | var $panel = $("div.JPopBox-tip-white"); 523 | typeof shownFn === 'function' && shownFn($panel); 524 | }); 525 | $(self.popBoxEl).jPopBox('show'); 526 | }, 527 | Update: function (Fn) { 528 | var $panel = $("div.JPopBox-tip-white"); 529 | Fn($panel); 530 | }, 531 | Destroy: function () { 532 | $(this.popBoxEl).jPopBox("hideDelayed"); 533 | }, 534 | CreateStyle: function () { 535 | var s = ""; 536 | s += StringFormat("#panelBody{0}>div input,#panelBody{0}>div select{padding: 3px; margin: 0; background: #fff; font-size: 14px; border: 1px solid #a9a9a9; color:black;width: auto;min-height: auto; }", this.randomCode); 537 | s += StringFormat("#panelBody{0}>div:first-child{padding-bottom: 5px;height:30px}", this.randomCode); 538 | s += StringFormat("#panelBody{0}>div:last-child hr{border: 1px inset #eeeeee;background: none;height: 0px;margin: 0px;}", this.randomCode); 539 | return s; 540 | } 541 | }; 542 | 543 | //文本翻译面板 544 | var TextTransPanel = { 545 | Create: function (popBoxEl, randomCode) { 546 | var self = this; 547 | var html = this.GetHtml(); 548 | var transEngineOptionsHtml = ""; 549 | //翻译引擎 550 | for (var k in Trans.transEngineList) { 551 | if (Trans.transEngineList.hasOwnProperty(k)) { 552 | var v = Trans.transEngineList[k].codeText; 553 | var selectOption = ""; 554 | if (Trans.transEngine == k) { 555 | 556 | selectOption = 'selected="selected"'; 557 | } 558 | transEngineOptionsHtml += StringFormat('', k, v, selectOption); 559 | } 560 | } 561 | var TextTransPanelHtml = StringFormat('
' + 562 | '翻译引擎:    ' + 563 | '翻译语言: ⇨ ' + 564 | ' ' + 565 | '
' + 566 | '
' + 567 | '

' + 568 | '
{6}
' + 569 | '
', randomCode, "", transEngineOptionsHtml, html.targetLangListHtml, html.origLangListHtml, "", ""); 570 | Panel.popBoxEl = popBoxEl; 571 | Panel.randomCode = randomCode; 572 | Panel.Create("文本翻译", "auto bottom", false, TextTransPanelHtml, function ($panel) { 573 | $panel.css({ 574 | position: "fixed", 575 | top: "20px" 576 | }); 577 | //翻译引擎 578 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(0)", randomCode)).change(function (e) { 579 | Trans.transEngine = $(this).find("option:selected").val(); 580 | Trans.Update(); 581 | Panel.Update(function ($panel) { 582 | var html = self.GetHtml(); 583 | //翻译内容 584 | $panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(1)", randomCode)).html(""); 585 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).html(html.origLangListHtml); 586 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)", randomCode)).html(html.targetLangListHtml); 587 | }); 588 | }); 589 | //源语言 590 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).change(function (e) { 591 | Trans.transOrigLang = $(this).find("option:selected").val(); 592 | Panel.Update(function ($panel) { 593 | var html = self.GetHtml(); 594 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)", randomCode)).html(html.targetLangListHtml); 595 | }); 596 | }); 597 | //目标语言 598 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(2)", randomCode)).change(function (e) { 599 | Trans.transTargetLang = $(this).find("option:selected").val(); 600 | }); 601 | //翻译 602 | $panel.find(StringFormat("#panelBody{0} div:eq(0) button:eq(0)", randomCode)).click(function (e) { 603 | var refTransText = $.trim($panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(0) textarea:eq(0)", randomCode)).val()); 604 | if (refTransText == "") { 605 | alert("请输入翻译文字!"); 606 | return; 607 | } 608 | Trans.transText = refTransText; 609 | Trans.Execute(function () { 610 | Panel.Update(function ($panel) { 611 | var html = self.GetHtml(); 612 | //源语言 613 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).html(html.origLangListHtml); 614 | //翻译内容 615 | $panel.find(StringFormat("#panelBody{0} div:eq(1) div:eq(1)", randomCode)).html(html.transHtml); 616 | }); 617 | }); 618 | }); 619 | }); 620 | }, 621 | GetHtml: function () { 622 | var origLangListHtml = []; 623 | var targetLangListHtml = []; 624 | var returnHtml = {}; 625 | var transHtml = []; 626 | 627 | var langList = Trans.GetLangList(); 628 | var origLang = Trans.transResult.origLang; 629 | 630 | if (Trans.transResult.trans.length > 0 && Trans.transResult.orig.length > 0) { 631 | transHtml.push(''); 632 | for (var i = 0; i < Trans.transResult.trans.length; i++) { 633 | var transtxt = Trans.transResult.trans[i]; 634 | transHtml.push(transtxt); 635 | } 636 | transHtml.push(""); 637 | Trans.transOrigLang = origLang; 638 | } 639 | else { 640 | var txt = "该翻译引擎不支持 " + langList[Trans.transOrigLang] + " 翻译成 " + langList[Trans.transTargetLang]; 641 | transHtml.push(StringFormat("{0}", txt)); 642 | } 643 | //源语言 644 | for (var origKey in langList) { 645 | if (langList.hasOwnProperty(origKey)) { 646 | var origVal = langList[origKey]; 647 | var origSelectOption = ""; 648 | if (Trans.transOrigLang.toUpperCase() == origKey.toUpperCase()) { 649 | origSelectOption = 'selected="selected"'; 650 | } 651 | origLangListHtml.push(StringFormat('', origKey, origVal, origSelectOption)); 652 | } 653 | } 654 | //目标语言 655 | for (var targetKey in langList) { 656 | if (langList.hasOwnProperty(targetKey) && targetKey != Trans.transOrigLang && targetKey.toUpperCase() != "AUTO") { 657 | var targetVal = langList[targetKey]; 658 | var targetSelectOption = ""; 659 | targetLangListHtml.push(StringFormat('', targetKey, targetVal, targetSelectOption)); 660 | } 661 | } 662 | returnHtml.origLangListHtml = origLangListHtml.join(""); 663 | returnHtml.targetLangListHtml = targetLangListHtml.join(""); 664 | returnHtml.transHtml = transHtml.join(""); 665 | return returnHtml; 666 | } 667 | }; 668 | 669 | //划词翻译面板 670 | var WordTransPanel = { 671 | Create: function (popBoxEl, randomCode) { 672 | var self = this; 673 | var html = this.GetTransContHtml(); 674 | var transEngineOptionsHtml = ""; 675 | for (var k in Trans.transEngineList) { 676 | if (Trans.transEngineList.hasOwnProperty(k)) { 677 | var v = Trans.transEngineList[k].codeText; 678 | var selectOption = ""; 679 | if (Trans.transEngine == k) { 680 | selectOption = 'selected="selected"'; 681 | } 682 | transEngineOptionsHtml += StringFormat('', k, v, selectOption); 683 | } 684 | } 685 | var wordTransPanelHtml = StringFormat( 686 | '
翻译引擎: 翻译语言:
' + 687 | '
{1}
' 688 | , randomCode, html.transHtml, transEngineOptionsHtml, html.langListHtml, html.origLangName); 689 | 690 | Panel.popBoxEl = popBoxEl; 691 | Panel.randomCode = randomCode; 692 | Panel.Create("", "auto bottom", false, wordTransPanelHtml, function ($panel) { 693 | //目标语言 694 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).change(function (e) { 695 | Trans.transTargetLang = $(this).find("option:selected").val(); 696 | Trans.Execute(function () { 697 | self.Update(randomCode); 698 | }); 699 | }); 700 | //翻译引擎 701 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(0)", randomCode)).change(function (e) { 702 | Trans.transEngine = $(this).find("option:selected").val(); 703 | Trans.Update(); 704 | Trans.Execute(function () { 705 | self.Update(randomCode); 706 | }); 707 | }); 708 | }); 709 | }, 710 | Update: function (randomCode) { 711 | var self = this; 712 | Panel.Update(function ($panel) { 713 | var html = self.GetTransContHtml(); 714 | $panel.find(StringFormat("#panelBody{0} div:eq(0) input:eq(0)", randomCode)).val("").val(html.origLangName); 715 | $panel.find(StringFormat("#panelBody{0} div:eq(0) select:eq(1)", randomCode)).html("").html(html.langListHtml); 716 | $panel.find(StringFormat("#panelBody{0} div:eq(1)", randomCode)).html("").html(html.transHtml); 717 | }); 718 | }, 719 | GetTransContHtml: function () { 720 | var transObj = {}; 721 | var langListHtml = []; 722 | var langList = Trans.GetLangList(); 723 | var origLang = Trans.transResult.origLang; 724 | var transContHtml = ""; 725 | 726 | if (Trans.transResult.trans.length > 0 && Trans.transResult.orig.length > 0) { 727 | var transHtml = []; 728 | transHtml.push('
    '); 729 | for (var i = 0; i < Trans.transResult.trans.length; i++) { 730 | var transtxt = Trans.transResult.trans[i]; 731 | transHtml.push(StringFormat('
  • {0}
  • ', transtxt)); 732 | } 733 | transHtml.push("
"); 734 | 735 | var origHtml = []; 736 | origHtml.push('
    '); 737 | for (var j = 0; j < Trans.transResult.orig.length; j++) { 738 | var origtxt = Trans.transResult.orig[j]; 739 | origHtml.push(StringFormat('
  • {0}
  • ', origtxt)); 740 | } 741 | origHtml.push("
"); 742 | transContHtml = origHtml.join("") + "
" + transHtml.join(""); 743 | Trans.transOrigLang = origLang; 744 | } 745 | else { 746 | var txt = "该翻译引擎不支持 " + langList[Trans.transOrigLang] + " 翻译成 " + langList[Trans.transTargetLang]; 747 | transContHtml = StringFormat("
{0}
", txt); 748 | } 749 | for (var k in langList) { 750 | if (langList.hasOwnProperty(k) && k != Trans.transOrigLang && k.toUpperCase() != "AUTO") { 751 | var v = langList[k]; 752 | var selectOption = ""; 753 | if (Trans.transTargetLang == k) { 754 | selectOption = 'selected="selected"'; 755 | } 756 | langListHtml.push(StringFormat('', k, v, selectOption)); 757 | } 758 | } 759 | transObj.origLangName = langList[Trans.transOrigLang]; 760 | transObj.transHtml = transContHtml; 761 | transObj.langListHtml = langListHtml.join(""); 762 | return transObj; 763 | } 764 | }; 765 | 766 | //设置面板 767 | var SettingPanel = { 768 | config: [{ title: "", item: [{ code: "", text: "" }] }], 769 | Create: function (popBoxEl, randomCode) { 770 | var self = this; 771 | var settingHtml = []; 772 | this.InitConfig(); 773 | settingHtml.push('
'); 774 | /* 775 | settingHtml.push('
'); 776 | settingHtml.push('
默认翻译引擎:
'); 777 | settingHtml.push(StringFormat('
',randomCode)); 778 | settingHtml.push(StringFormat('
',randomCode)); 779 | settingHtml.push('
'); 780 | */ 781 | for (var index = 0; index < this.config.length; index++) { 782 | var configItem = this.config[index]; 783 | settingHtml.push('
'); 784 | settingHtml.push(StringFormat('
{0}
', configItem.title)); 785 | for (var itemIndex = 0; itemIndex < configItem.item.length; itemIndex++) { 786 | var itemObj = configItem.item[itemIndex]; 787 | settingHtml.push(StringFormat('
', randomCode, itemObj.code, itemObj.text)); 788 | } 789 | settingHtml.push('
'); 790 | } 791 | 792 | settingHtml.push('
'); 793 | settingHtml.push(StringFormat('', randomCode)); 794 | settingHtml.push(StringFormat('', randomCode)); 795 | settingHtml.push('
'); 796 | settingHtml.push('
'); 797 | 798 | var settingHtmlStr = settingHtml.join(""); 799 | Panel.popBoxEl = popBoxEl; 800 | Panel.randomCode = randomCode; 801 | Panel.Create("网页翻译助手设置", "auto bottom", false, settingHtmlStr, function ($panel) { 802 | $panel.css({ 803 | position: "fixed", 804 | top: "20px" 805 | }); 806 | self.Update(randomCode); 807 | //保存设置 808 | $panel.find(StringFormat("#panelBody{0} #saveBtn{0}", randomCode)).click(function (e) { 809 | var defaultTransEngine = $panel.find(StringFormat("#panelBody{0} input[name='transEngine{0}']:checked", randomCode)).val(); 810 | options.defaulttransengine = defaultTransEngine; 811 | SetSettingOptions(); 812 | $panel.find(StringFormat("#panelBody{0} #saveStatus{0}", randomCode)).fadeIn(function () { 813 | setTimeout(function () { 814 | $panel.find(StringFormat("#panelBody{0} #saveStatus{0}", randomCode)).fadeOut(); 815 | }, 1500); 816 | }); 817 | }); 818 | }); 819 | }, 820 | Update: function (randomCode) { 821 | GetSettingOptions(); 822 | Panel.Update(function ($panel) { 823 | $panel.find(StringFormat("#panelBody{0} input[name='transEngine{0}'][value='{1}']", randomCode, options.defaulttransengine)).prop("checked", true); 824 | }); 825 | }, 826 | InitConfig: function () { 827 | this.config = []; 828 | var configObj = { title: "", item: [{ code: "", text: "" }] }; 829 | configObj.title = "默认翻译引擎:"; 830 | configObj.item = []; 831 | for (var k in Trans.transEngineList) { 832 | if (Trans.transEngineList.hasOwnProperty(k)) { 833 | var v = Trans.transEngineList[k].codeText; 834 | configObj.item.push({ code: k, text: v }); 835 | } 836 | } 837 | this.config.push(configObj); 838 | } 839 | }; 840 | 841 | //主程序 842 | var WebTranslate = function () { 843 | var transIconBase64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAENklEQVRoQ+2ZTVITQRTH/28qyNIJFxCqnGwNJzCcwHACcelkIZxAPAG4IFkaTyCegHgC4jZjVfACzLBEUvOs7jCx57unZ6JSRZZJ9+v3e+/1++gQHviHHrj+eAT41x4s9YA99F5qK3nb+h4c7QTa6xtYmAuwNfLeg3Fc5QxmBGy1doO3O1dV9tVZmwmwdeYdg/DeTDB/vnY7B2Z7q+/KBhh6XF3UcgeDr3y3s2O6v+q+FIB9NutaRJdVBanrQ+bdYNCZ1pGhuzcNMPJ6FuNCV0DWuhA4ClzntI4M3b1rARBhRERjXSWK1oUhfy3y5loAmlBclcHAue86+1lyHwTAfXb4cD1wUmm9EgAzfhLoPLT4fGUNRpeAPgH6Bc/ARcw89Qed3eRWLQAGbhg4LrqY9sjrUYgxEZ7FDmH6GAPWUZ7JJvBh0ijXrpPStxRAKs/c00mLAiKewcyLmn0yt63Nha/yGgGEoP3Afb4KGSEYT+5eWxbZQniUJUT9INAFEeT3RXGr4wSxpj30JqoXKgMw8M13nV50oH02OyDQSUxJWX55zKB+8vu8uP1rAKr17dF8m8LFZUr5Em1Cau2ozZ09/NEn8Dum1puypq+2B1SXbQ1nY4Be61rvTxjxOPy1cRS12e2z2SURdcHITIuq/FoA4vL6rrOK56QwBn/n241eXv/fHs6mBHpRFViEHVsb+8I79QAYgT9w2pECSWFlFlxZuiqBSAz3YVcLQJxbFEJyeGEcwMJyAmO+UVPtltKSJzOZyiTuhAX+woyv/sDpNxZCMkUqKVReYl5MCXiaZdSQ+U0w6MgGLt2S59eD9tC7IKCn7s/zukkanfiusxdLo0SnaYi4gnkTXXLYiQqfLJa3re3kfaodQkkvSOuKQra5OLB4WbBCC5PgrTNZQZ7MbXqymKdqAnAjwZXsU5aRGgGQsQ7e02olpPJ3FzJNJj4ixonwSkIz71qgvpi7RYPoD5ztrLBsBCASHDIfBoPOx7yksmwl8ClLeXnHgW/ENAXxO2GUyENF42ejAEsl+Eq204DSTnOXiHqipS7LmOFtq02bi/OovykbPRsHKFNQ4/dTkXoj68ui9WtjL78Y1mzmNBQyWiKqOETPT3hWBPE/egBRwRLZjDbvJqLdyIP4LwFCwl6UdssgzAAaeNgqjKtEFxqDAGKF0whAHK72MUZBXrApOSRFxXGZnfhKfVc1B6j1uFuGrD8nGwNIL6wBIq/nyUOuBbDqbUbeaiYus230uxWil/U8X9RWZ7cSszmBVm2GVjeqq2TZuqwRtAqAeECwiD6p5/xVgGhQSTd1PAVRyd9QvK1a/r6Xio24kdzS/8jKLF30e3voBXkDUFW5WRObkLFWAOOXjASdvPzU6mY9w6wVYFmk/nSfla0O3IAxYat1mPeGtFaAqgqbrH8EMLFak3sePdCkNU1k/QadtchPhjx3/AAAAABJRU5ErkJggg=="; 844 | var $doc = $(document); 845 | var $body = $("html body"); 846 | var $head = $("html head"); 847 | var randomCode = "yyMM000000"; //属性随机码,年月加六位随机码。用于元素属性后缀,以防止属性名称重复。 848 | var createHtml = function () { 849 | var wordTransIconHtml = StringFormat('
', randomCode, transIconBase64); 850 | $body.append(StringFormat('
', randomCode) + wordTransIconHtml + '
'); 851 | }; 852 | var createStyle = function () { 853 | //尽可能避开csp认证 854 | GM_xmlhttpRequest({ 855 | method: "get", 856 | url: "https://cdn.jsdelivr.net/gh/zyufstudio/jQuery@master/jPopBox/dist/jPopBox.min.css", 857 | onload: function (r) { 858 | GM_addStyle(r.responseText + ".JPopBox-tip-white{width: 482px;max-width: 550px;min-width: 450px;}"); 859 | } 860 | }); 861 | var s = ""; 862 | s += StringFormat(".wordTrans{0}{background-color: rgb(245, 245, 245);box-sizing: content-box;cursor: pointer;z-index: 2147483647;border-width: 1px;border-style: solid;border-color: rgb(220, 220, 220);border-image: initial;border-radius: 5px;padding: 0.5px;position: absolute;display: none}", randomCode); 863 | s += StringFormat(".wordTransIcon{0}{background-image: url({1});background-size: 25px;height: 25px;width: 25px;}", randomCode, transIconBase64); 864 | s += Panel.CreateStyle(); 865 | GM_addStyle(s); 866 | }; 867 | var ShowWordTransIcon = function () { 868 | var $wordTransIcon = $("div#wordTrans" + randomCode); 869 | var isSelect = false; 870 | var isPanel = false; 871 | var isWordTransIcon = false; 872 | $doc.on({ 873 | "selectionchange": function (e) { 874 | isSelect = true; 875 | }, 876 | "mousedown": function (e) { 877 | var $targetEl = $(e.target); 878 | isPanel = $targetEl.parents().is("div.JPopBox-tip-white"); 879 | isWordTransIcon = $targetEl.parents().is(StringFormat("div#wordTrans{0}", randomCode)); 880 | //点击翻译图标外域和翻译面板外域时,隐藏图标和翻译面板 881 | if (!isWordTransIcon && !isPanel) { 882 | $wordTransIcon.hide(); 883 | Trans.Clear(); 884 | Panel.Destroy(); 885 | } 886 | else { 887 | //点击翻译图标,取消鼠标默认事件,防止选中的文本消失 888 | if (isWordTransIcon) { 889 | ClearBubble(e); 890 | } 891 | } 892 | }, 893 | "mouseup": function (e) { 894 | var selectText = window.getSelection().toString().trim(); 895 | if (!isPanel && isSelect && selectText) { 896 | $wordTransIcon.show().css({ 897 | left: e.pageX + 'px', 898 | top: e.pageY + 12 + 'px' 899 | }); 900 | isSelect = false; 901 | } 902 | } 903 | }); 904 | $wordTransIcon.click(function (e) { 905 | Trans.Clear(); 906 | Panel.Destroy(); 907 | var selecter = window.getSelection(); 908 | var selectText = selecter.toString().trim(); 909 | GetSettingOptions(); 910 | Trans.transText = selectText; 911 | Trans.transType = "word"; 912 | Trans.transEngine = options.defaulttransengine;//defaultTransEngine; 913 | Trans.Update(); 914 | Trans.Execute(function () { 915 | WordTransPanel.Create($wordTransIcon, randomCode); 916 | $wordTransIcon.hide(); 917 | }); 918 | }); 919 | }; 920 | var guid = ""; 921 | var RegMenu = function () { 922 | GM_registerMenuCommand("文本翻译", function () { 923 | var $body = $("html body"); 924 | $("div#wordTrans" + randomCode).hide(); 925 | Trans.Clear(); 926 | Panel.Destroy(); 927 | GetSettingOptions(); 928 | Trans.transEngine = options.defaulttransengine;//defaultTransEngine; 929 | Trans.Update(); 930 | TextTransPanel.Create($body, randomCode); 931 | }); 932 | GM_registerMenuCommand("Google整页翻译", function () { 933 | if (guid == "") 934 | guid = Guid(); 935 | var cbscript = StringFormat('!function(){!function(){function e(){window.setTimeout(function(){window[t].showBanner(!0)},10)}function n(){return new google.translate.TranslateElement({autoDisplay:!1,floatPosition:0,multilanguagePage:!0,includedLanguages:"zh-CN,zh-TW,en",pageLanguage:"auto"})}var t=(document.documentElement.lang,"TE_{0}"),o="TECB_{0}";if(window[t])e();else if(!window.google||!google.translate||!google.translate.TranslateElement){window[o]||(window[o]=function(){window[t]=n(),e()});var a=document.createElement("script");a.src="https://translate.google.cn/translate_a/element.js?cb="+encodeURIComponent(o)+"&client=tee",document.getElementsByTagName("head")[0].appendChild(a)}}()}();', guid); 936 | $head.append(StringFormat('', cbscript)); 937 | }); 938 | GM_registerMenuCommand("设置", function () { 939 | SettingPanel.Create($body, randomCode); 940 | }); 941 | }; 942 | this.init = function () { 943 | randomCode = DateFormat(new Date(), "yyMM").toString() + (Math.floor(Math.random() * (999999 - 100000 + 1)) + 100000).toString(); 944 | Trans.RegisterEngine(); 945 | createStyle(); 946 | createHtml(); 947 | ShowWordTransIcon(); 948 | RegMenu(); 949 | }; 950 | }; 951 | var webTrans = new WebTranslate(); 952 | webTrans.init(); 953 | 954 | }()); 955 | -------------------------------------------------------------------------------- /tampermonkey-scripts/fast-git-code/index.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name FastGitCode 3 | // @namespace https://github.com/holleworldabc/browser-tools 4 | // @version 1.0.4 5 | // @description fast show git ccode! 6 | // @author wm 7 | // @match https://github.com/* 8 | // @grant none 9 | // ==/UserScript== 10 | 11 | (function () { 12 | const version = '1.0'; 13 | const updateDate = '20200311'; 14 | //------------------------------------------- 15 | const fileNav = document.getElementsByClassName('file-navigation')[0]; 16 | const btn = document.createElement("span"); 17 | btn.className = "btn btn-sm btn-blue"; 18 | btn.innerText = "Source Graph"; 19 | btn.style.marginLeft = '8px'; 20 | btn.onclick = function () { 21 | const url = window.location.href; 22 | const sourceGraph = 'https://sourcegraph.com/' 23 | const last = url.split('://')[1]; 24 | window.open(sourceGraph + last); 25 | } 26 | fileNav.appendChild(btn); 27 | //------------------------------------ 28 | console.log("load FastGitCode ok!"); 29 | console.log("version:" + version); 30 | console.log("lastUpdateDate:" + updateDate); 31 | })() 32 | -------------------------------------------------------------------------------- /tampermonkey-scripts/fast-git-code/readme.md: -------------------------------------------------------------------------------- 1 | [**回到首页**](https://github.com/holleworldabc/mini-tools/) 2 | 3 | ### FastGitCode 4 | 5 | > version 1.0.0 6 | 7 | FastGitCode is a TanpermonKey plugin that allows you to quickly view GitHub source code in a vscode-like manner. 8 | 9 | > FastGitCode是一个可以快速的使用类似于vscode方式查看GitHub源码的TanpermonKey插件。 10 | 11 | #### use 12 | 13 | > 使用方法 14 | 15 | ##### Fast step: install plugin 16 | 17 | > 第一步: 安装插件 18 | 19 | use chrome open this url https://greasyfork.org/zh-CN/scripts/397712-fastgitcode. 20 | 21 | > 用chrome浏览器打开这个网页. 22 | 23 | ![click this green buton](https://i.loli.net/2020/03/11/osTA7D4FVGi1e6u.png) 24 | 25 | click this green button. 26 | 27 | > 点击这个绿色的按钮. 28 | 29 | ![click blue btn](https://i.loli.net/2020/03/11/CZSOXns4JdDfci6.png) 30 | 31 | Click the button in the red box. 32 | 33 | > 点击红框内的按钮. 34 | 35 | ok! 36 | 37 | > 安装完成 38 | 39 | ##### The second step: use plugin 40 | 41 | > 第二步:使用插件 42 | 43 | Open a git repository 44 | 45 | ![Click the button in the red box](https://i.loli.net/2020/03/11/QA8rcDT6alEUB7u.png) 46 | 47 | Click blue Button. 48 | 49 | > 点击蓝色的按钮 50 | 51 | ![ok!](https://i.loli.net/2020/03/11/2nB3GlXWVhPEsMg.png) 52 | 53 | ok! 54 | 55 | > 成功! 56 | 57 | 58 | 59 | Wait for more plugins 60 | 61 | ## About 62 | 63 | wm develop 64 | 65 | 2020.3.11 -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/Readme.assets/GIF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-dir-tree/Readme.assets/GIF.gif -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/Readme.assets/image-20200405213921183.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-dir-tree/Readme.assets/image-20200405213921183.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/Readme.assets/image-20200405214022718.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-dir-tree/Readme.assets/image-20200405214022718.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/Readme.assets/微信截图_20200405213621.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-dir-tree/Readme.assets/微信截图_20200405213621.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/Readme.md: -------------------------------------------------------------------------------- 1 | # jianshu-dir-tree 2 | 3 | `jianshu-dir-tree`是一个非常完美的目录生成的小工具,通过`jianshu-dir-tree`我们可以快速的浏览简书文章的标题,并且通过点击来跳转到相应的位置,而且最主要的是该小工具在设计的时候非常嵌合简书网页的UI设计。 4 | 5 | 多说无益,大家快来试试吧。 6 | 7 | 疯狂暗示->⭐ 8 | 9 | ## 特性 10 | 11 | - 目录生成 12 | - 动画 13 | - 契合UI 14 | - 泼油猴插件 15 | 16 | ## 演示 17 | 18 | ![GIF](Readme.assets/GIF.gif) 19 | 20 | ## 安装 21 | 22 | ### 准备 23 | 24 | - 自行安装泼油猴(tampermonkey) 25 | 26 | ### 开始 27 | 28 | 打开 **[泼油猴插件网 greasyfork](https://greasyfork.org/zh-CN/scripts/399616-jianshu-dir-tree)** 安装脚本 29 | 30 | ![微信截图_20200405213621](Readme.assets/%E5%BE%AE%E4%BF%A1%E6%88%AA%E5%9B%BE_20200405213621.png) 31 | 32 | 安装成功 33 | 34 | ![image-20200405213921183](Readme.assets/image-20200405213921183.png) 35 | 36 | 打开简书的一篇文章 37 | 38 | ![image-20200405214022718](Readme.assets/image-20200405214022718.png) 39 | 40 | 成功ok 41 | 42 | -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-dir-tree/index.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name jianshu-dir-tree 3 | // @name:zh 简书目录树 4 | // @name:en jianshu-dir-tree 5 | // @namespace https://github.com/WumaCoder/mini-tools.git 6 | // @homepageURL https://github.com/WumaCoder/mini-tools.git 7 | // @version 1.0.3 8 | // @updateURL https://greasyfork.org/zh-CN/scripts/399616-jianshu-dir-tree 9 | // @description 简书目录的完美解决方案 10 | // @description:en JIANSHU directory 11 | // @author WumaCoder 12 | // @match *://www.jianshu.com/p/* 13 | // @require https://cdnjs.cloudflare.com/ajax/libs/animejs/3.1.0/anime.min.js 14 | // @grant none 15 | // @license GPL-3.0-only 16 | // ==/UserScript== 17 | 18 | (function () { 19 | /** 20 | * 请求拦截器 21 | * @param {Function} callback({status,responseURL}) 回调函数 22 | */ 23 | function responseHander (callback) { 24 | const statusText = { 25 | 'Continue': 100, 26 | 'Switching Protocols': 101, 27 | 'OK': 200, 28 | 'Created': 201, 29 | 'Accepted': 202, 30 | 'Non-Authoritative Information': 203, 31 | 'No Content': 204, 32 | 'Reset Content': 205, 33 | 'Partial Content': 206, 34 | 'Multiple Choices': 300, 35 | 'Moved Permanently': 301, 36 | 'Found': 302, 37 | 'See Other': 303, 38 | 'Not Modified ': 304, 39 | 'Use Proxy': 305, 40 | 'Temporary Redirect': 307, 41 | 'Bad Request': 400, 42 | 'Unauthorized': 401, 43 | 'Payment Required': 402, 44 | 'Forbidden': 403, 45 | 'Not Found': 404, 46 | 'Method Not Allowed': 405, 47 | 'Not Acceptable ': 406, 48 | 'Proxy Authentication Required ': 407, 49 | 'Request Timeout': 408, 50 | 'Conflict': 409, 51 | 'Gone': 410, 52 | 'Length Required': 411, 53 | 'Precondition Failed': 412, 54 | 'Request Entity Too Large': 413, 55 | 'Request-URI Too Long': 414, 56 | 'Unsupported Media Type': 415, 57 | 'Requested Range Not Suitable': 416, 58 | 'Expectation Failed': 417, 59 | 'Internal Server Error': 500, 60 | 'Not Implemented': 501, 61 | 'Bad Gateway': 502, 62 | 'Service Unavailable': 503, 63 | 'Gateway Timeout': 504, 64 | 'HTTP Version Not Supported': 505, 65 | 'Other': 12029, 66 | } 67 | Object.defineProperty(XMLHttpRequest.prototype, "status", { 68 | get: function () { 69 | const status = statusText[this.statusText] || statusText['Other']; 70 | callback({ status, responseURL: this.responseURL }); 71 | return status; 72 | }, 73 | }) 74 | } 75 | /** 76 | * 提取主要信息 77 | * @param {Element} el 页面元素 78 | */ 79 | function parserTag (el) { 80 | return { 81 | hx: el.tagName.substr(1) * 1, 82 | name: el.innerText, 83 | top: el.offsetTop - 80 84 | } 85 | } 86 | /** 87 | * 获取简书文章中的标题 88 | */ 89 | function getHxTags () { 90 | const hxTags = []; 91 | const tags = document.querySelector("section:nth-child(1) > article").children; 92 | for (let i = 0; i < tags.length; i++) { 93 | const item = tags[i]; 94 | if (['H1', 'H2', 'H3', 'H4', 'H5', 'H6'].includes(item.tagName)) { 95 | hxTags.push(item); 96 | } 97 | } 98 | return hxTags; 99 | } 100 | /** 101 | * 初始化自定义样式 102 | */ 103 | function initStyle () { 104 | const style = document.createElement("style"); 105 | style.innerText = ` 106 | .jianshu-a{ 107 | display: block; 108 | font-size: 14px; 109 | line-height: 22px; 110 | color: #2d2d2d; 111 | margin-bottom: 4px; 112 | overflow: hidden; 113 | text-overflow: ellipsis; 114 | display: -webkit-box; 115 | -webkit-line-clamp: 1; 116 | -webkit-box-orient: vertical; 117 | word-break: break-all; 118 | } 119 | .jianshu-active{ 120 | color:#ec7259; 121 | } 122 | .jianshu-a:hover{ 123 | color:inherit; 124 | text-decoration: underline; 125 | } 126 | .jianshu-scroll{ 127 | overflow-y: auto; 128 | height: 85vh; 129 | } 130 | .jianshu-scroll::-webkit-scrollbar,html::-webkit-scrollbar{ 131 | background:#fff; 132 | width:0px; 133 | transintion:all 0.36s; 134 | } 135 | html::-webkit-scrollbar{ 136 | width:6px 137 | } 138 | .jianshu-scroll::-webkit-scrollbar-thumb,html::-webkit-scrollbar-thumb{ 139 | background:#ec7259; 140 | } 141 | #jianshu-dir:hover > .jianshu-scroll::-webkit-scrollbar{ 142 | width:6px; 143 | } 144 | 145 | `; 146 | document.body.appendChild(style); 147 | } 148 | /** 149 | * 初始化事件 150 | */ 151 | function initEvent () { 152 | window.scrollTop = 0; 153 | const activeCall = throttle(active, 100); 154 | window.addEventListener('scroll', (e) => { 155 | window.scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; 156 | activeCall(); 157 | }); 158 | } 159 | /** 160 | * 节流函数 161 | * @param {Function} func 回调 162 | * @param {Number} wait 时间 163 | */ 164 | function throttle (func, wait) { 165 | let previous = 0; 166 | return function () { 167 | let now = Date.now(); 168 | let context = this; 169 | let args = arguments; 170 | if (now - previous > wait) { 171 | func.apply(context, args); 172 | previous = now; 173 | } 174 | } 175 | } 176 | // 防抖 177 | function debounce (fn, wait) { 178 | let timeout = null; 179 | return function () { 180 | if (timeout !== null) clearTimeout(timeout); 181 | timeout = setTimeout(fn, wait); 182 | } 183 | } 184 | /** 185 | * 激活当前标题 186 | */ 187 | function active () { 188 | const hxTags = window.hxTags; 189 | const scrollTop = window.scrollTop; 190 | const hxElements = document.getElementsByClassName('jianshu-a'); 191 | const jianshuDir = document.getElementsByClassName('jianshu-scroll')[0]; 192 | let j = 0; 193 | for (let i = 0; i < hxTags.length; i++) { 194 | const el = hxTags[i]; 195 | if (el.top > scrollTop) { 196 | hxElements[i].classList.add('jianshu-active'); 197 | j = i + 1; 198 | break; 199 | } else { 200 | hxElements[i].classList.remove('jianshu-active'); 201 | } 202 | } 203 | if (j - 1 < 10) 204 | toScroll((x, y) => jianshuDir.scrollTop = y, jianshuDir.scrollTop, 0); 205 | else 206 | toScroll((x, y) => jianshuDir.scrollTop = y, jianshuDir.scrollTop, hxElements[j - 10 % hxTags.length].offsetTop); 207 | for (; j < hxTags.length; j++) { 208 | const el = hxElements[j]; 209 | el.classList.remove('jianshu-active'); 210 | } 211 | } 212 | /** 213 | * 设置滚动条位置 214 | * @param {number} top 滚动条跳转位置 215 | */ 216 | function toScroll (scrollFun, start, end) { 217 | let scroll = { 218 | charged: '0%', 219 | cycles: start 220 | } 221 | anime({ 222 | targets: scroll, 223 | charged: '100%', 224 | cycles: end, 225 | round: 1, 226 | easing: 'easeInOutQuint', 227 | update: function () { 228 | scrollFun(0, scroll.cycles); 229 | } 230 | }); 231 | } 232 | /** 233 | * 创建目录面板 234 | * @param {Array} hxTags 标题标签 235 | */ 236 | function createDirView (hxTags) { 237 | const listElement = document.querySelector("[role=main] > aside > div > div > section") 238 | const titleElement = listElement.firstChild.cloneNode(); 239 | const itemElement = listElement.lastChild.cloneNode(); 240 | let minHx = 10; 241 | listElement.innerHTML = ""; 242 | listElement.parentNode.id = "jianshu-dir"; 243 | listElement.classList.add('jianshu-scroll'); 244 | titleElement.innerHTML = "目录"; 245 | listElement.appendChild(titleElement); 246 | hxTags.forEach(item => { 247 | if (minHx > item.hx) { 248 | minHx = item.hx; 249 | } 250 | }); 251 | hxTags.forEach(item => { 252 | const listItem = itemElement.cloneNode(); 253 | listItem.addEventListener('click', () => toScroll(window.scrollTo, window.scrollTop, item.top)); 254 | listItem.style.marginLeft = `${(item.hx - minHx) * 10}px`; 255 | const tag = `${item.name}`; 256 | listItem.innerHTML = tag; 257 | listElement.appendChild(listItem); 258 | }) 259 | if (hxTags.length === 0) { 260 | const listItem = itemElement.cloneNode(); 261 | listItem.id = "listItem"; 262 | listItem.innerHTML = `本篇文章暂无目录`; 263 | listElement.appendChild(listItem); 264 | } 265 | console.log("============================================"); 266 | console.log("jianshu-dir-tree load ok"); 267 | console.log("github: https://github.com/WumaCoder/mini-tools 获取更多脚本"); 268 | console.log("jianshu-dir-tree version 1.0.2"); 269 | console.log("============================================"); 270 | 271 | } 272 | /** 273 | * 入口 274 | */ 275 | function main () { 276 | const fun = debounce(() => { 277 | const hxTags = getHxTags().map(item => parserTag(item)); 278 | window.hxTags = hxTags; 279 | createDirView(hxTags); 280 | }, 100); 281 | responseHander(({ responseURL }) => { 282 | if (responseURL.indexOf('recommendations') === -1) { 283 | return; 284 | } 285 | fun(); 286 | }); 287 | } 288 | 289 | initEvent(); 290 | initStyle(); 291 | main(); 292 | // Your code here... 293 | })(); 294 | -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/index.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name 简书搜索强化 3 | // @name:zh 简书搜索强化 4 | // @name:en jianshu-search-pro 5 | // @namespace https://github.com/WumaCoder/mini-tools.git 6 | // @homepageURL https://github.com/WumaCoder/mini-tools.git 7 | // @version 1.0.1 8 | // @description 对简书的搜索功能进行增强,增强搜索收藏的文章,增强搜索喜欢的文章。 9 | // @description:en Enhance the search function of the short book, enhance the search of favorite articles, enhance the search of favorite articles. 10 | // @author WumaCoder 11 | // @match *://www.jianshu.com/* 12 | // @grant GM_setValue 13 | // @grant GM_getValue 14 | // @grant GM_registerMenuCommand 15 | // @grant GM_unregisterMenuCommand 16 | // @grant GM_openInTab 17 | // @license GPL-3.0-only 18 | // ==/UserScript== 19 | 20 | (function () { 21 | 22 | /*! 23 | * @name menuCommand.js 24 | * @version 0.0.1 25 | * @author Blaze 26 | * @date 2019/9/21 14:22 27 | * 来自 HTML5视频播放器增强脚本 28 | */ 29 | const monkeyMenu = { 30 | on (title, fn, accessKey) { 31 | return window.GM_registerMenuCommand && window.GM_registerMenuCommand(title, fn, accessKey) 32 | }, 33 | off (id) { 34 | return window.GM_unregisterMenuCommand && window.GM_unregisterMenuCommand(id) 35 | }, 36 | /* 切换类型的菜单功能 */ 37 | switch (title, fn, defVal) { 38 | const t = this; 39 | t.on(title, fn); 40 | } 41 | }; 42 | 43 | /** 44 | * 将字符串转化为doument 45 | */ 46 | function toDocument (str = "") { 47 | str = str.replace(/\r\n\f/igm, ''); 48 | const dom = document.createElement('DOM'); 49 | dom.innerHTML = str; 50 | return dom; 51 | } 52 | /** 53 | * 初始化自定义样式 54 | */ 55 | function initStyle () { 56 | const style = document.createElement("style"); 57 | style.innerText = ` 58 | .tip-li-icon{ 59 | float: left; 60 | margin-right: 10px; 61 | font-size: 18px; 62 | color: #ea6f5a; 63 | } 64 | nav form .search-input:focus+.search-btn{ 65 | background-color:#ea6f5a; 66 | } 67 | `; 68 | document.body.appendChild(style); 69 | } 70 | /** 71 | * 初始化事件 72 | */ 73 | function initEvent () { 74 | const q = document.getElementById('q'); 75 | const call = debounce(() => { 76 | const store = window._store; 77 | const reg = new RegExp(`[${q.value.split('').join(']+.*[')}]+`, 'igm'); 78 | createSearchTipView( 79 | store.likedList.filter(item => item.title.match(reg)), 80 | store.bookmarksList.filter(item => item.title.match(reg)) 81 | ); 82 | }, 500); 83 | q.addEventListener('input', call); 84 | } 85 | 86 | /** 87 | * 初始化菜单 88 | */ 89 | function initMenu () { 90 | monkeyMenu.on('清空缓存', function () { 91 | restore(); 92 | alert("请刷新网页"); 93 | }); 94 | monkeyMenu.on('更新脚本', function () { 95 | window.GM_openInTab('https://greasyfork.org/zh-CN/scripts/400102-%E7%AE%80%E4%B9%A6%E6%90%9C%E7%B4%A2%E5%BC%BA%E5%8C%96', { 96 | active: true, 97 | insert: true, 98 | setParent: true 99 | }); 100 | }); 101 | monkeyMenu.on('关于', function () { 102 | window.GM_openInTab('https://github.com/WumaCoder/mini-tools', { 103 | active: true, 104 | insert: true, 105 | setParent: true 106 | }); 107 | }); 108 | monkeyMenu.on('反馈', function () { 109 | window.GM_openInTab('https://github.com/WumaCoder/mini-tools/issues', { 110 | active: true, 111 | insert: true, 112 | setParent: true 113 | }); 114 | }); 115 | } 116 | 117 | /** 118 | * 初始化数据 119 | */ 120 | function initData () { 121 | window._store = { userInfo: null, likedList: [], likedInfo: null, bookmarksList: [], bookmarksInfo: null }; 122 | window._store.likedList = GM_getValue('likedList', []); 123 | window._store.bookmarksList = GM_getValue('bookmarksList', []); 124 | } 125 | /** 126 | * 节流函数 127 | * @param {Function} func 回调 128 | * @param {Number} wait 时间 129 | */ 130 | function throttle (func, wait) { 131 | let previous = 0; 132 | return function () { 133 | let now = Date.now(); 134 | let context = this; 135 | let args = arguments; 136 | if (now - previous > wait) { 137 | func.apply(context, args); 138 | previous = now; 139 | } 140 | } 141 | } 142 | // 防抖 143 | function debounce (fn, wait) { 144 | let timeout = null; 145 | return function () { 146 | if (timeout !== null) clearTimeout(timeout); 147 | timeout = setTimeout(fn, wait); 148 | } 149 | } 150 | 151 | async function getLikedInfo () { 152 | const url = `https://www.jianshu.com/users/${window._store.userInfo.current_user.slug}/liked_notes`; 153 | const res = await $.get(url); 154 | const dom = toDocument(res); 155 | const aTag = dom.querySelector("li.active > a"); 156 | const count = aTag.innerText.replace('喜欢的文章 ', '') * 1; 157 | window._store.likedInfo = { 158 | count, 159 | url 160 | }; 161 | } 162 | 163 | async function getUserInfo () { 164 | const res = await $.get('https://www.jianshu.com/settings/basic'); 165 | const dom = toDocument(res); 166 | const infoEl = dom.querySelector("script[data-name=page-data]"); 167 | window._store.userInfo = JSON.parse(infoEl.text); 168 | } 169 | 170 | async function getBookmarksInfo () { 171 | const res = await $.get('https://www.jianshu.com/bookmarks'); 172 | const dom = toDocument(res); 173 | const infoEl = dom.querySelector("script[data-name=bookmark_page_data]"); 174 | window._store.bookmarksInfo = JSON.parse(infoEl.text); 175 | const pageInfo = window._store.bookmarksInfo; 176 | const res1 = await $.get('https://www.jianshu.com/bookmarks', { page: pageInfo.totalPages }); 177 | const dom1 = toDocument(res1); 178 | const aList = dom1.querySelectorAll("ul.note-list > li > div > a"); 179 | pageInfo.count = aList.length + (pageInfo.totalPages - 1) * 20; 180 | } 181 | 182 | 183 | /** 184 | * 获取收藏的文章列表 185 | * @param {number } page 页码 186 | * @returns 是否有重复项目 187 | */ 188 | async function getBookmarksList (page) { 189 | const res = await $.get(`https://www.jianshu.com/bookmarks`, { page }); 190 | const dom = toDocument(res); 191 | const aList = dom.querySelectorAll("ul.note-list > li > div > a"); 192 | let flag = 0; 193 | for (let i = 0; i < aList.length; i++) { 194 | const element = aList[i]; 195 | const temp = { href: element.href, title: element.innerText }; 196 | if (window._store.bookmarksList.filter(item => temp.href == item.href).length >= 1) { 197 | flag = 1; 198 | break; 199 | } 200 | window._store.bookmarksList.push(temp); 201 | } 202 | GM_setValue('bookmarksList', window._store.bookmarksList); 203 | return flag; 204 | } 205 | 206 | /** 207 | * 获取喜欢的文章列表 208 | * @param {number } page 页码 209 | * @returns 是否有重复项目 210 | */ 211 | async function getLikedList (page) { 212 | const res = await $.get(`https://www.jianshu.com/users/${window._store.userInfo.current_user.slug}/liked_notes?page=${page}`); 213 | const dom = toDocument(res); 214 | const aList = dom.querySelectorAll("ul.note-list > li > div > a"); 215 | let flag = 0; 216 | for (let i = 0; i < aList.length; i++) { 217 | const element = aList[i]; 218 | const temp = { href: element.href, title: element.innerText }; 219 | if (window._store.likedList.filter(item => temp.href == item.href).length >= 1) { 220 | flag = 1; 221 | break; 222 | } 223 | window._store.likedList.push(temp); 224 | } 225 | GM_setValue('likedList', window._store.likedList); 226 | return flag; 227 | } 228 | 229 | /** 230 | * 恢复 231 | */ 232 | function restore () { 233 | GM_setValue('likedList', []) 234 | } 235 | 236 | function createLine () { 237 | const line = document.createElement('LI'); 238 | line.style.borderBottom = "1px solid #f0f0f0"; 239 | return line.cloneNode(true); 240 | } 241 | 242 | function createLi (url, title, type) { 243 | const li = document.createElement('LI'); 244 | li.innerHTML = ` 245 | 246 | ${title} 247 | `; 248 | return li.cloneNode(true) 249 | } 250 | 251 | function clearChildNode (el) { 252 | for (let i = el.childNodes.length - 1; i >= 0; i--) { 253 | const element = el.childNodes[i]; 254 | el.removeChild(element); 255 | } 256 | } 257 | 258 | function cloneChildNode (el, el2) { 259 | for (let i = 0; i < el.childNodes.length; i++) { 260 | const element = el.childNodes[i]; 261 | el2.appendChild(element.cloneNode(true)); 262 | } 263 | } 264 | 265 | function createSearchTipView (likedList, bookmarksList) { 266 | const tipElement = document.querySelector(".search-recent-item-wrap"); 267 | clearChildNode(tipElement); 268 | if (likedList) { 269 | likedList.length = likedList.length > 10 ? 10 : likedList.length; 270 | likedList.forEach(item => { 271 | tipElement.appendChild(createLi(item.href, item.title, 'ic-like')); 272 | }); 273 | if (likedList.length === 0) { 274 | tipElement.appendChild(createLi('#', '暂无匹配的喜欢文章', 'ic-like')); 275 | } 276 | } 277 | if (bookmarksList) { 278 | bookmarksList.length = bookmarksList.length > 10 ? 10 : bookmarksList.length; 279 | tipElement.appendChild(createLine()); 280 | bookmarksList.forEach(item => { 281 | tipElement.appendChild(createLi(item.href, item.title, 'ic-mark')); 282 | }); 283 | if (bookmarksList.length === 0) { 284 | tipElement.appendChild(createLi('#', '暂无匹配的收藏文章', 'ic-mark')); 285 | } 286 | } 287 | } 288 | 289 | /** 290 | * 入口 291 | */ 292 | async function main () { 293 | // restore(); 294 | initMenu(); 295 | initEvent(); 296 | initStyle(); 297 | initData(); 298 | 299 | await getUserInfo(); 300 | console.log('***********初始化简书搜索强化*************'); 301 | await getLikedInfo(); 302 | for (let i = 1; i <= Math.ceil(window._store.likedInfo.count / 9); i++) { 303 | if (await getLikedList(i)) break; 304 | console.log(`加载喜欢的文章中...(${window._store.likedInfo.count}/${window._store.likedList.length})`); 305 | } 306 | 307 | await getBookmarksInfo(); 308 | for (let i = 1; i <= Math.ceil(window._store.bookmarksInfo.count / 20); i++) { 309 | if (await getBookmarksList(i)) break; 310 | console.log(`加载收藏的文章中...(${window._store.bookmarksInfo.count}/${window._store.bookmarksList.length})`); 311 | } 312 | console.log('***********加载成功*************'); 313 | console.log('***********简书搜索强化1.0.1*************'); 314 | console.log('***********欢迎来Star~~*************'); 315 | } 316 | main(); 317 | //--------------------- 318 | 319 | // Your code here... 320 | })(); 321 | -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/readme.assets/GIF.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-search-pro/readme.assets/GIF.gif -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200408234130843.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200408234130843.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200408235758979.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200408235758979.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200409112703717.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lydanne/mini-tools/bcbb14754362f1c59a76f5094f25b390d956f0ce/tampermonkey-scripts/jianshu-search-pro/readme.assets/image-20200409112703717.png -------------------------------------------------------------------------------- /tampermonkey-scripts/jianshu-search-pro/readme.md: -------------------------------------------------------------------------------- 1 | # 简书搜索强化插件 2 | 3 | ## 快速通道 4 | 5 | - **[GITHUB](https://github.com/WumaCoder/mini-tools.git)** 6 | 7 | - **[作者码云](https://gitee.com/wuma/mini-tools)** 8 | 9 | - **[作者简书](https://www.jianshu.com/u/c5090bf9d2f3)** 10 | 11 | - **[插件安装](https://greasyfork.org/zh-CN/scripts/400102-%E7%AE%80%E4%B9%A6%E6%90%9C%E7%B4%A2%E5%BC%BA%E5%8C%96)** 12 | 13 | ## 引言 14 | 15 | 你们有没有这样的痛点,每次想看简书收藏的文章都要找好一会 16 | 17 | 其实我本人是经常把简书中的一些非常的文章进行【点赞】或者【收藏】(疯狂暗示),目的是为了以后可以在看一下,但最开始收藏的时候其实还好,我一翻就可以找到我想看的那一篇 18 | 19 | 但是随着文章会越来越多,每次想找文章真的非常的费劲,每次得找好一会,有时候还找不到…..(真不知道官方为啥不出这个功能) 20 | 21 | 不过现在不是问题了,一款牛 x 的插件横空出世,一个不甘于与现状的我,连夜开发终于写出一个解决这个痛点的泼油猴插件`简书搜索强化`。 22 | 23 | `简书搜索强化`是一个增强简书自带搜索功能的插件,通过该插件,我们可以查找我们【喜欢】和【收藏】(再次暗示)的文章,从而提高学习效率。 24 | 25 | ## 演示 26 | 27 | ![GIF.gif](https://upload-images.jianshu.io/upload_images/16469057-c881d4f8f676c11d.gif?imageMogr2/auto-orient/strip) 28 | 29 | ![image-20200408234130843.png](https://upload-images.jianshu.io/upload_images/16469057-8af8ea1cc1cc3e1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 30 | 31 | ## 注意 32 | 33 | 第一次安装后打开简书页面需要,等待那么一会儿(根据网速和收藏喜欢内的文章数量来看),因为插件需要先将所有的目录进行缓存,所以需要稍等片刻。 34 | 35 | 通过控制台可以查看初始化进度 36 | 37 | ## 其他牛 x 的插件 38 | 39 | - [简书目录树,给不一样的简书目录](https://github.com/WumaCoder/mini-tools/tree/master/tampermonkey-scripts/jianshu-dir-tree) 40 | 41 | ## 最后 42 | 43 | 更多的牛 x 插件我会慢慢的发布,请大家关注我,谢谢 44 | 45 | 如果可以给一个 star~或者【赞】,这样可以提高我的编程效率,嘻嘻 46 | --------------------------------------------------------------------------------