pax_global_header00006660000000000000000000000064120244631770014520gustar00rootroot0000000000000052 comment=0d5f4ab5c7b3b769ebdb12c2dc33b68e5d34226d luakit-2012.09.13-r1/000077500000000000000000000000001202446317700137305ustar00rootroot00000000000000luakit-2012.09.13-r1/.gitattributes000066400000000000000000000000471202446317700166240ustar00rootroot00000000000000build-utils/getversion.sh export-subst luakit-2012.09.13-r1/.gitignore000066400000000000000000000001271202446317700157200ustar00rootroot00000000000000*.o *.swp *~ *.1 apidocs luakit common/tokenize.h common/tokenize.c globalconf.h lunit luakit-2012.09.13-r1/AUTHORS000066400000000000000000000064411202446317700150050ustar00rootroot00000000000000Author of luakit: Mason Larobina (mason-l) 2010-2012 Contributors to luakit (A to Z): Alexis Daboville 2011 André Aparício 2011 Ben Armston 2012 Chris van Dijk (quigybo) 2010 Clint Adams (Clint) 2010 Constantin Schomburg (xconstruct) 2011 Fabian Streitel 2010-2011 Gregor Uhlenheuer (kongo) 2010 HarryD 2010 Henning Hasemann 2010 Henrik Hallberg (halhen) 2010 Javier Rojas (jerojasro) 2011 Jonas Höglund (FireFly) 2012 Kumar Appaiah (kmap) 2010 LokiChaos 2012 Matthew Wild (MattJ) 2010 Michael Dietrich (emdete) 2010 Pawel Tomak (grodzik) 2010 Pawel Zuzelski (pawelz) 2010 Pete Elmore 2010 Piotr Husiatyński (husio) 2010 Richard Gay 2010 Roman Leonov (liaonau) 2011 Stefan Bolte (portix) 2011 Stjujsckij Nickolaj 2011 Vasuvi 2010-2011 Author of `lib/markdown.lua`: Niklas Frykholm 2008 Author of the JavaScript greasemonkey methods in `lib/userscripts.lua`: Jim Tuttle 2009 Author of the `lib/go_input.lua` javascript: Aldrik Dunbar (n30n) 2009 Author of the `lib/follow_selected.lua` javascript: israellevin 2009 Inherited authors from the awesomewm project: Julien Danjou 2007-2009 Pierre Habouzit 2008 Michael Gehring 2008 Aldo Cortesi 2007-2008 Inherited authors from the uzbl project: Dequis 2009 Dieter Plaetinck (Dieter@be) 2009-2010 Robert Manea (robm) 2009-2010 Simon Lipp (sloonz) 2010 Inherited authors from the surf project: Enno Boland (tox) 2009-2010 For volumetric graphs on specific contributers "impact" checkout: https://github.com/mason-larobina/luakit/graphs/impact If you believe you should be in this file or notice that I have missed an attribution to somebody else please contact me (Mason) or email me a patch for his file with your/his/her name or names inserted. luakit-2012.09.13-r1/COPYING000066400000000000000000000010641202446317700147640ustar00rootroot00000000000000See AUTHORS file for list of copyright holders. Several source files are based on files in the surf micro-browser project and those sections have been re-licensed under the GPLv3 licence (see COPYING.GPLv3). Several source files are based on files in the uzbl micro-browser project and those sections are licensed under the GPLv3 license (see COPYING.GPLv3). Several source files are based on files in the awesome window-manager project and those sections are licensed under the GPLv3 licence (see COPYING.GPLv3). Everything else is GPLv3 (see COPYING.GPLv3). luakit-2012.09.13-r1/COPYING.GPLv3000066400000000000000000001044671202446317700156710ustar00rootroot00000000000000GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . luakit-2012.09.13-r1/Makefile000066400000000000000000000056771202446317700154070ustar00rootroot00000000000000# Include makefile config include config.mk # Token lib generation TLIST = common/tokenize.list THEAD = common/tokenize.h TSRC = common/tokenize.c SRCS = $(filter-out $(TSRC),$(wildcard *.c) $(wildcard common/*.c) $(wildcard clib/*.c) $(wildcard clib/soup/*.c) $(wildcard widgets/*.c)) $(TSRC) HEADS = $(wildcard *.h) $(wildcard common/*.h) $(wildcard widgets/*.h) $(wildcard clib/*.h) $(wildcard clib/soup/*.h) $(THEAD) globalconf.h OBJS = $(foreach obj,$(SRCS:.c=.o),$(obj)) all: options newline luakit luakit.1 options: @echo luakit build options: @echo "CC = $(CC)" @echo "LUA_PKG_NAME = $(LUA_PKG_NAME)" @echo "CFLAGS = $(CFLAGS)" @echo "CPPFLAGS = $(CPPFLAGS)" @echo "LDFLAGS = $(LDFLAGS)" @echo "INSTALLDIR = $(INSTALLDIR)" @echo "MANPREFIX = $(MANPREFIX)" @echo "DOCDIR = $(DOCDIR)" @echo @echo build targets: @echo "SRCS = $(SRCS)" @echo "HEADS = $(HEADS)" @echo "OBJS = $(OBJS)" $(THEAD) $(TSRC): $(TLIST) ./build-utils/gentokens.lua $(TLIST) $@ globalconf.h: globalconf.h.in sed 's#LUAKIT_INSTALL_PATH .*#LUAKIT_INSTALL_PATH "$(PREFIX)/share/luakit"#' globalconf.h.in > globalconf.h $(OBJS): $(HEADS) config.mk .c.o: @echo $(CC) -c $< -o $@ @$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@ widgets/webview.o: $(wildcard widgets/webview/*.c) luakit: $(OBJS) @echo $(CC) -o $@ $(OBJS) @$(CC) -o $@ $(OBJS) $(LDFLAGS) luakit.1: luakit help2man -N -o $@ ./$< apidoc: luadoc/luakit.lua mkdir -p apidocs luadoc --nofiles -d apidocs luadoc/* lib/* doc: globalconf.h $(THEAD) $(TSRC) doxygen -s luakit.doxygen clean: rm -rf apidocs doc luakit $(OBJS) $(TSRC) $(THEAD) globalconf.h luakit.1 install: install -d $(INSTALLDIR)/share/luakit/ install -d $(DOCDIR) install -m644 README.md AUTHORS COPYING* $(DOCDIR) cp -r lib $(INSTALLDIR)/share/luakit/ chmod 755 $(INSTALLDIR)/share/luakit/lib/ chmod 755 $(INSTALLDIR)/share/luakit/lib/lousy/ chmod 755 $(INSTALLDIR)/share/luakit/lib/lousy/widget/ chmod 644 $(INSTALLDIR)/share/luakit/lib/*.lua chmod 644 $(INSTALLDIR)/share/luakit/lib/lousy/*.lua chmod 644 $(INSTALLDIR)/share/luakit/lib/lousy/widget/*.lua install -d $(INSTALLDIR)/bin install luakit $(INSTALLDIR)/bin/luakit install -d $(DESTDIR)/etc/xdg/luakit/ install config/*.lua $(DESTDIR)/etc/xdg/luakit/ chmod 644 $(DESTDIR)/etc/xdg/luakit/*.lua install -d $(DESTDIR)/usr/share/pixmaps install extras/luakit.png $(DESTDIR)/usr/share/pixmaps/ install -d $(DESTDIR)/usr/share/applications install -m0644 extras/luakit.desktop $(DESTDIR)/usr/share/applications/ install -d $(MANPREFIX)/man1/ install -m644 luakit.1 $(MANPREFIX)/man1/ uninstall: rm -rf $(INSTALLDIR)/bin/luakit $(INSTALLDIR)/share/luakit $(MANPREFIX)/man1/luakit.1 rm -rf /usr/share/applications/luakit.desktop /usr/share/pixmaps/luakit.png lunit: git clone git://repo.or.cz/lunit.git run-tests: luakit lunit @./luakit -c tests/lunit-run.lua tests/test_*.lua newline: options;@echo .PHONY: all clean options install newline apidoc doc luakit-2012.09.13-r1/PATCHES000066400000000000000000000010121202446317700147340ustar00rootroot00000000000000If you plan to submit patches, here's a checklist: Commits: - make commits of logical units; - do not modify piece of code not related to your commit; - do not try to fix style of code you are not writing, it's just adding noise for no gain; - check for unnecessary whitespace with "git diff --check" before commiting; - do not check in commented out code or unneeded files; - provide a meaningful commit message; - the first line of the commit message should be a short; description and should skip the full stop; luakit-2012.09.13-r1/README.md000066400000000000000000000075441202446317700152210ustar00rootroot00000000000000# Luakit luakit is a fast, light and simple to use micro-browser framework exensible by Lua using the WebKit web content engine and the GTK+ toolkit. ## Dont Panic! You don't have to be a developer to use luakit on a daily basis. If you are familiar with vimperator, pentadactyl, jumanji, uzbl & etc you will find luakit behaves similarly out of the box. ## Requirements * gtk2 * Lua (5.1) * lfs (lua file system) * libwebkit (webkit-gtk) * libunique * sqlite3 * help2man ## Compiling To compile the stock luakit run: make To link against LuaJIT (if you have LuaJIT installed) run: make USE_LUAJIT=1 To build without libunique (which uses dbus) run: make USE_UNIQUE=0 To build with a custom compiler run: make CC=clang Note to packagers: you may wish to build luakit with: make DEVELOPMENT_PATHS=0 To prevent luakit searching in relative paths (`./config` & `./lib`) for user configs. The `USE_LUAJIT=1`, `USE_UNIQUE=0`, `PREFIX=/path`, `DEVELOPMENT_PATHS=0`, `CC=clang` build options do not conflict. You can use whichever you desire. ## Installing To install luakit run: sudo make install The luakit binary will be installed at: /usr/local/bin/luakit And configs to: /etc/xdg/luakit/ And the luakit libraries to: /usr/local/share/luakit/lib/ To change the install prefix you will need to re-compile luakit (after a `make clean`) with the following option: make PREFIX=/usr sudo make PREFIX=/usr install ## Use Luakit Just run: luakit [URI..] Or to see the full list of luakit launch options run: luakit -h ## Configuration The configuration options are endless, the entire browser is constructed by the config files present in `/etc/xdg/luakit` There are several files of interest: * rc.lua -- is the main config file which dictates which and in what order different parts of the browser are loaded. * binds.lua -- defines every action the browser takes when you press a button or combination of buttons (even mouse buttons, direction key, etc) and the browser commands (I.e. `:quit`, `:restart`, `:open`, `:lua `, etc). * theme.lua -- change fonts and colours used by the interface widgets. * window.lua -- is responsible for building the luakit browser window and defining several helper methods (I.e. `w:new_tab(uri)`, `w:close_tab()`, `w:close_win()`, etc). * webview.lua -- is a wrapper around the webview widget object and is responsible for watching webview signals (I.e. "key-press", "load-status", "resource-request-starting", etc). This file also provides several window methods which operate on the current webview tab (I.e. `w:reload()`, `w:eval_js("code here..")`, `w:back()`, `w:forward()`). * modes.lua -- manages the modal aspect of the browser and the actions that occur when switching modes. * globals.lua -- change global options like scroll/zoom step, default window size, useragent, search engines, etc. Just copy the files you wish to change (and the rc.lua) into `$XDG_CONFIG_HOME/luakit` (defaults to `~/.config/luakit/`) and luakit will use those files when you next launch it. ## Uninstall To delete luakit from your system run: sudo make uninstall If you installed with a custom prefix remember to add the identical prefix here also, example: sudo make PREFIX=/usr uninstall ## Reporting Bugs Please use the bug tracker at: http://luakit.org/projects/luakit/issues ## Community ### Mailing list Subscribe to the development mailing list here: http://lists.luakit.org/mailman/listinfo/luakit-dev Or view the archives at: http://lists.luakit.org/archive/luakit-dev/ ### IRC Join us in `#luakit` on the `irc.oftc.net` network. luakit-2012.09.13-r1/build-utils/000077500000000000000000000000001202446317700161655ustar00rootroot00000000000000luakit-2012.09.13-r1/build-utils/gentokens.lua000077500000000000000000000063641202446317700207010ustar00rootroot00000000000000#!/usr/bin/env lua -- build-utils/gentokens.lua - gen tokenize lib -- -- Copyright © 2010 Mason Larobina -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see . tokenize_h = [[ /* This file is autogenerated by build-utils/gentokens.lua */ #ifndef LUAKIT_COMMON_TOKENIZE_H #define LUAKIT_COMMON_TOKENIZE_H #include typedef enum luakit_token_t { L_TK_UNKNOWN=0, /* (luakit_token_t) 0 == L_TK_UNKNOWN */ %s } luakit_token_t; __attribute__((pure)) enum luakit_token_t l_tokenize(const gchar *); __attribute__((pure)) const gchar * token_tostring(luakit_token_t); #endif ]] tokenize_c = [[ /* This file is autogenerated by build-utils/gentokens.lua */ #include #include "common/tokenize.h" typedef struct { luakit_token_t tok; const gchar *name; } token_map_t; token_map_t tokens_table[] = { %s { 0, NULL }, }; luakit_token_t l_tokenize(const gchar *s) { static GHashTable *tokens = NULL; if (!tokens) { tokens = g_hash_table_new(g_str_hash, g_str_equal); for (token_map_t *t = tokens_table; t->name; t++) g_hash_table_insert(tokens, (gpointer) t->name, (gpointer) t->tok); } return (luakit_token_t) g_hash_table_lookup(tokens, s); } const gchar * token_tostring(luakit_token_t tok) { if (tok == L_TK_UNKNOWN) return NULL; return tokens_table[((gint)tok) - 1].name; } ]] if #arg ~= 2 then error("invalid args, usage: gentokens.lua [tokens.list] [out.c/out.h]") end -- Load list of tokens local tokens = {} for token in io.lines(arg[1]) do if #token > 0 then if not string.match(token, "^[%w_]+$") then error(string.format("invalid token: %q", token)) end enum = "L_TK_"..string.upper(token) tokens[enum] = { enum = enum, token = token } end end -- Order tokens local order = {} for k, _ in pairs(tokens) do table.insert(order, k) end table.sort(order) if string.match(arg[2], "%.h$") then -- Gen list of tokens local enums = {} for _, k in pairs(order) do table.insert(enums, string.format("%s,", k)) end -- Write header file fh = io.open(arg[2], "w") fh:write(string.format(tokenize_h, table.concat(enums, "\n "))) fh:close() elseif string.match(arg[2], "%.c$") then -- Gen table of { token, "literal" } local tokmap = {} for _, k in pairs(order) do local t = tokens[k] table.insert(tokmap, string.format('{ %s, %q },', t.enum, t.token)) end -- Write source file fh = io.open(arg[2], "w") fh:write(string.format(tokenize_c, table.concat(tokmap, "\n "))) fh:close() else error("Unknown action for file: " .. arg[2]) end luakit-2012.09.13-r1/build-utils/getversion.sh000077500000000000000000000021211202446317700207050ustar00rootroot00000000000000#!/bin/sh # Get the current version using various methods. # The following will serve as a fallback version string (which is the short # hash of the latest commit before the application was packaged (if it was # packaged)). You will find that this file is listed inside the .gitattributes # file like so: # # ./build-utils/getversion.sh export-subst # # This tells git to replace the format string in the following line with the # current short hash upon the calling of the `git archive ` command. VERSION_FROM_ARCHIVE=0d5f4ab # The preferred method is to use the git describe command but this is only # possible if the .git directory is present. if [ -d .git -a -r .git ] then VERSION_FROM_GIT=$(git describe --tags --always) fi if [ x"$VERSION_FROM_GIT" != x ]; then echo $VERSION_FROM_GIT; exit 0; fi if [ "$VERSION_FROM_ARCHIVE" != ':%h$' ]; then echo $VERSION_FROM_ARCHIVE; exit 0; fi echo "ERROR: Commit hash detection failure. Dear packager, please figure out"\ "what has gone wrong and or get in touch with us." >&2 exit 2 # vim: ft=sh:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/000077500000000000000000000000001202446317700146415ustar00rootroot00000000000000luakit-2012.09.13-r1/clib/download.c000066400000000000000000000445161202446317700166260ustar00rootroot00000000000000/* * clib/download.c - wrapper for the WebKitDownload class * * Copyright © 2011 Fabian Streitel * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "common/luaobject.h" #include "clib/download.h" #include "luah.h" #include "globalconf.h" #include #include #include #include #include /** Internal data structure for luakit's downloads. */ typedef struct { /** Common \ref lua_object_t header. \see LUA_OBJECT_HEADER */ LUA_OBJECT_HEADER /** \privatesection */ /** The \c WebKitDownload that handles the actual data transfer. */ WebKitDownload* webkit_download; /** The reference to the Lua object representing the download. * As long as the download is running, the object will be reffed to * prevent its garbage-collection. */ gpointer ref; /** The URI that is being downloaded. */ gchar *uri; /** The destination path in the filesystem where the file is save to. */ gchar *destination; /** The error message in case of a download failure. */ gchar *error; } download_t; static lua_class_t download_class; LUA_OBJECT_FUNCS(download_class, download_t, download) #define luaH_checkdownload(L, idx) luaH_checkudata(L, idx, &(download_class)) /** * Allow garbage collection of the download. * * This function unrefs the download from the object registry. * It also deletes the backup files that may be created by WebKit while * downloading. * * \param L The Lua VM state. * \param download The \ref download_t to unref. */ static void luaH_download_unref(lua_State *L, download_t *download) { if (download->ref) { luaH_object_unref(L, download->ref); download->ref = NULL; } /* delete the annoying backup file generated while downloading */ gchar *backup = g_strdup_printf("%s~", download->destination); g_unlink(backup); g_free(backup); } /** * Returns true if the download is currently in progress. * * \param download The \ref download_t whose progress to check. */ static gboolean download_is_started(download_t *download) { WebKitDownloadStatus status = webkit_download_get_status( download->webkit_download); return status == WEBKIT_DOWNLOAD_STATUS_STARTED; } /** * Frees all data associated with the download and disposes * of the Lua object. * * \param L The Lua VM state. * * \luastack * \lparam A \c download object to free. */ static gint luaH_download_gc(lua_State *L) { download_t *download = luaH_checkdownload(L, 1); g_object_unref(G_OBJECT(download->webkit_download)); if (download->destination) g_free(download->destination); if (download->uri) g_free(download->uri); if (download->error) g_free(download->error); return luaH_object_gc(L); } /** * Callback from the \c WebKitDownload in case of errors. * * Fills the \c error member of \ref download_t. * * \returns \c FALSE */ static gboolean error_cb(WebKitDownload* UNUSED(d), gint UNUSED(error_code), gint UNUSED(error_detail), gchar *reason, download_t *download) { /* save error message */ if (download->error) g_free(download->error); download->error = g_strdup(reason); /* emit error signal if able */ if (download->ref) { lua_State *L = globalconf.L; luaH_object_push(L, download->ref); lua_pushstring(L, reason); luaH_object_emit_signal(L, -2, "error", 1, 0); lua_pop(L, 1); /* unref download */ luaH_download_unref(L, download); } return FALSE; } /** * Creates a new download on the stack. * * \param L The Lua VM state. * * \luastack * \lvalue A table containing properties to set on the download. * \lreturn A new \c download object. */ static gint luaH_download_new(lua_State *L) { luaH_class_new(L, &download_class); download_t *download = luaH_checkdownload(L, -1); /* create download from constructor properties */ WebKitNetworkRequest *request = webkit_network_request_new( download->uri); download->webkit_download = webkit_download_new(request); g_object_ref(G_OBJECT(download->webkit_download)); /* raise error signal on error */ g_signal_connect(G_OBJECT(download->webkit_download), "error", G_CALLBACK(error_cb), download); /* return download */ return 1; } /** * Pushes the given download onto the Lua stack. * * Obtains a GTK reference on the \c WebKitDownload. * * \param L The Lua VM state. * \param d The \c WebKitDownload to push onto the stack. * * \luastack * \lreturn A \c download object. */ gint luaH_download_push(lua_State *L, WebKitDownload *d) { download_class.allocator(L); download_t *download = luaH_checkdownload(L, -1); /* steal webkit download */ download->uri = g_strdup(webkit_download_get_uri(d)); download->webkit_download = d; g_object_ref(G_OBJECT(download->webkit_download)); /* raise error signal on error */ g_signal_connect(G_OBJECT(download->webkit_download), "error", G_CALLBACK(error_cb), download); /* return download */ return 1; } /** * Sets the destination of a download. * * Converts the given destination to a \c file:// URI. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lvalue A string containing the new destination for the download. */ static gint luaH_download_set_destination(lua_State *L, download_t *download) { if (download_is_started(download)) { luaH_warn(L, "cannot change destination while download is running"); return 0; } const gchar *destination = luaL_checkstring(L, -1); gchar *uri = g_filename_to_uri(destination, NULL, NULL); if (uri) { download->destination = g_strdup(destination); webkit_download_set_destination_uri(download->webkit_download, uri); g_free(uri); luaH_object_emit_signal(L, -3, "property::destination", 0, 0); /* g_filename_to_uri failed on destination path */ } else { lua_pushfstring(L, "invalid destination: '%s'", destination); lua_error(L); } return 0; } /** * Returns the destination URI of the given download. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. */ LUA_OBJECT_EXPORT_PROPERTY(download, download_t, destination, lua_pushstring) /** * Returns the current progress in percent of the given download. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The progress of the download as a number between 0.0 and 1.0 */ static gint luaH_download_get_progress(lua_State *L, download_t *download) { gdouble progress = webkit_download_get_progress(download->webkit_download); if (progress == 1) luaH_download_unref(L, download); lua_pushnumber(L, progress); return 1; } /** * Returns the value of the Content-Type webkit network reponse header. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The network request Content-Type. */ static gint luaH_download_get_mime_type(lua_State *L, download_t *download) { WebKitNetworkResponse *response = webkit_download_get_network_response( download->webkit_download); if (!response) return 0; SoupMessage *msg = webkit_network_response_get_message(response); SoupMessageHeaders *headers; g_object_get(G_OBJECT(msg), "response-headers", &headers, NULL); const gchar *mime_type = soup_message_headers_get_one(headers, "Content-Type"); if (mime_type) { lua_pushstring(L, mime_type); return 1; } return 0; } /** * Returns the status of the given download. * * The status will be one of the following: * - \c finished * - \c created * - \c started * - \c cancelled * - \c error * * Returns nothing if an error occurs. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The status of the download as a string or \c nil. */ static gint luaH_download_get_status(lua_State *L, download_t *download) { WebKitDownloadStatus status = webkit_download_get_status( download->webkit_download); switch (status) { case WEBKIT_DOWNLOAD_STATUS_FINISHED: luaH_download_unref(L, download); lua_pushstring(L, "finished"); break; case WEBKIT_DOWNLOAD_STATUS_CREATED: lua_pushstring(L, "created"); break; case WEBKIT_DOWNLOAD_STATUS_STARTED: lua_pushstring(L, "started"); break; case WEBKIT_DOWNLOAD_STATUS_CANCELLED: luaH_download_unref(L, download); lua_pushstring(L, "cancelled"); break; case WEBKIT_DOWNLOAD_STATUS_ERROR: luaH_download_unref(L, download); lua_pushstring(L, "error"); break; default: luaH_warn(L, "unknown download status"); return 0; } return 1; } /* \fn static gint luaH_download_get_error(lua_State *L) * Returns the message of the last error that occurred for the given download. * * If no error occurred so far, returns \c nil. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The message of the last download error as a string or \c nil. */ LUA_OBJECT_EXPORT_PROPERTY(download, download_t, error, lua_pushstring) /** * Returns the expected total size of the download. * * May vary during downloading as not all servers send this correctly. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The total size of the download in bytes as a number. */ static gint luaH_download_get_total_size(lua_State *L, download_t *download) { gdouble total_size = webkit_download_get_total_size( download->webkit_download); lua_pushnumber(L, total_size); return 1; } /** * Returns the current size of the download, i.e. the bytes already downloaded. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The current size of the download in bytes as a number. */ static gint luaH_download_get_current_size(lua_State *L, download_t *download) { gdouble current_size = webkit_download_get_current_size( download->webkit_download); lua_pushnumber(L, current_size); return 1; } /** * Returns the elapsed time since starting the download. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The elapsed time since starting the download in seconds as a number. */ static gint luaH_download_get_elapsed_time(lua_State *L, download_t *download) { gdouble elapsed_time = webkit_download_get_elapsed_time( download->webkit_download); lua_pushnumber(L, elapsed_time); return 1; } /** * Returns the suggested filename for the download. * This is provided by \c WebKit and inferred from the URI and response headers. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The filename that WebKit suggests for the download as a string. */ static gint luaH_download_get_suggested_filename(lua_State *L, download_t *download) { const gchar *suggested_filename = webkit_download_get_suggested_filename( download->webkit_download); lua_pushstring(L, suggested_filename); return 1; } /** * Sets the URI of the download. * This does not have any effect if the download is already running. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lvalue The new URI to download. */ static gint luaH_download_set_uri(lua_State *L, download_t *download) { gchar *uri = (gchar*) luaL_checkstring(L, -1); /* use http protocol if none specified */ if (g_strrstr(uri, "://")) uri = g_strdup(uri); else uri = g_strdup_printf("http://%s", uri); download->uri = uri; return 0; } /** * Returns the URI that is being downloaded. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \luastack * \lparam A \c download object. * \lreturn The URI of this download as a string. */ LUA_OBJECT_EXPORT_PROPERTY(download, download_t, uri, lua_pushstring) /** * Checks prerequesites for downloading. * - clears the last error message of the download. * - checks that a destination has been set. * * \param L The Lua VM state. * \param download The \ref download_t of the download. * * \returns \c TRUE if the download is ready to begin. */ static gboolean download_check_prerequesites(lua_State *L, download_t *download) { /* clear last download error message */ if (download->error) { g_free(download->error); download->error = NULL; } /* get download destination */ const gchar *destination = webkit_download_get_destination_uri( download->webkit_download); if (!destination) { download->error = g_strdup("Download destination not set"); luaH_warn(L, "%s", download->error); return FALSE; } /* ready to go */ return TRUE; } /** * Starts the download. * * Will produce a warning if the download is already running. * References the download to prevent its garbage collection. * Will raise a Lua error if the start failed. * * \param L The Lua VM state. * * \luastack * \lparam A \c download object to start. */ static gint luaH_download_start(lua_State *L) { download_t *download = luaH_checkdownload(L, 1); if (!download_is_started(download)) { /* prevent lua garbage collection while downloading */ lua_pushvalue(L, 1); download->ref = luaH_object_ref(L, -1); /* check if we can download to destination */ if (download_check_prerequesites(L, download)) webkit_download_start(download->webkit_download); /* check for webkit/glib errors from download start */ if (download->error) { lua_pushstring(L, download->error); lua_error(L); } } else luaH_warn(L, "download already stared"); return 0; } /** * Aborts the download. * * Will produce a warning if the download is not running. * Unreferences the download to allow its garbage collection. * * \param L The Lua VM state. * * \luastack * \lparam A \c download object to abort. */ static gint luaH_download_cancel(lua_State *L) { download_t *download = luaH_checkdownload(L, 1); if (download_is_started(download)) { webkit_download_cancel(download->webkit_download); luaH_download_unref(L, download); } else luaH_warn(L, "download not started"); return 0; } /** * Creates the Lua download class. * * \param L The Lua VM state. */ void download_class_setup(lua_State *L) { static const struct luaL_reg download_methods[] = { LUA_CLASS_METHODS(download) { "__call", luaH_download_new }, { NULL, NULL } }; static const struct luaL_reg download_meta[] = { LUA_OBJECT_META(download) LUA_CLASS_META { "start", luaH_download_start }, { "cancel", luaH_download_cancel }, { "__gc", luaH_download_gc }, { NULL, NULL }, }; luaH_class_setup(L, &download_class, "download", (lua_class_allocator_t) download_new, NULL, NULL, download_methods, download_meta); luaH_class_add_property(&download_class, L_TK_DESTINATION, (lua_class_propfunc_t) luaH_download_set_destination, (lua_class_propfunc_t) luaH_download_get_destination, (lua_class_propfunc_t) luaH_download_set_destination); luaH_class_add_property(&download_class, L_TK_PROGRESS, NULL, (lua_class_propfunc_t) luaH_download_get_progress, NULL); luaH_class_add_property(&download_class, L_TK_STATUS, NULL, (lua_class_propfunc_t) luaH_download_get_status, NULL); luaH_class_add_property(&download_class, L_TK_ERROR, NULL, (lua_class_propfunc_t) luaH_download_get_error, NULL); luaH_class_add_property(&download_class, L_TK_TOTAL_SIZE, NULL, (lua_class_propfunc_t) luaH_download_get_total_size, NULL); luaH_class_add_property(&download_class, L_TK_CURRENT_SIZE, NULL, (lua_class_propfunc_t) luaH_download_get_current_size, NULL); luaH_class_add_property(&download_class, L_TK_ELAPSED_TIME, NULL, (lua_class_propfunc_t) luaH_download_get_elapsed_time, NULL); luaH_class_add_property(&download_class, L_TK_MIME_TYPE, NULL, (lua_class_propfunc_t) luaH_download_get_mime_type, NULL); luaH_class_add_property(&download_class, L_TK_SUGGESTED_FILENAME, NULL, (lua_class_propfunc_t) luaH_download_get_suggested_filename, NULL); luaH_class_add_property(&download_class, L_TK_URI, (lua_class_propfunc_t) luaH_download_set_uri, (lua_class_propfunc_t) luaH_download_get_uri, (lua_class_propfunc_t) NULL); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/download.h000066400000000000000000000022171202446317700166230ustar00rootroot00000000000000/* * clib/download.h - WebKitDownload wrapper header * * Copyright © 2011 Fabian Streitel * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef LUAKIT_CLIB_DOWNLOAD_H #define LUAKIT_CLIB_DOWNLOAD_H #include #include void download_class_setup(lua_State*); gint luaH_download_push(lua_State*, WebKitDownload*); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/luakit.c000066400000000000000000000444731202446317700163120ustar00rootroot00000000000000/* * clib/luakit.c - Generic functions for Lua scripts * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/signal.h" #include "clib/widget.h" #include "clib/luakit.h" #include "luah.h" #include #include #include #include #include #include /* setup luakit module signals */ LUA_CLASS_FUNCS(luakit, luakit_class) GtkClipboard* luaH_clipboard_get(lua_State *L, gint idx) { #define CB_CASE(t) case L_TK_##t: return gtk_clipboard_get(GDK_SELECTION_##t); switch(l_tokenize(luaL_checkstring(L, idx))) { CB_CASE(PRIMARY) CB_CASE(SECONDARY) CB_CASE(CLIPBOARD) default: break; } return NULL; #undef CB_CASE } /** __index metamethod for the luakit.selection table which * returns text from an X selection. * \see http://en.wikipedia.org/wiki/X_Window_selection * \see http://developer.gnome.org/gtk/stable/gtk-Clipboards.html#gtk-clipboard-wait-for-text * * \param L The Lua VM state. * \return The number of elements pushed on stack. */ static gint luaH_luakit_selection_index(lua_State *L) { GtkClipboard *selection = luaH_clipboard_get(L, 2); if (selection) { gchar *text = gtk_clipboard_wait_for_text(selection); if (text) { lua_pushstring(L, text); g_free(text); return 1; } } return 0; } /** __newindex metamethod for the luakit.selection table which * sets an X selection. * \see http://en.wikipedia.org/wiki/X_Window_selection * \see http://developer.gnome.org/gtk/stable/gtk-Clipboards.html#gtk-clipboard-set-text * * \param L The Lua VM state. * \return The number of elements pushed on stack (0). * * \lcode * luakit.selection.primary = "Malcolm Reynolds" * luakit.selection.clipboard = "John Crichton" * print(luakit.selection.primary) // outputs "Malcolm Reynolds" * luakit.selection.primary = nil // clears the primary selection * print(luakit.selection.primary) // outputs nothing * \endcode */ static gint luaH_luakit_selection_newindex(lua_State *L) { GtkClipboard *selection = luaH_clipboard_get(L, 2); if (selection) { const gchar *text = !lua_isnil(L, 3) ? luaL_checkstring(L, 3) : NULL; if (text && *text) gtk_clipboard_set_text(selection, text, -1); else gtk_clipboard_clear(selection); } return 0; } static gint luaH_luakit_selection_table_push(lua_State *L) { /* create selection table */ lua_newtable(L); /* setup metatable */ lua_createtable(L, 0, 2); lua_pushliteral(L, "__index"); lua_pushcfunction(L, luaH_luakit_selection_index); lua_rawset(L, -3); lua_pushliteral(L, "__newindex"); lua_pushcfunction(L, luaH_luakit_selection_newindex); lua_rawset(L, -3); lua_setmetatable(L, -2); return 1; } /** Escapes a string for use in a URI. * \see http://developer.gnome.org/glib/stable/glib-URI-Functions.html#g-uri-escape-string * * \param L The Lua VM state. * \return The number of elements pushed on stack. * * \luastack * \lparam string The string to escape for use in a URI. * \lparam allowed Optional string of allowed characters to leave unescaped in * the \c string. * \lreturn The escaped string. */ static gint luaH_luakit_uri_encode(lua_State *L) { const gchar *string = luaL_checkstring(L, 1); const gchar *allowed = NULL; /* get list of reserved characters that are allowed in the string */ if (1 < lua_gettop(L) && !lua_isnil(L, 2)) allowed = luaL_checkstring(L, 2); gchar *res = g_uri_escape_string(string, allowed, true); lua_pushstring(L, res); g_free(res); return 1; } /** Unescapes an escaped string used in a URI. * \see http://developer.gnome.org/glib/stable/glib-URI-Functions.html#g-uri-unescape-string * * \param L The Lua VM state. * \return The number of elements pushed on stack. * * \luastack * \lparam string The string to unescape. * \lparam illegal Optional string of illegal chars which should not appear in * the unescaped string. * \lreturn The unescaped string or \c nil if illegal chars found. */ static gint luaH_luakit_uri_decode(lua_State *L) { const gchar *string = luaL_checkstring(L, 1); const gchar *illegal = NULL; /* get list of illegal chars not to be found in the unescaped string */ if (1 < lua_gettop(L) && !lua_isnil(L, 2)) illegal = luaL_checkstring(L, 2); gchar *res = g_uri_unescape_string(string, illegal); if (!res) return 0; lua_pushstring(L, res); g_free(res); return 1; } /** Shows a Gtk save dialog. * \see http://developer.gnome.org/gtk/stable/GtkDialog.html * * \param L The Lua VM state. * \return The number of elements pushed on stack. * * \luastack * \lparam title The title of the dialog window. * \lparam parent The parent window of the dialog or \c nil. * \lparam default_folder The folder to initially display in the file dialog. * \lparam default_name The filename to preselect in the dialog. * \lreturn The name of the selected file or \c nil if the * dialog was cancelled. */ static gint luaH_luakit_save_file(lua_State *L) { const gchar *title = luaL_checkstring(L, 1); /* get window to display dialog over */ GtkWindow *parent_window = NULL; if (!lua_isnil(L, 2)) { widget_t *parent = luaH_checkudata(L, 2, &widget_class); if (!GTK_IS_WINDOW(parent->widget)) luaL_argerror(L, 2, "window widget"); parent_window = GTK_WINDOW(parent->widget); } const gchar *default_folder = luaL_checkstring(L, 3); const gchar *default_name = luaL_checkstring(L, 4); GtkWidget *dialog = gtk_file_chooser_dialog_new(title, parent_window, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); /* set default folder, name and overwrite confirmation policy */ gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), default_folder); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), default_name); gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); lua_pushstring(L, filename); g_free(filename); } else lua_pushnil(L); gtk_widget_destroy(dialog); return 1; } /** Executes a child synchronously (waits for the child to exit before * returning). The exit status and all stdout and stderr output from the * child is returned. * \see http://developer.gnome.org/glib/stable/glib-Spawning-Processes.html#g-spawn-command-line-sync * * \param L The Lua VM state. * \return The number of elements pushed on stack (3). * * \luastack * \lparam cmd The command to run (from a shell). * \lreturn The exit status of the child. * \lreturn The childs stdout. * \lreturn The childs stderr. */ static gint luaH_luakit_spawn_sync(lua_State *L) { GError *e = NULL; gchar *_stdout = NULL; gchar *_stderr = NULL; gint rv; struct sigaction sigact; struct sigaction oldact; const gchar *command = luaL_checkstring(L, 1); /* Note: we have to temporarily clear the SIGCHLD handler. Otherwise * g_spawn_sync wouldn't be able to read subprocess' return value. */ sigact.sa_handler=SIG_DFL; sigemptyset (&sigact.sa_mask); sigact.sa_flags=0; if (sigaction(SIGCHLD, &sigact, &oldact)) fatal("Can't clear SIGCHLD handler"); g_spawn_command_line_sync(command, &_stdout, &_stderr, &rv, &e); /* restore SIGCHLD handler */ if (sigaction(SIGCHLD, &oldact, NULL)) fatal("Can't restore SIGCHLD handler"); /* raise error on spawn function error */ if(e) { lua_pushstring(L, e->message); g_clear_error(&e); lua_error(L); } /* push exit status, stdout, stderr on to stack and return */ lua_pushinteger(L, WEXITSTATUS(rv)); lua_pushstring(L, _stdout); lua_pushstring(L, _stderr); g_free(_stdout); g_free(_stderr); return 3; } /* Calls the Lua function defined as callback for a (async) spawned process * The called Lua function receives 2 arguments: * Exit type: one of: "exit" (normal exit), "signal" (terminated by * signal), "unknown" (another reason) * Exit number: When normal exit happened, the exit code of the process. When * finished by a signal, the signal number. -1 otherwise. */ void async_callback_handler(GPid pid, gint status, gpointer cb_ref) { lua_State *L = globalconf.L; /* push callback function onto stack */ luaH_object_push(L, cb_ref); /* push exit reason & exit status onto lua stack */ if (WIFEXITED(status)) { lua_pushliteral(L, "exit"); lua_pushinteger(L, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) { lua_pushliteral(L, "signal"); lua_pushinteger(L, WTERMSIG(status)); } else { lua_pushliteral(L, "unknown"); lua_pushinteger(L, -1); } if (lua_pcall(L, 2, 0, 0)) { warn("error in callback function: %s", lua_tostring(L, -1)); lua_pop(L, 1); } luaH_object_unref(L, cb_ref); g_spawn_close_pid(pid); } /** Executes a child program asynchronously (your program will not block waiting * for the child to exit). * * \see \ref async_callback_handler * \see http://developer.gnome.org/glib/stable/glib-Shell-related-Utilities.html#g-shell-parse-argv * \see http://developer.gnome.org/glib/stable/glib-Spawning-Processes.html#g-spawn-async * * \param L The Lua VM state. * \return The number of elements pushed on stack (0). * * \luastack * \lparam command The command to execute a child program. * \lparam callback Optional Lua callback function. * \lreturn The child pid. * * \lcode * local editor = "gvim" * local filename = "config" * * function editor_callback(exit_reason, exit_status) * if exit_reason == "exit" then * print(string.format("Contents of %q:", filename)) * for line in io.lines(filename) do * print(line) * end * else * print("Editor exited with status: " .. exit_status) * end * end * * luakit.spawn(string.format("%s %q", editor, filename), editor_callback) * \endcode */ static gint luaH_luakit_spawn(lua_State *L) { GError *e = NULL; GPid pid = 0; const gchar *command = luaL_checkstring(L, 1); gint argc = 0; gchar **argv = NULL; gpointer cb_ref = NULL; /* check callback function type */ if (lua_gettop(L) > 1 && !lua_isnil(L, 2)) { if (lua_isfunction(L, 2)) cb_ref = luaH_object_ref(L, 2); else luaL_typerror(L, 2, lua_typename(L, LUA_TFUNCTION)); } /* parse arguments */ if (!g_shell_parse_argv(command, &argc, &argv, &e)) goto spawn_error; /* spawn command */ if (!g_spawn_async(NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD|G_SPAWN_SEARCH_PATH, NULL, NULL, &pid, &e)) goto spawn_error; /* attach users Lua callback */ if (cb_ref) g_child_watch_add(pid, async_callback_handler, cb_ref); g_strfreev(argv); lua_pushnumber(L, pid); return 1; spawn_error: luaH_object_unref(L, cb_ref); lua_pushstring(L, e->message); g_clear_error(&e); g_strfreev(argv); lua_error(L); return 0; } /** Get seconds from unix epoch with nanosecond precision (or nearest * supported by the users system). * \see http://www.kernel.org/doc/man-pages/online/pages/man2/clock_gettime.2.html * * \param L The Lua VM state. * \return The number of elements pushed on the stack (1). */ static gint luaH_luakit_time(lua_State *L) { lua_pushnumber(L, l_time()); return 1; } /** Wrapper around the execl POSIX function. The exec family of functions * replaces the current process image with a new process image. This function * will only return if there was an error with the execl call. * \see http://en.wikipedia.org/wiki/Execl * * \param L The Lua VM state. * \return The number of elements pushed on the stack (0). */ static gint luaH_luakit_exec(lua_State *L) { static const gchar *shell = NULL; if (!shell && !(shell = g_getenv("SHELL"))) shell = "/bin/sh"; execl(shell, shell, "-c", luaL_checkstring(L, 1), NULL); return 0; } /** luakit module index metamethod. * * \param L The Lua VM state. * \return The number of elements pushed on stack. */ static gint luaH_luakit_index(lua_State *L) { if(luaH_usemetatable(L, 1, 2)) return 1; widget_t *w; const gchar *prop = luaL_checkstring(L, 2); luakit_token_t token = l_tokenize(prop); switch(token) { /* push string properties */ PS_CASE(CACHE_DIR, globalconf.cache_dir) PS_CASE(CONFIG_DIR, globalconf.config_dir) PS_CASE(DATA_DIR, globalconf.data_dir) PS_CASE(EXECPATH, globalconf.execpath) PS_CASE(CONFPATH, globalconf.confpath) /* push boolean properties */ PB_CASE(VERBOSE, globalconf.verbose) PB_CASE(NOUNIQUE, globalconf.nounique) case L_TK_WINDOWS: lua_newtable(L); for (guint i = 0; i < globalconf.windows->len; i++) { w = globalconf.windows->pdata[i]; luaH_object_push(L, w->ref); lua_rawseti(L, -2, i+1); } return 1; case L_TK_WEBKIT_VERSION: lua_pushfstring(L, "%d.%d.%d", WEBKIT_MAJOR_VERSION, WEBKIT_MINOR_VERSION, WEBKIT_MICRO_VERSION); return 1; case L_TK_WEBKIT_USER_AGENT_VERSION: lua_pushfstring(L, "%d.%d", WEBKIT_USER_AGENT_MAJOR_VERSION, WEBKIT_USER_AGENT_MINOR_VERSION); return 1; case L_TK_SELECTION: return luaH_luakit_selection_table_push(L); case L_TK_INSTALL_PATH: lua_pushliteral(L, LUAKIT_INSTALL_PATH); return 1; case L_TK_VERSION: lua_pushliteral(L, VERSION); return 1; case L_TK_DEV_PATHS: #ifdef DEVELOPMENT_PATHS lua_pushboolean(L, TRUE); #else lua_pushboolean(L, FALSE); #endif return 1; default: break; } return 0; } /** Quit the main GTK loop. * \see http://developer.gnome.org/gtk/stable/gtk-General.html#gtk-main-quit * * \param L The Lua VM state. * \return The number of elements pushed on stack. */ static gint luaH_luakit_quit(lua_State* UNUSED(L)) { if (gtk_main_level()) gtk_main_quit(); else exit(EXIT_SUCCESS); return 0; } /** Calls the idle callback function. If the callback function returns false the * idle source is removed, the Lua function is unreffed and will not be called * again. * \see luaH_luakit_idle_add * * \param func Lua callback function. * \return TRUE to keep source alive, FALSE to remove. */ static gboolean idle_cb(gpointer func) { lua_State *L = globalconf.L; /* get original stack size */ gint top = lua_gettop(L); gboolean keep = FALSE; /* call function */ luaH_object_push(L, func); if (lua_pcall(L, 0, 1, 0)) /* remove idle source if error in callback */ warn("error in idle callback: %s", lua_tostring(L, -1)); else /* keep the source alive? */ keep = lua_toboolean(L, -1); /* allow collection of idle callback func */ if (!keep) luaH_object_unref(L, func); /* leave stack how we found it */ lua_settop(L, top); return keep; } /** Adds a function to be called whenever there are no higher priority GTK * events pending in the default main loop. If the function returns false it * is automatically removed from the list of event sources and will not be * called again. * \see http://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html#g-idle-add * * \param L The Lua VM state. * \return The number of elements pushed on the stack (0). * * \luastack * \lparam func The callback function. */ static gint luaH_luakit_idle_add(lua_State *L) { luaH_checkfunction(L, 1); gpointer func = luaH_object_ref(L, 1); g_idle_add(idle_cb, func); return 0; } /** Removes an idle callback by function. * \see http://developer.gnome.org/glib/unstable/glib-The-Main-Event-Loop.html#g-idle-remove-by-data * * \param L The Lua VM state. * \return The number of elements pushed on the stack (0). * * \luastack * \lparam func The callback function. * \lreturn true if callback removed. */ static gint luaH_luakit_idle_remove(lua_State *L) { luaH_checkfunction(L, 1); gpointer func = (gpointer)lua_topointer(L, 1); lua_pushboolean(L, g_idle_remove_by_data(func)); luaH_object_unref(L, func); return 1; } /** Setup luakit module. * * \param L The Lua VM state. */ void luakit_lib_setup(lua_State *L) { static const struct luaL_reg luakit_lib[] = { LUA_CLASS_METHODS(luakit) { "__index", luaH_luakit_index }, { "exec", luaH_luakit_exec }, { "quit", luaH_luakit_quit }, { "save_file", luaH_luakit_save_file }, { "spawn", luaH_luakit_spawn }, { "spawn_sync", luaH_luakit_spawn_sync }, { "time", luaH_luakit_time }, { "uri_decode", luaH_luakit_uri_decode }, { "uri_encode", luaH_luakit_uri_encode }, { "idle_add", luaH_luakit_idle_add }, { "idle_remove", luaH_luakit_idle_remove }, { NULL, NULL } }; /* create signals array */ luakit_class.signals = signal_new(); /* export luakit lib */ luaH_openlib(L, "luakit", luakit_lib, luakit_lib); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/luakit.h000066400000000000000000000017301202446317700163040ustar00rootroot00000000000000/* * clib/luakit.h - Generic functions for Lua scripts * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_CLIB_LUAKIT_H #define LUAKIT_CLIB_LUAKIT_H /* lua luakit class for signals */ lua_class_t luakit_class; void luakit_lib_setup(lua_State *L); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/000077500000000000000000000000001202446317700156275ustar00rootroot00000000000000luakit-2012.09.13-r1/clib/soup/auth.c000066400000000000000000000212611202446317700167360ustar00rootroot00000000000000/* * clib/soup/auth.c - authentication management * * Copyright © 2009 Igalia S.L. * Copyright © 2010 Fabian Streitel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clib/soup/soup.h" #include "luah.h" #include #include #include #include static void luakit_auth_dialog_session_feature_init(SoupSessionFeatureInterface *interface, gpointer data); G_DEFINE_TYPE_WITH_CODE(LuakitAuthDialog, luakit_auth_dialog, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(SOUP_TYPE_SESSION_FEATURE, luakit_auth_dialog_session_feature_init)) typedef struct { SoupMessage *msg; SoupAuth *auth; SoupSession *session; SoupSessionFeature *manager; GtkWidget *login_entry; GtkWidget *password_entry; GtkWidget *checkbutton; } LuakitAuthData; static void free_auth_data(LuakitAuthData *auth_data) { g_object_unref(auth_data->msg); g_slice_free(LuakitAuthData, auth_data); } static void luakit_store_password(SoupURI *soup_uri, const gchar *login, const gchar *password) { lua_State *L = globalconf.L; gchar *uri = soup_uri_to_string(soup_uri, FALSE); lua_pushstring(L, uri); lua_pushstring(L, login); lua_pushstring(L, password); signal_object_emit(L, soup_class.signals, "store-password", 3, 0); g_free(uri); } static void luakit_find_password(SoupURI *soup_uri, const gchar **login, const gchar **password) { lua_State *L = globalconf.L; gchar *uri = soup_uri_to_string(soup_uri, FALSE); lua_pushstring(L, uri); gint ret = signal_object_emit(L, soup_class.signals, "authenticate", 1, LUA_MULTRET); g_free(uri); if (ret >= 2) { *password = luaL_checkstring(L, -1); *login = luaL_checkstring(L, -2); } lua_pop(L, ret); } static void response_callback(GtkDialog *dialog, gint response_id, LuakitAuthData *auth_data) { const gchar *login; const gchar *password; SoupURI *uri; gboolean store_password; switch(response_id) { case GTK_RESPONSE_OK: login = gtk_entry_get_text(GTK_ENTRY(auth_data->login_entry)); password = gtk_entry_get_text(GTK_ENTRY(auth_data->password_entry)); soup_auth_authenticate(auth_data->auth, login, password); store_password = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(auth_data->checkbutton)); if (store_password) { uri = soup_message_get_uri(auth_data->msg); luakit_store_password(uri, login, password); } default: break; } soup_session_unpause_message(auth_data->session, auth_data->msg); free_auth_data(auth_data); gtk_widget_destroy(GTK_WIDGET(dialog)); } static GtkWidget * table_add_entry(GtkWidget *table, gint row, const gchar *label_text, const gchar *value, gpointer UNUSED(user_data)) { GtkWidget *label = gtk_label_new(label_text); gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); GtkWidget *entry = gtk_entry_new(); gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE); if (value) gtk_entry_set_text(GTK_ENTRY(entry), value); gtk_table_attach(GTK_TABLE(table), label, 0, 1, row, row + 1, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); gtk_table_attach_defaults(GTK_TABLE(table), entry, 1, 2, row, row + 1); return entry; } static void show_auth_dialog(LuakitAuthData *auth_data, const char *login, const char *password) { GtkWidget *widget = gtk_dialog_new(); GtkWindow *window = GTK_WINDOW(widget); GtkDialog *dialog = GTK_DIALOG(widget); gtk_dialog_add_buttons(dialog, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); /* set dialog properties */ gtk_dialog_set_has_separator(dialog, FALSE); gtk_container_set_border_width(GTK_CONTAINER(dialog), 5); gtk_box_set_spacing(GTK_BOX(dialog->vbox), 2); gtk_container_set_border_width(GTK_CONTAINER(dialog->action_area), 5); gtk_box_set_spacing(GTK_BOX(dialog->action_area), 6); gtk_window_set_resizable(window, FALSE); gtk_window_set_title(window, ""); gtk_window_set_icon_name(window, GTK_STOCK_DIALOG_AUTHENTICATION); gtk_dialog_set_default_response(dialog, GTK_RESPONSE_OK); /* build contents */ GtkWidget *hbox = gtk_hbox_new(FALSE, 12); gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); gtk_box_pack_start(GTK_BOX(dialog->vbox), hbox, TRUE, TRUE, 0); GtkWidget *icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION, GTK_ICON_SIZE_DIALOG); gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0.0); gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); GtkWidget *main_vbox = gtk_vbox_new(FALSE, 18); gtk_box_pack_start(GTK_BOX(hbox), main_vbox, TRUE, TRUE, 0); SoupURI *uri = soup_message_get_uri(auth_data->msg); gchar *msg = g_strdup_printf("A username and password are being requested by the site %s", uri->host); GtkWidget *msg_label = gtk_label_new(msg); g_free(msg); gtk_misc_set_alignment(GTK_MISC(msg_label), 0.0, 0.5); gtk_label_set_line_wrap(GTK_LABEL(msg_label), TRUE); gtk_box_pack_start(GTK_BOX(main_vbox), GTK_WIDGET(msg_label), FALSE, FALSE, 0); GtkWidget *vbox = gtk_vbox_new(FALSE, 6); gtk_box_pack_start(GTK_BOX(main_vbox), vbox, FALSE, FALSE, 0); /* the table that holds the entries */ GtkWidget *entry_container = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); gtk_alignment_set_padding(GTK_ALIGNMENT(entry_container), 0, 0, 0, 0); gtk_box_pack_start(GTK_BOX(vbox), entry_container, FALSE, FALSE, 0); GtkWidget *table = gtk_table_new(2, 2, FALSE); gtk_table_set_col_spacings(GTK_TABLE(table), 12); gtk_table_set_row_spacings(GTK_TABLE(table), 6); gtk_container_add(GTK_CONTAINER(entry_container), table); auth_data->login_entry = table_add_entry(table, 0, "Username:", login, NULL); auth_data->password_entry = table_add_entry(table, 1, "Password:", password, NULL); gtk_entry_set_visibility(GTK_ENTRY(auth_data->password_entry), FALSE); GtkWidget *remember_box = gtk_vbox_new(FALSE, 6); gtk_box_pack_start(GTK_BOX(vbox), remember_box, FALSE, FALSE, 0); GtkWidget *checkbutton = gtk_check_button_new_with_label("Store password"); gtk_label_set_line_wrap(GTK_LABEL(gtk_bin_get_child(GTK_BIN(checkbutton))), TRUE); gtk_box_pack_start(GTK_BOX(remember_box), checkbutton, FALSE, FALSE, 0); auth_data->checkbutton = checkbutton; g_signal_connect(dialog, "response", G_CALLBACK(response_callback), auth_data); gtk_widget_show_all(widget); } static void session_authenticate(SoupSession *session, SoupMessage *msg, SoupAuth *auth, gboolean UNUSED(retrying), gpointer user_data) { SoupSessionFeature *manager = SOUP_SESSION_FEATURE(user_data); soup_session_pause_message(session, msg); /* We need to make sure the message sticks around when pausing it */ g_object_ref(msg); SoupURI *uri = soup_message_get_uri(msg); LuakitAuthData *auth_data = g_slice_new(LuakitAuthData); auth_data->msg = msg; auth_data->auth = auth; auth_data->session = session; auth_data->manager = manager; const gchar *login = NULL; const gchar *password = NULL; luakit_find_password(uri, &login, &password); show_auth_dialog(auth_data, login, password); /* TODO: g_free login and password? */ } static void attach(SoupSessionFeature *manager, SoupSession *session) { g_signal_connect(session, "authenticate", G_CALLBACK(session_authenticate), manager); } static void detach(SoupSessionFeature *manager, SoupSession *session) { g_signal_handlers_disconnect_by_func(session, session_authenticate, manager); } static void luakit_auth_dialog_class_init(LuakitAuthDialogClass* UNUSED(klass)) { } static void luakit_auth_dialog_init(LuakitAuthDialog* UNUSED(instance)) { } static void luakit_auth_dialog_session_feature_init(SoupSessionFeatureInterface *interface, gpointer UNUSED(data)) { interface->attach = attach; interface->detach = detach; } LuakitAuthDialog * luakit_auth_dialog_new() { return g_object_new(LUAKIT_TYPE_AUTH_DIALOG, NULL); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/auth.h000066400000000000000000000034651202446317700167510ustar00rootroot00000000000000/* * clib/soup/auth.h - authentication management header * * Copyright © 2009 Igalia S.L. * Copyright © 2010 Fabian Streitel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_CLIB_SOUP_AUTH_H #define LUAKIT_CLIB_SOUP_AUTH_H #include #define LUAKIT_TYPE_AUTH_DIALOG (luakit_auth_dialog_get_type ()) #define LUAKIT_AUTH_DIALOG(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), LUAKIT_TYPE_AUTH_DIALOG, LuakitAuthDialog)) #define LUAKIT_AUTH_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LUAKIT_TYPE_AUTH_DIALOG, LuakitAuthDialog)) #define LUAKIT_IS_AUTH_DIALOG(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), LUAKIT_TYPE_AUTH_DIALOG)) #define LUAKIT_IS_AUTH_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LUAKIT_TYPE_AUTH_DIALOG)) #define LUAKIT_AUTH_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LUAKIT_TYPE_AUTH_DIALOG, LuakitAuthDialog)) typedef struct { GObject parent_instance; } LuakitAuthDialog; typedef struct { GObjectClass parent_class; } LuakitAuthDialogClass; GType luakit_auth_dialog_get_type(); LuakitAuthDialog *luakit_auth_dialog_new(); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/cookiejar.c000066400000000000000000000211401202446317700177370ustar00rootroot00000000000000/* * clib/soup/cookiejar.c - LuakitCookieJar * * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "clib/soup/soup.h" #include "luah.h" #include #include #include #include #include static void luakit_cookie_jar_session_feature_init(SoupSessionFeatureInterface *interface, gpointer data); G_DEFINE_TYPE_WITH_CODE (LuakitCookieJar, luakit_cookie_jar, SOUP_TYPE_COOKIE_JAR, G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, luakit_cookie_jar_session_feature_init)) inline LuakitCookieJar* luakit_cookie_jar_new(void) { return g_object_new(LUAKIT_TYPE_COOKIE_JAR, NULL); } static gint luaH_cookie_push(lua_State *L, SoupCookie *c) { lua_createtable(L, 0, 7); #define PUSH_PROP(prop, type) \ lua_pushliteral(L, #prop); \ lua_push##type(L, c->prop); \ lua_rawset(L, -3); PUSH_PROP(name, string) PUSH_PROP(value, string) PUSH_PROP(domain, string) PUSH_PROP(path, string) PUSH_PROP(secure, boolean) PUSH_PROP(http_only, boolean) #undef PUSH_PROP /* push expires */ lua_pushliteral(L, "expires"); if (c->expires) lua_pushnumber(L, soup_date_to_time_t(c->expires)); else lua_pushnumber(L, -1); lua_rawset(L, -3); return 1; } static SoupCookie* luaH_cookie_from_table(lua_State *L, gint idx, gchar **error) { g_assert(error != NULL); SoupDate *date; SoupCookie *cookie = NULL; const gchar *name = NULL, *value = NULL, *domain = NULL, *path = NULL; gboolean secure = FALSE, http_only = FALSE; gint top = lua_gettop(L), expires = 0, type; /* correct relative index */ if (idx < 0) idx = top + idx + 1; /* cookie.domain */ if (luaH_rawfield(L, idx, "domain") == LUA_TSTRING) domain = lua_tostring(L, -1); /* cookie.name */ if (luaH_rawfield(L, idx, "path") == LUA_TSTRING) path = lua_tostring(L, -1); /* cookie.name */ if ((type = luaH_rawfield(L, idx, "name")) == LUA_TSTRING) name = lua_tostring(L, -1); else if (type == LUA_TNIL) name = ""; /* cookie.expires */ if (luaH_rawfield(L, idx, "expires") == LUA_TNUMBER) expires = lua_tointeger(L, -1); /* cookie.value */ if ((type = luaH_rawfield(L, idx, "value")) == LUA_TSTRING) value = lua_tostring(L, -1); else if (type == LUA_TNIL) { /* expire cookie if value = nil */ value = ""; expires = 0; } /* cookie.http_only */ if ((type = luaH_rawfield(L, idx, "http_only")) == LUA_TNUMBER) http_only = lua_tointeger(L, -1) ? TRUE : FALSE; else if (type == LUA_TBOOLEAN) http_only = lua_toboolean(L, -1); /* cookie.secure */ if ((type = luaH_rawfield(L, idx, "secure")) == LUA_TNUMBER) secure = lua_tointeger(L, -1) ? TRUE : FALSE; else if (type == LUA_TBOOLEAN) secure = lua_toboolean(L, -1); /* truncate luaH_rawfield leftovers */ lua_settop(L, top); /* create soup cookie */ if (domain && domain[0] && path && path[0] && name && value) cookie = soup_cookie_new(name, value, domain, path, 0); if (!cookie) { *error = g_strdup_printf("soup_cookie_new call failed (" "domain '%s', path '%s', name '%s', value '%s', " "secure %d, http_only %d)", domain, path, name, value, secure, http_only); return NULL; } soup_cookie_set_secure(cookie, secure); soup_cookie_set_http_only(cookie, http_only); /* set expiry date from unixtime */ if (expires > 0) { date = soup_date_new_from_time_t((time_t) expires); soup_cookie_set_expires(cookie, date); soup_date_free(date); /* set session cookie */ } else if (expires == -1) soup_cookie_set_max_age(cookie, expires); return cookie; } static GSList* cookies_from_table(lua_State *L, gint idx) { GSList *cookies = NULL; SoupCookie *cookie; /* iterate over cookies table */ lua_pushnil(L); while (lua_next(L, idx)) { /* error if not table */ if (!lua_istable(L, -1)) { g_slist_free_full(cookies, (GDestroyNotify)soup_cookie_free); luaL_error(L, "invalid cookie (table expected, got %s)", lua_typename(L, lua_type(L, -1))); } gchar *error; /* create soup cookie from table */ if ((cookie = luaH_cookie_from_table(L, -1, &error))) { cookies = g_slist_prepend(cookies, cookie); /* bad cookie, raise error */ } else if (error) { g_slist_free_full(cookies, (GDestroyNotify)soup_cookie_free); lua_pushstring(L, error); g_free(error); lua_error(L); } lua_pop(L, 1); /* pop cookie */ } return cookies; } gint luaH_cookiejar_add_cookies(lua_State *L) { SoupCookieJar *sj = SOUP_COOKIE_JAR(soupconf.cookiejar); LuakitCookieJar *j = LUAKIT_COOKIE_JAR(soupconf.cookiejar); GSList *cookies; gboolean silent = TRUE; /* cookies table */ luaH_checktable(L, 1); /* optional silent parameter */ if (lua_gettop(L) >= 2) silent = luaH_checkboolean(L, 2); /* get cookies from table */ if ((cookies = cookies_from_table(L, 1))) { j->silent = silent; /* insert cookies */ for (GSList *p = cookies; p; p = g_slist_next(p)) soup_cookie_jar_add_cookie(sj, p->data); g_slist_free(cookies); j->silent = FALSE; } return 0; } static void request_started(SoupSessionFeature *feature, SoupSession* UNUSED(session), SoupMessage *msg, SoupSocket* UNUSED(socket)) { SoupCookieJar *sj = SOUP_COOKIE_JAR(feature); SoupURI *uri = soup_message_get_uri(msg); lua_State *L = globalconf.L; /* give user a chance to add cookies from other instances into the jar */ gchar *str = soup_uri_to_string(uri, FALSE); lua_pushstring(L, str); g_free(str); signal_object_emit(L, soup_class.signals, "request-started", 1, 0); /* generate cookie header */ gchar *header = soup_cookie_jar_get_cookies(sj, uri, TRUE); if (header) { soup_message_headers_replace(msg->request_headers, "Cookie", header); g_free(header); } else soup_message_headers_remove(msg->request_headers, "Cookie"); } /* soup_cookie_equal wasn't good enough */ inline static gboolean soup_cookie_truly_equal(SoupCookie *c1, SoupCookie *c2) { return (!g_strcmp0(c1->name, c2->name) && !g_strcmp0(c1->value, c2->value) && !g_strcmp0(c1->path, c2->path) && (c1->secure == c2->secure) && (c1->http_only == c2->http_only) && (c1->expires && c2->expires && (soup_date_to_time_t(c1->expires) == soup_date_to_time_t(c2->expires)))); } static void changed(SoupCookieJar *sj, SoupCookie *old, SoupCookie *new) { if (LUAKIT_COOKIE_JAR(sj)->silent) return; lua_State *L = globalconf.L; /* do nothing if cookies are equal */ if (old && new && soup_cookie_truly_equal(old, new)) return; if (old) luaH_cookie_push(L, old); else lua_pushnil(L); if (new) luaH_cookie_push(L, new); else lua_pushnil(L); signal_object_emit(L, soup_class.signals, "cookie-changed", 2, 0); } static void finalize(GObject *object) { G_OBJECT_CLASS(luakit_cookie_jar_parent_class)->finalize(object); } static void luakit_cookie_jar_init(LuakitCookieJar *j) { j->silent = FALSE; } static void luakit_cookie_jar_class_init(LuakitCookieJarClass *class) { G_OBJECT_CLASS(class)->finalize = finalize; SOUP_COOKIE_JAR_CLASS(class)->changed = changed; } static void luakit_cookie_jar_session_feature_init(SoupSessionFeatureInterface *interface, gpointer UNUSED(data)) { interface->request_started = request_started; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/cookiejar.h000066400000000000000000000030761202446317700177540ustar00rootroot00000000000000/* * clib/soup/cookiejar.h - LuakitCookieJar header * * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef LUAKIT_CLIB_SOUP_COOKIEJAR_H #define LUAKIT_CLIB_SOUP_COOKIEJAR_H #include "luah.h" #include #define LUAKIT_TYPE_COOKIE_JAR (luakit_cookie_jar_get_type ()) #define LUAKIT_COOKIE_JAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LUAKIT_TYPE_COOKIE_JAR, LuakitCookieJar)) #define LUAKIT_COOKIE_JAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LUAKIT_TYPE_COOKIE_JAR, LuakitCookieJarClass)) typedef struct { SoupCookieJar parent; gboolean silent; } LuakitCookieJar; typedef struct { SoupCookieJarClass parent_class; } LuakitCookieJarClass; LuakitCookieJar *luakit_cookie_jar_new(void); gint luaH_cookiejar_add_cookies(lua_State *L); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/soup.c000066400000000000000000000133431202446317700167650ustar00rootroot00000000000000/* * clib/soup/soup.c - soup library * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clib/soup/soup.h" #include "common/property.h" #include "common/signal.h" #include #include /* setup soup module signals */ LUA_CLASS_FUNCS(soup, soup_class); property_t soup_properties[] = { { L_TK_ACCEPT_LANGUAGE, "accept-language", CHAR, TRUE }, { L_TK_ACCEPT_LANGUAGE_AUTO, "accept-language-auto", BOOL, TRUE }, { L_TK_IDLE_TIMEOUT, "idle-timeout", INT, TRUE }, { L_TK_MAX_CONNS, "max-conns", INT, TRUE }, { L_TK_MAX_CONNS_PER_HOST, "max-conns-per-host", INT, TRUE }, { L_TK_PROXY_URI, "proxy-uri", URI, TRUE }, { L_TK_SSL_CA_FILE, "ssl-ca-file", CHAR, TRUE }, { L_TK_SSL_STRICT, "ssl-strict", BOOL, TRUE }, { L_TK_TIMEOUT, "timeout", INT, TRUE }, { 0, NULL, 0, 0 }, }; static gint luaH_soup_uri_tostring(lua_State *L) { const gchar *p; gint port; /* check for uri table */ luaH_checktable(L, 1); /* create empty soup uri object */ SoupURI *uri = soup_uri_new(NULL); soup_uri_set_scheme(uri, "http"); #define GET_PROP(prop) \ lua_pushliteral(L, #prop); \ lua_rawget(L, 1); \ if (!lua_isnil(L, -1) && (p = lua_tostring(L, -1)) && p[0]) \ soup_uri_set_##prop(uri, p); \ lua_pop(L, 1); GET_PROP(scheme) GET_PROP(user) GET_PROP(password) GET_PROP(host) GET_PROP(path) GET_PROP(query) GET_PROP(fragment) lua_pushliteral(L, "port"); lua_rawget(L, 1); if (!lua_isnil(L, -1) && (port = lua_tonumber(L, -1))) soup_uri_set_port(uri, port); lua_pop(L, 1); gchar *str = soup_uri_to_string(uri, FALSE); lua_pushstring(L, str); g_free(str); soup_uri_free(uri); return 1; } gint luaH_soup_push_uri(lua_State *L, SoupURI *uri) { const gchar *p; /* create table for uri properties */ lua_newtable(L); #define PUSH_PROP(prop) \ if ((p = uri->prop) && p[0]) { \ lua_pushliteral(L, #prop); \ lua_pushstring(L, p); \ lua_rawset(L, -3); \ } PUSH_PROP(scheme) PUSH_PROP(user) PUSH_PROP(password) PUSH_PROP(host) PUSH_PROP(path) PUSH_PROP(query) PUSH_PROP(fragment) if (uri->port) { lua_pushliteral(L, "port"); lua_pushnumber(L, uri->port); lua_rawset(L, -3); } return 1; } static gint luaH_soup_parse_uri(lua_State *L) { gchar *str = (gchar*)luaL_checkstring(L, 1); /* check for blank uris */ if (!str[0] || !g_strcmp0(str, "about:blank")) return 0; /* default to http:// scheme */ if (!g_strrstr(str, "://")) str = g_strdup_printf("http://%s", str); else str = g_strdup(str); /* parse & push uri */ SoupURI *uri = soup_uri_new(str); g_free(str); if (uri) { luaH_soup_push_uri(L, uri); soup_uri_free(uri); } return uri ? 1 : 0; } static gint luaH_soup_index(lua_State *L) { if (!lua_isstring(L, 2)) return 0; return luaH_gobject_index(L, soup_properties, l_tokenize(lua_tostring(L, 2)), G_OBJECT(soupconf.session)); } static gint luaH_soup_newindex(lua_State *L) { if (!lua_isstring(L, 2)) return 0; luakit_token_t token = l_tokenize(lua_tostring(L, 2)); if (token == L_TK_ACCEPT_POLICY) { g_object_set(G_OBJECT(soupconf.cookiejar), "accept-policy", (gint)luaL_checknumber(L, 3), NULL); return luaH_class_property_signal(L, &soup_class, token); } if (luaH_gobject_newindex(L, soup_properties, token, 3, G_OBJECT(soupconf.session))) return luaH_class_property_signal(L, &soup_class, token); return 0; } void soup_lib_setup(lua_State *L) { static const struct luaL_reg soup_lib[] = { LUA_CLASS_METHODS(soup) { "__index", luaH_soup_index }, { "__newindex", luaH_soup_newindex }, { "parse_uri", luaH_soup_parse_uri }, { "uri_tostring", luaH_soup_uri_tostring }, { "add_cookies", luaH_cookiejar_add_cookies }, { NULL, NULL }, }; /* create signals array */ soup_class.signals = signal_new(); /* init soup struct */ soupconf.cookiejar = luakit_cookie_jar_new(); soupconf.session = webkit_get_default_session(); soup_session_add_feature(soupconf.session, (SoupSessionFeature*) soupconf.cookiejar); /* remove old auth dialog and add luakit's auth feature instead */ soup_session_remove_feature_by_type(soupconf.session, WEBKIT_TYPE_SOUP_AUTH_DIALOG); soup_session_add_feature(soupconf.session, (SoupSessionFeature*) luakit_auth_dialog_new()); /* export soup lib */ luaH_openlib(L, "soup", soup_lib, soup_lib); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/soup/soup.h000066400000000000000000000024561202446317700167750ustar00rootroot00000000000000/* * clib/soup/soup.h - soup library * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_CLIB_SOUP_H #define LUAKIT_CLIB_SOUP_H #include "clib/soup/cookiejar.h" #include "clib/soup/auth.h" #include "luah.h" #include #include typedef struct { /* shared libsoup session */ SoupSession *session; /* shared custom cookie jar */ LuakitCookieJar *cookiejar; } soup_t; soup_t soupconf; /* lua soup class for signals */ lua_class_t soup_class; void soup_lib_setup(lua_State *L); gint luaH_soup_push_uri(lua_State *L, SoupURI *uri); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/sqlite3.c000066400000000000000000000307601202446317700163770ustar00rootroot00000000000000/* * clib/sqlite3.c - luakit sqlite3 wrapper * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clib/sqlite3.h" #include "common/luaobject.h" #include "common/luaclass.h" #include "globalconf.h" #include /** Internal data structure for all Lua \c sqlite3 object instances. */ typedef struct { /** Common \ref lua_object_t header. \see LUA_OBJECT_HEADER */ LUA_OBJECT_HEADER /** \privatesection */ /** File path used to open the SQLite3 database connection handle. */ char *filename; /** Internal SQLite3 connection handle object. \see http://www.sqlite.org/c3ref/sqlite3.html */ sqlite3 *db; } sqlite3_t; typedef struct { sqlite3_t *sqlite; sqlite3_stmt *stmt; gpointer parent_ref; } sqlite3_stmt_t; #define luaH_checksqlite3(L, idx) luaH_checkudata(L, idx, &sqlite3_class) #define luaH_checkstmtud(L, idx) luaH_checkudata(L, idx, &sqlite3_stmt_class) static lua_class_t sqlite3_class, sqlite3_stmt_class; LUA_OBJECT_FUNCS(sqlite3_class, sqlite3_t, sqlite3) static inline void luaH_sqlite3_checkopen(lua_State *L, sqlite3_t *sqlite) { if (!sqlite->db) { lua_pushliteral(L, "sqlite3: database handle closed"); lua_error(L); } } static gint luaH_sqlite3_stmt_gc(lua_State *L) { sqlite3_stmt_t *stmt = luaH_checkstmtud(L, 1); /* release hold over parent sqlite3 object */ luaH_object_unref(L, stmt->parent_ref); sqlite3_finalize(stmt->stmt); return 0; } /* create userdata object for executing prepared/compiled SQL statements */ sqlite3_stmt_t* sqlite3_stmt_new(lua_State *L) { sqlite3_stmt_t *p = lua_newuserdata(L, sizeof(sqlite3_stmt_t)); p_clear(p, 1); luaH_settype(L, &sqlite3_stmt_class); lua_newtable(L); lua_newtable(L); lua_setmetatable(L, -2); lua_setfenv(L, -2); return p; } /* return compliled SQL statement userdata object */ static gint luaH_sqlite3_compile(lua_State *L) { sqlite3_t *sqlite = luaH_checksqlite3(L, 1); luaH_sqlite3_checkopen(L, sqlite); const gchar *sql = luaL_checkstring(L, 2); /* compile SQL statement */ const gchar *tail; sqlite3_stmt *stmt; if (sqlite3_prepare_v2(sqlite->db, sql, -1, &stmt, &tail)) { lua_pushfstring(L, "sqlite3: statement compilation failed (%s)", sqlite3_errmsg(sqlite->db)); sqlite3_finalize(stmt); lua_error(L); } else if (!stmt) { lua_pushfstring(L, "sqlite3: no SQL found in string: \"%s\"", sql); lua_error(L); } /* create userdata object */ sqlite3_stmt_t *p = sqlite3_stmt_new(L); p->sqlite = sqlite; p->stmt = stmt; /* store reference to parent sqlite3 object to prevent it being collected * while a sqlite3_stmt object is still around */ p->parent_ref = luaH_object_ref(L, 1); /* sqlite3_prepare_v2 only compiles the first statement found in the sql * query, if there are several `tail` points to the first character of the * next query */ if (tail && *tail) { lua_pushstring(L, tail); return 2; } return 1; } static gint luaH_sqlite3_close(lua_State *L) { sqlite3_t *sqlite = luaH_checksqlite3(L, 1); if (sqlite->filename) { g_free(sqlite->filename); sqlite->filename = NULL; } if (sqlite->db) { sqlite3_close(sqlite->db); sqlite->db = NULL; } return 0; } static gint luaH_sqlite3_gc(lua_State *L) { luaH_sqlite3_close(L); return luaH_object_gc(L); } static gint luaH_sqlite3_set_filename(lua_State *L, sqlite3_t *sqlite) { const gchar *filename = luaL_checkstring(L, -1); /* open database */ if (sqlite3_open(filename, &sqlite->db)) { lua_pushfstring(L, "sqlite3: failed to open \"%s\" (%s)", filename, sqlite3_errmsg(sqlite->db)); sqlite3_close(sqlite->db); lua_error(L); } sqlite->filename = g_strdup(filename); return 0; } static gint luaH_sqlite3_get_filename(lua_State *L, sqlite3_t *sqlite) { lua_pushstring(L, sqlite->filename); return 1; } static gint luaH_sqlite3_changes(lua_State *L) { sqlite3_t *sqlite = luaH_checksqlite3(L, 1); luaH_sqlite3_checkopen(L, sqlite); lua_pushnumber(L, sqlite3_changes(sqlite->db)); return 1; } static gint luaH_param_index(lua_State *L, sqlite3_stmt *stmt, gint idx) { int type = lua_type(L, idx); if (type == LUA_TNUMBER) return lua_tointeger(L, idx); else if (type == LUA_TSTRING) return sqlite3_bind_parameter_index(stmt, lua_tostring(L, idx)); return 0; } static gint luaH_bind_value(lua_State *L, sqlite3_stmt *stmt, gint bidx, gint idx) { switch (lua_type(L, idx)) { case LUA_TNUMBER: return sqlite3_bind_double(stmt, bidx, lua_tonumber(L, idx)); case LUA_TBOOLEAN: return sqlite3_bind_int(stmt, bidx, lua_toboolean(L, idx) ? 1 : 0); case LUA_TSTRING: return sqlite3_bind_text(stmt, bidx, lua_tostring(L, idx), -1, SQLITE_TRANSIENT); default: warn("sqlite3: unable to bind Lua value (type %s)", lua_typename(L, lua_type(L, idx))); break; } return SQLITE_OK; /* ignore invalid types */ } static gint luaH_sqlite3_do_exec(lua_State *L, sqlite3_stmt *stmt) { gint ret = sqlite3_step(stmt), rows = 0, ncol; /* determine if this statement returns rows */ if (ret == SQLITE_DONE || ret == SQLITE_ROW) { if ((ncol = sqlite3_column_count(stmt))) /* user will expect table even if SQLITE_DONE */ lua_newtable(L); else /* statement doesn't return rows */ lua_pushnil(L); } check_next_step: switch (ret) { case SQLITE_DONE: goto exec_done; case SQLITE_ROW: lua_newtable(L); for (gint i = 0; i < ncol; i++) { /* push column name */ lua_pushstring(L, sqlite3_column_name(stmt, i)); /* push column value */ switch (sqlite3_column_type(stmt, i)) { case SQLITE_INTEGER: case SQLITE_FLOAT: lua_pushnumber(L, sqlite3_column_double(stmt, i)); lua_rawset(L, -3); break; case SQLITE_BLOB: case SQLITE_TEXT: lua_pushstring(L, sqlite3_column_blob(stmt, i)); lua_rawset(L, -3); break; case SQLITE_NULL: default: lua_pop(L, 1); break; } } lua_rawseti(L, -2, ++rows); break; /* there was an error */ default: return -1; } /* process next row (or check if done) */ ret = sqlite3_step(stmt); goto check_next_step; exec_done: return 1; } static gint luaH_sqlite3_exec(lua_State *L) { sqlite3_t *sqlite = luaH_checksqlite3(L, 1); luaH_sqlite3_checkopen(L, sqlite); /* get SQL query */ const gchar *sql = luaL_checkstring(L, 2), *tail; /* check type before we prepare statement */ if (!lua_isnoneornil(L, 3)) luaH_checktable(L, 3); gint top = lua_gettop(L), ret = 0; /* compile SQL statement */ sqlite3_stmt *stmt; next_statement: if (sqlite3_prepare_v2(sqlite->db, sql, -1, &stmt, &tail)) { lua_pushfstring(L, "sqlite3: statement compilation failed (%s)", sqlite3_errmsg(sqlite->db)); sqlite3_finalize(stmt); lua_error(L); } else if (!stmt) return 0; /* is there values to bind to this statement? */ if (!lua_isnoneornil(L, 3)) { /* iterate through table and bind values to the compiled statement */ lua_pushnil(L); gint idx; while (lua_next(L, 3)) { /* check valid parameter index */ if ((idx = luaH_param_index(L, stmt, -2)) == 0) { lua_pop(L, 1); continue; } /* bind value at index */ ret = luaH_bind_value(L, stmt, idx, -1); /* check for sqlite3_bind_* error */ if (!(ret == SQLITE_OK || ret == SQLITE_RANGE)) { lua_pushfstring(L, "sqlite3: sqlite3_bind_* failed (%s)", sqlite3_errmsg(sqlite->db)); sqlite3_finalize(stmt); lua_error(L); } /* pop value */ lua_pop(L, 1); } } ret = luaH_sqlite3_do_exec(L, stmt); sqlite3_finalize(stmt); /* check for error */ if (ret == -1) { lua_pushfstring(L, "sqlite3: exec error (%s)", sqlite3_errmsg(sqlite->db)); lua_error(L); } /* the sqlite3_prepare_*() functions only compile the first statement * in the input string. If there are more `tail` points to the first * character of the next statement (valid or not). */ if (tail && *tail) { sql = tail; lua_settop(L, top); goto next_statement; } return 1; } static gint luaH_sqlite3_stmt_exec(lua_State *L) { sqlite3_stmt_t *stmt = luaH_checkstmtud(L, 1); sqlite3_t *sqlite = stmt->sqlite; luaH_sqlite3_checkopen(L, sqlite); /* reset prepared statement back to original state */ sqlite3_reset(stmt->stmt); gint ret; /* is there values to bind to this statement? */ if (!lua_isnoneornil(L, 2)) { luaH_checktable(L, 2); /* clear bound values */ sqlite3_clear_bindings(stmt->stmt); /* iterate through table and bind values to the compiled statement */ lua_pushnil(L); gint idx; while (lua_next(L, 2)) { /* check valid parameter index */ if ((idx = luaH_param_index(L, stmt->stmt, -2)) == 0) { lua_pop(L, 1); continue; } /* bind value at index */ ret = luaH_bind_value(L, stmt->stmt, idx, -1); /* check for sqlite3_bind_* error */ if (!(ret == SQLITE_OK || ret == SQLITE_RANGE)) { lua_pushfstring(L, "sqlite3: sqlite3_bind_* failed (%s)", sqlite3_errmsg(sqlite->db)); sqlite3_finalize(stmt->stmt); lua_error(L); } /* pop value */ lua_pop(L, 1); } } ret = luaH_sqlite3_do_exec(L, stmt->stmt); /* check for error */ if (ret == -1) { lua_pushfstring(L, "sqlite3: exec error (%s)", sqlite3_errmsg(sqlite->db)); lua_error(L); } return 1; } static gint luaH_sqlite3_new(lua_State *L) { luaH_class_new(L, &sqlite3_class); sqlite3_t *sqlite = luaH_checksqlite3(L, -1); if (!sqlite->db) { lua_pushliteral(L, "sqlite3: database not opened, forgot filename?"); lua_error(L); } return 1; } void sqlite3_class_setup(lua_State *L) { static const struct luaL_reg sqlite3_methods[] = { LUA_CLASS_METHODS(sqlite3) { "__call", luaH_sqlite3_new }, { NULL, NULL }, }; static const struct luaL_reg sqlite3_meta[] = { LUA_OBJECT_META(sqlite3) LUA_CLASS_META { "exec", luaH_sqlite3_exec }, { "close", luaH_sqlite3_close }, { "compile", luaH_sqlite3_compile }, { "changes", luaH_sqlite3_changes }, { "__gc", luaH_sqlite3_gc }, { NULL, NULL }, }; luaH_class_setup(L, &sqlite3_class, "sqlite3", (lua_class_allocator_t) sqlite3_new, NULL, NULL, sqlite3_methods, sqlite3_meta); luaH_class_add_property(&sqlite3_class, L_TK_FILENAME, (lua_class_propfunc_t) luaH_sqlite3_set_filename, (lua_class_propfunc_t) luaH_sqlite3_get_filename, NULL); static const struct luaL_reg sqlite3_stmt_meta[] = { { "exec", luaH_sqlite3_stmt_exec }, { "__gc", luaH_sqlite3_stmt_gc }, { NULL, NULL }, }; luaH_class_setup(L, &sqlite3_stmt_class, "sqlite3::statement", NULL, NULL, NULL, NULL, sqlite3_stmt_meta); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/sqlite3.h000066400000000000000000000017541202446317700164050ustar00rootroot00000000000000/* * clib/sqlite3.h - luakit sqlite3 wrapper * * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef LUAKIT_CLIB_SQLITE3_H #define LUAKIT_CLIB_SQLITE3_H #include void sqlite3_class_setup(lua_State*); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/timer.c000066400000000000000000000102031202446317700161210ustar00rootroot00000000000000/* * timer.c - Simple timer class * * Copyright © 2010 Fabian Streitel * Copyright © 2010 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "clib/timer.h" #include "common/luaobject.h" #include "globalconf.h" #include "luah.h" #include typedef struct { LUA_OBJECT_HEADER gpointer ref; int id; int interval; } ltimer_t; static lua_class_t timer_class; LUA_OBJECT_FUNCS(timer_class, ltimer_t, timer) #define TIMER_STOPPED -1 #define luaH_checktimer(L, idx) luaH_checkudata(L, idx, &(timer_class)) static void luaH_timer_destroy(lua_State *L, ltimer_t *timer) { GSource *source = g_main_context_find_source_by_id(NULL, timer->id); if (source != NULL) g_source_destroy(source); /* allow timer to be garbage collected */ luaH_object_unref(L, timer->ref); timer->ref = NULL; timer->id = TIMER_STOPPED; } static gboolean timer_handle_timeout(gpointer data) { ltimer_t *timer = (ltimer_t *) data; luaH_object_push(globalconf.L, timer->ref); luaH_object_emit_signal(globalconf.L, -1, "timeout", 1, 0); return TRUE; } static int luaH_timer_new(lua_State *L) { luaH_class_new(L, &timer_class); ltimer_t *timer = luaH_checktimer(L, -1); timer->id = TIMER_STOPPED; return 1; } static int luaH_timer_start(lua_State *L) { ltimer_t *timer = luaH_checktimer(L, 1); if (!timer->interval) luaL_error(L, "interval not set"); if (timer->id == TIMER_STOPPED) { /* ensure timer isn't collected while running */ timer->ref = luaH_object_ref(L, 1); timer->id = g_timeout_add(timer->interval, timer_handle_timeout, timer); } else luaH_warn(L, "timer already started"); return 0; } static int luaH_timer_stop(lua_State *L) { ltimer_t *timer = luaH_checktimer(L, 1); if (timer->id == TIMER_STOPPED) luaH_warn(L, "timer already stopped"); else luaH_timer_destroy(L, timer); return 0; } static int luaH_timer_set_interval(lua_State *L, ltimer_t *timer) { timer->interval = luaL_checkint(L, -1); return 0; } static int luaH_timer_get_interval(lua_State *L, ltimer_t *timer) { lua_pushinteger(L, timer->interval); return 1; } static int luaH_timer_get_started(lua_State *L, ltimer_t *timer) { lua_pushboolean(L, (timer->id != TIMER_STOPPED)); return 1; } void timer_class_setup(lua_State *L) { static const struct luaL_reg timer_methods[] = { LUA_CLASS_METHODS(timer) { "__call", luaH_timer_new }, { NULL, NULL } }; static const struct luaL_reg timer_meta[] = { LUA_OBJECT_META(timer) LUA_CLASS_META { "start", luaH_timer_start }, { "stop", luaH_timer_stop }, { "__gc", luaH_object_gc }, { NULL, NULL }, }; luaH_class_setup(L, &timer_class, "timer", (lua_class_allocator_t) timer_new, luaH_class_index_miss_property, luaH_class_newindex_miss_property, timer_methods, timer_meta); luaH_class_add_property(&timer_class, L_TK_INTERVAL, (lua_class_propfunc_t) luaH_timer_set_interval, (lua_class_propfunc_t) luaH_timer_get_interval, (lua_class_propfunc_t) luaH_timer_set_interval); luaH_class_add_property(&timer_class, L_TK_STARTED, NULL, (lua_class_propfunc_t) luaH_timer_get_started, NULL); } #undef luaH_checktimer // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/timer.h000066400000000000000000000017341202446317700161370ustar00rootroot00000000000000/* * timer.h - Timer class header * * Copyright © 2010 Fabian Streitel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef LUAKIT_CLIB_TIMER_H #define LUAKIT_CLIB_TIMER_H #include void timer_class_setup(lua_State *); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/unique.c000066400000000000000000000067421202446317700163240ustar00rootroot00000000000000/* * clib/unique.c - libunique bindings for writing single instance * applications * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #if WITH_UNIQUE #include "clib/unique.h" #include "luah.h" #include #include /* setup unique module signals */ lua_class_t unique_class; LUA_CLASS_FUNCS(unique, unique_class); static UniqueApp *application = NULL; #define MESSAGE_ID (1) #define PING_ID (2) static UniqueResponse message_cb(UniqueApp* UNUSED(a), gint id, UniqueMessageData *message_data, guint UNUSED(time), lua_State *L) { if (id == MESSAGE_ID && message_data) { gchar *text = unique_message_data_get_text(message_data); lua_pushstring(L, text); g_free(text); GdkScreen *screen = unique_message_data_get_screen(message_data); lua_pushlightuserdata(L, screen); signal_object_emit(L, unique_class.signals, "message", 2, 0); } return UNIQUE_RESPONSE_OK; } static gint luaH_unique_new(lua_State *L) { if (application) luaL_error(L, "unique app already setup"); const gchar *name = luaL_checkstring(L, 1); application = unique_app_new_with_commands(name, NULL, "message", MESSAGE_ID, "ping", PING_ID, NULL); g_signal_connect(G_OBJECT(application), "message-received", G_CALLBACK(message_cb), L); return 0; } static gint luaH_unique_is_running(lua_State *L) { if (!application) luaL_error(L, "unique app not setup"); gboolean running = unique_app_is_running(application); /* Double-check instance is running, found unique_app_is_running * returning TRUE when luakit wasn't running on some systems. */ if (running) running = (unique_app_send_message(application, PING_ID, NULL) == UNIQUE_RESPONSE_OK); lua_pushboolean(L, running); return 1; } static gint luaH_unique_send_message(lua_State *L) { if (!application) luaL_error(L, "unique app not setup"); if (!unique_app_is_running(application)) luaL_error(L, "no other instances running"); const gchar *text = luaL_checkstring(L, 1); UniqueMessageData *data = unique_message_data_new(); unique_message_data_set_text(data, text, -1); unique_app_send_message(application, MESSAGE_ID, data); unique_message_data_free(data); return 0; } void unique_lib_setup(lua_State *L) { static const struct luaL_reg unique_lib[] = { LUA_CLASS_METHODS(unique) { "new", luaH_unique_new }, { "send_message", luaH_unique_send_message }, { "is_running", luaH_unique_is_running }, { NULL, NULL } }; /* create signals array */ unique_class.signals = signal_new(); /* export unique lib */ luaH_openlib(L, "unique", unique_lib, unique_lib); } #endif /* #if WITH_UNIQUE */ // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/unique.h000066400000000000000000000020241202446317700163160ustar00rootroot00000000000000/* * clib/unique.c - libunique bindings for writing single instance * applications * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #if WITH_UNIQUE #ifndef LUAKIT_CLIB_UNIQUE_H #define LUAKIT_CLIB_UNIQUE_H #include void unique_lib_setup(lua_State*); #endif /* #if LUAKIT_CLIB_UNIQUE_H */ #endif /* #if WITH_UNIQUE */ // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/widget.c000066400000000000000000000121761202446317700162770ustar00rootroot00000000000000/* * clib/widget.c - widget managing * * Copyright © 2010 Mason Larobina * Copyright © 2007-2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clib/widget.h" widget_info_t widgets_list[] = { { L_TK_ENTRY, "entry", widget_entry }, { L_TK_EVENTBOX, "eventbox", widget_eventbox }, { L_TK_HBOX, "hbox", widget_box }, { L_TK_HPANED, "hpaned", widget_paned }, { L_TK_LABEL, "label", widget_label }, { L_TK_NOTEBOOK, "notebook", widget_notebook }, { L_TK_SOCKET, "socket", widget_socket }, { L_TK_VBOX, "vbox", widget_box }, { L_TK_VPANED, "vpaned", widget_paned }, { L_TK_WEBVIEW, "webview", widget_webview }, { L_TK_WINDOW, "window", widget_window }, }; LUA_OBJECT_FUNCS(widget_class, widget_t, widget); /** Collect a widget structure. * \param L The Lua VM state. * \return 0 */ static gint luaH_widget_gc(lua_State *L) { widget_t *w = luaH_checkudata(L, 1, &widget_class); debug("collecting widget at %p of type '%s'", w, w->info->name); if(w->destructor) w->destructor(w); return luaH_object_gc(L); } /** Create a new widget. * \param L The Lua VM state. * * \luastack * \lparam A table with at least a type value. * \lreturn A brand new widget. */ static gint luaH_widget_new(lua_State *L) { luaH_class_new(L, &widget_class); widget_t *w = luaH_checkudata(L, -1, &widget_class); /* save ref to the lua class instance */ lua_pushvalue(L, -1); w->ref = luaH_object_ref_class(L, -1, &widget_class); return 1; } /** Generic widget. * \param L The Lua VM state. * \return The number of elements pushed on stack. * \luastack * \lfield visible The widget visibility. * \lfield mouse_enter A function to execute when the mouse enter the widget. * \lfield mouse_leave A function to execute when the mouse leave the widget. */ static gint luaH_widget_index(lua_State *L) { const char *prop = luaL_checkstring(L, 2); luakit_token_t token = l_tokenize(prop); /* Try standard method */ if(luaH_class_index(L)) return 1; /* Then call special widget index */ widget_t *widget = luaH_checkudata(L, 1, &widget_class); return widget->index ? widget->index(L, widget, token) : 0; } /** Generic widget newindex. * \param L The Lua VM state. * \return The number of elements pushed on stack. */ static gint luaH_widget_newindex(lua_State *L) { const char *prop = luaL_checkstring(L, 2); luakit_token_t token = l_tokenize(prop); /* Try standard method */ luaH_class_newindex(L); /* Then call special widget newindex */ widget_t *widget = luaH_checkudata(L, 1, &widget_class); return widget->newindex ? widget->newindex(L, widget, token) : 0; } static gint luaH_widget_set_type(lua_State *L, widget_t *w) { if (w->info) luaL_error(L, "widget is already of type: %s", w->info->name); const gchar *type = luaL_checkstring(L, -1); luakit_token_t tok = l_tokenize(type); widget_info_t *winfo; for (guint i = 0; i < LENGTH(widgets_list); i++) { if (widgets_list[i].tok != tok) continue; winfo = &widgets_list[i]; w->info = winfo; winfo->wc(w, tok); /* store pointer to lua widget struct in gobject data */ g_object_set_data(G_OBJECT(w->widget), GOBJECT_LUAKIT_WIDGET_DATA_KEY, (gpointer)w); luaH_object_emit_signal(L, -3, "init", 0, 0); return 0; } luaL_error(L, "unknown widget type: %s", type); return 0; } static gint luaH_widget_get_type(lua_State *L, widget_t *w) { if (!w->info) return 0; lua_pushstring(L, w->info->name); return 1; } void widget_class_setup(lua_State *L) { static const struct luaL_reg widget_methods[] = { LUA_CLASS_METHODS(widget) { "__call", luaH_widget_new }, { NULL, NULL } }; static const struct luaL_reg widget_meta[] = { LUA_OBJECT_META(widget) { "__index", luaH_widget_index }, { "__newindex", luaH_widget_newindex }, { "__gc", luaH_widget_gc }, { NULL, NULL } }; luaH_class_setup(L, &widget_class, "widget", (lua_class_allocator_t) widget_new, NULL, NULL, widget_methods, widget_meta); luaH_class_add_property(&widget_class, L_TK_TYPE, (lua_class_propfunc_t) luaH_widget_set_type, (lua_class_propfunc_t) luaH_widget_get_type, NULL); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/widget.h000066400000000000000000000054641202446317700163060ustar00rootroot00000000000000/* * clib/widget.h - widget managing header * * Copyright © 2010 Mason Larobina * Copyright © 2007-2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_CLIB_WIDGET_H #define LUAKIT_CLIB_WIDGET_H typedef struct widget_t widget_t; #include "common/util.h" #include "common/luaclass.h" #include "common/luaobject.h" #include "luah.h" #include #define GOBJECT_LUAKIT_WIDGET_DATA_KEY "luakit_widget_data" #define GOBJECT_TO_LUAKIT_WIDGET(gtk_widget) ((widget_t*)g_object_get_data(G_OBJECT(gtk_widget), \ GOBJECT_LUAKIT_WIDGET_DATA_KEY)) typedef widget_t *(widget_constructor_t)(widget_t *, luakit_token_t); typedef void (widget_destructor_t)(widget_t *); widget_constructor_t widget_box; widget_constructor_t widget_entry; widget_constructor_t widget_eventbox; widget_constructor_t widget_label; widget_constructor_t widget_notebook; widget_constructor_t widget_paned; widget_constructor_t widget_webview; widget_constructor_t widget_window; widget_constructor_t widget_socket; typedef const struct { luakit_token_t tok; const gchar *name; widget_constructor_t *wc; } widget_info_t; /* Widget */ struct widget_t { LUA_OBJECT_HEADER /* Widget type information */ widget_info_t *info; /* Widget destructor */ widget_destructor_t *destructor; /* Index function */ gint (*index)(lua_State *, widget_t *, luakit_token_t); /* Newindex function */ gint (*newindex)(lua_State *, widget_t *, luakit_token_t); /* Lua object ref */ gpointer ref; /* Main gtk widget */ GtkWidget *widget; /* Misc private data */ gpointer data; }; lua_class_t widget_class; void widget_class_setup(lua_State *); static inline widget_t* luaH_checkwidget(lua_State *L, gint udx) { widget_t *w = luaH_checkudata(L, udx, &widget_class); if (!w->widget) luaL_argerror(L, udx, "using destroyed widget"); return w; } static inline widget_t* luaH_checkwidgetornil(lua_State *L, gint udx) { if (lua_isnil(L, udx)) return NULL; return luaH_checkwidget(L, udx); } #define luaH_towidget(L, udx) luaH_toudata(L, udx, &widget_class) #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/xdg.c000066400000000000000000000036441202446317700155760ustar00rootroot00000000000000/* * clib/xdg.c - XDG Base Directory Specification paths * * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "clib/xdg.h" #include "luah.h" static gint luaH_xdg_index(lua_State *L) { if (!lua_isstring(L, 2)) /* ignore non-string indexes */ return 0; switch(l_tokenize(lua_tostring(L, 2))) { PS_CASE(CACHE_DIR, g_get_user_cache_dir()) PS_CASE(CONFIG_DIR, g_get_user_config_dir()) PS_CASE(DATA_DIR, g_get_user_data_dir()) #define UD_CASE(TOK) \ case L_TK_##TOK##_DIR: \ lua_pushstring(L, g_get_user_special_dir(G_USER_DIRECTORY_##TOK)); \ return 1; UD_CASE(DESKTOP) UD_CASE(DOCUMENTS) UD_CASE(DOWNLOAD) UD_CASE(MUSIC) UD_CASE(PICTURES) UD_CASE(PUBLIC_SHARE) UD_CASE(TEMPLATES) UD_CASE(VIDEOS) default: break; } return 0; } void xdg_lib_setup(lua_State *L) { static const struct luaL_reg xdg_lib[] = { { "__index", luaH_xdg_index }, { NULL, NULL }, }; luaH_openlib(L, "xdg", xdg_lib, xdg_lib); } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/clib/xdg.h000066400000000000000000000017521202446317700156010ustar00rootroot00000000000000/* * clib/xdg.h - XDG Base Directory Specification paths * * Copyright © 2011 Mason Larobina * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #ifndef LUAKIT_CLIB_XDG_H #define LUAKIT_CLIB_XDG_H #include void xdg_lib_setup(lua_State*); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/000077500000000000000000000000001202446317700152205ustar00rootroot00000000000000luakit-2012.09.13-r1/common/luaclass.c000066400000000000000000000252211202446317700171750ustar00rootroot00000000000000/* * luaclass.c - useful functions for handling Lua classes * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/luaclass.h" #include "common/luaobject.h" #include struct lua_class_property { /** Callback function called when the property is found in object creation. */ lua_class_propfunc_t new; /** Callback function called when the property is found in object __index. */ lua_class_propfunc_t index; /** Callback function called when the property is found in object __newindex. */ lua_class_propfunc_t newindex; }; static GPtrArray *luaH_classes = NULL; /* Convert a object to a udata if possible. * `ud` is the index. * `class` is the wanted class. * Returns a pointer to the object, NULL otherwise. */ gpointer luaH_toudata(lua_State *L, gint ud, lua_class_t *class) { gpointer p = lua_touserdata(L, ud); if(p) /* value is a userdata? */ if(lua_getmetatable(L, ud)) /* does it have a metatable? */ { lua_pushlightuserdata(L, class); lua_rawget(L, LUA_REGISTRYINDEX); if(!lua_rawequal(L, -1, -2)) /* does it have the correct mt? */ p = NULL; lua_pop(L, 2); /* remove both metatables */ } return p; } /* Check for a udata class. * `ud` is the object index on the stack. * `class` is the wanted class. * Returns a pointer to the wanted class. */ gpointer luaH_checkudata(lua_State *L, gint ud, lua_class_t *class) { gpointer p = luaH_toudata(L, ud, class); if(!p) luaL_typerror(L, ud, class->name); return p; } /* Get an object lua_class. * `idx` of the index of the object on the stack. */ lua_class_t * luaH_class_get(lua_State *L, gint idx) { gint type = lua_type(L, idx); lua_class_t *class; if(type == LUA_TUSERDATA && luaH_classes) for (guint i = 0; i < luaH_classes->len; i++) { class = luaH_classes->pdata[i]; if(luaH_toudata(L, idx, class)) return class; } return NULL; } /** Enhanced version of lua_typename that recognizes setup Lua classes. * \param L The Lua VM state. * \param idx The index of the object on the stack. */ const gchar * luaH_typename(lua_State *L, gint idx) { gint type = lua_type(L, idx); if(type == LUA_TUSERDATA) { lua_class_t *lua_class = luaH_class_get(L, idx); if(lua_class) return lua_class->name; } return lua_typename(L, type); } void luaH_openlib(lua_State *L, const gchar *name, const struct luaL_reg methods[], const struct luaL_reg meta[]) { luaL_newmetatable(L, name); /* 1 */ lua_pushvalue(L, -1); /* dup metatable 2 */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable 1 */ luaL_register(L, NULL, meta); /* 1 */ luaL_register(L, name, methods); /* 2 */ lua_pushvalue(L, -1); /* dup self as metatable 3 */ lua_setmetatable(L, -2); /* set self as metatable 2 */ lua_pop(L, 2); } void luaH_class_add_property(lua_class_t *lua_class, luakit_token_t token, lua_class_propfunc_t cb_new, lua_class_propfunc_t cb_index, lua_class_propfunc_t cb_newindex) { lua_class_property_t *prop; g_assert(token != L_TK_UNKNOWN); prop = g_new0(lua_class_property_t, 1); /* populate property */ prop->new = cb_new; prop->index = cb_index; prop->newindex = cb_newindex; /* add property to class properties tree */ g_hash_table_insert((GHashTable*) lua_class->properties, (gpointer) token, prop); } void luaH_class_setup(lua_State *L, lua_class_t *class, const gchar *name, lua_class_allocator_t allocator, lua_class_propfunc_t index_miss_property, lua_class_propfunc_t newindex_miss_property, const struct luaL_reg methods[], const struct luaL_reg meta[]) { /* Create the metatable */ lua_newtable(L); /* Register it with class pointer as key in the registry */ lua_pushlightuserdata(L, class); /* Duplicate the metatable */ lua_pushvalue(L, -2); lua_rawset(L, LUA_REGISTRYINDEX); lua_pushvalue(L, -1); /* dup metatable 2 */ lua_setfield(L, -2, "__index"); /* metatable.__index = metatable 1 */ luaL_register(L, NULL, meta); /* 1 */ if (methods) { luaL_register(L, name, methods); /* 2 */ lua_pushvalue(L, -1); /* dup self as metatable 3 */ lua_setmetatable(L, -2); /* set self as metatable 2 */ lua_pop(L, 2); } else lua_pop(L, 1); class->allocator = allocator; class->name = name; class->index_miss_property = index_miss_property; class->newindex_miss_property = newindex_miss_property; class->signals = signal_new(); class->properties = (lua_class_property_array_t*) g_hash_table_new( g_direct_hash, g_direct_equal); if (!luaH_classes) luaH_classes = g_ptr_array_new(); g_ptr_array_add(luaH_classes, class); } void luaH_class_add_signal(lua_State *L, lua_class_t *lua_class, const gchar *name, gint ud) { luaH_checkfunction(L, ud); if (globalconf.verbose) { gchar *origin = luaH_callerinfo(L); debug("add " ANSI_COLOR_BLUE "\"%s\"" ANSI_COLOR_RESET " on %p from " ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET, name, lua_class, origin); g_free(origin); } signal_add(lua_class->signals, name, luaH_object_ref(L, ud)); } void luaH_class_remove_signal(lua_State *L, lua_class_t *lua_class, const gchar *name, gint ud) { luaH_checkfunction(L, ud); gpointer ref = (gpointer) lua_topointer(L, ud); signal_remove(lua_class->signals, name, ref); luaH_object_unref(L, (gpointer) ref); lua_remove(L, ud); } gint luaH_class_emit_signal(lua_State *L, lua_class_t *lua_class, const gchar *name, gint nargs, gint nret) { return signal_object_emit(L, lua_class->signals, name, nargs, nret); } gint luaH_class_property_signal(lua_State *L, lua_class_t *lua_class, luakit_token_t tok) { gchar *signame = g_strdup_printf("property::%s", token_tostring(tok)); signal_object_emit(L, lua_class->signals, signame, 0, 0); g_free(signame); return 0; } /* Try to use the metatable of an object. * `idxobj` is the index of the object. * `idxfield` is the index of the field (attribute) to get. * Returns the number of element pushed on stack. */ gint luaH_usemetatable(lua_State *L, gint idxobj, gint idxfield) { /* Get metatable of the object. */ lua_getmetatable(L, idxobj); /* Get the field */ lua_pushvalue(L, idxfield); lua_rawget(L, -2); /* Do we have a field like that? */ if(!lua_isnil(L, -1)) { /* Yes, so return it! */ lua_remove(L, -2); return 1; } /* No, so remove everything. */ lua_pop(L, 2); return 0; } /* Get a property of a object. * `lua_class` is the Lua class. * `fieldidx` is the index of the field name. * Return is the object property if found, NULL otherwise. */ static lua_class_property_t * luaH_class_property_get(lua_State *L, lua_class_t *lua_class, gint fieldidx) { const gchar *attr = luaL_checkstring(L, fieldidx); luakit_token_t token = l_tokenize(attr); return g_hash_table_lookup((GHashTable*) lua_class->properties, (gpointer) token); } /* Generic index meta function for objects. * Return the number of elements pushed on stack. */ gint luaH_class_index(lua_State *L) { /* Try to use metatable first. */ if(luaH_usemetatable(L, 1, 2)) return 1; lua_class_t *class = luaH_class_get(L, 1); lua_class_property_t *prop = luaH_class_property_get(L, class, 2); /* Property does exist and has an index callback */ if(prop) { if(prop->index) return prop->index(L, luaH_checkudata(L, 1, class)); } else { if(class->index_miss_property) return class->index_miss_property(L, luaH_checkudata(L, 1, class)); } return 0; } /* Generic newindex meta function for objects. * Returns the number of elements pushed on stack. */ gint luaH_class_newindex(lua_State *L) { /* Try to use metatable first. */ if(luaH_usemetatable(L, 1, 2)) return 1; lua_class_t *class = luaH_class_get(L, 1); lua_class_property_t *prop = luaH_class_property_get(L, class, 2); /* Property does exist and has a newindex callback */ if(prop) { if(prop->newindex) return prop->newindex(L, luaH_checkudata(L, 1, class)); } else { if(class->newindex_miss_property) return class->newindex_miss_property(L, luaH_checkudata(L, 1, class)); } return 0; } /* Generic constructor function for objects. * Returns the number of elements pushed on stack. */ gint luaH_class_new(lua_State *L, lua_class_t *lua_class) { /* Check we have a table that should contains some properties */ luaH_checktable(L, 2); /* Create a new object */ lua_object_t *object = lua_class->allocator(L); /* Push the first key before iterating */ lua_pushnil(L); /* Iterate over the property keys */ while(lua_next(L, 2)) { /* Check that the key is a string. * We cannot call tostring blindly or Lua will convert a key that is a * number TO A STRING, confusing lua_next() */ if(lua_isstring(L, -2)) { /* Lookup the property */ const char *attr = lua_tostring(L, -2); lua_class_property_t *prop = g_hash_table_lookup( (GHashTable*) lua_class->properties, (gpointer) l_tokenize(attr)); if(prop && prop->new) prop->new(L, object); } /* Remove value */ lua_pop(L, 1); } return 1; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/luaclass.h000066400000000000000000000117201202446317700172010ustar00rootroot00000000000000/* * luaclass.h - useful functions for handling Lua classes * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_LUACLASS_H #define LUAKIT_COMMON_LUACLASS_H #include "common/lualib.h" #include "common/signal.h" #include "common/tokenize.h" #include typedef struct lua_class_property lua_class_property_t; typedef GHashTable lua_class_property_array_t; #define LUA_OBJECT_HEADER \ signal_t *signals; /* Generic type for all objects. All Lua objects can be casted * to this type. */ typedef struct { LUA_OBJECT_HEADER } lua_object_t; typedef lua_object_t *(*lua_class_allocator_t)(lua_State *); typedef gint (*lua_class_propfunc_t)(lua_State *, lua_object_t *); typedef struct { /** Class name */ const gchar *name; /** Class signals */ signal_t *signals; /** Allocator for creating new objects of that class */ lua_class_allocator_t allocator; /** Class properties */ lua_class_property_array_t *properties; /** Function to call when a indexing an unknown property */ lua_class_propfunc_t index_miss_property; /** Function to call when a indexing an unknown property */ lua_class_propfunc_t newindex_miss_property; } lua_class_t; const gchar *luaH_typename(lua_State *, gint); lua_class_t *luaH_class_get(lua_State *, gint); void luaH_class_add_signal(lua_State *, lua_class_t *, const gchar *name, gint ud); void luaH_class_remove_signal(lua_State *, lua_class_t *, const gchar *name, gint ud); gint luaH_class_emit_signal(lua_State *, lua_class_t *, const gchar *name, gint nargs, gint nret); gint luaH_class_property_signal(lua_State *, lua_class_t *, luakit_token_t); void luaH_openlib(lua_State *, const gchar *, const struct luaL_reg[], const struct luaL_reg[]); void luaH_class_setup(lua_State *, lua_class_t *, const gchar *, lua_class_allocator_t, lua_class_propfunc_t, lua_class_propfunc_t, const struct luaL_reg[], const struct luaL_reg[]); void luaH_class_add_property(lua_class_t *, luakit_token_t token, lua_class_propfunc_t, lua_class_propfunc_t, lua_class_propfunc_t); gint luaH_usemetatable(lua_State *, gint, gint); gint luaH_class_index(lua_State *); gint luaH_class_newindex(lua_State *); gint luaH_class_new(lua_State *, lua_class_t *); gpointer luaH_checkudata(lua_State *, gint, lua_class_t *); gpointer luaH_toudata(lua_State *L, gint ud, lua_class_t *); static inline gpointer luaH_checkudataornil(lua_State *L, gint udx, lua_class_t *class) { if(lua_isnil(L, udx)) return NULL; return luaH_checkudata(L, udx, class); } #define LUA_CLASS_FUNCS(prefix, lua_class) \ static inline gint \ luaH_##prefix##_class_add_signal(lua_State *L) { \ luaH_class_add_signal(L, &(lua_class), luaL_checkstring(L, 1), 2); \ return 0; \ } \ \ static inline gint \ luaH_##prefix##_class_remove_signal(lua_State *L) { \ luaH_class_remove_signal(L, &(lua_class), luaL_checkstring(L, 1), 2); \ return 0; \ } \ \ static inline gint \ luaH_##prefix##_class_emit_signal(lua_State *L) { \ return luaH_class_emit_signal(L, &(lua_class), luaL_checkstring(L, 1), \ lua_gettop(L) - 1, LUA_MULTRET); \ } #define LUA_CLASS_METHODS(class) \ { "add_signal", luaH_##class##_class_add_signal }, \ { "remove_signal", luaH_##class##_class_remove_signal }, \ { "emit_signal", luaH_##class##_class_emit_signal }, #define LUA_CLASS_META \ { "__index", luaH_class_index }, \ { "__newindex", luaH_class_newindex }, #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/lualib.h000066400000000000000000000073221202446317700166450ustar00rootroot00000000000000/* * lualib.h - useful functions and type for Lua * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_LUALIB #define LUAKIT_COMMON_LUALIB #include #include #include #include #include "common/util.h" /** Lua function to call on dofuction() error */ lua_CFunction lualib_dofunction_on_error; #define luaH_checkfunction(L, n) do { \ if(!lua_isfunction(L, n)) \ luaL_typerror(L, n, "function"); \ } while(0) /** Dump the Lua stack. Useful for debugging. * \param L The Lua VM state. */ static inline void luaH_dumpstack(lua_State *L) { g_fprintf(stderr, "-------- Lua stack dump ---------\n"); for(int i = lua_gettop(L); i; i--) { int t = lua_type(L, i); switch (t) { case LUA_TSTRING: g_fprintf(stderr, "%d: string: `%s'\n", i, lua_tostring(L, i)); break; case LUA_TBOOLEAN: g_fprintf(stderr, "%d: bool: %s\n", i, lua_toboolean(L, i) ? "true" : "false"); break; case LUA_TNUMBER: g_fprintf(stderr, "%d: number: %g\n", i, lua_tonumber(L, i)); break; case LUA_TNIL: g_fprintf(stderr, "%d: nil\n", i); break; default: g_fprintf(stderr, "%d: %s\t#%d\t%p\n", i, lua_typename(L, t), (gint) lua_objlen(L, i), lua_topointer(L, i)); break; } } g_fprintf(stderr, "------- Lua stack dump end ------\n"); } /** Convert s stack index to positive. * \param L The Lua VM state. * \param ud The index. * \return A positive index. */ static inline gint luaH_absindex(lua_State *L, gint ud) { return (ud > 0 || ud <= LUA_REGISTRYINDEX) ? ud : lua_gettop(L) + ud + 1; } static inline gint luaH_dofunction_error(lua_State *L) { if(lualib_dofunction_on_error) return lualib_dofunction_on_error(L); return 0; } /** Execute an Lua function on top of the stack. * \param L The Lua stack. * \param nargs The number of arguments for the Lua function. * \param nret The number of returned value from the Lua function. * \return True on no error, false otherwise. */ static inline gboolean luaH_dofunction(lua_State *L, gint nargs, gint nret) { /* Move function before arguments */ lua_insert(L, - nargs - 1); /* Push error handling function */ lua_pushcfunction(L, luaH_dofunction_error); /* Move error handling function before args and function */ lua_insert(L, - nargs - 2); gint error_func_pos = lua_gettop(L) - nargs - 1; if(lua_pcall(L, nargs, nret, - nargs - 2)) { warn("%s", lua_tostring(L, -1)); /* Remove error function and error string */ lua_pop(L, 2); return FALSE; } /* Remove error function */ lua_remove(L, error_func_pos); return TRUE; } #define luaH_checktable(L, n) do { \ if(!lua_istable(L, n)) \ luaL_typerror(L, n, "table"); \ } while(0) #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/luaobject.c000066400000000000000000000313201202446317700173330ustar00rootroot00000000000000/* * luaobject.c - useful functions for handling Lua objects * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/luaobject.h" /* Setup the object system at startup. */ void luaH_object_setup(lua_State *L) { /* Push identification string */ lua_pushliteral(L, LUAKIT_OBJECT_REGISTRY_KEY); /* Create an empty table */ lua_newtable(L); /* Create an empty metatable */ lua_newtable(L); /* Set this empty table as the registry metatable. * It's used to store the number of reference on stored objects. */ lua_setmetatable(L, -2); /* Register table inside registry */ lua_rawset(L, LUA_REGISTRYINDEX); } /* Increment a object reference in its store table. * Removes the referenced object from the stack. * `tud` is the table index on the stack. * `oud` is the object index on the stack. * Returns a pointer to the object. */ gpointer luaH_object_incref(lua_State *L, gint tud, gint oud) { /* Get pointer value of the item */ gpointer p = (gpointer) lua_topointer(L, oud); /* Not reference able. */ if(!p) { lua_remove(L, oud); return NULL; } /* Push the pointer (key) */ lua_pushlightuserdata(L, p); /* Push the data (value) */ lua_pushvalue(L, oud < 0 ? oud - 1 : oud); /* table.lightudata = data */ lua_rawset(L, tud < 0 ? tud - 2 : tud); /* refcount++ */ /* Get the metatable */ lua_getmetatable(L, tud); /* Push the pointer (key) */ lua_pushlightuserdata(L, p); /* Get the number of references */ lua_rawget(L, -2); /* Get the number of references and increment it */ gint count = lua_tonumber(L, -1) + 1; lua_pop(L, 1); /* Push the pointer (key) */ lua_pushlightuserdata(L, p); /* Push count (value) */ lua_pushinteger(L, count); /* Set metatable[pointer] = count */ lua_rawset(L, -3); /* Pop metatable */ lua_pop(L, 1); /* Remove referenced item */ lua_remove(L, oud); return p; } /** Decrement a object reference in its store table. * `tud` is the table index on the stack. * `oud` is the object index on the stack. * Returns a pointer to the object. */ void luaH_object_decref(lua_State *L, gint tud, gpointer p) { if(!p) return; /* First, refcount-- */ /* Get the metatable */ lua_getmetatable(L, tud); /* Push the pointer (key) */ lua_pushlightuserdata(L, p); /* Get the number of references */ lua_rawget(L, -2); /* Get the number of references and decrement it */ gint count = lua_tonumber(L, -1) - 1; lua_pop(L, 1); /* Push the pointer (key) */ lua_pushlightuserdata(L, p); /* Hasn't the ref reached 0? */ if(count) lua_pushinteger(L, count); else /* Yup, delete it, set nil as value */ lua_pushnil(L); /* Set meta[pointer] = count/nil */ lua_rawset(L, -3); /* Pop metatable */ lua_pop(L, 1); /* Wait, no more ref? */ if(!count) { /* Yes? So remove it from table */ lua_pushlightuserdata(L, p); /* Push nil as value */ lua_pushnil(L); /* table[pointer] = nil */ lua_rawset(L, tud < 0 ? tud - 2 : tud); } } gint luaH_settype(lua_State *L, lua_class_t *lua_class) { lua_pushlightuserdata(L, lua_class); lua_rawget(L, LUA_REGISTRYINDEX); lua_setmetatable(L, -2); return 1; } /* Add a signal to an object. * `oud` is the object index on the stack. * `name` is the name of the signal. * `ud` is the index of function to call when signal is emitted. */ void luaH_object_add_signal(lua_State *L, gint oud, const gchar *name, gint ud) { luaH_checkfunction(L, ud); lua_object_t *obj = lua_touserdata(L, oud); if (globalconf.verbose) { gchar *origin = luaH_callerinfo(L); debug("add " ANSI_COLOR_BLUE "\"%s\"" ANSI_COLOR_RESET " on %p from " ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET, name, obj, origin); g_free(origin); } signal_add(obj->signals, name, luaH_object_ref_item(L, oud, ud)); } /* Remove a signal to an object. * `oud` is the object index on the stack. * `name` is the name of the signal. * `ud` is the index of function to call when signal is emitted. */ void luaH_object_remove_signal(lua_State *L, gint oud, const gchar *name, gint ud) { luaH_checkfunction(L, ud); lua_object_t *obj = lua_touserdata(L, oud); gpointer ref = (gpointer) lua_topointer(L, ud); signal_remove(obj->signals, name, ref); luaH_object_unref_item(L, oud, ref); lua_remove(L, ud); } /* Emit a signal from a signals array and return the results of the first * handler that returns something. * `signals` is the signals array. * `name` is the name of the signal. * `nargs` is the number of arguments to pass to the called functions. * `nret` is the number of return values this function pushes onto the stack. * A positive number means that any missing values will be padded with nil * and any superfluous values will be removed. * LUA_MULTRET means that any number of values is returned without any * adjustment. * 0 means that all return values are removed and that ALL handler functions are * executed. * Returns the number of return values pushed onto the stack. */ gint signal_object_emit(lua_State *L, signal_t *signals, const gchar *name, gint nargs, gint nret) { signal_array_t *sigfuncs = signal_lookup(signals, name); if (globalconf.verbose) { gchar *origin = luaH_callerinfo(L); debug("emit " ANSI_COLOR_BLUE "\"%s\"" ANSI_COLOR_RESET " on %p from " ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET " (%d args, %d nret)", name, signals, origin ? origin : "", nargs, nret); g_free(origin); } if (sigfuncs) { gint nbfunc = sigfuncs->len; luaL_checkstack(L, lua_gettop(L) + nbfunc + nargs + 1, "too much signal"); /* Push all functions and then execute, because this list can change * while executing funcs. */ for (gint i = 0; i < nbfunc; i++) { luaH_object_push(L, sigfuncs->pdata[i]); } for (gint i = 0; i < nbfunc; i++) { gint stacksize = lua_gettop(L); /* push all args */ for (gint j = 0; j < nargs; j++) lua_pushvalue(L, - nargs - nbfunc + i); /* push first function */ lua_pushvalue(L, - nargs - nbfunc + i); /* remove this first function */ lua_remove(L, - nargs - nbfunc - 1 + i); luaH_dofunction(L, nargs, LUA_MULTRET); gint ret = lua_gettop(L) - stacksize + 1; /* Note that only if nret && ret will the signal execution stop */ if (nret && ret) { /* remove all args and functions */ for (gint j = 0; j < nargs + nbfunc - i - 1; j++) { lua_remove(L, - ret - 1); } /* Adjust the number of results to match nret */ if (nret != LUA_MULTRET && ret != nret) { /* Pad with nils */ for (; ret < nret; ret++) lua_pushnil(L); /* Or truncate stack */ if (ret > nret) { lua_pop(L, ret - nret); ret = nret; } } /* Return the number of returned arguments */ return ret; } else if (nret == 0) { /* ignore all return values */ lua_pop(L, ret); } } } /* remove args */ lua_pop(L, nargs); return 0; } /* Emit a signal to an object. * `oud` is the object index on the stack. * `name` is the name of the signal. * `nargs` is the number of arguments to pass to the called functions. * `nret` is the number of return values this function pushes onto the stack. * A positive number means that any missing values will be padded with nil * and any superfluous values will be removed. * LUA_MULTRET means that any number of values is returned without any * adjustment. * 0 means that all return values are removed and that ALL handler functions are * executed. * Returns the number of return values pushed onto the stack. */ gint luaH_object_emit_signal(lua_State *L, gint oud, const gchar *name, gint nargs, gint nret) { gint ret, top, bot = lua_gettop(L) - nargs + 1; gint oud_abs = luaH_absindex(L, oud); lua_object_t *obj = lua_touserdata(L, oud); if (globalconf.verbose) { gchar *origin = luaH_callerinfo(L); debug("emit " ANSI_COLOR_BLUE "\"%s\"" ANSI_COLOR_RESET " on %p from " ANSI_COLOR_GREEN "%s" ANSI_COLOR_RESET " (%d args, %d nret)", name, obj, origin ? origin : "", nargs, nret); g_free(origin); } if(!obj) luaL_error(L, "trying to emit signal on non-object"); signal_array_t *sigfuncs = signal_lookup(obj->signals, name); if (sigfuncs) { guint nbfunc = sigfuncs->len; luaL_checkstack(L, lua_gettop(L) + nbfunc + nargs + 2, "too much signal"); /* Push all functions and then execute, because this list can change * while executing funcs. */ for (guint i = 0; i < nbfunc; i++) luaH_object_push_item(L, oud_abs, sigfuncs->pdata[i]); for (guint i = 0; i < nbfunc; i++) { /* push object */ lua_pushvalue(L, oud_abs); /* push all args */ for (gint j = 0; j < nargs; j++) lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* push first function */ lua_pushvalue(L, - nargs - nbfunc - 1 + i); /* remove this first function */ lua_remove(L, - nargs - nbfunc - 2 + i); top = lua_gettop(L) - 2 - nargs; luaH_dofunction(L, nargs + 1, LUA_MULTRET); ret = lua_gettop(L) - top; /* Note that only if nret && ret will the signal execution stop */ if (nret && ret) { /* Adjust the number of results to match nret (including 0) */ if (nret != LUA_MULTRET && ret != nret) { /* Pad with nils */ for (; ret < nret; ret++) lua_pushnil(L); /* Or truncate stack */ if (ret > nret) { lua_pop(L, ret - nret); ret = nret; } } /* Remove all signal functions and args from the stack */ for (gint i = bot; i <= top; i++) lua_remove(L, bot); /* Return the number of returned arguments */ return ret; } else if (nret == 0) { /* ignore all return values */ lua_pop(L, ret); } } } lua_pop(L, nargs); return 0; } gint luaH_object_property_signal(lua_State *L, gint oud, luakit_token_t tok) { gchar *signame = g_strdup_printf("property::%s", token_tostring(tok)); luaH_object_emit_signal(L, oud, signame, 0, 0); g_free(signame); return 0; } gint luaH_object_add_signal_simple(lua_State *L) { luaH_object_add_signal(L, 1, luaL_checkstring(L, 2), 3); return 0; } gint luaH_object_remove_signal_simple(lua_State *L) { luaH_object_remove_signal(L, 1, luaL_checkstring(L, 2), 3); return 0; } gint luaH_object_emit_signal_simple(lua_State *L) { return luaH_object_emit_signal(L, 1, luaL_checkstring(L, 2), lua_gettop(L) - 2, LUA_MULTRET); } gint luaH_object_tostring(lua_State *L) { lua_class_t *lua_class = luaH_class_get(L, 1); lua_pushfstring(L, "%s: %p", lua_class->name, luaH_checkudata(L, 1, lua_class)); return 1; } /** Garbage collect a Lua object. * \param L The Lua VM state. * \return The number of elements pushed on stack. */ gint luaH_object_gc(lua_State *L) { lua_object_t *item = lua_touserdata(L, 1); if (item->signals) signal_destroy(item->signals); return 0; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/luaobject.h000066400000000000000000000152511202446317700173450ustar00rootroot00000000000000/* * luaobject.h - useful functions for handling Lua objects * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_LUAOBJECT_H #define LUAKIT_COMMON_LUAOBJECT_H #include "common/luaclass.h" #include "common/lualib.h" #include "common/signal.h" #include "globalconf.h" gint luaH_settype(lua_State *L, lua_class_t *lua_class); void luaH_object_setup(lua_State *L); gpointer luaH_object_incref(lua_State *L, gint tud, gint oud); void luaH_object_decref(lua_State *L, gint tud, gpointer oud); /* Store an item in the environment table of an object. * Removes the stored object from the stack. * `ud` is the index of the object on the stack. * `iud` is the index of the item on the stack. * Return the item reference. */ static inline gpointer luaH_object_ref_item(lua_State *L, gint ud, gint iud) { /* Get the env table from the object */ lua_getfenv(L, ud); gpointer p = luaH_object_incref(L, -1, iud < 0 ? iud - 1 : iud); /* Remove env table */ lua_pop(L, 1); return p; } /* Unref an item from the environment table of an object. * `ud` is the index of the object on the stack. * `p` is the item. */ static inline void luaH_object_unref_item(lua_State *L, gint ud, gpointer p) { /* Get the env table from the object */ lua_getfenv(L, ud); /* Decrement */ luaH_object_decref(L, -1, p); /* Remove env table */ lua_pop(L, 1); } /* Push an object item on the stack. * `ud` is the object index on the stack. * `p` is the item pointer. * Returns the number of element pushed on stack. */ static inline gint luaH_object_push_item(lua_State *L, gint ud, gpointer p) { /* Get env table of the object */ lua_getfenv(L, ud); /* Push key */ lua_pushlightuserdata(L, p); /* Get env.pointer */ lua_rawget(L, -2); /* Remove env table */ lua_remove(L, -2); return 1; } static inline void luaH_object_registry_push(lua_State *L) { lua_pushliteral(L, LUAKIT_OBJECT_REGISTRY_KEY); lua_rawget(L, LUA_REGISTRYINDEX); } /* Reference an object and return a pointer to it. That only works with * userdata, table, thread or function. * Removes the referenced object from the stack. * `oud` is the object index on the stack. * Returns the object reference, or NULL if not referenceable. */ static inline gpointer luaH_object_ref(lua_State *L, gint oud) { luaH_object_registry_push(L); gpointer p = luaH_object_incref(L, -1, oud < 0 ? oud - 1 : oud); lua_pop(L, 1); return p; } /* Reference an object and return a pointer to it checking its type. That only * works with userdata. * `oud` is the object index on the stack. * `class` is the class of object expected * Return the object reference, or NULL if not referenceable. */ static inline gpointer luaH_object_ref_class(lua_State *L, gint oud, lua_class_t *class) { luaH_checkudata(L, oud, class); return luaH_object_ref(L, oud); } /* Unreference an object and return a pointer to it. That only works with * userdata, table, thread or function. * `oud` is the object index on the stack. */ static inline void luaH_object_unref(lua_State *L, gpointer p) { luaH_object_registry_push(L); luaH_object_decref(L, -1, p); lua_pop(L, 1); } /* Push a referenced object onto the stack. * `p` is the object to push. * Returns is the number of element pushed on stack. */ static inline gint luaH_object_push(lua_State *L, gpointer p) { luaH_object_registry_push(L); lua_pushlightuserdata(L, p); lua_rawget(L, -2); lua_remove(L, -2); return 1; } gint signal_object_emit(lua_State *, signal_t *signals, const gchar *name, gint nargs, gint nret); void luaH_object_add_signal(lua_State *L, gint oud, const gchar *name, gint ud); void luaH_object_remove_signal(lua_State *L, gint oud, const gchar *name , gint ud); gint luaH_object_emit_signal(lua_State *L, gint oud, const gchar *name, gint nargs, gint nret); gint luaH_object_add_signal_simple(lua_State *L); gint luaH_object_remove_signal_simple(lua_State *L); gint luaH_object_emit_signal_simple(lua_State *L); gint luaH_object_property_signal(lua_State *, gint, luakit_token_t); #define LUA_OBJECT_FUNCS(lua_class, type, prefix) \ LUA_CLASS_FUNCS(prefix, lua_class) \ static inline type * \ prefix##_new(lua_State *L) { \ type *p = lua_newuserdata(L, sizeof(type)); \ p_clear(p, 1); \ p->signals = signal_new(); \ luaH_settype(L, &(lua_class)); \ lua_newtable(L); \ lua_newtable(L); \ lua_setmetatable(L, -2); \ lua_setfenv(L, -2); \ lua_pushvalue(L, -1); \ luaH_class_emit_signal(L, &(lua_class), "new", 1, 0); \ return p; \ } #define OBJECT_EXPORT_PROPERTY(pfx, type, field) \ fieldtypeof(type, field) \ pfx##_get_##field(type *object) { \ return object->field; \ } #define LUA_OBJECT_EXPORT_PROPERTY(pfx, type, field, pusher) \ static gint \ luaH_##pfx##_get_##field(lua_State *L, type *object) { \ pusher(L, object->field); \ return 1; \ } gint luaH_object_tostring(lua_State *); gint luaH_object_gc(lua_State *); #define LUA_OBJECT_META(prefix) \ { "__tostring", luaH_object_tostring }, \ { "add_signal", luaH_object_add_signal_simple }, \ { "remove_signal", luaH_object_remove_signal_simple }, \ { "emit_signal", luaH_object_emit_signal_simple }, #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/property.c000066400000000000000000000103171202446317700172520ustar00rootroot00000000000000/* * common/property.c - GObject property set/get lua functions * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/property.h" #include "clib/soup/soup.h" #include static gint luaH_gobject_get(lua_State *L, property_t *p, GObject *object) { SoupURI *u; property_tmp_t tmp; #define TG_CASE(type, dest, pfunc) \ case type: \ g_object_get(object, p->name, &(dest), NULL); \ pfunc(L, dest); \ return 1; switch(p->type) { TG_CASE(BOOL, tmp.b, lua_pushboolean) TG_CASE(INT, tmp.i, lua_pushnumber) TG_CASE(FLOAT, tmp.f, lua_pushnumber) TG_CASE(DOUBLE, tmp.d, lua_pushnumber) case CHAR: g_object_get(object, p->name, &tmp.c, NULL); lua_pushstring(L, tmp.c); g_free(tmp.c); return 1; case URI: g_object_get(object, p->name, &u, NULL); tmp.c = u ? soup_uri_to_string(u, 0) : NULL; lua_pushstring(L, tmp.c); if (u) soup_uri_free(u); g_free(tmp.c); return 1; default: break; } /* unhandled property type */ g_assert_not_reached(); } static gboolean luaH_gobject_set(lua_State *L, property_t *p, gint vidx, GObject *object) { SoupURI *u; property_tmp_t tmp; size_t len; #define TS_CASE(type, cast, dest, cfunc) \ case type: \ dest = (cast)cfunc(L, vidx); \ g_object_set(object, p->name, dest, NULL); \ break; switch(p->type) { TS_CASE(BOOL, gboolean, tmp.b, luaH_checkboolean); TS_CASE(INT, gint, tmp.i, luaL_checknumber); TS_CASE(FLOAT, gfloat, tmp.f, luaL_checknumber); TS_CASE(DOUBLE, gdouble, tmp.d, luaL_checknumber); case CHAR: if (lua_isnil(L, vidx)) tmp.c = NULL; else tmp.c = (gchar*) luaL_checkstring(L, vidx); g_object_set(object, p->name, tmp.c, NULL); break; case URI: if (lua_isnil(L, vidx)) { g_object_set(object, p->name, NULL, NULL); break; } tmp.c = (gchar*) luaL_checklstring(L, vidx, &len); /* use http protocol if none specified */ if (!len || g_strrstr(tmp.c, "://")) tmp.c = g_strdup(tmp.c); else tmp.c = g_strdup_printf("http://%s", tmp.c); u = soup_uri_new(tmp.c); gboolean valid = !u || SOUP_URI_VALID_FOR_HTTP(u); if (valid) { g_object_set(object, p->name, u, NULL); g_free(tmp.c); } soup_uri_free(u); if (!valid) { lua_pushfstring(L, "invalid uri: %s", tmp.c); g_free(tmp.c); lua_error(L); } break; default: /* unhandled property type */ g_assert_not_reached(); } return TRUE; } gint luaH_gobject_index(lua_State *L, property_t *props, luakit_token_t tok, GObject *object) { for (property_t *p = props; p->tok; p++) if (p->tok == tok) return luaH_gobject_get(L, p, object); return 0; } gboolean luaH_gobject_newindex(lua_State *L, property_t *props, luakit_token_t tok, gint vidx, GObject *object) { for (property_t *p = props; p->tok; p++) { if (p->tok != tok) continue; if (p->writable) return luaH_gobject_set(L, p, vidx, object); else warn("read-only property: %s", p->name); break; } return FALSE; } luakit-2012.09.13-r1/common/property.h000066400000000000000000000026471202446317700172660ustar00rootroot00000000000000/* * common/property.h - GObject property set/get lua functions * * Copyright © 2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_PROPERTY_H #define LUAKIT_COMMON_PROPERTY_H #include #include #include "common/tokenize.h" typedef enum { BOOL, CHAR, DOUBLE, FLOAT, INT, URI, } property_value_t; typedef union { gchar *c; gboolean b; gdouble d; gfloat f; gint i; } property_tmp_t; typedef struct { luakit_token_t tok; const gchar *name; property_value_t type; gboolean writable; } property_t; gint luaH_gobject_index(lua_State *, property_t *, luakit_token_t, GObject *); gboolean luaH_gobject_newindex(lua_State *, property_t *, luakit_token_t, gint, GObject *); #endif luakit-2012.09.13-r1/common/signal.h000066400000000000000000000052411202446317700166500ustar00rootroot00000000000000/* * signal.h - Signal handling functions * * Copyright © 2010 Mason Larobina * Copyright © 2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_SIGNAL #define LUAKIT_COMMON_SIGNAL #include #include "common/util.h" typedef GTree signal_t; typedef GPtrArray signal_array_t; /* signals tree key compare function */ static inline gint signal_cmp(gconstpointer a, gconstpointer b, gpointer UNUSED(p)) { return g_strcmp0(a, b); } /* signals tree data destroy function */ static inline void signal_array_destroy(gpointer *sigfuncs) { g_ptr_array_free((GPtrArray*) sigfuncs, TRUE); } /* create binary search tree for fast signal array lookups */ static inline signal_t* signal_new(void) { return (signal_t*) g_tree_new_full((GCompareDataFunc) signal_cmp, NULL, (GDestroyNotify) g_free, (GDestroyNotify) signal_array_destroy); } /* destory signals tree */ static inline void signal_destroy(signal_t *signals) { g_tree_destroy((GTree*) signals); } static inline signal_array_t* signal_lookup(signal_t *signals, const gchar *name) { return (signal_array_t*) g_tree_lookup((GTree*) signals, (gpointer) name); } /* add a signal inside a signal array */ static inline void signal_add(signal_t *signals, const gchar *name, gpointer func) { signal_array_t *sigfuncs = signal_lookup(signals, name); if (!sigfuncs) { sigfuncs = (signal_array_t*) g_ptr_array_new(); g_tree_insert((GTree*) signals, (gpointer) g_strdup(name), sigfuncs); } g_ptr_array_add((GPtrArray*) sigfuncs, func); } /* remove a signal inside a signal array */ static inline void signal_remove(signal_t *signals, const gchar *name, gpointer func) { signal_array_t *sigfuncs = signal_lookup(signals, name); if (sigfuncs) { g_ptr_array_remove((GPtrArray*) sigfuncs, func); /* prune empty sigfuncs array from the tree */ if (!sigfuncs->len) g_tree_remove((GTree*) signals, (gpointer) name); } } #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/common/tokenize.list000066400000000000000000000046741202446317700177600ustar00rootroot00000000000000accept_language accept_language_auto accept_policy align append auto_load_images auto_resize_window auto_shrink_images bg bottom cache_dir can_go_back can_go_forward changes child children clear_search clipboard close_inspector config_dir confpath count current current_size cursive_font_family custom_encoding data_dir decorated default_encoding default_font_family default_font_size default_monospace_font_size desktop_dir destination destroy dev_paths documents_dir domain download_dir editable elapsed_time enable_caret_browsing enable_default_context_menu enable_developer_extras enable_dom_paste enable_file_access_from_file_uris enable_html5_database enable_html5_local_storage enable_java_applet enable_offline_web_application_cache enable_page_cache enable_plugins enable_private_browsing enable_scripts enable_site_specific_quirks enable_spatial_navigation enable_spell_checking enable_universal_access_from_file_uris enable_xss_auditor encoding end enforce_96_dpi entry error eval_js eventbox exec execpath expires fantasy_font_family fg filename focus focused font frames full_content_zoom fullscreen get_title go_back go_forward hbox hide history homogeneous hovered_uri hpaned http_only icon icon_uri id idle_timeout indexof insert inspector install_path interval javascript_can_access_clipboard javascript_can_open_windows_automatically label left loading loading_chrome load_string max_conns max_conns_per_host maximized mime_type minimum_font_size minimum_logical_font_size monospace_font_family music_dir name notebook nounique open pack pack1 pack2 padding path pictures_dir plugged position primary print_backgrounds progress proxy_uri public_share_dir register_function reload reload_bypass_cache remove reorder resizable_text_areas right sans_serif_font_family save_file screen scroll search secondary secure selectable selection select_region serif_font_family set_default_size set_title show show_border show_frame show_inspector show_scrollbars show_tabs socket spacing spawn spawn_sync spell_checking_languages ssl_ca_file ssl_strict ssl_trusted start started status stop suggested_filename switch tab_key_cycles_through_elements templates_dir text time timeout title top total_size transparent type urgency_hint uri uri_decode uri_encode user_agent user_stylesheet_uri value vbox verbose version videos_dir view_source visible vpaned webkit_user_agent_version webkit_version webview width window windows x xid xmax xpage_size y ymax ypage_size zoom_level zoom_step luakit-2012.09.13-r1/common/util.c000066400000000000000000000056101202446317700163430ustar00rootroot00000000000000/* * util.c - useful functions * * Copyright © 2010 Mason Larobina * Copyright © 2007-2008 Julien Danjou * Copyright © 2006 Pierre Habouzit * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/util.h" #include "globalconf.h" #include #include #include #include /* Print error and exit with EXIT_FAILURE code. */ void _fatal(gint line, const gchar *fct, const gchar *fmt, ...) { va_list ap; va_start(ap, fmt); gint atty = isatty(STDERR_FILENO); if (atty) g_fprintf(stderr, ANSI_COLOR_BG_RED); g_fprintf(stderr, "[%#12f] ", l_time() - globalconf.starttime); g_fprintf(stderr, "E: luakit: %s:%d: ", fct, line); g_vfprintf(stderr, fmt, ap); va_end(ap); if (atty) g_fprintf(stderr, ANSI_COLOR_RESET); g_fprintf(stderr, "\n"); exit(EXIT_FAILURE); } /* Print error message on stderr. */ void _warn(gint line, const gchar *fct, const gchar *fmt, ...) { va_list ap; va_start(ap, fmt); gint atty = isatty(STDERR_FILENO); if (atty) g_fprintf(stderr, ANSI_COLOR_RED); g_fprintf(stderr, "[%#12f] ", l_time() - globalconf.starttime); g_fprintf(stderr, "E: luakit: %s:%d: ", fct, line); g_vfprintf(stderr, fmt, ap); va_end(ap); if (atty) g_fprintf(stderr, ANSI_COLOR_RESET); g_fprintf(stderr, "\n"); } /* Print debug message on stderr. */ void _debug(gint line, const gchar *fct, const gchar *fmt, ...) { if (globalconf.verbose) { va_list ap; va_start(ap, fmt); g_fprintf(stderr, "[%#12f] ", l_time() - globalconf.starttime); g_fprintf(stderr, "D: luakit: %s:%d: ", fct, line); g_vfprintf(stderr, fmt, ap); va_end(ap); g_fprintf(stderr, "\n"); } } gboolean file_exists(const gchar *filename) { return (access(filename, F_OK) == 0); } /* Pretty-format calling function filename, function name & line number for * debugging purposes */ gchar* luaH_callerinfo(lua_State *L) { lua_Debug ar; /* get information about calling lua function */ if (lua_getstack(L, 1, &ar) && lua_getinfo(L, "Sln", &ar)) return g_strdup_printf("%s%s%s:%d", ar.short_src, ar.name ? ":" : "", ar.name ? ar.name : "", ar.currentline); return NULL; } luakit-2012.09.13-r1/common/util.h000066400000000000000000000057011202446317700163510ustar00rootroot00000000000000/* * util.h - useful functions * * Copyright © 2010 Mason Larobina * Copyright © 2007-2008 Julien Danjou * Copyright © 2006 Pierre Habouzit * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_COMMON_UTIL_H #define LUAKIT_COMMON_UTIL_H #include #include #include #include #include /* ANSI term color codes */ #define ANSI_COLOR_RESET "\x1b[0m" #define ANSI_COLOR_RED "\x1b[31m" #define ANSI_COLOR_GREEN "\x1b[32m" #define ANSI_COLOR_YELLOW "\x1b[33m" #define ANSI_COLOR_BLUE "\x1b[34m" #define ANSI_COLOR_MAGENTA "\x1b[35m" #define ANSI_COLOR_CYAN "\x1b[36m" #define ANSI_COLOR_BG_RED "\x1b[41m" /* Useful macros */ #define NONULL(x) (x ? x : "") #define LENGTH(x) sizeof(x)/sizeof((x)[0]) #ifdef UNUSED #elif defined(__GNUC__) # define UNUSED(x) UNUSED_ ## x __attribute__((unused)) #elif defined(__LCLINT__) # define UNUSED(x) /*@unused@*/ x #else # define UNUSED(x) x #endif /* stack pushing macros */ #define PB_CASE(t, b) case L_TK_##t: lua_pushboolean (L, b); return 1; #define PF_CASE(t, f) case L_TK_##t: lua_pushcfunction (L, f); return 1; #define PI_CASE(t, i) case L_TK_##t: lua_pushinteger (L, i); return 1; #define PN_CASE(t, n) case L_TK_##t: lua_pushnumber (L, n); return 1; #define PS_CASE(t, s) case L_TK_##t: lua_pushstring (L, s); return 1; #define fatal(string, ...) _fatal(__LINE__, __FUNCTION__, string, ##__VA_ARGS__) void _fatal(int, const gchar *, const gchar *, ...); #define warn(string, ...) _warn(__LINE__, __FUNCTION__, string, ##__VA_ARGS__) void _warn(int, const gchar *, const gchar *, ...); #define debug(string, ...) _debug(__LINE__, __FUNCTION__, string, ##__VA_ARGS__) void _debug(int, const gchar *, const gchar *, ...); /* A NULL resistant strlen. Unlike it's libc sibling, l_strlen returns a * ssize_t, and supports its argument being NULL. */ static inline ssize_t l_strlen(const gchar *s) { return s ? strlen(s) : 0; } static inline gdouble l_time() { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (tv.tv_usec / 1e6); } #define p_clear(p, count) ((void)memset((p), 0, sizeof(*(p)) * (count))) gboolean file_exists(const gchar*); void l_exec(const gchar*); gchar *luaH_callerinfo(lua_State*); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config.mk000066400000000000000000000055061202446317700155340ustar00rootroot00000000000000# === Luakit Makefile Configuration ========================================== # Compile/link options. CC ?= gcc CFLAGS += -std=gnu99 -ggdb -W -Wall -Wextra LDFLAGS += CPPFLAGS += # Get current luakit version. VERSION ?= $(shell ./build-utils/getversion.sh) CPPFLAGS += -DVERSION=\"$(VERSION)\" # === Paths ================================================================== PREFIX ?= /usr/local MANPREFIX ?= $(PREFIX)/share/man DOCDIR ?= $(PREFIX)/share/luakit/docs INSTALLDIR := $(DESTDIR)$(PREFIX) MANPREFIX := $(DESTDIR)$(MANPREFIX) DOCDIR := $(DESTDIR)$(DOCDIR) # Should luakit be built to load relative config paths (./lib ./config) ? # (Useful when running luakit from it's source directory, disable otherwise). ifneq ($(DEVELOPMENT_PATHS),0) CPPFLAGS += -DDEVELOPMENT_PATHS endif # === Platform specific ====================================================== uname_s := $(shell uname -s) # Mac OSX ifeq ($(uname_s),Darwin) LINKER_EXPORT_DYNAMIC = 0 endif # Some systems need the --export-dynamic linker option to load other # dynamically linked C Lua modules (for example lua-filesystem). ifneq ($(LINKER_EXPORT_DYNAMIC),0) LDFLAGS += -Wl,--export-dynamic endif # === Lua package name detection ============================================= LUA_PKG_NAMES += lua lua-5.1 lua5.1 lua51 # Force linking against Lua's Just-In-Time compiler. # See http://luajit.org/ for more information. ifeq ($(USE_LUAJIT),1) LUA_PKG_NAME := luajit else # User hasn't specificed, use LuaJIT if we can find it. ifneq ($(USE_LUAJIT),0) LUA_PKG_NAMES := luajit $(LUA_PKG_NAMES) endif endif # Search for Lua package name if not forced by user. ifeq ($(LUA_PKG_NAME),) LUA_PKG_NAME = $(shell sh -c '(for name in $(LUA_PKG_NAMES); do \ pkg-config --exists $$name && echo $$name; done) | head -n 1') endif # === Required build packages ================================================ # Packages required to build luakit. PKGS += gtk+-2.0 PKGS += gthread-2.0 PKGS += webkit-1.0 PKGS += sqlite3 PKGS += $(LUA_PKG_NAME) # For systems using older WebKit-GTK versions which bundle JavaScriptCore # within the WebKit-GTK package. ifneq ($(NO_JAVASCRIPTCORE),1) PKGS += javascriptcoregtk-1.0 endif # Build luakit with libunique bindings? (single instance support) ifneq ($(USE_UNIQUE),0) CPPFLAGS += -DWITH_UNIQUE PKGS += unique-1.0 endif # Check user has correct packages installed (and found by pkg-config). PKGS_OK := $(shell pkg-config --print-errors --exists $(PKGS) && echo 1) ifneq ($(PKGS_OK),1) $(error Cannot find required package(s\) to build luakit. Please \ check you have the above packages installed and try again.) endif # Add pkg-config options to compile flags. CFLAGS += $(shell pkg-config --cflags $(PKGS)) CFLAGS += -I./ # Add pkg-config options to linker flags. LDFLAGS += $(shell pkg-config --libs $(PKGS)) luakit-2012.09.13-r1/config/000077500000000000000000000000001202446317700151755ustar00rootroot00000000000000luakit-2012.09.13-r1/config/binds.lua000066400000000000000000000506561202446317700170130ustar00rootroot00000000000000----------------- -- Keybindings -- ----------------- -- Binding aliases local key, buf, but = lousy.bind.key, lousy.bind.buf, lousy.bind.but local cmd, any = lousy.bind.cmd, lousy.bind.any -- Util aliases local match, join = string.match, lousy.util.table.join local strip, split = lousy.util.string.strip, lousy.util.string.split -- Globals or defaults that are used in binds local scroll_step = globals.scroll_step or 20 local zoom_step = globals.zoom_step or 0.1 -- Add binds to a mode function add_binds(mode, binds, before) assert(binds and type(binds) == "table", "invalid binds table type: " .. type(binds)) mode = type(mode) ~= "table" and {mode} or mode for _, m in ipairs(mode) do local mdata = get_mode(m) if mdata and before then mdata.binds = join(binds, mdata.binds or {}) elseif mdata then mdata.binds = mdata.binds or {} for _, b in ipairs(binds) do table.insert(mdata.binds, b) end else new_mode(m, { binds = binds }) end end end -- Add commands to command mode function add_cmds(cmds, before) add_binds("command", cmds, before) end -- Adds the default menu widget bindings to a mode menu_binds = { -- Navigate items key({}, "j", function (w) w.menu:move_down() end), key({}, "k", function (w) w.menu:move_up() end), key({}, "Down", function (w) w.menu:move_down() end), key({}, "Up", function (w) w.menu:move_up() end), key({}, "Tab", function (w) w.menu:move_down() end), key({"Shift"}, "Tab", function (w) w.menu:move_up() end), } -- Add binds to special mode "all" which adds its binds to all modes. add_binds("all", { key({}, "Escape", "Return to `normal` mode.", function (w) w:set_mode() end), key({"Control"}, "[", "Return to `normal` mode.", function (w) w:set_mode() end), -- Mouse bindings but({}, 8, "Go back.", function (w) w:back() end), but({}, 9, "Go forward.", function (w) w:forward() end), -- Open link in new tab or navigate to selection but({}, 2, [[Open link under mouse cursor in new tab or navigate to the contents of `luakit.selection.primary`.]], function (w, m) -- Ignore button 2 clicks in form fields if not m.context.editable then -- Open hovered uri in new tab local uri = w.view.hovered_uri if uri then w:new_tab(uri, false) else -- Open selection in current tab uri = luakit.selection.primary if uri then w:navigate(w:search_open(uri)) end end end end), -- Open link in new tab when Ctrl-clicked. but({"Control"}, 1, "Open link under mouse cursor in new tab.", function (w, m) local uri = w.view.hovered_uri if uri then w:new_tab(uri, false) end end), -- Zoom binds but({"Control"}, 4, "Increase text zoom level.", function (w, m) w:zoom_in() end), but({"Control"}, 5, "Reduce text zoom level.", function (w, m) w:zoom_out() end), -- Horizontal mouse scroll binds but({"Shift"}, 4, "Scroll left.", function (w, m) w:scroll{ xrel = -scroll_step } end), but({"Shift"}, 5, "Scroll right.", function (w, m) w:scroll{ xrel = scroll_step } end), }) add_binds("normal", { -- Autoparse the `[count]` before a binding and re-call the hit function -- with the count removed and added to the opts table. any([[Meta-binding to detect the `^[count]` syntax. The `[count]` is parsed and stripped from the internal buffer string and the value assigned to `state.count`. Then `lousy.bind.hit()` is re-called with the modified buffer string & original modifier state. #### Example binding lousy.bind.key({}, "%", function (w, state) w:scroll{ ypct = state.count } end, { count = 0 }) This binding demonstrates several concepts. Firstly that you are able to specify per-binding default values of `count`. In this case if the user types `"%"` the document will be scrolled vertically to `0%` (the top). If the user types `"100%"` then the document will be scrolled to `100%` (the bottom). All without the need to use `lousy.bind.buf` bindings everywhere and or using a `^(%d*)` pattern prefix on every binding which would like to make use of the `[count]` syntax.]], function (w, m) local count, buf if m.buffer then count = string.match(m.buffer, "^(%d+)") end if count then buf = string.sub(m.buffer, #count + 1, (m.updated_buf and -2) or -1) local opts = join(m, {count = tonumber(count)}) opts.buffer = (#buf > 0 and buf) or nil if lousy.bind.hit(w, m.binds, m.mods, m.key, opts) then return true end end return false end), key({}, "i", "Enter `insert` mode.", function (w) w:set_mode("insert") end), key({}, ":", "Enter `command` mode.", function (w) w:set_mode("command") end), -- Scrolling key({}, "j", "Scroll document down.", function (w) w:scroll{ yrel = scroll_step } end), key({}, "k", "Scroll document up.", function (w) w:scroll{ yrel = -scroll_step } end), key({}, "h", "Scroll document left.", function (w) w:scroll{ xrel = -scroll_step } end), key({}, "l", "Scroll document right.", function (w) w:scroll{ xrel = scroll_step } end), key({}, "Down", "Scroll document down.", function (w) w:scroll{ yrel = scroll_step } end), key({}, "Up", "Scroll document up.", function (w) w:scroll{ yrel = -scroll_step } end), key({}, "Left", "Scroll document left.", function (w) w:scroll{ xrel = -scroll_step } end), key({}, "Right", "Scroll document right.", function (w) w:scroll{ xrel = scroll_step } end), key({}, "^", "Scroll to the absolute left of the document.", function (w) w:scroll{ x = 0 } end), key({}, "$", "Scroll to the absolute right of the document.", function (w) w:scroll{ x = -1 } end), key({}, "0", "Scroll to the absolute left of the document.", function (w, m) if not m.count then w:scroll{ y = 0 } else return false end end), key({"Control"}, "e", "Scroll document down.", function (w) w:scroll{ yrel = scroll_step } end), key({"Control"}, "y", "Scroll document up.", function (w) w:scroll{ yrel = -scroll_step } end), key({"Control"}, "d", "Scroll half page down.", function (w) w:scroll{ ypagerel = 0.5 } end), key({"Control"}, "u", "Scroll half page up.", function (w) w:scroll{ ypagerel = -0.5 } end), key({"Control"}, "f", "Scroll page down.", function (w) w:scroll{ ypagerel = 1.0 } end), key({"Control"}, "b", "Scroll page up.", function (w) w:scroll{ ypagerel = -1.0 } end), key({}, "space", "Scroll page down.", function (w) w:scroll{ ypagerel = 1.0 } end), key({"Shift"}, "space", "Scroll page up.", function (w) w:scroll{ ypagerel = -1.0 } end), key({}, "BackSpace", "Scroll page up.", function (w) w:scroll{ ypagerel = -1.0 } end), key({}, "Page_Down", "Scroll page down.", function (w) w:scroll{ ypagerel = 1.0 } end), key({}, "Page_Up", "Scroll page up.", function (w) w:scroll{ ypagerel = -1.0 } end), key({}, "Home", "Go to the end of the document.", function (w) w:scroll{ y = 0 } end), key({}, "End", "Go to the top of the document.", function (w) w:scroll{ y = -1 } end), -- Specific scroll buf("^gg$", "Go to the top of the document.", function (w, b, m) w:scroll{ ypct = m.count } end, {count=0}), buf("^G$", "Go to the bottom of the document.", function (w, b, m) w:scroll{ ypct = m.count } end, {count=100}), buf("^%%$", "Go to `[count]` percent of the document.", function (w, b, m) w:scroll{ ypct = m.count } end), -- Zooming key({}, "+", "Enlarge text zoom of the current page.", function (w, m) w:zoom_in(zoom_step * m.count) end, {count=1}), key({}, "-", "Reduce text zom of the current page.", function (w, m) w:zoom_out(zoom_step * m.count) end, {count=1}), key({}, "=", "Reset zoom level.", function (w, m) w:zoom_set() end), buf("^z[iI]$", [[Enlarge text zoom of current page with `zi` or `zI` to reduce full zoom.]], function (w, b, m) w:zoom_in(zoom_step * m.count, b == "zI") end, {count=1}), buf("^z[oO]$", [[Reduce text zoom of current page with `zo` or `zO` to reduce full zoom.]], function (w, b, m) w:zoom_out(zoom_step * m.count, b == "zO") end, {count=1}), -- Zoom reset or specific zoom ([count]zZ for full content zoom) buf("^z[zZ]$", [[Set current page zoom to `[count]` percent with `[count]zz`, use `[count]zZ` to set full zoom percent.]], function (w, b, m) w:zoom_set(m.count/100, b == "zZ") end, {count=100}), -- Fullscreen key({}, "F11", "Toggle fullscreen mode.", function (w) w.win.fullscreen = not w.win.fullscreen end), -- Clipboard key({}, "p", [[Open a URL based on the current primary selection contents in the current tab.]], function (w) local uri = luakit.selection.primary if not uri then w:notify("No primary selection...") return end w:navigate(w:search_open(uri)) end), key({}, "P", [[Open a URL based on the current primary selection contents in `[count=1]` new tab(s).]], function (w, m) local uri = luakit.selection.primary if not uri then w:notify("No primary selection...") return end for i = 1, m.count do w:new_tab(w:search_open(uri)) end end, {count = 1}), -- Yanking key({}, "y", "Yank current URI to primary selection.", function (w) local uri = string.gsub(w.view.uri or "", " ", "%%20") luakit.selection.primary = uri w:notify("Yanked uri: " .. uri) end), -- Commands key({"Control"}, "a", "Increment last number in URL.", function (w) w:navigate(w:inc_uri(1)) end), key({"Control"}, "x", "Decrement last number in URL.", function (w) w:navigate(w:inc_uri(-1)) end), key({}, "o", "Open one or more URLs.", function (w) w:enter_cmd(":open ") end), key({}, "t", "Open one or more URLs in a new tab.", function (w) w:enter_cmd(":tabopen ") end), key({}, "w", "Open one or more URLs in a new window.", function (w) w:enter_cmd(":winopen ") end), key({}, "O", "Open one or more URLs based on current location.", function (w) w:enter_cmd(":open " .. (w.view.uri or "")) end), key({}, "T", "Open one or more URLs based on current location in a new tab.", function (w) w:enter_cmd(":tabopen " .. (w.view.uri or "")) end), key({}, "W", "Open one or more URLs based on current locaton in a new window.", function (w) w:enter_cmd(":winopen " .. (w.view.uri or "")) end), -- History key({}, "H", "Go back in the browser history `[count=1]` items.", function (w, m) w:back(m.count) end), key({}, "L", "Go forward in the browser history `[count=1]` times.", function (w, m) w:forward(m.count) end), key({}, "XF86Back", "Go back in the browser history.", function (w, m) w:back(m.count) end), key({}, "XF86Forward", "Go forward in the browser history.", function (w, m) w:forward(m.count) end), key({"Control"}, "o", "Go back in the browser history.", function (w, m) w:back(m.count) end), key({"Control"}, "i", "Go forward in the browser history.", function (w, m) w:forward(m.count) end), -- Tab key({"Control"}, "Page_Up", "Go to previous tab.", function (w) w:prev_tab() end), key({"Control"}, "Page_Down", "Go to next tab.", function (w) w:next_tab() end), key({"Control"}, "Tab", "Go to next tab.", function (w) w:next_tab() end), key({"Shift","Control"}, "Tab", "Go to previous tab.", function (w) w:prev_tab() end), buf("^gT$", "Go to previous tab.", function (w) w:prev_tab() end), buf("^gt$", "Go to next tab (or `[count]` nth tab).", function (w, b, m) if not w:goto_tab(m.count) then w:next_tab() end end, {count=0}), buf("^g0$", "Go to first tab.", function (w) w:goto_tab(1) end), buf("^g$$", "Go to last tab.", function (w) w:goto_tab(-1) end), key({"Control"}, "t", "Open a new tab.", function (w) w:new_tab(globals.homepage) end), key({"Control"}, "w", "Close current tab.", function (w) w:close_tab() end), key({}, "d", "Close current tab (or `[count]` tabs).", function (w, m) for i=1,m.count do w:close_tab() end end, {count=1}), key({}, "<", "Reorder tab left `[count=1]` positions.", function (w, m) w.tabs:reorder(w.view, w.tabs:current() - m.count) end, {count=1}), key({}, ">", "Reorder tab right `[count=1]` positions.", function (w, m) w.tabs:reorder(w.view, (w.tabs:current() + m.count) % w.tabs:count()) end, {count=1}), buf("^gH$", "Open homepage in new tab.", function (w) w:new_tab(globals.homepage) end), buf("^gh$", "Open homepage.", function (w) w:navigate(globals.homepage) end), buf("^gy$", "Duplicate current tab.", function (w) w:new_tab(w.view.history or "") end), key({}, "r", "Reload current tab.", function (w) w:reload() end), key({}, "R", "Reload current tab (skipping cache).", function (w) w:reload(true) end), key({"Control"}, "c", "Stop loading the current tab.", function (w) w.view:stop() end), key({"Control", "Shift"}, "R", "Restart luakit (reloading configs).", function (w) w:restart() end), -- Window buf("^ZZ$", "Quit and save the session.", function (w) w:save_session() w:close_win() end), buf("^ZQ$", "Quit and don't save the session.", function (w) w:close_win() end), buf("^D$", "Quit and don't save the session.", function (w) w:close_win() end), -- Enter passthrough mode key({"Control"}, "z", "Enter `passthrough` mode, ignores all luakit keybindings.", function (w) w:set_mode("passthrough") end), }) add_binds("insert", { key({"Control"}, "z", "Enter `passthrough` mode, ignores all luakit keybindings.", function (w) w:set_mode("passthrough") end), }) readline_bindings = { key({"Shift"}, "Insert", "Insert contents of primary selection at cursor position.", function (w) w:insert_cmd(luakit.selection.primary) end), key({"Control"}, "w", "Delete previous word.", function (w) w:del_word() end), key({"Control"}, "u", "Delete until beginning of current line.", function (w) w:del_line() end), key({"Control"}, "h", "Delete character to the left.", function (w) w:del_backward_char() end), key({"Control"}, "d", "Delete character to the right.", function (w) w:del_forward_char() end), key({"Control"}, "a", "Move cursor to beginning of current line.", function (w) w:beg_line() end), key({"Control"}, "e", "Move cursor to end of current line.", function (w) w:end_line() end), key({"Control"}, "f", "Move cursor forward one character.", function (w) w:forward_char() end), key({"Control"}, "b", "Move cursor backward one character.", function (w) w:backward_char() end), key({"Mod1"}, "f", "Move cursor forward one word.", function (w) w:forward_word() end), key({"Mod1"}, "b", "Move cursor backward one word.", function (w) w:backward_word() end), } add_binds({"command", "search"}, readline_bindings) -- Switching tabs with Mod1+{1,2,3,...} mod1binds = {} for i=1,10 do table.insert(mod1binds, key({"Mod1"}, tostring(i % 10), "Jump to tab at index "..i..".", function (w) w.tabs:switch(i) end)) end add_binds("normal", mod1binds) -- Command bindings which are matched in the "command" mode from text -- entered into the input bar. add_cmds({ buf("^%S+!", [[Detect bang syntax in `:command!` and recursively calls `lousy.bind.match_cmd(..)` removing the bang from the command string and setting `bang = true` in the bind opts table.]], function (w, cmd, opts) local cmd, args = string.match(cmd, "^(%S+)!+(.*)") if cmd then opts = join(opts, { bang = true }) return lousy.bind.match_cmd(w, opts.binds, cmd .. args, opts) end end), cmd("c[lose]", "Close current tab.", function (w) w:close_tab() end), cmd("print", "Print current page.", function (w) w.view:eval_js("print()") end), cmd("stop", "Stop loading.", function (w) w.view:stop() end), cmd("reload", "Reload page", function (w) w:reload() end), cmd("restart", "Restart browser (reload config files).", function (w) w:restart() end), cmd("write", "Save current session.", function (w) w:save_session() end), cmd("noh[lsearch]", "Clear search highlighting.", function (w) w:clear_search() end), cmd("back", "Go back in the browser history `[count=1]` items.", function (w, a) w:back(tonumber(a) or 1) end), cmd("f[orward]", "Go forward in the browser history `[count=1]` items.", function (w, a) w:forward(tonumber(a) or 1) end), cmd("inc[rease]", "Increment last number in URL.", function (w, a) w:navigate(w:inc_uri(tonumber(a) or 1)) end), cmd("o[pen]", "Open one or more URLs.", function (w, a) w:navigate(w:search_open(a)) end), cmd("t[abopen]", "Open one or more URLs in a new tab.", function (w, a) w:new_tab(w:search_open(a)) end), cmd("w[inopen]", "Open one or more URLs in a new window.", function (w, a) window.new{w:search_open(a)} end), cmd({"javascript", "js"}, "Evaluate JavaScript snippet.", function (w, a) w.view:eval_js(a) end), -- Tab manipulation commands cmd("tab", "Execute command and open result in new tab.", function (w, a) w:new_tab() w:run_cmd(":" .. a) end), cmd("tabd[o]", "Execute command in each tab.", function (w, a) w:each_tab(function (v) w:run_cmd(":" .. a) end) end), cmd("tabdu[plicate]", "Duplicate current tab.", function (w) w:new_tab(w.view.history) end), cmd("tabfir[st]", "Switch to first tab.", function (w) w:goto_tab(1) end), cmd("tabl[ast]", "Switch to last tab.", function (w) w:goto_tab(-1) end), cmd("tabn[ext]", "Switch to the next tab.", function (w) w:next_tab() end), cmd("tabp[revious]", "Switch to the previous tab.", function (w) w:prev_tab() end), cmd("q[uit]", "Close the current window.", function (w, a, o) w:close_win(o.bang) end), cmd({"viewsource", "vs"}, "View the source code of the current document.", function (w, a, o) w:toggle_source(not o.bang and true or nil) end), cmd({"wqall", "wq"}, "Save the session and quit.", function (w, a, o) w:save_session() w:close_win(o.bang) end), cmd("lua", "Evaluate Lua snippet.", function (w, a) if a then local ret = assert( loadstring("return function(w) return "..a.." end"))()(w) if ret then print(ret) end else w:set_mode("lua") end end), cmd("dump", "Dump current tabs html to file.", function (w, a) local fname = string.gsub(w.win.title, '[^%w%.%-]', '_')..'.html' -- sanitize filename local file = a or luakit.save_file("Save file", w.win, xdg.download_dir or '.', fname) if file then local fd = assert(io.open(file, "w"), "failed to open: " .. file) local html = assert(w.view:eval_js("document.documentElement.outerHTML"), "Unable to get HTML") assert(fd:write(html), "unable to save html") io.close(fd) w:notify("Dumped HTML to: " .. file) end end), }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config/globals.lua000066400000000000000000000061061202446317700173260ustar00rootroot00000000000000-- Global variables for luakit globals = { homepage = "http://luakit.org/", -- homepage = "http://github.com/mason-larobina/luakit", scroll_step = 40, zoom_step = 0.1, max_cmd_history = 100, max_srch_history = 100, -- http_proxy = "http://example.com:3128", default_window_size = "800x600", -- Disables loading of hostnames from /etc/hosts (for large host files) -- load_etc_hosts = false, -- Disables checking if a filepath exists in search_open function -- check_filepath = false, } -- Make useragent local _, arch = luakit.spawn_sync("uname -sm") -- Only use the luakit version if in date format (reduces identifiability) local lkv = string.match(luakit.version, "^(%d+.%d+.%d+)") globals.useragent = string.format("Mozilla/5.0 (%s) AppleWebKit/%s+ (KHTML, like Gecko) WebKitGTK+/%s luakit%s", string.sub(arch, 1, -2), luakit.webkit_user_agent_version, luakit.webkit_version, (lkv and ("/" .. lkv)) or "") -- Search common locations for a ca file which is used for ssl connection validation. local ca_files = { -- $XDG_DATA_HOME/luakit/ca-certificates.crt luakit.data_dir .. "/ca-certificates.crt", "/etc/certs/ca-certificates.crt", "/etc/ssl/certs/ca-certificates.crt", } -- Use the first ca-file found for _, ca_file in ipairs(ca_files) do if os.exists(ca_file) then soup.ssl_ca_file = ca_file break end end -- Change to stop navigation sites with invalid or expired ssl certificates soup.ssl_strict = false -- Set cookie acceptance policy cookie_policy = { always = 0, never = 1, no_third_party = 2 } soup.accept_policy = cookie_policy.always -- List of search engines. Each item must contain a single %s which is -- replaced by URI encoded search terms. All other occurances of the percent -- character (%) may need to be escaped by placing another % before or after -- it to avoid collisions with lua's string.format characters. -- See: http://www.lua.org/manual/5.1/manual.html#pdf-string.format search_engines = { duckduckgo = "https://duckduckgo.com/?q=%s", github = "https://github.com/search?q=%s", google = "https://google.com/search?q=%s", imdb = "http://www.imdb.com/find?s=all&q=%s", wikipedia = "https://en.wikipedia.org/wiki/Special:Search?search=%s", } -- Set google as fallback search engine search_engines.default = search_engines.google -- Use this instead to disable auto-searching --search_engines.default = "%s" -- Per-domain webview properties -- See http://webkitgtk.org/reference/webkitgtk/stable/WebKitWebSettings.html domain_props = { --[[ ["all"] = { enable_scripts = false, enable_plugins = false, enable_private_browsing = false, user_stylesheet_uri = "", }, ["youtube.com"] = { enable_scripts = true, enable_plugins = true, }, ["bbs.archlinux.org"] = { user_stylesheet_uri = "file://" .. luakit.data_dir .. "/styles/dark.css", enable_private_browsing = true, }, ]] } -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config/modes.lua000066400000000000000000000116521202446317700170140ustar00rootroot00000000000000------------------------------- -- luakit mode configuration -- ------------------------------- -- Table of modes and their callback hooks local modes = {} local lousy = require "lousy" local join = lousy.util.table.join local order = 0 -- Add new mode table (optionally merges with original mode) function new_mode(name, desc, mode, replace) assert(string.match(name, "^[%w-_]+$"), "invalid mode name: " .. name) -- Detect optional description if type(desc) == "table" then desc, mode, replace = nil, desc, mode end local traceback = debug.traceback("Creation traceback:", 2) order = order + 1 modes[name] = join({ order = order, traceback = traceback }, (not replace and modes[name]) or {}, mode or {}, { name = name, desc = desc }) end -- Get mode table function get_mode(name) return modes[name] end function get_modes() return lousy.util.table.clone(modes) end -- Attach window & input bar signals for mode hooks window.init_funcs.modes_setup = function (w) -- Calls the `enter` and `leave` mode hooks. w:add_signal("mode-changed", function (_, name, ...) local leave = (w.mode or {}).leave -- Get new modes functions/hooks/data local mode = assert(modes[name], "invalid mode: " .. name) -- Call last modes leave hook. if leave then leave(w) end -- Create w.mode object w.mode = mode -- Update window binds w:update_binds(name) -- Call new modes enter hook. if mode.enter then mode.enter(w, ...) end w:emit_signal("mode-entered", mode) end) local input = w.ibar.input -- Calls the changed hook on input widget changed. input:add_signal("changed", function () local changed = w.mode.changed if changed then changed(w, input.text) end end) input:add_signal("property::position", function () local move_cursor = w.mode.move_cursor if move_cursor then move_cursor(w, input.position) end end) -- Calls the `activate` hook on input widget activate. input:add_signal("activate", function () local mode = w.mode if mode and mode.activate then local text, hist = input.text, mode.history if mode.activate(w, text) == false then return end -- Check if last history item is identical if hist and hist.items and hist.items[hist.len or -1] ~= text then table.insert(hist.items, text) end end end) end -- Add mode related window methods window.methods.set_mode = lousy.mode.set local mget = lousy.mode.get window.methods.is_mode = function (w, name) return name == mget(w) end -- Setup normal mode new_mode("normal", [[When luakit first starts you will find yourself in this mode.]], { enter = function (w) w:set_prompt() w:set_input() end, }) new_mode("all", [[Special meta-mode in which the bindings for this mode are present in all modes.]]) -- Setup insert mode new_mode("insert", [[When clicking on form fields luakit will enter the insert mode which allows you to enter text in form fields without accidentally triggering normal mode bindings.]], { enter = function (w) w:set_prompt("-- INSERT --") w:set_input() w.view:focus() end, -- Send key events to webview passthrough = true, }) new_mode("passthrough", [[Luakit will pass every key event to the WebView until the user presses Escape.]], { enter = function (w) w:set_prompt("-- PASS THROUGH --") w:set_input() end, -- Send key events to webview passthrough = true, -- Don't exit mode when clicking outside of form fields reset_on_focus = false, -- Don't exit mode on navigation reset_on_navigation = false, }) -- Setup command mode new_mode("command", [[Enter commands.]], { enter = function (w) w:set_prompt() w:set_input(":") end, changed = function (w, text) -- Auto-exit command mode if user backspaces ":" in the input bar. if not string.match(text, "^:") then w:set_mode() end end, activate = function (w, text) w:set_mode() local cmd = string.sub(text, 2) if not string.find(cmd, "%S") then return end local success, match = xpcall( function () return w:match_cmd(cmd) end, function (err) w:error(debug.traceback(err, 3)) end) if success and not match then w:error(string.format("Not a browser command: %q", cmd)) end end, history = {maxlen = 50}, }) new_mode("lua", [[Execute arbitrary Lua commands within the luakit environment.]], { enter = function (w) w:set_prompt(">") w:set_input("") end, activate = function (w, text) w:set_input("") local ret = assert(loadstring("return function(w) return "..text.." end"))()(w) if ret then print(ret) end end, history = {maxlen = 50}, }) luakit-2012.09.13-r1/config/rc.lua000066400000000000000000000116571202446317700163160ustar00rootroot00000000000000----------------------------------------------------------------------- -- luakit configuration file, more information at http://luakit.org/ -- ----------------------------------------------------------------------- require "lfs" if unique then unique.new("org.luakit") -- Check for a running luakit instance if unique.is_running() then if uris[1] then for _, uri in ipairs(uris) do if lfs.attributes(uri) then uri = os.abspath(uri) end unique.send_message("tabopen " .. uri) end else unique.send_message("winopen") end luakit.quit() end end -- Load library of useful functions for luakit require "lousy" -- Small util functions to print output (info prints only when luakit.verbose is true) function warn(...) io.stderr:write(string.format(...) .. "\n") end function info(...) if luakit.verbose then io.stdout:write(string.format(...) .. "\n") end end -- Load users global config -- ("$XDG_CONFIG_HOME/luakit/globals.lua" or "/etc/xdg/luakit/globals.lua") require "globals" -- Load users theme -- ("$XDG_CONFIG_HOME/luakit/theme.lua" or "/etc/xdg/luakit/theme.lua") lousy.theme.init(lousy.util.find_config("theme.lua")) theme = assert(lousy.theme.get(), "failed to load theme") -- Load users window class -- ("$XDG_CONFIG_HOME/luakit/window.lua" or "/etc/xdg/luakit/window.lua") require "window" -- Load users webview class -- ("$XDG_CONFIG_HOME/luakit/webview.lua" or "/etc/xdg/luakit/webview.lua") require "webview" -- Load users mode configuration -- ("$XDG_CONFIG_HOME/luakit/modes.lua" or "/etc/xdg/luakit/modes.lua") require "modes" -- Load users keybindings -- ("$XDG_CONFIG_HOME/luakit/binds.lua" or "/etc/xdg/luakit/binds.lua") require "binds" ---------------------------------- -- Optional user script loading -- ---------------------------------- require "webinspector" -- Add sqlite3 cookiejar require "cookies" -- Cookie blocking by domain (extends cookies module) -- Add domains to the whitelist at "$XDG_CONFIG_HOME/luakit/cookie.whitelist" -- and blacklist at "$XDG_CONFIG_HOME/luakit/cookie.blacklist". -- Each domain must be on it's own line and you may use "*" as a -- wildcard character (I.e. "*google.com") --require "cookie_blocking" -- Block all cookies by default (unless whitelisted) --cookies.default_allow = false -- Add uzbl-like form filling require "formfiller" -- Add proxy support & manager require "proxy" -- Add quickmarks support & manager require "quickmarks" -- Add session saving/loading support require "session" -- Add command to list closed tabs & bind to open closed tabs require "undoclose" -- Add command to list tab history items require "tabhistory" -- Add greasemonkey-like javascript userscript support require "userscripts" -- Add bookmarks support require "bookmarks" require "bookmarks_chrome" -- Add download support require "downloads" require "downloads_chrome" -- Example using xdg-open for opening downloads / showing download folders --downloads.add_signal("open-file", function (file, mime) -- luakit.spawn(string.format("xdg-open %q", file)) -- return true --end) -- Add vimperator-like link hinting & following require "follow" -- Use a custom charater set for hint labels --local s = follow.label_styles --follow.label_maker = s.sort(s.reverse(s.charset("asdfqwerzxcv"))) -- Match only hint labels --follow.pattern_maker = follow.pattern_styles.match_label -- Add command history require "cmdhist" -- Add search mode & binds require "search" -- Add ordering of new tabs require "taborder" -- Save web history require "history" require "history_chrome" require "introspector" -- Add command completion require "completion" -- NoScript plugin, toggle scripts and or plugins on a per-domain basis. -- `,ts` to toggle scripts, `,tp` to toggle plugins, `,tr` to reset. -- Remove all "enable_scripts" & "enable_plugins" lines from your -- domain_props table (in config/globals.lua) as this module will conflict. --require "noscript" require "follow_selected" require "go_input" require "go_next_prev" require "go_up" ----------------------------- -- End user script loading -- ----------------------------- -- Restore last saved session local w = (session and session.restore()) if w then for i, uri in ipairs(uris) do w:new_tab(uri, i == 1) end else -- Or open new window window.new(uris) end ------------------------------------------- -- Open URIs from other luakit instances -- ------------------------------------------- if unique then unique.add_signal("message", function (msg, screen) local cmd, arg = string.match(msg, "^(%S+)%s*(.*)") local w = lousy.util.table.values(window.bywidget)[1] if cmd == "tabopen" then w:new_tab(arg) elseif cmd == "winopen" then w = window.new((arg ~= "") and { arg } or {}) end w.win.screen = screen w.win.urgency_hint = true end) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config/theme.lua000066400000000000000000000032041202446317700170010ustar00rootroot00000000000000-------------------------- -- Default luakit theme -- -------------------------- local theme = {} -- Default settings theme.font = "monospace normal 9" theme.fg = "#fff" theme.bg = "#000" -- Genaral colours theme.success_fg = "#0f0" theme.loaded_fg = "#33AADD" theme.error_fg = "#FFF" theme.error_bg = "#F00" -- Warning colours theme.warning_fg = "#F00" theme.warning_bg = "#FFF" -- Notification colours theme.notif_fg = "#444" theme.notif_bg = "#FFF" -- Menu colours theme.menu_fg = "#000" theme.menu_bg = "#fff" theme.menu_selected_fg = "#000" theme.menu_selected_bg = "#FF0" theme.menu_title_bg = "#fff" theme.menu_primary_title_fg = "#f00" theme.menu_secondary_title_fg = "#666" -- Proxy manager theme.proxy_active_menu_fg = '#000' theme.proxy_active_menu_bg = '#FFF' theme.proxy_inactive_menu_fg = '#888' theme.proxy_inactive_menu_bg = '#FFF' -- Statusbar specific theme.sbar_fg = "#fff" theme.sbar_bg = "#000" -- Downloadbar specific theme.dbar_fg = "#fff" theme.dbar_bg = "#000" theme.dbar_error_fg = "#F00" -- Input bar specific theme.ibar_fg = "#000" theme.ibar_bg = "#fff" -- Tab label theme.tab_fg = "#888" theme.tab_bg = "#222" theme.tab_ntheme = "#ddd" theme.selected_fg = "#fff" theme.selected_bg = "#000" theme.selected_ntheme = "#ddd" theme.loading_fg = "#33AADD" theme.loading_bg = "#000" -- Trusted/untrusted ssl colours theme.trust_fg = "#0F0" theme.notrust_fg = "#F00" return theme -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config/webview.lua000066400000000000000000000254711202446317700173610ustar00rootroot00000000000000-------------------------- -- WebKit WebView class -- -------------------------- -- Webview class table webview = {} -- Table of functions which are called on new webview widgets. webview.init_funcs = { -- Set useragent set_useragent = function (view, w) view.user_agent = globals.useragent end, -- Check if checking ssl certificates checking_ssl = function (view, w) local ca_file = soup.ssl_ca_file if ca_file and os.exists(ca_file) then w.checking_ssl = true end end, -- Update window and tab titles title_update = function (view, w) view:add_signal("property::title", function (v) w:update_tablist() if w.view == v then w:update_win_title() end end) end, -- Update uri label in statusbar uri_update = function (view, w) view:add_signal("property::uri", function (v) w:update_tablist() if w.view == v then w:update_uri() end end) end, -- Update history indicator hist_update = function (view, w) view:add_signal("load-status", function (v, status) if w.view == v then w:update_hist() end end) end, -- Update tab titles tablist_update = function (view, w) view:add_signal("load-status", function (v, status) if status == "provisional" or status == "finished" or status == "failed" then w:update_tablist() end end) end, -- Update scroll widget scroll_update = function (view, w) view:add_signal("expose", function (v) if w.view == v then w:update_scroll() end end) end, -- Update progress widget progress_update = function (view, w) for _, sig in ipairs({"load-status", "property::progress"}) do view:add_signal(sig, function (v) if w.view == v then w:update_progress() w:update_ssl() end end) end end, -- Display hovered link in statusbar link_hover_display = function (view, w) view:add_signal("link-hover", function (v, link) if w.view == v and link then w:update_uri(link) end end) view:add_signal("link-unhover", function (v) if w.view == v then w:update_uri() end end) end, -- Clicking a form field automatically enters insert mode. form_insert_mode = function (view, w) view:add_signal("button-press", function (v, mods, button, context) -- Clear start search marker (w.search_state or {}).marker = nil if button == 1 and context.editable then view:emit_signal("form-active") end end) -- Emit root-active event in button release to prevent "missing" -- buttons or links when the input bar hides. view:add_signal("button-release", function (v, mods, button, context) if button == 1 and not context.editable then view:emit_signal("root-active") end end) view:add_signal("form-active", function () if not w.mode.passthrough then w:set_mode("insert") end end) view:add_signal("root-active", function () if w.mode.reset_on_focus ~= false then w:set_mode() end end) end, -- Catch keys in non-passthrough modes mode_key_filter = function (view, w) view:add_signal("key-press", function () if not w.mode.passthrough then return true end end) end, -- Try to match a button event to a users button binding else let the -- press hit the webview. button_bind_match = function (view, w) view:add_signal("button-release", function (v, mods, button, context) (w.search_state or {}).marker = nil if w:hit(mods, button, { context = context }) then return true end end) end, -- Reset the mode on navigation mode_reset_on_nav = function (view, w) view:add_signal("load-status", function (v, status) if status == "provisional" and w.view == v then if w.mode.reset_on_navigation ~= false then w:set_mode() end end end) end, -- Domain properties domain_properties = function (view, w) view:add_signal("load-status", function (v, status) if status ~= "committed" or v.uri == "about:blank" then return end -- Get domain local domain = lousy.uri.parse(v.uri).host -- Strip leading www. domain = string.match(domain or "", "^www%.(.+)") or domain or "all" -- Build list of domain props tables to join & load. -- I.e. for luakit.org load .luakit.org, luakit.org, .org local props = {domain_props.all or {}, domain_props[domain] or {}} repeat table.insert(props, 2, domain_props["."..domain] or {}) domain = string.match(domain, "%.(.+)") until not domain -- Join all property tables for k, v in pairs(lousy.util.table.join(unpack(props))) do info("Domain prop: %s = %s (%s)", k, tostring(v), domain) view[k] = v end end) end, -- Action to take on mime type decision request. mime_decision = function (view, w) -- Return true to accept or false to reject from this signal. view:add_signal("mime-type-decision", function (v, uri, mime) info("Requested link: %s (%s)", uri, mime) -- i.e. block binary files like *.exe --if mime == "application/octet-stream" then -- return false --end end) end, -- Action to take on window open request. --window_decision = function (view, w) -- view:add_signal("new-window-decision", function (v, uri, reason) -- if reason == "link-clicked" then -- window.new({uri}) -- else -- w:new_tab(uri) -- end -- return true -- end) --end, create_webview = function (view, w) -- Return a newly created webview in a new tab view:add_signal("create-web-view", function (v) return w:new_tab() end) end, -- Creates context menu popup from table (and nested tables). -- Use `true` for menu separators. --populate_popup = function (view, w) -- view:add_signal("populate-popup", function (v) -- return { -- true, -- { "_Toggle Source", function () w:toggle_source() end }, -- { "_Zoom", { -- { "Zoom _In", function () w:zoom_in() end }, -- { "Zoom _Out", function () w:zoom_out() end }, -- true, -- { "Zoom _Reset", function () w:zoom_set() end }, }, }, -- } -- end) --end, -- Action to take on resource request. resource_request_decision = function (view, w) view:add_signal("resource-request-starting", function(v, uri) info("Requesting: %s", uri) -- Return false to cancel the request. end) end, } -- These methods are present when you index a window instance and no window -- method is found in `window.methods`. The window then checks if there is an -- active webview and calls the following methods with the given view instance -- as the first argument. All methods must take `view` & `w` as the first two -- arguments. webview.methods = { -- Reload with or without ignoring cache reload = function (view, w, bypass_cache) if bypass_cache then view:reload_bypass_cache() else view:reload() end end, -- Toggle source view toggle_source = function (view, w, show) if show == nil then view.view_source = not view.view_source else view.view_source = show end view:reload() end, -- Zoom functions zoom_in = function (view, w, step, full_zoom) view.full_content_zoom = not not full_zoom step = step or globals.zoom_step or 0.1 view.zoom_level = view.zoom_level + step end, zoom_out = function (view, w, step, full_zoom) view.full_content_zoom = not not full_zoom step = step or globals.zoom_step or 0.1 view.zoom_level = math.max(0.01, view.zoom_level) - step end, zoom_set = function (view, w, level, full_zoom) view.full_content_zoom = not not full_zoom view.zoom_level = level or 1.0 end, -- History traversing functions back = function (view, w, n) view:go_back(n or 1) end, forward = function (view, w, n) view:go_forward(n or 1) end, } function webview.methods.scroll(view, w, new) local s = view.scroll for _, axis in ipairs{ "x", "y" } do -- Relative px movement if rawget(new, axis.."rel") then s[axis] = s[axis] + new[axis.."rel"] -- Relative page movement elseif rawget(new, axis .. "pagerel") then s[axis] = s[axis] + math.ceil(s[axis.."page_size"] * new[axis.."pagerel"]) -- Absolute px movement elseif rawget(new, axis) then local n = new[axis] if n == -1 then s[axis] = s[axis.."max"] else s[axis] = n end -- Absolute page movement elseif rawget(new, axis.."page") then s[axis] = math.ceil(s[axis.."page_size"] * new[axis.."page"]) -- Absolute percent movement elseif rawget(new, axis .. "pct") then s[axis] = math.ceil(s[axis.."max"] * (new[axis.."pct"]/100)) end end end function webview.new(w) local view = widget{type = "webview"} view.show_scrollbars = false view.enforce_96_dpi = false -- Call webview init functions for k, func in pairs(webview.init_funcs) do func(view, w) end return view end -- Insert webview method lookup on window structure table.insert(window.indexes, 1, function (w, k) if k == "view" then local view = w.tabs[w.tabs:current()] if view and type(view) == "widget" and view.type == "webview" then w.view = view return view end end -- Lookup webview method local func = webview.methods[k] if not func then return end local view = w.view if view then return function (_, ...) return func(view, w, ...) end end end) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/config/window.lua000066400000000000000000000650711202446317700172200ustar00rootroot00000000000000------------------ -- Window class -- ------------------ require "lfs" -- Window class table window = {} -- List of active windows by window widget window.bywidget = setmetatable({}, { __mode = "k" }) -- Widget construction aliases local function entry() return widget{type="entry"} end local function eventbox() return widget{type="eventbox"} end local function hbox() return widget{type="hbox"} end local function label() return widget{type="label"} end local function notebook() return widget{type="notebook"} end local function vbox() return widget{type="vbox"} end -- Build and pack window widgets function window.build() -- Create a table for widgets and state variables for a window local w = { win = widget{type="window"}, ebox = eventbox(), layout = vbox(), paned = widget{type="vpaned"}, tabs = notebook(), -- Tablist widget tablist = lousy.widget.tablist(), -- Status bar widgets sbar = { layout = hbox(), ebox = eventbox(), -- Left aligned widgets l = { layout = hbox(), ebox = eventbox(), uri = label(), hist = label(), loaded = label(), }, -- Fills space between the left and right aligned widgets sep = eventbox(), -- Right aligned widgets r = { layout = hbox(), ebox = eventbox(), buf = label(), ssl = label(), tabi = label(), scroll = label(), }, }, -- Vertical menu window widget (completion results, bookmarks, qmarks, ..) menu = lousy.widget.menu(), -- Input bar widgets ibar = { layout = hbox(), ebox = eventbox(), prompt = label(), input = entry(), }, closed_tabs = {} } -- Assemble window w.ebox.child = w.paned w.paned:pack1(w.layout) w.win.child = w.ebox -- Pack tablist w.layout:pack(w.tablist.widget) -- Pack notebook w.layout:pack(w.tabs, { expand = true, fill = true }) -- Pack left-aligned statusbar elements local l = w.sbar.l l.layout:pack(l.uri) l.layout:pack(l.hist) l.layout:pack(l.loaded) l.ebox.child = l.layout -- Pack right-aligned statusbar elements local r = w.sbar.r r.layout:pack(r.buf) r.layout:pack(r.ssl) r.layout:pack(r.tabi) r.layout:pack(r.scroll) r.ebox.child = r.layout -- Pack status bar elements local s = w.sbar s.layout:pack(l.ebox) s.layout:pack(s.sep, { expand = true, fill = true }) s.layout:pack(r.ebox) s.ebox.child = s.layout w.layout:pack(s.ebox) -- Pack menu widget w.layout:pack(w.menu.widget) w.menu:hide() -- Pack input bar local i = w.ibar i.layout:pack(i.prompt) i.layout:pack(i.input, { expand = true, fill = true }) i.ebox.child = i.layout w.layout:pack(i.ebox) -- Other settings i.input.show_frame = false w.tabs.show_tabs = false l.loaded:hide() l.hist:hide() l.uri.selectable = true r.ssl:hide() -- Allows indexing of window struct by window widget window.bywidget[w.win] = w return w end -- Table of functions to call on window creation. Normally used to add signal -- handlers to the new windows widgets. window.init_funcs = { -- Attach notebook widget signals notebook_signals = function (w) w.tabs:add_signal("page-added", function (nbook, view, idx) luakit.idle_add(function () w:update_tab_count() w:update_tablist() return false end) end) w.tabs:add_signal("switch-page", function (nbook, view, idx) w.view = nil w:set_mode() -- Update widgets after tab switch luakit.idle_add(function () w:update_tab_count() w:update_win_title() w:update_uri() w:update_progress() w:update_tablist() w:update_buf() w:update_ssl() w:update_hist() return false end) end) w.tabs:add_signal("page-reordered", function (nbook, view, idx) w:update_tab_count() w:update_tablist() end) end, last_win_check = function (w) w.win:add_signal("destroy", function () -- call the quit function if this was the last window left if #luakit.windows == 0 then luakit.quit() end if w.close_win then w:close_win() end end) end, key_press_match = function (w) w.win:add_signal("key-press", function (_, mods, key) -- Match & exec a bind local success, match = xpcall( function () return w:hit(mods, key) end, function (err) w:error(debug.traceback(err, 3)) end) if success and match then return true end end) end, tablist_tab_click = function (w) w.tablist:add_signal("tab-clicked", function (_, index, mods, button) if button == 1 then w.tabs:switch(index) return true elseif button == 2 then w:close_tab(w.tabs[index]) return true end end) end, apply_window_theme = function (w) local s, i = w.sbar, w.ibar -- Set foregrounds for wi, v in pairs({ [s.l.uri] = theme.uri_sbar_fg, [s.l.hist] = theme.hist_sbar_fg, [s.l.loaded] = theme.sbar_loaded_fg, [s.r.buf] = theme.buf_sbar_fg, [s.r.tabi] = theme.tabi_sbar_fg, [s.r.scroll] = theme.scroll_sbar_fg, [i.prompt] = theme.prompt_ibar_fg, [i.input] = theme.input_ibar_fg, }) do wi.fg = v end -- Set backgrounds for wi, v in pairs({ [s.l.ebox] = theme.sbar_bg, [s.r.ebox] = theme.sbar_bg, [s.sep] = theme.sbar_bg, [s.ebox] = theme.sbar_bg, [i.ebox] = theme.ibar_bg, [i.input] = theme.input_ibar_bg, }) do wi.bg = v end -- Set fonts for wi, v in pairs({ [s.l.uri] = theme.uri_sbar_font, [s.l.hist] = theme.hist_sbar_font, [s.l.loaded] = theme.sbar_loaded_font, [s.r.buf] = theme.buf_sbar_font, [s.r.ssl] = theme.ssl_sbar_font, [s.r.tabi] = theme.tabi_sbar_font, [s.r.scroll] = theme.scroll_sbar_font, [i.prompt] = theme.prompt_ibar_font, [i.input] = theme.input_ibar_font, }) do wi.font = v end end, set_default_size = function (w) local size = globals.default_window_size or "800x600" if string.match(size, "^%d+x%d+$") then w.win:set_default_size(string.match(size, "^(%d+)x(%d+)$")) else warn("E: window.lua: invalid window size: %q", size) end end, set_window_icon = function (w) local path = (luakit.dev_paths and os.exists("./extras/luakit.png")) or os.exists("/usr/share/pixmaps/luakit.png") if path then w.win.icon = path end end, clear_urgency_hint = function (w) w.win:add_signal("focus", function () w.win.urgency_hint = false end) end, } -- Helper functions which operate on the window widgets or structure. window.methods = { -- Wrapper around the bind plugin's hit method hit = function (w, mods, key, opts) local opts = lousy.util.table.join(opts or {}, { enable_buffer = w:is_mode("normal"), buffer = w.buffer, }) local caught, newbuf = lousy.bind.hit(w, w.binds, mods, key, opts) if w.win then -- Check binding didn't cause window to exit w.buffer = newbuf w:update_buf() end return caught end, -- Wrapper around the bind plugin's match_cmd method match_cmd = function (w, buffer) return lousy.bind.match_cmd(w, get_mode("command").binds, buffer) end, -- enter command or characters into command line enter_cmd = function (w, cmd, opts) w:set_mode("command") w:set_input(cmd, opts) end, -- run command as if typed into the command line run_cmd = function (w, cmd, opts) w:enter_cmd(cmd, opts) w:activate() end, -- insert a string into the command line at the current cursor position insert_cmd = function (w, str) if not str then return end local i = w.ibar.input local text = i.text local pos = i.position local left, right = string.sub(text, 1, pos), string.sub(text, pos+1) i.text = left .. str .. right i.position = pos + #str end, -- Emulates pressing the Return key in input field activate = function (w) w.ibar.input:emit_signal("activate") end, del_word = function (w) local i = w.ibar.input local text = i.text local pos = i.position if text and #text > 1 and pos > 1 then local left, right = string.sub(text, 2, pos), string.sub(text, pos+1) if not string.find(left, "%s") then left = "" elseif string.find(left, "%w+%s*$") then left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1) elseif string.find(left, "%W+%s*$") then left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1) end i.text = string.sub(text, 1, 1) .. left .. right i.position = #left + 1 end end, del_line = function (w) local i = w.ibar.input if not string.match(i.text, "^[:/?]$") then i.text = string.sub(i.text, 1, 1) i.position = -1 end end, del_backward_char = function (w) local i = w.ibar.input local text = i.text local pos = i.position if pos > 1 then i.text = string.sub(text, 0, pos - 1) .. string.sub(text, pos + 1) i.position = pos - 1 end end, del_forward_char = function (w) local i = w.ibar.input local text = i.text local pos = i.position i.text = string.sub(text, 0, pos) .. string.sub(text, pos + 2) i.position = pos end, beg_line = function (w) local i = w.ibar.input i.position = 1 end, end_line = function (w) local i = w.ibar.input i.position = -1 end, forward_char = function (w) local i = w.ibar.input i.position = i.position + 1 end, backward_char = function (w) local i = w.ibar.input local pos = i.position if pos > 1 then i.position = pos - 1 end end, forward_word = function (w) local i = w.ibar.input local text = i.text local pos = i.position if text and #text > 1 then local right = string.sub(text, pos+1) if string.find(right, "%w+") then local _, move = string.find(right, "%w+") i.position = pos + move end end end, backward_word = function (w) local i = w.ibar.input local text = i.text local pos = i.position if text and #text > 1 and pos > 1 then local left = string.reverse(string.sub(text, 2, pos)) if string.find(left, "%w+") then local _, move = string.find(left, "%w+") i.position = pos - move end end end, -- Shows a notification until the next keypress of the user. notify = function (w, msg, set_mode) if set_mode ~= false then w:set_mode() end w:set_prompt(msg, { fg = theme.notif_fg, bg = theme.notif_bg }) end, warning = function (w, msg, set_mode) if set_mode ~= false then w:set_mode() end w:set_prompt(msg, { fg = theme.warning_fg, bg = theme.warning_bg }) end, error = function (w, msg, set_mode) if set_mode ~= false then w:set_mode() end w:set_prompt("Error: "..msg, { fg = theme.error_fg, bg = theme.error_bg }) end, -- Set and display the prompt set_prompt = function (w, text, opts) local prompt, ebox, opts = w.ibar.prompt, w.ibar.ebox, opts or {} prompt:hide() -- Set theme fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg if prompt.fg ~= fg then prompt.fg = fg end if ebox.bg ~= bg then ebox.bg = bg end -- Set text or remain hidden if text then prompt.text = lousy.util.escape(text) prompt:show() end end, -- Set display and focus the input bar set_input = function (w, text, opts) local input, opts = w.ibar.input, opts or {} input:hide() -- Set theme fg, bg = opts.fg or theme.ibar_fg, opts.bg or theme.ibar_bg if input.fg ~= fg then input.fg = fg end if input.bg ~= bg then input.bg = bg end -- Set text or remain hidden if text then input.text = "" input:show() input:focus() input.text = text input.position = opts.pos or -1 end end, -- GUI content update functions update_tab_count = function (w) w.sbar.r.tabi.text = string.format("[%d/%d]", w.tabs:current(), w.tabs:count()) end, update_win_title = function (w) local uri, title = w.view.uri, w.view.title title = (title or "luakit") .. ((uri and " - " .. uri) or "") local max = globals.max_title_len or 80 if #title > max then title = string.sub(title, 1, max) .. "..." end w.win.title = title end, update_uri = function (w, link) w.sbar.l.uri.text = lousy.util.escape((link and "Link: " .. link) or (w.view and w.view.uri) or "about:blank") end, update_progress = function (w) local p = w.view.progress local loaded = w.sbar.l.loaded if not w.view:loading() or p == 1 then loaded:hide() else loaded:show() loaded.text = string.format("(%d%%)", p * 100) end end, update_scroll = function (w) local scroll, label = w.view.scroll, w.sbar.r.scroll local y, max, text = scroll.y, scroll.ymax if max == 0 then text = "All" elseif y == 0 then text = "Top" elseif y == max then text = "Bot" else text = string.format("%2d%%", (y / max) * 100) end if label.text ~= text then label.text = text end end, update_ssl = function (w) local trusted = w.view:ssl_trusted() local ssl = w.sbar.r.ssl if trusted ~= nil and not w.checking_ssl then ssl.fg = theme.notrust_fg ssl.text = "(nocheck)" ssl:show() elseif trusted == true then ssl.fg = theme.trust_fg ssl.text = "(trust)" ssl:show() elseif trusted == false then ssl.fg = theme.notrust_fg ssl.text = "(notrust)" ssl:show() else ssl:hide() end end, update_hist = function (w) local hist = w.sbar.l.hist local back, forward = w.view:can_go_back(), w.view:can_go_forward() local s = (back and "+" or "") .. (forward and "-" or "") if s ~= "" then hist.text = '['..s..']' hist:show() else hist:hide() end end, update_buf = function (w) local buf = w.sbar.r.buf if w.buffer then buf.text = lousy.util.escape(string.format(" %-3s", w.buffer)) buf:show() else buf:hide() end end, update_binds = function (w, mode) -- Generate the list of active key & buffer binds for this mode w.binds = lousy.util.table.join((get_mode(mode) or {}).binds or {}, get_mode('all').binds or {}) -- Clear & hide buffer w.buffer = nil w:update_buf() end, update_tablist = function (w) local current = w.tabs:current() local fg, bg, nfg, snfg = theme.tab_fg, theme.tab_bg, theme.tab_ntheme, theme.selected_ntheme local lfg, bfg, gfg = theme.tab_loading_fg, theme.tab_notrust_fg, theme.tab_trust_fg local escape = lousy.util.escape local tabs, tfmt = {}, ' %s %s' for i, view in ipairs(w.tabs.children) do -- Get tab number theme local ntheme = nfg if view:loading() then -- Show loading on all tabs ntheme = lfg elseif current == i then -- Show ssl trusted/untrusted on current tab local trusted = view:ssl_trusted() ntheme = snfg if trusted == false or (trusted ~= nil and not w.checking_ssl) then ntheme = bfg elseif trusted then ntheme = gfg end end local title = view.title or view.uri or "(Untitled)" tabs[i] = { title = string.format(tfmt, ntheme or fg, i, escape(title)), fg = (current == i and theme.tab_selected_fg) or fg, bg = (current == i and theme.tab_selected_bg) or bg, } end if #tabs < 2 then tabs, current = {}, 0 end w.tablist:update(tabs, current) end, new_tab = function (w, arg, switch, order) local view -- Use blank tab first if w.has_blank and w.tabs:count() == 1 and w.tabs[1].uri == "about:blank" then view = w.tabs[1] end w.has_blank = nil -- Make new webview widget if not view then view = webview.new(w) -- Get tab order function if not order and taborder then order = (switch == false and taborder.default_bg) or taborder.default end pos = w.tabs:insert((order and order(w, view)) or -1, view) if switch ~= false then w.tabs:switch(pos) end end -- Load uri or webview history table if type(arg) == "string" then view.uri = arg elseif type(arg) == "table" then view.history = arg end -- Update statusbar widgets w:update_tab_count() w:update_tablist() return view end, -- close the current tab close_tab = function (w, view, blank_last) view = view or w.view -- Treat a blank last tab as an empty notebook (if blank_last=true) if blank_last ~= false and w.tabs:count() == 1 then if not view:loading() and view.uri == "about:blank" then return end w:new_tab("about:blank", false) w.has_blank = true end -- Save tab history local tab = {hist = view.history,} -- And relative location local index = w.tabs:indexof(view) if index ~= 1 then tab.after = w.tabs[index-1] end table.insert(w.closed_tabs, tab) view:destroy() w:update_tab_count() w:update_tablist() end, close_win = function (w, force) -- Ask plugins if it's OK to close last window if not force and (#luakit.windows == 1) then local emsg = luakit.emit_signal("can-close", w) if emsg then assert(type(emsg) == "string", "invalid exit error message") w:error(string.format("Can't close luakit: %s (force close " .. "with :q! or :wq!)", emsg)) return false end end w:emit_signal("close") -- Close all tabs while w.tabs:count() ~= 0 do w:close_tab(nil, false) end -- Destroy tablist w.tablist:destroy() -- Remove from window index window.bywidget[w.win] = nil -- Clear window struct w = setmetatable(w, {}) -- Recursively remove widgets from window local children = lousy.util.recursive_remove(w.win) -- Destroy all widgets for i, c in ipairs(lousy.util.table.join(children, {w.win})) do if c.hide then c:hide() end c:destroy() end -- Remove all window table vars for k, _ in pairs(w) do w[k] = nil end -- Quit if closed last window if #luakit.windows == 0 then luakit.quit() end end, -- Navigate current view or open new tab navigate = function (w, uri, view) view = view or w.view if view then local js = string.match(uri, "^javascript:(.+)$") if js then return view:eval_js(luakit.uri_decode(js)) end view.uri = uri else return w:new_tab(uri) end end, -- Save, restart luakit and reload session. restart = function (w) -- Generate luakit launch command. local args = {({string.gsub(luakit.execpath, " ", "\\ ")})[1]} if luakit.verbose then table.insert(args, "-v") end -- Relaunch without libunique bindings? if luakit.nounique then table.insert(args, "-U") end -- Get new config path local conf if luakit.confpath ~= "/etc/xdg/luakit/rc.lua" and os.exists(luakit.confpath) then conf = luakit.confpath table.insert(args, string.format("-c %q", conf)) end -- Check config has valid syntax local cmd = table.concat(args, " ") if luakit.spawn_sync(cmd .. " -k") ~= 0 then return w:error("Cannot restart, syntax error in configuration file"..((conf and ": "..conf) or ".")) end -- Save session. local wins = {} for _, w in pairs(window.bywidget) do table.insert(wins, w) end session.save(wins) -- Replace current process with new luakit instance. luakit.exec(cmd) end, -- Intelligent open command which can detect a uri or search argument. search_open = function (w, arg) local lstring = lousy.util.string local match, find = string.match, string.find -- Detect blank uris if not arg or match(arg, "^%s*$") then return "about:blank" end -- Strip whitespace and split by whitespace into args table local args = lstring.split(lstring.strip(arg)) -- Guess if first argument is an address, search engine, file if #args == 1 then local uri = args[1] if uri == "about:blank" then return uri end -- Check if search engine name if search_engines[uri] then return string.format(search_engines[uri], "") end -- Navigate if . or / in uri (I.e. domains, IP's, scheme://) if find(uri, "%.") or find(uri, "/") then return uri end -- Navigate if this is a javascript-uri if find(uri, "^javascript:") then return uri end -- Valid hostnames to check local hosts = { "localhost" } if globals.load_etc_hosts ~= false then hosts = lousy.util.get_etc_hosts() end -- Check hostnames for _, h in pairs(hosts) do if h == uri or match(uri, "^"..h..":%d+$") then return uri end end -- Check for file in filesystem if globals.check_filepath ~= false then if lfs.attributes(uri) then return "file://" .. uri end end end -- Find search engine (or use search_engines.default) local engine = "default" if args[1] and search_engines[args[1]] then engine = args[1] table.remove(args, 1) end -- URI encode search terms local terms = luakit.uri_encode(table.concat(args, " ")) return string.format(search_engines[engine], terms) end, -- Increase (or decrease) the last found number in the current uri inc_uri = function (w, arg) local uri = string.gsub(w.view.uri, "(%d+)([^0-9]*)$", function (num, rest) return string.format("%0"..#num.."d", tonumber(num) + (arg or 1)) .. rest end) return uri end, -- Tab traversing functions next_tab = function (w, n) w.tabs:switch((((n or 1) + w.tabs:current() -1) % w.tabs:count()) + 1) end, prev_tab = function (w, n) w.tabs:switch(((w.tabs:current() - (n or 1) -1) % w.tabs:count()) + 1) end, goto_tab = function (w, n) if n and (n == -1 or n > 0) then return w.tabs:switch((n <= w.tabs:count() and n) or -1) end end, -- For each tab, switches to that tab and calls the given function passing -- it the view contained in the tab. each_tab = function (w, fn) for index = 1, w.tabs:count() do w:goto_tab(index) fn(w.tabs[index]) end end, -- If argument is form-active or root-active, emits signal. Ignores all -- other signals. emit_form_root_active_signal = function (w, s) if s == "form-active" then w.view:emit_signal("form-active") elseif s == "root-active" then w.view:emit_signal("root-active") end end, } -- Ordered list of class index functions. Other classes (E.g. webview) are able -- to add their own index functions to this list. window.indexes = { -- Find function in window.methods first function (w, k) return window.methods[k] end } -- Create new window function window.new(uris) local w = window.build() -- Set window metatable setmetatable(w, { __index = function (_, k) -- Check widget structure first local v = rawget(w, k) if v then return v end -- Call each window index function for _, index in ipairs(window.indexes) do v = index(w, k) if v then return v end end end, }) -- Setup window widget for signals lousy.signal.setup(w) -- Call window init functions for _, func in pairs(window.init_funcs) do func(w) end -- Populate notebook with tabs for _, uri in ipairs(uris or {}) do w:new_tab(w:search_open(uri), false) end -- Make sure something is loaded if w.tabs:count() == 0 then w:new_tab(w:search_open(globals.homepage), false) end -- Set initial mode w:set_mode() -- Show window w.win:show() return w end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/extras/000077500000000000000000000000001202446317700152365ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/bookmarks_migrate.lua000077500000000000000000000036311202446317700214470ustar00rootroot00000000000000local usage = [[Usage: luakit -c bookmark_migrate.lua [old bookmark db path] [new bookmark db path] Imports bookmarks from old database schema: CREATE TABLE IF NOT EXISTS bookmarks ( id INTEGER PRIMARY KEY, uri TEXT NOT NULL, title TEXT, desc TEXT, created INTEGER, modified INTEGER ); CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY, name TEXT ); CREATE TABLE IF NOT EXISTS tagmap ( id INTEGER PRIMARY KEY, bookmark_id INTEGER, tag_id INTEGER, FOREIGN KEY(bookmark_id) REFERENCES bookmarks(id), FOREIGN KEY(tag_id) REFERENCES tags(id) ); To the new schema: CREATE TABLE IF NOT EXISTS bookmarks ( id INTEGER PRIMARY KEY, uri TEXT NOT NULL, title TEXT NOT NULL, desc TEXT NOT NULL, tags TEXT NOT NULL, created INTEGER, modified INTEGER ); ]] local old_db_path, new_db_path = unpack(uris) if not old_db_path or not new_db_path then io.stdout:write(usage) luakit.quit(1) end old_db = sqlite3{ filename = old_db_path } new_db = sqlite3{ filename = new_db_path } local rows = old_db:exec [[ SELECT b.*, group_concat(t.name, ' ') AS tags FROM bookmarks AS b LEFT JOIN tagmap AS map LEFT JOIN tags AS t ON map.bookmark_id = b.id AND map.tag_id = t.id GROUP BY b.id ]] for i, b in ipairs(rows) do print(string.format("IMPORT (%q, %q, %q, %q, %d, %d)", b.uri or "", b.title or "", b.desc or "", b.tags or "", b.created or 0, b.modified or 0)) new_db:exec("INSERT INTO bookmarks VALUES (NULL, ?, ?, ?, ?, ?, ?)", { b.uri or "", b.title or "", b.desc or "", b.tags or "", b.created or os.time(), b.modified or os.time() }) end print("Import finished.") print("\nVacuuming database...") new_db:exec "VACUUM" print("Vacuum done.") luakit.quit(0) luakit-2012.09.13-r1/extras/convert_formfiller.rb000066400000000000000000000043341202446317700214700ustar00rootroot00000000000000#!/usr/bin/ruby # This script converts files from the old formfiller syntax to the new # Lua DSL. # # To use it, run the following in your favorite shell: # # for f in ~/.local/share/luakit/forms/*; do ./convert.rb $f >> ~/.local/share/luakit/forms.lua; done # # BEWARE: this script is not fool-proof. Errors may occur during conversion, # so do a manual check after the conversion! def die(msg) puts msg exit 1 end file = ARGV[0] or die("ruby convert.rb ") f = File.new(file, "r") forms = [] while (line = f.gets) form = forms.last if line =~ /^!profile/ form = { :inputs => [], } forms << form end next if line =~ /^> vim/ form[:profile] = $1 if line =~ /^!profile=(.*)/ if line =~ /^!form\[(.*?)\|(.*?)\|(.*?)\|(.*?)\]:autosubmit=(.)/ form[:name] = $1 unless $1 == "" form[:id] = $2 unless $2 == "" form[:method] = $3 unless $3 == "" form[:action] = $4 if %w{get post}.include?($4) form[:submit] = ($5 == "1") elsif line =~ /^(.*?)(\{.*?\})?\((.*?)\):(.*)/ input = {} input[:name] = $1 input[:type] = $3 if %w{text password checkbox radio submit reset file hidden image buttontext password checkbox radio submit reset file hidden image button}.include?($3) input[:fill] = case fill = $4 when /^on$/i then true when /^off$/i then false else fill end form[:inputs] << input end end exit unless forms.size > 0 puts < 1 puts <EVWW˃dttT^X,Ļ!u}@~1z`ǎbXϷHNDMGG333wJUQe ~W|߿"055`R7o GwB>/_211nk)E8&//SNa8|0ܹcǎ_m۶Nqq1yyyLw*yf0wIO]u=?\nn.6YL&{wɓ'ZzS.` 7'В?8qH$¹s稯'??[ri:4)}zڵk?~:= ۷o4/^i&vƍyuJ{uuu}}}a*++QJٳg>/%ǁO/ @JEcc#W\I\pN("ח)bπg Ξ=Kvv6X,嗶[P~[M7sssh(U*b!DՎZnHhtsAAtkxd= ,텪>7@ Vy Blٲn022RW^s.97ӵx<.Erˠ3Ki2f[ E555q…*O:$e`rA(Rl 6$i7r_rFAi6 2 2Gv4OIENDB`luakit-2012.09.13-r1/extras/vim/000077500000000000000000000000001202446317700160315ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/vim/ftdetect/000077500000000000000000000000001202446317700176335ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/vim/ftdetect/luakit.vim000066400000000000000000000001511202446317700216360ustar00rootroot00000000000000au BufNewFile,BufRead */luakit/**/*.lua set ft=luakit au BufNewFile,BufRead */luakit/*.lua set ft=luakit luakit-2012.09.13-r1/extras/vim/ftplugin/000077500000000000000000000000001202446317700176615ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/vim/ftplugin/luakit.vim000066400000000000000000000036541202446317700216770ustar00rootroot00000000000000" Vim filetype plugin " Language: luakit configuration " Maintainer: Gregor Uhlenheuer <kongo2002@googlemail.com> " Last Change: Tue 14 Sep 2010 01:18:20 PM CEST " Custom configuration: " " Set 'g:luakit_prefix' to the path prefix where the system-wide " luakit configuration files are installed to. The default is set to " something like '/etc/xdg' or '/usr/share/xdg'. " If this variable is not defined the path is tried to determine via the " environment variable $XDG_CONFIG_DIRS " " let g:luakit_prefix = '/etc/xdg' " " Defined mappings (buffer-local): " " ld Diff current config file with its system-wide counterpart if exists('b:did_luakit') finish endif let b:did_luakit = 1 if !exists('g:luakit_prefix') let g:luakit_prefix = '/etc/xdg' endif function! s:GetFile() let fcomponents = [] if $XDG_CONFIG_DIRS != '' call add(fcomponents, $XDG_CONFIG_DIRS) else call add(fcomponents, g:luakit_prefix) endif call add(fcomponents, "luakit") call add(fcomponents, expand('%:t')) let config_file = join(fcomponents, '/') if filereadable(config_file) return config_file endif return '' endfunction if !exists('*CompareLuakitFile') function! CompareLuakitFile() let file = GetFile() if file != '' if file != expand('%:p') exe 'vert diffsplit' file wincmd p else echohl WarningMsg echom 'You cannot compare the file with itself' echohl None endif else echohl WarningMsg echom 'Could not find system-wide luakit '''.expand('%:t').''' file' echom 'Please define ''g:luakit_prefix''' echohl None endif endfunction endif com! -buffer LuakitDiff call CompareLuakitFile() nmap ld :LuakitDiff runtime! ftplugin/lua.vim luakit-2012.09.13-r1/extras/vim/indent/000077500000000000000000000000001202446317700173125ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/vim/indent/luakit.vim000066400000000000000000000003601202446317700213170ustar00rootroot00000000000000" Vim indent file " Language: luakit configuration " Maintainer: Gregor Uhlenheuer <kongo2002@googlemail.com> " Last Change: Fri 27 Aug 2010 09:49:20 PM CEST if exists('b:did_indent') finish endif runtime! indent/lua.vim luakit-2012.09.13-r1/extras/vim/syntax/000077500000000000000000000000001202446317700173575ustar00rootroot00000000000000luakit-2012.09.13-r1/extras/vim/syntax/luakit.vim000066400000000000000000000022311202446317700213630ustar00rootroot00000000000000" Vim syntax file " Language: luakit configuration " Maintainer: Gregor Uhlenheuer <kongo2002@googlemail.com> " Last Change: Fri 27 Aug 2010 09:46:46 PM CEST if exists('b:current_syntax') finish endif runtime! syntax/lua.vim " Javascript between [=[ & ]=] regions unlet b:current_syntax syntax include @JAVASCRIPT syntax/javascript.vim try | syntax include @JAVASCRIPT after/syntax/javascript.vim | catch | endtry syntax region jsBLOCK matchgroup=jsBLOCKMATCH start=/\[=\[/ end=/\]=\]/ contains=@JAVASCRIPT hi link jsBLOCKMATCH SpecialComment " HTML between [==[ & ]==] regions unlet b:current_syntax syntax include @HTML syntax/html.vim try | syntax include @HTML after/syntax/html.vim | catch | endtry syntax region htmlBLOCK matchgroup=htmlBLOCKMATCH start=/\[==\[/ end=/\]==\]/ contains=@HTML hi link htmlBLOCKMATCH SpecialComment " CSS between [===[ & ]===] regions unlet b:current_syntax syntax include @CSS syntax/css.vim try | syntax include @CSS after/syntax/css.vim | catch | endtry syntax region cssBLOCK matchgroup=cssBLOCKMATCH start=/\[===\[/ end=/\]===\]/ contains=@CSS hi link cssBLOCKMATCH SpecialComment let b:current_syntax = 'luakit' luakit-2012.09.13-r1/globalconf.h.in000066400000000000000000000054541202446317700166240ustar00rootroot00000000000000/* * globalconf.h - main config struct * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_GLOBALCONF #define LUAKIT_GLOBALCONF /** Install location of luakit shared files. This serves as the base path for * all system Lua module loading paths. */ #define LUAKIT_INSTALL_PATH "/usr/local/share/luakit" /** Registry key for the Lua registry API to store a private reference counting * table. This table prevents garbage collection of objects (userdata or * tables) while in use by C functions or objects. * \see http://www.lua.org/manual/5.1/manual.html#3.5 */ #define LUAKIT_OBJECT_REGISTRY_KEY "luakit.object.registry" #include #include #include "common/signal.h" /** Global luakit state struct. */ typedef struct { /** Main Lua VM state. * \see http://www.lua.org/manual/5.1/manual.html#lua_State */ lua_State *L; /** User path $XDG_CONFIG_DIR/luakit/ (defaults to ~/.config/luakit/) where * configuration files should be stored. * \see http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ gchar *config_dir; /** User path $XDG_DATA_DIR/luakit/ (defaults to ~/.local/share/luakit/) * where data files should be stored. * \see http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ gchar *data_dir; /** User path $XDG_CACHE_DIR/luakit/ (defaults to ~/.cache/luakit/) where * non-essential data files should be stored. * \see http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html */ gchar *cache_dir; /** Path to the currently loaded config file. */ gchar *confpath; /** The luakit executable path. */ gchar *execpath; /** Print verbose output. */ gboolean verbose; /** Ignore loading libunqiue bindings (for a single instance session) */ gboolean nounique; /** Pointer array to all active window userdata objects. */ GPtrArray *windows; /** Pointer array to all active webview userdata objects. */ GPtrArray *webviews; /** Start time for debug messages */ gdouble starttime; } globalconf_t; globalconf_t globalconf; #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/000077500000000000000000000000001202446317700144765ustar00rootroot00000000000000luakit-2012.09.13-r1/lib/bookmarks.lua000066400000000000000000000071441202446317700171770ustar00rootroot00000000000000----------------------------------------------------------- -- Simple sqlite3 bookmarks -- -- © 2010-2012 Mason Larobina -- ----------------------------------------------------------- local lousy = require "lousy" local string = string local table = table local type = type local assert = assert local ipairs = ipairs local os = os local error = error local capi = { luakit = luakit, sqlite3 = sqlite3 } local keys = lousy.util.table.keys module("bookmarks") lousy.signal.setup(_M, true) -- Path to users bookmarks database db_path = capi.luakit.data_dir .. "/bookmarks.db" function init() db = capi.sqlite3{ filename = _M.db_path } db:exec [[ PRAGMA synchronous = OFF; PRAGMA secure_delete = 1; CREATE TABLE IF NOT EXISTS bookmarks ( id INTEGER PRIMARY KEY, uri TEXT NOT NULL, title TEXT NOT NULL, desc TEXT NOT NULL, tags TEXT NOT NULL, created INTEGER, modified INTEGER ); ]] end capi.luakit.idle_add(init) -- Validate tag name local function valid_tag_name(name) return not not string.match(name, "^%w[%w-]*$") end function get(id) assert(type(id) == "number", "invalid bookmark id (number expected)") local rows = db:exec([[ SELECT * FROM bookmarks WHERE id = ? ]], { id }) return rows[1] end function remove(id) assert(type(id) == "number", "invalid bookmark id (number expected)") _M.emit_signal("remove", id) db:exec([[ DELETE FROM bookmarks WHERE id = ? ]], { id }) end local function parse_tags(tags) local ret = {} local remains = string.gsub(tags, "%w[%w-]*", function (tag) ret[tag] = true return "" end) return ret, remains end local function update_tags(b, tags) table.sort(tags) tags = table.concat(tags, " ") db:exec([[ UPDATE bookmarks SET tags = ?, modified = ? WHERE id = ? ]], { tags, os.time(), b.id }) _M.emit_signal("update", id) end function tag(id, new_tags, replace) local b = assert(get(id), "bookmark not found") if type(new_tags) == "table" then new_tags = table.concat(new_tags, " ") end local all_tags = string.format("%s %s", new_tags, (not replace and b.tags) or "") local tags, remains = parse_tags(all_tags) if string.find(remains, "[^%s,]") then error("invalid tags: " .. remains) end update_tags(b, keys(tags)) end function untag(id, name) local b = assert(get(id), "bookmark not found") if b.tags then local tags = parse_tags(b.tags) tags[name] = nil update_tags(b, keys(tags)) end end -- Add new bookmark function add(uri, opts) opts = opts or {} assert(type(uri) == "string" and #uri > 0, "invalid bookmark uri") assert(opts.title == nil or type(opts.title) == "string", "invalid bookmark title") assert(opts.desc == nil or type(opts.desc) == "string", "invalid bookmark description") assert(opts.created == nil or type(opts.created) == "number", "invalid creation time") -- Default to http:// scheme if none provided if not string.match(uri, "^%w+://") then uri = "http://" .. uri end db:exec("INSERT INTO bookmarks VALUES (NULL, ?, ?, ?, ?, ?, ?)", { uri, opts.title or "", opts.desc or "", "", opts.created or os.time(), os.time() -- modified time (now) }) local id = db:exec("SELECT last_insert_rowid() AS id")[1].id _M.emit_signal("add", id) -- Add bookmark tags if opts.tags then tag(id, opts.tags) end return id end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/bookmarks_chrome.lua000066400000000000000000000341451202446317700205350ustar00rootroot00000000000000-- Grab what we need from the Lua environment local table = table local string = string local io = io local print = print local pairs = pairs local ipairs = ipairs local math = math local assert = assert local setmetatable = setmetatable local rawget = rawget local rawset = rawset local type = type local os = os local error = error -- Grab the luakit environment we need local bookmarks = require("bookmarks") local lousy = require("lousy") local chrome = require("chrome") local markdown = require("markdown") local sql_escape = lousy.util.sql_escape local add_binds = add_binds local add_cmds = add_cmds local webview = webview local capi = { luakit = luakit } module("bookmarks.chrome") -- Display the bookmark uri and title. show_uri = false stylesheet = [===[ .bookmark { line-height: 1.6em; padding: 0.4em 0.5em; margin: 0; left: 0; right: 0; border: 1px solid #fff; border-radius: 0.3em; } .bookmark .title, .bookmark .uri { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bookmark .top { position: relative; } .bookmark .title a { font-weight: normal; font-size: 1.4em; text-decoration: none; } .bookmark .title a:hover { text-decoration: underline; } .bookmark .uri, .bookmark .desc { display: none; } .bookmark .uri { color: #aaa; } .bookmark .bottom { white-space: nowrap; } .bookmark .bottom a { text-decoration: none; -webkit-user-select: none; cursor: default; } .bookmark .bottom a:hover { cursor: pointer; } .bookmark .tags a { color: #666; background-color: #f6f6f6; padding: 0.1em 0.4em; margin: 0 0.3em; -webkit-border-radius: 0.2em; -webkit-box-shadow: 0 0.1em 0.1em #666; } .bookmark .tags a:hover { color: #111; } .bookmark .desc { color: #222; border-left: 0.3em solid #ddd; margin: 0 0 0.2em 0.5em; padding: 0 0 0 0.5em; max-width: 60em; } .bookmark .desc > * { margin-top: 0.2em; margin-bottom: 0.2em; } .bookmark .desc > :first-child { margin-top: 0; } .bookmark .desc > :last-child { margin-bottom: 0; } .bookmark .controls a { color: #888; padding: 0.1em 0.4em; margin: 0 0; } .bookmark .controls a:hover { background-color: #fff; -webkit-border-radius: 0.2em; -webkit-box-shadow: 0 0.1em 0.1em #666; } .bookmark .date { color: #444; margin-right: 0.2em; } #templates { display: none; } #blackout { position: fixed; left: 0; right: 0; top: 0; bottom: 0; opacity: 0.5; background-color: #000; z-index: 100; } #edit-dialog { position: fixed; z-index: 101; font-size: 1.3em; font-weight: 100; top: 6em; left: 50%; margin-left: -20em; margin-bottom: 6em; padding: 2em; width: 36em; background-color: #eee; border-radius: 0.5em; box-shadow: 0 0.5em 1em #000; } #edit-dialog td:first-child { vertical-align: middle; text-align: right; width: 4em; } #edit-dialog td { padding: 0.3em; } #edit-dialog input, #edit-dialog textarea { font-size: inherit; border: none; outline: none; margin: 0; padding: 0; background-color: #fff; border-radius: 0.25em; box-shadow: 0 0.1em 0.1em #888; } #edit-dialog input[type="text"], #edit-dialog textarea { width: 30em; padding: 0.5em; } #edit-dialog input[type="button"] { padding: 0.5em 1em; margin-right: 0.5em; color: #444; } #edit-dialog textarea { height: 5em; } #edit-view { display: none; } ]===] local html = [==[ Bookmarks
Title:
URI:
Tags:
Info:
]==] local main_js = [=[ $(document).ready(function () { 'use strict' var bookmark_html = $("#bookmark-skelly").html(), $results = $("#results"), $search = $("#search"), $edit_view = $("#edit-view"), $edit_dialog = $("#edit-dialog"); function make_bookmark(b) { var $b = $(bookmark_html); $b.attr("bookmark_id", b.id); $b.find(".title a").attr("href", b.uri).text(b.title || b.uri); $b.find(".date").text(b.date); if (b.title) $b.find(".uri").text(b.uri).show(); if (b.markdown_desc) $b.find(".desc").html(b.markdown_desc).show(); if (b.tags) { var $tags = $b.find(".tags"), tags = b.tags.split(" "), len = tags.length, i = 0; for (; i < len;) $tags.append($("").text(tags[i++])); } return $b.prop("outerHTML"); } function show_edit(b) { b = b || {}; var $e = $edit_dialog; $e.attr("bookmark_id", b.id); $e.attr("created", b.created); $e.find(".title").val(b.title); $e.find(".uri").val(b.uri); $e.find(".tags").val(b.tags); $e.find(".desc").val(b.desc); $edit_view.fadeIn("fast", function () { $edit_dialog.find(".title").focus(); }); } function find_bookmark_parent(that) { return $(that).parents(".bookmark").eq(0); } function search() { var results = bookmarks_search({ query: $search.val() }); if (results.length === "undefined") { $results.empty(); return; } /* display results */ var html = ""; for (var i = 0; i < results.length; i++) html += make_bookmark(results[i]); $results.get(0).innerHTML = html; }; /* input field callback */ $search.keydown(function(ev) { if (ev.which == 13) { /* Return */ search(); $search.blur(); reset_mode(); } }); // 'delete' callback $results.on("click", ".bookmark .controls .delete-button", function (e) { var $b = find_bookmark_parent(this); // delete bookmark from database bookmarks_remove(parseInt($b.attr("bookmark_id"))); // remove/hide bookmark from list $b.slideUp(function() { $b.remove(); }); }); $results.on("click", ".bookmark .tags a", function () { $search.val($(this).text()); search(); }); $results.on("click", ".bookmark .controls .edit-button", function (e) { var $b = find_bookmark_parent(this); var b = bookmarks_get(parseInt($b.attr("bookmark_id"))); show_edit(b); }); function edit_submit() { var $e = $edit_dialog, id = $e.attr("bookmark_id"), created = $e.attr("created"); try { bookmarks_add($e.find(".uri").val(), { title: $e.find(".title").val(), tags: $e.find(".tags").val(), desc: $e.find(".desc").val(), created: created ? parseInt(created) : undefined, }); } catch (err) { alert(err); return; } // Delete existing bookmark (only when editing bookmark) if (id) bookmarks_remove(parseInt(id)); search(); $edit_view.fadeOut("fast"); }; $edit_dialog.on("click", ".submit-button", function (e) { edit_submit(); }); $edit_dialog.find('input[type="text"]').keydown(function(ev) { if (ev.which == 13) /* Return */ edit_submit(); }); $edit_dialog.on("click", ".cancel-button", function (e) { $edit_view.fadeOut("fast"); }); $("#new-button").click(function () { show_edit(); }); $("#clear-button").click(function () { $search.val(""); search(); }); $("#search-button").click(function () { search(); }); search(); var values = new_bookmark_values(); if (values) show_edit(values); }); ]=] local new_bookmark_values export_funcs = { bookmarks_search = function (opts) if not bookmarks.db then bookmarks.init() end local sql = { "SELECT", "*", "FROM bookmarks" } local where, args, argc = {}, {}, 1 string.gsub(opts.query or "", "(-?)([^%s]+)", function (notlike, term) if term ~= "" then table.insert(where, (notlike == "-" and "NOT " or "") .. string.format("(text GLOB ?%d)", argc, argc)) argc = argc + 1 table.insert(args, "*"..string.lower(term).."*") end end) if #where ~= 0 then sql[2] = [[ *, lower(uri||title||desc||tags) AS text ]] table.insert(sql, "WHERE " .. table.concat(where, " AND ")) end local order_by = [[ ORDER BY created DESC LIMIT ?%d OFFSET ?%d ]] table.insert(sql, string.format(order_by, argc, argc+1)) local limit, page = opts.limit or 100, opts.page or 1 table.insert(args, limit) table.insert(args, limit > 0 and (limit * (page - 1)) or 0) sql = table.concat(sql, " ") if #where ~= 0 then local wrap = [[SELECT id, uri, title, desc, tags, created, modified FROM (%s)]] sql = string.format(wrap, sql) end local rows = bookmarks.db:exec(sql, args) local date = os.date for _, row in ipairs(rows) do row.date = date("%d %B %Y", row.created) local desc = row.desc if desc and string.find(desc, "%S") then row.markdown_desc = markdown(desc) end end return rows end, bookmarks_add = bookmarks.add, bookmarks_get = bookmarks.get, bookmarks_remove = bookmarks.remove, new_bookmark_values = function () local values = new_bookmark_values new_bookmark_values = nil return values end, } chrome.add("bookmarks", function (view, meta) local uri = "luakit://bookmarks/" local style = chrome.stylesheet .. _M.stylesheet if not _M.show_uri then style = style .. " .bookmark .uri { display: none !important; } " end local html = string.gsub(html, "{%%(%w+)}", { stylesheet = style }) view:load_string(html, uri) function on_first_visual(_, status) -- Wait for new page to be created if status ~= "first-visual" then return end -- Hack to run-once view:remove_signal("load-status", on_first_visual) -- Double check that we are where we should be if view.uri ~= uri then return end -- Export luakit JS<->Lua API functions for name, func in pairs(export_funcs) do view:register_function(name, func) end view:register_function("reset_mode", function () meta.w:set_mode() -- HACK to unfocus search box end) -- Load jQuery JavaScript library local jquery = lousy.load("lib/jquery.min.js") local _, err = view:eval_js(jquery, { no_return = true }) assert(not err, err) -- Load main luakit://download/ JavaScript local _, err = view:eval_js(main_js, { no_return = true }) assert(not err, err) end view:add_signal("load-status", on_first_visual) end) chrome_page = "luakit://bookmarks/" local key, buf = lousy.bind.key, lousy.bind.buf add_binds("normal", { key({}, "B", "Shortcut to add a bookmark to the current URL", function(w) new_bookmark_values = { uri = w.view.uri, title = w.view.title } w:new_tab(chrome_page) end), buf("^gb$", "Open bookmarks manager in the current tab.", function(w) w:navigate(chrome_page) end), buf("^gB$", "Open bookmarks manager in a new tab.", function(w) w:new_tab(chrome_page) end) }) local cmd = lousy.bind.cmd add_cmds({ cmd("bookmarks", "Open bookmarks manager in a new tab.", function (w) w:new_tab(chrome_page) end), cmd("bookmark", "Add bookmark", function (w, a) if not a then new_bookmark_values = { uri = w.view.uri, title = w.view.title } else a = lousy.util.string.split(a) new_bookmark_values = { uri = a[1], tags = table.concat(a, " ", 2) } end w:new_tab(chrome_page) end), }) luakit-2012.09.13-r1/lib/chrome.lua000066400000000000000000000114711202446317700164620ustar00rootroot00000000000000------------------------------------------------------------ -- Add custom luakit:// scheme rendering functions -- -- © 2010-2012 Mason Larobina -- -- © 2010 Fabian Streitel -- ------------------------------------------------------------ -- Get lua environment local assert = assert local string = string local type = type local xpcall = xpcall local debug = debug -- Get luakit environment local webview = webview module("chrome") -- Common stylesheet that can be sourced from several chrome modules for a -- consitent looking theme. stylesheet = [===[ body { background-color: white; color: black; display: block; font-size: 62.5%; /* 1em == 10px @ 96dpi */ margin: 0; padding: 0; font-family: sans-serif; } #page-header { font-size: 1.3em; background-color: #eee; position: fixed; top: 0; left: 0; width: 100%; margin: 0; padding: 0; border-bottom: 1px solid #ddd; -webkit-box-shadow: 0 0.5em 2em #fff; overflow: hidden; white-space: nowrap; } header input { font-size: inherit; font-weight: 100; padding: 0.5em 0.75em; border: none; outline: none; margin: 0; background-color: #fff; } header #search-box { display: inline-block; margin: 1em 0 1em 1em; padding: 0; background-color: #fff; border-radius: 0.25em; box-shadow: 0 1px 1px #888; } header #search { width: 20em; font-weight: normal; color: #111; border-radius: 0.25em 0 0 0.25em; margin: 0; padding-right: 0; } header #clear-button { margin-left: 0; font-weight: 100; color: #444; border-radius: 0 0.25em 0.25em 0; } header #clear-button:hover { color: #000; } header #clear-button:active { background-color: #eee; } .button { box-shadow: 0 1px 1px #888; margin: 1em 0 1em 0.5em; border-radius: 0.25em; color: #444; } .button:hover { color: #000; } .button:active { background-color: #eee; } .button[disabled] { color: #888; background-color: #eee; } header .rhs { position: absolute; right: 0; padding: 0 1em 0.5em 0; margin: 0; display: inline-block; background-color: inherit; box-shadow: -1em 0 1em #eee; } header .rhs .button { margin-bottom: 0; } .content-margin { padding: 6.5em 1em 1em 1em; } .hidden { display: none; } ]===] -- luakit:// page handlers local handlers = {} function add(page, func) -- Do some sanity checking assert(type(page) == "string", "invalid chrome page name (string expected, got "..type(page)..")") assert(string.match(page, "^[%w%-]+$"), "illegal characters in chrome page name: " .. page) assert(type(func) == "function", "invalid chrome handler (function expected, got "..type(func)..")") handlers[page] = func end function remove(page) handlers[page] = nil end error_html = [==[ Chrome handler error

Error in %q handler function:

%s
]==] -- Catch all navigations to the luakit:// scheme webview.init_funcs.chrome = function (view, w) view:add_signal("navigation-request", function (_, uri) -- Match "luakit://page/path" local page, path = string.match(uri, "^luakit://([^/]+)/?(.*)") if not page then return end local func = handlers[page] if func then -- Give the handler function everything it may need local meta = { page = page, path = path, w = w, uri = "luakit://" .. page .. "/" .. path } -- Render error output in webview with traceback local function error_handler(err) view:load_string(string.format(error_html, uri, debug.traceback(err, 2)), uri) end -- Call luakit:// page handler local ok, err = xpcall(function () return func(view, meta) end, error_handler) if not ok or err ~= false then -- Stop the navigation request return false end end -- Load blank error page view:load_string("

No chrome handler for: " .. uri .. "

", uri) end) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/cmdhist.lua000066400000000000000000000042531202446317700166400ustar00rootroot00000000000000------------------------------------------------------ -- Enables command history in modes that support it -- -- © 2010 Mason Larobina -- ------------------------------------------------------ -- Input bar history binds, these are only present in modes with a history -- table so we can make some assumptions. This auto-magic is present when -- a mode contains a `history` table item (with history settings therein). local key = lousy.bind.key local hist_binds = { key({}, "Up", function (w) local h = w.mode.history local lc = h.cursor if not h.cursor and h.len > 0 then h.cursor = h.len elseif (h.cursor or 0) > 1 then h.cursor = h.cursor - 1 end if h.cursor and h.cursor ~= lc then if not h.orig then h.orig = w.ibar.input.text end w:set_input(h.items[h.cursor]) end end), key({}, "Down", function (w) local h = w.mode.history if not h.cursor then return end if h.cursor >= h.len then w:set_input(h.orig) h.cursor = nil h.orig = nil else h.cursor = h.cursor + 1 w:set_input(h.items[h.cursor]) end end), } -- Add the Up & Down keybindings to modes which support command history window.init_funcs.add_hist_binds = function (w) w:add_signal("mode-entered", function () local mode = w.mode -- Setup history state if mode and mode.history then local h = mode.history if not h.items then h.items = {} end h.len = #(h.items) h.cursor = nil h.orig = nil -- Add Up & Down history bindings for _, b in ipairs(hist_binds) do table.insert(w.binds, b) end -- Trim history if h.maxlen and h.len > (h.maxlen * 1.5) then local items = {} for i = (h.len - h.maxlen), h.len do table.insert(items, h.items[i]) end h.items = items h.len = #items end end end) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/completion.lua000066400000000000000000000201101202446317700173440ustar00rootroot00000000000000------------------------------------------------------------ -- Command completion -- -- © 2010-2011 Mason Larobina -- -- © 2010 Fabian Streitel -- ------------------------------------------------------------ -- Get Lua environment local ipairs = ipairs local setmetatable = setmetatable local string = string local table = table local unpack = unpack -- Get luakit environment local lousy = require "lousy" local history = require "history" local bookmarks = require "bookmarks" local escape = lousy.util.escape local new_mode, get_mode = new_mode, get_mode local add_binds = add_binds local capi = { luakit = luakit } module "completion" -- Store completion state (indexed by window) local data = setmetatable({}, { __mode = "k" }) -- Add completion start trigger local key = lousy.bind.key add_binds("command", { key({}, "Tab", function (w) w:set_mode("completion") end), }) -- Return to command mode with original text and with original cursor position function exit_completion(w) local state = data[w] w:enter_cmd(state.orig_text, { pos = state.orig_pos }) end -- Command completion binds add_binds("completion", { key({}, "Tab", "Select next matching completion item.", function (w) w.menu:move_down() end), key({"Shift"}, "Tab", "Select previous matching completion item.", function (w) w.menu:move_up() end), key({}, "Up", "Select next matching completion item.", function (w) w.menu:move_up() end), key({}, "Down", "Select previous matching completion item.", function (w) w.menu:move_down() end), key({"Control"}, "j", "Select next matching completion item.", function (w) w.menu:move_down() end), key({"Control"}, "k", "Select previous matching completion item.", function (w) w.menu:move_up() end), key({}, "Escape", "Stop completion and restore original command.", exit_completion), key({"Control"}, "[", "Stop completion and restore original command.", exit_completion), }) function update_completions(w, text, pos) local state = data[w] -- Other parts of the code are triggering input changed events if state.lock then return end local input = w.ibar.input local text, pos = text or input.text, pos or input.position -- Don't rebuild the menu if the text & cursor position are the same if text == state.text and pos == state.pos then return end -- Exit completion if cursor outside a word if string.sub(text, pos, pos) == " " then w:enter_cmd(text, { pos = pos }) end -- Update left and right strings state.text, state.pos = text, pos state.left = string.sub(text, 2, pos) state.right = string.sub(text, pos + 1) -- Call each completion function local groups = {} for _, func in ipairs(_M.order) do table.insert(groups, func(state) or {}) end -- Join all result tables rows = lousy.util.table.join(unpack(groups)) if rows[1] then -- Prevent callbacks triggering recursive updates. state.lock = true w.menu:build(rows) w.menu:show() if not state.built then state.built = true if rows[2] then w.menu:move_down() end end state.lock = false elseif not state.built then exit_completion(w) else w.menu:hide() end end new_mode("completion", { enter = function (w) -- Clear state local state = {} data[w] = state -- Save original text and cursor position local input = w.ibar.input state.orig_text = input.text state.orig_pos = input.position -- Update input text when scrolling through completion menu items w.menu:add_signal("changed", function (m, row) state.lock = true if row then input.text = row.left .. " " .. state.right input.position = #row.left else input.text = state.orig_text input.position = state.orig_pos end state.lock = false end) update_completions(w) end, changed = function (w, text) if not data[w].lock then update_completions(w, text) end end, move_cursor = function (w, pos) if not data[w].lock then update_completions(w, nil, pos) end end, leave = function (w) w.menu:hide() w.menu:remove_signals("changed") end, activate = function (w, text) local pos = w.ibar.input.position if string.sub(text, pos+1, pos+1) == " " then pos = pos+1 end w:enter_cmd(text, { pos = pos }) end, }) -- Completion functions funcs = { -- Add command completion items to the menu command = function (state) -- We are only interested in the first word if string.match(state.left, "%s") then return end -- Check each command binding for matches local pat = "^" .. state.left local cmds = {} for _, b in ipairs(get_mode("command").binds) do if b.cmds then for i, cmd in ipairs(b.cmds) do if string.match(cmd, pat) then if i == 1 then cmd = ":" .. cmd else cmd = string.format(":%s (:%s)", cmd, b.cmds[1]) end cmds[cmd] = { escape(cmd), escape(b.desc) or "", left = ":" .. b.cmds[1] } break end end end end -- Sort commands local keys = lousy.util.table.keys(cmds) -- Return if no results if not keys[1] then return end -- Build completion menu items local ret = {{ "Command", "Description", title = true }} for _, cmd in ipairs(keys) do table.insert(ret, cmds[cmd]) end return ret end, -- Add history completion items to the menu history = function (state) -- Find word under cursor (also checks not first word) local term = string.match(state.left, "%s(%S+)$") if not term then return end local sql = [[ SELECT uri, title, lower(uri||title) AS text FROM history WHERE text GLOB ? ORDER BY visits DESC LIMIT 25 ]] local rows = history.db:exec(sql, { string.format("*%s*", term) }) if not rows[1] then return end -- Strip last word (so that we can append the completion uri) local left = ":" .. string.sub(state.left, 1, string.find(state.left, "%s(%S+)$")) -- Build rows local ret = {{ "History", "URI", title = true }} for _, row in ipairs(rows) do table.insert(ret, { escape(row.title), escape(row.uri), left = left .. row.uri }) end return ret end, -- add bookmarks completion to the menu bookmarks = function (state) -- Find word under cursor (also checks not first word) local term = string.match(state.left, "%s(%S+)$") if not term then return end local sql = [[ SELECT uri, title, lower(uri||title||tags) AS text FROM bookmarks WHERE text GLOB ? ORDER BY title DESC LIMIT 25 ]] local rows = bookmarks.db:exec(sql, { string.format("*%s*", term) }) if not rows[1] then return end -- Strip last word (so that we can append the completion uri) local left = ":" .. string.sub(state.left, 1, string.find(state.left, "%s(%S+)$")) -- Build rows local ret = {{ "Bookmarks", "URI", title = true }} for _, row in ipairs(rows) do local title = row.title ~= "" and row.title or row.uri table.insert(ret, { escape(title), escape(row.uri), left = left .. row.uri }) end return ret end, } -- Order of completion items order = { funcs.command, funcs.history, funcs.bookmarks, } -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/cookie_blocking.lua000066400000000000000000000036421202446317700203270ustar00rootroot00000000000000------------------------------------------------------ -- Simple domain-based cookie blocking -- -- © 2011 Mason Larobina -- ------------------------------------------------------ require "cookies" cookies.whitelist_path = luakit.config_dir .. "/cookie.whitelist" cookies.blacklist_path = luakit.config_dir .. "/cookie.blacklist" local cache = {} local function mkglob(s) s = string.gsub(s, "[%^%$%(%)%%%.%[%]%+%-%?]", "%%%1") s = string.gsub(s, "*", "%%S*") return "^"..s.."$" end local function load_rules(file) assert(file, "invalid path") local strip = lousy.util.string.strip if os.exists(file) then local rules = {} for line in io.lines(file) do table.insert(rules, mkglob(strip(line))) end return rules end end function cookies.reload_lists() cache = {} -- clear cache cookies.whitelist = load_rules(cookies.whitelist_path) cookies.blacklist = load_rules(cookies.blacklist_path) end function match_domain(rules, domain) local match = string.match for _, pat in ipairs(rules) do if match(domain, pat) then return true end end end cookies.add_signal("accept-cookie", function (cookie) local domain = cookie.domain -- Get cached block/allow result for given domain if cache[domain] ~= nil then return cache[domain] end local wl, bl = cookies.whitelist, cookies.blacklist -- Check if domain in whitelist if wl and wl[1] and match_domain(wl, domain) then cache[domain] = true return true end -- Check if domain in blacklist if bl and bl[1] and match_domain(bl, domain) then cache[domain] = false return false end cache[domain] = cookies.default_allow return cache[domain] end) -- Initial load of users cookie.whitelist / cookie.blacklist files cookies.reload_lists() -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/cookies.lua000066400000000000000000000103651202446317700166420ustar00rootroot00000000000000------------------------------------------------------------ -- Cross-instance luakit cookie management (with sqlite3) -- -- © 2011 Mason Larobina -- ------------------------------------------------------------ local math = require "math" local string = string local ipairs = ipairs local pairs = pairs local print = print local lousy = require "lousy" local capi = { luakit = luakit, soup = soup, sqlite3 = sqlite3, timer = timer } local time, floor = luakit.time, math.floor module "cookies" db_path = capi.luakit.data_dir .. "/cookies.db" -- Last access time local atime = 0 -- Set max session age to 3600 session_timeout = 3600 force_session_timeout = true -- Setup signals on module lousy.signal.setup(_M, true) -- Return microseconds from the unixtime epoch function micro() return floor(time() * 1e6) end function init() -- Return if database handle already open if db then return end db = capi.sqlite3{ filename = _M.db_path } db:exec [[ PRAGMA synchronous = OFF; PRAGMA secure_delete = 1; CREATE TABLE IF NOT EXISTS moz_cookies ( id INTEGER PRIMARY KEY, name TEXT, value TEXT, host TEXT, path TEXT, expiry INTEGER, lastAccessed INTEGER, isSecure INTEGER, isHttpOnly INTEGER ); CREATE TRIGGER IF NOT EXISTS delete_old_cookie BEFORE INSERT ON moz_cookies BEGIN DELETE FROM moz_cookies WHERE ( host == new.host AND path == new.path AND name == new.name ); END; ]] query_all_since = db:compile [[ SELECT id, name, value, host AS domain, path, expiry AS expires, isSecure AS secure, isHttpOnly AS http_only FROM moz_cookies WHERE lastAccessed >= ? AND expiry >= ? ]] query_insert = db:compile [[ INSERT INTO moz_cookies VALUES(NULL, ?, ?, ?, ?, ?, ?, ?, ?) ]] query_expire = db:compile [[ UPDATE moz_cookies SET expiry = 0, value = NULL, lastAccessed = ? WHERE host = ? AND path = ? AND name = ? ]] query_delete_expired = db:compile [[ DELETE FROM moz_cookies WHERE expiry < ? AND lastAccessed < ? ]] end -- Open database handle after window has time to open capi.luakit.idle_add(init) -- Load all cookies after the last check time capi.soup.add_signal("request-started", function () if not db then init() end local old_atime, new_atime = atime, micro() -- Rate limit select queries to 1 p/s if (new_atime - old_atime) > 1e6 then local cookies = query_all_since:exec{ old_atime, -- On first exec don't load any expired cookies (atime == 0 and time()) or 0 } atime = new_atime if cookies[1] then capi.soup.add_cookies(cookies) end end end) capi.soup.add_signal("cookie-changed", function (old, new) if new then if _M.emit_signal("accept-cookie", new) == false then new.expires = 0 -- expire cookie capi.soup.add_cookies{new} return end -- Set session cookie timeout & keep session cookies in memory if new.expires == -1 then if _M.force_session_timeout then new.expires = math.ceil(time() + _M.session_timeout) capi.soup.add_cookies{new} end return end -- Insert new cookie query_insert:exec { new.name, new.value, new.domain, new.path, new.expires, micro(), new.secure, new.http_only } return end -- Expire old cookie if old then query_expire:exec { micro(), -- lastAccessed old.domain, old.path, old.name } end end) -- When closing luakit delete most expired cookies. capi.luakit.add_signal("can-close", function () if query_delete_expired then local t = time() query_delete_expired:exec{ t, (t - 86400) * 1e6 } end end) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/downloads.lua000066400000000000000000000153101202446317700171730ustar00rootroot00000000000000----------------------------------------------------------- -- Downloads for luakit -- -- © 2010-2012 Mason Larobina -- -- © 2010 Fabian Streitel -- ----------------------------------------------------------- -- Grab environment we need from the standard lib local assert = assert local ipairs = ipairs local os = os local pairs = pairs local setmetatable = setmetatable local string = string local table = table local type = type local tostring = tostring local print = print -- Grab environment from luakit libs local lousy = require("lousy") local webview = webview local window = window local add_cmds = add_cmds local add_binds = add_binds local capi = { download = download, timer = timer, luakit = luakit, widget = widget, xdg = xdg } module("downloads") -- Unique ids for downloads in this luakit instance local id_count = 0 local function next_download_id() id_count = id_count + 1 return tostring(id_count) end -- Default download directory default_dir = capi.xdg.download_dir or (os.getenv("HOME") .. "/downloads") -- Setup signals on download module lousy.signal.setup(_M, true) -- Private data for the download instances (speed tracking) local downloads = {} function get_all() return lousy.util.table.clone(downloads) end -- Get download object from id (passthrough if already given download object) function to_download(id) if type(id) == "download" then return id end for d, data in pairs(downloads) do if id == data.id then return d end end end function get(id) local d = assert(to_download(id), "download.get() expected valid download object or id") return d, downloads[d] end local function is_running(d) local status = d.status return status == "created" or status == "started" end function do_open(d, w) if _M.emit_signal("open-file", d.destination, d.mime_type, w) ~= true then if w then w:error(string.format("Couldn't open: %q (%s)", d.destination, d.mime_type)) end end end local status_timer = capi.timer{interval=1000} status_timer:add_signal("timeout", function () local running = 0 for d, data in pairs(downloads) do -- Create list of running downloads if is_running(d) then running = running + 1 end -- Raise "download::status" signals local status = d.status if status ~= data.last_status then data.last_status = status _M.emit_signal("download::status", d, data) -- Open download if status == "finished" and data.opening then do_open(d) end end end -- Stop the status_timer after all downloads finished if running == 0 then status_timer:stop() end -- Update window download status widget for _, w in pairs(window.bywidget) do w.sbar.r.downloads.text = (running == 0 and "") or running.."↓" end _M.emit_signal("status-tick", running) end) function add(uri, opts) opts = opts or {} local d = (type(uri) == "string" and capi.download{uri=uri}) or uri assert(type(d) == "download", string.format("download.add() expected uri or download object " .. "(got %s)", type(d) or "nil")) -- Emit signal to get initial download location local fn = _M.emit_signal("download-location", d.uri, d.suggested_filename, d.mime_type) assert(fn == nil or type(fn) == "string" and #fn > 1, string.format("invalid filename: %q", tostring(file))) -- Ask the user where we should download the file to if not fn then fn = capi.luakit.save_file("Save file", opts.window, default_dir, d.suggested_filename) end if fn then d.destination = fn d:start() local data = { created = capi.luakit.time(), id = next_download_id(), } downloads[d] = data if not status_timer.started then status_timer:start() end _M.emit_signal("download::status", d, downloads[d]) return true end end function cancel(id) local d = assert(to_download(id), "download.cancel() expected valid download object or id") d:cancel() _M.emit_signal("download::status", d, downloads[d]) end function remove(id) local d = assert(to_download(id), "download.remove() expected valid download object or id") if is_running(d) then cancel(d) end _M.emit_signal("removed-download", d, downloads[d]) downloads[d] = nil end function restart(id) local d = assert(to_download(id), "download.restart() expected valid download object or id") local new_d = add(d.uri) -- TODO use soup message from old download if new_d then remove(d) end return new_d end function open(id, w) local d = assert(to_download(id), "download.open() expected valid download object or id") local data = assert(downloads[d], "download removed") if d.status == "finished" then data.opening = false do_open(d, w) else -- Set open flag to open file when download finishes data.opening = true end end -- Clear all finished, cancelled or aborted downloads function clear() for d, _ in pairs(downloads) do if not is_running(d) then downloads[d] = nil end end _M.emit_signal("cleared-downloads") end -- Catch "download-request" webview widget signals webview.init_funcs.download_request = function (view, w) view:add_signal("download-request", function (v, d) add(d, { window = w.win }) return true end) end window.init_funcs.download_status = function (w) local r = w.sbar.r r.downloads = capi.widget{type="label"} r.layout:pack(r.downloads) r.layout:reorder(r.downloads, 1) -- Apply theme local theme = lousy.theme.get() r.downloads.fg = theme.downloads_sbar_fg r.downloads.font = theme.downloads_sbar_font end -- Prevent luakit from soft-closing if there are downloads still running capi.luakit.add_signal("can-close", function () local count = 0 for d, _ in pairs(downloads) do if is_running(d) then count = count + 1 end end if count > 0 then return count .. " download(s) still running" end end) -- Download normal mode binds. local key = lousy.bind.key add_binds("normal", { key({"Control"}, "D", "Generate `:download` command with current URI.", function (w) w:enter_cmd(":download " .. (w.view.uri or "http://")) end), }) -- Download commands local cmd = lousy.bind.cmd add_cmds({ cmd("down[load]", "Download the given URI.", function (w, a) add(a, { window = w.win }) end), }) luakit-2012.09.13-r1/lib/downloads_chrome.lua000066400000000000000000000264661202446317700205460ustar00rootroot00000000000000-- Grab what we need from the Lua environment local table = table local string = string local io = io local print = print local pairs = pairs local ipairs = ipairs local math = math local assert = assert local setmetatable = setmetatable local rawget = rawget local rawset = rawset local type = type local os = os local error = error -- Grab the luakit environment we need local downloads = require("downloads") local lousy = require("lousy") local chrome = require("chrome") local add_binds = add_binds local add_cmds = add_cmds local webview = webview local capi = { luakit = luakit } module("downloads.chrome") local html = [==[ Downloads
Downloads
]==] local main_js = [=[ var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; function basename(url) {     return url.substring(url.lastIndexOf('/') + 1); }; function make_download(d) { var e = "
"; var dt = new Date(d.created * 1000); e += ("
" + dt.getDate() + " " + months[dt.getMonth()] + " " + dt.getFullYear() + "
"); e += (""); e += (""); e += "
"; //
return e; } function getid(that) { return $(that).parents(".download").eq(0).attr("id"); }; function update_list() { var downloads = downloads_get_all(["status", "speed"]); // return if no downloads to display if (downloads.length === "undefined") { setTimeout(update, 1000); // update 1s from now return; } for (var i = 0; i < downloads.length; i++) { var d = downloads[i]; // find existing element var $elem = $("#"+d.id).eq(0); // create new download element if ($elem.length === 0) { // get some more information d = download_get(d.id, ["status", "destination", "created", "uri"]); var elem_html = make_download(d); // ordered insert var inserted = false; var $all = $("#downloads-list .download"); for (var j = 0; j < $all.length; j++) { if (d.created > $all.eq(j).attr("created")) { $all.eq(j).before(elem_html); inserted = true; break; } } // back of the bus if (!inserted) { $("#downloads-list").append(elem_html); } $elem = $("#"+d.id).eq(0); $elem.fadeIn(); } // update download controls when download status changes if (d.status !== $elem.attr("status")) { $elem.find(".controls a").hide(); switch (d.status) { case "created": case "started": $elem.find(".cancel,.show").fadeIn(); break; case "finished": $elem.find(".show,.remove").fadeIn(); break; case "error": case "cancelled": $elem.find(".remove,.restart").fadeIn(); break; } // save latest download status $elem.attr("status", d.status); } // update status text var $st = $elem.find(".status").eq(0); switch (d.status) { case "started": var speed = Math.round((d.speed || 0) / 1024); if (speed >= 1024) { // MB/s $st.text("downloading - " + Math.round(speed/10.24)/100 + " MB/s") } else { $st.text("downloading - " + speed + " KB/s") } break; case "error": $st.html("Error"); break; case "cancelled": $st.html("Cancelled"); break; case "created": case "finished": default: $st.html(""); break; } } setTimeout(update_list, 1000); }; $(document).ready(function () { $("#downloads-list").on("click", ".controls .show", function (e) { download_show(getid(this)); }); $("#downloads-list").on("click", ".controls .restart", function (e) { download_restart(getid(this)); }); $("#downloads-list").on("click", ".controls .remove", function (e) { var id = getid(this); download_remove(id); elem = $("#"+id); elem.fadeOut("fast", function () { elem.remove(); }); }); $("#downloads-list").on("click", ".controls .cancel", function (e) { download_cancel(getid(this)); }); $("#downloads-list").on("click", ".details .title a", function (e) { download_open(getid(this)); return false; }); update_list(); }); ]=] -- default filter local default_filter = { destination = true, status = true, created = true, current_size = true, total_size = true, mime_type = true, uri = true, opening = true } local function collate_download_data(d, data, filter) local f = filter or default_filter local ret = { id = data.id } -- download object properties if rawget(f, "destination") then rawset(ret, "destination", d.destination) end if rawget(f, "status") then rawset(ret, "status", d.status) end if rawget(f, "uri") then rawset(ret, "uri", d.uri) end if rawget(f, "current_size") then rawset(ret, "current_size", d.current_size) end if rawget(f, "total_size") then rawset(ret, "total_size", d.total_size) end if rawget(f, "mime_type") then rawset(ret, "mime_type", d.mime_type) end -- data table properties if rawget(f, "created") then rawset(ret, "created", data.created) end if rawget(f, "opening") then rawset(ret, "opening", not not data.opening) end if rawget(f, "speed") then rawset(ret, "speed", data.speed) end return ret end local export_funcs = { download_get = function (id, filter) local d, data = downloads.get(id) if filter then assert(type(filter) == "table", "invalid filter table") for _, key in ipairs(filter) do rawset(filter, key, true) end end return collate_download_data(d, data, filter) end, downloads_get_all = function (filter) local ret = {} if filter then assert(type(filter) == "table", "invalid filter table") for _, key in ipairs(filter) do rawset(filter, key, true) end end for d, data in pairs(downloads.get_all()) do table.insert(ret, collate_download_data(d, data, filter)) end return ret end, download_show = function (id) local d, data = downloads.get(id) local dirname = string.gsub(d.destination, "(.*/)(.*)", "%1") if downloads.emit_signal("open-file", dirname, "inode/directory") ~= true then error("Couldn't show download directory (no inode/directory handler)") end end, download_cancel = downloads.cancel, download_restart = downloads.restart, download_open = downloads.open, download_remove = downloads.remove, downloads_clear = downloads.clear, } downloads.add_signal("status-tick", function (running) if running == 0 then for _, data in pairs(downloads.get_all()) do data.speed = nil end return end for d, data in pairs(downloads.get_all()) do if d.status == "started" then local last, curr = rawget(data, "last_size") or 0, d.current_size rawset(data, "speed", curr - last) rawset(data, "last_size", curr) end end end) chrome.add("downloads", function (view, meta) view:load_string(html, "luakit://downloads/") function on_first_visual(_, status) -- Wait for new page to be created if status ~= "first-visual" then return end -- Hack to run-once view:remove_signal("load-status", on_first_visual) -- Double check that we are where we should be if view.uri ~= "luakit://downloads/" then return end -- Export luakit JS<->Lua API functions for name, func in pairs(export_funcs) do view:register_function(name, func) end -- Load jQuery JavaScript library local jquery = lousy.load("lib/jquery.min.js") local _, err = view:eval_js(jquery, { no_return = true }) assert(not err, err) -- Load main luakit://download/ JavaScript local _, err = view:eval_js(main_js, { no_return = true }) assert(not err, err) end view:add_signal("load-status", on_first_visual) end) local page = "luakit://downloads/" local buf, cmd = lousy.bind.buf, lousy.bind.cmd add_binds("normal", { buf("^gd$", [[Open [luakit://downloads](luakit://downloads/) in current tab.]], function (w) w:navigate(page) end), buf("^gD$", [[Open [luakit://downloads](luakit://downloads/) in new tab.]], function (w) w:new_tab(page) end), }) add_cmds({ cmd("downloads", [[Open [luakit://downloads](luakit://downloads/) in new tab.]], function (w) w:new_tab(page) end), }) luakit-2012.09.13-r1/lib/follow.lua000066400000000000000000000734741202446317700165220ustar00rootroot00000000000000------------------------------------------------------------ -- Link hinting for luakit -- -- © 2010-2012 Mason Larobina -- -- © 2010-2011 Fabian Streitel -- ------------------------------------------------------------ -- Get Lua environ local print = print local pairs, ipairs = pairs, ipairs local table, string = table, string local assert, type = assert, type local floor = math.floor local rawset, rawget = rawset, rawget -- Get luakit environ local lousy = require "lousy" local new_mode, add_binds = new_mode, add_binds local window = window local capi = { luakit = luakit, timer = timer } module("follow") -- After each follow ignore all keys pressed by the user to prevent the -- accidental activation of other key bindings. ignore_delay = 200 follow_js = [=[ window.luakit_follow = (function (window, document) { // Follow session state var state = {}; // Unlink element from DOM (and return it) function unlink(e) { if (typeof e === "string") e = document.getElementById(e); if (e) { e.parentNode.removeChild(e); return e; } } function create_stylesheet(id, rules) { var style = document.createElement('style'); style.id = id; style.type = 'text/css'; style.appendChild(document.createTextNode(rules)); return style; } function create_overlay(id, html) { var overlay = document.createElement("div"); overlay.id = id; overlay.innerHTML = html; return overlay; } var sort_hints_top_left = function (a, b) { var dtop = a.top - b.top; return dtop !== 0 ? dtop : a.left - b.left; } function eval_selector_make_hints(selector) { var elems = document.querySelectorAll(selector), len = elems.length, win_h = window.innerHeight, win_w = window.innerWidth, hints = [], i = 0, j = 0, e, r, top, bottom, left, right; for (; i < len;) { e = elems[i++]; r = e.getClientRects()[0]; // Check if element outside viewport if (!r || (top = r.top) > win_h || (bottom = r.bottom) < 0 || (left = r.left) > win_w || (right = r.right) < 0) continue; hints[j++] = { element: e, tag: e.tagName, left: left, top: top, width: right - left, height: bottom - top, text: e.value || e.textContent }; } hints.sort(sort_hints_top_left); return hints; } function assign_labels_make_html(hints, labels) { var win = document.defaultView, scroll_x = win.scrollX, scroll_y = win.scrollY, i = 0, len = hints.length, h, tag, label, left, top, h_left, h_top, l_left, l_top, html = "" for (; i < len; i++) { h = hints[i]; label = labels[i]; h.label = label; tag = h.tag; left = h.left; top = h.top; // hint position offset by window scroll h_left = scroll_x + left; h_top = scroll_y + top; // hint label position offset by -10x -10y if ((l_left = h_left - 10) < 0) l_left = scroll_x; if ((l_top = h_top - 10) < 0) l_top = scroll_y; h.html = "" + "" + label + "\n"; html += h.html; } return html; } function show_hints(html) { if (html === state.last_html) return; var current_overlay = state.overlay, overlay = create_overlay("luakit_follow_overlay", html); if (current_overlay) document.body.replaceChild(overlay, current_overlay); else document.body.appendChild(overlay); state.last_html = html; state.overlay = overlay; } function cleanup() { unlink(state.stylesheet); unlink(state.overlay); // Reset follow state state = {}; } function init(selector, stylesheet) { var head = document.getElementsByTagName('head')[0], hints = eval_selector_make_hints(selector), style = create_stylesheet("luakit_follow_stylesheet", stylesheet); head.appendChild(style); // append follow stylesheet to state.stylesheet = style; state.hints = state.filtered = hints; return hints.length; } function unfocus() { var f = state.focused; if (f) { f.element.className = f.orig_class; delete state.focused; } } function focus(step) { var hints = state.filtered, last = state.focused, i = 0; unfocus(); // Nothing to focus in this frame if (hints.length === 0) return "next-frame"; var labels = document.querySelectorAll("#luakit_follow_overlay .hint_overlay"), len = labels.length; // Focus first hint in this frame if (step === "first") i = 0 // Focus last hint in this frame else if (step === "last") i = len - 1; // Focus hints[current + step] else if (last) i = last.index + step; // Out of bounds check = check next frame if (i >= len || i < 0) return "next-frame"; e = labels[i]; state.focused = { element: e, index: i, orig_class: e.className }; e.className += " hint_selected"; return i; } // Give all hints their labels and render the hint overlay. function show(labels) { var html = assign_labels_make_html(state.hints, labels.split(" ")); show_hints(html); } function filter(hint_pat, text_pat) { var hpat = hint_pat !== "" && hint_pat, tpat = text_pat !== "" && text_pat, i = 0, html = ""; // No filter patterns given, render all hints if (!hpat && !tpat) { var hints = state.hints, len = hints.length; for (; i < len;) html += hints[i++].html; show_hints(html); delete state.last_hpat; delete state.last_tpat; state.filtered = hints; return len; } // Check if we can cut corners by checking last filter patterns var last_hpat = state.last_hpat, last_tpat = state.last_tpat, hints; if (last_hpat || last_tpat) { // No action if new patterns equal last patterns if (last_hpat === hpat && last_tpat === tpat) return state.filtered.length; // Check type hasn't changed if (!!hpat === !!last_hpat && !!tpat === !!last_tpat) { // If new patterns are substrings of last patterns then we can // filter against the results of the last filter call if ((!hpat || hpat.substring(0, last_hpat.length) === last_hpat) && (!tpat || tpat.substring(0, last_tpat.length) === last_tpat)) { hints = state.filtered; } } } // Patterns are different or it's our first run if (!hints) hints = state.hints; var hint_re = hpat && new RegExp(hpat), text_re = tpat && new RegExp(tpat), matches = [], len = hints.length, j = 0, h; // Filter hints for (; i < len;) { h = hints[i++]; if ((hint_re && hint_re.test(h.label)) || (text_re && text_re.test(h.text))) { matches[j++] = h; html += h.html; } } // Save info for next call state.last_hpat = hpat; state.last_tpat = tpat; state.filtered = matches; show_hints(html); return matches.length; } function focused_element() { return state.filtered[state.focused.index].element; } function visible_elements() { var hints = state.filtered, len = hints.length, elems = [], i = 0; for (; i < len; i++) elems[i] = hints[i].element; return elems; } return { init: init, cleanup: cleanup, show: show, filter: filter, focus: focus, unfocus: unfocus, focused_element: focused_element, visible_elements: visible_elements, } })(window, document); ]=] check_js = [=[ (function () { if (!document.body) return; return window.luakit_follow ? "ready" : "first-run"; })() ]=] stylesheet = [===[ #luakit_follow_overlay { position: absolute; left: 0; top: 0; } #luakit_follow_overlay .hint_overlay { display: block; position: absolute; background-color: #ffff99; border: 1px dotted #000; opacity: 0.3; z-index: 10001; } #luakit_follow_overlay .hint_label { display: block; position: absolute; background-color: #000088; border: 1px dashed #000; color: #fff; font-size: 10px; font-family: monospace, courier, sans-serif; opacity: 0.4; z-index: 10002; } #luakit_follow_overlay .hint_overlay_body { background-color: #ff0000; } #luakit_follow_overlay .hint_selected { background-color: #00ff00 !important; } ]===] -- Calculates the minimum number of characters needed in a hint given a -- charset of a certain length (I.e. the base) local function max_hint_len(size, base) local floor, len = floor, 0 while size > 0 do size, len = floor(size / base), len + 1 end return len end local function charset(seq, size) local floor, sub, reverse = floor, string.sub, string.reverse local insert, concat = table.insert, table.concat local base, digits, labels = #seq, {}, {} for i = 1, base do rawset(digits, i, sub(seq, i, i)) end local maxlen = max_hint_len(size, base) local zeroseq = string.rep(rawget(digits, 1), maxlen) for n = 1, size do local t, i, j, d = {}, 1, n repeat d, n = (n % base) + 1, floor(n / base) rawset(t, i, rawget(digits, d)) i = i + 1 until n == 0 rawset(labels, j, sub(zeroseq, 1, maxlen - i + 1) .. reverse(concat(t, ""))) end return labels end -- Different hint label styles label_styles = { charset = function (seq) assert(type(seq) == "string" and #seq > 0, "invalid sequence") return function (size) return charset(seq, size) end end, numbers = function () return function (size) return charset("0123456789", size) end end, -- Chainable style: sorts labels sort = function (make_labels) return function (size) local labels = make_labels(size) table.sort(labels) return labels end end, -- Chainable style: reverses label strings reverse = function (make_labels) return function (size) local rawset, rawget, reverse = rawset, rawget, string.reverse local labels = make_labels(size) for i = 1, #labels do rawset(labels, i, reverse(rawget(labels, i))) end return labels end end, } -- Default follow style local s = label_styles label_maker = s.sort(s.reverse(s.numbers())) -- JavaScript regex escape function local function regex_escape(s) local s = string.gsub(s, "([.?*+^$[%]\\(){}|-])", "\\%1") return s end -- Different hint matching styles pattern_styles = { -- JavaScript regular expression match hint text re_match_text = function (text) return "", text end, -- JavaScript regex match hint labels & text re_match_both = function (text) return text, text end, -- String match hint label & regex match text match_label_re_text = function (text) return #text > 0 and "^"..regex_escape(text) or "", text end, -- Only match label match_label = function (text) return #text > 0 and "^"..regex_escape(text) or "", "" end, } -- Default pattern style pattern_maker = pattern_styles.match_label_re_text local function focus(w, step) local state = w.follow_state local view, frames, i = state.view, state.frames, state.focus_frame -- Asked to focus 1st elem & last focus was on different frame if i and step == 0 and i ~= 1 then view:eval_js("window.luakit_follow.unfocus()", frames[i]) i = 1 -- start at first frame end i = i or 1 -- default to first frame on first focus local orig_i = i -- prevent inf loop local focus_call = (step == 0 and [=[luakit_follow.focus("first")]=]) or string.format([=[luakit_follow.focus(%s)]=], step) while true do local d = assert(frames[i], "invalid index") local ret, err = view:eval_js(focus_call, d) assert(not err, err) -- Hint was focused if type(ret) == "number" then state.focus_frame = i return -- Focus first hint on next frame elseif ret == "next-frame" then if step < 0 then focus_call = [=[luakit_follow.focus("last")]=] end i = (step == 0 and i or i + step - 1) % #frames + 1 end if i == orig_i then break end end state.focus_frame = nil end local eval_format = [=[ (%s)(window.luakit_follow.focused_element()) ]=] local hit_nop = function () return true end local function ignore_keys(w) local delay = _M.ignore_delay if not delay or delay == 0 then return end -- Replace w:hit(..) with a no-op w.hit = hit_nop local t = capi.timer{ interval = delay } t:add_signal("timeout", function (t) t:stop() w.hit = nil end) t:start() end local function follow(w) local state = w.follow_state local view, mode = state.view, state.mode assert(type(state.focus_frame) == "number", "no frame focused") local d = assert(state.frames[state.focus_frame], "invalid focus frame index") local evaluator = string.format(eval_format, state.evaluator) local ret, err = view:eval_js(evaluator, d) assert(not err, err) if state.persist then w:set_mode("follow", mode) else w:set_mode() end -- Call mode callback to deal with evaluator return if mode.func then mode.func(ret) end ignore_keys(w) end local eval_all_format = [=[ (function (elems) { var evaluator = (%s); var ret = [], len = elems.length, i = 0, j = 0, val; for (; i < len;) if (val = evaluator(elems[i++])) ret[j++] = val; return ret; })(window.luakit_follow.visible_elements()) ]=] local function follow_all_hints(w) local state = w.follow_state local view, mode = state.view, state.mode assert(type(state.focus_frame) == "number", "no frame focused") local d = assert(state.frames[state.focus_frame], "invalid focus frame index") local evaluator = string.format(eval_all_format, state.evaluator) local ret, err = view:eval_js(evaluator, d) assert(not err, err) if state.persist then w:set_mode("follow", mode) else w:set_mode() end -- Call mode callback to deal with evaluator return if mode.func then for _, v in ipairs(ret) do mode.func(v) end end ignore_keys(w) end new_mode("follow", { enter = function (w, mode) assert(type(mode) == "table", "invalid follow mode") local label_maker = mode.label_maker or _M.label_maker assert(type(label_maker) == "function", "invalid label_maker function") assert(type(mode.pattern_maker or _M.pattern_maker) == "function", "invalid pattern_maker function") local selector = mode.selector_func or _M.selectors[mode.selector] assert(type(selector) == "string", "invalid follow selector") local evaluator = mode.evaluator_func or _M.evaluators[mode.evaluator] assert(type(evaluator) == "string", "invalid follow evaluator") local stylesheet = mode.stylesheet or _M.stylesheet assert(type(stylesheet) == "string", "invalid stylesheet") local view = w.view local all_frames, frames = view.frames, {} local state = { mode = mode, view = view, frames = frames, evaluator = evaluator, persist = not not w.follow_persist } w.follow_state = state w.follow_persist = nil local init_js = string.format([=[luakit_follow.init(%q, %q)]=], selector, stylesheet) local size = 0 for _, f in ipairs(all_frames) do local d, count = { frame = f }, 0 -- Check if document ready and if follow lib already loaded local ret, err = view:eval_js(check_js, d) assert(not err, err) if ret == "first-run" then local _, err = view:eval_js(follow_js, d) assert(not err, err) end if ret then count, err = view:eval_js(init_js, d) assert(not err, err) end if count > 0 then d.count, size = count, size + count table.insert(frames, d) end end -- No hintable items found if size == 0 then w:set_mode() w:notify("No matches...") return end -- Make all the labels local labels = label_maker(size) -- Give each frame its hint labels local offset = 0 for _, d in ipairs(frames) do local show = string.format([=[luakit_follow.show(%q)]=], table.concat(labels, " ", offset + 1, offset + d.count)) local _, err = view:eval_js(show, d) assert(not err, err) offset = offset + d.count end focus(w, 0) if mode.prompt then w:set_prompt(string.format("Follow (%s):", mode.prompt)) else w:set_prompt("Follow:") end w:set_input("") end, changed = function (w, text) local state = w.follow_state or {} local frames, mode = state.frames, state.mode local active_count = 0 -- Make the hint label/text matching patterns local pattern_maker = mode.pattern_maker or _M.pattern_maker local hint_pat, text_pat = pattern_maker(text) local filter = string.format("luakit_follow.filter(%q, %q)", hint_pat or "", text_pat or "") for _, d in ipairs(frames) do local count, err = assert(state.view:eval_js(filter, d)) if type(count) == "number" and count > 0 then active_count = active_count + count end end -- Focus first matching hint focus(w, 0) if active_count == 1 and text ~= "" then follow(w) -- follow the link return end end, leave = function (w) local state = w.follow_state or {} local view, frames = state.view, state.frames if not view or not frames then return end for _, d in ipairs(frames) do view:eval_js([=[window.luakit_follow.cleanup()]=], d) end end, }) local key = lousy.bind.key add_binds("follow", { key({}, "Tab", function (w) focus(w, 1) end), key({"Shift"}, "Tab", function (w) focus(w, -1) end), key({}, "Return", function (w) follow(w) end), key({"Shift"}, "Return", function (w) follow_all_hints(w) end), }) selectors = { clickable = 'a, area, textarea, select, input:not([type=hidden]), button', focus = 'a, area, textarea, select, input:not([type=hidden]), button, body, applet, object', uri = 'a, area', desc = '*[title], img[alt], applet[alt], area[alt], input[alt]', image = 'img, input[type=image]', thumbnail = "a img", } evaluators = { click = [=[function (element) { function click(element) { var mouse_event = document.createEvent("MouseEvent"); mouse_event.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); element.dispatchEvent(mouse_event); } var tag = element.tagName; if (tag === "INPUT" || tag === "TEXTAREA") { var t = element.type.toUpperCase(); if (t === "RADIO" || t == "CHECKBOX") element.checked = !element.checked; else if (t === "SUBMIT" || t === "RESET" || t === "BUTTON") click(element); else { element.focus(); return "form-active"; } } else click(element); return "root-active"; }]=], focus = [=[function (element) { element.focus(); var tag = element.tagName; if (tag == "INPUT" || tag == "TEXTAREA") return "form-active"; return "root-active"; }]=], uri = [=[function (element) { return element.src || element.href; }]=], desc = [=[function (element) { return element.title || element.alt; }]=], src = [=[function (element) { return element.src; }]=], parent_href = [=[function (element) { return element.parentNode.href; }]=] } local buf = lousy.bind.buf add_binds("normal", { buf("^f$", [[Start `follow` mode. Hint all clickable elements (as defined by the `follow.selectors.clickable` selector) and open links in the current tab.]], function (w) w:set_mode("follow", { selector = "clickable", evaluator = "click", func = function (s) w:emit_form_root_active_signal(s) end, }) end), -- Open new tab buf("^F$", [[Start follow mode. Hint all links (as defined by the `follow.selectors.uri` selector) and open links in a new tab.]], function (w) w:set_mode("follow", { prompt = "background tab", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:new_tab(uri, false) end }) end), -- Start extended follow mode buf("^;$", [[Start `ex-follow` mode. See the [ex-follow](#mode-ex-follow) help section for the list of follow modes.]], function (w) w:set_mode("ex-follow") end), buf("^g;$", [[Start `ex-follow` mode and stay there until `` is pressed.]], function (w) w:set_mode("ex-follow", true) end), }) -- Extended follow mode new_mode("ex-follow", { enter = function (w, persist) w.follow_persist = persist end, }) add_binds("ex-follow", { key({}, ";", [[Hint all focusable elements (as defined by the `follow.selectors.focus` selector) and focus the matched element.]], function (w) w:set_mode("follow", { prompt = "focus", selector = "focus", evaluator = "focus", func = function (s) w:emit_form_root_active_signal(s) end, }) end), -- Yank element uri or description into primary selection key({}, "y", [[Hint all links (as defined by the `follow.selectors.uri` selector) and set the primary selection to the matched elements URI.]], function (w) w:set_mode("follow", { prompt = "yank", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") uri = string.gsub(uri, " ", "%%20") capi.luakit.selection.primary = uri w:notify("Yanked uri: " .. uri, false) end }) end), -- Yank element description key({}, "Y", [[Hint all links (as defined by the `follow.selectors.uri` selector) and set the primary selection to the matched elements URI.]], function (w) w:set_mode("follow", { prompt = "yank desc", selector = "desc", evaluator = "desc", func = function (desc) assert(type(desc) == "string") capi.luakit.selection.primary = desc w:notify("Yanked desc: " .. desc) end }) end), -- Open image src key({}, "i", [[Hint all images (as defined by the `follow.selectors.image` selector) and open matching image location in the current tab.]], function (w) w:set_mode("follow", { prompt = "open image", selector = "image", evaluator = "src", func = function (src) assert(type(src) == "string") w:navigate(src) end }) end), -- Open image src in new tab key({}, "I", [[Hint all images (as defined by the `follow.selectors.image` selector) and open matching image location in a new tab.]], function (w) w:set_mode("follow", { prompt = "tab image", selector = "image", evaluator = "src", func = function (src) assert(type(src) == "string") w:new_tab(src) end }) end), -- Open thumbnail link key({}, "x", [[Hint all thumbnails (as defined by the `follow.selectors.thumbnail` selector) and open link in current tab.]], function (w) w:set_mode("follow", { prompt = "open image link", selector = "thumbnail", evaluator = "parent_href", func = function (uri) assert(type(uri) == "string") w:navigate(uri) end }) end), -- Open thumbnail link in new tab key({}, "X", [[Hint all thumbnails (as defined by the `follow.selectors.thumbnail` selector) and open link in a new tab.]], function (w) w:set_mode("follow", { prompt = "tab image link", selector = "thumbnail", evaluator = "parent_href", func = function (uri) assert(type(uri) == "string") w:new_tab(uri, false) end }) end), -- Open link key({}, "o", [[Hint all links (as defined by the `follow.selectors.uri` selector) and open its location in the current tab.]], function (w) w:set_mode("follow", { prompt = "open", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:navigate(uri) end }) end), -- Open link in new tab key({}, "t", [[Hint all links (as defined by the `follow.selectors.uri` selector) and open its location in a new tab.]], function (w) w:set_mode("follow", { prompt = "open tab", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:new_tab(uri) end }) end), -- Open link in background tab key({}, "b", [[Hint all links (as defined by the `follow.selectors.uri` selector) and open its location in a background tab.]], function (w) w:set_mode("follow", { prompt = "background tab", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:new_tab(uri, false) end }) end), -- Open link in new window key({}, "w", [[Hint all links (as defined by the `follow.selectors.uri` selector) and open its location in a new window.]], function (w) w:set_mode("follow", { prompt = "open window", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") window.new{uri} end }) end), -- Set command `:open ` key({}, "O", [[Hint all links (as defined by the `follow.selectors.uri` selector) and generate a `:open` command with the elements URI.]], function (w) w:set_mode("follow", { prompt = ":open", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:enter_cmd(":open " .. uri) end }) end), -- Set command `:tabopen ` key({}, "T", [[Hint all links (as defined by the `follow.selectors.uri` selector) and generate a `:tabopen` command with the elements URI.]], function (w) w:set_mode("follow", { prompt = ":tabopen", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:enter_cmd(":tabopen " .. uri) end }) end), -- Set command `:winopen ` key({}, "W", [[Hint all links (as defined by the `follow.selectors.uri` selector) and generate a `:winopen` command with the elements URI.]], function (w) w:set_mode("follow", { prompt = ":winopen", selector = "uri", evaluator = "uri", func = function (uri) assert(type(uri) == "string") w:enter_cmd(":winopen " .. uri) end }) end), }) luakit-2012.09.13-r1/lib/follow_selected.lua000066400000000000000000000052121202446317700203530ustar00rootroot00000000000000------------------------------------------------------------------------------ -- Add {A,C,S,}-Return binds to follow selected link (or link in selection) -- -- © 2010 Chris van Dijk (quigybo) -- -- © 2010 Mason Larobina (mason-l) -- -- © 2010 Paweł Zuzelski (pawelz) -- -- © 2009 israellevin -- ------------------------------------------------------------------------------ -- Return selected uri or first uri in selection local return_selected = [=[ (function(document) { var selection = window.getSelection(), container = document.createElement('div'), range, elements, i = 0; if (selection.toString() !== "") { range = selection.getRangeAt(0); // Check for links contained within the selection container.appendChild(range.cloneContents()); var elements = container.getElementsByTagName('a'), len = elements.length, i = 0, href; for (; i < len;) if ((href = elements[i++].href)) return href; // Check for links which contain the selection container = range.startContainer; while (container !== document) { if ((href = container.href)) return href; container = container.parentNode; } } // Check for active links var element = document.activeElement; return element.src || element.href; })(document); ]=] -- Add binding to normal mode to follow selected link local key = lousy.bind.key add_binds("normal", { -- Follow selected link key({}, "Return", function (w) local uri = w.view:eval_js(return_selected) if not uri then return false end assert(type(uri) == "string") w:navigate(uri) end), -- Follow selected link in new tab key({"Control"}, "Return", function (w) local uri = w.view:eval_js(return_selected) if not uri then return false end assert(type(uri) == "string") w:new_tab(uri, false) end), -- Follow selected link in new window key({"Shift"}, "Return", function (w) local uri = w.view:eval_js(return_selected) if not uri then return false end assert(type(uri) == "string") window.new({uri}) end), -- Download selected uri key({"Mod1"}, "Return", function (w) local uri = w.view:eval_js(return_selected) if not uri then return false end assert(type(uri) == "string") w:download(uri) end), }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/formfiller.lua000066400000000000000000000424001202446317700173420ustar00rootroot00000000000000------------------------------------------------------------------ -- Luakit formfiller -- -- © 2011 Fabian Streitel (karottenreibe) -- -- © 2011 Mason Larobina (mason-l) -- ------------------------------------------------------------------ local lousy = require("lousy") local string, table, io = string, table, io local loadstring, pcall = loadstring, pcall local setfenv = setfenv local warn = warn local print, type = print, type local pairs, ipairs = pairs, ipairs local tostring, tonumber = tostring, tonumber local capi = { luakit = luakit } local new_mode, add_binds = new_mode, add_binds local menu_binds = menu_binds local term = globals.term or "xterm" local editor = globals.editor or (os.getenv("EDITOR") or "vim") local editor_cmd = string.format("%s -e %s", term, editor) --- Provides functionaliy to auto-fill forms based on a Lua DSL. -- The configuration is stored in $XDG_DATA_DIR/luakit/forms.lua -- -- The following is an example for a formfiller definition: -- --
-- 
on "luakit.org" { --
form "profile1" { --
method = "post", --
action = "/login", --
className = "someFormClass", --
id = "form_id", --
input { --
name = "username", --
type = "text", --
className = "someClass", --
id = "username_field", --
value = "myUsername", --
}, --
input { --
name = "password", --
value = "myPassword", --
}, --
input { --
name = "autologin", --
type = "checkbox", --
checked = true, --
}, --
submit = true, --
}, --
} --
-- --
    --
  • The form function's string argument is optional. -- It allows you to define multiple profiles for use with the -- zL binding. --
  • All entries are matched top to bottom, until one fully matches -- or calls submit(). --
  • The submit attribute of a form can also be a number, which -- gives index of the submit button to click (starting with 1). -- If there is no such button ore the argument is true, -- form.submit() will be called instead. --
  • Instead of submit, you can also use focus = true -- inside an input to focus that element or select = true -- to select the text inside it. -- focus will trigger input mode. --
  • The string argument to the on function and all of -- the attributes of the form and input -- tables take JavaScript regular expressions. -- BEWARE their escaping! --
-- -- There is a conversion script in the luakit repository that converts -- from the old formfiller format to the new one. For more information, -- see the converter script under extras/convert_formfiller.rb -- module("formfiller") -- The Lua DSL file containing the formfiller rules local file = capi.luakit.data_dir .. "/forms.lua" -- The global formfiller JS code local formfiller_js = [=[ var formfiller = { toA: function (arr) { var ret = []; for (var i = 0; i < arr.length; ++i) { ret.push(arr[i]); } return ret; }, rexEscape: function (str) { return str.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&"); }, toLuaString: function (str) { return "'" + str.replace(/[\\'']/g, "\\$&") + "'"; }, click: function (element) { var mouseEvent = document.createEvent("MouseEvent"); mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); element.dispatchEvent(mouseEvent); }, forms: [], inputs: [], AttributeMatcher: function (tag, attrs, parents) { var documents = formfiller.toA(window.frames).map(function (frame) { return frame.document; }); documents.push(document); this.parents = parents || documents; var keys = [] for (var k in attrs) { keys.push(k); } this.getAll = function () { var elements = []; this.parents.forEach(function (p) { try { formfiller.toA(p.getElementsByTagName(tag)).filter(function (element) { return keys.every(function (key) { return new RegExp(attrs[key]).test(element[key]); }); }).forEach(function (e) { elements.push(e); }); } catch (e) { // ignore errors, is probably cross-domain stuff } }); return elements; }; }, }; ]=] -- Invokes an AttributeMatcher for the given tag and attributes with the -- given data on the given parents. -- Saves all found elements in an array under formfiller.{tag}s -- @param w The window to fill the forms on -- @param tag The element tag name to match against -- @param attributes The attributes to match -- @param data The attribute data to use for matching -- @param parents Start the search using formfiller.{parents} -- @return true iff something matched local function match(w, tag, attributes, data, parents) local js_template = [=[ var matcher = new formfiller.AttributeMatcher("{tag}", { {attrs} }, {parents}); var elements = matcher.getAll(); formfiller.{tag}s = elements; (elements.length > 0); ]=] -- ensure all attributes are there local attrs = "" for _, k in ipairs(attributes) do if data[k] then attrs = attrs .. string.format("%s: %q, ", k, data[k]) end end local js = string.gsub(js_template, "{(%w+)}", { attrs = attrs, tag = tag, parents = parents and string.format("formfiller.%s", parents) or "null", }) return w.view:eval_js(js) end -- The function environment for the formfiller script local DSL = { print = function (s, ...) print(...) end, -- DSL method to match a page by its URI on = function (s, pattern) return function (forms) table.insert(s.rules, { pattern = pattern, forms = forms, }) end end, -- DSL method to match a form by its attributes form = function (s, data) local transform = function (inputs, profile) local form = { profile = profile, inputs = {}, } for k, v in pairs(inputs) do if type(k) == "number" then form.inputs[k] = v else form[k] = v end end return form end if type(data) == "string" then local profile = data return function (inputs) return transform(inputs, profile) end else return transform(data) end end, -- DSL method to match an input element by its attributes input = function (s, attrs) return attrs end, } --- Reads the rules from the formfiller DSL file function init(w) w.formfiller_state = { rules = {}, menu_cache = {}, } -- the environment of the DSL script -- load the script local f = io.open(file, "r") if not f then return end -- file doesn't exist local code = f:read("*all") f:close() local dsl, message = loadstring(code) if not dsl then warn(string.format("loading formfiller data failed: %s", message)) return end -- execute in sandbox local env = {} -- wrap the DSL functions so they can access the state for k, fun in pairs(DSL) do env[k] = function (...) return DSL[k](w.formfiller_state, ...) end end setfenv(dsl, env) local success, err = pcall(dsl) if not success then warn("error in " .. file .. ": " .. err) end end --- Edits the formfiller rules. function edit() capi.luakit.spawn(string.format("%s %q", editor_cmd, file)) end --- Adds a new entry to the formfiller based on the current webpage. -- @param w The window for which to add an entry. function add(w) -- load JS prerequisites w.view:eval_js(formfiller_js) local js = [=[ var addAttr = function (str, elem, attr, indent, tail) { if (typeof(elem[attr]) == "string" && elem[attr] !== "") { str += indent + attr + ' = ' + formfiller.toLuaString(formfiller.rexEscape(elem[attr])) + tail; } return str; } var rendered_something = false; var str = 'on ' + formfiller.toLuaString(formfiller.rexEscape(location.href)) + ' {\n'; formfiller.toA(document.forms).forEach(function (form) { var inputs = formfiller.toA(form.getElementsByTagName("input")).filter(function (input) { return (input.type !== "button" && input.type !== "submit" && input.type !== "hidden"); }); if (inputs.length === 0) { return; } str += " form {\n"; ["method", "action", "id", "className", "name"].forEach(function (attr) { str = addAttr(str, form, attr, " ", ",\n"); }); inputs.forEach(function (input) { str += " input {\n "; ["id", "className", "name", "type"].forEach(function (attr) { str = addAttr(str, input, attr, "", ", "); }); if (input.type === "radio" || input.type === "checkbox") { str += "\n checked = " + input.checked + ",\n"; } else { str += "\n value = " + formfiller.toLuaString(input.value) + ",\n"; } str += " },\n"; }); str += " submit = true,\n"; str += " },\n"; rendered_something = true; }); str += "}\n\n"; rendered_something ? str : false; ]=] local ret = w.view:eval_js(js) if not ret then return w:error("no forms with inputs found") end local f = io.open(file, "a") f:write(ret) f:close() edit() end -- Matches all rules against the current view's URI. -- @param w The window on which to fill the forms -- @param rules The rules to filter -- @return A new table containing only the rules that matched the URI local function filter_rules(w, rules) local filtered = {} for _, rule in ipairs(rules) do -- match page URI in JS so we don't mix JS and Lua regexes in the formfiller config local js_template = [=[ (new RegExp({pattern}).test(location.href)); ]=] local js = string.gsub(js_template, "{(%w+)}", { pattern = string.format("%q", rule.pattern) }) if w.view:eval_js(js) then table.insert(filtered, rule) end end return filtered end -- Matches all forms against the current view. -- @param w The window on which to fill the forms -- @param forms The forms to filter -- @return A new table containing only the forms that matched the current view local function filter_forms(w, forms) local filtered = {} for _, form in ipairs(forms) do if match(w, "form", {"method", "name", "id", "action", "className"}, form) then table.insert(filtered, form) end end return filtered end -- Shows a menu with all forms that contain a profile if there is more than one. -- @param w The window on which to fill the forms -- @param forms The forms to search for profiles -- @return true iff the menu was shown local function show_menu(w, forms) local menu = {} for _, form in ipairs(forms) do if form.profile then table.insert(menu, { form.profile, form = form }) end end -- show menu if necessary if #menu < 2 then return false else w.formfiller_state.menu_cache = menu w:set_mode("formfiller") return true end end -- Fills the currently selected input with the given value -- @param w The window on which to fill the forms -- @param val The value to fill the input with local function fill_input(w, val) local js_template = [=[ if (formfiller.inputs) { var val = {str}; formfiller.inputs.forEach(function (i) { if (i.type === "radio" || i.type === "checkbox") { i.checked = (val && val !== "false"); } else { i.value = val; } }); } ]=] local js = string.gsub(js_template, "{(%w+)}", { str = string.format("%q", tostring(val)) }) w.view:eval_js(js) end -- Focuses the currently selected input. -- @param w The window on which to fill the forms local function focus_input(w) local js = [=[ if (formfiller.inputs && formfiller.inputs[0]) { formfiller.inputs[0].focus(); "true"; } else { "false"; } ]=] if w.view:eval_js(js) then w:set_mode("insert") end end -- Selects all text in the currently selected input. -- @param w The window on which to fill the forms local function select_input(w) local js = [=[ if (formfiller.inputs && formfiller.inputs[0]) { formfiller.inputs[0].select(); } ]=] w.view:eval_js(js) end -- Submits the currently selected form by clicking the nth button or -- calling form.submit(). -- @param w The window on which to fill the forms -- @param n The index of the button to click or -1 to use form.submit() local function submit_form(w, n) local js = string.format([=[ if (formfiller.forms && formfiller.forms[0]) { var inputs = formfiller.forms[0].getElementsByTagName('input'); inputs = formfiller.toA(inputs).filter(function (input) { return /submit/i.test(input.type); }); var n = %i - 1; if (inputs[n]) { formfiller.click(inputs[n]); } else { formfiller.forms[0].submit(); } } ]=], n) w.view:eval_js(js) end -- Applies all values to all matching inputs in a form and optionally focuses and selects -- the inputs or submits the form. -- @param w The window on which to fill the forms -- @returns true iff the form matched fully or was submitted local function apply_form(w, form) local full_match = true for _, input in ipairs(form.inputs) do if match(w, "input", {"name", "id", "className", "type"}, input, "forms") then local val = input.value or input.checked if val then fill_input(w, val) end if input.focus then focus_input(w) end if input.select then select_input(w) end else full_match = false end end local s = form.submit if s then submit_form(w, type(s) == "number" and s or -1) return true else return full_match end end --- Fills the current page from the formfiller rules. -- @param w The window on which to fill the forms -- @param fast Prevents any menus from being shown function load(w, fast) -- reload the DSL init(w) -- load JS prerequisites w.view:eval_js(formfiller_js) -- filter out all rules that do not match the current URI local rules = filter_rules(w, w.formfiller_state.rules) for _, rule in ipairs(rules) do -- filter out all forms that do not match the current page local forms = filter_forms(w, rule.forms) -- assemble a list of menu items to display, if any exist if fast or not show_menu(w, forms) then -- apply until the first form submits for _, form in ipairs(forms) do if apply_form(w, form) then return end end end end end -- Add formfiller mode new_mode("formfiller", { enter = function (w) local rows = {{ "Profile", title = true }} for _, m in ipairs(w.formfiller_state.menu_cache) do table.insert(rows, m) end w.menu:build(rows) end, leave = function (w) w.menu:hide() end, }) local key = lousy.bind.key add_binds("formfiller", lousy.util.table.join({ -- use profile key({}, "Return", "Select formfiller profile.", function (w) local row = w.menu:get() local form = row.form w:set_mode() if apply_form(w, form) then return end end), }, menu_binds)) -- Setup formfiller binds local buf = lousy.bind.buf add_binds("normal", { buf("^za$", "Add formfiller form.", function (w) add(w) end), buf("^ze$", "Edit formfiller forms for current domain.", function (w) edit() end), buf("^zl$", "Load formfiller form (use first profile).", function (w) load(w, true) end), buf("^zL$", "Load formfiller form.", function (w) load(w) end), }) luakit-2012.09.13-r1/lib/go_input.lua000066400000000000000000000031121202446317700170220ustar00rootroot00000000000000----------------------------------------------------------- -- Go to the first input on a page and enter insert mode -- -- © 2009 Aldrik Dunbar (n30n) -- -- © 2010 Paweł Zuzelski (pawelz) -- ----------------------------------------------------------- local go_input = [=[ (function (count) { var elements = document.querySelectorAll("textarea, input" + [ ":not([type='button'])", ":not([type='checkbox'])", ":not([type='hidden'])", ":not([type='image'])", ":not([type='radio'])", ":not([type='reset'])", ":not([type='submit'])"].join("")); if (elements) { var el, i = 0, n = 0; while((el = elements[i++])) { var style = getComputedStyle(el, null); if (style.display !== 'none' && style.visibility === 'visible') { n++; if (n == count) { if (el.type === "file") { el.click(); } else { el.focus(); } return "form-active"; } } } } return "root-active"; })]=] -- Add `w:go_input()` webview method webview.methods.go_input = function(view, w, count) local js = string.format("%s(%d);", go_input, count or 1) w:emit_form_root_active_signal(w.view:eval_js(js)) end -- Add `gi` binding to normal mode local buf = lousy.bind.buf add_binds("normal", { buf("^gi$", function (w, b, m) w:go_input(m.count) end, {count=1}) }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/go_next_prev.lua000066400000000000000000000042051202446317700177010ustar00rootroot00000000000000---------------------------------------------------------------- -- Follow "next" or "prev" links on a page -- -- © 2009 Aldrik Dunbar (n30n) -- -- © 2010 Mason Larobina (mason-l) -- ---------------------------------------------------------------- local go_next = [=[ (function() { function click(e) { if (e.href) document.location = e.href; else { var ev = document.createEvent("MouseEvent"); ev.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); e.dispatchEvent(ev); } } var e = document.querySelector("[rel='next']"); if (e) // Wow a developer that knows what he's doing! click(e); else { // Search from the bottom of the page up for a next link. var els = document.getElementsByTagName("a"), i = els.length; while ((e = els[--i])) { if (e.text.search(/(\bnext\b|^>$|^(>>|»)$|^(>|»)|(>|»)$|\bmore\b)/i) > -1) { click(e); break; } } } })(); ]=] local go_prev = [=[ (function() { function click(e) { if (e.href) document.location = e.href; else { var ev = document.createEvent("MouseEvent"); ev.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); e.dispatchEvent(ev); } } var e = document.querySelector("[rel='prev']"); if (e) click(e); else { var els = document.getElementsByTagName("a"), i = els.length; while ((e = els[--i])) { if (e.text.search(/(\b(prev|previous)\b|^<$|^(<<|«)$|^(<|«)|(<|«)$)/i) > -1) { click(e); break; } } } })(); ]=] -- Add `[[` & `]]` bindings to the normal mode. local buf = lousy.bind.buf add_binds("normal", { buf("^%]%]$", function (w) w.view:eval_js(go_next) end), buf("^%[%[$", function (w) w.view:eval_js(go_prev) end), }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/go_up.lua000066400000000000000000000044111202446317700163120ustar00rootroot00000000000000--------------------------------------------------------------------- -- Go one step upward in the URI path structure. -- -- © 2010-2012 Mason Larobina (mason-l) -- -- © 2012 LokiChaos -- --------------------------------------------------------------------- -- TODO check host against public TLD list to prevent returning only -- top-level domain. local parse_uri, uri_tostring = soup.parse_uri, soup.uri_tostring local match = string.match local M = {} local function go_up_step(u) -- Step 1: remove fragment if u.fragment then u.fragment = nil return end -- Step 2: remove query params if u.query then u.query = nil return end -- Step 3: remove sub-path from uri local path = u.path if path and path ~= "/" then u.path = match(path, "(.*/)[^/]*/$") or match(path, "(.*/)[^/]+$") return end -- Step 4: remove sub-domains from host local host = u.host if host then u.user = nil u.password = nil u.host = match(host, "%.(.+)$") or host return end end function M.go_up(uri, n) local u = soup.parse_uri(uri) if not u then error("invalid uri: " .. tostring(uri)) end for i = 1, (n or 1) do go_up_step(u) end return soup.uri_tostring(u) end function M.go_upmost(uri) local u = soup.parse_uri(uri) if not u then error("invalid uri: " .. tostring(uri)) end u.path = "/" u.fragment = nil u.query = nil return soup.uri_tostring(u) end -- Add `gu` & `gU` binds to the normal mode. local buf = lousy.bind.buf add_binds("normal", { buf("^gu$", "Go `[count=1]` step upward in the URI path structure.", function (w, _, m) local uri = w.view.uri if not uri or uri == "about:blank" then return end w.view.uri = M.go_up(uri, m.count or 1) end), buf("^gU$", "Go to up-most URI (maintains host).", function (w) local uri = w.view.uri if not uri or uri == "about:blank" then return end w.view.uri = M.go_upmost(uri) end), }) -- Return module table return setmetatable(M, { __call = function (M, ...) return M.go_up(...) end }) luakit-2012.09.13-r1/lib/history.lua000066400000000000000000000055411202446317700167070ustar00rootroot00000000000000----------------------------------------------------------- -- Save history in sqlite3 database -- -- © 2010-2011 Mason Larobina -- ----------------------------------------------------------- local os = require "os" local webview = webview local table = table local string = string local lousy = require "lousy" local capi = { luakit = luakit, sqlite3 = sqlite3 } module "history" -- Path of history sqlite database to open/create/update db_path = capi.luakit.data_dir .. "/history.db" -- Setup signals on history module lousy.signal.setup(_M, true) function init() -- Return if database handle already open if db then return end db = capi.sqlite3{ filename = _M.db_path } db:exec [[ PRAGMA synchronous = OFF; PRAGMA secure_delete = 1; CREATE TABLE IF NOT EXISTS history ( id INTEGER PRIMARY KEY, uri TEXT, title TEXT, visits INTEGER, last_visit INTEGER ); ]] query_find_last = db:compile [[ SELECT id FROM history WHERE uri = ? ORDER BY last_visit DESC LIMIT 1 ]] query_insert = db:compile [[ INSERT INTO history VALUES (NULL, ?, ?, ?, ?) ]] query_update_visits = db:compile [[ UPDATE history SET visits = visits + 1, last_visit = ? WHERE id = ? ]] query_update_title = db:compile [[ UPDATE history SET title = ? WHERE id = ? ]] end capi.luakit.idle_add(init) function add(uri, title, update_visits) if not db then init() end -- Ignore blank uris if not uri or uri == "" or uri == "about:blank" then return end -- Ask user if we should ignore uri if _M.emit_signal("add", uri, title) == false then return end -- Find existing item local item = (query_find_last:exec{uri})[1] if item then if update_visits ~= false then query_update_visits:exec{os.time(), item.id} end if title then query_update_title:exec{title, item.id} end else query_insert:exec{uri, title, 1, os.time()} end end webview.init_funcs.save_hist = function (view) -- Add items & update visit count view:add_signal("load-status", function (_, status) -- Don't add history items when in private browsing mode if view.enable_private_browsing then return end if status == "committed" then add(view.uri) end end) -- Update titles view:add_signal("property::title", function () -- Don't add history items when in private browsing mode if view.enable_private_browsing then return end local title = view.title if title and title ~= "" then add(view.uri, title, false) end end) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/history_chrome.lua000066400000000000000000000267001202446317700202440ustar00rootroot00000000000000-- Grab what we need from the Lua environment local table = table local string = string local io = io local print = print local pairs = pairs local ipairs = ipairs local math = math local assert = assert local setmetatable = setmetatable local rawget = rawget local rawset = rawset local type = type local os = os local error = error -- Grab the luakit environment we need local history = require("history") local lousy = require("lousy") local chrome = require("chrome") local add_binds = add_binds local add_cmds = add_cmds local webview = webview local capi = { luakit = luakit } module("history.chrome") stylesheet = [===[ .day-heading { font-size: 1.6em; font-weight: 100; margin: 1em 0 0.5em 0.5em; -webkit-user-select: none; cursor: default; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .day-sep { height: 1em; } .item { font-size: 1.3em; font-weight: 400; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .item span { padding: 0.2em; } .item .time { -webkit-user-select: none; cursor: default; color: #888; display: inline-block; width: 5em; text-align: right; border-right: 1px solid #ddd; padding-right: 0.5em; margin-right: 0.1em; } .item a { text-decoration: none; } .item .domain a { color: #aaa; } .item .domain a:hover { color: #666; } .item.selected { background-color: #eee; } .nav-button-box { margin: 2em; } .nav-button-box a { display: none; border: 1px solid #aaa; padding: 0.4em 1em; } ]===] local html = [==[ History
]==] local main_js = [=[ $(document).ready(function () { 'use strict'; var limit = 100, page = 1, results_len = 0; var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; var days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var item_html = $("#item-skelly").html(); $("#templates").remove(); function make_history_item(h) { var $e = $(item_html); $e.attr("history_id", h.id); $e.find(".time").text(h.time); $e.find(".title a") .attr("href", h.uri) .text(h.title || h.uri); var domain = /:\/\/([^/]+)\//.exec(h.uri); $e.find(".domain a").text(domain && domain[1] || ""); return $e.prop("outerHTML"); }; var $search = $('#search').eq(0), $results = $('#results').eq(0), $results_header = $("#results-header").eq(0), $clear_all = $("#clear-all-button").eq(0), $clear_results = $("#clear-results-button").eq(0), $clear_selected = $("#clear-selected-button").eq(0), $next = $("#nav-next").eq(0), $prev = $("#nav-prev").eq(0); function update_clear_buttons(all, results, selected) { $clear_all.attr("disabled", !!all); $clear_results.attr("disabled", !!results); $clear_selected.attr("disabled", !!selected); } function update_nav_buttons() { if (results_len === limit) $next.show(); else $next.hide(); if (page > 1) $prev.show(); else $prev.hide(); } function search() { var query = $search.val(), results = history_search({ query: query, limit: limit, page: page }); // Used to trigger hiding of next nav button when results_len < limit results_len = results.length || 0; update_clear_buttons(query, !query, true); if (!results.length) { $results.empty(); update_nav_buttons(); return; } var last_date, last_time = 0, group_html; var i = 0, len = results.length, html = ""; var sep = $("
").addClass("day-sep").prop("outerHTML"), $heading = $("
").addClass("day-heading"); for (; i < len;) { var h = results[i++]; if (h.date !== last_date) { last_date = h.date; html += $heading.text(h.date).prop("outerHTML"); } else if ((last_time - h.last_visit) > 3600) html += sep; last_time = h.last_visit; html += make_history_item(h); } update_nav_buttons(query); $results.get(0).innerHTML = html; } /* input field callback */ $search.keydown(function(ev) { if (ev.which == 13) { /* Return */ reset_mode(); page = 1; search(); $search.blur(); } }); $("#clear-button").click(function () { $search.val(""); page = 1; search(); }); $("#search-button").click(function () { page = 1; search(); }); // Auto search history by domain when clicking on domain $results.on("click", ".item .domain a", function (e) { $search.val($(this).text()); search(); }); // Select items & enable/disable clear all selected button $results.on("click", ".item", function (e) { var $e = $(this); if ($e.hasClass("selected")) { $(this).removeClass("selected"); if ($results.find(".selected").length === 0) $clear_selected.attr("disabled", true); } else { $(this).addClass("selected"); $clear_selected.attr("disabled", false); } }); $clear_all.click(function () { if (confirm("Clear all browsing history?")) { history_clear_all(); $results.fadeOut("fast", function () { $results.empty(); $results.show(); search(); }); $clear_all.blur(); } }); $next.click(function () { page++; search(); }); $prev.click(function () { page = Math.max(page-1,1); search(); }); function clear_elems($elems) { var ids = [], last = $elems.length - 1; $elems.each(function (index) { var $e = $(this); ids.push($e.attr("history_id")); if (index == last) $e.fadeOut("fast", function () { search(); }); else $e.fadeOut("fast"); }); if (ids.length) history_clear_list(ids); }; $clear_results.click(function () { clear_elems($results.find(".item")); $clear_results.blur(); }); $clear_selected.click(function () { clear_elems($results.find(".selected")); $clear_selected.blur(); }); var query = initial_search_term(); if (query) $search.val(query); search(); }); ]=] local initial_search_term export_funcs = { history_search = function (opts) local sql = { "SELECT", "*", "FROM history" } local where, args, argc = {}, {}, 1 string.gsub(opts.query or "", "(-?)([^%s]+)", function (notlike, term) if term ~= "" then table.insert(where, (notlike == "-" and "NOT " or "") .. string.format("(text GLOB ?%d)", argc, argc)) argc = argc + 1 table.insert(args, "*"..string.lower(term).."*") end end) if #where ~= 0 then sql[2] = [[ *, lower(uri||title) AS text ]] table.insert(sql, "WHERE " .. table.concat(where, " AND ")) end local order_by = [[ ORDER BY last_visit DESC LIMIT ?%d OFFSET ?%d ]] table.insert(sql, string.format(order_by, argc, argc+1)) local limit, page = opts.limit or 100, opts.page or 1 table.insert(args, limit) table.insert(args, limit > 0 and (limit * (page - 1)) or 0) sql = table.concat(sql, " ") if #where ~= 0 then local wrap = [[SELECT id, uri, title, last_visit FROM (%s)]] sql = string.format(wrap, sql) end local rows = history.db:exec(sql, args) for i, row in ipairs(rows) do local time = rawget(row, "last_visit") rawset(row, "date", os.date("%A, %d %B %Y", time)) rawset(row, "time", os.date("%H:%M", time)) end return rows end, history_clear_all = function () history.db:exec [[ DELETE FROM history ]] end, history_clear_list = function (ids) if not ids or #ids == 0 then return end local marks = {} for i=1,#ids do marks[i] = "?" end history.db:exec("DELETE FROM history WHERE id IN (" .. table.concat(marks, ",") .. " )", ids) end, initial_search_term = function () local term = initial_search_term initial_search_term = nil return term end, } chrome.add("history", function (view, meta) local html = string.gsub(html, "{%%(%w+)}", { -- Merge common chrome stylesheet and history stylesheet stylesheet = chrome.stylesheet .. stylesheet }) view:load_string(html, meta.uri) function on_first_visual(_, status) -- Wait for new page to be created if status ~= "first-visual" then return end -- Hack to run-once view:remove_signal("load-status", on_first_visual) -- Double check that we are where we should be if view.uri ~= meta.uri then return end -- Export luakit JS<->Lua API functions for name, func in pairs(export_funcs) do view:register_function(name, func) end view:register_function("reset_mode", function () meta.w:set_mode() -- HACK to unfocus search box end) -- Load jQuery JavaScript library local jquery = lousy.load("lib/jquery.min.js") local _, err = view:eval_js(jquery, { no_return = true }) assert(not err, err) -- Load main luakit://download/ JavaScript local _, err = view:eval_js(main_js, { no_return = true }) assert(not err, err) end view:add_signal("load-status", on_first_visual) end) -- Prevent history items from turning up in history history.add_signal("add", function (uri) if string.match(uri, "^luakit://history/") then return false end end) local cmd = lousy.bind.cmd add_cmds({ cmd("history", function (w, query) initial_search_term = query w:new_tab("luakit://history/") end), }) luakit-2012.09.13-r1/lib/introspector.lua000066400000000000000000000257071202446317700177470ustar00rootroot00000000000000-- Grab what we need from the Lua environment local table = table local string = string local io = io local print = print local pairs = pairs local ipairs = ipairs local math = math local assert = assert local setmetatable = setmetatable local rawget = rawget local rawset = rawset local type = type local os = os local error = error local package = package local debug = debug -- Grab the luakit environment we need local lousy = require("lousy") local globals = globals local dedent = lousy.util.string.dedent local chrome = require("chrome") local history = require("history") local markdown = require("markdown") local get_modes = get_modes local get_mode = get_mode local add_binds = add_binds local add_cmds = add_cmds local webview = webview local capi = { luakit = luakit } module("introspector") local html = [==[ Luakit Introspector

Luakit Help


    Function source:

]==] main_js = [=[ $(document).ready(function () { var $body = $(document.body); var mode_section_html = $("#mode-section-skelly").html(), mode_bind_html = $("#mode-bind-skelly").html(); // Remove all templates $("#templates").remove(); // Get all modes & sub-data var modes = help_get_modes(); for (var i = 0; i < modes.length; i++) { var mode = modes[i]; var $mode = $(mode_section_html); $mode.attr("id", "mode-" + mode.name); $mode.find("h3.mode-name").text(mode.name + " mode"); $mode.find("p.mode-desc").html(mode.desc); $mode.find("pre.mode-traceback").text(mode.traceback); var $binds = $mode.find(".binds"); var binds = mode.binds; for (var j = 0; j < binds.length; j++) { var b = binds[j]; var $bind = $(mode_bind_html); $bind.addClass("bind_type_" + b.type); $bind.find(".key").text(b.key); $bind.find(".func-source code").text(b.func); $bind.find(".filename").text(b.filename); var $l = $bind.find(".linedefined"); $l.text("#" + b.linedefined); $l.attr("filename", b.filename); $l.attr("line", b.linedefined); if (b.desc) $bind.find(".desc").html(b.desc); else $bind.find(".clear").hide(); $binds.append($bind); } $body.append($mode); } $body.on("click", ".bind .linedefined", function (event) { event.preventDefault(); var $e = $(this); open_editor($e.attr("filename"), $e.attr("line")); return false; }) $body.on("click", ".bind .desc a", function (event) { event.stopPropagation(); // prevent source toggling }) $body.on("click", ".bind", function (e) { var $src = $(this).find(".func-source"); if ($src.is(":visible")) $src.slideUp(); else $src.slideDown(); }) }); ]=] local function bind_tostring(b) local join = lousy.util.table.join local t = b.type local m = b.mods if t == "key" then if m or string.wlen(b.key) > 1 then return "<".. (m and (m.."-") or "") .. b.key .. ">" else return b.key end elseif t == "buffer" then local p = b.pattern if string.sub(p,1,1) .. string.sub(p, -1, -1) == "^$" then return string.sub(p, 2, -2) end return b.pattern elseif t == "button" then return "<" .. (m and (m.."-") or "") .. "Mouse" .. b.button .. ">" elseif t == "any" then return "any" elseif t == "command" then local cmds = {} for i, cmd in ipairs(b.cmds) do cmds[i] = ":"..cmd end return table.concat(cmds, ", ") end end local source_lines = {} local function function_source_range(func, info) local lines = source_lines[info.short_src] if not lines then local source = lousy.load(info.short_src) lines = {} string.gsub(source, "([^\n]*)\n", function (line) table.insert(lines, line) end) source_lines[info.short_src] = lines end return dedent(table.concat(lines, "\n", info.linedefined, info.lastlinedefined), true) end export_funcs = { help_get_modes = function () local ret = {} local modes = lousy.util.table.values(get_modes()) table.sort(modes, function (a, b) return a.order < b.order end) for _, mode in pairs(modes) do local binds = {} if mode.binds then for i, b in pairs(mode.binds) do local info = debug.getinfo(b.func, "uS") binds[i] = { type = b.type, key = bind_tostring(b), desc = b.desc and markdown(dedent(b.desc)) or nil, filename = info.short_src, linedefined = info.linedefined, lastlinedefined = info.lastlinedefined, func = function_source_range(b.func, info), } end end table.insert(ret, { name = mode.name, desc = mode.desc and markdown(dedent(mode.desc)) or nil, binds = binds, traceback = mode.traceback }) end -- Clear source file cache source_lines = {} return ret end, open_editor = function (file, line) local cmd = string.format("%s -e %s %q +%d", globals.term or "xterm", globals.editor or "vim", file, line) capi.luakit.spawn(cmd) end, } chrome.add("help", function (view, meta) view:load_string(html, meta.uri) function on_first_visual(_, status) -- Wait for new page to be created if status ~= "first-visual" then return end -- Hack to run-once view:remove_signal("load-status", on_first_visual) -- Double check that we are where we should be if view.uri ~= meta.uri then return end -- Export luakit JS<->Lua API functions for name, func in pairs(export_funcs) do view:register_function(name, func) end -- Load jQuery JavaScript library local jquery = lousy.load("lib/jquery.min.js") local _, err = view:eval_js(jquery, { no_return = true }) assert(not err, err) -- Load main luakit://download/ JavaScript local _, err = view:eval_js(main_js, { no_return = true }) assert(not err, err) end view:add_signal("load-status", on_first_visual) end) local cmd = lousy.bind.cmd add_cmds({ cmd("help", "Open [luakit://help/](luakit://help/) in a new tab.", function (w) w:new_tab("luakit://help/") end), }) -- Prevent history items from turning up in history history.add_signal("add", function (uri) if string.match(uri, "^luakit://help/") then return false end end) luakit-2012.09.13-r1/lib/jquery.min.js000066400000000000000000002711711202446317700171460ustar00rootroot00000000000000/*! jQuery v1.7.2 jquery.com | jquery.org/license */ (function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f .clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); luakit-2012.09.13-r1/lib/lousy/000077500000000000000000000000001202446317700156515ustar00rootroot00000000000000luakit-2012.09.13-r1/lib/lousy/bind.lua000066400000000000000000000246211202446317700172750ustar00rootroot00000000000000--------------------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- @copyright 2010 Mason Larobina --------------------------------------------------------------------------- -- Grab environment we need local assert = assert local ipairs = ipairs local pairs = pairs local setmetatable = setmetatable local string = string local table = table local type = type local unpack = unpack local util = require("lousy.util") local join = util.table.join local keys = util.table.keys local print = print -- Key, buffer and command binding functions. module("lousy.bind") -- Modifiers to ignore ignore_mask = { Mod2 = true, Mod3 = true, Mod5 = true, Lock = true, } -- A table that contains mappings for key names. map = { ISO_Left_Tab = "Tab", } function parse_mods(mods, remove_shift) local t = {} for _, mod in ipairs(mods) do if not ignore_mask[mod] then mod = map[mod] or mod t[mod] = true end end -- For single character bindings shift is not processed as it should -- have already transformed the keycode within gdk. if remove_shift then t.Shift = nil end mods = table.concat(keys(t), "-") return mods ~= "" and mods or nil end -- Create new key binding. function key(mods, key, desc, func, opts) -- Detect optional description & adjust argument positions if type(desc) == "function" then desc, func, opts = nil, desc, func end assert(type(mods) == "table", "invalid modifiers table") assert(type(key) == "string" and #key > 0, "invalid key string") assert(not desc or type(desc) == "string", "invalid description") assert(type(func) == "function", "invalid callback function") return { type = "key", mods = parse_mods(mods, string.wlen(key) == 1), key = key, desc = desc, func = func, opts = opts or {}, } end -- Create new button binding. function but(mods, button, desc, func, opts) -- Detect optional description & adjust argument positions if type(desc) == "function" then desc, func, opts = nil, desc, func end assert(type(mods) == "table", "invalid modifiers table") assert(type(button) == "number", "invalid button number") assert(not desc or type(desc) == "string", "invalid description") assert(type(func) == "function", "invalid callback function") return { type = "button", mods = parse_mods(mods), button = button, desc = desc, func = func, opts = opts or {}, } end -- Create new buffer binding. function buf(pattern, desc, func, opts) -- Detect optional description & adjust argument positions if type(desc) == "function" then desc, func, opts = nil, desc, func end assert(type(pattern) == "string" and #pattern > 0, "invalid pattern string") assert(not desc or type(desc) == "string", "invalid description") assert(type(func) == "function", "invalid callback function") return { type = "buffer", pattern = pattern, desc = desc, func = func, opts = opts or {}, } end -- Create new command binding. function cmd(cmds, desc, func, opts) -- Detect optional description & adjust argument positions if type(desc) == "function" then desc, func, opts = nil, desc, func end -- Parse "co[mmand]" or literal. if type(cmds) == "string" then if string.match(cmds, "^(%w+)%[(%w+)%]") then local l, r = string.match(cmds, "^(%w+)%[(%w+)%]") cmds = {l..r, l} else cmds = {cmds,} end end assert(type(cmds) == "table", "invalid commands table type") assert(#cmds > 0, "empty commands table") assert(not desc or type(desc) == "string", "invalid description") assert(type(func) == "function", "invalid callback function") return { type = "command", cmds = cmds, desc = desc, func = func, opts = opts or {}, } end -- Create a binding which is always called. function any(desc, func, opts) -- Detect optional description & adjust argument positions if type(desc) == "function" then desc, func, opts = nil, desc, func end assert(not desc or type(desc) == "string", "invalid description") assert(type(func) == "function", "invalid callback function") return { type = "any", func = func, desc = desc, opts = opts or {}, } end -- Try and match an any binding. function match_any(object, binds, args) for _, b in ipairs(binds) do if b.type == "any" then if b.func(object, join(b.opts, args)) ~= false then return true end end end return false end -- Try and match a key binding in a given table of bindings and call that -- bindings callback function. function match_key(object, binds, mods, key, args) for _, b in ipairs(binds) do if b.type == "key" and b.key == key and b.mods == mods then if b.func(object, join(b.opts, args)) ~= false then return true end end end return false end -- Try and match a button binding in a given table of bindings and call that -- bindings callback function. function match_but(object, binds, mods, button, args) for _, b in ipairs(binds) do if b.type == "button" and b.button == button and b.mods == mods then if b.func(object, join(b.opts, args)) ~= false then return true end end end return false end --- Try and match a buffer binding in a given table of bindings and call that -- bindings callback function. -- @param object The first argument of the bind callback function. -- @param binds The table of binds in which to check for a match. -- @param buffer The buffer string to match. -- @param args The bind options/state/metadata table which is applied over the -- opts table given when the bind was created. -- @return True if a binding was matched and called. function match_buf(object, binds, buffer, args) assert(buffer and string.match(buffer, "%S"), "invalid buffer") for _, b in ipairs(binds) do if b.type == "buffer" and string.match(buffer, b.pattern) then if b.func(object, buffer, join(b.opts, args)) ~= false then return true end --elseif b.type == "any" then -- if b.func(object, join(b.opts, args)) ~= false then -- return true -- end end end return false end --- Try and match a command or buffer binding in a given table of bindings -- and call that bindings callback function. -- @param object The first argument of the bind callback function. -- @param binds The table of binds in which to check for a match. -- @param buffer The buffer string to match. -- @param args The bind options/state/metadata table which is applied over the -- opts table given when the bind was created. -- @return True if either type of binding was matched and called. function match_cmd(object, binds, buffer, args) assert(buffer and string.match(buffer, "%S"), "invalid buffer") -- The command is the first word in the buffer string local command = string.match(buffer, "^(%S+)") -- And the argument is the entire string thereafter local argument = string.match(string.sub(buffer, #command + 1), "^%s+([^%s].*)$") -- Set args.cmd to tell buf/any binds they were called from match_cmd args = join(args or {}, { binds = binds, cmd = buffer, }) for _, b in ipairs(binds) do -- Command matching if b.type == "command" and util.table.hasitem(b.cmds, command) then if b.func(object, argument, join(b.opts, args)) ~= false then return true end -- Buffer matching elseif b.type == "buffer" and string.match(buffer, b.pattern) then if b.func(object, buffer, join(b.opts, args)) ~= false then return true end -- Any matching elseif b.type == "any" then if b.func(object, join(b.opts, args)) ~= false then return true end end end return false end --- Attempt to match either a key or buffer binding and execute it. This -- function is also responsible for performing operations on the buffer when -- necessary and the buffer is enabled. -- @param object The first argument of the bind callback function. -- @param binds The table of binds in which to check for a match. -- @param mods The modifiers to match. -- @param key The key name to match. -- @param args The bind options/state/metadata table which is applied over the -- opts table given when the bind was created. -- @return True if a key or buffer binding was matched or if a key was added to -- the buffer. -- @return The new buffer truncated to 10 characters (if you need more buffer -- then use the input bar for whatever you are doing). function hit(object, binds, mods, key, args) -- Convert keys using map key = map[key] or key local len = string.wlen(key) -- Compile metadata table args = join(args or {}, { object = object, binds = binds, mods = mods, key = key, }) mods = parse_mods(mods, type(key) == "string" and len == 1) if match_any(object, binds, args) then return true -- Match button bindings elseif type(key) == "number" then if match_but(object, binds, mods, key, args) then return true end return false -- Match key bindings elseif (not args.buffer or not args.enable_buffer) or mods or len ~= 1 then -- Check if the current buffer affects key bind (I.e. if the key has a -- `[count]` prefix) if match_key(object, binds, mods, key, args) then return true end end -- Clear buffer if not args.enable_buffer or mods then return false -- Else match buffer elseif len == 1 then if not args.updated_buf then args.buffer = (args.buffer or "") .. key args.updated_buf = true end if match_buf(object, binds, args.buffer, args) then return true end end -- Return buffer if valid if args.buffer then return false, string.sub(args.buffer, 1, 10) end return false end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/init.lua000066400000000000000000000007761202446317700173310ustar00rootroot00000000000000--------------------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- @copyright 2010 Mason Larobina --------------------------------------------------------------------------- require("lousy.util") require("lousy.bind") require("lousy.mode") require("lousy.theme") require("lousy.signal") require("lousy.widget") require("lousy.uri") require("lousy.load") --- Useful functions for luakit. module("lousy") -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/load.lua000066400000000000000000000025251202446317700172770ustar00rootroot00000000000000-- Get lua environment local io = require "io" local assert = assert local string = string local print = print local setmetatable = setmetatable local type = type -- Get luakit environment local capi = { luakit = luakit } module("lousy.load") -- Keep loaded resources in memory local data = {} local function load_resource(path, memorize) -- Have we already loaded this resource? if memorize and data[path] then return data[path] end -- Attempt to open & read resource local file = io.open(path) if file then -- Read resource local dat = file:read("*a") file:close() -- Memorize if asked if memorize then data[path] = dat end -- Return file contents return dat end end local function search_load(path, memorize) assert(type(path) == "string", "invalid path") memorize = not not memorise if string.sub(path, 1, 1) ~= "/" then -- Can we search relative paths? if capi.luakit.dev_paths then local dat = load_resource("./"..path, memorize) if dat then return dat end end path = capi.luakit.install_path.."/"..path end return assert(load_resource(path, memorize), "unable to load resource: " .. path) end setmetatable(_M, { __call = function (_, ...) return search_load(...) end }) luakit-2012.09.13-r1/lib/lousy/mode.lua000066400000000000000000000040521202446317700173010ustar00rootroot00000000000000--------------------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- @copyright 2010 Mason Larobina --------------------------------------------------------------------------- --- Grab environment we need local setmetatable = setmetatable local type = type local error = error --- Mode setting and getting operations for objects. module("lousy.mode") --- The default mode if no default modes are set. local default_mode = "normal" --- Weak table of objects current modes. local current_modes = {} setmetatable(current_modes, { __mode = "k" }) --- Weak table of objects default modes. local default_modes = {} setmetatable(default_modes, { __mode = "k" }) --- Check if the mode can be set on an object. -- An object is considered mode-able if it has an "emit_signal" method. -- @param object The object to check. function is_modeable(object) local t = type(object) return ((t == "table" or t == "userdata" or t == "lightuserdata") and type(object.emit_signal) == "function") end --- Get the current mode for a given object. -- @param object A mode-able object. -- @return The current mode of the given object, or the default mode of that object, -- or "normal". function get(object) if not is_modeable(object) then return error("attempt to get mode on non-modeable object") end return current_modes[object] or default_modes[object] or default_mode end --- Set the mode for a given object. -- @param object A mode-able object. -- @param mode A mode name (I.e. "insert", "command", ...) -- @return The newly set mode. function set(object, mode, ...) if not is_modeable(object) then return error("attempt to set mode on non-modeable object") end local mode = mode or default_modes[object] or default_mode current_modes[object] = mode -- Raises a mode change signal on the object. object:emit_signal("mode-changed", mode, ...) return mode end setmetatable(_M, { __call = function(_, ...) return set(...) end }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/signal.lua000066400000000000000000000063121202446317700176330ustar00rootroot00000000000000-------------------------------------------------------------- -- Mimic the luakit signal api functions for tables -- -- @author Fabian Streitel <karottenreibe@gmail.com> -- -- @author Mason Larobina <mason.larobina@gmail.com> -- -- @copyright 2010 Fabian Streitel, Mason Larobina -- -------------------------------------------------------------- -- Grab environment we need local assert = assert local io = io local ipairs = ipairs local setmetatable = setmetatable local string = string local table = table local tostring = tostring local type = type local unpack = unpack local verbose = luakit.verbose --- Provides a signal API similar to GTK's signals. module("lousy.signal") -- Private signal data for objects local data = setmetatable({}, { __mode = "k" }) local methods = { "add_signal", "emit_signal", "remove_signal", "remove_signals", } local function get_data(object) local d = data[object] assert(d, "object isn't setup for signals") return d end function add_signal(object, signame, func) local signals = get_data(object).signals -- Check signal name assert(type(signame) == "string", "invalid signame type: " .. type(signame)) assert(string.match(signame, "^[%w_%-:]+$"), "invalid chars in signame: " .. signame) -- Check handler function assert(type(func) == "function", "invalid handler function") -- Add to signals table if not signals[signame] then signals[signame] = { func, } else table.insert(signals[signame], func) end end function emit_signal(object, signame, ...) local d = get_data(object) local sigfuncs = d.signals[signame] or {} if verbose then io.stderr:write(string.format("D: lousy.signal: emit_signal: " .. "%q on %s\n", signame, tostring(object))) end for _, sigfunc in ipairs(sigfuncs) do local ret if d.module then ret = { sigfunc(...) } else ret = { sigfunc(object, ...) } end if ret[1] ~= nil then return unpack(ret) end end end -- Remove a signame & function pair. function remove_signal(object, signame, func) local signals = get_data(object).signals local sigfuncs = signals[signame] or {} for i, sigfunc in ipairs(sigfuncs) do if sigfunc == func then table.remove(sigfuncs, i) -- Remove empty sigfuncs table if #sigfuncs == 0 then signals[signame] = nil end return func end end end -- Remove all signal handlers with the given signame. function remove_signals(object, signame) local signals = get_data(object).signals signals[signame] = nil end function setup(object, module) assert(not data[object], "given object already setup for signals") data[object] = { signals = {}, module = module } for _, fn in ipairs(methods) do assert(not object[fn], "signal object method conflict: " .. fn) if module then local func = _M[fn] object[fn] = function (...) return func(object, ...) end else object[fn] = _M[fn] end end return object end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/theme.lua000066400000000000000000000040711202446317700174600ustar00rootroot00000000000000--------------------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- @author Damien Leone <damien.leone@gmail.com> -- @author Julien Danjou <julien@danjou.info> -- @copyright 2008-2009 Damien Leone, Julien Danjou, 2010 Mason Larobina --------------------------------------------------------------------------- --- Get environment we need local setmetatable = setmetatable local string = string local util = require "lousy.util" local error = error local rawget = rawget local pcall = pcall local dofile = dofile local type = type --- Theme library for lousy. module "lousy.theme" local theme -- Searches recursively for theme value. -- (I.e. `w.bg = theme.some_thing_bg` -> -- `w.bg = (theme.some_thing_bg or theme.thing_bg or theme.bg)`) local function index(t, k) local v = rawget(t, k) if v then return v end -- Knock a "level_" from the key name if string.find(k, "_") then local ret = index(t, string.sub(k, string.find(k, "_") + 1, -1)) -- Cache result if ret then t[k] = ret end return ret end end -- Minimum default theme local default_theme = { fg = "#fff", bg = "#000", font = "monospace normal 9", } --- Load the theme table from file. -- @param path The filepath of the theme. function init(path) if not path then return error("error loading theme: no path specified") end -- Load theme table local success success, theme = pcall(function() return dofile(path) end) if not success then return error("error loading theme file " .. theme) elseif not theme then return error("error loading theme file " .. path) elseif type(theme) ~= "table" then return error("error loading theme: not a table") end -- Merge with defaults and set metatable theme = setmetatable(util.table.join(default_theme, theme), { __index = index }) return theme end --- Get the current theme. -- @return The current theme table. function get() return theme end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/uri.lua000066400000000000000000000066341202446317700171640ustar00rootroot00000000000000------------------------------------------------------ -- URI parsing functions -- -- © 2011 Mason Larobina -- ------------------------------------------------------ -- Get lua environment local table = table local string = string local ipairs = ipairs local pairs = pairs local tostring = tostring local type = type local setmetatable = setmetatable local getmetatable = getmetatable local assert = assert local rawset = rawset -- Get luakit environment local util = require "lousy.util" local capi = { soup = soup } local uri_encode = luakit.uri_encode local uri_decode = luakit.uri_decode module "lousy.uri" local opts_metatable = { __tostring = function (opts) local ret, done = {}, {} -- Get opt order from metatable local mt = getmetatable(opts) -- Add original args first in order if mt and mt.order then for _, k in ipairs(mt.order) do local v = opts[k] if v and v ~= "" then table.insert(ret, uri_encode(k) .. "=" .. uri_encode(v)) done[k] = true end end end -- Add new args for k, v in pairs(opts) do if not done[k] and v ~= "" then table.insert(ret, uri_encode(k) .. "=" .. uri_encode(v)) end end -- Join query opts return table.concat(ret, "&") end, __add = function (op1, op2) assert(type(op1) == "table" and type(op2) == "table", "non-table operands") local ret = util.table.copy(op1) for k, v in pairs(op2) do ret[k] = v end return ret end, } --- Parse uri query --@param query the query component of a uri --@return table of options function parse_query(query) local opts, order = {}, {} string.gsub(query or "", "&*([^&=]+)=([^&]+)", function (k, v) opts[k] = uri_decode(v) table.insert(order, k) end) -- Put order table in opts metatable local mt = util.table.clone(opts_metatable) mt.order = order return setmetatable(opts, mt) end -- Allowed URI table properties local uri_allowed = { scheme = true, user = true, password = true, host = true, path = true, query = true, fragment = true, opts = true } -- URI table metatable local uri_metatable = { __tostring = function (uri) local t = util.table.clone(uri) t.query = tostring(t.opts) return capi.soup.uri_tostring(t) end, __add = function (op1, op2) assert(type(op1) == "table" and type(op2) == "table", "non-table operands") local ret = util.table.copy(op1) for k, v in pairs(op2) do assert(uri_allowed[k], "invalid property: " .. k) if k == "query" and type(v) == "string" then uri.opts = parse_query(v) else uri[k] = v end end return ret end, } -- Parse uri string and return uri table function parse(uri) -- Get uri table local uri = capi.soup.parse_uri(uri) if not uri then return end -- Parse uri.query and set uri.opts uri.opts = parse_query(uri.query) uri.query = nil return setmetatable(uri, uri_metatable) end -- Duplicate uri object function copy(uri) assert(type(uri) == "table", "not a table") return parse(tostring(uri)) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/util.lua000066400000000000000000000257461202446317700173470ustar00rootroot00000000000000--------------------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- @author Julien Danjou <julien@danjou.info> -- @copyright 2010 Mason Larobina, 2008 Julien Danjou --------------------------------------------------------------------------- --- Grab environment we need local assert = assert local print = print local debug = debug local error = error local setmetatable = setmetatable local getmetatable = getmetatable local io = io local ipairs = ipairs local loadstring = loadstring local os = os local pairs = pairs local rstring = string local rtable = table local type = type local tonumber = tonumber local tostring = tostring local math = require "math" local capi = { luakit = luakit } --- Utility functions for lousy. module("lousy.util") table = {} string = {} local xml_entity_names = { ["'"] = "'", ["\""] = """, ["<"] = "<", [">"] = ">", ["&"] = "&" }; local xml_entity_chars = { lt = "<", gt = ">", nbsp = " ", quot = "\"", apos = "'", ndash = "-", mdash = "-", amp = "&" }; --- Escape a string from XML characters. -- @param text The text to escape. -- @return A string with all XML characters escaped. function escape(text) return text and text:gsub("['&<>\"]", xml_entity_names) or nil end --- Unescape a string from XML entities. -- @param text The text to un-escape. -- @return A string with all the XML entities un-escaped. function unescape(text) return text and text:gsub("&(%a+);", xml_entity_chars) or nil end --- Create a directory -- @param dir The directory. -- @return mkdir return code function mkdir(dir) return os.execute(rstring.format("mkdir -p %q", dir)) end --- Eval Lua code. -- @return The return value of Lua code. function eval(s) return assert(loadstring(s))() end --- Check if a file is a Lua valid file. -- This is done by loading the content and compiling it with loadfile(). -- @param path The file path. -- @return A function if everything is alright, a string with the error -- otherwise. function checkfile(path) local f, e = loadfile(path) -- Return function if function, otherwise return error. if f then return f end return e end --- Return the difference of one table against another. -- @param t The original table. -- @param other The table to perform the difference against. -- @return All elements in the first table that are not in the other table. function table.difference(t, other) local ret = {} for k, v in pairs(t) do if type(k) == "number" then local found = false for _, ov in ipairs(other) do if ov == v then found = true break end end if not found then rtable.insert(ret, v) end else if not other[k] then ret[k] = v end end end return ret end --- Join all tables given as parameters. -- This will iterate all tables and insert all their keys into a new table. -- @param args A list of tables to join -- @return A new table containing all keys from the arguments. function table.join(...) local ret = {} for _, t in pairs({...}) do for k, v in pairs(t) do if type(k) == "number" then rtable.insert(ret, v) else ret[k] = v end end end return ret end --- Check if a table has an item and return its key. -- @param t The table. -- @param item The item to look for in values of the table. -- @return The key were the item is found, or nil if not found. function table.hasitem(t, item) for k, v in pairs(t) do if v == item then return k end end end --- Get a sorted table with all integer keys from a table -- @param t the table for which the keys to get -- @return A table with keys function table.keys(t) local keys = { } for k, _ in pairs(t) do rtable.insert(keys, k) end rtable.sort(keys, function (a, b) return type(a) == type(b) and a < b or false end) return keys end --- Reverse a table -- @param t the table to reverse -- @return the reversed table function table.reverse(t) local tr = { } -- reverse all elements with integer keys for _, v in ipairs(t) do rtable.insert(tr, 1, v) end -- add the remaining elements for k, v in pairs(t) do if type(k) ~= "number" then tr[k] = v end end return tr end --- Clone a table -- @param t the table to clone -- @return a clone of t function table.clone(t) local c = { } for k, v in pairs(t) do c[k] = v end return c end --- Clone table and set metatable -- @param t the table to clone -- @return a clone of t with t's metatable function table.copy(t) local c = table.clone(t) return setmetatable(c, getmetatable(t)) end --- Check if two tables are identical. -- @param a The first table. -- @param b The second table. -- @return True if both tables are identical. function table.isclone(a, b) if #a ~= #b then return false end for k, v in pairs(a) do if a[k] ~= b[k] then return false end end return true end --- Clone a table with all values as array items. -- @param t the table to clone -- @return all values in t function table.values(t) local ret = {} for _, v in pairs(t) do rtable.insert(ret, v) end return ret end --- Convert a table to an array by removing all keys that are not sequential numbers. -- @param t the table to converts -- @return a new table with all non-number keys removed function table.toarray(t) local ret = {} for k, v in ipairs(t) do ret[k] = v end return ret end --- Check if a file exists and is readable. -- @param f The file path. -- @return True if the file exists and is readable. function os.exists(f) assert(type(f) == "string", "invalid path") fh, err = io.open(f) if fh then fh:close() return f end end --- Python like string split (source: lua wiki) -- @param s The string to split. -- @param pattern The split pattern (I.e. "%s+" to split text by one or more -- whitespace characters). -- @param ret The table to insert the split items in to or a new table if nil. -- @return A table of the string split by the pattern. function string.split(s, pattern, ret) if not pattern then pattern = "%s+" end if not ret then ret = {} end local pos = 1 local fstart, fend = rstring.find(s, pattern, pos) while fstart do rtable.insert(ret, rstring.sub(s, pos, fstart - 1)) pos = fend + 1 fstart, fend = rstring.find(s, pattern, pos) end rtable.insert(ret, rstring.sub(s, pos)) return ret end -- Python like string strip. -- @param s The string to strip. -- @param pattern The pattern to strip from the left-most and right-most of the -- string. -- @return The inner string segment. function string.strip(s, pattern) local p = pattern or "%s*" local sub_start, sub_end -- Find start point local _, f_end = rstring.find(s, "^"..p) if f_end then sub_start = f_end + 1 end -- Find end point local f_start = rstring.find(s, p.."$") if f_start then sub_end = f_start - 1 end return rstring.sub(s, sub_start or 1, sub_end or #s) end function string.dedent(text, first) local min = first and #rstring.match(text, "^(%s*)") or nil rstring.gsub(text, "\n(%s*)", function (spaces) local len = #spaces if not min or len < min then min = len end end) if min and min > 0 then local pat = "\n" .. rstring.rep(" ", min) text = rstring.gsub(text, pat, "\n") end return first and rstring.sub(text, min + 1) or text end local function find_file(paths) for _, p in ipairs(paths) do if os.exists(p) then return p end end return error(rstring.format("No such file at: \n\t%s\n", rtable.concat(paths, ",\n\t"))) end --- Search and return the filepath of a file in the current working directory, -- or $XDG_CONFIG_HOME/luakit/ or /etc/xdg/luakit/. -- @param f The relative filepath. -- @return The first valid filepath or an error. function find_config(f) if rstring.match(f, "^/") then return f end -- Search locations local paths = { "config/"..f, capi.luakit.config_dir.."/"..f, "/etc/xdg/luakit/"..f } return find_file(paths) end --- Search and return the filepath of a file in the current working directory, -- in the users $XDG_DATA_HOME/luakit/ or the luakit install dir. -- @param f The relative filepath. -- @return The first valid filepath or an error. function find_data(f) if rstring.match(f, "^/") then return f end -- Search locations local paths = { f, capi.luakit.data_dir.."/"..f, capi.luakit.install_path.."/"..f } return find_file(paths) end --- Search and return the filepath of a file in the current working directory -- or in the users $XDG_CACHE_HOME/luakit/ -- @param f The relative filepath. -- @return The first valid filepath or an error. function find_cache(f) -- Ignore absolute paths if rstring.match(f, "^/") then return f end -- Search locations local paths = { capi.luakit.cache_dir.."/"..f } return find_file(paths) end --- Recursively traverse widget tree and return all widgets. -- @param wi The widget. function recursive_remove(wi) local ret = {} -- Empty other container widgets for _, child in ipairs(wi.children or {}) do wi:remove(child) rtable.insert(ret, child) for _, c in ipairs(recursive_remove(child)) do rtable.insert(ret, c) end end return ret end --- Convert a number to string independent from locale. -- @param num A number. -- @param sigs Signifigant figures (if float). -- @return The string representation of the number. function ntos(num, sigs) local dec = rstring.sub(tostring(num % 1), 3, 2 + (sigs or 4)) num = tostring(math.floor(num)) return (#dec == 0 and num) or (num .. "." .. dec) end --- Escape values for SQL queries. -- In sqlite3: "A string constant is formed by enclosing the string in single -- quotes ('). A single quote within the string can be encoded by putting two -- single quotes in a row - as in Pascal." -- Read: http://sqlite.org/lang_expr.html function sql_escape(s) return "'" .. rstring.gsub(s or "", "'", "''") .. "'" end --- Get all hostnames in /etc/hosts -- @param Force re-load of /etc/hosts -- @return Table of all hostnames in /etc/hosts local etc_hosts function get_etc_hosts(force) -- Unless forced return previous hostnames if not force and etc_hosts then return etc_hosts end -- Parse /etc/hosts local match, find, gsub = rstring.match, rstring.find, rstring.gsub local h = { localhost = "localhost" } for line in io.lines("/etc/hosts") do if not find(line, "^#") then local names = match(line, "^%S+%s+(.+)$") gsub(names or "", "(%S+)", function (name) h[name] = name -- key add removes duplicates end) end end etc_hosts = table.values(h) return etc_hosts end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/lousy/widget/000077500000000000000000000000001202446317700171345ustar00rootroot00000000000000luakit-2012.09.13-r1/lib/lousy/widget/init.lua000066400000000000000000000005501202446317700206020ustar00rootroot00000000000000------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- -- @copyright 2010 Mason Larobina -- ------------------------------------------------------------- require "lousy.widget.tablist" require "lousy.widget.menu" --- Widget module for luakit module "lousy.widget" luakit-2012.09.13-r1/lib/lousy/widget/menu.lua000066400000000000000000000203011202446317700205770ustar00rootroot00000000000000------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- -- @copyright 2010 Mason Larobina -- ------------------------------------------------------------- -- Grab environment we need local capi = { widget = widget } local setmetatable = setmetatable local math = require "math" local signal = require "lousy.signal" local type = type local assert = assert local ipairs = ipairs local table = table local util = require "lousy.util" local get_theme = require("lousy.theme").get module "lousy.widget.menu" local data = setmetatable({}, { __mode = "k" }) function update(menu) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Get theme table local theme = get_theme() local fg, bg, font = theme.menu_fg, theme.menu_bg, theme.menu_font local sfg, sbg = theme.menu_selected_fg, theme.menu_selected_bg -- Hide widget while re-drawing menu.widget:hide() -- Build & populate rows for i = 1, math.max(d.max_rows, #(d.table)) do -- Get row local index = i + d.offset - 1 local row = (i <= d.max_rows) and d.rows[index] -- Get row widget table local rw = d.table[i] -- Make new row if row and not rw then -- Row widget struct rw = { ebox = capi.widget{type = "eventbox"}, hbox = capi.widget{type = "hbox"}, cols = {}, } rw.ebox.child = rw.hbox d.table[i] = rw -- Add to main vbox menu.widget:pack(rw.ebox) -- Remove row elseif not row and rw then -- Destroy row columns (label widgets) for _, l in ipairs(rw.cols) do rw.hbox:remove(l) l:destroy() end rw.ebox:remove(rw.hbox) rw.hbox:destroy() menu.widget:remove(rw.ebox) rw.ebox:destroy() d.table[i] = nil end -- Populate columns if row and rw then -- Match up row data with row widget (for callbacks) rw.data = row -- Try to find last off-screen title row and replace with current if i == 1 and not row.title and d.offset > 1 then local j = d.offset - 1 while j > 0 do local r = d.rows[j] -- Only check rows with same number of columns if r.ncols ~= row.ncols then break end -- Check if title row if r.title then row, index = r, j break end j = j - 1 end end -- Is this the selected row? selected = not row.title and index == d.cursor -- Set row bg local rbg if row.title then rbg = (row.bg or theme.menu_title_bg) or bg else rbg = (selected and (row.selected_bg or sbg)) or row.bg or bg end if rw.ebox.bg ~= rbg then rw.ebox.bg = rbg end for c = 1, math.max(row.ncols, #(rw.cols)) do -- Get column text local text = row[c] text = (type(text) == "function" and text(row)) or text -- Get table cell local cell = rw.cols[c] -- Make new row column widget if text and not cell then cell = capi.widget{type = "label"} rw.hbox:pack(cell, { expand = true, fill = true }) rw.cols[c] = cell cell.font = font cell.width = 1 -- Remove row column widget elseif not text and cell then rw.hbox:remove(cell) rw.cols[c] = nil cell:destroy() end -- Set cell props if text and cell and row.title then cell.text = text local fg = row.fg or (c == 1 and theme.menu_primary_title_fg or theme.menu_secondary_title_fg) or fg if cell.fg ~= fg then cell.fg = fg end elseif text and cell then cell.text = text local fg = (selected and (row.selected_fg or sfg)) or row.fg or fg if cell.fg ~= fg then cell.fg = fg end end end end end -- Show widget menu.widget:show() end function build(menu, rows) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Check rows for i, row in ipairs(rows) do assert(type(row) == "table", "invalid row in rows table") assert(#row >= 1, "empty row") row.ncols = #row end d.rows = rows d.nrows = #rows -- Initial positions d.cursor = 0 d.offset = 1 update(menu) end local function calc_offset(menu) local d = data[menu] if d.cursor < 1 then return elseif d.cursor <= d.offset then d.offset = math.max(d.cursor - 1, 1) elseif d.cursor > (d.offset + d.max_rows - 1) then d.offset = math.max(d.cursor - d.max_rows + 1, 1) end end function move_up(menu) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Move cursor if not d.cursor or d.cursor < 1 then d.cursor = d.nrows else d.cursor = d.cursor - 1 end -- Get next non-title row (you can't select titles) while d.cursor > 0 and d.cursor <= d.nrows and d.rows[d.cursor].title do d.cursor = d.cursor - 1 end calc_offset(menu) update(menu) -- Emit changed signals menu:emit_signal("changed", menu:get()) end function move_down(menu) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Move cursor if d.cursor == d.nrows then d.cursor = 0 else d.cursor = (d.cursor or 0) + 1 end -- Get next non-title row (you can't select titles) while d.cursor > 0 and d.cursor <= d.nrows and d.rows[d.cursor].title do d.cursor = d.cursor + 1 end calc_offset(menu) update(menu) -- Emit changed signals menu:emit_signal("changed", menu:get()) end function get(menu, index) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Return row at given index or current cursor position. return d.rows[index or d.cursor] end function del(menu, index) assert(data[menu] and type(menu.widget) == "widget", "invalid menu widget") -- Get private menu widget data local d = data[menu] -- Unable to delete this index, return if d.cursor < 1 then return end table.remove(d.rows, d.cursor) -- Update rows count d.nrows = #(d.rows) -- Check cursor d.cursor = math.min(d.cursor, d.nrows) d.offset = math.min(d.offset, math.max(d.nrows - d.max_rows + 1, 1)) update(menu) -- Emit changed signals menu:emit_signal("changed", menu:get()) end function new(args) args = args or {} local menu = { widget = capi.widget{type = "vbox"}, -- Add widget methods build = build, update = update, get = get, del = del, move_up = move_up, move_down = move_down, hide = function(menu) menu.widget:hide() end, show = function(menu) menu.widget:show() end, } -- Save private widget data data[menu] = { -- Hold the rows & columns of label widgets which construct the -- menu list widget. table = {}, max_rows = args.max_rows or 10, nrows = 0, rows = {}, } -- Setup class signals signal.setup(menu) return menu end setmetatable(_M, { __call = function(_, ...) return new(...) end }) luakit-2012.09.13-r1/lib/lousy/widget/tablist.lua000066400000000000000000000063151202446317700213060ustar00rootroot00000000000000------------------------------------------------------------- -- @author Mason Larobina <mason.larobina@gmail.com> -- -- @copyright 2010 Mason Larobina -- ------------------------------------------------------------- -- Grab environment we need local assert = assert local setmetatable = setmetatable local table = table local type = type local signal = require "lousy.signal" local get_theme = require("lousy.theme").get local capi = { widget = widget } module "lousy.widget.tablist" local data = setmetatable({}, { __mode = "k" }) function update(tlist, tabs, current) -- Check function arguments assert(data[tlist] and type(tlist.widget) == "widget", "invalid tablist widget") assert(type(tabs) == "table", "invalid tabs table") assert(current >= 0 and current <= #tabs, "current index out of range") -- Hide tablist while re-drawing widgets tlist.widget:hide() local labels = data[tlist].labels local theme = get_theme() -- Make some new tab labels local tcount, lcount = #tabs, #labels if tcount > lcount then for i = lcount+1, tcount do local tl = { ebox = capi.widget{type = "eventbox"}, label = capi.widget{type = "label"} } tl.label.font = theme.tab_font tl.label.width = 1 tl.ebox.child = tl.label tl.ebox:add_signal("button-release", function (e, mods, but) return tlist:emit_signal("tab-clicked", i, mods, but) end) tl.ebox:add_signal("button-double-click", function (e, mods, but) return tlist:emit_signal("tab-double-clicked", i, mods, but) end) tlist.widget:pack(tl.ebox, { expand = true, fill = true }) labels[i] = tl end end -- Delete some tab labels if lcount > tcount then for i = tcount+1, lcount do local tl = table.remove(labels, tcount+1) tlist.widget:remove(tl.ebox) tl.label:destroy() tl.ebox:destroy() end end -- Update titles & theme local fg, bg = theme.tab_fg, theme.tab_bg for i = 1, tcount do local tab, l, e = tabs[i], labels[i].label, labels[i].ebox local title = tab.title or "(Untitled)" local fg, bg = tab.fg or fg, tab.bg or bg if l.text ~= title then l.text = title end if l.fg ~= fg then l.fg = fg end if e.bg ~= bg then e.bg = bg end end -- Show tablist if tcount > 0 then tlist.widget:show() end -- Emit update signal tlist:emit_signal("updated") return tlist end function destroy(tlist) -- Destroy all tablabels update(tlist, {}, 0) -- Destroy tablist container widget tlist.widget:destroy() -- Destroy private widget data data[tlist] = nil end function new() -- Create tablist widget table local tlist = { widget = capi.widget{type = "hbox"}, update = update, destroy = destroy, } -- Save private widget data data[tlist] = { labels = {}, } -- Setup class signals signal.setup(tlist) return tlist end setmetatable(_M, { __call = function(_, ...) return new(...) end }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/markdown.lua000066400000000000000000001152651202446317700170350ustar00rootroot00000000000000#!/usr/bin/env lua --[[ # markdown.lua -- version 0.32 **Author:** Niklas Frykholm, **Date:** 31 May 2008 This is an implementation of the popular text markup language Markdown in pure Lua. Markdown can convert documents written in a simple and easy to read text format to well-formatted HTML. For a more thourough description of Markdown and the Markdown syntax, see . The original Markdown source is written in Perl and makes heavy use of advanced regular expression techniques (such as negative look-ahead, etc) which are not available in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground up. It is probably not completely bug free. If you notice any bugs, please report them to me. A unit test that exposes the error is helpful. ## Usage require "markdown" markdown(source) ``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the Markdown transformation to the specified string. ``markdown.lua`` can also be used directly from the command line: lua markdown.lua test.md Creates a file ``test.html`` with the converted content of ``test.md``. Run: lua markdown.lua -h For a description of the command-line options. ``markdown.lua`` uses the same license as Lua, the MIT license. ## License Copyright © 2008 Niklas Frykholm. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ## Version history - **0.32** -- 31 May 2008 - Fix for links containing brackets - **0.31** -- 1 Mar 2008 - Fix for link definitions followed by spaces - **0.30** -- 25 Feb 2008 - Consistent behavior with Markdown when the same link reference is reused - **0.29** -- 24 Feb 2008 - Fix for
 blocks with spaces in them
-	**0.28** -- 18 Feb 2008
	-	Fix for link encoding
-	**0.27** -- 14 Feb 2008
	-	Fix for link database links with ()
-	**0.26** -- 06 Feb 2008
	-	Fix for nested italic and bold markers
-	**0.25** -- 24 Jan 2008
	-	Fix for encoding of naked <
-	**0.24** -- 21 Jan 2008
	-	Fix for link behavior.
-	**0.23** -- 10 Jan 2008
	-	Fix for a regression bug in longer expressions in italic or bold.
-	**0.22** -- 27 Dec 2007
	-	Fix for crash when processing blocks with a percent sign in them.
-	**0.21** -- 27 Dec 2007
	- 	Fix for combined strong and emphasis tags
-	**0.20** -- 13 Oct 2007
	-	Fix for < as well in image titles, now matches Dingus behavior
-	**0.19** -- 28 Sep 2007
	-	Fix for quotation marks " and ampersands & in link and image titles.
-	**0.18** -- 28 Jul 2007
	-	Does not crash on unmatched tags (behaves like standard markdown)
-	**0.17** -- 12 Apr 2007
	-	Fix for links with %20 in them.
-	**0.16** -- 12 Apr 2007
	-	Do not require arg global to exist.
-	**0.15** -- 28 Aug 2006
	-	Better handling of links with underscores in them.
-	**0.14** -- 22 Aug 2006
	-	Bug for *`foo()`*
-	**0.13** -- 12 Aug 2006
	-	Added -l option for including stylesheet inline in document.
	-	Fixed bug in -s flag.
	-	Fixed emphasis bug.
-	**0.12** -- 15 May 2006
	-	Fixed several bugs to comply with MarkdownTest 1.0 
-	**0.11** -- 12 May 2006
	-	Fixed bug for escaping `*` and `_` inside code spans.
	-	Added license terms.
	-	Changed join() to table.concat().
-	**0.10** -- 3 May 2006
	-	Initial public release.

// Niklas
]]


-- Set up a table for holding local functions to avoid polluting the global namespace
local M = {}
local MT = {__index = _G}
setmetatable(M, MT)
setfenv(1, M)

----------------------------------------------------------------------
-- Utility functions
----------------------------------------------------------------------

-- Locks table t from changes, writes an error if someone attempts to change the table.
-- This is useful for detecting variables that have "accidently" been made global. Something
-- I tend to do all too much.
function lock(t)
	function lock_new_index(t, k, v)
		error("module has been locked -- " .. k .. " must be declared local", 2)
	end

	local mt = {__newindex = lock_new_index}
	if getmetatable(t) then mt.__index = getmetatable(t).__index end
	setmetatable(t, mt)
end

-- Returns the result of mapping the values in table t through the function f
function map(t, f)
	local out = {}
	for k,v in pairs(t) do out[k] = f(v,k) end
	return out
end

-- The identity function, useful as a placeholder.
function identity(text) return text end

-- Functional style if statement. (NOTE: no short circuit evaluation)
function iff(t, a, b) if t then return a else return b end end

-- Splits the text into an array of separate lines.
function split(text, sep)
	sep = sep or "\n"
	local lines = {}
	local pos = 1
	while true do
		local b,e = text:find(sep, pos)
		if not b then table.insert(lines, text:sub(pos)) break end
		table.insert(lines, text:sub(pos, b-1))
		pos = e + 1
	end
	return lines
end

-- Converts tabs to spaces
function detab(text)
	local tab_width = 4
	local function rep(match)
		local spaces = -match:len()
		while spaces<1 do spaces = spaces + tab_width end
		return match .. string.rep(" ", spaces)
	end
	text = text:gsub("([^\n]-)\t", rep)
	return text
end

-- Applies string.find for every pattern in the list and returns the first match
function find_first(s, patterns, index)
	local res = {}
	for _,p in ipairs(patterns) do
		local match = {s:find(p, index)}
		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end
	end
	return unpack(res)
end

-- If a replacement array is specified, the range [start, stop] in the array is replaced
-- with the replacement array and the resulting array is returned. Without a replacement
-- array the section of the array between start and stop is returned.
function splice(array, start, stop, replacement)
	if replacement then
		local n = stop - start + 1
		while n > 0 do
			table.remove(array, start)
			n = n - 1
		end
		for i,v in ipairs(replacement) do
			table.insert(array, start, v)
		end
		return array
	else
		local res = {}
		for i = start,stop do
			table.insert(res, array[i])
		end
		return res
	end
end

-- Outdents the text one step.
function outdent(text)
	text = "\n" .. text
	text = text:gsub("\n  ? ? ?", "\n")
	text = text:sub(2)
	return text
end

-- Indents the text one step.
function indent(text)
	text = text:gsub("\n", "\n    ")
	return text
end

-- Does a simple tokenization of html data. Returns the data as a list of tokens.
-- Each token is a table with a type field (which is either "tag" or "text") and
-- a text field (which contains the original token data).
function tokenize_html(html)
	local tokens = {}
	local pos = 1
	while true do
		local start = find_first(html, {"", start)
		elseif html:match("^<%?", start) then
			_,stop = html:find("?>", start)
		else
			_,stop = html:find("%b<>", start)
		end
		if not stop then
			-- error("Could not match html tag " .. html:sub(start,start+30))
		 	table.insert(tokens, {type="text", text=html:sub(start, start)})
			pos = start + 1
		else
			table.insert(tokens, {type="tag", text=html:sub(start, stop)})
			pos = stop + 1
		end
	end
	return tokens
end

----------------------------------------------------------------------
-- Hash
----------------------------------------------------------------------

-- This is used to "hash" data into alphanumeric strings that are unique
-- in the document. (Note that this is not cryptographic hash, the hash
-- function is not one-way.) The hash procedure is used to protect parts
-- of the document from further processing.

local HASH = {
	-- Has the hash been inited.
	inited = false,

	-- The unique string prepended to all hash values. This is to ensure
	-- that hash values do not accidently coincide with an actual existing
	-- string in the document.
	identifier = "",

	-- Counter that counts up for each new hash instance.
	counter = 0,

	-- Hash table.
	table = {}
}

-- Inits hashing. Creates a hash_identifier that doesn't occur anywhere
-- in the text.
function init_hash(text)
	HASH.inited = true
	HASH.identifier = ""
	HASH.counter = 0
	HASH.table = {}

	local s = "HASH"
	local counter = 0
	local id
	while true do
		id  = s .. counter
		if not text:find(id, 1, true) then break end
		counter = counter + 1
	end
	HASH.identifier = id
end

-- Returns the hashed value for s.
function hash(s)
	assert(HASH.inited)
	if not HASH.table[s] then
		HASH.counter = HASH.counter + 1
		local id = HASH.identifier .. HASH.counter .. "X"
		HASH.table[s] = id
	end
	return HASH.table[s]
end

----------------------------------------------------------------------
-- Protection
----------------------------------------------------------------------

-- The protection module is used to "protect" parts of a document
-- so that they are not modified by subsequent processing steps.
-- Protected parts are saved in a table for later unprotection

-- Protection data
local PD = {
	-- Saved blocks that have been converted
	blocks = {},

	-- Block level tags that will be protected
	tags = {"p", "div", "h1", "h2", "h3", "h4", "h5", "h6", "blockquote",
	"pre", "table", "dl", "ol", "ul", "script", "noscript", "form", "fieldset",
	"iframe", "math", "ins", "del"}
}

-- Pattern for matching a block tag that begins and ends in the leftmost
-- column and may contain indented subtags, i.e.
-- 
-- A nested block. --
-- Nested data. --
--
function block_pattern(tag) return "\n<" .. tag .. ".-\n[ \t]*\n" end -- Pattern for matching a block tag that begins and ends with a newline function line_pattern(tag) return "\n<" .. tag .. ".-[ \t]*\n" end -- Protects the range of characters from start to stop in the text and -- returns the protected string. function protect_range(text, start, stop) local s = text:sub(start, stop) local h = hash(s) PD.blocks[h] = s text = text:sub(1,start) .. h .. text:sub(stop) return text end -- Protect every part of the text that matches any of the patterns. The first -- matching pattern is protected first, etc. function protect_matches(text, patterns) while true do local start, stop = find_first(text, patterns) if not start then break end text = protect_range(text, start, stop) end return text end -- Protects blocklevel tags in the specified text function protect(text) -- First protect potentially nested block tags text = protect_matches(text, map(PD.tags, block_pattern)) -- Then protect block tags at the line level. text = protect_matches(text, map(PD.tags, line_pattern)) -- Protect
and comment tags text = protect_matches(text, {"\n]->[ \t]*\n"}) text = protect_matches(text, {"\n[ \t]*\n"}) return text end -- Returns true if the string s is a hash resulting from protection function is_protected(s) return PD.blocks[s] end -- Unprotects the specified text by expanding all the nonces function unprotect(text) for k,v in pairs(PD.blocks) do v = v:gsub("%%", "%%%%") text = text:gsub(k, v) end return text end ---------------------------------------------------------------------- -- Block transform ---------------------------------------------------------------------- -- The block transform functions transform the text on the block level. -- They work with the text as an array of lines rather than as individual -- characters. -- Returns true if the line is a ruler of (char) characters. -- The line must contain at least three char characters and contain only spaces and -- char characters. function is_ruler_of(line, char) if not line:match("^[ %" .. char .. "]*$") then return false end if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end return true end -- Identifies the block level formatting present in the line function classify(line) local info = {line = line, text = line} if line:match("^ ") then info.type = "indented" info.outdented = line:sub(5) return info end for _,c in ipairs({'*', '-', '_', '='}) do if is_ruler_of(line, c) then info.type = "ruler" info.ruler_char = c return info end end if line == "" then info.type = "blank" return info end if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") info.type = "header" info.level = m1:len() info.text = m2 return info end if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") info.type = "list_item" info.list_type = "numeric" info.number = 0 + number info.text = text return info end if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") info.type = "list_item" info.list_type = "bullet" info.bullet = bullet info.text= text return info end if line:match("^>[ \t]?(.*)") then info.type = "blockquote" info.text = line:match("^>[ \t]?(.*)") return info end if is_protected(line) then info.type = "raw" info.html = unprotect(line) return info end info.type = "normal" return info end -- Find headers constisting of a normal line followed by a ruler and converts them to -- header entries. function headers(array) local i = 1 while i <= #array - 1 do if array[i].type == "normal" and array[i+1].type == "ruler" and (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then local info = {line = array[i].line} info.text = info.line info.type = "header" info.level = iff(array[i+1].ruler_char == "=", 1, 2) table.remove(array, i+1) array[i] = info end i = i + 1 end return array end -- Find list blocks and convert them to protected data blocks function lists(array, sublist) local function process_list(arr) local function any_blanks(arr) for i = 1, #arr do if arr[i].type == "blank" then return true end end return false end local function split_list_items(arr) local acc = {arr[1]} local res = {} for i=2,#arr do if arr[i].type == "list_item" then table.insert(res, acc) acc = {arr[i]} else table.insert(acc, arr[i]) end end table.insert(res, acc) return res end local function process_list_item(lines, block) while lines[#lines].type == "blank" do table.remove(lines) end local itemtext = lines[1].text for i=2,#lines do itemtext = itemtext .. "\n" .. outdent(lines[i].line) end if block then itemtext = block_transform(itemtext, true) if not itemtext:find("
") then itemtext = indent(itemtext) end
				return "    
  • " .. itemtext .. "
  • " else local lines = split(itemtext) lines = map(lines, classify) lines = lists(lines, true) lines = blocks_to_html(lines, true) itemtext = table.concat(lines, "\n") if not itemtext:find("
    ") then itemtext = indent(itemtext) end
    				return "    
  • " .. itemtext .. "
  • " end end local block_list = any_blanks(arr) local items = split_list_items(arr) local out = "" for _, item in ipairs(items) do out = out .. process_list_item(item, block_list) .. "\n" end if arr[1].list_type == "numeric" then return "
      \n" .. out .. "
    " else return "
      \n" .. out .. "
    " end end -- Finds the range of lines composing the first list in the array. A list -- starts with (^ list_item) or (blank list_item) and ends with -- (blank* $) or (blank normal). -- -- A sublist can start with just (list_item) does not need a blank... local function find_list(array, sublist) local function find_list_start(array, sublist) if array[1].type == "list_item" then return 1 end if sublist then for i = 1,#array do if array[i].type == "list_item" then return i end end else for i = 1, #array-1 do if array[i].type == "blank" and array[i+1].type == "list_item" then return i+1 end end end return nil end local function find_list_end(array, start) local pos = #array for i = start, #array-1 do if array[i].type == "blank" and array[i+1].type ~= "list_item" and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then pos = i-1 break end end while pos > start and array[pos].type == "blank" do pos = pos - 1 end return pos end local start = find_list_start(array, sublist) if not start then return nil end return start, find_list_end(array, start) end while true do local start, stop = find_list(array, sublist) if not start then break end local text = process_list(splice(array, start, stop)) local info = { line = text, type = "raw", html = text } array = splice(array, start, stop, {info}) end -- Convert any remaining list items to normal for _,line in ipairs(array) do if line.type == "list_item" then line.type = "normal" end end return array end -- Find and convert blockquote markers. function blockquotes(lines) local function find_blockquote(lines) local start for i,line in ipairs(lines) do if line.type == "blockquote" then start = i break end end if not start then return nil end local stop = #lines for i = start+1, #lines do if lines[i].type == "blank" or lines[i].type == "blockquote" then elseif lines[i].type == "normal" then if lines[i-1].type == "blank" then stop = i-1 break end else stop = i-1 break end end while lines[stop].type == "blank" do stop = stop - 1 end return start, stop end local function process_blockquote(lines) local raw = lines[1].text for i = 2,#lines do raw = raw .. "\n" .. lines[i].text end local bt = block_transform(raw) if not bt:find("
    ") then bt = indent(bt) end
    		return "
    \n " .. bt .. "\n
    " end while true do local start, stop = find_blockquote(lines) if not start then break end local text = process_blockquote(splice(lines, start, stop)) local info = { line = text, type = "raw", html = text } lines = splice(lines, start, stop, {info}) end return lines end -- Find and convert codeblocks. function codeblocks(lines) local function find_codeblock(lines) local start for i,line in ipairs(lines) do if line.type == "indented" then start = i break end end if not start then return nil end local stop = #lines for i = start+1, #lines do if lines[i].type ~= "indented" and lines[i].type ~= "blank" then stop = i-1 break end end while lines[stop].type == "blank" do stop = stop - 1 end return start, stop end local function process_codeblock(lines) local raw = detab(encode_code(outdent(lines[1].line))) for i = 2,#lines do raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) end return "
    " .. raw .. "\n
    " end while true do local start, stop = find_codeblock(lines) if not start then break end local text = process_codeblock(splice(lines, start, stop)) local info = { line = text, type = "raw", html = text } lines = splice(lines, start, stop, {info}) end return lines end -- Convert lines to html code function blocks_to_html(lines, no_paragraphs) local out = {} local i = 1 while i <= #lines do local line = lines[i] if line.type == "ruler" then table.insert(out, "
    ") elseif line.type == "raw" then table.insert(out, line.html) elseif line.type == "normal" then local s = line.line while i+1 <= #lines and lines[i+1].type == "normal" do i = i + 1 s = s .. "\n" .. lines[i].line end if no_paragraphs then table.insert(out, span_transform(s)) else table.insert(out, "

    " .. span_transform(s) .. "

    ") end elseif line.type == "header" then local s = "" .. span_transform(line.text) .. "" table.insert(out, s) else table.insert(out, line.line) end i = i + 1 end return out end -- Perform all the block level transforms function block_transform(text, sublist) local lines = split(text) lines = map(lines, classify) lines = headers(lines) lines = lists(lines, sublist) lines = codeblocks(lines) lines = blockquotes(lines) lines = blocks_to_html(lines) local text = table.concat(lines, "\n") return text end -- Debug function for printing a line array to see the result -- of partial transforms. function print_lines(lines) for i, line in ipairs(lines) do print(i, line.type, line.text or line.line) end end ---------------------------------------------------------------------- -- Span transform ---------------------------------------------------------------------- -- Functions for transforming the text at the span level. -- These characters may need to be escaped because they have a special -- meaning in markdown. escape_chars = "'\\`*_{}[]()>#+-.!'" escape_table = {} function init_escape_table() escape_table = {} for i = 1,#escape_chars do local c = escape_chars:sub(i,i) escape_table[c] = hash(c) end end -- Adds a new escape to the escape table. function add_escape(text) if not escape_table[text] then escape_table[text] = hash(text) end return escape_table[text] end -- Escape characters that should not be disturbed by markdown. function escape_special_chars(text) local tokens = tokenize_html(text) local out = "" for _, token in ipairs(tokens) do local t = token.text if token.type == "tag" then -- In tags, encode * and _ so they don't conflict with their use in markdown. t = t:gsub("%*", escape_table["*"]) t = t:gsub("%_", escape_table["_"]) else t = encode_backslash_escapes(t) end out = out .. t end return out end -- Encode backspace-escaped characters in the markdown source. function encode_backslash_escapes(t) for i=1,escape_chars:len() do local c = escape_chars:sub(i,i) t = t:gsub("\\%" .. c, escape_table[c]) end return t end -- Unescape characters that have been encoded. function unescape_special_chars(t) local tin = t for k,v in pairs(escape_table) do k = k:gsub("%%", "%%%%") t = t:gsub(v,k) end if t ~= tin then t = unescape_special_chars(t) end return t end -- Encode/escape certain characters inside Markdown code runs. -- The point is that in code, these characters are literals, -- and lose their special Markdown meanings. function encode_code(s) s = s:gsub("%&", "&") s = s:gsub("<", "<") s = s:gsub(">", ">") for k,v in pairs(escape_table) do s = s:gsub("%"..k, v) end return s end -- Handle backtick blocks. function code_spans(s) s = s:gsub("\\\\", escape_table["\\"]) s = s:gsub("\\`", escape_table["`"]) local pos = 1 while true do local start, stop = s:find("`+", pos) if not start then return s end local count = stop - start + 1 -- Find a matching numbert of backticks local estart, estop = s:find(string.rep("`", count), stop+1) local brstart = s:find("\n", stop+1) if estart and (not brstart or estart < brstart) then local code = s:sub(stop+1, estart-1) code = code:gsub("^[ \t]+", "") code = code:gsub("[ \t]+$", "") code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) code = "" .. encode_code(code) .. "" code = add_escape(code) s = s:sub(1, start-1) .. code .. s:sub(estop+1) pos = start + code:len() else pos = stop + 1 end end return s end -- Encode alt text... enodes &, and ". function encode_alt(s) if not s then return s end s = s:gsub('&', '&') s = s:gsub('"', '"') s = s:gsub('<', '<') return s end -- Handle image references function images(text) local function reference_link(alt, id) alt = encode_alt(alt:match("%b[]"):sub(2,-2)) id = id:match("%[(.*)%]"):lower() if id == "" then id = text:lower() end link_database[id] = link_database[id] or {} if not link_database[id].url then return nil end local url = link_database[id].url or id url = encode_alt(url) local title = encode_alt(link_database[id].title) if title then title = " title=\"" .. title .. "\"" else title = "" end return add_escape ('' .. alt .. '") end local function inline_link(alt, link) alt = encode_alt(alt:match("%b[]"):sub(2,-2)) local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") url = url or link:match("%(?%)") url = encode_alt(url) title = encode_alt(title) if title then return add_escape('' .. alt .. '') else return add_escape('' .. alt .. '') end end text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) text = text:gsub("!(%b[])(%b())", inline_link) return text end -- Handle anchor references function anchors(text) local function reference_link(text, id) text = text:match("%b[]"):sub(2,-2) id = id:match("%b[]"):sub(2,-2):lower() if id == "" then id = text:lower() end link_database[id] = link_database[id] or {} if not link_database[id].url then return nil end local url = link_database[id].url or id url = encode_alt(url) local title = encode_alt(link_database[id].title) if title then title = " title=\"" .. title .. "\"" else title = "" end return add_escape("") .. text .. add_escape("") end local function inline_link(text, link) text = text:match("%b[]"):sub(2,-2) local url, title = link:match("%(?[ \t]*['\"](.+)['\"]") title = encode_alt(title) url = url or link:match("%(?%)") or "" url = encode_alt(url) if title then return add_escape("") .. text .. "" else return add_escape("") .. text .. add_escape("") end end text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) text = text:gsub("(%b[])(%b())", inline_link) return text end -- Handle auto links, i.e. . function auto_links(text) local function link(s) return add_escape("") .. s .. "" end -- Encode chars as a mix of dec and hex entitites to (perhaps) fool -- spambots. local function encode_email_address(s) -- Use a deterministic encoding to make unit testing possible. -- Code 45% hex, 45% dec, 10% plain. local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} local plain = {code = function(c) return c end, count = 0, rate = 0.1} local codes = {hex, dec, plain} local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end local out = "" for i = 1,s:len() do for _,code in ipairs(codes) do code.count = code.count + code.rate end if codes[1].count < codes[2].count then swap(codes,1,2) end if codes[2].count < codes[3].count then swap(codes,2,3) end if codes[1].count < codes[2].count then swap(codes,1,2) end local code = codes[1] local c = s:sub(i,i) -- Force encoding of "@" to make email address more invisible. if c == "@" and code == plain then code = codes[2] end out = out .. code.code(c) code.count = code.count - 1 end return out end local function mail(s) s = unescape_special_chars(s) local address = encode_email_address("mailto:" .. s) local text = encode_email_address(s) return add_escape("") .. text .. "" end -- links text = text:gsub("<(https?:[^'\">%s]+)>", link) text = text:gsub("<(ftp:[^'\">%s]+)>", link) -- mail text = text:gsub("%s]+)>", mail) text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) return text end -- Encode free standing amps (&) and angles (<)... note that this does not -- encode free >. function amps_and_angles(s) -- encode amps not part of &..; expression local pos = 1 while true do local amp = s:find("&", pos) if not amp then break end local semi = s:find(";", amp+1) local stop = s:find("[ \t\n&]", amp+1) if not semi or (stop and stop < semi) or (semi - amp) > 15 then s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) pos = amp+1 else pos = amp+1 end end -- encode naked <'s s = s:gsub("<([^a-zA-Z/?$!])", "<%1") s = s:gsub("<$", "<") -- what about >, nothing done in the original markdown source to handle them return s end -- Handles emphasis markers (* and _) in the text. function emphasis(text) for _, s in ipairs {"%*%*", "%_%_"} do text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "%1") text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "%1") end for _, s in ipairs {"%*", "%_"} do text = text:gsub(s .. "([^%s_])" .. s, "%1") text = text:gsub(s .. "([^%s_])" .. s, "%1") text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "%1") text = text:gsub(s .. "([^<>_]-[^<>_]-[^<>_]-)" .. s, "%1") end return text end -- Handles line break markers in the text. function line_breaks(text) return text:gsub(" +\n", "
    \n") end -- Perform all span level transforms. function span_transform(text) text = code_spans(text) text = escape_special_chars(text) text = images(text) text = anchors(text) text = auto_links(text) text = amps_and_angles(text) text = emphasis(text) text = line_breaks(text) return text end ---------------------------------------------------------------------- -- Markdown ---------------------------------------------------------------------- -- Cleanup the text by normalizing some possible variations to make further -- processing easier. function cleanup(text) -- Standardize line endings text = text:gsub("\r\n", "\n") -- DOS to UNIX text = text:gsub("\r", "\n") -- Mac to UNIX -- Convert all tabs to spaces text = detab(text) -- Strip lines with only spaces and tabs while true do local subs text, subs = text:gsub("\n[ \t]+\n", "\n\n") if subs == 0 then break end end return "\n" .. text .. "\n" end -- Strips link definitions from the text and stores the data in a lookup table. function strip_link_definitions(text) local linkdb = {} local function link_def(id, url, title) id = id:match("%[(.+)%]"):lower() linkdb[id] = linkdb[id] or {} linkdb[id].url = url or linkdb[id].url linkdb[id].title = title or linkdb[id].title return "" end local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*]+)>?[ \t]*" local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" text = text:gsub(def_title1, link_def) text = text:gsub(def_title2, link_def) text = text:gsub(def_title3, link_def) text = text:gsub(def_no_title, link_def) return text, linkdb end link_database = {} -- Main markdown processing function function markdown(text) init_hash(text) init_escape_table() text = cleanup(text) text = protect(text) text, link_database = strip_link_definitions(text) text = block_transform(text) text = unescape_special_chars(text) return text end ---------------------------------------------------------------------- -- End of module ---------------------------------------------------------------------- setfenv(1, _G) M.lock(M) -- Expose markdown function to the world markdown = M.markdown -- Class for parsing command-line options local OptionParser = {} OptionParser.__index = OptionParser -- Creates a new option parser function OptionParser:new() local o = {short = {}, long = {}} setmetatable(o, self) return o end -- Calls f() whenever a flag with specified short and long name is encountered function OptionParser:flag(short, long, f) local info = {type = "flag", f = f} if short then self.short[short] = info end if long then self.long[long] = info end end -- Calls f(param) whenever a parameter flag with specified short and long name is encountered function OptionParser:param(short, long, f) local info = {type = "param", f = f} if short then self.short[short] = info end if long then self.long[long] = info end end -- Calls f(v) for each non-flag argument function OptionParser:arg(f) self.arg = f end -- Runs the option parser for the specified set of arguments. Returns true if all arguments -- where successfully parsed and false otherwise. function OptionParser:run(args) local pos = 1 while pos <= #args do local arg = args[pos] if arg == "--" then for i=pos+1,#args do if self.arg then self.arg(args[i]) end return true end end if arg:match("^%-%-") then local info = self.long[arg:sub(3)] if not info then print("Unknown flag: " .. arg) return false end if info.type == "flag" then info.f() pos = pos + 1 else param = args[pos+1] if not param then print("No parameter for flag: " .. arg) return false end info.f(param) pos = pos+2 end elseif arg:match("^%-") then for i=2,arg:len() do local c = arg:sub(i,i) local info = self.short[c] if not info then print("Unknown flag: -" .. c) return false end if info.type == "flag" then info.f() else if i == arg:len() then param = args[pos+1] if not param then print("No parameter for flag: -" .. c) return false end info.f(param) pos = pos + 1 else param = arg:sub(i+1) info.f(param) end break end end pos = pos + 1 else if self.arg then self.arg(arg) end pos = pos + 1 end end return true end -- Handles the case when markdown is run from the command line local function run_command_line(arg) -- Generate output for input s given options local function run(s, options) s = markdown(s) if not options.wrap_header then return s end local header = "" if options.header then local f = io.open(options.header) or error("Could not open file: " .. options.header) header = f:read("*a") f:close() else header = [[ TITLE ]] local title = options.title or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or s:match("

    (.-)

    ") or "Untitled" header = header:gsub("TITLE", title) if options.inline_style then local style = "" local f = io.open(options.stylesheet) if f then style = f:read("*a") f:close() else error("Could not include style sheet " .. options.stylesheet .. ": File not found") end header = header:gsub('', "") else header = header:gsub("STYLESHEET", options.stylesheet) end header = header:gsub("CHARSET", options.charset) end local footer = "" if options.footer then local f = io.open(options.footer) or error("Could not open file: " .. options.footer) footer = f:read("*a") f:close() end return header .. s .. footer end -- Generate output path name from input path name given options. local function outpath(path, options) if options.append then return path .. ".html" end local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end return path .. ".html" end -- Default commandline options local options = { wrap_header = true, header = nil, footer = nil, charset = "utf-8", title = nil, stylesheet = "default.css", inline_style = false } local help = [[ Usage: markdown.lua [OPTION] [FILE] Runs the markdown text markup to HTML converter on each file specified on the command line. If no files are specified, runs on standard input. No header: -n, --no-wrap Don't wrap the output in ... tags. Custom header: -e, --header FILE Use content of FILE for header. -f, --footer FILE Use content of FILE for footer. Generated header: -c, --charset SET Specifies charset (default utf-8). -i, --title TITLE Specifies title (default from first

    tag). -s, --style STYLE Specifies style sheet file (default default.css). -l, --inline-style Include the style sheet file inline in the header. Generated files: -a, --append Append .html extension (instead of replacing). Other options: -h, --help Print this help text. -t, --test Run the unit tests. ]] local run_stdin = true local op = OptionParser:new() op:flag("n", "no-wrap", function () options.wrap_header = false end) op:param("e", "header", function (x) options.header = x end) op:param("f", "footer", function (x) options.footer = x end) op:param("c", "charset", function (x) options.charset = x end) op:param("i", "title", function(x) options.title = x end) op:param("s", "style", function(x) options.stylesheet = x end) op:flag("l", "inline-style", function(x) options.inline_style = true end) op:flag("a", "append", function() options.append = true end) op:flag("t", "test", function() local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") local f = io.open(n) if f then f:close() dofile(n) else error("Cannot find markdown-tests.lua") end run_stdin = false end) op:flag("h", "help", function() print(help) run_stdin = false end) op:arg(function(path) local file = io.open(path) or error("Could not open file: " .. path) local s = file:read("*a") file:close() s = run(s, options) file = io.open(outpath(path, options), "w") or error("Could not open output file: " .. outpath(path, options)) file:write(s) file:close() run_stdin = false end ) if not op:run(arg) then print(help) run_stdin = false end if run_stdin then local s = io.read("*a") s = run(s, options) io.write(s) end end -- If we are being run from the command-line, act accordingly if arg and arg[0]:find("markdown%.lua$") then run_command_line(arg) else return markdown end luakit-2012.09.13-r1/lib/noscript.lua000066400000000000000000000073731202446317700170540ustar00rootroot00000000000000-------------------------------------------------------- -- NoScript plugin for luakit -- -- (C) 2011 Mason Larobina -- -------------------------------------------------------- -- Get Lua environment local os = require "os" local tonumber = tonumber local assert = assert local table = table local string = string -- Get luakit environment local webview = webview local add_binds = add_binds local lousy = require "lousy" local sql_escape = lousy.util.sql_escape local capi = { luakit = luakit, sqlite3 = sqlite3 } module "noscript" -- Default blocking values enable_scripts = true enable_plugins = true create_table = [[ CREATE TABLE IF NOT EXISTS by_domain ( id INTEGER PRIMARY KEY, domain TEXT, enable_scripts INTEGER, enable_plugins INTEGER );]] db = capi.sqlite3{ filename = capi.luakit.data_dir .. "/noscript.db" } db:exec("PRAGMA synchronous = OFF; PRAGMA secure_delete = 1;") db:exec(create_table) local function btoi(bool) return bool and 1 or 0 end local function itob(int) return tonumber(int) ~= 0 end local function get_domain(uri) uri = assert(lousy.uri.parse(uri), "invalid uri") return string.lower(uri.host) end local function match_domain(domain) local rows = db:exec(string.format("SELECT * FROM by_domain " .. "WHERE domain == %s;", sql_escape(domain))) if rows[1] then return rows[1] end end local function update(id, field, value) db:exec(string.format("UPDATE by_domain SET %s = %d WHERE id == %d;", field, btoi(value), id)) end local function insert(domain, enable_scripts, enable_plugins) db:exec(string.format("INSERT INTO by_domain VALUES (NULL, %s, %d, %d);", sql_escape(domain), btoi(enable_scripts), btoi(enable_plugins))) end function webview.methods.toggle_scripts(view, w) local domain = get_domain(view.uri) local enable_scripts = _M.enable_scripts local row = match_domain(domain) if row then enable_scripts = itob(row.enable_scripts) update(row.id, "enable_scripts", not enable_scripts) else insert(domain, not enable_scripts, _M.enable_plugins) end w:notify(string.format("%sabled scripts for domain: %s", enable_scripts and "Dis" or "En", domain)) end function webview.methods.toggle_plugins(view, w) local domain = get_domain(view.uri) local enable_plugins = _M.enable_plugins local row = match_domain(domain) if row then enable_plugins = itob(row.enable_plugins) update(row.id, "enable_plugins", not enable_plugins) else insert(domain, _M.enable_scripts, not enable_plugins) end w:notify(string.format("%sabled plugins for domain: %s", enable_plugins and "Dis" or "En", domain)) end function webview.methods.toggle_remove(view, w) local domain = get_domain(view.uri) db:exec(string.format("DELETE FROM by_domain WHERE domain == %s;", sql_escape(domain))) w:notify("Removed rules for domain: " .. domain) end webview.init_funcs.noscript_load = function (view) view:add_signal("load-status", function (v, status) if status ~= "committed" or v.uri == "about:blank" then return end local enable_scripts, enable_plugins = _M.enable_scripts, _M.enable_plugins local domain = get_domain(v.uri) local row = match_domain(domain) if row then enable_scripts = itob(row.enable_scripts) enable_plugins = itob(row.enable_plugins) end view.enable_scripts = enable_scripts view.enable_plugins = enable_plugins end) end local buf = lousy.bind.buf add_binds("normal", { buf("^,ts$", function (w) w:toggle_scripts() end), buf("^,tp$", function (w) w:toggle_plugins() end), buf("^,tr$", function (w) w:toggle_remove() end), }) luakit-2012.09.13-r1/lib/proxy.lua000066400000000000000000000167551202446317700164000ustar00rootroot00000000000000------------------------------------------------------------ -- Dynamic proxy settings -- -- Copyright © Piotr Husiatyński -- ------------------------------------------------------------ -- Grab environment we need local io = io local os = os local pairs = pairs local ipairs = ipairs local error = error local string = string local lousy = require "lousy" local theme = theme local unpack = unpack local table = table local capi = { luakit = luakit, soup = soup } local webview = webview local widget = widget local window = window -- Check for mode/bind functions local add_binds, add_cmds = add_binds, add_cmds local new_mode, menu_binds = new_mode, menu_binds module("proxy") --- Module global variables local proxies_file = capi.luakit.data_dir .. '/proxymenu' local proxies = {} local noproxy = { address = '' } local active = noproxy -- Return ordered list of proxy names function get_names() return lousy.util.table.keys(proxies) end -- Return address of proxy given by name function get(name) return proxies[name] end --- Get active proxy configuration: { name = "name", address = "address" } function get_active() return active end --- Load proxies list from file -- @param fd_name custom proxy storage of nil to use default function load(fd_name) local fd_name = fd_name or proxies_file if not os.exists(fd_name) then return end local strip = lousy.util.string.strip for line in io.lines(fd_name) do local status, name, address = string.match(line, "^(.)%s(.+)%s(.+)$") if address then name, address = strip(name), strip(address) if status == '*' then active = { address = address, name = name } end proxies[name] = address end end end --- Save proxies list to file -- @param fd_name custom proxy storage of nil to use default function save(fd_name) local fd = io.open(fd_name or proxies_file, "w") for name, address in pairs(proxies) do if address ~= "" then local status = (active.name == name and '*') or ' ' fd:write(string.format("%s %s %s\n", status, name, address)) end end io.close(fd) end --- Add new proxy server to current list -- @param name proxy configuration name -- @param address proxy server address -- @param save_file do not save configuration if false function set(name, address, save_file) local name = lousy.util.string.strip(name) if not string.match(name, "^([%w%p]+)$") then error("Invalid proxy name: " .. name) end proxies[name] = lousy.util.string.strip(address) if save_file ~= false then save() end end --- Delete selected proxy from list -- @param name proxy server name function del(name) local name = lousy.util.string.strip(name) if proxies[name] then -- if deleted proxy was the active one, turn proxy off if name == active.name then active = noproxy end proxies[name] = nil save() end end --- Set given proxy to active. Return true on success, else false -- @param name proxy configuration name or nil to unset proxy. function set_active(name) if name then local name = lousy.util.string.strip(name) if not proxies[name] then error("Unknown proxy: " .. name) end active = { name = name, address = proxies[name] } else active = noproxy end save() return true end -- Load the initial proxy address webview.init_funcs.set_proxy = function (view, w) local active = get_active() if active and active.address ~= '' then capi.soup.proxy_uri = active.address end -- The proxy property is set globablly so this function only needs to be -- called once. Other proxy changes take place from the interactive -- `:proxy` menu. webview.init_funcs.set_proxy = nil end -- Create a proxy indicator widget and add it to the status bar window.init_funcs.build_proxy_indicator = function (w) local r = w.sbar.r r.proxyi = widget{type="label"} r.layout:pack(r.proxyi) r.layout:reorder(r.proxyi, 2) r.proxyi.fg = theme.proxyi_sbar_fg r.proxyi.font = theme.proxyi_sbar_font w:update_proxy_indicator() end -- Helper function to update text in proxy indicator window.methods.update_proxy_indicator = function (w) local name = get_active().name local proxyi = w.sbar.r.proxyi if name then local text = string.format("[%s]", name) if proxyi.text ~= text then proxyi.text = text end proxyi:show() else proxyi:hide() end end -- Update proxy indicator in status bar on change of address webview.init_funcs.proxy_indicator_update = function (view, w) view:add_signal("property::proxy-uri", function (v) w:update_proxy_indicator() end) end new_mode("proxymenu", { enter = function (w) local afg, ifg = theme.proxy_active_menu_fg, theme.proxy_inactive_menu_fg local abg, ibg = theme.proxy_active_menu_bg, theme.proxy_inactive_menu_bg local a = get_active() local rows = {{ "Proxy Name", " Server address", title = true }, {" None", "", address = '', fg = (a.address == '' and afg) or ifg, bg = (a.address == '' and abg) or ibg},} for _, name in ipairs(get_names()) do local address = get(name) table.insert(rows, { " " .. name, " " .. address, name = name, address = lousy.util.escape(address), fg = (a.name == name and afg) or ifg, bg = (a.name == name and abg) or ibg, }) end w.menu:build(rows) w:notify("Use j/k to move, d delete, e edit, a add, Return activate.", false) end, leave = function (w) w.menu:hide() end, }) local cmd = lousy.bind.cmd add_cmds({ cmd("proxy", "add proxy server", function (w, a) local params = lousy.util.string.split(a or '') if not a then w:set_mode("proxymenu") elseif #params == 2 then local name, address = unpack(params) set(name, address) else w:error("Bad usage. Correct format :proxy
    ") end end), }) local key = lousy.bind.key add_binds("proxymenu", lousy.util.table.join({ -- Select proxy key({}, "Return", function (w) local row = w.menu:get() if row and row.address then set_active(row.name) w:set_mode() capi.soup.proxy_uri = row.address if row.name then w:notify(string.format("Using proxy: %s (%s)", row.name, row.address)) else w:notify("Unset proxy.") end end end), -- Delete proxy key({}, "d", function (w) local row = w.menu:get() if row and row.name then del(row.name) w.menu:del() end end), -- Edit proxy key({}, "e", function (w) local row = w.menu:get() if row and row.name then w:enter_cmd(string.format(":proxy %s %s", row.name, row.address)) end end), -- New proxy key({}, "a", function (w) w:enter_cmd(":proxy ") end), -- Exit menu key({}, "q", function (w) w:set_mode() end), }, menu_binds)) -- Initialize module load() -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/quickmarks.lua000066400000000000000000000212701202446317700173550ustar00rootroot00000000000000---------------------------------------------------------------- -- Vimperator style quickmarking -- -- @author Piotr Husiatyński <phusiatynski@gmail.com> -- -- @author Mason Larobina <mason.larobina@gmail.com> -- ---------------------------------------------------------------- -- Get lua environment local os = os local io = io local assert = assert local string = string local ipairs = ipairs local pairs = pairs local tostring = tostring local type = type local table = table -- Get luakit environment local lousy = require "lousy" local window = window local add_binds, add_cmds = add_binds, add_cmds local new_mode, menu_binds = new_mode, menu_binds local capi = { luakit = luakit } module("quickmarks") local qmarks local quickmarks_file = capi.luakit.data_dir .. '/quickmarks' local function check_token(token) assert(string.match(tostring(token), "^(%w)$"), "invalid token: " .. tostring(token)) return token end --- Load quick bookmarks from storage file into memory -- @param fd_name bookmarks storage file path of nil to use default one function load(fd_name) if not qmarks then qmarks = {} end local fd_name = fd_name or quickmarks_file if not os.exists(fd_name) then return end for line in io.lines(fd_name) do local token, uris = string.match(lousy.util.string.strip(line), "^(%w)%s+(.+)$") if token then qmarks [token] = lousy.util.string.split(uris, ",%s+") end end end --- Save quick bookmarks to file -- @param fd_name bookmarks storage file path of nil to use default one function save(fd_name) -- Quickmarks init check if not qmarks then load() end local fd = io.open(fd_name or quickmarks_file, "w") for _, token in ipairs(lousy.util.table.keys(qmarks )) do local uris = table.concat(qmarks [token], ", ") fd:write(string.format("%s %s\n", token, uris)) end io.close(fd) end --- Return url related to given key or nil if does not exist -- @param token quick bookmarks mapping token -- @param load_file Call quickmark.load() before get function get(token, load_file) -- Load quickmarks from other sessions if not qmarks or load_file ~= false then load() end return qmarks[check_token(token)] end --- Return a list of all the tokens in the quickmarks table function get_tokens() if not qmarks then load() end return lousy.util.table.keys(qmarks ) end --- Set new quick bookmarks mapping -- @param token The token under which given uris will be available -- @param uris List of locations to quickmark -- @param load_file Call quickmark.load() before set -- @param save_file Call quickmark.save() after set function set(token, uris, load_file, save_file) -- Load quickmarks from other sessions if not qmarks or load_file ~= false then load() end -- Parse uris: "http://forum1.com, google.com, imdb some artist" if uris and type(uris) == "string" then uris = lousy.util.string.split(uris, ",%s+") elseif uris and type(uris) ~= "table" then error("invalid locations type: ", type(uris)) end qmarks[check_token(token)] = uris -- By default, setting new quickmark saves them to if save_file ~= false then save() end end --- Delete a quickmark -- @param token The quickmark token -- @param load_file Call quickmark.load() before deletion -- @param save_file Call quickmark.save() after deletion function del(token, load_file, save_file) -- Load quickmarks from other sessions if not qmarks or load_file ~= false then load() end qmarks[check_token(token)] = nil if save_file ~= false then save() end end --- Delete all quickmarks -- @param save_file Call quickmark.save() function. function delall(save_file) qmarks = {} if save_file ~= false then save() end end -- Add quickmarking binds to normal mode local buf = lousy.bind.buf add_binds("normal", { buf("^g[onw][a-zA-Z0-9]$", [[Jump to quickmark in current tab with `go{a-zA-Z0-9}`, `gn{a-zA-Z0-9}` to open in new tab and or `gw{a-zA-Z0-9}` to open a quickmark in a new window.]], function (w, b, m) local mode, token = string.match(b, "^g(.)(.)$") local uris = lousy.util.table.clone(get(token) or {}) for i, uri in ipairs(uris) do uris[i] = w:search_open(uri) end for c=1,m.count do if mode == "w" then window.new(uris) else for i, uri in ipairs(uris or {}) do if mode == "o" and c == 1 and i == 1 then w:navigate(uri) else w:new_tab(uri, i == 1) end end end end end, {count=1}), buf("^M[a-zA-Z0-9]$", [[Add quickmark for current URL.]], function (w, b) local token = string.match(b, "^M(.)$") local uri = w.view.uri set(token, {uri}) w:notify(string.format("Quickmarked %q: %s", token, uri)) end), }) -- Add quickmarking commands local cmd = lousy.bind.cmd add_cmds({ -- Quickmark add (`:qmark f http://forum1.com, forum2.com, imdb some artist`) cmd("qma[rk]", "Add a quickmark.", function (w, a) local token, uris = string.match(lousy.util.string.strip(a), "^(%w)%s+(.+)$") assert(token, "invalid token") uris = lousy.util.string.split(uris, ",%s+") set(token, uris) w:notify(string.format("Quickmarked %q: %s", token, table.concat(uris, ", "))) end), -- Quickmark edit (`:qmarkedit f` -> `:qmark f furi1, furi2, ..`) cmd({"qmarkedit", "qme"}, "Edit a quickmark.", function (w, a) token = lousy.util.string.strip(a) assert(#token == 1, "invalid token length: " .. token) local uris = get(token) w:enter_cmd(string.format(":qmark %s %s", token, table.concat(uris or {}, ", "))) end), -- Quickmark del (`:delqmarks b-p Aa z 4-9`) cmd("delqm[arks]", "Delete a quickmark.", function (w, a) -- Find and del all range specifiers string.gsub(a, "(%w%-%w)", function (range) range = "["..range.."]" for _, token in ipairs(get_tokens()) do if string.match(token, range) then del(token, false) end end end) -- Delete lone tokens string.gsub(a, "(%w)", function (token) del(token, false) end) save() end), -- View all quickmarks in an interactive menu cmd("qmarks", "List all quickmarks.", function (w) w:set_mode("qmarklist") end), -- Delete all quickmarks cmd({"delqmarks!", "delqm!"}, "Delete all quickmarks.", function (w) delall() end), }) -- Add mode to display all quickmarks in an interactive menu new_mode("qmarklist", { enter = function (w) local rows = {{ "Quickmarks", " URI(s)", title = true }} for _, qmark in ipairs(get_tokens()) do local uris = lousy.util.escape(table.concat(get(qmark, false), ", ")) table.insert(rows, { " " .. qmark, " " .. uris, qmark = qmark }) end w.menu:build(rows) w:notify("Use j/k to move, d delete, e edit, t tabopen, w winopen.", false) end, leave = function (w) w.menu:hide() end, }) -- Add additional binds to quickmarks menu mode local key = lousy.bind.key add_binds("qmarklist", lousy.util.table.join({ -- Delete quickmark key({}, "d", function (w) local row = w.menu:get() if row and row.qmark then del(row.qmark) w.menu:del() end end), -- Edit quickmark key({}, "e", function (w) local row = w.menu:get() if row and row.qmark then local uris = get(row.qmark) w:enter_cmd(string.format(":qmark %s %s", row.qmark, table.concat(uris or {}, ", "))) end end), -- Open quickmark key({}, "Return", function (w) local row = w.menu:get() if row and row.qmark then for i, uri in ipairs(get(row.qmark) or {}) do uri = w:search_open(uri) if i == 1 then w:navigate(uri) else w:new_tab(uri, false) end end end end), -- Open quickmark in new tab key({}, "t", function (w) local row = w.menu:get() if row and row.qmark then for _, uri in ipairs(get(row.qmark) or {}) do w:new_tab(w:search_open(uri), false) end end end), -- Open quickmark in new window key({}, "w", function (w) local row = w.menu:get() w:set_mode() if row and row.qmark then window.new(get(row.qmark) or {}) end end), -- Exit menu key({}, "q", function (w) w:set_mode() end), }, menu_binds)) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/search.lua000066400000000000000000000132051202446317700164470ustar00rootroot00000000000000------------------------------------------------------ -- Search for a string in the current webview -- -- © 2010 Mason Larobina -- ------------------------------------------------------ -- Add searching binds to normal mode local key = lousy.bind.key add_binds("normal", { key({}, "/", "Search for string on current page.", function (w) w:start_search("/") end), key({}, "?", "Reverse search for string on current page.", function (w) w:start_search("?") end), key({}, "n", "Find next search result.", function (w, m) for i=1,m.count do w:search(nil, true) end if w.search_state.ret == false then w:error("Pattern not found: " .. w.search_state.last_search) elseif w.search_state.wrapped then if w.search_state.forward then w:warning("Search hit BOTTOM, continuing at TOP") else w:warning("Search hit TOP, continuing at BOTTOM") end end end, {count=1}), key({}, "N", "Find previous search result.", function (w, m) for i=1,m.count do w:search(nil, false) end if w.search_state.ret == false then w:error("Pattern not found: " .. w.search_state.last_search) elseif w.search_state.wrapped then if w.search_state.forward then w:warning("Search hit TOP, continuing at BOTTOM") else w:warning("Search hit BOTTOM, continuing at TOP") end end end, {count=1}), }) -- Setup search mode new_mode("search", { enter = function (w) -- Clear old search state w.search_state = {} w:set_prompt() w:set_input("/") end, leave = function (w) w.ibar.input.fg = theme.ibar_fg w.ibar.input.bg = theme.ibar_bg -- Check if search was aborted and return to original position local s = w.search_state if s.marker then w:scroll(s.marker) s.marker = nil end end, changed = function (w, text) -- Check that the first character is '/' or '?' and update search if string.match(text, "^[?/]") then s = w.search_state s.last_search = string.sub(text, 2) if #text > 3 then w:search(string.sub(text, 2), (string.sub(text, 1, 1) == "/")) if s.ret == false then if s.marker then w:scroll(s.marker) end w.ibar.input.fg = theme.ibar_error_fg w.ibar.input.bg = theme.ibar_error_bg else w.ibar.input.fg = theme.ibar_fg w.ibar.input.bg = theme.ibar_bg end else w:clear_search(false) end else w:clear_search() w:set_mode() end end, activate = function (w, text) w.search_state.marker = nil -- Search if haven't already (won't have for short strings) if not w.search_state.searched then w:search(string.sub(text, 2), (string.sub(text, 1, 1) == "/")) end -- Ghost the last search term if w.search_state.ret then w:set_mode() w:set_prompt(text) else w:error("Pattern not found: " .. string.sub(text, 2)) end end, history = {maxlen = 50}, }) -- Add binds to search mode add_binds("search", { key({"Control"}, "j", "Select next search result.", function (w) w:search(w.search_state.last_search, true) end), key({"Control"}, "k", "Select previous result.", function (w) w:search(w.search_state.last_search, false) end), }) -- Add search functions to webview for k, m in pairs({ start_search = function (view, w, text) if string.match(text, "^[?/]") then w:set_mode("search") if not string.match(text, "^/$") then w:set_input(text) end else return error("invalid search term, must start with '?' or '/'") end end, search = function (view, w, text, forward, wrap) if forward == nil then forward = true end -- Get search state (or new state) if not w.search_state then w.search_state = {} end local s = w.search_state -- Check if wrapping should be performed if wrap == nil then if s.wrap ~= nil then wrap = s.wrap else wrap = true end end -- Get search term text = text or s.last_search if not text or #text == 0 then return w:clear_search() end s.last_search = text if s.forward == nil then -- Haven't searched before, save some state. s.forward = forward s.wrap = wrap local scroll = view.scroll s.marker = { x = scroll.x, y = scroll.y } else -- Invert direction if originally searching in reverse forward = (s.forward == forward) end s.searched = true s.wrapped = false s.ret = view:search(text, text ~= string.lower(text), forward, s.wrapped); if not s.ret and wrap then s.wrapped = true s.ret = view:search(text, text ~= string.lower(text), forward, s.wrapped); end end, clear_search = function (view, w, clear_state) w.ibar.input.fg = theme.ibar_fg w.ibar.input.bg = theme.ibar_bg view:clear_search() if clear_state ~= false then w.search_state = {} else w.search_state.searched = false end end, }) do webview.methods[k] = m end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/session.lua000066400000000000000000000051251202446317700166670ustar00rootroot00000000000000------------------------------------------------------ -- Session saving / loading functions -- -- © 2010 Mason Larobina -- ------------------------------------------------------ local function rm(file) luakit.spawn(string.format("rm %q", file)) end -- Session functions session = { -- The file which we'll use for session info, $XDG_DATA_HOME/luakit/session file = luakit.data_dir .. "/session", -- Save all given windows uris to file. save = function (wins) local lines = {} -- Save tabs from all the given windows for wi, w in pairs(wins) do local current = w.tabs:current() for ti, tab in ipairs(w.tabs.children) do table.insert(lines, string.format("%d\t%d\t%s\t%s", wi, ti, tostring(current == ti), tab.uri)) end end if #lines > 0 then local fh = io.open(session.file, "w") fh:write(table.concat(lines, "\n")) io.close(fh) else rm(session.file) end end, -- Load window and tab state from file load = function (delete) if not os.exists(session.file) then return end local ret = {} -- Read file local lines = {} local fh = io.open(session.file, "r") for line in fh:lines() do table.insert(lines, line) end io.close(fh) -- Delete file if delete ~= false then rm(session.file) end -- Parse session file local split = lousy.util.string.split for _, line in ipairs(lines) do local wi, ti, current, uri = unpack(split(line, "\t")) wi = tonumber(wi) current = (current == "true") if not ret[wi] then ret[wi] = {} end table.insert(ret[wi], {uri = uri, current = current}) end return (#ret > 0 and ret) or nil end, -- Spawn windows from saved session and return the last window restore = function (delete) wins = session.load(delete) if not wins or #wins == 0 then return end -- Spawn windows local w for _, win in ipairs(wins) do w = nil for _, item in ipairs(win) do if not w then w = window.new({item.uri}) else w:new_tab(item.uri, item.current) end end end return w end, } -- Save current window session helper window.methods.save_session = function (w) session.save({w,}) end -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/tabhistory.lua000066400000000000000000000047011202446317700173730ustar00rootroot00000000000000-------------------------------------------------------- -- View and open history items in an interactive menu -- -- © 2010 Fabian Streitel -- -- © 2010 Mason Larobina -- -------------------------------------------------------- local util = require("lousy.util") local join = util.table.join -- View history items in an interactive menu. new_mode("tabhistory", { leave = function (w) w.menu:hide() end, enter = function (w) local h = w.view.history local rows = {{"Title", "URI", title = true},} for i, hi in ipairs(h.items) do local title, uri = util.escape(hi.title), util.escape(hi.uri) local marker = (i == h.index and "* " or " ") table.insert(rows, 2, { (marker..title), uri, index=i}) end w.menu:build(rows) w:notify("Use j/k to move, w winopen, t tabopen.", false) end, }) -- Add history menu binds. local key = lousy.bind.key add_binds("tabhistory", join({ -- Open history item in new tab. key({}, "t", function (w) local row = w.menu:get() if row and row.index then local v = w.view local uri = v.history.items[row.index].uri w:new_tab(uri, false) end end), -- Open history item in new window. key({}, "w", function (w) local row = w.menu:get() w:set_mode() if row and row.index then local v = w.view local uri = v.history.items[row.index].uri window.new({uri}) end end), -- Go to history item. key({}, "Return", function (w) local row = w.menu:get() w:set_mode() if row and row.index then local v = w.view local offset = row.index - v.history.index if offset < 0 then v:go_back(-offset) elseif offset > 0 then v:go_forward(offset) end end end), }, menu_binds)) -- Additional window methods. window.methods.tab_history = function (w) if #(w.view.history.items) < 2 then w:notify("No history items to display") else w:set_mode("tabhistory") end end -- Add `:history` command to view all history items for the current tab in an interactive menu. local cmd = lousy.bind.cmd add_cmds({ cmd("tabhistory", "list history for tab", window.methods.tab_history), }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/taborder.lua000066400000000000000000000054701202446317700170110ustar00rootroot00000000000000-------------------------------------------- -- Rules for where to put new tabs -- -- © 2010 Henrik Hallberg -- -------------------------------------------- -- When a new tab is opened in a window, a tab order function is called to -- determine where in the tab list it should be placed. window.new_tab() -- accepts a tab order function as parameter. If this is not sent, -- taborder.default is used if the new tab will be immediately switched to. -- Otherwise, i.e. if a background tab is opened, taborder.bgdefault is used. -- -- A tab order function receives the current window, and the view that is being -- opened as parameters. In return, it gives the index at which the new tab -- should be put. taborder = { first = function() return 1 end, last = function(w) return w.tabs:count() + 1 end, after_current = function (w) return w.tabs:current() + 1 end, before_current = function (w) return w.tabs:current() end, -- Put new child tab next to the parent after unbroken chain of descendants -- Logical way to use when one "queues" background-followed links by_origin = function(w, newview) local newindex = 0 local currentview = w.view if not currentview then return 1 end local kids = taborder.kidsof local views = w.tabs.children if kids[currentview] then -- Collect all descendants local desc = { currentview } local i = 1 repeat desc = lousy.util.table.join(desc, kids[desc[i]]) i = i + 1 until i > #desc -- Find the non-descendant closest after current. This is where -- the new tab should be put. for i = #views, 1, -1 do if not lousy.util.table.hasitem(desc, views[i]) then newindex = i end if views[i] == currentview then break end end -- There were no non-descendants after current. Put new tab last. if newindex == 0 then newindex = taborder.last(w, newview) end else kids[currentview] = {} newindex = taborder.after_current(w, newview) end table.insert(kids[currentview], newview) return newindex end, } -- Default: open regular tabs last taborder.default = taborder.last -- Default: open background tabs by origin taborder.default_bg = taborder.by_origin -- Weak table to remember which tab was spawned from which parent -- Note that family bonds are tied only if tabs are spawned within -- family rules, e.g. from by_origin. Tabs created elsewhere are orphans. taborder.kidsof = {} setmetatable(taborder.kidsof, { __mode = "k" }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/undoclose.lua000066400000000000000000000073741202446317700172070ustar00rootroot00000000000000------------------------------------------------------ -- View and undo closed tabs in an interactive menu -- -- © 2010 Chris van Dijk -- -- © 2010 Mason Larobina -- ------------------------------------------------------ -- Undo a closed tab (with complete tab history) window.methods.undo_close_tab = function (w, index) -- Convert negative indexes if index and index < 0 then index = #(w.closed_tabs) + index + 1 end local tab = table.remove(w.closed_tabs, index) if not tab then return end local view = w:new_tab(tab.hist) -- Attempt to open in last position if tab.after then local i = w.tabs:indexof(tab.after) w.tabs:reorder(view, (i and i+1) or -1) else w.tabs:reorder(view, 1) end end local key = lousy.bind.key add_binds("normal", { key({}, "u", "Undo closed tab (restoring tab history).", function (w, m) w:undo_close_tab(-m.count) end, {count=1}), }) -- View closed tabs in a list local escape = lousy.util.escape new_mode("undolist", { enter = function (w) local rows = {{ "Title", " URI", title = true }} for uid, tab in ipairs(w.closed_tabs) do tab.uid = uid local item = tab.hist.items[tab.hist.index] local title, uri = escape(item.title), escape(item.uri) table.insert(rows, 2, { " " .. title, " " .. uri, uid = uid }) end w.menu:build(rows) w:notify("Use j/k to move, d delete, u undo, w winopen.", false) end, leave = function (w) w.menu:hide() end, }) -- Add undolist menu binds add_binds("undolist", lousy.util.table.join({ -- Delete closed tab history key({}, "d", "Delete closed tab history item.", function (w) local row = w.menu:get() if row and row.uid then for i, tab in ipairs(w.closed_tabs) do if tab.uid == row.uid then table.remove(w.closed_tabs, i) break end end w.menu:del() end end), key({}, "u", "Undo closed tab in new background tab.", function (w) local row = w.menu:get() if row and row.uid then for i, tab in ipairs(w.closed_tabs) do if tab.uid == row.uid then w:new_tab(table.remove(w.closed_tabs, i).hist, false) break end end w.menu:del() end end), -- Undo closed tab in new window key({}, "w", "Undo closed tab in new window.", function (w) local row = w.menu:get() w:set_mode() if row and row.uid then for i, tab in ipairs(w.closed_tabs) do if tab.uid == row.uid then window.new({table.remove(w.closed_tabs, i).hist}) return end end end end), -- Undo closed tab in current tab key({}, "Return", "Undo closed tab in current tab.", function (w) local row = w.menu:get() w:set_mode() if row and row.uid then for i, tab in ipairs(w.closed_tabs) do if tab.uid == row.uid then w:undo_close_tab(i) end end end end), -- Exit menu key({}, "q", "Close menu.", function (w) w:set_mode() end), }, menu_binds)) -- Add `:undolist` command to view all closed tabs in an interactive menu local cmd = lousy.bind.cmd add_cmds({ cmd("undolist", "Undo closed tabs menu.", function (w, a) if #(w.closed_tabs) == 0 then w:notify("No closed tabs to display") else w:set_mode("undolist") end end), }) -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/userscripts.lua000066400000000000000000000273341202446317700176000ustar00rootroot00000000000000------------------------------------------------------- -- Userscript support for luakit -- -- © 2011 Constantin Schomburg -- -- © 2010 Fabian Streitel -- -- © 2010 Mason Larobina -- ------------------------------------------------------- -- Grab environment we need local io = io local ipairs = ipairs local os = os local pairs = pairs local setmetatable = setmetatable local string = string local table = table local warn = warn local webview = webview local bind = require("lousy.bind") local util = require("lousy.util") local lfs = require("lfs") local add_binds, add_cmds = add_binds, add_cmds local new_mode, menu_binds = new_mode, menu_binds local capi = { luakit = luakit } --- Evaluates and manages userscripts. -- JavaScript userscripts must end in .user.js module("userscripts") -- Pure JavaScript implementation of greasemonkey methods commonly used -- in chome/firefox userscripts. local gm_functions = [=[ // (C) 2009 Jim Tuttle (http://userscripts.org/users/79247) // Original source: http://userscripts.org/scripts/review/41441 if(typeof GM_getValue === "undefined") { GM_getValue = function(name){ var nameEQ = escape("_greasekit" + name) + "=", ca = document.cookie.split(';'); for (var i = 0, c; i < ca.length; i++) { var c = ca[i]; while (c.charAt(0) == ' ') c = c.substring(1, c.length); if (c.indexOf(nameEQ) == 0) { var value = unescape(c.substring(nameEQ.length, c.length)); //alert(name + ": " + value); return value; } } return null; } } if(typeof GM_setValue === "undefined") { GM_setValue = function( name, value, options ){ options = (options || {}); if ( options.expiresInOneYear ){ var today = new Date(); today.setFullYear(today.getFullYear()+1, today.getMonth, today.getDay()); options.expires = today; } var curCookie = escape("_greasekit" + name) + "=" + escape(value) + ((options.expires) ? "; expires=" + options.expires.toGMTString() : "") + ((options.path) ? "; path=" + options.path : "") + ((options.domain) ? "; domain=" + options.domain : "") + ((options.secure) ? "; secure" : ""); document.cookie = curCookie; } } if(typeof GM_xmlhttpRequest === "undefined") { GM_xmlhttpRequest = function(/* object */ details) { details.method = details.method.toUpperCase() || "GET"; if(!details.url) { throw("GM_xmlhttpRequest requires an URL."); return; } // build XMLHttpRequest object var oXhr, aAjaxes = []; if(typeof ActiveXObject !== "undefined") { var oCls = ActiveXObject; aAjaxes[aAjaxes.length] = {cls:oCls, arg:"Microsoft.XMLHTTP"}; aAjaxes[aAjaxes.length] = {cls:oCls, arg:"Msxml2.XMLHTTP"}; aAjaxes[aAjaxes.length] = {cls:oCls, arg:"Msxml2.XMLHTTP.3.0"}; } if(typeof XMLHttpRequest !== "undefined") aAjaxes[aAjaxes.length] = {cls:XMLHttpRequest, arg:undefined}; for(var i=aAjaxes.length; i--; ) try{ oXhr = new aAjaxes[i].cls(aAjaxes[i].arg); if(oXhr) break; } catch(e) {} // run it if(oXhr) { if("onreadystatechange" in details) oXhr.onreadystatechange = function() { details.onreadystatechange(oXhr) }; if("onload" in details) oXhr.onload = function() { details.onload(oXhr) }; if("onerror" in details) oXhr.onerror = function() { details.onerror(oXhr) }; oXhr.open(details.method, details.url, true); if("headers" in details) for(var header in details.headers) oXhr.setRequestHeader(header, details.headers[header]); if("data" in details) oXhr.send(details.data); else oXhr.send(); } else { throw ("This Browser is not supported, please upgrade."); } } } if(typeof GM_addStyle === "undefined") { GM_addStyle = function(/* String */ styles) { var oStyle = document.createElement("style"); oStyle.setAttribute("type", "text\/css"); oStyle.appendChild(document.createTextNode(styles)); document.getElementsByTagName("head")[0].appendChild(oStyle); } } if(typeof GM_log === "undefined") { GM_log = function(log) { if(console) console.log(log); else alert(log); } } ]=] --- Stores all the scripts. local scripts = {} --- Stores information on the currently loaded scripts on a webview widget local lstate = setmetatable({}, { __mode = "k" }) --- The directory, in which to search for userscripts. -- By default, this is $XDG_DATA_HOME/luakit/scripts dir = capi.luakit.data_dir .. "/scripts" -- Userscript class methods local prototype = { -- Run the userscript on the given webview widget run = function (s, view) -- Load common greasemonkey methods if not lstate[view].gmloaded then view:eval_js(gm_functions, { no_return = true }) lstate[view].gmloaded = true end view:eval_js(s.js, { source = s.file, no_return = true }) lstate[view].loaded[s.file] = s end, -- Check if the given uri matches the userscripts include/exclude patterns match = function (s, uri) local matches = false for _, p in ipairs(s.include) do if string.match(uri, p) then matches = true break end end if matches then for _, p in ipairs(s.exclude) do if string.match(uri, p) then return false end end end return matches end, } -- Parse and convert a simple glob matching pattern in the `@include`, -- `@exclude` or `@match` userscript header options into an RE. local function parse_pattern(pat) pat = string.gsub(string.gsub(pat, "[%^%$%(%)%%%.%[%]%+%-%?]", "%%%1"), "*", ".*") return '^' .. pat .. '$' end local function parse_header(header, file) local ret = { file = file, include = {}, exclude = {} } for i, line in ipairs(util.string.split(header, "\n")) do local singles = { name = true, description = true, version = true, homepage = true } -- Parse `// @key value` lines in header. local key, val = string.match(line, "^// @([%w%-]+)%s+(.+)$") if key then val = util.string.strip(val or "") if singles[key] then -- Only grab the first of its kind if not ret[key] then ret[key] = val end elseif key == "include" or key == "exclude" then table.insert(ret[key], parse_pattern(val)) elseif key == "run-at" and val == "document-start" then ret.on_start = true end end end return ret end --- Loads a js userscript. local function load_js(file) -- Open script local f = io.open(file, "r") local js = f:read("*all") f:close() -- Inspect userscript header local header = string.match(js, "//%s*==UserScript==%s*\n(.*)\n//%s*==/UserScript==") if header then local script = parse_header(header, file) script.js = js script.file = file scripts[file] = setmetatable(script, { __index = prototype }) else warn("(userscripts.lua): Invalid userscript header in file: %s", file) end end --- Loads all userscripts from the userscripts.dir. local function load_all() if not os.exists(dir) then return end for file in lfs.dir(dir) do if string.match(file, "%.user%.js$") then load_js(dir .. "/" .. file) end end end -- Invoke all userscripts for a given webviews current uri local function invoke(view, on_start) local uri = view.uri or "about:blank" for _, script in pairs(scripts) do if on_start == script.on_start then if script:match(uri) then script:run(view) end end end end -- Saves an userscript function save(file, js) if not os.exists(dir) then util.mkdir(dir) end local f = io.open(dir .. "/" .. file, "w") f:write(js) f:close() load_js(dir .. "/" .. file) end -- Deletes an userscript function del(file) if not scripts[file] then return end os.remove(file) scripts[file] = nil end --- Hook on the webview's load-status signal to invoke the userscripts. webview.init_funcs.userscripts = function (view, w) view:add_signal("load-status", function (v, status) if status == "provisional" then -- Clear last userscript-loaded state lstate[v] = { loaded = {}, gmloaded = false } elseif status == "first-visual" then invoke(v, true) elseif status == "finished" then invoke(v) end end) end -- Add userscript commands local cmd = bind.cmd add_cmds({ -- Saves the content of the open view as an userscript cmd({"userscriptinstall", "usi", "usinstall"}, "install userscript", function (w, a) local view = w.view local file = string.match(view.uri, "/([^/]+%.user%.js)$") if (not file) then return w:error("URL is not a *.user.js file") end if view:loading() then w:error("Wait for script to finish loading first.") end local js = util.unescape(view:eval_js("document.body.getElementsByTagName('pre')[0].innerHTML")) local header = string.match(js, "//%s*==UserScript==%s*\n(.*)\n//%s*==/UserScript==") if not header then return w:error("Could not find userscript header") end save(file, js) w:notify("Installed userscript to: " .. dir .. "/" .. file) end), cmd({"userscripts", "uscripts"}, "list userscripts", function (w) w:set_mode("uscriptlist") end), }) -- Add mode to display all userscripts in menu new_mode("uscriptlist", { enter = function (w) local rows = {{ "Userscripts", "Description", title = true }} for file, script in pairs(scripts) do local active = script:match(w.view.uri) and "*" or " " local title = (script.name or file) .. " " .. (script.version or "") table.insert(rows, { " " .. active .. title, " " .. script.description, script = script }) end if #rows == 1 then w:notify(string.format("No userscripts installed. Use `:usinstall`" .. "or place .user.js files in %q manually.", dir)) return end w.menu:build(rows) w:notify("Use j/k to move, d delete, o visit website, t tabopen, w winopen. '*' indicates active scripts.", false) end, leave = function (w) w.menu:hide() end, }) local key = bind.key add_binds("uscriptlist", util.table.join({ -- Delete userscript key({}, "d", function (w) local row = w.menu:get() if row and row.script then del(row.script.file) w.menu:del() end end), -- Open userscript homepage key({}, "o", function (w) local row = w.menu:get() if row and row.script and row.script.homepage then w:navigate(row.script.homepage) end end), -- Open userscript homepage in new tab key({}, "t", function (w) local row = w.menu:get() if row and row.script and row.script.homepage then w:new_tab(row.script.homepage, false) end end), -- Open userscript homepage in new window key({}, "w", function (w) local row = w.menu:get() if row and row.script and row.script.homepage then window.new(row.script.homepage) end end), -- Close menu key({}, "q", function (w) w:set_mode() end), }, menu_binds)) -- Initialize the userscripts load_all() -- vim: et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/lib/webinspector.lua000066400000000000000000000043241202446317700177100ustar00rootroot00000000000000-------------------------------------------------------- -- Bindings for the web inspector -- -- (C) 2012 Fabian Streitel -- -- (C) 2012 Mason Larobina -- -------------------------------------------------------- local windows = setmetatable({}, { __mode = "k" }) local function switch_inspector(w, view) -- Hide old widget if w.paned.bottom then w.paned:remove(w.paned.bottom) end -- Show new widget local iview = view.inspector if iview and not windows[iview] then w.paned:pack2(iview) end end local function close_window(iview) local win = windows[iview] if win then win:remove(iview) windows[iview] = nil win:destroy() return true end end window.init_funcs.inspector_setup = function (w) w.tabs:add_signal("switch-page", function (_, view) switch_inspector(w, view) end) end webview.init_funcs.inspector_setup = function (view, w) view.enable_developer_extras = true view:add_signal("create-inspector-web-view", function () return widget{type="webview"} end) view:add_signal("show-inspector", function () switch_inspector(w, view) -- We start in paned view view.inspector:eval_js("WebInspector.attached = true;") end) view:add_signal("close-inspector", function (_, iview) if not close_window(iview) then w.paned:remove(iview) end iview:destroy() end) view:add_signal("attach-inspector", function () local iview = view.inspector close_window(iview) switch_inspector(w, view) end) view:add_signal("detach-inspector", function () local iview = view.inspector local win = widget{type="window"} w.paned:remove(iview) win.child = iview windows[iview] = win win:show() end) end local cmd = lousy.bind.cmd add_cmds({ cmd("in[spect]", "open DOM inspector", function (w, _, o) local v = w.view if o.bang then -- "inspect!" toggles inspector (v.inspector and v.close_inspector or v.show_inspector)(v) else w.view:show_inspector() end end), }) luakit-2012.09.13-r1/luadoc/000077500000000000000000000000001202446317700151775ustar00rootroot00000000000000luakit-2012.09.13-r1/luadoc/luakit.lua000066400000000000000000000042551202446317700172010ustar00rootroot00000000000000--- luakit core API -- @author Mason Larobina <mason.larobina<AT>gmail.com> -- @author Paweł Zuzelski <pawelz<AT>pld-linux.org> -- @copyright 2010 Mason Larobina, Paweł Zuzelski module("luakit") --- Luakit global table -- @field config_dir config directory path (default: XDG_CONFIG_HOME) -- @field data_dir data directory path (default: XDG_DATA_HOME) -- @field cache_dir cache directory path (default: XDG_CACHE_HOME) -- @field verbose verbosity (boolean value) -- @field install_path luakit installation path (read only property) -- @field version luakit version (read only property) -- @field webkit_major_version webkit major version that luakit is linked against (read only property) -- @field webkit_minor_version webkit minor version that luakit is linked against (read only property) -- @field webkit_micro_version webkit micro version that luakit is linked against (read only property) -- @class table -- @name luakit --- All active window widgets -- @class table -- @name windows --- Quit luakit -- @param - -- @name quit -- @class function --- Get selection -- @param clipboard X clipboard name ('primary', 'secondary' or 'clipboard') -- @return A string with the selection (clipboard) content. -- @name get_selection -- @class function --- Set selection -- @param text UTF-8 string to be copied to clipboard -- @param clipboard X clipboard name ('primary', 'secondary' or 'clipboard') -- @name set_selection -- @class function --- Spawn process asynchronously -- @param cmd Command to execute. It is parsed with simple shell-like parser. -- @name spawn -- @class function --- Spawn process synchronously -- @param cmd Command to execute. It is parsed with simple shell-like parser. -- @return An exit status of the command. -- @return A string containig data printed on stdout. -- @return A string containig data printed on stderr. -- @name spawn_sync -- @class function --- Escape a string for use in a URI. -- @param string A string to encode. -- @return A percent-encoded string. -- @name uri-encode -- @class function --- Unescape a whole escaped string. -- @param string A percent-encoded string. -- @return An unescaped string. -- @name uri-decode -- @class function luakit-2012.09.13-r1/luah.c000066400000000000000000000305721202446317700150340ustar00rootroot00000000000000/* * luah.c - Lua helper functions * * Copyright © 2010-2011 Mason Larobina * Copyright © 2008-2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" /* include clib headers */ #include "clib/download.h" #include "clib/luakit.h" #include "clib/soup/soup.h" #include "clib/sqlite3.h" #include "clib/timer.h" #include "clib/unique.h" #include "clib/widget.h" #include "clib/xdg.h" #include #include void luaH_modifier_table_push(lua_State *L, guint state) { gint i = 1; lua_newtable(L); if (state & GDK_MODIFIER_MASK) { #define MODKEY(key, name) \ if (state & GDK_##key##_MASK) { \ lua_pushstring(L, name); \ lua_rawseti(L, -2, i++); \ } MODKEY(SHIFT, "Shift"); MODKEY(LOCK, "Lock"); MODKEY(CONTROL, "Control"); MODKEY(MOD1, "Mod1"); MODKEY(MOD2, "Mod2"); MODKEY(MOD3, "Mod3"); MODKEY(MOD4, "Mod4"); MODKEY(MOD5, "Mod5"); #undef MODKEY } } void luaH_keystr_push(lua_State *L, guint keyval) { gchar ucs[7]; guint ulen; guint32 ukval = gdk_keyval_to_unicode(keyval); /* check for printable unicode character */ if (g_unichar_isgraph(ukval)) { ulen = g_unichar_to_utf8(ukval, ucs); ucs[ulen] = 0; lua_pushstring(L, ucs); } /* sent keysym for non-printable characters */ else lua_pushstring(L, gdk_keyval_name(keyval)); } /* UTF-8 aware string length computing. * Returns the number of elements pushed on the stack. */ static gint luaH_utf8_strlen(lua_State *L) { const gchar *cmd = luaL_checkstring(L, 1); lua_pushnumber(L, (ssize_t) g_utf8_strlen(NONULL(cmd), -1)); return 1; } /* Overload standard Lua next function to use __next key on metatable. * Returns the number of elements pushed on stack. */ static gint luaHe_next(lua_State *L) { if(luaL_getmetafield(L, 1, "__next")) { lua_insert(L, 1); lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); return lua_gettop(L); } luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 2); if(lua_next(L, 1)) return 2; lua_pushnil(L); return 1; } /* Overload lua_next() function by using __next metatable field to get * next elements. `idx` is the index number of elements in stack. * Returns 1 if more elements to come, 0 otherwise. */ gint luaH_mtnext(lua_State *L, gint idx) { if(luaL_getmetafield(L, idx, "__next")) { /* if idx is relative, reduce it since we got __next */ if(idx < 0) idx--; /* copy table and then move key */ lua_pushvalue(L, idx); lua_pushvalue(L, -3); lua_remove(L, -4); lua_pcall(L, 2, 2, 0); /* next returned nil, it's the end */ if(lua_isnil(L, -1)) { /* remove nil */ lua_pop(L, 2); return 0; } return 1; } else if(lua_istable(L, idx)) return lua_next(L, idx); /* remove the key */ lua_pop(L, 1); return 0; } /* Generic pairs function. * Returns the number of elements pushed on stack. */ static gint luaH_generic_pairs(lua_State *L) { lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ lua_pushvalue(L, 1); /* state, */ lua_pushnil(L); /* and initial value */ return 3; } /* Overload standard pairs function to use __pairs field of metatables. * Returns the number of elements pushed on stack. */ static gint luaHe_pairs(lua_State *L) { if(luaL_getmetafield(L, 1, "__pairs")) { lua_insert(L, 1); lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); return lua_gettop(L); } luaL_checktype(L, 1, LUA_TTABLE); return luaH_generic_pairs(L); } static gint luaH_ipairs_aux(lua_State *L) { gint i = luaL_checkint(L, 2) + 1; luaL_checktype(L, 1, LUA_TTABLE); lua_pushinteger(L, i); lua_rawgeti(L, 1, i); return (lua_isnil(L, -1)) ? 0 : 2; } /* Overload standard ipairs function to use __ipairs field of metatables. * Returns the number of elements pushed on stack. */ static gint luaHe_ipairs(lua_State *L) { if(luaL_getmetafield(L, 1, "__ipairs")) { lua_insert(L, 1); lua_call(L, lua_gettop(L) - 1, LUA_MULTRET); return lua_gettop(L); } luaL_checktype(L, 1, LUA_TTABLE); lua_pushvalue(L, lua_upvalueindex(1)); lua_pushvalue(L, 1); lua_pushinteger(L, 0); /* and initial value */ return 3; } /* Enhanced type() function which recognize luakit objects. * \param L The Lua VM state. * \return The number of arguments pushed on the stack. */ static gint luaHe_type(lua_State *L) { luaL_checkany(L, 1); lua_pushstring(L, luaH_typename(L, 1)); return 1; } /** Returns the absolute version of a relative file path, if that file exists. * * \param L The Lua VM state. * \return The number of elements pushed on the stack. * * \luastack * \lparam rel_path The relative file path to convert. * \lreturn Returns the full path of the given file. */ static gint luaH_abspath(lua_State *L) { const gchar *path = luaL_checkstring(L, 1); GFile *file = g_file_new_for_path(path); if (!file) return 0; gchar *absolute = g_file_get_path(file); if (!absolute) return 0; lua_pushstring(L, absolute); g_free(absolute); return 1; } /* Fix up and add handy standard lib functions */ static void luaH_fixups(lua_State *L) { /* export string.wlen */ lua_getglobal(L, "string"); lua_pushcfunction(L, &luaH_utf8_strlen); lua_setfield(L, -2, "wlen"); lua_pop(L, 1); /* export os.abspath */ lua_getglobal(L, "os"); lua_pushcfunction(L, &luaH_abspath); lua_setfield(L, -2, "abspath"); lua_pop(L, 1); /* replace next */ lua_pushliteral(L, "next"); lua_pushcfunction(L, luaHe_next); lua_settable(L, LUA_GLOBALSINDEX); /* replace pairs */ lua_pushliteral(L, "pairs"); lua_pushcfunction(L, luaHe_next); lua_pushcclosure(L, luaHe_pairs, 1); /* pairs get next as upvalue */ lua_settable(L, LUA_GLOBALSINDEX); /* replace ipairs */ lua_pushliteral(L, "ipairs"); lua_pushcfunction(L, luaH_ipairs_aux); lua_pushcclosure(L, luaHe_ipairs, 1); lua_settable(L, LUA_GLOBALSINDEX); /* replace type */ lua_pushliteral(L, "type"); lua_pushcfunction(L, luaHe_type); lua_settable(L, LUA_GLOBALSINDEX); } static gint luaH_panic(lua_State *L) { warn("unprotected error in call to Lua API (%s)", lua_tostring(L, -1)); return 0; } static gint luaH_dofunction_on_error(lua_State *L) { /* duplicate string error */ lua_pushvalue(L, -1); /* emit error signal */ signal_object_emit(L, luakit_class.signals, "debug::error", 1, 0); if(!luaL_dostring(L, "return debug.traceback(\"error while running function\", 3)")) { /* Move traceback before error */ lua_insert(L, -2); /* Insert sentence */ lua_pushliteral(L, "\nerror: "); /* Move it before error */ lua_insert(L, -2); lua_concat(L, 3); } return 1; } void luaH_init(void) { lua_State *L; /* Lua VM init */ L = globalconf.L = luaL_newstate(); /* Set panic fuction */ lua_atpanic(L, luaH_panic); /* Set error handling function */ lualib_dofunction_on_error = luaH_dofunction_on_error; luaL_openlibs(L); luaH_fixups(L); luaH_object_setup(L); /* Export luakit lib */ luakit_lib_setup(L); /* Export xdg lib */ xdg_lib_setup(L); /* Export soup lib */ soup_lib_setup(L); #if WITH_UNIQUE if (!globalconf.nounique) /* Export unique lib */ unique_lib_setup(L); #endif /* Export widget */ widget_class_setup(L); /* Export download */ download_class_setup(L); /* Export sqlite3 */ sqlite3_class_setup(L); /* Export timer */ timer_class_setup(L); /* add Lua search paths */ lua_getglobal(L, "package"); if(LUA_TTABLE != lua_type(L, 1)) { warn("package is not a table"); return; } lua_getfield(L, 1, "path"); if(LUA_TSTRING != lua_type(L, 2)) { warn("package.path is not a string"); lua_pop(L, 1); return; } /* compile list of package search paths */ GPtrArray *paths = g_ptr_array_new_with_free_func(g_free); #if DEVELOPMENT_PATHS /* allows for testing luakit in the project directory */ g_ptr_array_add(paths, g_strdup("./lib")); g_ptr_array_add(paths, g_strdup("./config")); #endif /* add users config dir (see: XDG_CONFIG_DIR) */ g_ptr_array_add(paths, g_strdup(globalconf.config_dir)); /* add system config dirs (see: XDG_CONFIG_DIRS) */ const gchar* const *config_dirs = g_get_system_config_dirs(); for (; *config_dirs; config_dirs++) g_ptr_array_add(paths, g_build_filename(*config_dirs, "luakit", NULL)); /* add luakit install path */ g_ptr_array_add(paths, g_build_filename(LUAKIT_INSTALL_PATH, "lib", NULL)); const gchar *path; for (guint i = 0; i < paths->len; i++) { path = paths->pdata[i]; /* Search for file */ lua_pushliteral(L, ";"); lua_pushstring(L, path); lua_pushliteral(L, "/?.lua"); lua_concat(L, 3); /* Search for lib */ lua_pushliteral(L, ";"); lua_pushstring(L, path); lua_pushliteral(L, "/?/init.lua"); lua_concat(L, 3); /* concat with package.path */ lua_concat(L, 3); } g_ptr_array_free(paths, TRUE); /* package.path = "concatenated string" */ lua_setfield(L, 1, "path"); /* remove package module from stack */ lua_pop(L, 1); } gboolean luaH_loadrc(const gchar *confpath, gboolean run) { debug("Loading rc: %s", confpath); lua_State *L = globalconf.L; if(!luaL_loadfile(L, confpath)) { if(run) { if(lua_pcall(L, 0, LUA_MULTRET, 0)) { g_fprintf(stderr, "%s\n", lua_tostring(L, -1)); } else return TRUE; } else lua_pop(L, 1); return TRUE; } else g_fprintf(stderr, "%s\n", lua_tostring(L, -1)); return FALSE; } /* Load a configuration file. */ gboolean luaH_parserc(const gchar *confpath, gboolean run) { const gchar* const *config_dirs = NULL; gboolean ret = FALSE; GPtrArray *paths = NULL; /* try to load, return if it's ok */ if(confpath) { if(luaH_loadrc(confpath, run)) ret = TRUE; goto bailout; } /* compile list of config search paths */ paths = g_ptr_array_new_with_free_func(g_free); #if DEVELOPMENT_PATHS /* allows for testing luakit in the project directory */ g_ptr_array_add(paths, g_strdup("./config/rc.lua")); #endif /* search users config dir (see: XDG_CONFIG_HOME) */ g_ptr_array_add(paths, g_build_filename(globalconf.config_dir, "rc.lua", NULL)); /* search system config dirs (see: XDG_CONFIG_DIRS) */ config_dirs = g_get_system_config_dirs(); for(; *config_dirs; config_dirs++) g_ptr_array_add(paths, g_build_filename(*config_dirs, "luakit", "rc.lua", NULL)); const gchar *path; for (guint i = 0; i < paths->len; i++) { path = paths->pdata[i]; if (file_exists(path)) { if(luaH_loadrc(path, run)) { globalconf.confpath = g_strdup(path); ret = TRUE; goto bailout; } else if(!run) goto bailout; } } bailout: if (paths) g_ptr_array_free(paths, TRUE); return ret; } gint luaH_class_index_miss_property(lua_State *L, lua_object_t* UNUSED(obj)) { signal_object_emit(L, luakit_class.signals, "debug::index::miss", 2, 0); return 0; } gint luaH_class_newindex_miss_property(lua_State *L, lua_object_t* UNUSED(obj)) { signal_object_emit(L, luakit_class.signals, "debug::newindex::miss", 3, 0); return 0; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/luah.h000066400000000000000000000121421202446317700150320ustar00rootroot00000000000000/* * luah.h - Lua helper functions * * Copyright © 2010 Mason Larobina * Copyright © 2008-2009 Julien Danjou * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_LUA_H #define LUAKIT_LUA_H #include "common/luaobject.h" #include "common/lualib.h" #include "globalconf.h" #include #define luaH_deprecate(L, repl) \ do { \ luaH_warn(L, "%s: This function is deprecated and will be removed, see %s", \ __FUNCTION__, repl); \ lua_pushlstring(L, __FUNCTION__, sizeof(__FUNCTION__)); \ signal_object_emit(L, &globalconf.signals, "debug::deprecation", 1, 0); \ } while(0) static inline gboolean luaH_checkboolean(lua_State *L, gint n) { if(!lua_isboolean(L, n)) luaL_typerror(L, n, "boolean"); return lua_toboolean(L, n); } static inline gboolean luaH_optboolean(lua_State *L, gint idx, gboolean def) { return luaL_opt(L, luaH_checkboolean, idx, def); } static inline lua_Number luaH_getopt_number(lua_State *L, gint idx, const gchar *name, lua_Number def) { lua_getfield(L, idx, name); if (lua_isnil(L, -1) || lua_isnumber(L, -1)) def = luaL_optnumber(L, -1, def); lua_pop(L, 1); return def; } static inline const gchar * luaH_getopt_lstring(lua_State *L, gint idx, const gchar *name, const gchar *def, size_t *len) { lua_getfield(L, idx, name); const gchar *s = luaL_optlstring(L, -1, def, len); lua_pop(L, 1); return s; } static inline gboolean luaH_getopt_boolean(lua_State *L, gint idx, const gchar *name, gboolean def) { lua_getfield(L, idx, name); gboolean b = luaH_optboolean(L, -1, def); lua_pop(L, 1); return b; } /* Register an Lua object. * \param L The Lua stack. * \param idx Index of the object in the stack. * \param ref A gint address: it will be filled with the gint * registered. If the address points to an already registered object, it will * be unregistered. * \return Always 0. */ static inline gint luaH_register(lua_State *L, gint idx, gint *ref) { lua_pushvalue(L, idx); if(*ref != LUA_REFNIL) luaL_unref(L, LUA_REGISTRYINDEX, *ref); *ref = luaL_ref(L, LUA_REGISTRYINDEX); return 0; } /* Unregister a Lua object. * \param L The Lua stack. * \param ref A reference to an Lua object. */ static inline void luaH_unregister(lua_State *L, gint *ref) { luaL_unref(L, LUA_REGISTRYINDEX, *ref); *ref = LUA_REFNIL; } /* Register a function. * \param L The Lua stack. * \param idx Index of the function in the stack. * \param fct A gint address: it will be filled with the gint * registered. If the address points to an already registered function, it will * be unregistered. * \return luaH_register value. */ static inline gint luaH_registerfct(lua_State *L, gint idx, gint *fct) { luaH_checkfunction(L, idx); return luaH_register(L, idx, fct); } /* Grab a function from the registry and execute it. * \param L The Lua stack. * \param ref The function reference. * \param nargs The number of arguments for the Lua function. * \param nret The number of returned value from the Lua function. * \return True on no error, false otherwise. */ static inline gboolean luaH_dofunction_from_registry(lua_State *L, gint ref, gint nargs, gint nret) { lua_rawgeti(L, LUA_REGISTRYINDEX, ref); return luaH_dofunction(L, nargs, nret); } /* Print a warning about some Lua code. * This is less mean than luaL_error() which setjmp via lua_error() and kills * everything. This only warn, it's up to you to then do what's should be done. * \param L The Lua VM state. * \param fmt The warning message. */ static inline void __attribute__ ((format(printf, 2, 3))) luaH_warn(lua_State *L, const gchar *fmt, ...) { va_list ap; luaL_where(L, 1); fprintf(stderr, "%sW: ", lua_tostring(L, -1)); lua_pop(L, 1); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } static inline gint luaH_rawfield(lua_State *L, gint idx, const gchar *field) { lua_pushstring(L, field); lua_rawget(L, idx); gint type = lua_type(L, -1); if (type == LUA_TNIL) lua_pop(L, 1); return type; } void luaH_init(); gboolean luaH_parserc(const gchar *, gboolean); gint luaH_mtnext(lua_State *, gint); gint luaH_class_index_miss_property(lua_State *, lua_object_t *); gint luaH_class_newindex_miss_property(lua_State *, lua_object_t *); void luaH_modifier_table_push(lua_State *, guint); void luaH_keystr_push(lua_State *, guint); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/luakit.c000066400000000000000000000137101202446317700153670ustar00rootroot00000000000000/* * luakit.c - luakit main functions * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "globalconf.h" #include "common/util.h" #include "luah.h" #include #include #include #include #include #include #include static void sigchld(int sigint); void sigchld(int UNUSED(signum)) { while(0 < waitpid(-1, NULL, WNOHANG)); } void init_lua(gchar **uris) { gchar *uri; lua_State *L; /* init globalconf structs */ globalconf.windows = g_ptr_array_new(); /* init lua */ luaH_init(); L = globalconf.L; /* push a table of the statup uris */ lua_newtable(L); for (gint i = 0; uris && (uri = uris[i]); i++) { lua_pushstring(L, uri); lua_rawseti(L, -2, i + 1); } lua_setglobal(L, "uris"); } void init_directories(void) { /* create luakit directory */ globalconf.cache_dir = g_build_filename(g_get_user_cache_dir(), "luakit", NULL); globalconf.config_dir = g_build_filename(g_get_user_config_dir(), "luakit", NULL); globalconf.data_dir = g_build_filename(g_get_user_data_dir(), "luakit", NULL); g_mkdir_with_parents(globalconf.cache_dir, 0771); g_mkdir_with_parents(globalconf.config_dir, 0771); g_mkdir_with_parents(globalconf.data_dir, 0771); } /* load command line options into luakit and return uris to load */ gchar** parseopts(int *argc, gchar *argv[], gboolean **nonblock) { GOptionContext *context; gboolean *version_only = NULL; gboolean *check_only = NULL; gchar **uris = NULL; /* save luakit exec path */ globalconf.execpath = g_strdup(argv[0]); globalconf.nounique = FALSE; /* define command line options */ const GOptionEntry entries[] = { { "check", 'k', 0, G_OPTION_ARG_NONE, &check_only, "check config and exit", NULL }, { "config", 'c', 0, G_OPTION_ARG_STRING, &globalconf.confpath, "configuration file to use", "FILE" }, { "nonblock", 'n', 0, G_OPTION_ARG_NONE, nonblock, "run in background", NULL }, { "nounique", 'U', 0, G_OPTION_ARG_NONE, &globalconf.nounique, "ignore libunique bindings", NULL }, { "uri", 'u', 0, G_OPTION_ARG_STRING_ARRAY, &uris, "uri(s) to load at startup", "URI" }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &globalconf.verbose, "print debugging output", NULL }, { "version", 'V', 0, G_OPTION_ARG_NONE, &version_only, "print version and exit", NULL }, { NULL, 0, 0, 0, NULL, NULL, NULL }, }; /* parse command line options */ context = g_option_context_new("[URI...]"); g_option_context_add_main_entries(context, entries, NULL); g_option_context_add_group(context, gtk_get_option_group(FALSE)); g_option_context_parse(context, argc, &argv, NULL); g_option_context_free(context); /* print version and exit */ if (version_only) { g_printf("luakit %s\n", VERSION); exit(EXIT_SUCCESS); } /* check config syntax and exit */ if (check_only) { init_directories(); init_lua(NULL); if (!luaH_parserc(globalconf.confpath, FALSE)) { g_fprintf(stderr, "Confiuration file syntax error.\n"); exit(EXIT_FAILURE); } else { g_fprintf(stderr, "Configuration file syntax OK.\n"); exit(EXIT_SUCCESS); } } if (uris && argv[1]) fatal("invalid mix of -u and default uri arguments"); if (uris) return uris; else return argv+1; } gint main(gint argc, gchar *argv[]) { gboolean *nonblock = NULL; gchar **uris = NULL; pid_t pid, sid; globalconf.starttime = l_time(); /* clean up any zombies */ struct sigaction sigact; sigact.sa_handler=sigchld; sigemptyset (&sigact.sa_mask); sigact.sa_flags = SA_NOCLDSTOP; if (sigaction(SIGCHLD, &sigact, NULL)) fatal("Can't install SIGCHLD handler"); /* set numeric locale to C (required for compatibility with LuaJIT and luakit scripts) */ gtk_set_locale(); gtk_disable_setlocale(); setlocale(LC_NUMERIC, "C"); /* parse command line opts and get uris to load */ uris = parseopts(&argc, argv, &nonblock); /* if non block mode - respawn, detach and continue in child */ if (nonblock) { pid = fork(); if (pid < 0) { fatal("Cannot fork: %d", errno); } else if (pid > 0) { exit(EXIT_SUCCESS); } sid = setsid(); if (sid < 0) { fatal("New SID creation failure: %d", errno); } } gtk_init(&argc, &argv); if (!g_thread_supported()) g_thread_init(NULL); init_directories(); init_lua(uris); /* hide command line parameters so process lists don't leak (possibly confidential) URLs */ for (gint i = 1; i < argc; i++) memset(argv[i], 0, strlen(argv[i])); /* parse and run configuration file */ if(!luaH_parserc(globalconf.confpath, TRUE)) fatal("couldn't find rc file"); if (!globalconf.windows->len) fatal("no windows spawned by rc file, exiting"); gtk_main(); return EXIT_SUCCESS; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/luakit.doxygen000066400000000000000000000221241202446317700166210ustar00rootroot00000000000000#--------------------------------------------------------------------------- # luakit doxygen config (from AwesomeWM project) # (C) 2011 Mason Larobina # (C) 2007-2010 Julien Danjou #--------------------------------------------------------------------------- DOXYFILE_ENCODING = UTF-8 PROJECT_NAME = luakit PROJECT_NUMBER = devel OUTPUT_DIRECTORY = doc CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = YES STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = NO QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 4 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES ALIASES += luastack="\par Lua Stack:" ALIASES += lvalue="- \em value \c " ALIASES += lparam="- \em param \c " ALIASES += lreturn="- \em returns " ALIASES += lfield="- \em fields " ALIASES += lcode="\par Lua Example: \n \code" #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = YES EXTRACT_ANON_NSPACES = YES HIDE_UNDOC_MEMBERS = NO HIDE_UNDOC_CLASSES = NO HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = ./ INPUT_ENCODING = UTF-8 FILE_PATTERNS = *.c *.h RECURSIVE = YES EXCLUDE = EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = EXCLUDE_SYMBOLS = EXAMPLE_PATH = EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = NO REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES REFERENCES_LINK_SOURCE = YES USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = NO COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO HTML_DYNAMIC_SECTIONS = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = YES TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_SCHEMA = XML_DTD = XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = NO INCLUDE_PATH = INCLUDE_FILE_PATTERNS = PREDEFINED = "__attribute__(x)=" \ "LUA_OBJECT_HEADER=signal_t *signals;" EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = NO MSCGEN_PATH = HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = YES TEMPLATE_RELATIONS = YES INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES CALL_GRAPH = YES CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 50 MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = YES DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO luakit-2012.09.13-r1/tests/000077500000000000000000000000001202446317700150725ustar00rootroot00000000000000luakit-2012.09.13-r1/tests/lunit-run.lua000077500000000000000000000004161202446317700175360ustar00rootroot00000000000000-- Save a reference to the original lua assert function orig_assert = assert -- Add ./lunit & ./tests to package.path package.path = package.path .. ';./lunit/?.lua;./tests/?.lua' require "lunit" local stats = lunit.main(uris) luakit.quit(stats.errors + stats.failed) luakit-2012.09.13-r1/tests/test_clib_luakit.lua000066400000000000000000000042241202446317700211200ustar00rootroot00000000000000require "lunit" module("test_clib_luakit", lunit.testcase, package.seeall) function test_luakit() assert_table(luakit) -- Check metatable mt = getmetatable(luakit) assert_function(mt.__index, "luakit mt missing __index") end function test_luakit_index() local funcprops = { "exec", "quit", "save_file", "spawn", "spawn_sync", "time", "uri_decode", "uri_encode", "idle_add", "idle_remove" } for _, p in ipairs(funcprops) do assert_function(luakit[p], "Missing/invalid function: luakit."..p) end local strprops = { "cache_dir", "config_dir", "data_dir", "execpath", "confpath", "install_path", "version" } for _, p in ipairs(strprops) do assert_string(luakit[p], "Missing/invalid property: luakit."..p) end local boolprops = { "dev_paths", "verbose", "nounique" } for _, p in ipairs(boolprops) do assert_boolean(luakit[p], "Missing/invalid property: luakit."..p) end assert_number(luakit.time(), "Invalid: luakit.time()") end function test_webkit_version() assert_match("^%d+%.%d+%.%d$", luakit.webkit_version, "Invalid format: luakit.webkit_version") assert_match("^%d+%.%d+$", luakit.webkit_user_agent_version, "Invalid format: luakit.webkit_user_agent_version") end function test_windows_table() assert_table(luakit.windows, "Missing/invalid luakit.windows table.") assert_equal(#luakit.windows, 0, "Invalid number of windows") win = widget{type="window"} assert_equal(#luakit.windows, 1, "luakit.windows not tracking opened windows.") win:destroy() assert_equal(#luakit.windows, 0, "luakit.windows not tracking closed windows.") end function test_invalid_prop() assert_nil(luakit.invalid_property) end function test_idle_add_del() local f = function () end assert_false(luakit.idle_remove(f), "Function can't be removed before it's been added.") for i = 1,5 do luakit.idle_add(f) end for i = 1,5 do assert_true(luakit.idle_remove(f), "Error removing callback.") end assert_false(luakit.idle_remove(f), "idle_remove removed incorrect number of callbacks.") end luakit-2012.09.13-r1/tests/test_clib_soup.lua000066400000000000000000000051561202446317700206220ustar00rootroot00000000000000require "lunit" module("test_clib_soup", lunit.testcase, package.seeall) function test_module() assert_table(soup) end function test_properties() -- accept_language accept_language_auto soup.accept_language_auto = true assert_equal(true, soup.accept_language_auto) assert_string(soup.accept_language) soup.accept_language = "en-au, en" assert_equal("en-au, en", soup.accept_language) assert_equal(false, soup.accept_language_auto) soup.idle_timeout = 60 assert_equal(60, soup.idle_timeout) soup.max_conns = 10 assert_equal(10, soup.max_conns) soup.max_conns_per_host = 10 assert_equal(10, soup.max_conns_per_host) soup.proxy_uri = "http://localhost/" assert_equal("http://localhost/", soup.proxy_uri) soup.proxy_uri = nil -- System dependant --soup.ssl_ca_file = "/etc/certs/ca-certificates.crt" --assert_equal("/etc/certs/ca-certificates.crt", soup.ssl_ca_file) soup.ssl_strict = true assert_equal(true, soup.ssl_strict) soup.timeout = 10 assert_equal(10, soup.timeout) end function test_add_cookies() assert_error(function () soup.add_cookies("error") end) assert_error(function () soup.add_cookies({"error"}) end) assert_error(function () soup.add_cookies({{}}) end) assert_pass(function () soup.add_cookies({ { domain = "google.com", path = "/", name = "test", value = "test", expires = 10, http_only = true, secure = true } }) end) assert_pass(function () soup.add_cookies({ { domain = "google.com", path = "/", name = nil, value = "test", expires = 10, http_only = true, secure = true } }) soup.add_cookies({ { domain = "google.com", path = "/", name = "", value = nil, expires = 10, http_only = true, secure = true } }) soup.add_cookies({ { domain = "google.com", path = "/", name = "", value = "", expires = 10, http_only = true, secure = true } }) soup.add_cookies({ { domain = "google.com", path = "/", name = "test", value = "test", expires = 10, http_only = 1, secure = 1 } }) end) assert_error(function () soup.add_cookies({ { domain = 10, path = "/", name = "test", value = "test", expires = 10, http_only = true, secure = true } }) end) assert_error(function () soup.add_cookies({ { domain = "google.com", path = 10, name = "test", value = "test", expires = 10, http_only = true, secure = true } }) end) end luakit-2012.09.13-r1/tests/test_clib_sqlite3.lua000066400000000000000000000060231202446317700212120ustar00rootroot00000000000000require "lunit" module("test_clib_sqlite3", lunit.testcase, package.seeall) function test_module() assert_table(sqlite3) end function test_open_db() local db = sqlite3{filename=":memory:"} assert_equal("sqlite3", type(db)) assert_error("Should error without constructor table", function () sqlite() end) assert_error("Should error without filename in constructor table", function () sqlite{} end) end function test_sqlite3_exec() local db = sqlite3{filename=":memory:"} local ret = db:exec([[CREATE TABLE IF NOT EXISTS test ( id INTEGER PRIMARY KEY, uri TEXT, created FLOAT )]]) assert_nil(ret) assert_pass(function () db:exec(";") end) local ret = db:exec(";") assert_nil(ret) local ret = db:exec([[SELECT * FROM test;]]) assert_table(ret) assert_equal(0, #ret) local ret = db:exec([[INSERT INTO test VALUES(NULL, "google.com", 1234.45)]]) assert_nil(ret) local ret = db:exec([[SELECT * FROM test;]]) assert_table(ret) assert_equal(1, #ret) ret = ret[1] assert_table(ret) assert_equal("google.com", ret.uri) assert_equal(1234.45, ret.created) local ret = db:exec([[INSERT INTO test VALUES(:id, :uri, :created);]], { [":uri"] = "reddit.com", [":created"] = 1000 }) assert_nil(ret) local ret = db:exec([[SELECT * FROM test;]]) assert_table(ret) assert_equal(2, #ret) ret = ret[2] assert_table(ret) assert_equal("reddit.com", ret.uri) assert_equal(1000, ret.created) -- for i, row in ipairs(ret) do -- for k,v in pairs(row) do -- print("row", i, k, v) -- end -- end end function test_compile_statement() local db = sqlite3{filename=":memory:"} local ret, tail = db:exec([[CREATE TABLE IF NOT EXISTS test ( id INTEGER PRIMARY KEY, uri TEXT, created FLOAT )]]) assert_nil(ret) assert_nil(tail) -- Compile some statements local insert, tail = db:compile([[INSERT INTO test VALUES(:id, :uri, :created);]]) assert_equal("sqlite3::statement", type(insert)) assert_nil(tail) local select_all, tail = db:compile([[SELECT * FROM test;]]) assert_equal("sqlite3::statement", type(select_all)) assert_nil(tail) local ret = insert:exec{ [":uri"] = "google.com", [":created"] = 1000 } assert_nil(ret) local ret = insert:exec{ [":uri"] = "reddit.com", [":created"] = 12.34 } assert_nil(ret) local ret = select_all:exec() assert_table(ret) assert_equal(2, #ret) assert_table(ret[1]) assert_equal("google.com", ret[1].uri) assert_equal(1000, ret[1].created) assert_table(ret[2]) assert_equal("reddit.com", ret[2].uri) assert_equal(12.34, ret[2].created) -- Re-run last statement with same bound values local ret = insert:exec() assert_nil(ret) local ret = select_all:exec() assert_table(ret) assert_equal(3, #ret) assert_table(ret[3]) assert_equal("reddit.com", ret[3].uri) assert_equal(12.34, ret[3].created) end luakit-2012.09.13-r1/tests/test_webview_javascript.lua000066400000000000000000000100641202446317700225330ustar00rootroot00000000000000require "lunit" module("test_webview_javascript", lunit.testcase, package.seeall) -- Dummy webview widget used for all the tests. local view = widget{type="webview"} function test_eval_js_return() assert_equal("a string", view:eval_js([["a string";]])) assert_equal(300, view:eval_js("100 + 200;")) assert_equal(true, view:eval_js("true;")) assert_equal(nil, view:eval_js("undefined;")) assert_equal(nil, view:eval_js("null;")) --assert_table(view:eval_js("{};")) TODO: Add support for table types local ret, err = view:eval_js("[10,20,30];") assert_equal(nil, err) assert_table(ret) assert_equal(3, #ret) assert_equal("10,20,30", table.concat(ret, ",")) local ret, err = view:eval_js([=[var o = {a_key: "Some string"}; o]=]) assert_equal(nil, err) assert_table(ret) assert_equal(0, #ret) assert_equal("Some string", ret.a_key) local ret, err = view:eval_js([=[ var o = { an_array: [10,20,30, { foo: "bar" }] }; o; ]=]); assert_equal(nil, err) assert_table(ret) assert_equal("10,20,30", table.concat(ret.an_array, ",", 1, 3)) assert_table(ret.an_array) assert_table(ret.an_array[4]) assert_equal("bar", ret.an_array[4].foo) end function test_catch_js_exception() local ret, err = view:eval_js("unknownVariable;") assert_match("^ReferenceError:", err) assert_nil(ret) end function test_register_function() view:register_function("my_add", function (a, b) return a + b end) assert_equal(90, view:eval_js("my_add(40,50);")) end function test_register_function_error() view:register_function("raise_error", function (msg) error(msg) end) local ret, err = view:eval_js([[raise_error("Some error message");]]) assert_match("Some error message$", err) assert_nil(ret) end function test_register_function_args() view:register_function("check_args", function (a_string, a_num, a_bool, a_undefined, a_null) orig_assert(type(a_string) == "string" and a_string == "a string") orig_assert(type(a_num) == "number" and a_num == 100) orig_assert(type(a_bool) == "boolean" and a_bool) orig_assert(type(a_undefined) == "nil") orig_assert(type(a_null) == "nil") end) local ret, err = view:eval_js([[check_args("a string", 100, true, undefined, null);]]) assert_equal(nil, err) assert_nil(ret) local ret, err = view:eval_js([[check_args(100, "a string", null, undefined);]]) assert_match("assertion failed!$", err) assert_nil(ret) end function test_register_function_return() view:register_function("return_num", function () return 100 end) local ret, err = view:eval_js("return_num();") assert_equal(nil, err) assert_equal(100, ret) view:register_function("return_string", function () return "a string" end) local ret, err = view:eval_js("return_string();") assert_equal(nil, err) assert_equal("a string", ret) view:register_function("return_bool", function () return true end) local ret, err = view:eval_js("return_bool();") assert_equal(nil, err) assert_equal(true, ret) view:register_function("return_nil", function () return nil end) local ret, err = view:eval_js("return_nil();") assert_equal(nil, err) assert_nil(ret) view:register_function("return_array", function () return { [200] = 200, key = "val", 10,20,30, [100] = "donkey"} end) local ret, err = view:eval_js([=[return_array().join(",");]=]) assert_equal(nil, err) assert_equal("10,20,30,donkey,200,val", ret) view:register_function("return_object", function () return { a = "Some", b = "string", [200] = "e" } end) local ret, err = view:eval_js([=[ var o = return_object(); o["a"] + " " + o["b"]; ]=]) assert_equal(nil, err) assert_equal("Some string", ret) view:register_function("return_sub_array", function () return {{10,20,{30}}} end) local ret, err = view:eval_js([=[ var a = return_sub_array(); a[0][0] + a[0][1] + a[0][2][0]; ]=]) assert_equal(nil, err) assert_equal(60, ret) end luakit-2012.09.13-r1/widgets/000077500000000000000000000000001202446317700153765ustar00rootroot00000000000000luakit-2012.09.13-r1/widgets/box.c000066400000000000000000000076571202446317700163510ustar00rootroot00000000000000/* * widgets/box.c - gtk hbox & vbox container widgets * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_box_pack(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint top = lua_gettop(L); gboolean expand = FALSE, fill = FALSE, start = TRUE; guint padding = 0; /* check for options table */ if (top > 2 && !lua_isnil(L, 3)) { luaH_checktable(L, 3); /* pack child from start or end of container? */ if (luaH_rawfield(L, 3, "from")) start = L_TK_END == l_tokenize(lua_tostring(L, -1)) ? FALSE : TRUE; /* expand? */ if (luaH_rawfield(L, 3, "expand")) expand = lua_toboolean(L, -1) ? TRUE : FALSE; /* fill? */ if (luaH_rawfield(L, 3, "fill")) fill = lua_toboolean(L, -1) ? TRUE : FALSE; /* padding? */ if (luaH_rawfield(L, 3, "padding")) padding = (guint)lua_tonumber(L, -1); /* return stack to original state */ lua_settop(L, top); } if (start) gtk_box_pack_start(GTK_BOX(w->widget), GTK_WIDGET(child->widget), expand, fill, padding); else gtk_box_pack_end(GTK_BOX(w->widget), GTK_WIDGET(child->widget), expand, fill, padding); return 0; } /* direct wrapper around gtk_box_reorder_child */ static gint luaH_box_reorder_child(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint pos = luaL_checknumber(L, 3); gtk_box_reorder_child(GTK_BOX(w->widget), GTK_WIDGET(child->widget), pos); return 0; } static gint luaH_box_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) LUAKIT_WIDGET_CONTAINER_INDEX_COMMON(w) /* push class methods */ PF_CASE(PACK, luaH_box_pack) PF_CASE(REORDER, luaH_box_reorder_child) /* push boolean properties */ PB_CASE(HOMOGENEOUS, gtk_box_get_homogeneous(GTK_BOX(w->widget))) /* push string properties */ PN_CASE(SPACING, gtk_box_get_spacing(GTK_BOX(w->widget))) default: break; } return 0; } static gint luaH_box_newindex(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) case L_TK_HOMOGENEOUS: gtk_box_set_homogeneous(GTK_BOX(w->widget), luaH_checkboolean(L, 3)); break; case L_TK_SPACING: gtk_box_set_spacing(GTK_BOX(w->widget), luaL_checknumber(L, 3)); break; default: return 0; } return luaH_object_property_signal(L, 1, token); } widget_t * widget_box(widget_t *w, luakit_token_t token) { w->index = luaH_box_index; w->newindex = luaH_box_newindex; w->destructor = widget_destructor; w->widget = (token == L_TK_VBOX) ? gtk_vbox_new(FALSE, 0) : gtk_hbox_new(FALSE, 0); g_object_connect(G_OBJECT(w->widget), "signal::add", G_CALLBACK(add_cb), w, "signal::parent-set", G_CALLBACK(parent_set_cb), w, "signal::remove", G_CALLBACK(remove_cb), w, NULL); gtk_widget_show(w->widget); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/common.c000066400000000000000000000147011202446317700170350ustar00rootroot00000000000000/* * widgets/common.c - common widget functions or callbacks * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include "luah.h" #include "globalconf.h" #include "common/luaobject.h" #include "common/lualib.h" #include "widgets/common.h" gboolean key_press_cb(GtkWidget* UNUSED(win), GdkEventKey *ev, widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_modifier_table_push(L, ev->state); luaH_keystr_push(L, ev->keyval); gint ret = luaH_object_emit_signal(L, -3, "key-press", 2, 1); gboolean catch = ret && lua_toboolean(L, -1) ? TRUE : FALSE; lua_pop(L, ret + 1); return catch; } gboolean button_cb(GtkWidget* UNUSED(win), GdkEventButton *ev, widget_t *w) { gint ret; lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_modifier_table_push(L, ev->state); lua_pushinteger(L, ev->button); switch (ev->type) { case GDK_2BUTTON_PRESS: ret = luaH_object_emit_signal(L, -3, "button-double-click", 2, 1); break; case GDK_BUTTON_RELEASE: ret = luaH_object_emit_signal(L, -3, "button-release", 2, 1); break; default: ret = luaH_object_emit_signal(L, -3, "button-press", 2, 1); break; } gboolean catch = ret && lua_toboolean(L, -1) ? TRUE : FALSE; lua_pop(L, ret + 1); return catch; } gboolean focus_cb(GtkWidget* UNUSED(win), GdkEventFocus *ev, widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); gint ret; if (ev->in) ret = luaH_object_emit_signal(L, -1, "focus", 0, 1); else ret = luaH_object_emit_signal(L, -1, "unfocus", 0, 1); /* catch focus event */ if (ret && lua_toboolean(L, -1)) { lua_pop(L, ret + 1); return TRUE; } lua_pop(L, ret + 1); /* propagate event further */ return FALSE; } /* gtk container add callback */ void add_cb(GtkContainer* UNUSED(c), GtkWidget *widget, widget_t *w) { widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); luaH_object_emit_signal(L, -2, "add", 1, 0); lua_pop(L, 1); } /* gtk container remove callback */ void remove_cb(GtkContainer* UNUSED(c), GtkWidget *widget, widget_t *w) { widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); luaH_object_emit_signal(L, -2, "remove", 1, 0); lua_pop(L, 1); } void parent_set_cb(GtkWidget *widget, GtkObject* UNUSED(old), widget_t *w) { lua_State *L = globalconf.L; widget_t *parent = NULL; GtkContainer *new; g_object_get(G_OBJECT(widget), "parent", &new, NULL); luaH_object_push(L, w->ref); if (new && (parent = GOBJECT_TO_LUAKIT_WIDGET(new))) luaH_object_push(L, parent->ref); else lua_pushnil(L); luaH_object_emit_signal(L, -2, "parent-set", 1, 0); lua_pop(L, 1); } gboolean true_cb() { return TRUE; } /* set child method for gtk container widgets */ gint luaH_widget_set_child(lua_State *L, widget_t *w) { widget_t *child = luaH_checkwidgetornil(L, 3); /* remove old child */ GtkWidget *widget = gtk_bin_get_child(GTK_BIN(w->widget)); if (widget) { g_object_ref(G_OBJECT(widget)); gtk_container_remove(GTK_CONTAINER(w->widget), GTK_WIDGET(widget)); } /* add new child to container */ if (child) gtk_container_add(GTK_CONTAINER(w->widget), GTK_WIDGET(child->widget)); return 0; } /* get child method for gtk container widgets */ gint luaH_widget_get_child(lua_State *L, widget_t *w) { GtkWidget *widget = gtk_bin_get_child(GTK_BIN(w->widget)); if (!widget) return 0; widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(w->widget); luaH_object_push(L, child->ref); return 1; } gint luaH_widget_remove(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); g_object_ref(G_OBJECT(child->widget)); gtk_container_remove(GTK_CONTAINER(w->widget), GTK_WIDGET(child->widget)); return 0; } gint luaH_widget_get_children(lua_State *L, widget_t *w) { GList *children = gtk_container_get_children(GTK_CONTAINER(w->widget)); GList *iter = children; /* push table of the containers children onto the stack */ lua_newtable(L); for (gint i = 1; iter; iter = iter->next) { luaH_object_push(L, GOBJECT_TO_LUAKIT_WIDGET(iter->data)->ref); lua_rawseti(L, -2, i++); } g_list_free(children); return 1; } gint luaH_widget_show(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); gtk_widget_show(w->widget); return 0; } gint luaH_widget_hide(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); gtk_widget_hide(w->widget); return 0; } gint luaH_widget_set_visible(lua_State *L, widget_t *w) { gboolean visible = luaH_checkboolean(L, 3); gtk_widget_set_visible(w->widget, visible); if (visible && w->info->tok == L_TK_WINDOW) gdk_window_set_events(gtk_widget_get_window(w->widget), GDK_ALL_EVENTS_MASK); return 0; } gint luaH_widget_get_visible(lua_State *L, widget_t *w) { lua_pushboolean(L, gtk_widget_get_visible(w->widget)); return 1; } gint luaH_widget_focus(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); gtk_widget_grab_focus(w->widget); return 0; } gint luaH_widget_destroy(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); if (w->destructor) w->destructor(w); w->destructor = NULL; luaH_object_unref(L, w->ref); return 0; } void widget_destructor(widget_t *w) { debug("destroy %p (%s)", w, w->info->name); if (w->widget) gtk_widget_destroy(GTK_WIDGET(w->widget)); w->widget = NULL; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/common.h000066400000000000000000000072021202446317700170400ustar00rootroot00000000000000/* * widgets/common.h - common widget functions or callbacks * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef LUAKIT_WIDGETS_COMMON_H #define LUAKIT_WIDGETS_COMMON_H #include "clib/widget.h" #define LUAKIT_WIDGET_INDEX_COMMON(widget) \ case L_TK_VISIBLE: \ return luaH_widget_get_visible(L, widget); \ case L_TK_SHOW: \ lua_pushcfunction(L, luaH_widget_show); \ return 1; \ case L_TK_HIDE: \ lua_pushcfunction(L, luaH_widget_hide); \ return 1; \ case L_TK_FOCUS: \ lua_pushcfunction(L, luaH_widget_focus); \ return 1; \ case L_TK_DESTROY: \ lua_pushcfunction(L, luaH_widget_destroy); \ return 1; #define LUAKIT_WIDGET_NEWINDEX_COMMON(widget) \ case L_TK_VISIBLE: \ return luaH_widget_set_visible(L, widget); #define LUAKIT_WIDGET_BIN_INDEX_COMMON(widget) \ case L_TK_CHILD: \ return luaH_widget_get_child(L, widget); #define LUAKIT_WIDGET_BIN_NEWINDEX_COMMON(widget) \ case L_TK_CHILD: \ luaH_widget_set_child(L, widget); \ break; #define LUAKIT_WIDGET_CONTAINER_INDEX_COMMON(widget) \ case L_TK_REMOVE: \ lua_pushcfunction(L, luaH_widget_remove); \ return 1; \ case L_TK_CHILDREN: \ return luaH_widget_get_children(L, widget); #define LUAKIT_WIDGET_SIGNAL_COMMON(w) \ "signal::focus-in-event", G_CALLBACK(focus_cb), w, \ "signal::focus-out-event", G_CALLBACK(focus_cb), w, \ "signal::parent-set", G_CALLBACK(parent_set_cb), w, gboolean button_cb(GtkWidget*, GdkEventButton*, widget_t*); gboolean focus_cb(GtkWidget*, GdkEventFocus*, widget_t*); gboolean key_press_cb(GtkWidget*, GdkEventKey*, widget_t*); gboolean key_release_cb(GtkWidget*, GdkEventKey*, widget_t*); gboolean true_cb(); gint luaH_widget_destroy(lua_State*); gint luaH_widget_focus(lua_State*); gint luaH_widget_get_child(lua_State*, widget_t*); gint luaH_widget_get_children(lua_State*, widget_t*); gint luaH_widget_hide(lua_State*); gint luaH_widget_remove(lua_State*); gint luaH_widget_set_child(lua_State*, widget_t*); gint luaH_widget_show(lua_State*); gint luaH_widget_get_visible(lua_State *L, widget_t*); gint luaH_widget_set_visible(lua_State *L, widget_t*); void add_cb(GtkContainer*, GtkWidget*, widget_t*); void parent_set_cb(GtkWidget*, GtkObject*, widget_t*); void remove_cb(GtkContainer*, GtkWidget*, widget_t*); void widget_destructor(widget_t*); #endif // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/entry.c000066400000000000000000000141601202446317700167050ustar00rootroot00000000000000/* * widgets/entry.c - gtk entry widget wrapper * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_entry_insert(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); /* get insert position (or append text) */ gint pos = -1, idx = 2; if (lua_gettop(L) > 2) { pos = luaL_checknumber(L, idx++); if (pos > 0) pos--; /* correct lua index */ } gtk_editable_insert_text(GTK_EDITABLE(w->widget), luaL_checkstring(L, idx), -1, &pos); return 0; } static gint luaH_entry_select_region(lua_State* L) { widget_t *w = luaH_checkwidget(L, 1); gint startpos = luaL_checknumber(L, 2); gint endpos = -1; if(lua_gettop(L) > 2) endpos = luaL_checknumber(L, 3); gtk_editable_select_region(GTK_EDITABLE(w->widget), startpos, endpos); return 0; } static gint luaH_entry_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) /* push class methods */ PF_CASE(INSERT, luaH_entry_insert) PF_CASE(SELECT_REGION, luaH_entry_select_region) /* push integer properties */ PI_CASE(POSITION, gtk_editable_get_position(GTK_EDITABLE(w->widget))) /* push string properties */ PS_CASE(TEXT, gtk_entry_get_text(GTK_ENTRY(w->widget))) PS_CASE(FG, g_object_get_data(G_OBJECT(w->widget), "fg")) PS_CASE(BG, g_object_get_data(G_OBJECT(w->widget), "bg")) PS_CASE(FONT, g_object_get_data(G_OBJECT(w->widget), "font")) /* push boolean properties */ PB_CASE(SHOW_FRAME, gtk_entry_get_has_frame(GTK_ENTRY(w->widget))) default: break; } return 0; } static gint luaH_entry_newindex(lua_State *L, widget_t *w, luakit_token_t token) { size_t len; const gchar *tmp; GdkColor c; PangoFontDescription *font; switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) case L_TK_TEXT: gtk_entry_set_text(GTK_ENTRY(w->widget), luaL_checklstring(L, 3, &len)); break; case L_TK_FG: case L_TK_BG: tmp = luaL_checklstring(L, 3, &len); if (!gdk_color_parse(tmp, &c)) luaL_argerror(L, 3, "unable to parse color"); if (token == L_TK_FG) { gtk_widget_modify_text(GTK_WIDGET(w->widget), GTK_STATE_NORMAL, &c); g_object_set_data_full(G_OBJECT(w->widget), "fg", g_strdup(tmp), g_free); } else { gtk_widget_modify_base(GTK_WIDGET(w->widget), GTK_STATE_NORMAL, &c); g_object_set_data_full(G_OBJECT(w->widget), "bg", g_strdup(tmp), g_free); } break; case L_TK_SHOW_FRAME: gtk_entry_set_has_frame(GTK_ENTRY(w->widget), luaH_checkboolean(L, 3)); break; case L_TK_POSITION: gtk_editable_set_position(GTK_EDITABLE(w->widget), luaL_checknumber(L, 3)); break; case L_TK_FONT: tmp = luaL_checklstring(L, 3, &len); font = pango_font_description_from_string(tmp); gtk_widget_modify_font(GTK_WIDGET(w->widget), font); g_object_set_data_full(G_OBJECT(w->widget), "font", g_strdup(tmp), g_free); break; default: warn("unknown property: %s", luaL_checkstring(L, 2)); return 0; } return luaH_object_property_signal(L, 1, token); } static void activate_cb(GtkEntry* UNUSED(e), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "activate", 0, 0); lua_pop(L, 1); } static void changed_cb(widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "changed", 0, 0); lua_pop(L, 1); } static void position_cb(GtkEntry* UNUSED(e), GParamSpec* UNUSED(ps), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "property::position", 0, 0); lua_pop(L, 1); } widget_t * widget_entry(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_entry_index; w->newindex = luaH_entry_newindex; w->destructor = widget_destructor; /* create gtk label widget as main widget */ w->widget = gtk_entry_new(); /* setup default settings */ gtk_entry_set_inner_border(GTK_ENTRY(w->widget), NULL); g_object_connect(G_OBJECT(w->widget), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::activate", G_CALLBACK(activate_cb), w, "signal::key-press-event", G_CALLBACK(key_press_cb), w, "signal::notify::cursor-position", G_CALLBACK(position_cb), w, // The following signals replace the old "signal::changed", since that // does not allow for the selection to be changed in it's callback. "swapped-signal-after::backspace", G_CALLBACK(changed_cb), w, "swapped-signal-after::delete-from-cursor", G_CALLBACK(changed_cb), w, "swapped-signal-after::insert-at-cursor", G_CALLBACK(changed_cb), w, "swapped-signal-after::paste-clipboard", G_CALLBACK(changed_cb), w, "swapped-signal::button-release-event", G_CALLBACK(changed_cb), w, NULL); // Further signal to replace "signal::changed" GtkEntry* entry = GTK_ENTRY(w->widget); g_object_connect(G_OBJECT(entry->im_context), "swapped-signal::commit", G_CALLBACK(changed_cb), w, NULL); gtk_widget_show(w->widget); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/eventbox.c000066400000000000000000000050271202446317700174000ustar00rootroot00000000000000/* * widgets/eventbox.c - gtk eventbox widget * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_eventbox_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) LUAKIT_WIDGET_BIN_INDEX_COMMON(w) LUAKIT_WIDGET_CONTAINER_INDEX_COMMON(w) /* push string properties */ PS_CASE(BG, g_object_get_data(G_OBJECT(w->widget), "bg")) default: break; } return 0; } static gint luaH_eventbox_newindex(lua_State *L, widget_t *w, luakit_token_t token) { size_t len; const gchar *tmp; GdkColor c; switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) LUAKIT_WIDGET_BIN_NEWINDEX_COMMON(w) case L_TK_BG: tmp = luaL_checklstring(L, 3, &len); if (!gdk_color_parse(tmp, &c)) luaL_argerror(L, 3, "unable to parse colour"); gtk_widget_modify_bg(GTK_WIDGET(w->widget), GTK_STATE_NORMAL, &c); g_object_set_data_full(G_OBJECT(w->widget), "bg", g_strdup(tmp), g_free); break; default: return 0; } return luaH_object_property_signal(L, 1, token); } widget_t * widget_eventbox(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_eventbox_index; w->newindex = luaH_eventbox_newindex; w->destructor = widget_destructor; w->widget = gtk_event_box_new(); gtk_widget_show(w->widget); g_object_connect(G_OBJECT(w->widget), "signal::add", G_CALLBACK(add_cb), w, "signal::button-press-event", G_CALLBACK(button_cb), w, "signal::button-release-event", G_CALLBACK(button_cb), w, "signal::parent-set", G_CALLBACK(parent_set_cb), w, "signal::remove", G_CALLBACK(remove_cb), w, NULL); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/label.c000066400000000000000000000135171202446317700166300ustar00rootroot00000000000000/* * widgets/label.c - gtk text area widget * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_label_get_align(lua_State *L, widget_t *w) { gfloat xalign, yalign; gtk_misc_get_alignment(GTK_MISC(w->widget), &xalign, &yalign); lua_createtable(L, 0, 2); /* set align.x */ lua_pushliteral(L, "x"); lua_pushnumber(L, xalign); lua_rawset(L, -3); /* set align.y */ lua_pushliteral(L, "y"); lua_pushnumber(L, yalign); lua_rawset(L, -3); return 1; } static gint luaH_label_set_align(lua_State *L, widget_t *w) { luaH_checktable(L, 3); /* get old alignment values */ gfloat xalign, yalign; gtk_misc_get_alignment(GTK_MISC(w->widget), &xalign, &yalign); /* get align.x */ if (luaH_rawfield(L, 3, "x")) { xalign = (gfloat) lua_tonumber(L, -1); lua_pop(L, 1); } /* get align.y */ if (luaH_rawfield(L, 3, "y")) { yalign = (gfloat) lua_tonumber(L, -1); lua_pop(L, 1); } gtk_misc_set_alignment(GTK_MISC(w->widget), xalign, yalign); return 0; } static gint luaH_label_get_padding(lua_State *L, widget_t *w) { gint xpad, ypad; gtk_misc_get_padding(GTK_MISC(w->widget), &xpad, &ypad); lua_createtable(L, 0, 2); /* set padding.x */ lua_pushliteral(L, "x"); lua_pushnumber(L, xpad); lua_rawset(L, -3); /* set padding.y */ lua_pushliteral(L, "y"); lua_pushnumber(L, ypad); lua_rawset(L, -3); return 1; } static gint luaH_label_set_padding(lua_State *L, widget_t *w) { luaH_checktable(L, 3); /* get old padding values */ gint xpad = 0, ypad = 0; gtk_misc_get_padding(GTK_MISC(w->widget), &xpad, &ypad); /* get padding.x */ if (luaH_rawfield(L, 3, "x")) { xpad = (gint) lua_tonumber(L, -1); lua_pop(L, 1); } /* get padding.y */ if (luaH_rawfield(L, 3, "y")) { ypad = (gint) lua_tonumber(L, -1); lua_pop(L, 1); } gtk_misc_set_padding(GTK_MISC(w->widget), xpad, ypad); return 0; } static gint luaH_label_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) case L_TK_PADDING: return luaH_label_get_padding(L, w); case L_TK_ALIGN: return luaH_label_get_align(L, w); /* push string properties */ PS_CASE(FG, g_object_get_data(G_OBJECT(w->widget), "fg")) PS_CASE(FONT, g_object_get_data(G_OBJECT(w->widget), "font")) PS_CASE(TEXT, gtk_label_get_label(GTK_LABEL(w->widget))) /* push boolean properties */ PB_CASE(SELECTABLE, gtk_label_get_selectable(GTK_LABEL(w->widget))) /* push integer properties */ PI_CASE(WIDTH, gtk_label_get_width_chars(GTK_LABEL(w->widget))) default: break; } return 0; } static gint luaH_label_newindex(lua_State *L, widget_t *w, luakit_token_t token) { size_t len; const gchar *tmp; GdkColor c; PangoFontDescription *font; switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) case L_TK_PADDING: return luaH_label_set_padding(L, w); case L_TK_ALIGN: return luaH_label_set_align(L, w); case L_TK_TEXT: gtk_label_set_markup(GTK_LABEL(w->widget), luaL_checklstring(L, 3, &len)); break; case L_TK_FG: tmp = luaL_checklstring(L, 3, &len); if (!gdk_color_parse(tmp, &c)) { warn("invalid color: %s", tmp); return 0; } gtk_widget_modify_fg(GTK_WIDGET(w->widget), GTK_STATE_NORMAL, &c); g_object_set_data_full(G_OBJECT(w->widget), "fg", g_strdup(tmp), g_free); break; case L_TK_FONT: tmp = luaL_checklstring(L, 3, &len); font = pango_font_description_from_string(tmp); gtk_widget_modify_font(GTK_WIDGET(w->widget), font); pango_font_description_free(font); g_object_set_data_full(G_OBJECT(w->widget), "font", g_strdup(tmp), g_free); break; case L_TK_SELECTABLE: gtk_label_set_selectable(GTK_LABEL(w->widget), luaH_checkboolean(L, 3)); break; case L_TK_WIDTH: gtk_label_set_width_chars(GTK_LABEL(w->widget), (gint)luaL_checknumber(L, 3)); return 0; default: warn("unknown property: %s", luaL_checkstring(L, 2)); return 0; } return luaH_object_property_signal(L, 1, token); } widget_t * widget_label(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_label_index; w->newindex = luaH_label_newindex; w->destructor = widget_destructor; /* create gtk label widget as main widget */ w->widget = gtk_label_new(NULL); /* setup default settings */ gtk_label_set_selectable(GTK_LABEL(w->widget), FALSE); gtk_label_set_use_markup(GTK_LABEL(w->widget), TRUE); gtk_misc_set_alignment(GTK_MISC(w->widget), 0, 0); gtk_misc_set_padding(GTK_MISC(w->widget), 2, 2); g_object_connect(G_OBJECT(w->widget), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::key-press-event", G_CALLBACK(key_press_cb), w, NULL); gtk_widget_show(w->widget); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/notebook.c000066400000000000000000000205201202446317700173610ustar00rootroot00000000000000/* * widgets/notebook.c - gtk notebook widget * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_notebook_current(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); gint n = gtk_notebook_get_n_pages(GTK_NOTEBOOK(w->widget)); if (n == 1) lua_pushnumber(L, 1); else lua_pushnumber(L, gtk_notebook_get_current_page( GTK_NOTEBOOK(w->widget)) + 1); return 1; } static gint luaH_notebook_atindex(lua_State *L, widget_t *w, gint idx) { /* correct index */ if (idx != -1) idx--; GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(w->widget), idx); if (!widget) return 0; widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); luaH_object_push(L, child->ref); return 1; } static gint luaH_notebook_indexof(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint i = gtk_notebook_page_num(GTK_NOTEBOOK(w->widget), child->widget); /* return index or nil */ if (!++i) return 0; lua_pushnumber(L, i); return 1; } static gint luaH_notebook_remove(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint i = gtk_notebook_page_num(GTK_NOTEBOOK(w->widget), child->widget); if (i == -1) luaL_argerror(L, 2, "child not in notebook"); GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(w->widget), i); g_object_ref(G_OBJECT(widget)); gtk_notebook_remove_page(GTK_NOTEBOOK(w->widget), i); return 0; } /* Inserts a widget into the notebook widget at an index */ static gint luaH_notebook_insert(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); /* get insert position (or append page) */ gint pos = -1, idx = 2; if (lua_gettop(L) > 2) { pos = luaL_checknumber(L, idx++); if (pos > 0) pos--; /* correct lua index */ } pos = gtk_notebook_insert_page(GTK_NOTEBOOK(w->widget), GTK_WIDGET(luaH_checkwidget(L, idx)->widget), NULL, pos); /* failed to insert page */ if (pos == -1) return 0; /* return new (lua corrected) index */ lua_pushnumber(L, ++pos); return 1; } /* Return the number of widgets in the notebook */ static gint luaH_notebook_count(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); lua_pushnumber(L, gtk_notebook_get_n_pages(GTK_NOTEBOOK(w->widget))); return 1; } static gint luaH_notebook_set_title(lua_State *L) { size_t len; widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); const gchar *title = luaL_checklstring(L, 3, &len); gtk_notebook_set_tab_label_text(GTK_NOTEBOOK(w->widget), child->widget, title); return 0; } static gint luaH_notebook_get_title(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); lua_pushstring(L, gtk_notebook_get_tab_label_text( GTK_NOTEBOOK(w->widget), child->widget)); return 1; } static gint luaH_notebook_switch(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); gint i = luaL_checknumber(L, 2); /* correct index */ if (i != -1) i--; gtk_notebook_set_current_page(GTK_NOTEBOOK(w->widget), i); lua_pushnumber(L, gtk_notebook_get_current_page(GTK_NOTEBOOK(w->widget))); return 1; } static gint luaH_notebook_reorder(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint i = luaL_checknumber(L, 3); /* correct lua index */ if (i != -1) i--; gtk_notebook_reorder_child(GTK_NOTEBOOK(w->widget), child->widget, i); lua_pushnumber(L, gtk_notebook_page_num(GTK_NOTEBOOK(w->widget), child->widget)); return 1; } static gint luaH_notebook_index(lua_State *L, widget_t *w, luakit_token_t token) { /* handle numerical index lookups */ if (token == L_TK_UNKNOWN && lua_isnumber(L, 2)) return luaH_notebook_atindex(L, w, (gint)luaL_checknumber(L, 2)); switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) /* push class methods */ PF_CASE(COUNT, luaH_notebook_count) PF_CASE(CURRENT, luaH_notebook_current) PF_CASE(GET_TITLE, luaH_notebook_get_title) PF_CASE(INDEXOF, luaH_notebook_indexof) PF_CASE(INSERT, luaH_notebook_insert) PF_CASE(REMOVE, luaH_notebook_remove) PF_CASE(SET_TITLE, luaH_notebook_set_title) PF_CASE(SWITCH, luaH_notebook_switch) PF_CASE(REORDER, luaH_notebook_reorder) case L_TK_CHILDREN: return luaH_widget_get_children(L, w); /* push boolean properties */ PB_CASE(SHOW_TABS, gtk_notebook_get_show_tabs(GTK_NOTEBOOK(w->widget))) PB_CASE(SHOW_BORDER, gtk_notebook_get_show_border(GTK_NOTEBOOK(w->widget))) default: break; } return 0; } static gint luaH_notebook_newindex(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) case L_TK_SHOW_TABS: gtk_notebook_set_show_tabs(GTK_NOTEBOOK(w->widget), luaH_checkboolean(L, 3)); break; case L_TK_SHOW_BORDER: gtk_notebook_set_show_border(GTK_NOTEBOOK(w->widget), luaH_checkboolean(L, 3)); break; default: return 0; } return luaH_object_property_signal(L, 1, token); } static void page_added_cb(GtkNotebook* UNUSED(n), GtkWidget *widget, guint i, widget_t *w) { widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); lua_pushnumber(L, i + 1); luaH_object_emit_signal(L, -3, "page-added", 2, 0); lua_pop(L, 1); } static void page_removed_cb(GtkNotebook* UNUSED(n), GtkWidget *widget, guint UNUSED(i), widget_t *w) { widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); luaH_object_emit_signal(L, -2, "page-removed", 1, 0); lua_pop(L, 1); } static void switch_cb(GtkNotebook *n, GtkNotebookPage* UNUSED(p), guint i, widget_t *w) { GtkWidget *widget = gtk_notebook_get_nth_page(GTK_NOTEBOOK(n), i); widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); lua_pushnumber(L, i + 1); luaH_object_emit_signal(L, -3, "switch-page", 2, 0); lua_pop(L, 1); } static void reorder_cb(GtkNotebook* UNUSED(n), GtkWidget *widget, guint i, widget_t *w) { widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_push(L, child->ref); lua_pushnumber(L, i + 1); luaH_object_emit_signal(L, -3, "page-reordered", 2, 0); lua_pop(L, 1); } widget_t * widget_notebook(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_notebook_index; w->newindex = luaH_notebook_newindex; w->destructor = widget_destructor; /* create and setup notebook widget */ w->widget = gtk_notebook_new(); gtk_notebook_set_show_border(GTK_NOTEBOOK(w->widget), FALSE); gtk_notebook_set_scrollable(GTK_NOTEBOOK(w->widget), TRUE); g_object_connect(G_OBJECT(w->widget), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::key-press-event", G_CALLBACK(key_press_cb), w, "signal::page-added", G_CALLBACK(page_added_cb), w, "signal::page-removed", G_CALLBACK(page_removed_cb), w, "signal::page-reordered", G_CALLBACK(reorder_cb), w, "signal::switch-page", G_CALLBACK(switch_cb), w, NULL); gtk_widget_show(w->widget); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/paned.c000066400000000000000000000066671202446317700166500ustar00rootroot00000000000000/* * widgets/paned.c - gtk paned container widget * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" static gint luaH_paned_pack(lua_State *L) { widget_t *w = luaH_checkwidget(L, 1); widget_t *child = luaH_checkwidget(L, 2); gint top = lua_gettop(L); gboolean resize = TRUE, shrink = TRUE; if (top > 2 && !lua_isnil(L, 3)) { luaH_checktable(L, 3); if (luaH_rawfield(L, 3, "resize")) resize = lua_toboolean(L, -1) ? TRUE : FALSE; if (luaH_rawfield(L, 3, "shrink")) shrink = lua_toboolean(L, -1) ? TRUE : FALSE; lua_settop(L, top); } /* get packing position from C closure upvalue */ luakit_token_t t = (luakit_token_t)lua_tonumber(L, lua_upvalueindex(1)); if (t == L_TK_PACK1) gtk_paned_pack1(GTK_PANED(w->widget), GTK_WIDGET(child->widget), resize, shrink); else gtk_paned_pack2(GTK_PANED(w->widget), GTK_WIDGET(child->widget), resize, shrink); return 0; } static gint luaH_paned_get_child(lua_State *L, widget_t *w, gint n) { GtkWidget *widget = NULL; if (n == 1) widget = gtk_paned_get_child1(GTK_PANED(w->widget)); else widget = gtk_paned_get_child2(GTK_PANED(w->widget)); if (!widget) return 0; widget_t *child = GOBJECT_TO_LUAKIT_WIDGET(widget); luaH_object_push(L, child->ref); return 1; } static gint luaH_paned_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) LUAKIT_WIDGET_CONTAINER_INDEX_COMMON(w) /* push paned widget methods */ case L_TK_PACK1: case L_TK_PACK2: lua_pushnumber(L, (gint) token); lua_pushcclosure(L, luaH_paned_pack, 1); return 1; case L_TK_TOP: case L_TK_LEFT: return luaH_paned_get_child(L, w, 1); case L_TK_BOTTOM: case L_TK_RIGHT: return luaH_paned_get_child(L, w, 2); default: break; } return 0; } static gint luaH_paned_newindex(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) default: break; } return 0; } widget_t * widget_paned(widget_t *w, luakit_token_t token) { w->index = luaH_paned_index; w->newindex = luaH_paned_newindex; w->destructor = widget_destructor; w->widget = (token == L_TK_VPANED) ? gtk_vpaned_new() : gtk_hpaned_new(); g_object_connect(G_OBJECT(w->widget), "signal::add", G_CALLBACK(add_cb), w, "signal::parent-set", G_CALLBACK(parent_set_cb), w, "signal::remove", G_CALLBACK(remove_cb), w, NULL); gtk_widget_show(w->widget); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/socket.c000066400000000000000000000046541202446317700170430ustar00rootroot00000000000000/* * widgets/socket.c - gtk socket widget * * Copyright © 2010-2011 Fabian Streitel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "luah.h" #include "widgets/common.h" #include static void plug_added_cb(GtkSocket* UNUSED(socket), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "plug-added", 1, 0); } static gboolean plug_removed_cb(GtkSocket* UNUSED(socket), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "plug-removed", 1, 0); return FALSE; } static gint luaH_socket_index(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) /* push integer methods */ PI_CASE(ID, (int) gtk_socket_get_id(GTK_SOCKET(w->widget))) /* push boolean methods */ PB_CASE(PLUGGED, gtk_socket_get_plug_window(GTK_SOCKET(w->widget))!=NULL) default: break; } return 0; } static gint luaH_socket_newindex(lua_State *L, widget_t *w, luakit_token_t token) { switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) case L_TK_ID: gtk_socket_add_id(GTK_SOCKET(w->widget), (GdkNativeWindow) luaL_checkint(L, 2)); break; default: break; } return 0; } widget_t * widget_socket(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_socket_index; w->newindex = luaH_socket_newindex; w->destructor = widget_destructor; w->widget = gtk_socket_new(); gtk_widget_show(w->widget); g_object_connect(G_OBJECT(w->widget), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::plug-added", G_CALLBACK(plug_added_cb), w, "signal::plug-removed", G_CALLBACK(plug_removed_cb), w, NULL); return w; } luakit-2012.09.13-r1/widgets/webview.c000066400000000000000000001016711202446317700172200ustar00rootroot00000000000000/* * widgets/webview.c - webkit webview widget * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include #include #include "globalconf.h" #include "widgets/common.h" #include "clib/soup/soup.h" #include "common/property.h" #include "luah.h" typedef struct { /** The parent widget_t struct */ widget_t *widget; /** The webview widget */ WebKitWebView *view; /** The GtkScrolledWindow for the webview widget */ GtkScrolledWindow *win; /** Current webview uri */ gchar *uri; /** Currently hovered uri */ gchar *hover; /** Scrollbar hide signal id */ gulong hide_id; /** Inspector properties */ WebKitWebInspector *inspector; /** Inspector webview widget */ widget_t *iview; } webview_data_t; #define luaH_checkwvdata(L, udx) ((webview_data_t*)(luaH_checkwebview(L, udx)->data)) static struct { GSList *refs; GSList *items; } last_popup = { NULL, NULL }; property_t webview_properties[] = { { L_TK_CUSTOM_ENCODING, "custom-encoding", CHAR, TRUE }, { L_TK_EDITABLE, "editable", BOOL, TRUE }, { L_TK_ENCODING, "encoding", CHAR, FALSE }, { L_TK_FULL_CONTENT_ZOOM, "full-content-zoom", BOOL, TRUE }, { L_TK_ICON_URI, "icon-uri", CHAR, FALSE }, { L_TK_PROGRESS, "progress", DOUBLE, FALSE }, { L_TK_TITLE, "title", CHAR, FALSE }, { L_TK_TRANSPARENT, "transparent", BOOL, TRUE }, { L_TK_URI, "uri", CHAR, FALSE }, /* dummy */ { L_TK_ZOOM_LEVEL, "zoom-level", FLOAT, TRUE }, { 0, NULL, 0, 0 }, }; property_t webview_settings_properties[] = { { L_TK_AUTO_LOAD_IMAGES, "auto-load-images", BOOL, TRUE }, { L_TK_AUTO_RESIZE_WINDOW, "auto-resize-window", BOOL, TRUE }, { L_TK_AUTO_SHRINK_IMAGES, "auto-shrink-images", BOOL, TRUE }, { L_TK_CURSIVE_FONT_FAMILY, "cursive-font-family", CHAR, TRUE }, { L_TK_DEFAULT_ENCODING, "default-encoding", CHAR, TRUE }, { L_TK_DEFAULT_FONT_FAMILY, "default-font-family", CHAR, TRUE }, { L_TK_DEFAULT_FONT_SIZE, "default-font-size", INT, TRUE }, { L_TK_DEFAULT_MONOSPACE_FONT_SIZE, "default-monospace-font-size", INT, TRUE }, { L_TK_ENABLE_CARET_BROWSING, "enable-caret-browsing", BOOL, TRUE }, { L_TK_ENABLE_DEFAULT_CONTEXT_MENU, "enable-default-context-menu", BOOL, TRUE }, { L_TK_ENABLE_DEVELOPER_EXTRAS, "enable-developer-extras", BOOL, TRUE }, { L_TK_ENABLE_DOM_PASTE, "enable-dom-paste", BOOL, TRUE }, { L_TK_ENABLE_FILE_ACCESS_FROM_FILE_URIS, "enable-file-access-from-file-uris", BOOL, TRUE }, { L_TK_ENABLE_HTML5_DATABASE, "enable-html5-database", BOOL, TRUE }, { L_TK_ENABLE_HTML5_LOCAL_STORAGE, "enable-html5-local-storage", BOOL, TRUE }, { L_TK_ENABLE_JAVA_APPLET, "enable-java-applet", BOOL, TRUE }, { L_TK_ENABLE_OFFLINE_WEB_APPLICATION_CACHE, "enable-offline-web-application-cache", BOOL, TRUE }, { L_TK_ENABLE_PAGE_CACHE, "enable-page-cache", BOOL, TRUE }, { L_TK_ENABLE_PLUGINS, "enable-plugins", BOOL, TRUE }, { L_TK_ENABLE_PRIVATE_BROWSING, "enable-private-browsing", BOOL, TRUE }, { L_TK_ENABLE_SCRIPTS, "enable-scripts", BOOL, TRUE }, { L_TK_ENABLE_SITE_SPECIFIC_QUIRKS, "enable-site-specific-quirks", BOOL, TRUE }, { L_TK_ENABLE_SPATIAL_NAVIGATION, "enable-spatial-navigation", BOOL, TRUE }, { L_TK_ENABLE_SPELL_CHECKING, "enable-spell-checking", BOOL, TRUE }, { L_TK_ENABLE_UNIVERSAL_ACCESS_FROM_FILE_URIS, "enable-universal-access-from-file-uris", BOOL, TRUE }, { L_TK_ENABLE_XSS_AUDITOR, "enable-xss-auditor", BOOL, TRUE }, { L_TK_ENFORCE_96_DPI, "enforce-96-dpi", BOOL, TRUE }, { L_TK_FANTASY_FONT_FAMILY, "fantasy-font-family", CHAR, TRUE }, { L_TK_JAVASCRIPT_CAN_ACCESS_CLIPBOARD, "javascript-can-access-clipboard", BOOL, TRUE }, { L_TK_JAVASCRIPT_CAN_OPEN_WINDOWS_AUTOMATICALLY, "javascript-can-open-windows-automatically", BOOL, TRUE }, { L_TK_MINIMUM_FONT_SIZE, "minimum-font-size", INT, TRUE }, { L_TK_MINIMUM_LOGICAL_FONT_SIZE, "minimum-logical-font-size", INT, TRUE }, { L_TK_MONOSPACE_FONT_FAMILY, "monospace-font-family", CHAR, TRUE }, { L_TK_PRINT_BACKGROUNDS, "print-backgrounds", BOOL, TRUE }, { L_TK_RESIZABLE_TEXT_AREAS, "resizable-text-areas", BOOL, TRUE }, { L_TK_SANS_SERIF_FONT_FAMILY, "sans-serif-font-family", CHAR, TRUE }, { L_TK_SERIF_FONT_FAMILY, "serif-font-family", CHAR, TRUE }, { L_TK_SPELL_CHECKING_LANGUAGES, "spell-checking-languages", CHAR, TRUE }, { L_TK_TAB_KEY_CYCLES_THROUGH_ELEMENTS, "tab-key-cycles-through-elements", BOOL, TRUE }, { L_TK_USER_AGENT, "user-agent", CHAR, TRUE }, { L_TK_USER_STYLESHEET_URI, "user-stylesheet-uri", CHAR, TRUE }, { L_TK_ZOOM_STEP, "zoom-step", FLOAT, TRUE }, { 0, NULL, 0, 0 }, }; static widget_t* luaH_checkwebview(lua_State *L, gint udx) { widget_t *w = luaH_checkwidget(L, udx); if (w->info->tok != L_TK_WEBVIEW) luaL_argerror(L, udx, "incorrect widget type (expected webview)"); return w; } #include "widgets/webview/javascript.c" #include "widgets/webview/frames.c" #include "widgets/webview/downloads.c" #include "widgets/webview/history.c" #include "widgets/webview/scroll.c" #include "widgets/webview/inspector.c" static gint luaH_webview_load_string(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); const gchar *string = luaL_checkstring(L, 2); const gchar *base_uri = luaL_checkstring(L, 3); WebKitWebFrame *frame = webkit_web_view_get_main_frame(d->view); webkit_web_frame_load_alternate_string(frame, string, base_uri, base_uri); return 0; } static void notify_cb(WebKitWebView* UNUSED(v), GParamSpec *ps, widget_t *w) { static GHashTable *wvprops = NULL; property_t *p; if (!wvprops) { wvprops = g_hash_table_new(g_str_hash, g_str_equal); for (p = webview_properties; p->name; p++) g_hash_table_insert(wvprops, (gpointer)p->name, (gpointer)p); } if ((p = g_hash_table_lookup(wvprops, ps->name))) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_property_signal(L, -1, p->tok); lua_pop(L, 1); } } static void update_uri(widget_t *w, const gchar *uri) { webview_data_t *d = w->data; if (!uri) { uri = webkit_web_view_get_uri(d->view); if (!uri || !uri[0]) uri = "about:blank"; } /* uris are the same, do nothing */ if (g_strcmp0(d->uri, uri)) { g_free(d->uri); d->uri = g_strdup(uri); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "property::uri", 0, 0); lua_pop(L, 1); } } static void notify_load_status_cb(WebKitWebView *v, GParamSpec* UNUSED(ps), widget_t *w) { /* Get load status */ WebKitLoadStatus s = webkit_web_view_get_load_status(v); /* get load status literal */ gchar *name = NULL; switch (s) { #define LT_CASE(a, l) case WEBKIT_LOAD_##a: name = l; break; LT_CASE(PROVISIONAL, "provisional") LT_CASE(COMMITTED, "committed") LT_CASE(FINISHED, "finished") LT_CASE(FIRST_VISUALLY_NON_EMPTY_LAYOUT, "first-visual") LT_CASE(FAILED, "failed") #undef LT_CASE default: warn("programmer error, unable to get load status literal"); break; } /* update uri after redirects & etc */ if (s == WEBKIT_LOAD_COMMITTED || s == WEBKIT_LOAD_FINISHED) update_uri(w, NULL); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); lua_pushstring(L, name); luaH_object_emit_signal(L, -2, "load-status", 1, 0); lua_pop(L, 1); } static gboolean resource_request_starting_cb(WebKitWebView* UNUSED(v), WebKitWebFrame* UNUSED(f), WebKitWebResource* UNUSED(we), WebKitNetworkRequest *r, WebKitNetworkResponse* UNUSED(response), widget_t *w) { const gchar *uri = webkit_network_request_get_uri(r); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); lua_pushstring(L, uri); gint ret = luaH_object_emit_signal(L, -2, "resource-request-starting", 1, 1); if (ret) { if (lua_isstring(L, -1)) /* redirect */ webkit_network_request_set_uri(r, lua_tostring(L, -1)); else if (!lua_toboolean(L, -1)) /* ignore */ webkit_network_request_set_uri(r, "about:blank"); } lua_pop(L, ret + 1); return TRUE; } static gboolean new_window_decision_cb(WebKitWebView* UNUSED(v), WebKitWebFrame* UNUSED(f), WebKitNetworkRequest *r, WebKitWebNavigationAction *na, WebKitWebPolicyDecision *pd, widget_t *w) { lua_State *L = globalconf.L; const gchar *uri = webkit_network_request_get_uri(r); gchar *reason = NULL; gint ret = 0; luaH_object_push(L, w->ref); lua_pushstring(L, uri); switch (webkit_web_navigation_action_get_reason(na)) { #define NR_CASE(a, l) case WEBKIT_WEB_NAVIGATION_REASON_##a: reason = l; break; NR_CASE(LINK_CLICKED, "link-clicked"); NR_CASE(FORM_SUBMITTED, "form-submitted"); NR_CASE(BACK_FORWARD, "back-forward"); NR_CASE(RELOAD, "reload"); NR_CASE(FORM_RESUBMITTED, "form-resubmitted"); NR_CASE(OTHER, "other"); #undef NR_CASE default: warn("programmer error, unable to get web navigation reason literal"); break; } lua_pushstring(L, reason); ret = luaH_object_emit_signal(L, -3, "new-window-decision", 2, 1); /* User responded with true, meaning a decision was made * and the signal was handled */ if (ret && lua_toboolean(L, -1)) { webkit_web_policy_decision_ignore(pd); lua_pop(L, ret + 1); return TRUE; } lua_pop(L, ret + 1); /* proceed with default behaviour */ return FALSE; } static WebKitWebView* create_web_view_cb(WebKitWebView* UNUSED(v), WebKitWebFrame* UNUSED(f), widget_t *w) { WebKitWebView *view = NULL; widget_t *new; lua_State *L = globalconf.L; luaH_object_push(L, w->ref); gint top = lua_gettop(L); gint ret = luaH_object_emit_signal(L, top, "create-web-view", 0, 1); /* check for new webview widget */ if (ret) { if ((new = luaH_towidget(L, top + 1))) { if (new->info->tok == L_TK_WEBVIEW) view = WEBKIT_WEB_VIEW(((webview_data_t*)new->data)->view); else warn("invalid return widget type (expected webview, got %s)", new->info->name); } else warn("invalid signal return object type (expected webview widget, " "got %s)", lua_typename(L, lua_type(L, top + 1))); } lua_settop(L, top); return view; } static void link_hover_cb(WebKitWebView* UNUSED(v), const gchar* UNUSED(t), const gchar *link, widget_t *w) { lua_State *L = globalconf.L; webview_data_t *d = w->data; /* links are identical, do nothing */ if (d->hover && !g_strcmp0(d->hover, link)) return; luaH_object_push(L, w->ref); if (d->hover) { lua_pushstring(L, d->hover); g_free(d->hover); luaH_object_emit_signal(L, -2, "link-unhover", 1, 0); } if (link) { d->hover = g_strdup(link); lua_pushstring(L, d->hover); luaH_object_emit_signal(L, -2, "link-hover", 1, 0); } else d->hover = NULL; luaH_object_emit_signal(L, -1, "property::hovered_uri", 0, 0); lua_pop(L, 1); } /* Raises the "navigation-request" signal on a webkit navigation policy * decision request. The default action is to load the requested uri. * * The signal handler is able to: * - return true for the handler execution to stop and the request to continue * - return false for the handler execution to stop and the request to hault * - do nothing and give the navigation decision to the next signal handler * * This signal is also where you would attach custom scheme handlers to take * over the navigation request by launching an external application. */ static gboolean navigation_decision_cb(WebKitWebView* UNUSED(v), WebKitWebFrame* UNUSED(f), WebKitNetworkRequest *r, WebKitWebNavigationAction* UNUSED(a), WebKitWebPolicyDecision *p, widget_t *w) { lua_State *L = globalconf.L; gint top = lua_gettop(L); const gchar *uri = webkit_network_request_get_uri(r); luaH_object_push(L, w->ref); lua_pushstring(L, uri); gint ret = luaH_object_emit_signal(L, -2, "navigation-request", 1, 1); gboolean ignore = ret && !lua_toboolean(L, top+1) ? TRUE : FALSE; if (ignore) /* User responded with false, do not continue navigation request */ webkit_web_policy_decision_ignore(p); lua_settop(L, top); return ignore; } static gint luaH_webview_reload(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_view_reload(d->view); return 0; } static gint luaH_webview_reload_bypass_cache(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_view_reload_bypass_cache(d->view); return 0; } static gint luaH_webview_search(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); const gchar *text = luaL_checkstring(L, 2); gboolean case_sensitive = luaH_checkboolean(L, 3); gboolean forward = luaH_checkboolean(L, 4); gboolean wrap = luaH_checkboolean(L, 5); webkit_web_view_unmark_text_matches(d->view); gboolean ret = webkit_web_view_search_text(d->view, text, case_sensitive, forward, wrap); if (ret) { webkit_web_view_mark_text_matches(d->view, text, case_sensitive, 0); webkit_web_view_set_highlight_text_matches(d->view, TRUE); } lua_pushboolean(L, ret); return 1; } static gint luaH_webview_clear_search(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_view_unmark_text_matches(d->view); return 0; } static gint luaH_webview_loading(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); WebKitLoadStatus s = webkit_web_view_get_load_status(d->view); lua_pushboolean(L, (s == WEBKIT_LOAD_FIRST_VISUALLY_NON_EMPTY_LAYOUT || s == WEBKIT_LOAD_PROVISIONAL || s == WEBKIT_LOAD_COMMITTED) ? TRUE : FALSE); return 1; } static gint luaH_webview_stop(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_view_stop_loading(d->view); return 0; } /* check for trusted ssl certificate */ static gint luaH_webview_ssl_trusted(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); const gchar *uri = webkit_web_view_get_uri(d->view); if (uri && !strncmp(uri, "https", 5)) { WebKitWebFrame *frame = webkit_web_view_get_main_frame(d->view); WebKitWebDataSource *src = webkit_web_frame_get_data_source(frame); WebKitNetworkRequest *req = webkit_web_data_source_get_request(src); SoupMessage *soup_msg = webkit_network_request_get_message(req); lua_pushboolean(L, (soup_msg && (soup_message_get_flags(soup_msg) & SOUP_MESSAGE_CERTIFICATE_TRUSTED)) ? TRUE : FALSE); return 1; } /* return nil if not viewing https uri */ return 0; } static gint luaH_webview_index(lua_State *L, widget_t *w, luakit_token_t token) { webview_data_t *d = w->data; gint ret; switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) /* push property methods */ PF_CASE(CLEAR_SEARCH, luaH_webview_clear_search) /* push search methods */ PF_CASE(SEARCH, luaH_webview_search) /* push history navigation methods */ PF_CASE(GO_BACK, luaH_webview_go_back) PF_CASE(GO_FORWARD, luaH_webview_go_forward) PF_CASE(CAN_GO_BACK, luaH_webview_can_go_back) PF_CASE(CAN_GO_FORWARD, luaH_webview_can_go_forward) /* push misc webview methods */ PF_CASE(EVAL_JS, luaH_webview_eval_js) PF_CASE(REGISTER_FUNCTION, luaH_webview_register_function) PF_CASE(LOAD_STRING, luaH_webview_load_string) PF_CASE(LOADING, luaH_webview_loading) PF_CASE(RELOAD, luaH_webview_reload) PF_CASE(RELOAD_BYPASS_CACHE, luaH_webview_reload_bypass_cache) PF_CASE(SSL_TRUSTED, luaH_webview_ssl_trusted) PF_CASE(STOP, luaH_webview_stop) /* push inspector webview methods */ PF_CASE(SHOW_INSPECTOR, luaH_webview_show_inspector) PF_CASE(CLOSE_INSPECTOR, luaH_webview_close_inspector) /* push string properties */ PS_CASE(HOVERED_URI, d->hover) PS_CASE(URI, d->uri) /* push boolean properties */ PB_CASE(VIEW_SOURCE, webkit_web_view_get_view_source_mode(d->view)) case L_TK_FRAMES: return luaH_webview_push_frames(L, d); case L_TK_HISTORY: return luaH_webview_push_history(L, d->view); case L_TK_SCROLL: return luaH_webview_push_scroll_table(L); case L_TK_INSPECTOR: if (!d->iview) return 0; luaH_object_push(L, ((widget_t*)d->iview)->ref); return 1; default: break; } if ((ret = luaH_gobject_index(L, webview_properties, token, G_OBJECT(d->view)))) return ret; return luaH_gobject_index(L, webview_settings_properties, token, G_OBJECT(webkit_web_view_get_settings(d->view))); } static gchar* parse_uri(const gchar *uri) { /* check for null uri */ if (!uri || !uri[0] || !g_strcmp0(uri, "about:blank")) return g_strdup("about:blank"); /* check for scheme or "about:blank" */ else if (g_strrstr(uri, "://")) return g_strdup(uri); /* check if uri points to a file */ else if (file_exists(uri)) { if (g_path_is_absolute(uri)) return g_strdup_printf("file://%s", uri); else { /* make path absolute */ gchar *cwd = g_get_current_dir(); gchar *path = g_build_filename(cwd, uri, NULL); gchar *new = g_strdup_printf("file://%s", path); g_free(cwd); g_free(path); return new; } } /* default to http:// scheme */ return g_strdup_printf("http://%s", uri); } /* The __newindex method for the webview object */ static gint luaH_webview_newindex(lua_State *L, widget_t *w, luakit_token_t token) { size_t len; webview_data_t *d = w->data; gchar *uri; switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) case L_TK_URI: uri = parse_uri(luaL_checklstring(L, 3, &len)); webkit_web_view_load_uri(d->view, uri); update_uri(w, uri); g_free(uri); return 0; case L_TK_SHOW_SCROLLBARS: show_scrollbars(d, luaH_checkboolean(L, 3)); return luaH_object_property_signal(L, 1, token); case L_TK_HISTORY: webview_set_history(L, d->view, 3); return luaH_object_property_signal(L, 1, token); case L_TK_VIEW_SOURCE: webkit_web_view_set_view_source_mode(d->view, luaH_checkboolean(L, 3)); return luaH_object_property_signal(L, 1, token); default: break; } /* check for webview widget gobject properties */ gboolean emit = luaH_gobject_newindex(L, webview_properties, token, 3, G_OBJECT(d->view)); /* check for webkit widget's settings gobject properties */ if (!emit) emit = luaH_gobject_newindex(L, webview_settings_properties, token, 3, G_OBJECT(webkit_web_view_get_settings(d->view))); return emit ? luaH_object_property_signal(L, 1, token) : 0; } static gboolean expose_cb(GtkWidget* UNUSED(widget), GdkEventExpose* UNUSED(e), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "expose", 0, 0); lua_pop(L, 1); return FALSE; } static gint luaH_push_hit_test(lua_State *L, WebKitWebView *v, GdkEventButton *ev) { /* get hit test */ WebKitHitTestResult *h = webkit_web_view_get_hit_test_result(v, ev); guint c; g_object_get(h, "context", &c, NULL); /* create new table to store hit test context data */ lua_newtable(L); const gchar *name; #define HTR_CHECK(a, l) \ if ((c & WEBKIT_HIT_TEST_RESULT_CONTEXT_##a)) { \ name = l; \ lua_pushstring(L, name); \ lua_pushboolean(L, TRUE); \ lua_rawset(L, -3); \ } /* add context items to table */ HTR_CHECK(DOCUMENT, "document") HTR_CHECK(LINK, "link") HTR_CHECK(IMAGE, "image") HTR_CHECK(MEDIA, "media") HTR_CHECK(SELECTION, "selection") HTR_CHECK(EDITABLE, "editable") #undef HTR_CHECK return 1; } static gboolean webview_button_cb(GtkWidget *view, GdkEventButton *ev, widget_t *w) { gint ret; lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_modifier_table_push(L, ev->state); lua_pushinteger(L, ev->button); /* push webview hit test context */ luaH_push_hit_test(L, WEBKIT_WEB_VIEW(view), ev); switch (ev->type) { case GDK_2BUTTON_PRESS: ret = luaH_object_emit_signal(L, -4, "button-double-click", 3, 1); break; case GDK_BUTTON_RELEASE: ret = luaH_object_emit_signal(L, -4, "button-release", 3, 1); break; default: ret = luaH_object_emit_signal(L, -4, "button-press", 3, 1); break; } /* User responded with TRUE, so do not propagate event any further */ if (ret && lua_toboolean(L, -1)) { lua_pop(L, ret + 1); return TRUE; } lua_pop(L, ret + 1); /* propagate event further */ return FALSE; } static void menu_item_cb(GtkMenuItem *menuitem, widget_t *w) { lua_State *L = globalconf.L; gpointer ref = g_object_get_data(G_OBJECT(menuitem), "lua_callback"); luaH_object_push(L, w->ref); luaH_object_push(L, ref); luaH_dofunction(L, 1, 0); } static void hide_popup_cb(void) { GSList *iter; lua_State *L = globalconf.L; /* dereference context menu items callback functions from the last context menu */ if (last_popup.refs) { for (iter = last_popup.refs; iter; iter = iter->next) luaH_object_unref(L, iter->data); g_slist_free(last_popup.refs); last_popup.refs = NULL; } /* destroy context menu item widgets from the last context menu */ if (last_popup.items) { for (iter = last_popup.items; iter; iter = iter->next) gtk_widget_destroy(iter->data); g_slist_free(last_popup.items); last_popup.items = NULL; } } static void populate_popup_from_table(lua_State *L, GtkMenu *menu, widget_t *w) { GtkWidget *item, *submenu; gpointer ref; const gchar *label; gint i, len = lua_objlen(L, -1); /* walk table and build context menu */ for(i = 1; i <= len; i++) { lua_rawgeti(L, -1, i); if((lua_type(L, -1) == LUA_TTABLE) && (lua_objlen(L, -1) >= 2)) { lua_rawgeti(L, -1, 1); label = lua_tostring(L, -1); lua_pop(L, 1); lua_rawgeti(L, -1, 2); /* add new submenu */ if(lua_type(L, -1) == LUA_TTABLE) { submenu = gtk_menu_new(); item = gtk_menu_item_new_with_mnemonic(label); last_popup.items = g_slist_prepend(last_popup.items, item); last_popup.items = g_slist_prepend(last_popup.items, submenu); gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); populate_popup_from_table(L, GTK_MENU(submenu), w); lua_pop(L, 1); /* add context menu item */ } else if(lua_type(L, -1) == LUA_TFUNCTION) { item = gtk_menu_item_new_with_mnemonic(label); last_popup.items = g_slist_prepend(last_popup.items, item); ref = luaH_object_ref(L, -1); last_popup.refs = g_slist_prepend(last_popup.refs, ref); g_object_set_data(G_OBJECT(item), "lua_callback", ref); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); g_signal_connect(item, "activate", G_CALLBACK(menu_item_cb), (gpointer)w); } /* add separator if encounters `true` */ } else if(lua_type(L, -1) == LUA_TBOOLEAN && lua_toboolean(L, -1)) { item = gtk_separator_menu_item_new(); last_popup.items = g_slist_prepend(last_popup.items, item); gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); gtk_widget_show(item); } lua_pop(L, 1); } } static void populate_popup_cb(WebKitWebView* UNUSED(v), GtkMenu *menu, widget_t *w) { lua_State *L = globalconf.L; gint top = lua_gettop(L); luaH_object_push(L, w->ref); gint ret = luaH_object_emit_signal(L, top + 1, "populate-popup", 0, 1); if (ret && lua_istable(L, -1)) populate_popup_from_table(L, menu, w); lua_settop(L, top); /* destroy all context menu items when we are finished with them */ #if WEBKIT_CHECK_VERSION(1, 4, 0) g_signal_connect(menu, "unrealize", G_CALLBACK(hide_popup_cb), NULL); #else g_signal_connect(menu, "hide", G_CALLBACK(hide_popup_cb), NULL); #endif } static gboolean scroll_event_cb(GtkWidget* UNUSED(v), GdkEventScroll *ev, widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_modifier_table_push(L, ev->state); lua_pushinteger(L, ((int)ev->direction) + 4); gint ret = luaH_object_emit_signal(L, -3, "button-release", 2, 1); gboolean catch = ret && lua_toboolean(L, -1) ? TRUE : FALSE; lua_pop(L, ret + 1); return catch; } static void webview_destructor(widget_t *w) { webview_data_t *d = w->data; /* close inspector window */ if (d->iview) { webkit_web_inspector_close(d->inspector); /* need to make sure "close-inspector" gtk signal is emitted or luakit segfaults */ while (g_main_context_iteration(NULL, FALSE)); } g_ptr_array_remove(globalconf.webviews, w); gtk_widget_destroy(GTK_WIDGET(d->view)); gtk_widget_destroy(GTK_WIDGET(d->win)); g_hash_table_remove(frames_by_view, d->view); g_free(d->uri); g_free(d->hover); g_slice_free(webview_data_t, d); } void size_request_cb(GtkWidget *UNUSED(widget), GtkRequisition *r, widget_t *w) { gtk_widget_set_size_request(GTK_WIDGET(w->widget), r->width, r->height); } /* redirect focus on scrolled window to child webview widget */ void swin_focus_cb(GtkWidget *UNUSED(wi), GdkEventFocus *UNUSED(e), widget_t *w) { webview_data_t *d = w->data; gtk_widget_grab_focus(GTK_WIDGET(d->view)); } widget_t * widget_webview(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_webview_index; w->newindex = luaH_webview_newindex; w->destructor = webview_destructor; /* create private webview data struct */ webview_data_t *d = g_slice_new0(webview_data_t); d->widget = w; w->data = d; /* keep a list of all webview widgets */ if (!globalconf.webviews) globalconf.webviews = g_ptr_array_new(); if (!frames_by_view) frames_by_view = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) g_hash_table_destroy); /* create widgets */ d->view = WEBKIT_WEB_VIEW(webkit_web_view_new()); d->inspector = webkit_web_view_get_inspector(d->view); d->win = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); w->widget = GTK_WIDGET(d->win); /* add webview to scrolled window */ gtk_container_add(GTK_CONTAINER(d->win), GTK_WIDGET(d->view)); /* set initial scrollbars state */ show_scrollbars(d, TRUE); /* insert data into global tables and arrays */ g_ptr_array_add(globalconf.webviews, w); g_hash_table_insert(frames_by_view, d->view, g_hash_table_new(g_direct_hash, g_direct_equal)); /* connect webview signals */ g_object_connect(G_OBJECT(d->view), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::button-press-event", G_CALLBACK(webview_button_cb), w, "signal::button-release-event", G_CALLBACK(webview_button_cb), w, "signal::create-web-view", G_CALLBACK(create_web_view_cb), w, "signal::document-load-finished", G_CALLBACK(document_load_finished_cb), w, "signal::download-requested", G_CALLBACK(download_request_cb), w, "signal::expose-event", G_CALLBACK(expose_cb), w, "signal::hovering-over-link", G_CALLBACK(link_hover_cb), w, "signal::key-press-event", G_CALLBACK(key_press_cb), w, "signal::mime-type-policy-decision-requested", G_CALLBACK(mime_type_decision_cb), w, "signal::navigation-policy-decision-requested", G_CALLBACK(navigation_decision_cb), w, "signal::new-window-policy-decision-requested", G_CALLBACK(new_window_decision_cb), w, "signal::notify", G_CALLBACK(notify_cb), w, "signal::notify::load-status", G_CALLBACK(notify_load_status_cb), w, "signal::populate-popup", G_CALLBACK(populate_popup_cb), w, "signal::resource-request-starting", G_CALLBACK(resource_request_starting_cb), w, "signal::scroll-event", G_CALLBACK(scroll_event_cb), w, "signal::size-request", G_CALLBACK(size_request_cb), w, NULL); g_object_connect(G_OBJECT(d->win), "signal::parent-set", G_CALLBACK(parent_set_cb), w, "signal::focus-in-event", G_CALLBACK(swin_focus_cb), w, NULL); g_object_connect(G_OBJECT(d->inspector), "signal::inspect-web-view", G_CALLBACK(inspect_webview_cb), w, "signal::show-window", G_CALLBACK(inspector_show_window_cb), w, "signal::close-window", G_CALLBACK(inspector_close_window_cb), w, "signal::attach-window", G_CALLBACK(inspector_attach_window_cb), w, "signal::detach-window", G_CALLBACK(inspector_detach_window_cb), w, NULL); /* show widgets */ gtk_widget_show(GTK_WIDGET(d->view)); gtk_widget_show(GTK_WIDGET(d->win)); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/webview/000077500000000000000000000000001202446317700170465ustar00rootroot00000000000000luakit-2012.09.13-r1/widgets/webview/downloads.c000066400000000000000000000037421202446317700212120ustar00rootroot00000000000000/* * widgets/webview/downloads.c - webkit webview download functions * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "clib/download.h" static gboolean mime_type_decision_cb(WebKitWebView *v, WebKitWebFrame* UNUSED(f), WebKitNetworkRequest *r, gchar *mime, WebKitWebPolicyDecision *pd, widget_t *w) { lua_State *L = globalconf.L; const gchar *uri = webkit_network_request_get_uri(r); luaH_object_push(L, w->ref); lua_pushstring(L, uri); lua_pushstring(L, mime); gint ret = luaH_object_emit_signal(L, -3, "mime-type-decision", 2, 1); if (ret && !lua_toboolean(L, -1)) /* User responded with false, ignore request */ webkit_web_policy_decision_ignore(pd); else if (!webkit_web_view_can_show_mime_type(v, mime)) webkit_web_policy_decision_download(pd); else webkit_web_policy_decision_use(pd); lua_pop(L, ret + 1); return TRUE; } static gboolean download_request_cb(WebKitWebView* UNUSED(v), WebKitDownload *dl, widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_download_push(L, dl); gint ret = luaH_object_emit_signal(L, 1, "download-request", 1, 1); gboolean handled = (ret && lua_toboolean(L, 2)); lua_pop(L, 1 + ret); return handled; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80 luakit-2012.09.13-r1/widgets/webview/frames.c000066400000000000000000000042661202446317700204770ustar00rootroot00000000000000/* * widgets/webview/frames.c - webkit webview frames functions * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ GHashTable *frames_by_view = NULL; typedef struct { WebKitWebView *view; WebKitWebFrame *frame; } frame_destroy_callback_t; static gint luaH_webview_push_frames(lua_State *L, webview_data_t *d) { GHashTable *frames = g_hash_table_lookup(frames_by_view, d->view); lua_createtable(L, g_hash_table_size(frames), 0); gint i = 1, tidx = lua_gettop(L); gpointer frame; GHashTableIter iter; g_hash_table_iter_init(&iter, frames); while (g_hash_table_iter_next(&iter, &frame, NULL)) { lua_pushlightuserdata(L, frame); lua_rawseti(L, tidx, i++); } return 1; } static void frame_destroyed_cb(frame_destroy_callback_t *st) { gpointer hash = g_hash_table_lookup(frames_by_view, st->view); /* the view might be destroyed before the frames */ if (hash) g_hash_table_remove(hash, st->frame); g_slice_free(frame_destroy_callback_t, st); } static void document_load_finished_cb(WebKitWebView *v, WebKitWebFrame *f, widget_t* UNUSED(w)) { /* add a bogus property to the frame so we get notified when it's destroyed */ frame_destroy_callback_t *st = g_slice_new(frame_destroy_callback_t); st->view = v; st->frame = f; g_object_set_data_full(G_OBJECT(f), "dummy-destroy-notify", st, (GDestroyNotify)frame_destroyed_cb); GHashTable *frames = g_hash_table_lookup(frames_by_view, v); g_hash_table_insert(frames, f, NULL); } luakit-2012.09.13-r1/widgets/webview/history.c000066400000000000000000000115721202446317700207210ustar00rootroot00000000000000/* * widgets/webview/history.c - webkit webview history functions * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ static gint luaH_webview_push_history(lua_State *L, WebKitWebView *view) { /* obtain the history list of the tab and get information about it */ WebKitWebBackForwardList *bflist = webkit_web_back_forward_list_new_with_web_view(view); WebKitWebHistoryItem *item; gint backlen = webkit_web_back_forward_list_get_back_length(bflist); gint forwardlen = webkit_web_back_forward_list_get_forward_length(bflist); /* compose an overall table with the history list and the position thereof */ lua_createtable(L, 0, 2); /* Set hist[index] = pos */ lua_pushliteral(L, "index"); lua_pushnumber(L, backlen + 1); lua_rawset(L, -3); /* create a table with the history items */ lua_createtable(L, backlen + forwardlen + 1, 0); for(gint i = -backlen; i <= forwardlen; i++) { /* each individual history item is composed of a URL and a page title */ item = webkit_web_back_forward_list_get_nth_item(bflist, i); lua_createtable(L, 0, 2); /* Set hist_item[uri] = uri */ lua_pushliteral(L, "uri"); lua_pushstring(L, item ? webkit_web_history_item_get_uri(item) : "about:blank"); lua_rawset(L, -3); /* Set hist_item[title] = title */ lua_pushliteral(L, "title"); lua_pushstring(L, item ? webkit_web_history_item_get_title(item) : ""); lua_rawset(L, -3); lua_rawseti(L, -2, backlen + i + 1); } /* Set hist[items] = hist_items_table */ lua_pushliteral(L, "items"); lua_insert(L, lua_gettop(L) - 1); lua_rawset(L, -3); return 1; } static void webview_set_history(lua_State *L, WebKitWebView *view, gint idx) { gint pos, bflen; WebKitWebBackForwardList *bflist; WebKitWebHistoryItem *item = NULL; gchar *uri = NULL; if(!lua_istable(L, idx)) luaL_error(L, "invalid history table"); /* get history items table */ lua_pushliteral(L, "items"); lua_rawget(L, idx); bflen = lua_objlen(L, -1); /* create new back-forward history list */ bflist = webkit_web_back_forward_list_new_with_web_view(view); webkit_web_back_forward_list_clear(bflist); /* get position of current history item */ lua_pushliteral(L, "index"); lua_rawget(L, idx); pos = (gint)lua_tonumber(L, -1); /* load last item if out of range */ pos = (pos < 1 || pos > bflen) ? 0 : pos - bflen; lua_pop(L, 1); /* now we actually set the history to the content of the list */ for (gint i = 1; i <= bflen; i++) { lua_rawgeti(L, -1, i); lua_pushliteral(L, "title"); lua_rawget(L, -2); lua_pushliteral(L, "uri"); lua_rawget(L, -3); if (pos || i < bflen) { item = webkit_web_history_item_new_with_data(lua_tostring(L, -1), NONULL(lua_tostring(L, -2))); webkit_web_back_forward_list_add_item(bflist, item); } else uri = g_strdup(lua_tostring(L, -1)); lua_pop(L, 3); } /* load last item */ if (uri) { webkit_web_view_load_uri(view, uri); g_free(uri); /* load item in history */ } else if (bflen && webkit_web_view_can_go_back_or_forward(view, pos)) { webkit_web_view_go_back_or_forward(view, pos); /* load "about:blank" on empty history list */ } else webkit_web_view_load_uri(view, "about:blank"); lua_pop(L, 1); } static gint luaH_webview_can_go_back(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); lua_pushboolean(L, webkit_web_view_can_go_back(d->view)); return 1; } static gint luaH_webview_can_go_forward(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); lua_pushboolean(L, webkit_web_view_can_go_forward(d->view)); return 1; } static gint luaH_webview_go_back(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); gint steps = (gint) luaL_checknumber(L, 2); webkit_web_view_go_back_or_forward(d->view, steps * -1); return 0; } static gint luaH_webview_go_forward(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); gint steps = (gint) luaL_checknumber(L, 2); webkit_web_view_go_back_or_forward(d->view, steps); return 0; } luakit-2012.09.13-r1/widgets/webview/inspector.c000066400000000000000000000057151202446317700212300ustar00rootroot00000000000000/* * widgets/webview/inspector.c - WebKitWebInspector wrappers * * Copyright © 2012 Mason Larobina * Copyright © 2011-2012 Fabian Streitel * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ static WebKitWebView* inspect_webview_cb(WebKitWebInspector *UNUSED(inspector), WebKitWebView *UNUSED(v), widget_t *w) { lua_State *L = globalconf.L; webview_data_t *d = w->data; luaH_object_push(L, w->ref); if (luaH_object_emit_signal(L, -1, "create-inspector-web-view", 0, 1)) { widget_t *new; if (((new = luaH_towidget(L, -1)) && new->info->tok == L_TK_WEBVIEW)) { d->iview = new; lua_pop(L, 2); return ((webview_data_t*)new->data)->view; } warn("invalid signal return type (expected webview widget, got %s)", lua_typename(L, lua_type(L, -1))); } lua_pop(L, 1); return NULL; } static gboolean inspector_show_window_cb(WebKitWebInspector* UNUSED(inspector), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "show-inspector", 0, 0); lua_pop(L, 1); return TRUE; } static gboolean inspector_close_window_cb(WebKitWebInspector* UNUSED(inspector), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); webview_data_t *d = w->data; luaH_object_push(L, d->iview); d->iview = NULL; luaH_object_emit_signal(L, -2, "close-inspector", 1, 0); lua_pop(L, 1); return TRUE; } static gboolean inspector_attach_window_cb(WebKitWebInspector* UNUSED(inspector), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "attach-inspector", 0, 0); lua_pop(L, 1); return TRUE; } static gboolean inspector_detach_window_cb(WebKitWebInspector* UNUSED(inspector), widget_t *w) { lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "detach-inspector", 0, 0); lua_pop(L, 1); return TRUE; } static gint luaH_webview_show_inspector(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_inspector_show(d->inspector); return 0; } static gint luaH_webview_close_inspector(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, 1); webkit_web_inspector_close(d->inspector); return 0; } luakit-2012.09.13-r1/widgets/webview/javascript.c000066400000000000000000000327231202446317700213670ustar00rootroot00000000000000/* * widgets/webview/javascript.c - webkit webview javascript functions * * Copyright © 2010-2012 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include static JSValueRef luaJS_tovalue(lua_State *L, JSContextRef context, gint idx, gchar **error); static gint luaJS_pushvalue(lua_State *L, JSContextRef context, JSValueRef value, gchar **error); static gchar* tostring(JSContextRef context, JSValueRef value, gchar **error) { JSStringRef str = JSValueToStringCopy(context, value, NULL); if (!str) { if (error) *error = g_strdup("string conversion failed"); return NULL; } size_t size = JSStringGetMaximumUTF8CStringSize(str); gchar *ret = g_malloc(sizeof(gchar)*size); JSStringGetUTF8CString(str, ret, size); JSStringRelease(str); return ret; } static gint luaJS_pushstring(lua_State *L, JSContextRef context, JSValueRef value, gchar **error) { gchar *str = tostring(context, value, error); if (str) { lua_pushstring(L, str); g_free(str); return 1; } return 0; } static gint luaJS_pushobject(lua_State *L, JSContextRef context, JSObjectRef obj, gchar **error) { gint top = lua_gettop(L); JSPropertyNameArrayRef keys = JSObjectCopyPropertyNames(context, obj); size_t count = JSPropertyNameArrayGetCount(keys); JSValueRef exception = NULL; lua_newtable(L); for (size_t i = 0; i < count; i++) { /* push table key onto stack */ JSStringRef key = JSPropertyNameArrayGetNameAtIndex(keys, i); size_t slen = JSStringGetMaximumUTF8CStringSize(key); gchar cstr[slen]; JSStringGetUTF8CString(key, cstr, slen); gchar *eptr = NULL; int n = strtol(cstr, &eptr, 10); if (!*eptr) /* end at '\0' ? == it's a number! */ lua_pushinteger(L, ++n); /* 0-index array to 1-index array */ else lua_pushstring(L, cstr); /* push table value into stack */ JSValueRef val = JSObjectGetProperty(context, obj, key, &exception); if (exception) { lua_settop(L, top); if (error) { gchar *err = tostring(context, exception, NULL); *error = g_strdup_printf("JSObjectGetProperty call failed (%s)", err ? err : "unknown reason"); g_free(err); } JSPropertyNameArrayRelease(keys); return 0; } luaJS_pushvalue(L, context, val, error); if (error && *error) { lua_settop(L, top); JSPropertyNameArrayRelease(keys); return 0; } lua_rawset(L, -3); } JSPropertyNameArrayRelease(keys); return 1; } /* Push JavaScript value onto Lua stack */ static gint luaJS_pushvalue(lua_State *L, JSContextRef context, JSValueRef value, gchar **error) { switch (JSValueGetType(context, value)) { case kJSTypeBoolean: lua_pushboolean(L, JSValueToBoolean(context, value)); return 1; case kJSTypeNumber: lua_pushnumber(L, JSValueToNumber(context, value, NULL)); return 1; case kJSTypeString: return luaJS_pushstring(L, context, value, error); case kJSTypeObject: return luaJS_pushobject(L, context, (JSObjectRef)value, error); case kJSTypeUndefined: case kJSTypeNull: lua_pushnil(L); return 1; default: break; } if (error) *error = g_strdup("Unable to convert value into equivalent Lua type"); return 0; } static JSValueRef luaJS_fromtable(lua_State *L, JSContextRef context, gint idx, gchar **error) { gint top = lua_gettop(L); /* convert relative index into abs */ if (idx < 0) idx = top + idx + 1; JSValueRef exception = NULL; JSObjectRef obj; size_t len = lua_objlen(L, idx); if (len) { obj = JSObjectMakeArray(context, 0, NULL, &exception); if (exception) { if (error) { gchar *err = tostring(context, exception, NULL); *error = g_strdup_printf("JSObjectMakeArray call failed (%s)", err ? err : "unknown reason"); g_free(err); } return NULL; } lua_pushnil(L); for (guint i = 0; lua_next(L, idx); i++) { JSValueRef val = luaJS_tovalue(L, context, -1, error); if (error && *error) { lua_settop(L, top); return NULL; } lua_pop(L, 1); JSObjectSetPropertyAtIndex(context, obj, i, val, &exception); } } else { obj = JSObjectMake(context, NULL, NULL); lua_pushnil(L); while (lua_next(L, idx)) { /* We only care about string attributes in the table */ if (lua_type(L, -2) == LUA_TSTRING) { JSValueRef val = luaJS_tovalue(L, context, -1, error); if (error && *error) { lua_settop(L, top); return NULL; } JSStringRef key = JSStringCreateWithUTF8CString(lua_tostring(L, -2)); JSObjectSetProperty(context, obj, key, val, kJSPropertyAttributeNone, &exception); JSStringRelease(key); if (exception) { if (error) { gchar *err = tostring(context, exception, NULL); *error = g_strdup_printf("JSObjectSetProperty call failed (%s)", err ? err : "unknown reason"); g_free(err); } return NULL; } } lua_pop(L, 1); } } return obj; } /* Make JavaScript value from Lua value */ static JSValueRef luaJS_tovalue(lua_State *L, JSContextRef context, gint idx, gchar **error) { JSStringRef str; JSValueRef ret; switch (lua_type(L, idx)) { case LUA_TBOOLEAN: return JSValueMakeBoolean(context, lua_toboolean(L, idx)); case LUA_TNUMBER: return JSValueMakeNumber(context, lua_tonumber(L, idx)); case LUA_TNIL: return JSValueMakeNull(context); case LUA_TNONE: return JSValueMakeUndefined(context); case LUA_TSTRING: str = JSStringCreateWithUTF8CString(lua_tostring(L, idx)); ret = JSValueMakeString(context, str); JSStringRelease(str); return ret; case LUA_TTABLE: return luaJS_fromtable(L, context, idx, error); default: break; } if (error) *error = g_strdup_printf("unhandled Lua->JS type conversion (type %s)", lua_typename(L, lua_type(L, idx))); return JSValueMakeUndefined(context); } /* create JavaScript exception object from string */ static JSValueRef luaJS_make_exception(JSContextRef context, const gchar *error) { JSStringRef estr = JSStringCreateWithUTF8CString(error); JSValueRef exception = JSValueMakeString(context, estr); JSStringRelease(estr); return JSValueToObject(context, exception, NULL); } static JSValueRef luaJS_registered_function_callback(JSContextRef context, JSObjectRef fun, JSObjectRef UNUSED(this), size_t argc, const JSValueRef *argv, JSValueRef *exception) { lua_State *L = globalconf.L; gint top = lua_gettop(L); /* push Lua callback function */ luaH_object_push(L, JSObjectGetPrivate(fun)); gchar *error = NULL; /* push function arguments onto Lua stack */ for (guint i = 0; i < argc; i++) { if (luaJS_pushvalue(L, context, argv[i], &error)) continue; /* raise JavaScript exception */ gchar *emsg = g_strdup_printf("bad argument #%d to Lua function (%s)", i, error); *exception = luaJS_make_exception(context, emsg); g_free(error); g_free(emsg); lua_settop(L, top); return JSValueMakeUndefined(context); } if (lua_pcall(L, argc, 1, 0)) { /* raise JS exception with Lua error message */ const gchar *error = luaL_checkstring(L, -1); lua_pop(L, 1); *exception = luaJS_make_exception(context, error); return JSValueMakeUndefined(context); } JSValueRef ret = luaJS_tovalue(L, context, -1, &error); lua_pop(L, 1); /* invalid return type from registered function */ if (error) { *exception = luaJS_make_exception(context, error); return JSValueMakeUndefined(context); } return ret; } static void luaJS_registered_function_gc(JSObjectRef obj) { gpointer userfunc = JSObjectGetPrivate(obj); luaH_object_unref(globalconf.L, userfunc); } static void luaJS_register_function(JSContextRef context, const gchar *name, gpointer ref) { JSStringRef js_name = JSStringCreateWithUTF8CString(name); JSClassDefinition def = kJSClassDefinitionEmpty; def.callAsFunction = luaJS_registered_function_callback; def.className = g_strdup(name); def.finalize = luaJS_registered_function_gc; JSClassRef class = JSClassCreate(&def); JSObjectRef fun = JSObjectMake(context, class, ref); JSObjectRef global = JSContextGetGlobalObject(context); JSObjectSetProperty(context, global, js_name, fun, kJSPropertyAttributeDontDelete | kJSPropertyAttributeReadOnly, NULL); JSStringRelease(js_name); JSClassRelease(class); } static gint luaJS_eval_js(lua_State *L, JSContextRef context, const gchar *script, const gchar *source, bool no_return) { JSStringRef js_source, js_script; JSValueRef result, exception = NULL; /* evaluate the script and get return value*/ js_script = JSStringCreateWithUTF8CString(script); js_source = JSStringCreateWithUTF8CString(source); result = JSEvaluateScript(context, js_script, NULL, js_source, 0, &exception); /* cleanup */ JSStringRelease(js_script); JSStringRelease(js_source); /* handle javascript exceptions while running script */ if (exception) { lua_pushnil(L); if (!luaJS_pushstring(L, context, exception, NULL)) lua_pushliteral(L, "Unknown JavaScript exception (unable to " "convert thrown exception object into string)"); return 2; } if (no_return) return 0; /* push return value onto lua stack */ gchar *error = NULL; if (luaJS_pushvalue(L, context, result, &error)) return 1; /* handle type conversion errors */ lua_pushnil(L); lua_pushstring(L, error); g_free(error); return 2; } static gint luaH_webview_register_function(lua_State *L) { WebKitWebFrame *frame = NULL; webview_data_t *d = luaH_checkwvdata(L, 1); const gchar *name = luaL_checkstring(L, 2); /* get lua callback function */ luaH_checkfunction(L, 3); lua_pushvalue(L, 3); gpointer ref = luaH_object_ref(L, -1); if (lua_gettop(L) >= 4) { if (lua_islightuserdata(L, 4)) frame = lua_touserdata(L, 4); else if (lua_isstring(L, 4)) { if (L_TK_FOCUSED == l_tokenize(lua_tostring(L, 4))) frame = webkit_web_view_get_focused_frame(d->view); } } /* Fall back on main frame */ if (!frame) frame = webkit_web_view_get_main_frame(d->view); /* register function */ JSGlobalContextRef context = webkit_web_frame_get_global_context(frame); luaJS_register_function(context, name, ref); return 0; } static gint luaH_webview_eval_js(lua_State *L) { WebKitWebFrame *frame = NULL; webview_data_t *d = luaH_checkwvdata(L, 1); const gchar *script = luaL_checkstring(L, 2); const gchar *usr_source = NULL; gchar *source = NULL; bool no_return = false; gint top = lua_gettop(L); if (top >= 3 && !lua_isnil(L, 3)) { luaH_checktable(L, 3); /* source filename to use in error messages and webinspector */ if (luaH_rawfield(L, 3, "source") && lua_isstring(L, -1)) usr_source = lua_tostring(L, -1); if (luaH_rawfield(L, 3, "frame")) { if (lua_islightuserdata(L, -1)) frame = lua_touserdata(L, -1); else if (lua_isstring(L, -1)) { if (L_TK_FOCUSED == l_tokenize(lua_tostring(L, -1))) frame = webkit_web_view_get_focused_frame(d->view); } } if (luaH_rawfield(L, 3, "no_return")) no_return = lua_toboolean(L, -1); lua_settop(L, top); } if (!usr_source) source = luaH_callerinfo(L); /* Fall back on main frame */ if (!frame) frame = webkit_web_view_get_main_frame(d->view); /* evaluate javascript script and push return result onto lua stack */ JSGlobalContextRef context = webkit_web_frame_get_global_context(frame); gint ret = luaJS_eval_js(L, context, script, usr_source ? usr_source : (const gchar*)source, no_return); g_free(source); return ret; } luakit-2012.09.13-r1/widgets/webview/scroll.c000066400000000000000000000070021202446317700205070ustar00rootroot00000000000000/* * widgets/webview/scroll.c - webkit webview scroll functions * * Copyright © 2010-2011 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ static gint luaH_webview_scroll_newindex(lua_State *L) { /* get webview widget upvalue */ webview_data_t *d = luaH_checkwvdata(L, lua_upvalueindex(1)); const gchar *prop = luaL_checkstring(L, 2); luakit_token_t t = l_tokenize(prop); GtkAdjustment *a; if (t == L_TK_X) a = gtk_scrolled_window_get_hadjustment(d->win); else if (t == L_TK_Y) a = gtk_scrolled_window_get_vadjustment(d->win); else return 0; gdouble value = luaL_checknumber(L, 3); gdouble max = gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a); gtk_adjustment_set_value(a, ((value < 0 ? 0 : value) > max ? max : value)); return 0; } static gint luaH_webview_scroll_index(lua_State *L) { webview_data_t *d = luaH_checkwvdata(L, lua_upvalueindex(1)); const gchar *prop = luaL_checkstring(L, 2); luakit_token_t t = l_tokenize(prop); GtkAdjustment *a = (*prop == 'x') ? gtk_scrolled_window_get_hadjustment(d->win) : gtk_scrolled_window_get_vadjustment(d->win); if (t == L_TK_X || t == L_TK_Y) { lua_pushnumber(L, gtk_adjustment_get_value(a)); return 1; } else if (t == L_TK_XMAX || t == L_TK_YMAX) { lua_pushnumber(L, gtk_adjustment_get_upper(a) - gtk_adjustment_get_page_size(a)); return 1; } else if (t == L_TK_XPAGE_SIZE || t == L_TK_YPAGE_SIZE) { lua_pushnumber(L, gtk_adjustment_get_page_size(a)); return 1; } return 0; } static gint luaH_webview_push_scroll_table(lua_State *L) { /* create scroll table */ lua_newtable(L); /* setup metatable */ lua_createtable(L, 0, 2); /* push __index metafunction */ lua_pushliteral(L, "__index"); lua_pushvalue(L, 1); /* copy webview userdata */ lua_pushcclosure(L, luaH_webview_scroll_index, 1); lua_rawset(L, -3); /* push __newindex metafunction */ lua_pushliteral(L, "__newindex"); lua_pushvalue(L, 1); /* copy webview userdata */ lua_pushcclosure(L, luaH_webview_scroll_newindex, 1); lua_rawset(L, -3); lua_setmetatable(L, -2); return 1; } void show_scrollbars(webview_data_t *d, gboolean show) { GObject *frame = G_OBJECT(webkit_web_view_get_main_frame(d->view)); /* show scrollbars */ if (show) { if (d->hide_id) g_signal_handler_disconnect(frame, d->hide_id); gtk_scrolled_window_set_policy(d->win, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); d->hide_id = 0; /* hide scrollbars */ } else if (!d->hide_id) { gtk_scrolled_window_set_policy(d->win, GTK_POLICY_NEVER, GTK_POLICY_NEVER); d->hide_id = g_signal_connect(frame, "scrollbars-policy-changed", G_CALLBACK(true_cb), NULL); } } luakit-2012.09.13-r1/widgets/window.c000066400000000000000000000140661202446317700170600ustar00rootroot00000000000000/* * widgets/window.c - gtk window widget wrapper * * Copyright © 2010 Mason Larobina * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include #include "luah.h" #include "widgets/common.h" #include "clib/soup/auth.h" typedef struct { widget_t *widget; GtkWindow *win; GdkWindowState state; } window_data_t; static widget_t * luaH_checkwindow(lua_State *L, gint udx) { widget_t *w = luaH_checkwidget(L, udx); if (w->info->tok != L_TK_WINDOW) luaL_argerror(L, udx, "expected window widget"); return w; } #define luaH_checkwindata(L, udx) ((window_data_t*)(luaH_checkwindow(L, udx)->data)) static void destroy_cb(GtkObject* UNUSED(win), widget_t *w) { /* remove window from global windows list */ g_ptr_array_remove(globalconf.windows, w); lua_State *L = globalconf.L; luaH_object_push(L, w->ref); luaH_object_emit_signal(L, -1, "destroy", 0, 0); lua_pop(L, 1); } static gint luaH_window_set_default_size(lua_State *L) { window_data_t *d = luaH_checkwindata(L, 1); gint width = (gint) luaL_checknumber(L, 2); gint height = (gint) luaL_checknumber(L, 3); gtk_window_set_default_size(d->win, width, height); return 0; } static gint luaH_window_index(lua_State *L, widget_t *w, luakit_token_t token) { window_data_t *d = w->data; switch(token) { LUAKIT_WIDGET_INDEX_COMMON(w) LUAKIT_WIDGET_BIN_INDEX_COMMON(w) LUAKIT_WIDGET_CONTAINER_INDEX_COMMON(w) /* push window class methods */ PF_CASE(SET_DEFAULT_SIZE, luaH_window_set_default_size) /* push string properties */ PS_CASE(TITLE, gtk_window_get_title(d->win)) /* push boolean properties */ PB_CASE(DECORATED, gtk_window_get_decorated(d->win)) PB_CASE(URGENCY_HINT, gtk_window_get_urgency_hint(d->win)) PB_CASE(FULLSCREEN, d->state & GDK_WINDOW_STATE_FULLSCREEN) PB_CASE(MAXIMIZED, d->state & GDK_WINDOW_STATE_MAXIMIZED) /* push integer properties */ PI_CASE(XID, GDK_WINDOW_XID(GTK_WIDGET(d->win)->window)) case L_TK_SCREEN: lua_pushlightuserdata(L, gtk_window_get_screen(d->win)); return 1; default: break; } return 0; } static gint luaH_window_newindex(lua_State *L, widget_t *w, luakit_token_t token) { window_data_t *d = w->data; switch(token) { LUAKIT_WIDGET_NEWINDEX_COMMON(w) LUAKIT_WIDGET_BIN_NEWINDEX_COMMON(w) case L_TK_DECORATED: gtk_window_set_decorated(d->win, luaH_checkboolean(L, 3)); break; case L_TK_URGENCY_HINT: gtk_window_set_urgency_hint(d->win, luaH_checkboolean(L, 3)); break; case L_TK_TITLE: gtk_window_set_title(d->win, luaL_checkstring(L, 3)); break; case L_TK_ICON: gtk_window_set_icon_from_file(d->win, luaL_checkstring(L, 3), NULL); break; case L_TK_SCREEN: if (!lua_islightuserdata(L, 3)) luaL_argerror(L, 3, "expected GdkScreen lightuserdata"); gtk_window_set_screen(d->win, (GdkScreen*)lua_touserdata(L, 3)); gtk_window_present(d->win); break; case L_TK_FULLSCREEN: if (luaH_checkboolean(L, 3)) gtk_window_fullscreen(d->win); else gtk_window_unfullscreen(d->win); return 0; case L_TK_MAXIMIZED: if (luaH_checkboolean(L, 3)) gtk_window_maximize(d->win); else gtk_window_unmaximize(d->win); return 0; default: return 0; } return luaH_object_property_signal(L, 1, token); } static gboolean window_state_cb(GtkWidget* UNUSED(widget), GdkEventWindowState *ev, widget_t *w) { window_data_t *d = (window_data_t*)w->data; d->state = ev->new_window_state; lua_State *L = globalconf.L; luaH_object_push(L, w->ref); if (ev->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) luaH_object_property_signal(L, -1, L_TK_MAXIMIZED); if (ev->changed_mask & GDK_WINDOW_STATE_FULLSCREEN) luaH_object_property_signal(L, -1, L_TK_FULLSCREEN); lua_pop(L, 1); return FALSE; } static void window_destructor(widget_t *w) { g_slice_free(window_data_t, w->data); widget_destructor(w); } widget_t * widget_window(widget_t *w, luakit_token_t UNUSED(token)) { w->index = luaH_window_index; w->newindex = luaH_window_newindex; w->destructor = window_destructor; /* create private window data struct */ window_data_t *d = g_slice_new0(window_data_t); d->widget = w; w->data = d; /* create and setup window widget */ w->widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); d->win = GTK_WINDOW(w->widget); gtk_window_set_wmclass(d->win, "luakit", "luakit"); gtk_window_set_default_size(d->win, 800, 600); gtk_window_set_title(d->win, "luakit"); GdkGeometry hints; hints.min_width = 1; hints.min_height = 1; gtk_window_set_geometry_hints(d->win, NULL, &hints, GDK_HINT_MIN_SIZE); g_object_connect(G_OBJECT(w->widget), LUAKIT_WIDGET_SIGNAL_COMMON(w) "signal::add", G_CALLBACK(add_cb), w, "signal::destroy", G_CALLBACK(destroy_cb), w, "signal::key-press-event", G_CALLBACK(key_press_cb), w, "signal::remove", G_CALLBACK(remove_cb), w, "signal::window-state-event", G_CALLBACK(window_state_cb), w, NULL); /* add to global windows list */ g_ptr_array_add(globalconf.windows, w); return w; } // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80