diff --git a/.gitignore b/.gitignore index 3417a1bf..b747724a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ .*.gz # A handy place to put stuff that git should ignore: -/ignore/ \ No newline at end of file +/ignore/ +/build/ diff --git a/.gitmodules b/.gitmodules index d2f73188..22e3dda5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -21,7 +21,10 @@ url = git://github.com/tautologistics/node-htmlparser.git [submodule "support/cockpit"] path = support/cockpit - url = git://github.com/ajaxorg/cockpit.git + url = git://github.com/mozilla/cockpit.git [submodule "support/pilot"] path = support/pilot url = git://github.com/ajaxorg/pilot.git +[submodule "support/dryice"] + path = support/dryice + url = git://github.com/mozilla/dryice.git \ No newline at end of file diff --git a/LICENSE b/LICENSE index 4dfec04a..853e4fd5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,476 @@ -This software is available under your choice of the following licenses: +Licensed under the tri-license MPL/LGPL/GPL. -* MPL 1.1 or later: http://www.mozilla.org/MPL/ -* GPL 2.0 or later: http://www.gnu.org/licenses/gpl.html -* LGPL 2.1 or later: http://www.gnu.org/licenses/lgpl.html \ No newline at end of file + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 \ No newline at end of file diff --git a/Makefile.dryice.js b/Makefile.dryice.js new file mode 100644 index 00000000..05a5dd08 --- /dev/null +++ b/Makefile.dryice.js @@ -0,0 +1,1060 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org Services B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabian Jakobs + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +//var copy = require('dryice').copy; +var copy = internalRequire('dryice').copy; + +var aceHome = __dirname; + +// Pilot sources +var pilot = copy.createDataObject(); +copy({ + source: [ { + root: aceHome + '/support/cockpit/support/pilot/lib', + include: /.*\.js$/, + exclude: /tests?\// + } ], + filter: [ copy.filter.moduleDefines ], + dest: pilot +}); + +// Cockpit sources +var cockpit = copy.createDataObject(); +copy({ + source: [ { + root: aceHome + '/support/cockpit/lib', + include: /.*\.js$/, + exclude: /tests?\// + } ], + filter: [ copy.filter.moduleDefines ], + dest: cockpit +}); +copy({ + source: [ { + root: aceHome + '/support/cockpit/lib', + include: /.*\.css$|.*\.html$/, + exclude: /tests?\// + } ], + filter: [ copy.filter.addDefines ], + dest: cockpit +}); +copy({ + source: [ { + root: aceHome + '/support/cockpit/lib', + include: /.*\.png$|.*\.gif$/, + exclude: /tests?\// + } ], + filter: [ copy.filter.base64 ], + dest: cockpit +}); + +// Ace sources +var ace = copy.createDataObject(); +copy({ + source: [ + // Exclude all themes/modes so we can just include textmate/js + { + root: aceHome + '/lib', + include: /.*\.js$/, + exclude: /tests?\/|theme\/|mode\// + }, + { base: aceHome + '/lib/', path: 'ace/theme/textmate.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/text.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/javascript.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/text_highlight_rules.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/javascript_highlight_rules.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/doc_comment_highlight_rules.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/matching_brace_outdent.js' }, + { base: aceHome + '/lib/', path: 'ace/mode/javascript_highlight_rules.js' } + ], + filter: [ copy.filter.moduleDefines ], + dest: ace +}); +copy({ + source: [ { + root: aceHome + '/lib', + include: /tm\.css|editor\.css/, + exclude: /tests?\// + } ], + filter: [ copy.filter.addDefines ], + dest: ace +}); + +// Piece together the parts that we want +var data = copy.createDataObject(); +copy({ + source: [ + 'build_support/mini_require.js', + pilot, + // cockpit, + ace, + 'build_support/boot.js' + ], + dest: data +}); + +copy({ + source: [ + 'build_support/editor.html' + ], + dest: 'build/editor.html' +}); + +// Create the compressed and uncompressed output files +copy({ + source: data, + filter: copy.filter.uglifyjs, + dest: 'build/ace.js' +}); +copy({ + source: data, + dest: 'build/ace-uncompressed.js' +}); + + + + + +function internalRequire(ignore) { +var exports = {}; + + + +var fs = require("fs"); +var ujs = require("uglify-js"); + +/** + * Dryice is ant for javascript + * @param obj An object that contains source, dest and (optionally) filter objects + * source must be one of: + * - string: a file to read (later a directory too) + * - a data object like { value:OUTPUT_DATA } + * - a findObj like { root:DIR, include:RegExp|[RegExp], exclude:RegExp|[RegExp] } + * - a baseObj like { base:BASE, path:PATH } where BASE+PATH = filename + * (this method allows use of commonjs filters) + * - an array containing entries like the 3 above + * - a function which returns one of the 4 above + * dest must be either a string (pointing to a file) or a data object + */ +function copy(obj) { + // Gather a list of all the input sources + addSource(obj, obj.source); + + // Concatenate all the input sources + var value = ''; + obj.sources.forEach(function(source) { + value += source.value; + }, this); + + // Run filters where onRead=false + value = runFilters(value, obj.filter, false); + + // Output + // TODO: for now we're ignoring the concept of directory destinations. + if (typeof obj.dest.value === 'string') { + obj.dest.value += value; + } + else if (typeof obj.dest === 'string') { + fs.writeFileSync(obj.dest, value); + } + else { + throw new Error('Can\'t handle type of dest: ' + typeof obj.dest); + } +} + +function addName(currentName, newName) { + return currentName === null ? currentName : newName; +} + +function addSource(obj, source) { + if (!obj.sources) { + obj.sources = []; + } + + if (typeof source === 'function') { + addSource(obj, source()); + } + else if (Array.isArray(source)) { + source.forEach(function(s) { + addSource(obj, s); + }, this); + } + else if (source.root) { + copy.findFiles(obj, source); + } + else if (source.base) { + addSourceBase(obj, source); + } + else if (typeof source === 'string') { + addSourceFile(obj, source); + } + else if (typeof source.value === 'string') { + if (!source.filtered) { + source.value = runFilters(source.value, obj.filter, true, source.name); + source.filtered = true; + } + obj.sources.push(source); + } + else { + throw new Error('Can\'t handle type of source: ' + typeof source); + } +} + +function addSourceFile(obj, filename) { + var read = fs.readFileSync(filename); + obj.sources.push({ + name: filename, + value: runFilters(read, obj.filter, true, filename) + }); +} + +function addSourceBase(obj, baseObj) { + var read = fs.readFileSync(baseObj.base + baseObj.path); + obj.sources.push({ + name: baseObj, + value: runFilters(read, obj.filter, true, baseObj) + }); +} + +function runFilters(value, filter, reading, name) { + if (!filter) { + return value; + } + + if (Array.isArray(filter)) { + filter.forEach(function(f) { + value = runFilters(value, f, reading, name); + }, this); + return value; + } + + if (filter.onRead == reading) { + return filter(value, name); + } + else { + return value; + } +} + +/** + * A holder is an in-memory store of a result of a copy operation. + *
+ * var holder = copy.createDataObject();
+ * copy({ source: 'x.txt', dest: holder });
+ * copy({ source: 'y.txt', dest: holder });
+ * copy({ source: holder, dest: 'z.txt' });
+ * 
+ */ +copy.createDataObject = function() { + return { value: '' }; +}; + +/** + * An object that contains include and exclude object + */ +copy.findFiles = function(obj, findObj) { + if (!findObj.filter) { + findObj.filter = createFilterFromRegex(findObj); + } + if (!findObj.path) { + findObj.path = ''; + } + + if (findObj.root.length > 0 && findObj.root.substr(-1) !== '/') { + findObj.root += '/'; + } + var path = findObj.path; + if (path.length > 0 && path.substr(-1) !== '/') { + path += '/'; + } + + fs.readdirSync(findObj.root + findObj.path).forEach(function(entry) { + var stat = fs.statSync(findObj.root + path + entry); + if (stat.isFile()) { + if (findObj.filter(path + entry)) { + addSourceBase(obj, { + base: findObj.root, + path: path + entry + }); + } + } + else if (stat.isDirectory()) { + findObj.path = path + entry; + copy.findFiles(obj, findObj); + } + }, this); +}; + +function createFilterFromRegex(obj) { + return function(path) { + function noPathMatch(pattern) { + return !pattern.test(path); + } + if (obj.include instanceof RegExp) { + if (noPathMatch(obj.include)) { + return false; + } + } + if (typeof obj.include === 'string') { + if (noPathMatch(new RegExp(obj.include))) { + return false; + } + } + if (Array.isArray(obj.include)) { + if (obj.include.every(noPathMatch)) { + return false; + } + } + + function pathMatch(pattern) { + return pattern.test(path); + } + if (obj.exclude instanceof RegExp) { + if (pathMatch(obj.exclude)) { + return false; + } + } + if (typeof obj.exclude === 'string') { + if (pathMatch(new RegExp(obj.exclude))) { + return false; + } + } + if (Array.isArray(obj.exclude)) { + if (obj.exclude.some(pathMatch)) { + return false; + } + } + + return true; + }; +} + +/** + * File filters + */ +copy.filter = {}; + +/** + * Compress the given input code using UglifyJS. + * + * @param string input + * @return string output + */ +copy.filter.uglifyjs = function(input) { + if (typeof input !== 'string') { + input = input.toString(); + } + + var opt = copy.filter.uglifyjs.options; + var ast = ujs.parser.parse(input, opt.parse_strict_semicolons); + + if (opt.mangle) { + ast = ujs.uglify.ast_mangle(ast, opt.mangle_toplevel); + } + + if (opt.squeeze) { + ast = ujs.uglify.ast_squeeze(ast, opt.squeeze_options); + if (opt.squeeze_more) { + ast = ujs.uglify.ast_squeeze_more(ast); + } + } + + return ujs.uglify.gen_code(ast, opt.beautify); +}; +copy.filter.uglifyjs.onRead = false; +/** + * UglifyJS filter options. + */ +copy.filter.uglifyjs.options = { + parse_strict_semicolons: false, + + /** + * The beautify argument used for process.gen_code(). See the UglifyJS + * documentation. + */ + beautify: false, + mangle: true, + mangle_toplevel: false, + squeeze: true, + + /** + * The options argument used for process.ast_squeeze(). See the UglifyJS + * documentation. + */ + squeeze_options: {}, + + /** + * Tells if you want to perform potentially unsafe compression. + */ + squeeze_more: false +}; + +/** + * A filter to munge CommonJS headers + */ +copy.filter.addDefines = function(input, source) { + if (typeof input !== 'string') { + input = input.toString(); + } + + if (!source) { + throw new Error('Missing filename for moduleDefines'); + } + + if (source.base) { + source = source.path; + } + + var module = source.replace(/\.css$/, ''); + + input = input.replace(/"/g, '\\"'); + input = '"' + input.replace(/\n/g, '" +\n "') + '"'; + + return 'define("text!' + source.toString() + '", ' + input + ');\n\n'; +}; +copy.filter.addDefines.onRead = true; + +/** + * + */ +copy.filter.base64 = function(input, source) { + if (typeof input === 'string') { + throw new Error('base64 filter needs to be the first in a filter set'); + } + + if (!source) { + throw new Error('Missing filename for moduleDefines'); + } + + if (source.base) { + source = source.path; + } + + if (source.substr(-4) === '.png') { + input = 'data:image/png;base64,' + input.toString('base64'); + } + else if (source.substr(-4) === '.gif') { + input = 'data:image/gif;base64,' + input.toString('base64'); + } + else { + throw new Error('Only gif/png supported by base64 filter: ' + source); + } + + return 'define("text!' + source + '", "' + input + '");\n\n'; +}; +copy.filter.base64.onRead = true; + +/** + * + */ +copy.filter.moduleDefines = function(input, source) { + if (typeof input !== 'string') { + input = input.toString(); + } + + if (!source) { + throw new Error('Missing filename for moduleDefines'); + } + + if (source.base) { + source = source.path; + } + source = source.replace(/\.js$/, ''); + + return input.replace(/\bdefine\(\s*function\(require,\s*exports,\s*module\)\s*\{/, + "define('" + source + "', function(require, exports, module) {"); +}; +copy.filter.moduleDefines.onRead = true; + + +exports.copy = copy; + + + + + + + + + + + +return exports; +}; + + + + + +var sys = require("sys"); +var fs = require("fs"); +var path = require("path"); +var util = require("util"); +var Step = require("step"); + + +function copy0(options, callback) { + var source = options.source; + var data = ""; + + if (typeof source == "function") { + source = source(options); + } + + if (typeof source == "string") { + source = [source]; + } else if (typeof options.source == "object") { + data = source.value || ""; + } + + var filters = options.filter || []; + if (typeof filters == "function") { + filters = [filters]; + } + + if (typeof dest == "string") { + var stat = fs.statSync(); + } + + // read the files + if (Array.isArray(source)) { + source.forEach(function(file) { + var stat = fs.statSync(file); + var result = fs.readFileSync(file); + + // execute the filters + filters.forEach(function(filter) { + filter(input, file); + }); + + data += result + "\n"; + }); + } + + // save the output + var dest = options.dest; + if (typeof dest == "string") { + fs.writeFileSync(dest, data); + } else { + dest.value = data; + } +} + + +var Builder = function Builder(appFolder) { + this.appFolder = appFolder; +}; + +Builder.prototype = { + DEBUG: false, + + /** + * Web application folder - the location where scripts, styles and resources + * are fetched from. + */ + appFolder: ".", + + /** + * Target build folder - the location where the packaged web application is + * saved to. + */ + buildFolder: "./build", + + /** + * The default folder and file modes (permissions for chmod). + */ + folderMode: 0755, + fileMode: 0644, + + /** + * Array holding regular expression patterns. When you add entire folders to + * your build you might want to skip certain files. + */ + ignoreFiles: [], + + log: sys.puts, + + debug: function(message) { + if (this.DEBUG) { + this.log(message); + } + }, + + /** + * Sets and creates the target build folder, asynchronously. + * + * @param string folder + * Tells the target folder where you want to save the packaged web + * application. + * + * @param function callback + * The callback you want executed after the folder is created. + * Callback arguments: + * string|Exception|Error error - Holds the error that occurred + * while trying to set the build folder. This is undefined|null + * when the operation was successful. + * + * Builder build - holds a reference to the build instance. + */ + setBuildFolder: function Builder_setBuildFolder(newFolder, callback) + { + var self = this; + + Step( + function checkFolder() { + path.exists(newFolder, this); + }, + + function makeFolder(exists) { + if (!exists) { + fs.mkdir(newFolder, self.folderMode, this); + } else { + return true; + } + }, + + function folderReady(err) { + if (!err) { + self.buildFolder = newFolder; + } + callback(err, self); + } + ); + }, + + /** + * Copy files from the web application folder to the target build folder. + * + * @param object|array|string aFiles + * Holds the list of files you want to copy to the target build + * folder. If this is a string, you copy only one file. If the + * argument is given as an object, keys are considered source files + * and values are considered target files - this allows you to copy + * files to different locations in the build folder, without + * maintaining the same structure as in the original folder. + * @param function callback + * The callback you want executed after the files are copied. The + * callback arguments are the same as for setBuildFolder(). + * @returns void + */ + copyFiles: function Builder_copyFiles(aFiles, callback) + { + var files = {}; + if (typeof aFiles == "string") { + files[aFiles] = aFiles; + } else if (Array.isArray(aFiles)) { + aFiles.forEach(function(file) { + files[file] = file; + }); + } else { + files = aFiles; + } + + var self = this; + + Step( + function copyFiles() { + var group = this.group(); + for (var file in files) { + fs_copyFile(self.appFolder + "/" + file, + self.buildFolder + "/" + files[file], + group()); + } + }, + + function filesReady(err) { + callback(err, self); + } + ); + }, + + /** + * Copy folders from the web application folder to the target build folder. + * + * @param object|array|string aFolders + * Holds the list of folders you want to copy to the target build + * folder. If this is a string, you copy only one folder. If the + * argument is given as an object, keys are considered source folders + * and values are considered target folders - this allows you to copy + * folders to different locations in the build folder, without + * maintaining the same structure as in the original folder. + * @param function callback + * The callback you want executed after the folders are copied. The + * callback arguments are the same as for setBuildFolder(). + * @param boolean [recursive=true] + * (Optional) True if you want to copy subfolders as well, false if + * you want to copy only the files contained in the folders given. + * Default is true. + * @returns void + */ + copyFolders: function Builder_copyFolders(aFolders, callback, recursive) + { + var folders = {}; + if (typeof aFolders == "string") { + folders[aFolders] = aFolders; + } else if (Array.isArray(aFolders)) { + folders.forEach(function(folder) { + folders[folder] = folder; + }); + } else { + folders = aFolders; + } + if (typeof recursive == "undefined") { + recursive = true; + } + + var self = this; + + Step( + function copyFolders() { + var group = this.group(); + for (var folder in folders) { + self.copyFolder(folder, folders[folder], group(), recursive); + } + }, + function foldersReady(err) { + callback(err, self); + } + ); + }, + + /** + * Copy one folder from the web application folder to the target build + * folder. + * + * @param string source + * The folder you want to copy. + * @param string [destination=source] + * The destination folder. By default this is the same as the source, + * but in the buildFolder. You can change the destination, relative + * to the buildFolder. + * @param function callback + * The callback you want executed after the folder is copied. The + * callback arguments are the same as for setBuildFolder(). + * @param boolean [recursive=true] + * (Optional) True if you want to copy the subfolders as well, false + * if you want to copy only the files contained in the given source + * folder. Default is true. + * @returns void + */ + copyFolder: function Builder_copyFolder() + { + var source, destination, recursive, callback; + switch (arguments.length) { + case 2: // source, callback + source = destination = arguments[0]; + callback = arguments[1]; + recursive = true; + break; + case 3: + // source, callback, recursive + // OR source, destination, callback + source = arguments[0]; + if (typeof arguments[1] == "function") { + destination = source; + callback = arguments[1]; + recursive = arguments[2]; + } else { + destination = arguments[1]; + callback = arguments[2]; + recursive = true; + } + break; + case 4: // source, destination, callback, recursive + source = arguments[0]; + destination = arguments[1]; + callback = arguments[2]; + recursive = arguments[3]; + break; + default: + throw new Error("Invalid arguments."); + } + + var self = this; + + // result from fs.readdir(), without . and .. + var readdir_files = []; + + var files = []; + var subfolders = []; + + Step( + function checkDestination() { + path.exists(self.buildFolder + "/" + destination, this); + }, + + function mkdir_destination(exists) { + if (!exists) { + fs.mkdir(self.buildFolder + "/" + destination, this); + } else { + return true; + } + }, + + function readdir_source(err) { + if (err) { + throw err; + } + + // find the files in the given source folder. + fs.readdir(self.appFolder + "/" + source, this); + }, + + function statFiles(err, result) { + if (err) { + throw err; + } + + var group = this.group(); + result.forEach(function(file) { + if (file != "." && file != ".." && + !self.shouldSkipFile("/" + source + "/" + file)) { + fs.stat(file, group()); + readdir_files.push(file); + } + }); + }, + + function copyFiles(err, stats) { + if (err) { + throw err; + } + + // separate files out from folders + stats.forEach(function(stat, index) { + if (stat.isDirectory()) { + subfolders.push(readdir_files[index]); + } else if (stat.isFile()) { + files.push(readdir_files[index]); + } + }); + + // copy the files + var group = this.group(); + files.forEach(function(file) { + fs_copyFile(self.appFolder + "/" + source + "/" + file, + self.buildFolder + "/" + destination + "/" + file, + group()); + }); + }, + + function copySubfolders(err) { + if (err) { + throw err; + } + + if (!recursive) { + return true; + } + + var group = this.group(); + subfolders.forEach(function(folder) { + self.copyFolder(source + "/" + folder, + destination + "/" + folder, + group(), true); + }); + }, + + function copyDone(err) { + callback(err, self); + } + ); + }, + + /** + * Tells if this builder should ignore a file, given the file name. The + * this.ignoreFiles array of regular expression patterns is used. + * + * @param string filename + * @returns boolean + * True if the file should be ignored, or false otherwise. + */ + shouldSkipFile: function Builder_shouldSkipFile(filename) + { + return this.ignoreFiles.some(function(pattern) { + return pattern.test(filename); + }); + } +}; + +Builder.executeManifest = function Builder_executeManifest(manifest, callback) { + return; // stub +}; + +var Script = function Script(build, filename) { + if (!build) { + throw new Error("The first argument must reference a dryice.Builder instance."); + } else if (!filename) { + throw new Error("The second argument must be the script file name."); + } + + this.build = build; + this.filename = filename; +}; + +Script.prototype = { + inputEncoding: "utf8", + outputEncoding: "utf8", + + /** + * List of functions or filter names that process each input file. + */ + inputFilters: [], + + /** + * List of functions or filter names that process the concatenated output + * file. + */ + outputFilters: [], + + /** + * Add files to the compiled script. + * + * @param array|string files + * @param function callback + * @returns void + */ + addFiles: function Builder_addFiles(files, callback) { + if (typeof files == "string") { + files = [files]; + } + + var self = this; + + Step( + function readFiles() { + var group = this.group(); + + files.forEach(function(file) { + fs.readFile(self.build.appFolder + "/" + file, + self.inputEncoding, group()); + }); + }, + + function filterFiles(err, data) { + if (err) { + throw err; + } + + for (var i = 0; i < data.length; i++) { + data[i] = self.executeFilters(self.inputFilters, data[i], + files[i]); + } + + self.output += data.join("\n"); + + return self; + }, + + callback + ); + }, + + run: function Builder_run(buildCallback) { + var self = this; + + Step( + function readFiles() { + var group = this.group(); + + self.input_files.forEach(function(file) { + fs.readFile(self.basedir + "/" + file, + self.input_encoding, group()); + }); + }, + + function filterInput(err, files) { + if (err) { + throw err; + } + + for (var i = 0; i < files.length; i++) { + files[i] = self.run_filters(self.input_filters, files[i], + self.input_files[i]); + } + + return files; + }, + + function postProcessOutput(err, output) { + if (err) { + throw err; + } + + output = output.join("\n"); + output = self.run_filters(self.output_filters, output); + + if (typeof self.output_file == "string") { + fs.writeFile(self.basedir + "/" + self.output_file, + output, self.output_encoding, this); + } else { + self.output_file.write(output); + self.output_file.end(); + return 1; + } + }, + + function buildComplete(err) { + buildCallback(err, self); + } + ); + }, + + /** + * Given an array of filters (functions or filter names), invoke these + * filters on the given input content. + * + * @param array filters + * @param string input + * @param string [filename] + * Optional filename, useful for some filters. + * @return string + * Final filtered output. + */ + executeFilters: function(filters, input, filename) { + filters.forEach(function(filter) { + if (typeof filter == "string") { + input = input_filters[filter].call(this, input, filename); + } else { + input = filter.call(this, input, filename); + } + }, this); + return input; + } +}; + + diff --git a/build_support/boot.js b/build_support/boot.js new file mode 100644 index 00000000..e311cebc --- /dev/null +++ b/build_support/boot.js @@ -0,0 +1,75 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kevin Dangoor (kdangoor@mozilla.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", + "pilot/environment" ]; + +require(deps, function() { + var catalog = require("pilot/plugin_manager").catalog; + catalog.registerPlugins([ "pilot/index" ]); +}); + +var ace = { + edit: function(el) { + if (typeof(el) == "string") { + el = document.getElementById(el); + } + var env = require("pilot/environment").create(); + var catalog = require("pilot/plugin_manager").catalog; + catalog.startupPlugins({ env: env }).then(function() { + var Document = require("ace/document").Document; + var JavaScriptMode = require("ace/mode/javascript").Mode; + var UndoManager = require("ace/undomanager").UndoManager; + var Editor = require("ace/editor").Editor; + var Renderer = require("ace/virtual_renderer").VirtualRenderer; + var theme = require("ace/theme/textmate"); + + var doc = new Document(el.innerHTML); + el.innerHTML = ''; + doc.setMode(new JavaScriptMode()); + doc.setUndoManager(new UndoManager()); + env.document = doc; + env.editor = new Editor(new Renderer(el, theme)); + env.editor.setDocument(doc); + env.editor.resize(); + window.addEventListener("resize", function() { + env.editor.resize(); + }, false); + el.env = env; + }); + } +}; diff --git a/build_support/editor.html b/build_support/editor.html new file mode 100644 index 00000000..8ff3e8d9 --- /dev/null +++ b/build_support/editor.html @@ -0,0 +1,28 @@ + + + + + + Editor + + + + +
function foo(items) { + for (var i = 0; i < items.length; i++) { + alert("Ace Rocks " + items[i]); + } +}
+ + + + + + diff --git a/build_support/mini_require.js b/build_support/mini_require.js new file mode 100644 index 00000000..77b12c27 --- /dev/null +++ b/build_support/mini_require.js @@ -0,0 +1,93 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org Services B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabian Jakobs + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +function require(module, callback) { + + if (Array.isArray(module)) { + var params = []; + module.forEach(function(m) { + params.push(require._lookup(m)); + }, this); + + if (callback) { + callback.apply(null, params); + } + } + + if (typeof module === 'string') { + payload = require._lookup(module); + if (callback) { + callback(); + } + return payload; + } +} +require.modules = {}; + +require._lookup = function(moduleName) { + var payload = require.modules[moduleName]; + var module_name = moduleName; + if (payload == null) { + console.error('Missing module: ' + moduleName); + console.trace(); + } + + if (typeof payload === 'function') { + var exports = {}; + var module = { + id: moduleName, + uri: '' + }; + payload(require, exports, module); + payload = exports; + // cache the resulting module object for next time + require.modules[module_name] = payload; + } + + return payload; +}; + +function define(module, payload) { + if (typeof module !== 'string') { + console.error('dropping module because define wasn\'t munged.'); + console.trace(); + return; + } + + console.log('defining module: ' + module + ' as a ' + typeof payload); + require.modules[module] = payload; +} diff --git a/demo/boot.js b/demo/boot.js index 086b59ae..e84bfe3f 100644 --- a/demo/boot.js +++ b/demo/boot.js @@ -44,8 +44,11 @@ var config = { } }; -require(config, ["pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", -"pilot/environment", "demo/startup"], function() { +var deps = [ "pilot/fixoldbrowsers", "pilot/plugin_manager", "pilot/settings", + "pilot/environment", "demo/startup" ]; + +require(config); +require(deps, function() { var catalog = require("pilot/plugin_manager").catalog; catalog.registerPlugins([ "pilot/index", "cockpit/index" ]).then(function() { var env = require("pilot/environment").create(); diff --git a/demo/startup.js b/demo/startup.js index ae3a9e86..bb4663b7 100644 --- a/demo/startup.js +++ b/demo/startup.js @@ -55,6 +55,10 @@ exports.launch = function(env) { var TextMode = require("ace/mode/text").Mode; var UndoManager = require("ace/undomanager").UndoManager; + var vim = require("ace/keyboard/vim").Vim; + var emacs = require("ace/keyboard/emacs").Emacs; + var HashHandler = require("ace/keyboard/hash_handler").HashHandler; + var docs = {}; docs.js = new Document(document.getElementById("jstext").innerHTML); @@ -80,6 +84,7 @@ exports.launch = function(env) { var container = document.getElementById("editor"); env.editor = new Editor(new Renderer(container, theme)); +<<<<<<< HEAD:demo/startup.js var modes = { text: new TextMode(), @@ -102,6 +107,17 @@ exports.launch = function(env) { } modeEl.onchange = setMode; setMode(); +======= + // This is how you can set a custom keyboardHandler. + // + // Define some basic keymapping using a hash: + // env.editor.setKeyboardHandler(new HashHandler({ + // "gotoright": "Tab" + // })); + // + // Use a more complex keymapping: + // env.editor.setKeyboardHandler(vim); +>>>>>>> jviereck/keymapping:demo/demo_startup.js var docEl = document.getElementById("doc"); function onDocChange() { @@ -235,4 +251,4 @@ exports.launch = function(env) { }); }; -}); \ No newline at end of file +}); diff --git a/lib/ace/commands/default_commands.js b/lib/ace/commands/default_commands.js index 7ff66bf9..817e8fab 100644 --- a/lib/ace/commands/default_commands.js +++ b/lib/ace/commands/default_commands.js @@ -20,6 +20,7 @@ * * Contributor(s): * Fabian Jakobs + * Julian Viereck * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,8 +38,14 @@ define(function(require, exports, module) { +var lang = require("pilot/lang"); var canon = require("pilot/canon"); +canon.addCommand({ + name: "null", + exec: function(env, args, request) { } +}); + canon.addCommand({ name: "selectall", exec: function(env, args, request) { env.editor.getSelection().selectAll(); } @@ -113,7 +120,7 @@ canon.addCommand({ }); canon.addCommand({ name: "golineup", - exec: function(env, args, request) { env.editor.navigateUp(); } + exec: function(env, args, request) { env.editor.navigateUp(args.times); } }); canon.addCommand({ name: "copylinesdown", @@ -136,8 +143,8 @@ canon.addCommand({ exec: function(env, args, request) { env.editor.getSelection().selectDown(); } }); canon.addCommand({ - name: "godown", - exec: function(env, args, request) { env.editor.navigateDown(); } + name: "golinedown", + exec: function(env, args, request) { env.editor.navigateDown(args.times); } }); canon.addCommand({ name: "selectwordleft", @@ -161,7 +168,7 @@ canon.addCommand({ }); canon.addCommand({ name: "gotoleft", - exec: function(env, args, request) { env.editor.navigateLeft(); } + exec: function(env, args, request) { env.editor.navigateLeft(args.times); } }); canon.addCommand({ name: "selectwordright", @@ -185,7 +192,7 @@ canon.addCommand({ }); canon.addCommand({ name: "gotoright", - exec: function(env, args, request) { env.editor.navigateRight(); } + exec: function(env, args, request) { env.editor.navigateRight(args.times); } }); canon.addCommand({ name: "selectpagedown", @@ -243,5 +250,12 @@ canon.addCommand({ name: "indent", exec: function(env, args, request) { env.editor.indent(); } }); +canon.addCommand({ + name: "inserttext", + exec: function(env, args, request) { + env.editor.insert(lang.stringRepeat(args.text || "", + args.times || 1)); + } +}); }); diff --git a/lib/ace/conf/keybindings/default_mac.js b/lib/ace/conf/keybindings/default_mac.js index 788ed577..99b7d6d3 100644 --- a/lib/ace/conf/keybindings/default_mac.js +++ b/lib/ace/conf/keybindings/default_mac.js @@ -60,7 +60,7 @@ exports.bindings = { "selecttoend": "Command-Shift-Down", "gotoend": "Command-End|Command-Down", "selectdown": "Shift-Down", - "godown": "Down", + "golinedown": "Down", "selectwordleft": "Option-Shift-Left", "gotowordleft": "Option-Left", "selecttolinestart": "Command-Shift-Left", diff --git a/lib/ace/conf/keybindings/default_win.js b/lib/ace/conf/keybindings/default_win.js index 3ae134b9..a27c098d 100644 --- a/lib/ace/conf/keybindings/default_win.js +++ b/lib/ace/conf/keybindings/default_win.js @@ -60,7 +60,7 @@ exports.bindings = { "selecttoend": "Alt-Shift-Down", "gotoend": "Ctrl-End|Ctrl-Down", "selectdown": "Shift-Down", - "godown": "Down", + "golinedown": "Down", "selectwordleft": "Ctrl-Shift-Left", "gotowordleft": "Ctrl-Left", "selecttolinestart": "Alt-Shift-Left", diff --git a/lib/ace/document.js b/lib/ace/document.js index c7892eaf..0aba7bd4 100644 --- a/lib/ace/document.js +++ b/lib/ace/document.js @@ -87,10 +87,12 @@ var Document = function(text, mode) { this.modified = true; this.fireChangeEvent(0); }; - + this.toString = function() { return this.$lines.join(this.$getNewLineCharacter()); }; + + this.getValue = this.toString; this.getSelection = function() { return this.selection; diff --git a/lib/ace/editor.js b/lib/ace/editor.js index 85ef430a..2ca39c43 100644 --- a/lib/ace/editor.js +++ b/lib/ace/editor.js @@ -40,8 +40,8 @@ define(function(require, exports, module) { var oop = require("pilot/oop"); var event = require("pilot/event"); var lang = require("pilot/lang"); -var TextInput = require("ace/textinput").TextInput; -var KeyBinding = require("ace/keybinding").KeyBinding; +var TextInput = require("ace/keyboard/textinput").TextInput; +var KeyBinding = require("ace/keyboard/keybinding").KeyBinding; var Document = require("ace/document").Document; var Search = require("ace/search").Search; var BackgroundTokenizer = require("ace/background_tokenizer").BackgroundTokenizer; @@ -54,7 +54,7 @@ var Editor =function(renderer, doc) { this.renderer = renderer; this.textInput = new TextInput(container, this); - this.keyBinding = new KeyBinding(container, this); + this.keyBinding = new KeyBinding(this); var self = this; event.addListener(container, "mousedown", function(e) { setTimeout(function() {self.focus();}); @@ -64,7 +64,7 @@ var Editor =function(renderer, doc) { return event.preventDefault(e); }); - var mouseTarget = renderer.getMouseEventTarget(); + var mouseTarget = renderer.getMouseEventTarget(); event.addListener(mouseTarget, "mousedown", this.onMouseDown.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 2, 500, this.onMouseDoubleClick.bind(this)); event.addMultiMouseDownListener(mouseTarget, 0, 3, 600, this.onMouseTripleClick.bind(this)); @@ -110,6 +110,14 @@ var Editor =function(renderer, doc) { } }; + this.setKeyboardHandler = function(keyboardHandler) { + this.keyBinding.setKeyboardHandler(keyboardHandler); + }; + + this.getKeyboardHandler = function() { + return this.keyBinding.getKeyboardHandler(); + } + this.setDocument = function(doc) { if (this.doc == doc) return; @@ -403,7 +411,7 @@ var Editor =function(renderer, doc) { } }; - this.onTextInput = function(text) { + this.insert = function(text) { if (this.$readOnly) return; @@ -477,6 +485,14 @@ var Editor =function(renderer, doc) { this.moveCursorToPosition(end); this.renderer.scrollCursorIntoView(); + } + + this.onTextInput = function(text) { + this.keyBinding.onTextInput.call(this.keyBinding, text); + }; + + this.onCommandKey = function(e, hashId, keyCode) { + this.keyBinding.onCommandKey(e, hashId, keyCode); }; this.$overwrite = false; @@ -589,7 +605,7 @@ var Editor =function(renderer, doc) { if (this.selection.isEmpty()) this.selection.selectLeft(); - + this.moveCursorToPosition(this.doc.remove(this.getSelectionRange())); this.clearSelection(); }; @@ -852,9 +868,9 @@ var Editor =function(renderer, doc) { this.$updateDesiredColumn(column); }; - this.navigateUp = function() { + this.navigateUp = function(times) { this.selection.clearSelection(); - this.selection.moveCursorBy(-1, 0); + this.selection.moveCursorBy(-(times || 1), 0); if (this.$desiredColumn) { var cursor = this.getCursorPosition(); @@ -863,9 +879,9 @@ var Editor =function(renderer, doc) { } }; - this.navigateDown = function() { + this.navigateDown = function(times) { this.selection.clearSelection(); - this.selection.moveCursorBy(1, 0); + this.selection.moveCursorBy(times || 1, 0); if (this.$desiredColumn) { var cursor = this.getCursorPosition(); @@ -879,24 +895,30 @@ var Editor =function(renderer, doc) { this.$desiredColumn = this.doc.documentToScreenColumn(cursor.row, cursor.column); }; - this.navigateLeft = function() { + this.navigateLeft = function(times) { if (!this.selection.isEmpty()) { var selectionStart = this.getSelectionRange().start; this.moveCursorToPosition(selectionStart); } else { - this.selection.moveCursorLeft(); + times = times || 1; + while (times--) { + this.selection.moveCursorLeft(); + } } this.clearSelection(); }; - this.navigateRight = function() { + this.navigateRight = function(times) { if (!this.selection.isEmpty()) { var selectionEnd = this.getSelectionRange().end; this.moveCursorToPosition(selectionEnd); } else { - this.selection.moveCursorRight(); + times = times || 1; + while (times--) { + this.selection.moveCursorRight(); + } } this.clearSelection(); }; diff --git a/lib/ace/keyboard/emacs.js b/lib/ace/keyboard/emacs.js new file mode 100644 index 00000000..ce0cbc06 --- /dev/null +++ b/lib/ace/keyboard/emacs.js @@ -0,0 +1,149 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Julian Viereck (julian.viereck@gmail.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var StateHandler = require("ace/keyboard/state_handler").StateHandler; +var matchCharacterOnly = require("ace/keyboard/state_handler").matchCharacterOnly; + +var emacsState = { + start: [ + { + key: "ctrl-x", + then: "c-x" + }, + { + regex: [ "(?:command-([0-9]*))*", "(down|ctrl-n)" ], + exec: "golinedown", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "(?:command-([0-9]*))*", "(right|ctrl-f)" ], + exec: "gotoright", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "(?:command-([0-9]*))*", "(up|ctrl-p)" ], + exec: "golineup", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "(?:command-([0-9]*))*", "(left|ctrl-b)" ], + exec: "gotoleft", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + comment: "This binding matches all printable characters except numbers as long as they are no numbers and print them n times.", + regex: [ "(?:command-([0-9]*))", "([^0-9]+)*" ], + match: matchCharacterOnly, + exec: "inserttext", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: "1" + }, + { + name: "text", + match: 2 + } + ] + }, + { + comment: "This binding matches numbers as long as there is no meta_number in the buffer.", + regex: [ "(command-[0-9]*)*", "([0-9]+)" ], + match: matchCharacterOnly, + disallowMatches: [ 1 ], + exec: "inserttext", + params: [ + { + name: "text", + match: 2, + type: "text" + } + ] + }, + { + regex: [ "command-([0-9]*)", "(command-[0-9]|[0-9])" ], + comment: "Stops execution if the regex /meta_[0-9]+/ matches to avoid resetting the buffer." + } + ], + "c-x": [ + { + key: "ctrl-g", + then: "start" + }, + { + key: "ctrl-s", + exec: "save", + then: "start" + } + ] +}; + +exports.Emacs = new StateHandler(emacsState); + +}); diff --git a/lib/ace/keybinding.js b/lib/ace/keyboard/hash_handler.js similarity index 54% rename from lib/ace/keybinding.js rename to lib/ace/keyboard/hash_handler.js index db54ac00..f968b39b 100644 --- a/lib/ace/keybinding.js +++ b/lib/ace/keyboard/hash_handler.js @@ -11,15 +11,16 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is Ajax.org Code Editor (ACE). + * The Original Code is Mozilla Skywriter. * * The Initial Developer of the Original Code is - * Ajax.org Services B.V. - * Portions created by the Initial Developer are Copyright (C) 2010 + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Fabian Jakobs + * Fabian Jakobs + * Julian Viereck (julian.viereck@gmail.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,72 +38,13 @@ define(function(require, exports, module) { -var useragent = require("pilot/useragent"); -var event = require("pilot/event"); -var default_mac = require("ace/conf/keybindings/default_mac").bindings; -var default_win = require("ace/conf/keybindings/default_win").bindings; -var canon = require("pilot/canon"); -require("ace/commands/default_commands"); +var keyUtil = require("pilot/keys"); -var KeyBinding = function(element, editor, config) { +function HashHandler(config) { this.setConfig(config); - - var _self = this; - event.addKeyListener(element, function(e) { - // opera on mac swaps ctrl and meta keys - if (useragent.isOpera && useragent.isMac) - var hashId = 0 | (e.metaKey ? 1 : 0) | (e.altKey ? 2 : 0) - | (e.shiftKey ? 4 : 0) | (e.ctrlKey ? 8 : 0); - else - var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0) - | (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0); - - var key = _self.keyNames[e.keyCode]; - - var commandName = (_self.config.reverse[hashId] || {})[(key - || String.fromCharCode(e.keyCode)).toLowerCase()]; - - var success = canon.exec(commandName, {editor: editor}); - if (success) { - return event.stopEvent(e); - } - }); -}; +} (function() { - this.keyMods = {"ctrl": 1, "alt": 2, "option" : 2, "shift": 4, "meta": 8, "command": 8}; - - this.keyNames = { - "8" : "Backspace", - "9" : "Tab", - "13" : "Enter", - "27" : "Esc", - "32" : "Space", - "33" : "PageUp", - "34" : "PageDown", - "35" : "End", - "36" : "Home", - "37" : "Left", - "38" : "Up", - "39" : "Right", - "40" : "Down", - "45" : "Insert", - "46" : "Delete", - "107": "+", - "112": "F1", - "113": "F2", - "114": "F3", - "115": "F4", - "116": "F5", - "117": "F6", - "118": "F7", - "119": "F8", - "120": "F9", - "121": "F10", - "122": "F11", - "123": "F12" - }; - function splitSafe(s, separator, limit, bLowerCase) { return (bLowerCase && s.toLowerCase() || s) .replace(/(?:^\s+|\n|\s+$)/g, "") @@ -117,8 +59,8 @@ var KeyBinding = function(element, editor, config) { l = parts.length; for (; i < l; ++i) { - if (this.keyMods[parts[i]]) - hashId = hashId | this.keyMods[parts[i]]; + if (keyUtil.KEY_MODS[parts[i]]) + hashId = hashId | keyUtil.KEY_MODS[parts[i]]; else key = parts[i] || "-"; //when empty, the splitSafe removed a '-' } @@ -145,14 +87,30 @@ var KeyBinding = function(element, editor, config) { } this.setConfig = function(config) { - this.config = config || (useragent.isMac - ? default_mac - : default_win); - if (typeof this.config.reverse == "undefined") - this.config.reverse = objectReverse.call(this, this.config, "|"); + this.$config = config; + if (typeof this.$config.reverse == "undefined") + this.$config.reverse = objectReverse.call(this, this.$config, "|"); }; -}).call(KeyBinding.prototype); + /** + * This function is called by keyBinding. + */ + this.handleKeyboard = function(data, hashId, textOrKey, keyCode) { + // Figure out if a commandKey was pressed or just some text was insert. + if (hashId != 0 || keyCode != 0) { + return { + command: (this.$config.reverse[hashId] || {})[textOrKey] + } + } else { + return { + command: "inserttext", + args: { + text: textOrKey + } + } + } + } +}).call(HashHandler.prototype) -exports.KeyBinding = KeyBinding; +exports.HashHandler = HashHandler; }); diff --git a/lib/ace/keyboard/keybinding.js b/lib/ace/keyboard/keybinding.js new file mode 100644 index 00000000..8d309126 --- /dev/null +++ b/lib/ace/keyboard/keybinding.js @@ -0,0 +1,108 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Ajax.org Code Editor (ACE). + * + * The Initial Developer of the Original Code is + * Ajax.org Services B.V. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Fabian Jakobs + * Julian Viereck + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var useragent = require("pilot/useragent"); +var keyUtil = require("pilot/keys"); +var event = require("pilot/event"); +var settings = require("pilot/settings").settings; +var HashHandler = require("ace/keyboard/hash_handler").HashHandler; +var default_mac = require("ace/conf/keybindings/default_mac").bindings; +var default_win = require("ace/conf/keybindings/default_win").bindings; +var canon = require("pilot/canon"); +require("ace/commands/default_commands"); + +var KeyBinding = function(editor, config) { + this.$editor = editor; + this.$data = { }; + this.$keyboardHandler = null; + this.$defaulKeyboardHandler = new HashHandler(config || (useragent.isMac + ? default_mac + : default_win)); +}; + +(function() { + this.setKeyboardHandler = function(keyboardHandler) { + if (this.$keyboardHandler != keyboardHandler) { + this.$data = { }; + this.$keyboardHandler = keyboardHandler; + } + }; + + this.getKeyboardHandler = function() { + return this.$keyboardHandler; + }; + + this.$callKeyboardHandler = function (e, hashId, keyOrText, keyCode) { + var toExecute; + if (this.$keyboardHandler) { + toExecute = + this.$keyboardHandler.handleKeyboard(this.$data, hashId, keyOrText, keyCode, e); + } + + // If there is nothing to execute yet, then use the default keymapping. + if (!toExecute || !toExecute.command) { + toExecute = this.$defaulKeyboardHandler. + handleKeyboard(this.$data, hashId, keyOrText, keyCode, e); + } + + if (toExecute) { + var success = canon.exec(toExecute.command, + {editor: this.$editor}, toExecute.args); + if (success) { + return event.stopEvent(e); + } + } + }; + + this.onCommandKey = function(e, hashId, keyCode) { + key = (keyUtil[keyCode] || + String.fromCharCode(keyCode)).toLowerCase(); + + this.$callKeyboardHandler(e, hashId, key, keyCode); + }; + + this.onTextInput = function(text) { + this.$callKeyboardHandler({}, 0, text, 0); + } + +}).call(KeyBinding.prototype); + +exports.KeyBinding = KeyBinding; +}); diff --git a/lib/ace/keyboard/state_handler.js b/lib/ace/keyboard/state_handler.js new file mode 100644 index 00000000..ab50bc74 --- /dev/null +++ b/lib/ace/keyboard/state_handler.js @@ -0,0 +1,239 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Julian Viereck (julian.viereck@gmail.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +// If you're developing a new keymapping and want to get an idea what's going +// on, then enable debugging. +var DEBUG = false; + +function StateHandler(keymapping) { + this.keymapping = this.$buildKeymappingRegex(keymapping); +} + +StateHandler.prototype = { + /** + * Build the RegExp from the keymapping as RegExp can't stored directly + * in the metadata JSON and as the RegExp used to match the keys/buffer + * need to be adapted. + */ + $buildKeymappingRegex: function(keymapping) { + for (state in keymapping) { + this.$buildBindingsRegex(keymapping[state]); + } + return keymapping; + }, + + $buildBindingsRegex: function(bindings) { + // Escape a given Regex string. + bindings.forEach(function(binding) { + if (binding.key) { + binding.key = new RegExp('^' + binding.key + '$'); + } else if (Array.isArray(binding.regex)) { + binding.key = new RegExp('^' + binding.regex[1] + '$'); + binding.regex = new RegExp(binding.regex.join('') + '$'); + } else if (binding.regex) { + binding.regex = new RegExp(binding.regex + '$'); + } + }); + }, + + $composeBuffer: function(data, hashId, key) { + // Initialize the data object. + if (data.state == null || data.buffer == null) { + data.state = "start"; + data.buffer = ""; + } + + var keyArray = []; + if (hashId & 1) keyArray.push("Ctrl"); + if (hashId & 8) keyArray.push("Command"); + if (hashId & 2) keyArray.push("Option"); + if (hashId & 4) keyArray.push("Shift"); + if (key) keyArray.push(key); + + var symbolicName = keyArray.join("-").toLowerCase(); + var bufferToUse = data.buffer + symbolicName; + + // Don't add the symbolic name to the key buffer if the alt_ key is + // part of the symbolic name. If it starts with alt_, this means + // that the user hit an alt keycombo and there will be a single, + // new character detected after this event, which then will be + // added to the buffer (e.g. alt_j will result in ∆). + // + // We test for 2 and not for & 2 as we only want to exclude the case where + // the option key is pressed alone. + if (hashId != 2) { + data.buffer = bufferToUse; + } + + return { + bufferToUse: bufferToUse, + symbolicName: symbolicName + }; + }, + + $find: function(data, buffer, symbolicName, hashId, key) { + // Holds the command to execute and the args if a command matched. + var result = {}; + + // Loop over all the bindings of the keymapp until a match is found. + this.keymapping[data.state].some(function(binding) { + var match; + + // Check if the key matches. + if (binding.key && !binding.key.test(symbolicName)) { + return false; + } + + // Check if the regex matches. + if (binding.regex && !(match = binding.regex.exec(buffer))) { + return false; + } + + // Check if the match function matches. + if (binding.match && !binding.match(buffer, hashId, key, symbolicName)) { + return false; + } + + // Check for disallowed matches. + if (binding.disallowMatches) { + for (var i = 0; i < binding.disallowMatches.length; i++) { + if (!!match[binding.disallowMatches[i]]) { + return false; + } + } + } + + // If there is a command to execute, then figure out the + // comand and the arguments. + if (binding.exec) { + result.command = binding.exec; + + // Bulid the arguments. + if (binding.params) { + var value; + result.args = {}; + binding.params.forEach(function(param) { + if (param.match != null && match != null) { + value = match[param.match] || param.defaultValue; + } else { + value = param.defaultValue; + } + + if (param.type === 'number') { + value = parseInt(value); + } + + result.args[param.name] = value; + }); + } + data.buffer = ""; + } + + // Handle the 'then' property. + if (binding.then) { + data.state = binding.then; + data.buffer = ""; + } + + // If no command is set, then execute the "null" fake command. + if (result.command == null) { + result.command = "null"; + } + + if (DEBUG) { + console.log("KeyboardStateMapper#find", binding); + } + return true; + }); + + if (result.command) { + return result; + } else { + data.buffer = ""; + return false; + } + }, + + /** + * This function is called by keyBinding. + */ + handleKeyboard: function(data, hashId, key) { + // Compute the current value of the keyboard input buffer. + var r = this.$composeBuffer(data, hashId, key); + var buffer = r.bufferToUse; + var symbolicName = r.symbolicName; + + r = this.$find(data, buffer, symbolicName, hashId, key); + if (DEBUG) { + console.log("KeyboardStateMapper#match", buffer, symbolicName, r); + } + + return r; + } +} + +/** + * This is a useful matching function and therefore is defined here so that + * users of KeyboardStateMapper can use it. + * + * @return boolean + * If no command key (Command|Option|Shift|Ctrl) is pressed, it + * returns true. If the only the Shift key is pressed + a character + * true is returned as well. Otherwise, false is returned. + * Summing up, the function returns true whenever the user typed + * a normal character on the keyboard and no shortcut. + */ +exports.matchCharacterOnly = function(buffer, hashId, key, symbolicName) { + // If no command keys are pressed, then catch the input. + if (hashId == 0) { + return true; + } + // If only the shift key is pressed and a character key, then + // catch that input as well. + else if ((hashId == 4) && key.length == 1) { + return true; + } + // Otherwise, we let the input got through. + else { + return false; + } +}; + +exports.StateHandler = StateHandler; +}); diff --git a/lib/ace/textinput.js b/lib/ace/keyboard/textinput.js similarity index 98% rename from lib/ace/textinput.js rename to lib/ace/keyboard/textinput.js index 5391c0e3..04188de1 100644 --- a/lib/ace/textinput.js +++ b/lib/ace/keyboard/textinput.js @@ -114,6 +114,7 @@ var TextInput = function(parentNode, host) { setTimeout(sendText, 0); }; + event.addCommandKeyListener(text, host.onCommandKey.bind(host)); event.addListener(text, "keypress", onTextInput); event.addListener(text, "textInput", onTextInput); event.addListener(text, "paste", function(e) { diff --git a/lib/ace/keyboard/vim.js b/lib/ace/keyboard/vim.js new file mode 100644 index 00000000..abe537f7 --- /dev/null +++ b/lib/ace/keyboard/vim.js @@ -0,0 +1,112 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Skywriter. + * + * The Initial Developer of the Original Code is + * Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Julian Viereck (julian.viereck@gmail.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +define(function(require, exports, module) { + +var StateHandler = require("ace/keyboard/state_handler").StateHandler; +var matchCharacterOnly = require("ace/keyboard/state_handler").matchCharacterOnly; + +var vimStates = { + start: [ + { + key: "i", + then: "insertMode" + }, + { + regex: [ "([0-9]*)", "(k|up)" ], + exec: "golineup", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "([0-9]*)", "(j|down|enter)" ], + exec: "golinedown", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "([0-9]*)", "(l|right)" ], + exec: "gotoright", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + regex: [ "([0-9]*)", "(h|left)" ], + exec: "gotoleft", + params: [ + { + name: "times", + match: 1, + type: "number", + defaultValue: 1 + } + ] + }, + { + comment: "Catch some keyboard input to stop it here", + match: matchCharacterOnly + } + ], + insertMode: [ + { + key: "esc", + then: "start" + } + ] +}; + +exports.Vim = new StateHandler(vimStates); + +}); diff --git a/lib/ace/mode/php.js b/lib/ace/mode/php.js index 12efc3d7..4795cde9 100644 --- a/lib/ace/mode/php.js +++ b/lib/ace/mode/php.js @@ -34,15 +34,15 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ - + define(function(require, exports, module) { var oop = require("pilot/oop"); -var TextMode = require("./text").Mode; -var Tokenizer = require("../tokenizer").Tokenizer; -var PhpHighlightRules = require("./php_highlight_rules").PhpHighlightRules; -var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent; -var Range = require("../range").Range; +var TextMode = require("ace/mode/text").Mode; +var Tokenizer = require("ace/tokenizer").Tokenizer; +var PhpHighlightRules = require("ace/mode/php_highlight_rules").PhpHighlightRules; +var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent; +var Range = require("ace/range").Range; var Mode = function() { this.$tokenizer = new Tokenizer(new PhpHighlightRules().getRules()); diff --git a/lib/ace/mode/python.js b/lib/ace/mode/python.js index d60c08b0..7196f01b 100644 --- a/lib/ace/mode/python.js +++ b/lib/ace/mode/python.js @@ -34,15 +34,15 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ - + define(function(require, exports, module) { var oop = require("pilot/oop"); -var TextMode = require("./text").Mode; -var Tokenizer = require("../tokenizer").Tokenizer; -var PythonHighlightRules = require("./python_highlight_rules").PythonHighlightRules; -var MatchingBraceOutdent = require("./matching_brace_outdent").MatchingBraceOutdent; -var Range = require("../range").Range; +var TextMode = require("ace/mode/text").Mode; +var Tokenizer = require("ace/tokenizer").Tokenizer; +var PythonHighlightRules = require("ace/mode/python_highlight_rules").PythonHighlightRules; +var MatchingBraceOutdent = require("ace/mode/matching_brace_outdent").MatchingBraceOutdent; +var Range = require("ace/range").Range; var Mode = function() { this.$tokenizer = new Tokenizer(new PythonHighlightRules().getRules()); diff --git a/lib/ace/mode/python_highlight_rules.js b/lib/ace/mode/python_highlight_rules.js index 5190178b..f4af5642 100644 --- a/lib/ace/mode/python_highlight_rules.js +++ b/lib/ace/mode/python_highlight_rules.js @@ -33,7 +33,7 @@ * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * - * ***** END LICENSE BLOCK ***** + * ***** END LICENSE BLOCK ***** * * TODO: python delimiters */ @@ -42,7 +42,7 @@ define(function(require, exports, module) { var oop = require("pilot/oop"); var lang = require("pilot/lang"); -var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules; +var TextHighlightRules = require("ace/mode/text_highlight_rules").TextHighlightRules; PythonHighlightRules = function() { @@ -50,12 +50,12 @@ PythonHighlightRules = function() { ("and|as|assert|break|class|continue|def|del|elif|else|except|exec|" + "finally|for|from|global|if|import|in|is|lambda|not|or|pass|print|" + "raise|return|try|while|with|yield").split("|") - ); - + ); + var builtinConstants = lang.arrayToMap( ("True|False|None|NotImplemented|Ellipsis|__debug__").split("|") ); - + var builtinFunctions = lang.arrayToMap( ("abs|divmod|input|open|staticmethod|all|enumerate|int|ord|str|any|" + "eval|isinstance|pow|sum|basestring|execfile|issubclass|print|super|" + @@ -66,19 +66,19 @@ PythonHighlightRules = function() { "__import__|complex|hash|min|set|apply|delattr|help|next|setattr|" + "buffer|dict|hex|object|slice|coerce|dir|id|oct|sorted|intern").split("|") ); - + var futureReserved = lang.arrayToMap( ("").split("|") ); var strPre = "(?:(?:[rubRUB])|(?:[ubUB][rR]))?"; - + var decimalInteger = "(?:(?:[1-9]\\d*)|(?:0))"; var octInteger = "(?:0[oO]?[0-7]+)"; var hexInteger = "(?:0[xX][\\dA-Fa-f]+)"; var binInteger = "(?:0[bB][01]+)"; var integer = "(?:" + decimalInteger + "|" + octInteger + "|" + hexInteger + "|" + binInteger + ")"; - + var exponent = "(?:[eE][+-]?\\d+)"; var fraction = "(?:\\.\\d+)"; var intPart = "(?:\\d+)"; diff --git a/static.py b/static.py new file mode 100644 index 00000000..774cbb9f --- /dev/null +++ b/static.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python2.4 +"""static - A stupidly simple WSGI way to serve static (or mixed) content. + +(See the docstrings of the various functions and classes.) + +Copyright (C) 2006-2009 Luke Arno - http://lukearno.com/ + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to: + +The Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. + +Luke Arno can be found at http://lukearno.com/ + +""" + +import mimetypes +import rfc822 +import time +import string +import sys +from os import path, stat, getcwd +from wsgiref import util +from wsgiref.headers import Headers +from wsgiref.simple_server import make_server +from optparse import OptionParser + +try: from pkg_resources import resource_filename, Requirement +except: pass + +try: import kid +except: pass + + +class MagicError(Exception): pass + + +class StatusApp: + """Used by WSGI apps to return some HTTP status.""" + + def __init__(self, status, message=None): + self.status = status + if message is None: + self.message = status + else: + self.message = message + + def __call__(self, environ, start_response, headers=[]): + if self.message: + Headers(headers).add_header('Content-type', 'text/plain') + start_response(self.status, headers) + if environ['REQUEST_METHOD'] == 'HEAD': + return [""] + else: + return [self.message] + + +class Cling(object): + """A stupidly simple way to serve static content via WSGI. + + Serve the file of the same path as PATH_INFO in self.datadir. + + Look up the Content-type in self.content_types by extension + or use 'text/plain' if the extension is not found. + + Serve up the contents of the file or delegate to self.not_found. + """ + + block_size = 16 * 4096 + index_file = 'index.html' + not_found = StatusApp('404 Not Found') + not_modified = StatusApp('304 Not Modified', "") + moved_permanently = StatusApp('301 Moved Permanently') + method_not_allowed = StatusApp('405 Method Not Allowed') + + def __init__(self, root, **kw): + """Just set the root and any other attribs passes via **kw.""" + self.root = root + for k, v in kw.iteritems(): + setattr(self, k, v) + + def __call__(self, environ, start_response): + """Respond to a request when called in the usual WSGI way.""" + if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'): + headers = [('Allow', 'GET, HEAD')] + return self.method_not_allowed(environ, start_response, headers) + path_info = environ.get('PATH_INFO', '') + full_path = self._full_path(path_info) + if not self._is_under_root(full_path): + return self.not_found(environ, start_response) + if path.isdir(full_path): + if full_path[-1] <> '/' or full_path == self.root: + location = util.request_uri(environ, include_query=False) + '/' + if environ.get('QUERY_STRING'): + location += '?' + environ.get('QUERY_STRING') + headers = [('Location', location)] + return self.moved_permanently(environ, start_response, headers) + else: + full_path = self._full_path(path_info + self.index_file) + content_type = self._guess_type(full_path) + try: + etag, last_modified = self._conditions(full_path, environ) + headers = [('Date', rfc822.formatdate(time.time())), + ('Last-Modified', last_modified), + ('ETag', etag)] + if_modified = environ.get('HTTP_IF_MODIFIED_SINCE') + if if_modified and (rfc822.parsedate(if_modified) + >= rfc822.parsedate(last_modified)): + return self.not_modified(environ, start_response, headers) + if_none = environ.get('HTTP_IF_NONE_MATCH') + if if_none and (if_none == '*' or etag in if_none): + return self.not_modified(environ, start_response, headers) + file_like = self._file_like(full_path) + headers.append(('Content-Type', content_type)) + start_response("200 OK", headers) + if environ['REQUEST_METHOD'] == 'GET': + return self._body(full_path, environ, file_like) + else: + return [''] + except (IOError, OSError), e: + print e + return self.not_found(environ, start_response) + + def _full_path(self, path_info): + """Return the full path from which to read.""" + return self.root + path_info + + def _is_under_root(self, full_path): + """Guard against arbitrary file retrieval.""" + if (path.abspath(full_path) + path.sep)\ + .startswith(path.abspath(self.root) + path.sep): + return True + else: + return False + + def _guess_type(self, full_path): + """Guess the mime type using the mimetypes module.""" + return mimetypes.guess_type(full_path)[0] or 'text/plain' + + def _conditions(self, full_path, environ): + """Return a tuple of etag, last_modified by mtime from stat.""" + mtime = stat(full_path).st_mtime + return str(mtime), rfc822.formatdate(mtime) + + def _file_like(self, full_path): + """Return the appropriate file object.""" + return open(full_path, 'rb') + + def _body(self, full_path, environ, file_like): + """Return an iterator over the body of the response.""" + way_to_send = environ.get('wsgi.file_wrapper', iter_and_close) + return way_to_send(file_like, self.block_size) + + +def iter_and_close(file_like, block_size): + """Yield file contents by block then close the file.""" + while 1: + try: + block = file_like.read(block_size) + if block: yield block + else: raise StopIteration + except StopIteration, si: + file_like.close() + return + + +def cling_wrap(package_name, dir_name, **kw): + """Return a Cling that serves from the given package and dir_name. + + This uses pkg_resources.resource_filename which is not the + recommended way, since it extracts the files. + + I think this works fine unless you have some _very_ serious + requirements for static content, in which case you probably + shouldn't be serving it through a WSGI app, IMHO. YMMV. + """ + resource = Requirement.parse(package_name) + return Cling(resource_filename(resource, dir_name), **kw) + + +def command(): + parser = OptionParser(usage="%prog DIR [HOST][:][PORT]", + version="static 0.3.6") + options, args = parser.parse_args() + if len(args) in (1, 2): + if len(args) == 2: + parts = args[1].split(":") + if len(parts) == 1: + host = parts[0] + port = None + elif len(parts) == 2: + host, port = parts + else: + sys.exit("Invalid host:port specification.") + elif len(args) == 1: + host, port = None, None + if not host: + host = '0.0.0.0' + if not port: + port = 9999 + try: + port = int(port) + except: + sys.exit("Invalid host:port specification.") + app = Cling(args[0]) + try: + make_server(host, port, app).serve_forever() + except KeyboardInterrupt, ki: + print "Cio, baby!" + except: + sys.exit("Problem initializing server.") + else: + parser.print_help(sys.stderr) + sys.exit(1) + + +def test(): + from wsgiref.validate import validator + app = Cling(getcwd()) + try: + print "Serving " + getcwd() + " to http://localhost:9999" + make_server('localhost', 9999, validator(app)).serve_forever() + except KeyboardInterrupt, ki: + print "" + print "Ciao, baby!" + + +if __name__ == '__main__': + test() + diff --git a/support/cockpit b/support/cockpit index 1b727ac7..36c55c39 160000 --- a/support/cockpit +++ b/support/cockpit @@ -1 +1 @@ -Subproject commit 1b727ac77facdae8c0ff95cffd5dc8be1df30e94 +Subproject commit 36c55c39e8ede94e964364e01ab5308f3f51c2eb diff --git a/support/dryice b/support/dryice new file mode 160000 index 00000000..89f99bb6 --- /dev/null +++ b/support/dryice @@ -0,0 +1 @@ +Subproject commit 89f99bb65c66d0c25d461ab4af5743ef1676a04d