diff --git a/.eslintrc b/.eslintrc index 5d7667557f..92fad475c3 100644 --- a/.eslintrc +++ b/.eslintrc @@ -224,8 +224,8 @@ "no-plusplus": 0, "react/display-name": 1, - "react/jsx-boolean-value": 1, - "react/jsx-quotes": [1, "single", "avoid-escape"], + "react/jsx-boolean-value": [1, "always"], + "jsx-quotes": [1, "prefer-single"], "react/jsx-no-undef": 1, "react/jsx-sort-props": 1, "react/jsx-uses-react": 1, diff --git a/.gitignore b/.gitignore index 0f96fb3ee1..8b70d1c075 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ public/js/commonFramework* public/js/sandbox* public/js/iFrameScripts* public/js/plugin* +public/js/vendor* +public/js/faux* public/css/main* server/rev-manifest.json diff --git a/.travis.yml b/.travis.yml index b6bd11e2c2..22cfb84161 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - 'node' - - '1.6.4' + - '4.2.1' sudo: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6830f00dc5..107edda489 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,12 +1,28 @@ -We're getting a lot of duplicate issues and bug reports that just aren't reporting actual bugs. -So, before you submit your issue, please read the [Help I've Found a Bug](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-I've-Found-a-Bug) wiki page. +## Code Not Working? +Do not file an issue until you have: +1. Read [Help I've Found a Bug](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-I've-Found-a-Bug) wiki page and followed the instructions there. +2. Asked for confirmation in the appropriate [Help Room](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Help-Rooms) +3. Please *do not* open an issue without a 3rd party confirmation of your problem. + +## I want to help! We welcome pull requests from Free Code Camp campers (our students) and seasoned JavaScript developers alike! Follow these steps to contribute: 1. Check our [public Waffle Board](https://waffle.io/freecodecamp/freecodecamp). -2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp). -3. Fork the project ([Need help with forking a project?](https://help.github.com/articles/fork-a-repo/)). You'll do all of your work on your forked copy. -4. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)) -5. Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example `fix/email-login` would be a branch where I fix something specific to email login. -6. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [Free Code Camp's JavaScript Style Guide](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Free-Code-Camp-JavaScript-Style-Guide) (you can find a summary of those rules [here](https://github.com/FreeCodeCamp/FreeCodeCamp/blob/staging/.eslintrc). Please do not ignore any linting errors, as they are meant to **help** you and to ensure a clean and simple code base. Make sure none of your JavaScript is longer than 80 characters per line. -7. Once your code is ready, submit a [pull request](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Pull-Request-Contribute) from your branch to Free Code Camp's `staging` branch. We'll do a quick code review and give you feedback, then iterate from there. It may also be helpful to read about git [rebasing](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/git-rebase). +2. Pick an issue that nobody has claimed and start working on it. If your issue isn't on the board, open an issue. If you think you can fix it yourself, start working on it. Feel free to ask for help in our [General Gitter Chat Room](https://gitter.im/FreeCodeCamp/FreeCodeCamp), or in our [Help Contributors Chat Room](https://gitter.im/FreeCodeCamp/HelpContributors) +3. You can find issues we are seeking assistance on by searching for the [Help Wanted](https://github.com/FreeCodeCamp/FreeCodeCamp/labels/help%20wanted) tag. + +## Contribution Guidelines + +1. Fork the project: [How To Fork And Maintain a Local Instance of Free Code Camp](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/How-To-Fork-And-Maintain-a-Local-Instance-of-Free-Code-Camp) +2. Create a branch specific to the issue or feature you are working on. Push your work to that branch. ([Need help with branching?](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)) + 3. Name the branch something like `fix/xxx` or `feature/xxx` where `xxx` is a short description of the changes or feature you are attempting to add. For example `fix/email-login` would be a branch where I fix something specific to email login. +4. You should have [ESLint running in your editor](http://eslint.org/docs/user-guide/integrations.html), and it will highlight anything doesn't conform to [Free Code Camp's JavaScript Style Guide](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Free-Code-Camp-JavaScript-Style-Guide) (you can find a summary of those rules [here](https://github.com/FreeCodeCamp/FreeCodeCamp/blob/staging/.eslintrc). Please do not ignore any linting errors, as they are meant to **help** you and to ensure a clean and simple code base. Make sure none of your JavaScript is longer than 80 characters per line. +5. Squash your Commits. Ref: [rebasing](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/git-rebase) + +## Create a Pull Request +1. Read our [How to Create a Pull Request for Free Code Camp](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/How-To-Create-A-Pull-Request-for-Free-Code-Camp) +2. Submit a [pull request](https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/Pull-Request-Contribute) from your branch to Free Code Camp's `staging` branch. + - The title (also called the subject) of your PR should be descriptive of your changes. i.e. `fix typo in basic-javascript challenge` + - If the PR is meant to fix a specific issue, append to the end of your PR's commit message `closes #1337`. This tells GitHub to close that issue if the PR is merged. + - Do NOT add issue numbers to the PR's title. i.e. `minor improvements in basic-javascript challenge` diff --git a/Procfile b/Procfile deleted file mode 100644 index 7769836542..0000000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: ./node_modules/.bin/forever -m 5 server.js \ No newline at end of file diff --git a/README.md b/README.md index 9b0c87d631..c81c0bb1e3 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,25 @@ [![Join the chat at https://gitter.im/freecodecamp/freecodecamp](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/freecodecamp/freecodecamp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -Welcome to Free Code Camp's open source codebase! +Welcome to Free Code Camp's open source codebase and curriculum! ======================= -Free Code Camp is an open-source community of busy people who learn to code and build projects for nonprofits. +Free Code Camp is an open-source community where you learn to code and help nonprofits. -Our campers (students) start by working through our free, self-paced, browser-based curriculum. Next, they build several practice projects. Finally, we pair two campers together with a stakeholder from a nonprofit organization, and help them build the solution the nonprofit has requested. +You start by working through our self-paced, browser-based full stack JavaScript curriculum. + +After you complete the first 400 hours worth of challenges (which involves building 10 single-page apps), you'll earn your Front End Development Certification. + +After you complete the second 400 hours worth of challenges (which involves building and deploying 5 full stack apps), you'll earn your Full Stack Development Certification. + +Then we'll pair you with another camper, an agile project manager, and a stakeholder from a nonprofit organization. Together, you'll plan and build an app that helps that nonprofit carry out its mission more effectively. **We help our campers build job-worthy portfolios of real apps used by real people, while helping nonprofits.** -80% of our campers are over 25, and nearly a fifth of our campers are women. - -This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [blog](http://blog.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). +This code is running live at [FreeCodeCamp.com](http://www.FreeCodeCamp.com). We also have [Gitter](https://gitter.im/FreeCodeCamp/FreeCodeCamp), a [Medium publication](http://medium.freecodecamp.com), and even a [Twitch.tv channel](http://twitch.tv/freecodecamp). [Join our community here](http://www.freecodecamp.com/signin). -*Note: We're currently very close to moving from Express to Loopback. As such, please keep in mind that the instructions here for setting up and running the project do not directly translate to the staging branch. Additionally, the file structure is quite a bit different. As always, the staging branch is the appropriate place to branch off of to fix/add something.* - Wiki ------------ @@ -54,22 +56,26 @@ The easiest way to get started is to clone the repository: # Get the latest snapshot git clone --depth=1 https://github.com/freecodecamp/freecodecamp.git freecodecamp +# Change directory cd freecodecamp # Install NPM dependencies npm install +# Install Gulp globally +npm install -g gulp + +# Install Bower globally +npm install -g bower + # Install Bower dependencies bower install # Create a .env file and populate it with the necessary API keys and secrets: touch .env - -# Install Gulp globally -npm install -g gulp ``` -Edit your `.env` file with the following API keys accordingly (if you only use email login, only the `MONGOHQ_URL`, `SESSION_SECRET`, `MANDRILL_USER` and `MANDRILL_PASSWORD` fields are necessary. Keep in mind if you want to use more services you'll have to get your own API keys for those services. +Edit your `.env` file with the following API keys accordingly. If you only use email login, only the `MONGOHQ_URL`, `SESSION_SECRET`, `MANDRILL_USER` and `MANDRILL_PASSWORD` fields are necessary. Keep in mind if you want to use more services you'll have to get your own API keys for those services. If you only use a subset or no OAuth2 authentication methods, you may want to remove them from ```server/passport-providers.js``` - otherwise the server will complain about missing clientIDs at launch. ``` MONGOHQ_URL='mongodb://localhost:27017/freecodecamp' @@ -107,20 +113,20 @@ DEBUG=true ``` ```bash -# Start the mongo server +# Start the mongo server in a seperate terminal mongod -# Create your mongo database. -# Type "mongo" in your terminal to access the mongo shell -use freecodecamp -# Exit the mongo shell with control + d - -# Seed your database with the challenges -node seed/ +# Initialize Free Code Camp +# This will seed the database for the first time. +# This command should only be run once. +npm run first-time # start the application gulp ``` +Now navigate to your browser and open http://localhost:3001 +If the app loads, congratulations - you're all set. Otherwise, let us know by opening a GitHub issue and with your error. + License ------- diff --git a/bower.json b/bower.json index bfa57a3e47..b43360485a 100644 --- a/bower.json +++ b/bower.json @@ -2,10 +2,6 @@ "name": "freecodecamp", "version": "0.0.0", "homepage": "http://freecodecamp.com", - "authors": [ - "Quincy Larson " - ], - "license": "MIT", "private": true, "ignore": [ "**/.*", @@ -22,8 +18,10 @@ "bootstrap": "~3.3.4", "font-awesome": "~4.3.0", "moment": "~2.10.2", - "ramda": "~0.13.0", - "jshint": "~2.7.0", - "lightbox2": "~2.8.1" + "jshint": "~2.9.0", + "lightbox2": "~2.8.1", + "rxjs": "~4.0.6", + "CodeMirror": "~5.8.0", + "chai": "~3.4.1" } } diff --git a/client/commonFramework.js b/client/commonFramework.js deleted file mode 100644 index fcb1ccc58d..0000000000 --- a/client/commonFramework.js +++ /dev/null @@ -1,1106 +0,0 @@ -// common namespace -// all classes should be stored here -var common = common || { - // init is an array of functions that are - // called at the beginning of dom ready - init: [] -}; - -common.challengeName = common.challengeName || window.challenge_Name ? - window.challenge_Name : - ''; - -common.challengeType = common.challengeType || window.challengeType ? - window.challengeType : - 0; - -common.challengeId = common.challengeId || window.challenge_Id; - -common.challengeSeed = common.challengeSeed || window.challengeSeed ? - window.challengeSeed : - []; - -common.seed = common.challengeSeed.reduce(function(seed, line) { - return seed + line + '\n'; -}, ''); - -// store code in the URL -common.codeUri = (function(common, encode, decode, location, history) { - var codeUri = { - encode: function(code) { - return encode(code); - }, - decode: function(code) { - try { - return decode(code); - } catch (ignore) { - return null; - } - }, - isInQuery: function(query) { - var decoded = codeUri.decode(query); - if (!decoded || typeof decoded.split !== 'function') { - return false; - } - return decoded - .split('?') - .splice(1) - .reduce(function(found, param) { - var key = param.split('=')[0]; - if (key === 'solution') { - return true; - } - return found; - }, false); - }, - isAlive: function() { - return codeUri.isInQuery(location.search) || - codeUri.isInQuery(location.hash); - }, - parse: function() { - var query; - if (location.search && codeUri.isInQuery(location.search)) { - query = location.search.replace(/^\?/, ''); - if (history && typeof history.replaceState === 'function') { - history.replaceState( - history.state, - null, - location.href.split('?')[0] - ); - location.hash = '#?' + query; - } - } else { - query = location.hash.replace(/^\#\?/, ''); - } - if (!query) { - return null; - } - - return query - .split('&') - .reduce(function(solution, param) { - var key = param.split('=')[0]; - var value = param.split('=')[1]; - if (key === 'solution') { - return codeUri.decode(value); - } - return solution; - }, null); - }, - querify: function(solution) { - location.hash = '?solution=' + codeUri.encode(solution); - return solution; - } - }; - - common.init.push(function() { - codeUri.parse(); - }); - - return codeUri; -}(common, encodeURIComponent, decodeURIComponent, location, history)); - -// codeStorage -common.codeStorageFactory = (function($, localStorage, codeUri) { - - var CodeStorageProps = { - version: 0.01, - keyVersion: 'saveVersion', - keyValue: null, - updateWait: 2000, - updateTimeoutId: null - }; - - var CodeStorage = { - hasSaved: function() { - return this.updateTimeoutId === null; - }, - - onSave: function(func) { - this.eventArray.push(func); - }, - - setSaveKey: function(key) { - this.keyValue = key + 'Val'; - }, - - getStoredValue: function() { - return '' + localStorage.getItem(this.keyValue); - }, - - setEditor: function(editor) { - this.editor = editor; - }, - - isAlive: function() { - var val = this.getStoredValue(); - return val !== 'null' && - val !== 'undefined' && - (val && val.length > 0); - }, - - updateStorage: function() { - if (typeof localStorage !== 'undefined') { - var value = this.editor.getValue(); - // store in localStorage - localStorage.setItem(this.keyValue, value); - // also store code in URL - codeUri.querify(value); - } else { - console.log('no web storage'); - } - this.updateTimeoutId = null; - } - }; - - function codeStorageFactory(editor, challengeName) { - var codeStorage = Object.create(CodeStorage); - $.extend(codeStorage, CodeStorageProps); - codeStorage.setEditor(editor); - codeStorage.setSaveKey(challengeName); - return codeStorage; - } - - var savedVersion = localStorage.getItem(CodeStorageProps.keyVersion); - if (savedVersion === null) { - localStorage.setItem( - CodeStorageProps.keyVersion, - CodeStorageProps.version - ); - } - - return codeStorageFactory; -}($, localStorage, common.codeUri)); - -common.codeOutput = (function(CodeMirror, document, challengeType) { - if (!CodeMirror) { - return {}; - } - if ( - challengeType === '0' || - challengeType === '7' - ) { - return {}; - } - var codeOutput = CodeMirror.fromTextArea( - document.getElementById('codeOutput'), - { - lineNumbers: false, - mode: 'text', - theme: 'monokai', - readOnly: 'nocursor', - lineWrapping: true - } - ); - - codeOutput.setValue( - '/**\n' + - ' * Your output will go here.\n' + - ' * Console.log() -type statements\n' + - ' * will appear in your browser\'s\n' + - ' * DevTools JavaScript console.\n' + - ' */' - ); - - codeOutput.setSize('100%', '100%'); - - return codeOutput; -}(window.CodeMirror, window.document, common.challengeType || 0)); - -var sandBox = (function(jailed, codeOutput) { - if (!jailed) { - return {}; - } - var plugin = null; - - var sandBox = { - }; - - var printCallback; - - // sends the input to the plugin for evaluation - function submit(code, callback) { - printCallback = callback; - - // postpone the evaluation until the plugin is initialized - plugin.whenConnected(function() { - if (requests === 0) { - startLoading(); - } - - requests++; - plugin.remote.run(code); - }); - } - - // puts the message on the terminal - var print = function(cls, msg) { - printCallback(cls, msg); - }; - - - // will restart the plugin if it does not respond - var disconnectTimeout = null; - var startLoading = function() { - disconnectTimeout = setTimeout(disconnect, 3000); - }; - - var endLoading = function() { - clearTimeout(disconnectTimeout); - }; - - var disconnect = function() { - plugin.disconnect(); - }; - - - // interface provided to the plugin - var api = { - output: function(data) { - endLoading(); - // print('input', data.input); - - if (data.error) { - print('Error', data); - reset(); - } else { - print(null, data); - reset(); - } - } - }; - - - // obtaining absolute path of this script - var scripts = document.getElementsByTagName('script'); - var path = scripts[scripts.length - 1].src - .split('?')[0] - .split('/') - .slice(0, -1) - .join('/') + '/'; - - var requests; - - // (re)initializes the plugin - var reset = function() { - requests = 0; - plugin = new jailed.Plugin(path + 'plugin.js', api); - plugin.whenDisconnected( function() { - // give some time to handle the last responce - setTimeout( function() { - endLoading(); - console.log('resetting on fatal plugin error'); - - if (common.challengeType === 0) { - codeOutput.setValue( - 'Sorry, your code is either too slow, has a fatal error, ' + - 'or contains an infinite loop.' - ); - } - reset(); - }, 10); - }); - }; - reset(); - sandBox.submit = submit; - return sandBox; -}(window.jailed, common.codeOutput)); - -function replaceSafeTags(value) { - return value - .replace(/fccss/gi, ''); -} - -var BDDregex = new RegExp( - '(expect(\\s+)?\\(.*\\;)|' + - '(assert(\\s+)?\\(.*\\;)|' + - '(assert\\.\\w.*\\;)|' + - '(.*\\.should\\..*\\;)/' -); - -var isInitRun = false; -var initPreview = true; - -var editor = (function(CodeMirror, emmetCodeMirror, common) { - var codeStorageFactory = common.codeStorageFactory; - if (!CodeMirror) { - return {}; - } - - var editor = CodeMirror.fromTextArea(document.getElementById('codeEditor'), { - lineNumbers: true, - mode: 'text', - theme: 'monokai', - runnable: true, - matchBrackets: true, - autoCloseBrackets: true, - scrollbarStyle: 'null', - lineWrapping: true, - gutters: ['CodeMirror-lint-markers'] - }); - - editor.setSize('100%', 'auto'); - - var codeStorage = common.codeStorage = - codeStorageFactory(editor, common.challengeName); - - editor.on('keyup', function() { - clearTimeout(codeStorage.updateTimeoutId); - codeStorage.updateTimeoutId = setTimeout( - codeStorage.updateStorage.bind(codeStorage), - codeStorage.updateWait - ); - }); - - // Initialize CodeMirror editor with a nice html5 canvas demo. - editor.on('keyup', function() { - clearTimeout(delay); - delay = setTimeout(updatePreview, 300); - }); - - editor.setOption('extraKeys', { - Tab: function(cm) { - if (cm.somethingSelected()) { - cm.indentSelection('add'); - } else { - var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Shift-Tab': function(cm) { - if (cm.somethingSelected()) { - cm.indentSelection('subtract'); - } else { - var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Ctrl-Enter': function() { - isInitRun = false; - bonfireExecute(true); - return false; - } - }); - - - var info = editor.getScrollInfo(); - - var after = editor.charCoords({ - line: editor.getCursor().line + 1, - ch: 0 - }, 'local').top; - - if (info.top + info.clientHeight < after) { - editor.scrollTo(null, after - info.clientHeight + 3); - } - - if (emmetCodeMirror) { - emmetCodeMirror( - editor, - { - 'Cmd-E': 'emmet.expand_abbreviation', - Tab: 'emmet.expand_abbreviation_with_tab', - Enter: 'emmet.insert_formatted_line_break_only' - } - ); - } - common.init.push(function() { - var editorValue; - if (common.codeUri.isAlive()) { - console.log('in query'); - editorValue = common.codeUri.parse(); - } else { - editorValue = codeStorage.isAlive() ? - codeStorage.getStoredValue() : - common.seed; - } - - editor.setValue(replaceSafeTags(editorValue)); - editor.refresh(); - }); - - return editor; -}(window.CodeMirror, window.emmetCodeMirror, common)); - - -var tests = tests || []; - -var libraryIncludes = "" + - "" + - "" + - "" + - "" + - "" + - "" + - '' + - ''; - -var editorValueForIFrame; -var iFrameScript = ""; -var delay; - -function workerError(error) { - var display = $('.runTimeError'); - var housing = $('#testSuite'); - if (display.html() !== error) { - display.remove(); - housing.prepend( - '
' + - error.replace(/j\$/gi, '$').replace(/jdocument/gi, 'document').replace(/jjQuery/gi, 'jQuery') + - '
' - ); - display.hide().fadeIn(function() { - setTimeout(function() { - display.fadeOut(function() { - display.remove(); - }); - }, 1000); - }); - } -} - -function scopejQuery(str) { - return str - .replace(/\$/gi, 'j$') - .replace(/document/gi, 'jdocument') - .replace(/jQuery/gi, 'jjQuery'); -} - -function safeHTMLRun(test) { - var codeStorage = common.codeStorage; - if (common.challengeType === '0') { - var previewFrame = document.getElementById('preview'); - var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; - if (editor.getValue().match(/\/gi) !== null) { - var s = editor - .getValue() - .split(/\<\s?script\s?\>/gi)[1] - .split(/\<\s?\/\s?script\s?\>/gi)[0]; - - // add feuxQuery - s = 'var document = \"\"; var $ = function() {return(new function() {this.add=function() {return(this);};this.addBack=function() {return(this);};this.addClass=function() {return(this);};this.after=function() {return(this);};this.ajaxComplete=function() {return(this);};this.ajaxError=function() {return(this);};this.ajaxSend=function() {return(this);};this.ajaxStart=function() {return(this);};this.ajaxStop=function() {return(this);};this.ajaxSuccess=function() {return(this);};this.andSelf=function() {return(this);};this.animate=function() {return(this);};this.append=function() {return(this);};this.appendTo=function() {return(this);};this.attr=function() {return(this);};this.before=function() {return(this);};this.bind=function() {return(this);};this.blur=function() {return(this);};this.callbacksadd=function() {return(this);};this.callbacksdisable=function() {return(this);};this.callbacksdisabled=function() {return(this);};this.callbacksempty=function() {return(this);};this.callbacksfire=function() {return(this);};this.callbacksfired=function() {return(this);};this.callbacksfireWith=function() {return(this);};this.callbackshas=function() {return(this);};this.callbackslock=function() {return(this);};this.callbackslocked=function() {return(this);};this.callbacksremove=function() {return(this);};this.change=function() {return(this);};this.children=function() {return(this);};this.clearQueue=function() {return(this);};this.click=function() {return(this);};this.clone=function() {return(this);};this.closest=function() {return(this);};this.contents=function() {return(this);};this.context=function() {return(this);};this.css=function() {return(this);};this.data=function() {return(this);};this.dblclick=function() {return(this);};this.delay=function() {return(this);};this.delegate=function() {return(this);};this.dequeue=function() {return(this);};this.detach=function() {return(this);};this.die=function() {return(this);};this.each=function() {return(this);};this.empty=function() {return(this);};this.end=function() {return(this);};this.eq=function() {return(this);};this.error=function() {return(this);};this.fadeIn=function() {return(this);};this.fadeOut=function() {return(this);};this.fadeTo=function() {return(this);};this.fadeToggle=function() {return(this);};this.filter=function() {return(this);};this.find=function() {return(this);};this.finish=function() {return(this);};this.first=function() {return(this);};this.focus=function() {return(this);};this.focusin=function() {return(this);};this.focusout=function() {return(this);};this.get=function() {return(this);};this.has=function() {return(this);};this.hasClass=function() {return(this);};this.height=function() {return(this);};this.hide=function() {return(this);};this.hover=function() {return(this);};this.html=function() {return(this);};this.index=function() {return(this);};this.innerHeight=function() {return(this);};this.innerWidth=function() {return(this);};this.insertAfter=function() {return(this);};this.insertBefore=function() {return(this);};this.is=function() {return(this);};this.jQuery=function() {return(this);};this.jquery=function() {return(this);};this.keydown=function() {return(this);};this.keypress=function() {return(this);};this.keyup=function() {return(this);};this.last=function() {return(this);};this.length=function() {return(this);};this.live=function() {return(this);};this.load=function() {return(this);};this.load=function() {return(this);};this.map=function() {return(this);};this.mousedown=function() {return(this);};this.mouseenter=function() {return(this);};this.mouseleave=function() {return(this);};this.mousemove=function() {return(this);};this.mouseout=function() {return(this);};this.mouseover=function() {return(this);};this.mouseup=function() {return(this);};this.next=function() {return(this);};this.nextAll=function() {return(this);};this.nextUntil=function() {return(this);};this.not=function() {return(this);};this.off=function() {return(this);};this.offset=function() {return(this);};this.offsetParent=function() {return(this);};this.on=function() {return(this);};this.one=function() {return(this);};this.outerHeight=function() {return(this);};this.outerWidth=function() {return(this);};this.parent=function() {return(this);};this.parents=function() {return(this);};this.parentsUntil=function() {return(this);};this.position=function() {return(this);};this.prepend=function() {return(this);};this.prependTo=function() {return(this);};this.prev=function() {return(this);};this.prevAll=function() {return(this);};this.prevUntil=function() {return(this);};this.promise=function() {return(this);};this.prop=function() {return(this);};this.pushStack=function() {return(this);};this.queue=function() {return(this);};this.ready=function() {return(this);};this.remove=function() {return(this);};this.removeAttr=function() {return(this);};this.removeClass=function() {return(this);};this.removeData=function() {return(this);};this.removeProp=function() {return(this);};this.replaceAll=function() {return(this);};this.replaceWith=function() {return(this);};this.resize=function() {return(this);};this.scroll=function() {return(this);};this.scrollLeft=function() {return(this);};this.scrollTop=function() {return(this);};this.select=function() {return(this);};this.selector=function() {return(this);};this.serialize=function() {return(this);};this.serializeArray=function() {return(this);};this.show=function() {return(this);};this.siblings=function() {return(this);};this.size=function() {return(this);};this.slice=function() {return(this);};this.slideDown=function() {return(this);};this.slideToggle=function() {return(this);};this.slideUp=function() {return(this);};this.stop=function() {return(this);};this.submit=function() {return(this);};this.text=function() {return(this);};this.toArray=function() {return(this);};this.toggle=function() {return(this);};this.toggle=function() {return(this);};this.toggleClass=function() {return(this);};this.trigger=function() {return(this);};this.triggerHandler=function() {return(this);};this.unbind=function() {return(this);};this.undelegate=function() {return(this);};this.unload=function() {return(this);};this.unwrap=function() {return(this);};this.val=function() {return(this);};this.width=function() {return(this);};this.wrap=function() {return(this);};this.wrapAll=function() {return(this);};this.wrapInner=function() {return(this);}});};$.ajax=function() {return($);};$.ajaxPrefilter=function() {return($);};$.ajaxSetup=function() {return($);};$.ajaxTransport=function() {return($);};$.boxModel=function() {return($);};$.browser=function() {return($);};$.Callbacks=function() {return($);};$.contains=function() {return($);};$.cssHooks=function() {return($);};$.cssNumber=function() {return($);};$.data=function() {return($);};$.Deferred=function() {return($);};$.dequeue=function() {return($);};$.each=function() {return($);};$.error=function() {return($);};$.extend=function() {return($);};$.fnextend=function() {return($);};$.fxinterval=function() {return($);};$.fxoff=function() {return($);};$.get=function() {return($);};$.getJSON=function() {return($);};$.getScript=function() {return($);};$.globalEval=function() {return($);};$.grep=function() {return($);};$.hasData=function() {return($);};$.holdReady=function() {return($);};$.inArray=function() {return($);};$.isArray=function() {return($);};$.isEmptyObject=function() {return($);};$.isFunction=function() {return($);};$.isNumeric=function() {return($);};$.isPlainObject=function() {return($);};$.isWindow=function() {return($);};$.isXMLDoc=function() {return($);};$.makeArray=function() {return($);};$.map=function() {return($);};$.merge=function() {return($);};$.noConflict=function() {return($);};$.noop=function() {return($);};$.now=function() {return($);};$.param=function() {return($);};$.parseHTML=function() {return($);};$.parseJSON=function() {return($);};$.parseXML=function() {return($);};$.post=function() {return($);};$.proxy=function() {return($);};$.queue=function() {return($);};$.removeData=function() {return($);};$.sub=function() {return($);};$.support=function() {return($);};$.trim=function() {return($);};$.type=function() {return($);};$.unique=function() {return($);};$.when=function() {return($);};$.always=function() {return($);};$.done=function() {return($);};$.fail=function() {return($);};$.isRejected=function() {return($);};$.isResolved=function() {return($);};$.notify=function() {return($);};$.notifyWith=function() {return($);};$.pipe=function() {return($);};$.progress=function() {return($);};$.promise=function() {return($);};$.reject=function() {return($);};$.rejectWith=function() {return($);};$.resolve=function() {return($);};$.resolveWith=function() {return($);};$.state=function() {return($);};$.then=function() {return($);};$.currentTarget=function() {return($);};$.data=function() {return($);};$.delegateTarget=function() {return($);};$.isDefaultPrevented=function() {return($);};$.isImmediatePropagationStopped=function() {return($);};$.isPropagationStopped=function() {return($);};$.metaKey=function() {return($);};$.namespace=function() {return($);};$.pageX=function() {return($);};$.pageY=function() {return($);};$.preventDefault=function() {return($);};$.relatedTarget=function() {return($);};$.result=function() {return($);};$.stopImmediatePropagation=function() {return($);};$.stopPropagation=function() {return($);};$.target=function() {return($);};$.timeStamp=function() {return($);};$.type=function() {return($);};$.which=function() {return($);};' + s; - - sandBox.submit(scopejQuery(s), function(cls, message) { - if (cls) { - console.log(message.error); - workerError(message.error); - } else if (test) { - preview.open(); - preview.write(libraryIncludes + editor.getValue() + iFrameScript); - codeStorage.updateStorage(); - preview.close(); - } else { - preview.open(); - preview.write(libraryIncludes + editor.getValue()); - codeStorage.updateStorage(); - preview.close(); - } - }); - } else if (test) { - preview.open(); - preview.write(libraryIncludes + editor.getValue() + iFrameScript); - codeStorage.updateStorage(); - preview.close(); - } else { - preview.open(); - preview.write(libraryIncludes + editor.getValue()); - codeStorage.updateStorage(); - preview.close(); - } - } -} - -function updatePreview() { - editorValueForIFrame = editor.getValue(); - var failedCommentTest = false; - if ( - editorValueForIFrame.match(/\<\!\-\-/gi) && - editorValueForIFrame.match(/\-\-\>/gi) == null - ) { - failedCommentTest = true; - } else if ( - editorValueForIFrame.match(/\<\!\-\-/gi) && - editorValueForIFrame.match(/\<\!\-\-/gi).length > editorValueForIFrame.match(/\-\-\>/gi).length - ) { - failedCommentTest = true; - } - - if (failedCommentTest) { - editor.setValue(editor.getValue() + '-->'); - editorValueForIFrame = editorValueForIFrame + '-->'; - } - - if (!editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi)) { - safeHTMLRun(false); - } else { - workerError('Unsafe $($)'); - } -} - -if (typeof prodOrDev !== 'undefined') { - - var nodeEnv = prodOrDev === 'production' ? - 'http://www.freecodecamp.com' : - 'http://localhost:3001'; - - if (common.challengeType === '0') { - setTimeout(updatePreview, 300); - } -} - -/** - * "post" methods - */ - -var testResults = []; -var postSuccess = function(data) { - var testDoc = document.createElement('div'); - $(testDoc).html( - "
" + - JSON.parse(data) + - '
' - ); - - $('#testSuite').append(testDoc); - testSuccess(); -}; - -var postError = function(data) { - var testDoc = document.createElement('div'); - - $(testDoc).html( - "
" + - JSON.parse(data) + - '
' - ); - - $('#testSuite').append(testDoc); -}; - -var goodTests = 0; -var testSuccess = function() { - goodTests++; - // test successful run show completion - if (goodTests === tests.length) { - return showCompletion(); - } -}; - -function ctrlEnterClickHandler(e) { - // ctrl + enter - if (e.ctrlKey && e.keyCode === 13) { - $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); - $('#submit-challenge').click(); - } -} - -function showCompletion() { - if (isInitRun) { - isInitRun = false; - return; - } - var time = Math.floor(Date.now()) - started; - ga( - 'send', - 'event', - 'Challenge', - 'solved', - common.challengeName + ', Time: ' + time + ', Attempts: ' + attempts - ); - var bonfireSolution = editor.getValue(); - var didCompleteWith = $('#completed-with').val() || null; - - $('#complete-courseware-dialog').modal('show'); - $('#complete-courseware-dialog .modal-header').click(); - - $('#submit-challenge').click(function(e) { - e.preventDefault(); - - $('#submit-challenge') - .attr('disabled', 'true') - .removeClass('btn-primary') - .addClass('btn-warning disabled'); - - var $checkmarkContainer = $('#checkmark-container'); - $checkmarkContainer.css({ height: $checkmarkContainer.innerHeight() }); - - $('#challenge-checkmark') - .addClass('zoomOutUp') - // .removeClass('zoomInDown') - .delay(1000) - .queue(function(next) { - $(this).replaceWith( - '
' + - 'submitting...
' - ); - next(); - }); - - $.post( - '/completed-bonfire/', { - challengeInfo: { - challengeId: common.challengeId, - challengeName: common.challengeName, - completedWith: didCompleteWith, - challengeType: common.challengeType, - solution: bonfireSolution - } - }, - function(res) { - if (res) { - window.location = - '/challenges/next-challenge?id=' + common.challengeId; - } - } - ); - }); -} - -var resetEditor = function resetEditor() { - editor.setValue(replaceSafeTags(common.seed)); - $('#testSuite').empty(); - bonfireExecute(true); - common.codeStorage.updateStorage(); -}; - -var attempts = 0; -if (attempts) { - attempts = 0; -} - - -var userTests; -var testSalt = Math.random(); - -var scrapeTests = function(userJavaScript) { - - // insert tests from mongo - for (var i = 0; i < tests.length; i++) { - userJavaScript += '\n' + tests[i]; - } - - var counter = 0; - var match = BDDregex.exec(userJavaScript); - - while (match) { - var replacement = '//' + counter + testSalt; - userJavaScript = userJavaScript.substring(0, match.index) + - replacement + - userJavaScript.substring(match.index + match[0].length); - - if (!userTests) { - userTests = []; - } - - userTests.push({ - 'text': match[0], - 'line': counter, - 'err': null - }); - - counter++; - match = BDDregex.exec(userJavaScript); - } - - return userJavaScript; -}; - -function removeComments(userJavaScript) { - var regex = new RegExp(/(\/\*[^(\*\/)]*\*\/)|\/\/[^\n]*/g); - return userJavaScript.replace(regex, ''); -} - -function removeLogs(userJavaScript) { - return userJavaScript.replace(/(console\.[\w]+\s*\(.*\;)/g, ''); -} - -var pushed = false; -var createTestDisplay = function() { - if (pushed) { - userTests.pop(); - } - for (var i = 0; i < userTests.length; i++) { - var didTestPass = !userTests[i].err; - var testText = userTests[i].text - .split('message: ') - .pop() - .replace(/\'\);/g, ''); - - var testDoc = document.createElement('div'); - - var iconClass = didTestPass ? - '"ion-checkmark-circled big-success-icon"' : - '"ion-close-circled big-error-icon"'; - - $(testDoc).html( - "
" + - testText + - "
" - ) - .appendTo($('#testSuite')); - } -}; - -(function(win, chai) { - if (!chai) { - return; - } - win.expect = chai.expect; - win.assert = chai.assert; - win.should = chai.should(); - -}(window, window.chai)); - - -var reassembleTest = function(test, data) { - var lineNum = test.line; - var regexp = new RegExp('\/\/' + lineNum + testSalt); - return data.input.replace(regexp, test.text); -}; - -var runTests = function(err, data) { - var editorValue = editor.getValue(); - // userTests = userTests ? null : []; - var allTestsPassed = true; - pushed = false; - $('#testSuite').children().remove(); - if (err && userTests.length > 0) { - userTests = [{ - text: 'Program Execution Failure', - err: 'No user tests were run.' - }]; - createTestDisplay(); - - // Add blocks to test exploits here! - } else if (editorValue.match(/if\s\(null\)\sconsole\.log\(1\);/gi)) { - allTestsPassed = false; - userTests = [{ - text: 'Program Execution Failure', - err: 'Invalid if (null) console.log(1); detected' - }]; - createTestDisplay(); - } else if (userTests) { - userTests.push(false); - pushed = true; - userTests.forEach(function( - chaiTestFromJSON, - indexOfTestArray, - __testArray - ) { - try { - if (chaiTestFromJSON) { - /* eslint-disable no-eval, no-unused-vars */ - var output = eval(reassembleTest(chaiTestFromJSON, data)); - /* eslint-enable no-eval, no-unused-vars */ - } - } catch (error) { - allTestsPassed = false; - __testArray[indexOfTestArray].err = error.message; - } finally { - if (!chaiTestFromJSON) { - createTestDisplay(); - } - } - }); - - if (allTestsPassed) { - allTestsPassed = false; - showCompletion(); - } else { - isInitRun = false; - } - } -}; - -// step challenge -common.init.push((function() { - var stepClass = '.challenge-step'; - var nextBtnClass = '.challenge-step-btn-next'; - var actionBtnClass = '.challenge-step-btn-action'; - var finishBtnClass = '.challenge-step-btn-finish'; - var submitBtnId = '#challenge-step-btn-submit'; - var submitModalId = '#challenge-step-modal'; - - function getNextStep($challengeSteps) { - var length = $challengeSteps.length; - var $nextStep = false; - var nextStepIndex = 0; - $challengeSteps.each(function(index) { - var $step = $(this); - if ( - !$step.hasClass('hidden') && - index + 1 !== length - ) { - nextStepIndex = index + 1; - } - }); - - $nextStep = $challengeSteps[nextStepIndex]; - - return $nextStep; - } - - function handleNextStepClick(e) { - e.preventDefault(); - var nextStep = getNextStep($(stepClass)); - $(this) - .parent() - .addClass('animated fadeOutLeft') - .delay(700) - .queue(function(next) { - $(this).addClass('hidden'); - if (nextStep) { - $(nextStep) - .removeClass('hidden') - .addClass('animated slideInRight') - .delay(1000) - .queue(function(next) { - $(this).removeClass('slideInRight'); - next(); - }); - } - next(); - }); - } - - function handleActionClick(e) { - var props = common.challengeSeed[0] || - { stepIndex: [] }; - - var $el = $(this); - var index = +$el.attr('id'); - var propIndex = props.stepIndex.indexOf(index); - - if (propIndex === -1) { - return $el - .parent() - .find('.disabled') - .removeClass('disabled'); - } - - // an API action - // prevent link from opening - e.preventDefault(); - var prop = props.properties[propIndex]; - var api = props.apis[propIndex]; - if (common[prop]) { - return $el - .parent() - .find('.disabled') - .removeClass('disabled'); - } - $ - .post(api) - .done(function(data) { - // assume a boolean indicates passing - if (typeof data === 'boolean') { - return $el - .parent() - .find('.disabled') - .removeClass('disabled'); - } - // assume api returns string when fails - $el - .parent() - .find('.disabled') - .replaceWith('

' + data + '

'); - }) - .fail(function() { - console.log('failed'); - }); - } - - function handleFinishClick(e) { - e.preventDefault(); - $(submitModalId).modal('show'); - $(submitModalId + '.modal-header').click(); - $(submitBtnId).click(handleSubmitClick); - } - - function handleSubmitClick(e) { - e.preventDefault(); - - $('#submit-challenge') - .attr('disabled', 'true') - .removeClass('btn-primary') - .addClass('btn-warning disabled'); - - var $checkmarkContainer = $('#checkmark-container'); - $checkmarkContainer.css({ height: $checkmarkContainer.innerHeight() }); - - $('#challenge-checkmark') - .addClass('zoomOutUp') - .delay(1000) - .queue(function(next) { - $(this).replaceWith( - '
' + - 'submitting...
' - ); - next(); - }); - - $.post( - '/completed-bonfire/', { - challengeInfo: { - challengeId: common.challengeId, - challengeName: common.challengeName, - challengeType: common.challengeType - } - }, - function(res) { - if (res) { - window.location = - '/challenges/next-challenge?id=' + common.challengeId; - } - } - ); - } - - return function($) { - $(nextBtnClass).click(handleNextStepClick); - $(actionBtnClass).click(handleActionClick); - $(finishBtnClass).click(handleFinishClick); - }; -}(window.$))); - -function bonfireExecute(shouldTest) { - var codeOutput = common.codeOutput; - initPreview = false; - goodTests = 0; - attempts++; - ga('send', 'event', 'Challenge', 'ran-code', common.challengeName); - userTests = null; - $('#testSuite').empty(); - - if ( - common.challengeType !== '0' && - !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) - ) { - var userJavaScript = editor.getValue(); - var failedCommentTest = false; - - // checks if the number of opening comments(/*) matches the number of - // closing comments(*/) - if ( - userJavaScript.match(/\/\*/gi) && - userJavaScript.match(/\/\*/gi).length > userJavaScript.match(/\*\//gi).length - ) { - failedCommentTest = true; - } - - userJavaScript = removeComments(userJavaScript); - userJavaScript = scrapeTests(userJavaScript); - // simple fix in case the user forgets to invoke their function - - if (userJavaScript.match(/function/gi)) { - if (userJavaScript.match(/function\s*?\(|function\s+\w+\s*?\(/gi)) { - sandBox.submit(userJavaScript, function(cls, message) { - if (failedCommentTest) { - editor.setValue(editor.getValue() + '*/'); - console.log('Caught Unfinished Comment'); - codeOutput.setValue('Unfinished multi-line comment'); - failedCommentTest = false; - } else if (cls) { - codeOutput.setValue(message.error); - if (shouldTest) { - runTests('Error', null); - } - } else { - codeOutput.setValue(message.output); - codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, '')); - message.input = removeLogs(message.input); - if (shouldTest) { - runTests(null, message); - } - } - }); - } else { - codeOutput.setValue('Unsafe or unfinished function declaration'); - } - } else { - sandBox.submit(userJavaScript, function(cls, message) { - - if (failedCommentTest) { - editor.setValue(editor.getValue() + '*/'); - console.log('Caught Unfinished Comment'); - codeOutput.setValue('Unfinished mulit-line comment'); - failedCommentTest = false; - } else if (cls) { - codeOutput.setValue(message.error); - if (shouldTest) { - runTests('Error', null); - } - } else { - codeOutput.setValue(message.output); - codeOutput.setValue(codeOutput.getValue().replace(/\\\"/gi, '')); - message.input = removeLogs(message.input); - - if (shouldTest) { - runTests(null, message); - } - } - }); - } - } else { - - editorValueForIFrame = editor.getValue(); - - if (failedCommentTest) { - editor.setValue(editor.getValue() + '-->'); - editorValueForIFrame = editorValueForIFrame + '-->'; - } - if ( - !editor.getValue().match(/\$\s*?\(\s*?\$\s*?\)/gi) && - common.challengeType === '0' - ) { - safeHTMLRun(shouldTest); - } else { - workerError('Unsafe $($)'); - } - } - setTimeout(function() { - var $marginFix = $('.innerMarginFix'); - $marginFix.css('min-height', $marginFix.height()); - }, 1000); -} - -$('#submitButton').on('click', function() { - isInitRun = false; - bonfireExecute(true); -}); - -$(document).ready(function() { - - common.init.forEach(function(init) { - init($); - }); - - // init modal keybindings on open - $('#complete-courseware-dialog').on('shown.bs.modal', function() { - $('#complete-courseware-dialog').keydown(ctrlEnterClickHandler); - }); - - // remove modal keybinds on close - $('#complete-courseware-dialog').on('hidden.bs.modal', function() { - $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); - }); - - var $preview = $('#preview'); - isInitRun = true; - - if (typeof $preview.html() !== 'undefined') { - $preview.load(function() { - if (initPreview) { - bonfireExecute(true); - } - }); - } else if (common.challengeType !== 7) { - bonfireExecute(true); - } - -}); diff --git a/client/commonFramework/add-loop-protect.js b/client/commonFramework/add-loop-protect.js new file mode 100644 index 0000000000..65aebd0ed4 --- /dev/null +++ b/client/commonFramework/add-loop-protect.js @@ -0,0 +1,17 @@ +window.common = (function(global) { + const { + loopProtect, + common = { init: [] } + } = global; + + loopProtect.hit = function hit(line) { + var err = `Error: Exiting potential infinite loop at line ${line}.`; + console.error(err); + }; + + common.addLoopProtect = function addLoopProtect(code = '') { + return loopProtect(code); + }; + + return common; +})(window); diff --git a/client/commonFramework/bindings.js b/client/commonFramework/bindings.js new file mode 100644 index 0000000000..9bc6253965 --- /dev/null +++ b/client/commonFramework/bindings.js @@ -0,0 +1,259 @@ +window.common = (function(global) { + const { + $, + Rx: { Observable }, + common = { init: [] } + } = global; + + common.ctrlEnterClickHandler = function ctrlEnterClickHandler(e) { + // ctrl + enter or cmd + enter + if ( + e.keyCode === 13 && + (e.metaKey || e.ctrlKey) + ) { + $('#complete-courseware-dialog').off('keydown', ctrlEnterClickHandler); + if ($('#submit-challenge').length > 0) { + $('#submit-challenge').click(); + } else { + window.location = '/challenges/next-challenge?id=' + common.challengeId; + } + } + }; + + common.init.push(function($) { + + var $marginFix = $('.innerMarginFix'); + $marginFix.css('min-height', $marginFix.height()); + + common.submitBtn$ = Observable.fromEvent($('#submitButton'), 'click'); + + common.resetBtn$ = Observable.fromEvent($('#reset-button'), 'click'); + + // init modal keybindings on open + $('#complete-courseware-dialog').on('shown.bs.modal', function() { + $('#complete-courseware-dialog').keydown(common.ctrlEnterClickHandler); + }); + + // remove modal keybinds on close + $('#complete-courseware-dialog').on('hidden.bs.modal', function() { + $('#complete-courseware-dialog').off( + 'keydown', + common.ctrlEnterClickHandler + ); + }); + + // video checklist binding + $('.challenge-list-checkbox').on('change', function() { + var checkboxId = $(this).parent().parent().attr('id'); + if ($(this).is(':checked')) { + $(this).parent().siblings().children().addClass('faded'); + if (!localStorage || !localStorage[checkboxId]) { + localStorage[checkboxId] = true; + } + } + + if (!$(this).is(':checked')) { + $(this).parent().siblings().children().removeClass('faded'); + if (localStorage[checkboxId]) { + localStorage.removeItem(checkboxId); + } + } + }); + + $('.checklist-element').each(function() { + var checklistElementId = $(this).attr('id'); + if (localStorage[checklistElementId]) { + $(this).children().children('li').addClass('faded'); + $(this).children().children('input').trigger('click'); + } + }); + + + // video challenge submit + $('#next-courseware-button').on('click', function() { + $('#next-courseware-button').unbind('click'); + if ($('.signup-btn-nav').length < 1) { + var data; + var completedWith = $('#completed-with').val() || null; + var publicURL = $('#public-url').val() || null; + var githubURL = $('#github-url').val() || null; + switch (common.challengeType) { + case common.challengeTypes.HTML: + case common.challengeTypes.JS: + case common.challengeTypes.VIDEO: + data = { + challengeInfo: { + challengeId: common.challengeId, + challengeName: common.challengeName + } + }; + $.post('/completed-challenge/', data) + .success(function(res) { + if (!res) { + return; + } + window.location.href = '/challenges/next-challenge?id=' + + common.challengeId; + }) + .fail(function() { + window.location.href = '/challenges'; + }); + + break; + case common.challengeTypes.BASEJUMP: + case common.challengeTypes.ZIPLINE: + data = { + challengeInfo: { + challengeId: common.challengeId, + challengeName: common.challengeName, + completedWith: completedWith, + publicURL: publicURL, + githubURL: githubURL, + challengeType: common.challengeType, + verified: false + } + }; + + $.post('/completed-zipline-or-basejump/', data) + .success(function() { + window.location.href = '/challenges/next-challenge?id=' + + common.challengeId; + }) + .fail(function() { + window.location.replace(window.location.href); + }); + break; + + case common.challengeTypes.BONFIRE: + window.location.href = '/challenges/next-challenge?id=' + + common.challengeId; + break; + + default: + console.log('Happy Coding!'); + break; + } + } + }); + + if (common.challengeName) { + window.ga('send', 'event', 'Challenge', 'load', common.challengeName); + } + + $('#complete-courseware-dialog').on('hidden.bs.modal', function() { + if (common.editor.focus) { + common.editor.focus(); + } + }); + + $('#trigger-issue-modal').on('click', function() { + $('#issue-modal').modal('show'); + }); + + $('#trigger-help-modal').on('click', function() { + $('#help-modal').modal('show'); + }); + + $('#trigger-reset-modal').on('click', function() { + $('#reset-modal').modal('show'); + }); + + $('#trigger-pair-modal').on('click', function() { + $('#pair-modal').modal('show'); + }); + + $('#completed-courseware').on('click', function() { + $('#complete-courseware-dialog').modal('show'); + }); + + $('#help-ive-found-a-bug-wiki-article').on('click', function() { + window.open( + 'https://github.com/FreeCodeCamp/FreeCodeCamp/wiki/' + + "Help-I've-Found-a-Bug", + '_blank' + ); + }); + + $('#search-issue').on('click', function() { + var queryIssue = window.location.href.toString().split('?')[0]; + window.open( + 'https://github.com/FreeCodeCamp/FreeCodeCamp/issues?q=' + + 'is:issue is:all ' + + (common.challengeName) + + ' OR ' + + queryIssue + .substr(queryIssue.lastIndexOf('challenges/') + 11) + .replace('/', ''), '_blank'); + }); + + $('#gist-share').on('click', function() { + var gistWindow = window.open('', '_blank'); + + $('#gist-share') + .attr('disabled', 'true') + .removeClass('btn-danger') + .addClass('btn-warning disabled'); + + function createCORSRequest(method, url) { + var xhr = new XMLHttpRequest(); + if ('withCredentials' in xhr) { + xhr.open(method, url, true); + } else if (typeof XDomainRequest !== 'undefined') { + xhr = new XDomainRequest(); + xhr.open(method, url); + } else { + xhr = null; + } + xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8'); + return xhr; + } + + var request = createCORSRequest('post', 'https://api.github.com/gists'); + if (!request) { + return null; + } + + request.onload = function() { + if ( + request.readyState === 4 && + request.status === 201 && + request.statusText === 'Created' + ) { + gistWindow.location.href = + JSON.parse(request.responseText)['html_url']; + } + }; + + var description = common.username ? + 'http://www.freecodecamp.com/' + common.username + ' \'s s' : + 'S'; + + var data = { + description: description + 'olution for ' + common.challengeName, + public: true, + files: {} + }; + var queryIssue = window.location.href.toString().split('?')[0]; + var filename = queryIssue + .substr(queryIssue.lastIndexOf('challenges/') + 11) + .replace('/', '') + '.js'; + + data.files[filename] = { + content: '// ' + + common.challengeName + + '\n' + + (common.username ? '// Author: @' + common.username + '\n' : '') + + '// Challenge: ' + + queryIssue + + '\n' + + '// Learn to Code at Free Code Camp (www.freecodecamp.com)' + + '\n\n' + + common.editor.getValue().trim() + }; + + request.send(JSON.stringify(data)); + }); + }); + + return common; +}(window)); diff --git a/client/commonFramework/code-storage.js b/client/commonFramework/code-storage.js new file mode 100644 index 0000000000..26f6bd5b5b --- /dev/null +++ b/client/commonFramework/code-storage.js @@ -0,0 +1,47 @@ +// depends on: codeUri +window.common = (function(global) { + const { + localStorage, + common = { init: [] } + } = global; + + var codeStorage = { + getStoredValue(key) { + if ( + !localStorage || + typeof localStorage.getItem !== 'function' || + !key || + typeof key !== 'string' + ) { + console.log('unable to save to storage'); + return ''; + } + return '' + localStorage.getItem(key + 'Val'); + }, + + isAlive: function(key) { + var val = this.getStoredValue(key); + return val !== 'null' && + val !== 'undefined' && + (val && val.length > 0); + }, + + updateStorage(key, code) { + if ( + !localStorage || + typeof localStorage.setItem !== 'function' || + !key || + typeof key !== 'string' + ) { + console.log('unable to save to storage'); + return code; + } + localStorage.setItem(key + 'Val', code); + return code; + } + }; + + common.codeStorage = codeStorage; + + return common; +}(window, window.common)); diff --git a/client/commonFramework/code-uri.js b/client/commonFramework/code-uri.js new file mode 100644 index 0000000000..3051c12bd2 --- /dev/null +++ b/client/commonFramework/code-uri.js @@ -0,0 +1,144 @@ +// store code in the URL +window.common = (function(global) { + const { + encodeURIComponent: encode, + decodeURIComponent: decode, + location, + history, + common = { init: [] } + } = global; + + const { + replaceScriptTags, + replaceSafeTags, + replaceFormActionAttr, + replaceFccfaaAttr + } = common; + + function encodeFcc(val) { + return replaceScriptTags(replaceFormActionAttr(val)); + } + + function decodeFcc(val) { + return replaceSafeTags(replaceFccfaaAttr(val)); + } + + var codeUri = { + encode: function(code) { + return encode(code); + }, + decode: function(code) { + try { + return decode(code); + } catch (ignore) { + return null; + } + }, + isInQuery: function(query) { + var decoded = codeUri.decode(query); + if (!decoded || typeof decoded.split !== 'function') { + return false; + } + return decoded + .split('?') + .splice(1) + .pop() + .split('&') + .reduce(function(found, param) { + var key = param.split('=')[0]; + if (key === 'solution') { + return true; + } + return found; + }, false); + }, + isAlive: function() { + return codeUri.enabled && + codeUri.isInQuery(location.search) || + codeUri.isInQuery(location.hash); + }, + getKeyInQuery(query, keyToFind = '') { + return query + .split('&') + .reduce(function(oldValue, param) { + var key = param.split('=')[0]; + var value = param.split('=')[1]; + if (key === keyToFind) { + return value; + } + return oldValue; + }, null); + }, + getSolutionFromQuery(query = '') { + return decodeFcc( + codeUri.decode(codeUri.getKeyInQuery(query, 'solution')) + ); + }, + parse: function() { + if (!codeUri.enabled) { + return null; + } + var query; + if (location.search && codeUri.isInQuery(location.search)) { + query = location.search.replace(/^\?/, ''); + + if (history && typeof history.replaceState === 'function') { + history.replaceState( + history.state, + null, + location.href.split('?')[0] + ); + location.hash = '#?' + encodeFcc(query); + } + } else { + query = location.hash.replace(/^\#\?/, ''); + } + + if (!query) { + return null; + } + + return this.getSolutionFromQuery(query); + }, + querify: function(solution) { + if (!codeUri.enabled) { + return null; + } + if (history && typeof history.replaceState === 'function') { + // grab the url up to the query + // destroy any hash symbols still clinging to life + const url = (location.href.split('?')[0]).replace(/(#*)$/, ''); + history.replaceState( + history.state, + null, + url + + '#?' + + (codeUri.shouldRun() ? '' : 'run=disabled&') + + 'solution=' + + codeUri.encode(encodeFcc(solution)) + ); + } else { + location.hash = '?solution=' + + codeUri.encode(encodeFcc(solution)); + } + + return solution; + }, + enabled: true, + shouldRun() { + return !this.getKeyInQuery( + (location.search || location.hash).replace(/^(\?|#\?)/, ''), + 'run' + ); + } + }; + + common.init.push(function() { + codeUri.parse(); + }); + + common.codeUri = codeUri; + common.shouldRun = () => codeUri.shouldRun(); + + return common; +}(window)); diff --git a/client/commonFramework/create-editor.js b/client/commonFramework/create-editor.js new file mode 100644 index 0000000000..7990fb9c4f --- /dev/null +++ b/client/commonFramework/create-editor.js @@ -0,0 +1,113 @@ +window.common = (function(global) { + const { + Rx: { Subject, Observable }, + CodeMirror, + emmetCodeMirror, + common = { init: [] } + } = global; + + const { challengeType = '0', challengeTypes } = common; + + if ( + !CodeMirror || + challengeType === challengeTypes.BASEJUMP || + challengeType === challengeTypes.ZIPLINE || + challengeType === challengeTypes.VIDEO || + challengeType === challengeTypes.STEP || + challengeType === challengeTypes.HIKES + ) { + common.editor = {}; + return common; + } + + var editor = CodeMirror.fromTextArea( + document.getElementById('codeEditor'), + { + lint: true, + lineNumbers: true, + mode: 'javascript', + theme: 'monokai', + runnable: true, + matchBrackets: true, + autoCloseBrackets: true, + scrollbarStyle: 'null', + lineWrapping: true, + gutters: ['CodeMirror-lint-markers'] + } + ); + + editor.setSize('100%', 'auto'); + + common.editorExecute$ = new Subject(); + common.editorKeyUp$ = Observable.fromEventPattern( + (handler) => editor.on('keyup', handler), + (handler) => editor.off('keyup', handler) + ); + + editor.setOption('extraKeys', { + Tab: function(cm) { + if (cm.somethingSelected()) { + cm.indentSelection('add'); + } else { + var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); + cm.replaceSelection(spaces); + } + }, + 'Shift-Tab': function(cm) { + if (cm.somethingSelected()) { + cm.indentSelection('subtract'); + } else { + var spaces = Array(cm.getOption('indentUnit') + 1).join(' '); + cm.replaceSelection(spaces); + } + }, + 'Ctrl-Enter': function() { + common.editorExecute$.onNext(); + return false; + }, + 'Cmd-Enter': function() { + common.editorExecute$.onNext(); + return false; + } + }); + + + var info = editor.getScrollInfo(); + + var after = editor.charCoords({ + line: editor.getCursor().line + 1, + ch: 0 + }, 'local').top; + + if (info.top + info.clientHeight < after) { + editor.scrollTo(null, after - info.clientHeight + 3); + } + + if (emmetCodeMirror) { + emmetCodeMirror( + editor, + { + 'Cmd-E': 'emmet.expand_abbreviation', + Tab: 'emmet.expand_abbreviation_with_tab', + Enter: 'emmet.insert_formatted_line_break_only' + } + ); + } + common.init.push(function() { + let editorValue; + if (common.codeUri.isAlive()) { + editorValue = common.codeUri.parse(); + } else { + editorValue = common.codeStorage.isAlive(common.challengeName) ? + common.codeStorage.getStoredValue(common.challengeName) : + common.seed; + } + + editor.setValue(common.replaceSafeTags(editorValue)); + editor.refresh(); + }); + + common.editor = editor; + + return common; +}(window)); diff --git a/client/commonFramework/detect-unsafe-code-stream.js b/client/commonFramework/detect-unsafe-code-stream.js new file mode 100644 index 0000000000..14ed08f77b --- /dev/null +++ b/client/commonFramework/detect-unsafe-code-stream.js @@ -0,0 +1,55 @@ +window.common = (function(global) { + const { + Rx: { Observable }, + common = { init: [] } + } = global; + + const detectFunctionCall = /function\s*?\(|function\s+\w+\s*?\(/gi; + const detectUnsafeJQ = /\$\s*?\(\s*?\$\s*?\)/gi; + const detectUnsafeConsoleCall = /if\s\(null\)\sconsole\.log\(1\);/gi; + + common.detectUnsafeCode$ = function detectUnsafeCode$(code) { + const openingComments = code.match(/\/\*/gi); + const closingComments = code.match(/\*\//gi); + + // checks if the number of opening comments(/*) matches the number of + // closing comments(*/) + if ( + openingComments && + ( + !closingComments || + openingComments.length > closingComments.length + ) + ) { + + return Observable.throw( + new Error('SyntaxError: Unfinished multi-line comment') + ); + } + + if (code.match(detectUnsafeJQ)) { + return Observable.throw( + new Error('Unsafe $($)') + ); + } + + if ( + code.match(/function/g) && + !code.match(detectFunctionCall) + ) { + return Observable.throw( + new Error('SyntaxError: Unsafe or unfinished function declaration') + ); + } + + if (code.match(detectUnsafeConsoleCall)) { + return Observable.throw( + new Error('Invalid if (null) console.log(1); detected') + ); + } + + return Observable.just(code); + }; + + return common; +}(window)); diff --git a/client/commonFramework/display-test-results.js b/client/commonFramework/display-test-results.js new file mode 100644 index 0000000000..f30790fddb --- /dev/null +++ b/client/commonFramework/display-test-results.js @@ -0,0 +1,28 @@ +window.common = (function({ $, common = { init: [] }}) { + + common.displayTestResults = function displayTestResults(data = []) { + $('#testSuite').children().remove(); + data.forEach(({ err = false, text = '' }) => { + var iconClass = err ? + '"ion-close-circled big-error-icon"' : + '"ion-checkmark-circled big-success-icon"'; + + $('
').html(` +
+
+ +
+
+ ${text.split('message: ').pop().replace(/\'\);/g, '')} +
+
+
+ `) + .appendTo($('#testSuite')); + }); + + return data; + }; + + return common; +}(window)); diff --git a/client/commonFramework/end.js b/client/commonFramework/end.js new file mode 100644 index 0000000000..8356b6a591 --- /dev/null +++ b/client/commonFramework/end.js @@ -0,0 +1,170 @@ +$(document).ready(function() { + const common = window.common; + const { Observable } = window.Rx; + const { + addLoopProtect, + challengeName, + challengeType, + challengeTypes + } = common; + + common.init.forEach(function(init) { + init($); + }); + + // only run if editor present + if (common.editor.getValue) { + const code$ = common.editorKeyUp$ + .debounce(750) + .map(() => common.editor.getValue()) + .distinctUntilChanged() + .shareReplay(); + + // update storage + code$.subscribe( + code => { + common.codeStorage.updateStorage(common.challengeName, code); + common.codeUri.querify(code); + }, + err => console.error(err) + ); + + code$ + // only run for HTML + .filter(() => common.challengeType === challengeTypes.HTML) + .flatMap(code => { + return common.detectUnsafeCode$(code) + .map(() => { + const combinedCode = common.head + code + common.tail; + + return addLoopProtect(combinedCode); + }) + .flatMap(code => common.updatePreview$(code)) + .flatMap(() => common.checkPreview$({ code })) + .catch(err => Observable.just({ err })); + }) + .subscribe( + ({ err }) => { + if (err) { + console.error(err); + return common.updatePreview$(` +

${err}

+ `).subscribe(() => {}); + } + }, + err => console.error(err) + ); + + } + + common.resetBtn$ + .doOnNext(() => { + common.editor.setValue(common.replaceSafeTags(common.seed)); + }) + .flatMap(() => { + return common.executeChallenge$() + .catch(err => Observable.just({ err })); + }) + .subscribe( + ({ err, output, originalCode }) => { + if (err) { + console.error(err); + return common.updateOutputDisplay('' + err); + } + common.codeStorage.updateStorage(challengeName, originalCode); + common.updateOutputDisplay('' + output); + }, + (err) => { + if (err) { + console.error(err); + } + common.updateOutputDisplay('' + err); + } + ); + + Observable.merge( + common.editorExecute$, + common.submitBtn$ + ) + .flatMap(() => { + common.appendToOutputDisplay('\n// testing challenge...'); + return common.executeChallenge$() + .map(({ tests, ...rest }) => { + const solved = tests.every(test => !test.err); + return { ...rest, tests, solved }; + }) + .catch(err => Observable.just({ err })); + }) + .subscribe( + ({ err, solved, output, tests }) => { + if (err) { + console.error(err); + if (common.challengeType === common.challengeTypes.HTML) { + return common.updatePreview$(` +

${err}

+ `).first().subscribe(() => {}); + } + return common.updateOutputDisplay('' + err); + } + common.updateOutputDisplay('' + output); + common.displayTestResults(tests); + if (solved) { + common.showCompletion(); + } + }, + ({ err }) => { + console.error(err); + common.updateOutputDisplay('' + err); + } + ); + + // initial challenge run to populate tests + if (challengeType === challengeTypes.HTML) { + var $preview = $('#preview'); + return Observable.fromCallback($preview.ready, $preview)() + .delay(500) + .flatMap(() => common.executeChallenge$()) + .catch(err => Observable.just({ err })) + .subscribe( + ({ err, tests }) => { + if (err) { + console.error(err); + if (common.challengeType === common.challengeTypes.HTML) { + return common.updatePreview$(` +

${err}

+ `).subscribe(() => {}); + } + return common.updateOutputDisplay('' + err); + } + common.displayTestResults(tests); + }, + ({ err }) => { + console.error(err); + } + ); + } + + if ( + challengeType === challengeTypes.BONFIRE || + challengeType === challengeTypes.JS + ) { + Observable.just({}) + .delay(500) + .flatMap(() => common.executeChallenge$()) + .catch(err => Observable.just({ err })) + .subscribe( + ({ err, originalCode, tests }) => { + if (err) { + console.error(err); + return common.updateOutputDisplay('' + err); + } + common.codeStorage.updateStorage(challengeName, originalCode); + common.displayTestResults(tests); + }, + (err) => { + console.error(err); + common.updateOutputDisplay('' + err); + } + ); + } +}); diff --git a/client/commonFramework/execute-challenge-stream.js b/client/commonFramework/execute-challenge-stream.js new file mode 100644 index 0000000000..2a29b9edf6 --- /dev/null +++ b/client/commonFramework/execute-challenge-stream.js @@ -0,0 +1,61 @@ +window.common = (function(global) { + const { + ga, + common = { init: [] } + } = global; + + const { + addLoopProtect, + getJsFromHtml, + detectUnsafeCode$, + updatePreview$, + challengeType, + challengeTypes + } = common; + + let attempts = 0; + + common.executeChallenge$ = function executeChallenge$() { + const code = common.editor.getValue(); + const originalCode = code; + const head = common.arrayToNewLineString(common.head); + const tail = common.arrayToNewLineString(common.tail); + const combinedCode = head + code + tail; + + attempts++; + + ga('send', 'event', 'Challenge', 'ran-code', common.challengeName); + + // run checks for unsafe code + return detectUnsafeCode$(code) + // add head and tail and detect loops + .map(() => { + if (challengeType !== challengeTypes.HTML) { + return ``; + } + + return addLoopProtect(combinedCode); + }) + .flatMap(code => updatePreview$(code)) + .flatMap(code => { + let output; + + if ( + challengeType === challengeTypes.HTML && + common.hasJs(code) + ) { + output = common.getJsOutput(getJsFromHtml(code)); + } else if (challengeType !== challengeTypes.HTML) { + output = common.getJsOutput(addLoopProtect(combinedCode)); + } + + return common.runPreviewTests$({ + tests: common.tests.slice(), + originalCode, + output + }); + }); + }; + + return common; +}(window)); diff --git a/client/commonFramework/get-iframe.js b/client/commonFramework/get-iframe.js new file mode 100644 index 0000000000..012dce7b50 --- /dev/null +++ b/client/commonFramework/get-iframe.js @@ -0,0 +1,23 @@ +window.common = (function(global) { + const { + common = { init: [] }, + document: doc + } = global; + + common.getIframe = function getIframe(id = 'preview') { + let previewFrame = doc.getElementById(id); + + // create and append a hidden preview frame + if (!previewFrame) { + previewFrame = doc.createElement('iframe'); + previewFrame.id = id; + previewFrame.setAttribute('style', 'display: none'); + doc.body.appendChild(previewFrame); + } + + return previewFrame.contentDocument || + previewFrame.contentWindow.document; + }; + + return common; +})(window); diff --git a/client/commonFramework/init.js b/client/commonFramework/init.js new file mode 100644 index 0000000000..ff4130efd1 --- /dev/null +++ b/client/commonFramework/init.js @@ -0,0 +1,120 @@ +window.common = (function(global) { + // common namespace + // all classes should be stored here + // called at the beginning of dom ready + const { + Rx: { Disposable, Observable, config }, + common = { init: [] } + } = global; + + config.longStackSupport = true; + common.head = common.head || []; + common.tail = common.tail || []; + common.salt = Math.random(); + + common.challengeTypes = { + HTML: '0', + JS: '1', + VIDEO: '2', + ZIPLINE: '3', + BASEJUMP: '4', + BONFIRE: '5', + HIKES: '6', + STEP: '7' + }; + + + common.arrayToNewLineString = function arrayToNewLineString(seedData) { + seedData = Array.isArray(seedData) ? seedData : [seedData]; + return seedData.reduce(function(seed, line) { + return '' + seed + line + '\n'; + }, ''); + }; + + common.seed = common.arrayToNewLineString(common.challengeSeed); + + common.replaceScriptTags = function replaceScriptTags(value) { + return value + .replace(/'); + }; + + common.replaceFormActionAttr = function replaceFormAction(value) { + return value.replace(/]*>/, function(val) { + return val.replace(/action(\s*?)=/, 'fccfaa$1='); + }); + }; + + common.replaceFccfaaAttr = function replaceFccfaaAttr(value) { + return value.replace(/]*>/, function(val) { + return val.replace(/fccfaa(\s*?)=/, 'action$1='); + }); + }; + + common.scopejQuery = function scopejQuery(str) { + return str + .replace(/\$/gi, 'j$') + .replace(/document/gi, 'jdocument') + .replace(/jQuery/gi, 'jjQuery'); + }; + + common.unScopeJQuery = function unScopeJQuery(str) { + return str + .replace(/j\$/gi, '$') + .replace(/jdocument/gi, 'document') + .replace(/jjQuery/gi, 'jQuery'); + }; + + const commentRegex = /(\/\*[^(\*\/)]*\*\/)|([ \n]\/\/[^\n]*)/g; + common.removeComments = function removeComments(str) { + return str.replace(commentRegex, ''); + }; + + const logRegex = /(console\.[\w]+\s*\(.*\;)/g; + common.removeLogs = function removeLogs(str) { + return str.replace(logRegex, ''); + }; + + common.reassembleTest = function reassembleTest(code = '', { line, text }) { + var regexp = new RegExp('//' + line + common.salt); + return code.replace(regexp, text); + }; + + common.getScriptContent$ = function getScriptContent$(script) { + return Observable.create(function(observer) { + const jqXHR = $.get(script, null, null, 'text') + .success(data => { + observer.onNext(data); + observer.onCompleted(); + }) + .fail(e => observer.onError(e)) + .always(() => observer.onCompleted()); + + return new Disposable(() => { + jqXHR.abort(); + }); + }); + }; + + const openScript = /\<\s?script\s?\>/gi; + const closingScript = /\<\s?\/\s?script\s?\>/gi; + + // detects if there is JavaScript in the first script tag + common.hasJs = function hasJs(code) { + return !!common.getJsFromHtml(code); + }; + + // grabs the content from the first script tag in the code + common.getJsFromHtml = function getJsFromHtml(code) { + // grab user javaScript + return (code.split(openScript)[1] || '').split(closingScript)[0] || ''; + }; + + return common; +})(window); diff --git a/client/commonFramework/output-display.js b/client/commonFramework/output-display.js new file mode 100644 index 0000000000..25d9b28acd --- /dev/null +++ b/client/commonFramework/output-display.js @@ -0,0 +1,51 @@ +window.common = (function(global) { + const { + CodeMirror, + document: doc, + common = { init: [] } + } = global; + + const { challengeTypes, challengeType = '0' } = common; + + if ( + !CodeMirror || + challengeType !== challengeTypes.JS && + challengeType !== challengeTypes.BONFIRE + ) { + common.updateOutputDisplay = () => {}; + common.appendToOutputDisplay = () => {}; + return common; + } + + var codeOutput = CodeMirror.fromTextArea( + doc.getElementById('codeOutput'), + { + lineNumbers: false, + mode: 'text', + theme: 'monokai', + readOnly: 'nocursor', + lineWrapping: true + } + ); + + codeOutput.setValue(`/** + * Your output will go here. + * Console.log() -type statements + * will appear in your browser\'s + * DevTools JavaScript console. + */`); + + codeOutput.setSize('100%', '100%'); + + common.updateOutputDisplay = function updateOutputDisplay(str = '') { + codeOutput.setValue(str); + return str; + }; + + common.appendToOutputDisplay = function appendToOutputDisplay(str = '') { + codeOutput.setValue(codeOutput.getValue() + str); + return str; + }; + + return common; +}(window)); diff --git a/client/commonFramework/phone-scroll-lock.js b/client/commonFramework/phone-scroll-lock.js new file mode 100644 index 0000000000..bf5af55c4f --- /dev/null +++ b/client/commonFramework/phone-scroll-lock.js @@ -0,0 +1,132 @@ +window.common = (function({ common = { init: [] }}) { + + common.lockTop = function lockTop() { + var magiVal; + + if ($(window).width() >= 990) { + if ($('.editorScrollDiv').html()) { + + magiVal = $(window).height() - $('.navbar').height(); + + if (magiVal < 0) { + magiVal = 0; + } + $('.editorScrollDiv').css('height', magiVal - 85 + 'px'); + } + + magiVal = $(window).height() - $('.navbar').height(); + + if (magiVal < 0) { + magiVal = 0; + } + + $('.scroll-locker') + .css('min-height', $('.editorScrollDiv').height()) + .css('height', magiVal - 185); + } else { + $('.editorScrollDiv').css('max-height', 500 + 'px'); + + $('.scroll-locker') + .css('position', 'inherit') + .css('top', 'inherit') + .css('width', '100%') + .css('max-height', '85%'); + } + }; + + common.init.push(function($) { + // fakeiphone positioning hotfix + if ( + $('.iphone-position').html() || + $('.iphone').html() + ) { + var startIphonePosition = parseInt( + $('.iphone-position') + .css('top') + .replace('px', ''), + 10 + ); + + var startIphone = parseInt( + $('.iphone') + .css('top') + .replace('px', ''), + 10 + ); + + $(window).on('scroll', function() { + var courseHeight = $('.courseware-height').height(); + var courseTop = $('.courseware-height').offset().top; + var windowScrollTop = $(window).scrollTop(); + var phoneHeight = $('.iphone-position').height(); + + if (courseHeight + courseTop - windowScrollTop - phoneHeight <= 0) { + $('.iphone-position').css( + 'top', + startIphonePosition + + courseHeight + + courseTop - + windowScrollTop - + phoneHeight + ); + + $('.iphone').css( + 'top', + startIphonePosition + + courseHeight + + courseTop - + windowScrollTop - + phoneHeight + + 120 + ); + } else { + $('.iphone-position').css('top', startIphonePosition); + $('.iphone').css('top', startIphone); + } + }); + } + + if ($('.scroll-locker').html()) { + + if ($('.scroll-locker').html()) { + common.lockTop(); + $(window).on('resize', function() { + common.lockTop(); + }); + $(window).on('scroll', function() { + common.lockTop(); + }); + } + + var execInProgress = false; + + // why is this not $??? + document + .getElementById('scroll-locker') + .addEventListener( + 'previewUpdateSpy', + function(e) { + if (execInProgress) { + return null; + } + execInProgress = true; + setTimeout(function() { + if ( + $($('.scroll-locker').children()[0]).height() - 800 > e.detail + ) { + $('.scroll-locker').scrollTop(e.detail); + } else { + var scrollTop = $($('.scroll-locker').children()[0]).height(); + + $('.scroll-locker').animate({ scrollTop: scrollTop }, 175); + } + execInProgress = false; + }, 750); + }, + false + ); + } + }); + + return common; +}(window)); diff --git a/client/commonFramework/report-issue.js b/client/commonFramework/report-issue.js new file mode 100644 index 0000000000..9218df0594 --- /dev/null +++ b/client/commonFramework/report-issue.js @@ -0,0 +1,56 @@ +window.common = (function({ common = { init: [] } }) { + common.init.push(function($) { + $('#report-issue').on('click', function() { + var textMessage = [ + 'Challenge [', + (common.challengeName || window.location.pathname), + '](', + window.location.href, + ') has an issue.\n', + 'User Agent is: ', + navigator.userAgent, + '.\n', + 'Please describe how to reproduce this issue, and include ', + 'links to screenshots if possible.\n\n' + ].join(''); + + if ( + common.editor && + typeof common.editor.getValue === 'function' && + common.editor.getValue().trim() + ) { + var type; + switch (common.challengeType) { + case common.challengeTypes.HTML: + type = 'html'; + break; + case common.challengeTypes.JS: + case common.challengeTypes.BONFIRE: + type = 'javascript'; + break; + default: + type = ''; + } + + textMessage += [ + 'My code:\n```', + type, + '\n', + common.editor.getValue(), + '\n```\n\n' + ].join(''); + } + + textMessage = encodeURIComponent(textMessage); + + $('#issue-modal').modal('hide'); + window.open( + 'https://github.com/freecodecamp/freecodecamp/issues/new?&body=' + + textMessage, + '_blank' + ); + }); + }); + + return common; +}(window)); diff --git a/client/commonFramework/run-tests-stream.js b/client/commonFramework/run-tests-stream.js new file mode 100644 index 0000000000..df5bc39dbd --- /dev/null +++ b/client/commonFramework/run-tests-stream.js @@ -0,0 +1,40 @@ +window.common = (function(global) { + const { + Rx: { Observable }, + chai, + common = { init: [] } + } = global; + + common.runTests$ = function runTests$({ + code, + originalCode, + userTests, + ...rest + }) { + + return Observable.from(userTests) + .map(function(test) { + + /* eslint-disable no-unused-vars */ + const assert = chai.assert; + const editor = { getValue() { return originalCode; }}; + /* eslint-enable no-unused-vars */ + + try { + if (test) { + /* eslint-disable no-eval */ + eval(common.reassembleTest(code, test)); + /* eslint-enable no-eval */ + } + } catch (e) { + test.err = e.message; + } + + return test; + }) + .toArray() + .map(tests => ({ ...rest, tests })); + }; + + return common; +}(window)); diff --git a/client/commonFramework/show-completion.js b/client/commonFramework/show-completion.js new file mode 100644 index 0000000000..434bb685fe --- /dev/null +++ b/client/commonFramework/show-completion.js @@ -0,0 +1,70 @@ +window.common = (function(global) { + const { + $, + ga = (() => {}), + common = { init: [] } + } = global; + + common.showCompletion = function showCompletion() { + var time = Math.floor(Date.now() - window.started); + + ga( + 'send', + 'event', + 'Challenge', + 'solved', + common.challengeName + ', Time: ' + time + ', Attempts: ' + 0 + ); + + var bonfireSolution = common.editor.getValue(); + var didCompleteWith = $('#completed-with').val() || null; + + $('#complete-courseware-dialog').modal('show'); + $('#complete-courseware-dialog .modal-header').click(); + + $('#submit-challenge').click(function(e) { + e.preventDefault(); + + $('#submit-challenge') + .attr('disabled', 'true') + .removeClass('btn-primary') + .addClass('btn-warning disabled'); + + var $checkmarkContainer = $('#checkmark-container'); + $checkmarkContainer.css({ height: $checkmarkContainer.innerHeight() }); + + $('#challenge-checkmark') + .addClass('zoomOutUp') + // .removeClass('zoomInDown') + .delay(1000) + .queue(function(next) { + $(this).replaceWith( + '
' + + 'submitting...
' + ); + next(); + }); + + $.post( + '/completed-bonfire/', { + challengeInfo: { + challengeId: common.challengeId, + challengeName: common.challengeName, + completedWith: didCompleteWith, + challengeType: common.challengeType, + solution: bonfireSolution + } + }, + function(res) { + if (res) { + window.location = + '/challenges/next-challenge?id=' + common.challengeId; + } + } + ); + }); + }; + + return common; +}(window)); diff --git a/client/commonFramework/step-challenge.js b/client/commonFramework/step-challenge.js new file mode 100644 index 0000000000..6b47565298 --- /dev/null +++ b/client/commonFramework/step-challenge.js @@ -0,0 +1,200 @@ +window.common = (function({ $, common = { init: [] }}) { + const stepClass = '.challenge-step'; + const prevBtnClass = '.challenge-step-btn-prev'; + const nextBtnClass = '.challenge-step-btn-next'; + const actionBtnClass = '.challenge-step-btn-action'; + const finishBtnClass = '.challenge-step-btn-finish'; + const submitBtnId = '#challenge-step-btn-submit'; + const submitModalId = '#challenge-step-modal'; + + function getPreviousStep($challengeSteps) { + var $prevStep = false; + var prevStepIndex = 0; + $challengeSteps.each(function(index) { + var $step = $(this); + if (!$step.hasClass('hidden')) { + prevStepIndex = index - 1; + } + }); + + $prevStep = $challengeSteps[prevStepIndex]; + + return $prevStep; + } + + function getNextStep($challengeSteps) { + var length = $challengeSteps.length; + var $nextStep = false; + var nextStepIndex = 0; + $challengeSteps.each(function(index) { + var $step = $(this); + if ( + !$step.hasClass('hidden') && + index + 1 !== length + ) { + nextStepIndex = index + 1; + } + }); + + $nextStep = $challengeSteps[nextStepIndex]; + + return $nextStep; + } + + function handlePrevStepClick(e) { + e.preventDefault(); + var prevStep = getPreviousStep($(stepClass)); + $(this) + .parent() + .parent() + .removeClass('slideInLeft slideInRight') + .addClass('animated fadeOutRight fast-animation') + .delay(250) + .queue(function(prev) { + $(this).addClass('hidden'); + if (prevStep) { + $(prevStep) + .removeClass('hidden') + .removeClass('fadeOutLeft fadeOutRight') + .addClass('animated slideInLeft fast-animation') + .delay(500) + .queue(function(prev) { + prev(); + }); + } + prev(); + }); + } + + function handleNextStepClick(e) { + e.preventDefault(); + var nextStep = getNextStep($(stepClass)); + $(this) + .parent() + .parent() + .removeClass('slideInRight slideInLeft') + .addClass('animated fadeOutLeft fast-animation') + .delay(250) + .queue(function(next) { + $(this).addClass('hidden'); + if (nextStep) { + $(nextStep) + .removeClass('hidden') + .removeClass('fadeOutRight fadeOutLeft') + .addClass('animated slideInRight fast-animation') + .delay(500) + .queue(function(next) { + next(); + }); + } + next(); + }); + } + + function handleActionClick(e) { + var props = common.challengeSeed[0] || + { stepIndex: [] }; + + var $el = $(this); + var index = +$el.attr('id'); + var propIndex = props.stepIndex.indexOf(index); + + if (propIndex === -1) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + + // an API action + // prevent link from opening + e.preventDefault(); + var prop = props.properties[propIndex]; + var api = props.apis[propIndex]; + if (common[prop]) { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + $ + .post(api) + .done(function(data) { + // assume a boolean indicates passing + if (typeof data === 'boolean') { + return $el + .parent() + .find('.disabled') + .removeClass('disabled'); + } + // assume api returns string when fails + $el + .parent() + .find('.disabled') + .replaceWith('

' + data + '

'); + }) + .fail(function() { + console.log('failed'); + }); + } + + function handleFinishClick(e) { + e.preventDefault(); + $(submitModalId).modal('show'); + $(submitModalId + '.modal-header').click(); + $(submitBtnId).click(handleSubmitClick); + } + + function handleSubmitClick(e) { + e.preventDefault(); + + $('#submit-challenge') + .attr('disabled', 'true') + .removeClass('btn-primary') + .addClass('btn-warning disabled'); + + var $checkmarkContainer = $('#checkmark-container'); + $checkmarkContainer.css({ height: $checkmarkContainer.innerHeight() }); + + $('#challenge-checkmark') + .addClass('zoomOutUp') + .delay(1000) + .queue(function(next) { + $(this).replaceWith( + '
' + + 'submitting...
' + ); + next(); + }); + + $.post( + '/completed-bonfire/', { + challengeInfo: { + challengeId: common.challengeId, + challengeName: common.challengeName, + challengeType: common.challengeType + } + }, + function(res) { + if (res) { + window.location = + '/challenges/next-challenge?id=' + common.challengeId; + } + } + ); + } + + common.init.push(function($) { + if (common.challengeType !== '7') { + return null; + } + + $(prevBtnClass).click(handlePrevStepClick); + $(nextBtnClass).click(handleNextStepClick); + $(actionBtnClass).click(handleActionClick); + $(finishBtnClass).click(handleFinishClick); + }); + + return common; +}(window)); diff --git a/client/commonFramework/update-preview.js b/client/commonFramework/update-preview.js new file mode 100644 index 0000000000..ab99e46a6f --- /dev/null +++ b/client/commonFramework/update-preview.js @@ -0,0 +1,99 @@ +window.common = (function(global) { + const { + Rx: { BehaviorSubject, Observable }, + common = { init: [] } + } = global; + + // the first script tag here is to proxy jQuery + // We use the same jQuery on the main window but we change the + // context to that of the iframe. + var libraryIncludes = ` + + + + + + + `; + const codeDisabledError = ` + + `; + + const iFrameScript$ = + common.getScriptContent$('/js/iFrameScripts.js').shareReplay(); + const jQueryScript$ = common.getScriptContent$( + '/bower_components/jquery/dist/jquery.js' + ).shareReplay(); + + // behavior subject allways remembers the last value + // we use this to determine if runPreviewTest$ is defined + // and prime it with false + common.previewReady$ = new BehaviorSubject(false); + + // These should be set up in the preview window + // if this error is seen it is because the function tried to run + // before the iframe has completely loaded + common.runPreviewTests$ = + common.checkPreview$ = + () => Observable.throw(new Error('Preview not fully loaded')); + + + common.updatePreview$ = function updatePreview$(code = '') { + const preview = common.getIframe('preview'); + + return Observable.combineLatest( + iFrameScript$, + jQueryScript$, + (iframe, jQuery) => ({ + iframeScript: ``, + jQuery: `` + }) + ) + .first() + .flatMap(({ iframeScript, jQuery }) => { + // we make sure to override the last value in the + // subject to false here. + common.previewReady$.onNext(false); + preview.open(); + preview.write( + libraryIncludes + + jQuery + + (common.shouldRun() ? code : codeDisabledError) + + '' + + iframeScript + ); + preview.close(); + // now we filter false values and wait for the first true + return common.previewReady$ + .filter(ready => ready) + .first() + // the delay here is to give code within the iframe + // control to run + .delay(400); + }) + .map(() => code); + }; + + return common; +}(window)); diff --git a/client/iFrameScripts.js b/client/iFrameScripts.js index 1d5db5413d..e9d1456f88 100644 --- a/client/iFrameScripts.js +++ b/client/iFrameScripts.js @@ -1,23 +1,85 @@ -(function() { - var expect = chai.expect; +/* eslint-disable no-undef, no-unused-vars, no-native-reassign */ +// the $ on the iframe window object is the same +// as the one used on the main site, but +// uses the iframe document as the context +window.$(document).ready(function() { + var _ = parent._; + var Rx = parent.Rx; + var chai = parent.chai; + var assert = chai.assert; var tests = parent.tests; - var editor = parent.editorValueForIFrame; + var common = parent.common; - setTimeout(function() { - for (var i = 0; i < tests.length; i++) { - var thisTest = true; - try { - eval(parent.tests[i]); - } catch (err) { - allTestsGood = false; - thisTest = false; - parent.postError(JSON.stringify(err.message.split(':').shift())); - } finally { - if (thisTest) { - parent.postSuccess(JSON.stringify(tests[i].split(',').pop().replace( - /\'/g, '').replace(/\)/, ''))); - } - } + common.getJsOutput = function evalJs(code = '') { + if (window.__err || !common.shouldRun()) { + return window.__err || 'code disabled'; } - }, 10); -})(); + let output; + try { + /* eslint-disable no-eval */ + output = eval(code); + /* eslint-enable no-eval */ + } catch (e) { + window.__err = e; + } + return output; + }; + + common.runPreviewTests$ = + function runPreviewTests$({ + tests = [], + originalCode, + ...rest + }) { + const code = originalCode; + const editor = { getValue() { return originalCode; } }; + if (window.__err) { + return Rx.Observable.throw(window.__err); + } + + // Iterate throught the test one at a time + // on new stacks + return Rx.Observable.from(tests, null, null, Rx.Scheduler.default) + // add delay here for firefox to catch up + .delay(100) + .map(test => { + const userTest = {}; + try { + /* eslint-disable no-eval */ + eval(test); + /* eslint-enable no-eval */ + } catch (e) { + userTest.err = e.message.split(':').shift(); + } finally { + if (!test.match(/message: /g)) { + // assumes test does not contain arrays + // This is a patch until all test fall into this pattern + userTest.text = test + .split(',') + .pop(); + userTest.text = 'message: ' + userTest.text + '\');'; + } else { + userTest.text = test; + } + } + return userTest; + }) + // gather tests back into an array + .toArray() + .map(tests => ({ ...rest, tests, originalCode })); + }; + + // used when updating preview without running tests + common.checkPreview$ = function checkPreview$(args) { + if (window.__err) { + return Rx.Observable.throw(window.__err); + } + return Rx.Observable.just(args); + }; + + // now that the runPreviewTest$ is defined + // we set the subject to true + // this will let the updatePreview + // script now that we are ready. + common.previewReady$.onNext(true); +}); diff --git a/client/index.js b/client/index.js index 44bdb0a82c..d76e2a78ef 100644 --- a/client/index.js +++ b/client/index.js @@ -22,6 +22,19 @@ const history = createHistory(); const appLocation = createLocation( location.pathname + location.search ); + +function location$(history) { + return Rx.Observable.create(function(observer) { + const dispose = history.listen(function(location) { + observer.onNext(location.pathname); + }); + + return Rx.Disposable.create(() => { + dispose(); + }); + }); +} + // returns an observable app$({ history, location: appLocation }) .flatMap( @@ -36,6 +49,27 @@ app$({ history, location: appLocation }) // redirects in the future ({ nextLocation, props }, appCat) => ({ nextLocation, props, appCat }) ) + .doOnNext(({ appCat }) => { + const appActions = appCat.getActions('appActions'); + + location$(history) + .pluck('pathname') + .distinctUntilChanged() + .doOnNext(route => debug('route change', route)) + .subscribe(route => appActions.updateRoute(route)); + + appActions.goBack.subscribe(function() { + history.goBack(); + }); + + appActions + .updateRoute + .pluck('route') + .doOnNext(route => debug('update route', route)) + .subscribe(function(route) { + history.pushState(null, route); + }); + }) .flatMap(({ props, appCat }) => { props.history = history; return Render( @@ -49,7 +83,7 @@ app$({ history, location: appLocation }) debug('react rendered'); }, err => { - debug('an error has occured', err.stack); + throw err; }, () => { debug('react closed subscription'); diff --git a/client/less/chat.less b/client/less/chat.less new file mode 100644 index 0000000000..a6f83171ca --- /dev/null +++ b/client/less/chat.less @@ -0,0 +1,24 @@ +.chat-embed-main-title { + display: flex; + flex-grow: 1; + padding-left: 31px; + padding-top: 7px; +} + +.gitter-chat-embed { + z-index: 100; + position: fixed; + + top: 0; + left: 60%; + bottom: 0; + right: 0; + + display: flex; + flex-direction: row; + transition: transform 0.3s cubic-bezier(0.16, 0.22, 0.22, 1.7); +} + +.gitter-chat-embed.is-collapsed:not(.is-loading) { + transform: translateX(110%); +} diff --git a/client/less/jobs.less b/client/less/jobs.less new file mode 100644 index 0000000000..e1d20aa500 --- /dev/null +++ b/client/less/jobs.less @@ -0,0 +1,16 @@ +.jobs-list-highlight { + background-color: #ffc +} + +a.jobs-list-highlight:hover { + background-color: #ffc +} + +.jobs-list { + cursor: pointer; + cursor: hand; +} + +.jobs-checkbox-spacer input[type="checkbox"] { + margin-left: -23px +} diff --git a/client/less/lib/bootstrap/navbar.less b/client/less/lib/bootstrap/navbar.less index 7a0753c175..c0ffe7f998 100755 --- a/client/less/lib/bootstrap/navbar.less +++ b/client/less/lib/bootstrap/navbar.less @@ -424,7 +424,6 @@ &:hover, &:focus { color: @navbar-default-link-active-color; - background-color: @navbar-default-link-active-bg; } } > .disabled > a { diff --git a/client/less/main.less b/client/less/main.less index 067290e9be..c0bc9c2c56 100644 --- a/client/less/main.less +++ b/client/less/main.less @@ -9,6 +9,11 @@ html,body,div,span,a,li,td,th { font-weight: 300; } +bold { + font-family: 'Lato-Bold', sans-serif; + font-weight: Bold; +} + li, .wrappable { white-space: pre; /* CSS 2.0 */ white-space: pre-wrap; /* CSS 2.1 */ @@ -57,7 +62,7 @@ body.top-and-bottom-margins { } body.no-top-and-bottom-margins { - margin: 70px 20px 50px 20px; + margin: 70px 20px 0px 20px; } h1, h2 { @@ -96,6 +101,10 @@ h1, h2, h3, h4, h5, h6, p, li { margin-right: 5px; } +.fa:hover { + text-decoration: none; +} + .img-center { margin: 0 auto; } @@ -141,6 +150,12 @@ ul { font-size: 26px; } +.navbar > .container { + width: auto; + padding-left: 0px; + padding-right: 0px; +} + .nav-height { height: 50px; border: none; @@ -216,8 +231,13 @@ ul { font-size: 20px; } -.large-li { - font-size: 24px; +.link-table td{ + font-size: 16px; + border-top: none !important; + @media (min-width: 767px) { + font-size: 24px; + } + } .capitalize { @@ -374,10 +394,6 @@ ul { } } -.table { - margin-left: -16px; -} - thead { font-size: 150%; } @@ -396,6 +412,19 @@ thead { width: 50px; } +.brownie-points-nav { + @media (min-width: 991px) and (max-width: 999px) { + margin-right: -10px; + } +} + +.signin-button-nav { + @media (min-width: 991px) and (max-width: 1010px) { + margin-left: -10px; + margin-right: -5px; + } +} + .navbar-nav a { color: @gray-lighter; font-size: 20px; @@ -415,6 +444,7 @@ thead { margin-top: -2px !important; padding-top: 10px !important; padding-bottom: 10px !important; + margin-right: -12px; } .public-profile-img { @@ -486,15 +516,27 @@ thead { color: #009900 } +.testimonial-image-jobs { + border-radius: 5px; + color: #009900 +} + + .default-border-radius { border-radius: 5px; } +.story-section { + height: 500px; +} + .testimonial-copy { - font-size: 20px; - text-align: center; + text-align: justify; + font-size: 18px !important; + margin-left: 20px; + margin-right: 20px; @media (min-width: 991px) and (max-width: 1199px) { - height: 120px; + height: 140px; } @media (min-width: 1200px) { height: 90px; @@ -582,6 +624,10 @@ form.code span { height: 100%; } +.CodeMirror { + line-height: 1 !important; +} + .CodeMirror-linenumber { font-size: 18px; font-family: "Ubuntu Mono"; @@ -679,6 +725,24 @@ iframe.iphone { transition: background .2s ease-in-out, border .2s ease-in-out; } +.btn-warning-ghost { + background: transparent; + color: @brand-warning; + + /* CSS Transition */ + -webkit-transition: background .2s ease-in-out, border .2s ease-in-out; + -moz-transition: background .2s ease-in-out, border .2s ease-in-out; + -ms-transition: background .2s ease-in-out, border .2s ease-in-out; + -o-transition: background .2s ease-in-out, border .2s ease-in-out; + transition: background .2s ease-in-out, border .2s ease-in-out; +} + +.population-table { + @media (max-width: 767px) { + font-size: 16px; + } +} + @media (max-width: 991px) { .navbar-header { float: none; @@ -721,7 +785,7 @@ iframe.iphone { } } -.hamburger { +.navbar-toggle { width: 80px; padding-left: 0px; padding-right: 8px; @@ -781,7 +845,8 @@ iframe.iphone { .news-box-search { @media (min-width: 768px) { - margin-top: -50px; + margin-top: -30px; + padding-bottom: 20px; } @media (max-width: 767px) { padding: 5px; @@ -800,6 +865,10 @@ iframe.iphone { margin-top: -5px; } +.landing-heading { + font-size: 50px !important; +} + .mobile-story-headline { font-size: 20px; } @@ -814,15 +883,9 @@ iframe.iphone { } hr { - -moz-border-bottom-colors: none; - -moz-border-image: none; - -moz-border-left-colors: none; - -moz-border-right-colors: none; - -moz-border-top-colors: none; - border-color: @gray; - border-style: solid none; - border-width: 1px 0; - margin: 18px 0; + border: 0; + height: 1px; + background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75), rgba(0, 0, 0, 0)); } .big-spacer { @@ -945,6 +1008,11 @@ code { margin: 0!important; } +.gitter-chat-embed { + z-index: 20000 !important; +} + + //uncomment this to see the dimensions of all elements outlined in red //* { // border-color: red; @@ -1052,3 +1120,6 @@ code { transform: rotate(0deg); } } + +@import "chat.less"; +@import "jobs.less"; diff --git a/client/main.js b/client/main.js index 9ba4e834b5..60c9cbbe39 100644 --- a/client/main.js +++ b/client/main.js @@ -1,19 +1,127 @@ -var mapShareKey = 'map-shares'; +var main = window.main || {}; + +main.mapShareKey = 'map-shares'; + +main.ga = window.ga || function() {}; + +main = (function(main) { + + // should be set before gitter script loads + ((window.gitter = {}).chat = {}).options = { + disableDefaultChat: true + }; + // wait for sidecar to load + + main.chat = {}; + main.chat.isOpen = false; + main.chat.createHelpChat = function createHelpChat() { + throw new Error('Sidecar chat has not initialized'); + }; + + document.addEventListener('gitter-sidecar-ready', function(e) { + main.chat.GitterChat = e.detail.Chat; + + main.chat.createHelpChat = function(room, helpChatBtnClass, roomTitle) { + roomTitle = roomTitle || 'Waypoint Help'; + + $('body').append( + '
); } diff --git a/common/app/Cat.js b/common/app/Cat.js index 383409b24c..31dae9d294 100644 --- a/common/app/Cat.js +++ b/common/app/Cat.js @@ -12,6 +12,6 @@ export default Cat() cat.register(HikesActions, null, services); cat.register(HikesStore, null, cat); - cat.register(JobActions, null, services); + cat.register(JobActions, null, cat, services); cat.register(JobsStore, null, cat); }); diff --git a/common/app/components/Flash/Queue.jsx b/common/app/components/Flash/Queue.jsx new file mode 100644 index 0000000000..ac36ca3f46 --- /dev/null +++ b/common/app/components/Flash/Queue.jsx @@ -0,0 +1,27 @@ +import React, { PropTypes } from 'react'; +import { Alert } from 'react-bootstrap'; + +export default React.createClass({ + displayName: 'FlashQueue', + + propTypes: { + messages: PropTypes.array + }, + + renderMessages(messages) { + return messages.map(() => { + return ( + + ); + }); + }, + + render() { + const { messages = [] } = this.props; + return ( +
+ { this.renderMessages(messages) } +
+ ); + } +}); diff --git a/common/app/components/Flash/index.jsx b/common/app/components/Flash/index.jsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/common/app/components/Footer/links.json b/common/app/components/Footer/links.json index 92199b127f..8089a868e4 100644 --- a/common/app/components/Footer/links.json +++ b/common/app/components/Footer/links.json @@ -2,7 +2,7 @@ { "className": "ion-speakerphone", "content": " Blog ", - "href": "http://blog.freecodecamp.com", + "href": "http://medium.freecodecamp.com", "target": "_blank" }, { diff --git a/common/app/components/Nav/Nav.jsx b/common/app/components/Nav/Nav.jsx index ad7f81f7b8..6cdb2faaea 100644 --- a/common/app/components/Nav/Nav.jsx +++ b/common/app/components/Nav/Nav.jsx @@ -1,8 +1,9 @@ import React, { PropTypes } from 'react'; +import { LinkContainer } from 'react-router-bootstrap'; import { Col, - CollapsibleNav, Nav, + NavbarBrand, Navbar, NavItem } from 'react-bootstrap'; @@ -10,17 +11,8 @@ import { import navLinks from './links.json'; import FCCNavItem from './NavItem.jsx'; +const win = typeof window !== 'undefined' ? window : {}; const fCClogo = 'https://s3.amazonaws.com/freecodecamp/freecodecamp_logo.svg'; -const navElements = navLinks.map((navItem, index) => { - return ( - - { navItem.content } - - ); -}); const logoElement = ( @@ -31,38 +23,87 @@ const logoElement = ( ); -const toggleButton = ( - ); -export default class extends React.Component { - constructor(props) { - super(props); +function getDashedName() { + let challengeDashedName; + if (typeof win.localStorage !== 'undefined') { + challengeDashedName = win.localStorage.getItem('currentDashedName'); } + return challengeDashedName && challengeDashedName !== 'undefined' ? + challengeDashedName : + ''; +} - static displayName = 'Nav' - static propTypes = { +export default React.createClass({ + displayName: 'Nav', + + propTypes: { points: PropTypes.number, picture: PropTypes.string, signedIn: PropTypes.bool, username: PropTypes.string - } + }, + + renderLinks() { + return navLinks.map(({ content, link, react, target }, index) => { + if (react) { + return ( + + + { content } + + + ); + } + return ( + + { content } + + ); + }); + }, + + renderLearnBtn() { + return ( + { + const challengeDashedName = getDashedName(); + const goTo = challengeDashedName ? + '/challenges/' + challengeDashedName : + '/map'; + win.location = goTo; + }}> + Learn + + ); + }, renderPoints(username, points) { if (!username) { return null; } return ( - [ { points } ] - + ); - } + }, renderSignin(username, picture) { if (username) { @@ -80,35 +121,36 @@ export default class extends React.Component { } else { return ( Sign In ); } - } + }, render() { const { username, points, picture } = this.props; + return ( - + fixedTop={ true }> + { logoElement } + + - + ); } -} +}); diff --git a/common/app/components/Nav/NavItem.jsx b/common/app/components/Nav/NavItem.jsx index eec245e7d8..4e4d084dcc 100644 --- a/common/app/components/Nav/NavItem.jsx +++ b/common/app/components/Nav/NavItem.jsx @@ -1,14 +1,14 @@ import React from 'react'; import classNames from 'classnames'; -import BootstrapMixin from 'react-bootstrap/lib/BootstrapMixin'; export default React.createClass({ displayName: 'FCCNavItem', - mixins: [BootstrapMixin], propTypes: { active: React.PropTypes.bool, 'aria-controls': React.PropTypes.string, + children: React.PropTypes.node, + className: React.PropTypes.string, disabled: React.PropTypes.bool, eventKey: React.PropTypes.any, href: React.PropTypes.string, @@ -30,7 +30,11 @@ export default React.createClass({ e.preventDefault(); if (!this.props.disabled) { - this.props.onSelect(this.props.eventKey, this.props.href, this.props.target); + this.props.onSelect( + this.props.eventKey, + this.props.href, + this.props.target + ); } } }, @@ -50,10 +54,11 @@ export default React.createClass({ ...props } = this.props; - let classes = { - active, - disabled - }; + const linkClassName = classNames(className, { + // 'active': active, we don't actually use the active class + // but it is used for a11y below + 'disabled': disabled + }); let linkProps = { role, @@ -75,9 +80,9 @@ export default React.createClass({ role='presentation'> + aria-selected={ active } + className={ linkClassName }> { children } diff --git a/common/app/components/Nav/links.json b/common/app/components/Nav/links.json index 4baea28e78..4adaa200fb 100644 --- a/common/app/components/Nav/links.json +++ b/common/app/components/Nav/links.json @@ -3,11 +3,21 @@ "link": "/map" }, { "content": "Chat", - "link": "//gitter.im/FreeCodeCamp/FreeCodeCamp" + "link": "//gitter.im/FreeCodeCamp/FreeCodeCamp", + "target": "_blank" },{ "content": "News", - "link": "/news" + "link": "/news", + "target": "_blank" +},{ + "content": "Wiki", + "link": "https://github.com/freecodecamp/freecodecamp/wiki/", + "target": "_blank" },{ "content": "Jobs", - "link": "/jobs" + "link": "/jobs", + "react": true +},{ + "content": "Links", + "link": "/links" }] diff --git a/common/app/components/NotFound/index.js b/common/app/components/NotFound/index.js deleted file mode 100644 index 8206b46115..0000000000 --- a/common/app/components/NotFound/index.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -export default class extends React.Component { - constructor(props) { - super(props); - } - static displayName = 'NotFound' - static propTypes = {} - componentDidMount() { - } - render() { - return null; - } -} diff --git a/common/app/components/NotFound/index.jsx b/common/app/components/NotFound/index.jsx new file mode 100644 index 0000000000..eb634a2f0d --- /dev/null +++ b/common/app/components/NotFound/index.jsx @@ -0,0 +1,22 @@ +import React, { PropTypes } from 'react'; + +const win = typeof window !== 'undefined' ? window : {}; + +function goToServer(path) { + win.location = '/' + path; +} + +export default React.createClass({ + displayName: 'NotFound', + propTypes: { + params: PropTypes.object + }, + componentWillMount() { + goToServer(this.props.params.splat); + }, + componentDidMount() { + }, + render() { + return ; + } +}); diff --git a/common/app/flux/Actions.js b/common/app/flux/Actions.js index 2cf852f483..52334c4876 100644 --- a/common/app/flux/Actions.js +++ b/common/app/flux/Actions.js @@ -8,15 +8,27 @@ export default Actions({ return { title: title + '| Free Code Camp' }; }, - setUser({ username, picture, progressTimestamps = [] }) { + setUser({ + username, + picture, + progressTimestamps = [], + isFrontEndCert, + isFullStackCert + }) { return { username, picture, - points: progressTimestamps.length + points: progressTimestamps.length, + isFrontEndCert, + isFullStackCert }; }, - getUser: null + getUser: null, + updateRoute(route) { + return { route }; + }, + goBack: null }) .refs({ displayName: 'AppActions' }) .init(({ instance: appActions, args: [services] }) => { diff --git a/common/app/flux/Store.js b/common/app/flux/Store.js index c745e16ac0..33741a1165 100644 --- a/common/app/flux/Store.js +++ b/common/app/flux/Store.js @@ -8,13 +8,17 @@ const initValue = { points: 0 }; -export default Store(initValue) - .refs({ displayName: 'AppStore' }) - .init(({ instance: appStore, args: [cat] }) => { - const { setUser, setTitle } = cat.getActions('appActions'); +export default Store({ + refs: { + displayName: 'AppStore', + value: initValue + }, + init({ instance: appStore, args: [cat] }) { + const { updateRoute, setUser, setTitle } = cat.getActions('appActions'); const register = createRegistrar(appStore); - register(setter(fromMany(setUser, setTitle))); + register(setter(fromMany(setUser, setTitle, updateRoute))); return appStore; - }); + } +}); diff --git a/common/app/routes/Bonfires/Actions.js b/common/app/routes/Bonfires/Actions.js deleted file mode 100644 index 1930b48890..0000000000 --- a/common/app/routes/Bonfires/Actions.js +++ /dev/null @@ -1,63 +0,0 @@ -var Action = require('thundercats').Action, - executeBonfire = require('./executeBonfire'), - getModel = require('../../utils/getModel'), - debug = require('debug')('freecc:common:bonfires'); - -var BonfireActions = Action.createActions([ - 'setUserCode', - 'testUserCode', - 'setResults', - 'setDisplay', - 'setBonfire', - 'getBonfire', - 'handleBonfireError', - 'openCompletionModal' -]); - -BonfireActions - .getBonfire - .subscribe(function(params) { - var Bonfire = getModel('bonfire'); - var bonfireName = params.bonfireName ? - params.bonfireName.replace(/\-/g, ' ') : - 'meet bonfire'; - debug('getting bonfire for: ', bonfireName); - var regQuery = { name: { like: bonfireName, options: 'i' } }; - Bonfire.find( - { where: regQuery }, - function(err, bonfire) { - if (err) { - return debug('bonfire get err', err); - } - if (!bonfire || bonfire.length < 1) { - return debug('404 no bonfire found for ', bonfireName); - } - bonfire = bonfire.pop(); - if (bonfire) { - debug( - 'found bonfire %s for route %s', - bonfire.name, - bonfireName - ); - } - BonfireActions.setBonfire(bonfire); - } - ); - }); - - -BonfireActions - .testUserCode - .subscribe(function({ userCode, tests }) { - debug('test bonfire'); - executeBonfire(userCode, tests, function(err, { output, results }) { - if (err) { - debug('error running tests', err); - return BonfireActions.setDisplay(err); - } - BonfireActions.setDisplay(output); - BonfireActions.setResults(results); - }); - }); - -module.exports = BonfireActions; diff --git a/common/app/routes/Bonfires/Store.js b/common/app/routes/Bonfires/Store.js deleted file mode 100644 index 4a83c223d6..0000000000 --- a/common/app/routes/Bonfires/Store.js +++ /dev/null @@ -1,67 +0,0 @@ -var BonfiresActions = require('./Actions'); -var { Store, setStateUtil } = require('thundercats'); - -var BonfiresStore = Store.create({ - - getInitialValue: function() { - return { - userCode: 'console.log(\'FreeCodeCamp!\')', - difficulty: 0, - description: [ - 'default state' - ], - tests: [], - results: null - }; - }, - - getOperations: function() { - var { - setBonfire, - setUserCode, - setResults, - setDisplay - } = BonfiresActions; - - return [ - setBonfire - .map(function(bonfire) { - var { - name, - description, - difficulty, - tests - } = bonfire; - var userCode = bonfire.challengeSeed; - return { - name, - userCode, - tests, - description, - difficulty - }; - }) - .map(setStateUtil), - - setUserCode - .map(function(userCode) { - return { userCode }; - }) - .map(setStateUtil), - - setDisplay - .map(function(display) { - return { display }; - }) - .map(setStateUtil), - - setResults - .map(function(results) { - return { results }; - }) - .map(setStateUtil) - ]; - } -}); - -module.exports = BonfiresStore; diff --git a/common/app/routes/Bonfires/components/Bonfires.jsx b/common/app/routes/Bonfires/components/Bonfires.jsx deleted file mode 100644 index 9da6db9320..0000000000 --- a/common/app/routes/Bonfires/components/Bonfires.jsx +++ /dev/null @@ -1,99 +0,0 @@ -var React = require('react'), - - // ## mixins - { ObservableStateMixin } = require('thundercats'), - - // ## components - SidePanel = require('./SidePanel.jsx'), - Results = require('./Results.jsx'), - Display = require('../displayCode'), - Editor = require('../editor'), - { Grid, Row, Col } = require('react-bootstrap'), - - // ## flux - BonfireActions = require('./Actions'), - BonfireStore = require('./Store'); - -var Bonfire = React.createClass({ - - mixins: [ObservableStateMixin], - - contextTypes: { - makePath: React.PropTypes.func.isRequired, - replaceWith: React.PropTypes.func.isRequired - }, - - getObservable: function() { - return BonfireStore; - }, - - componentDidMount: function() { - // get history object - var his = typeof window !== 'undefined' ? window.history : null; - // spinal-case bonfireName - var bonfireName = this.state.name.toLowerCase().replace(/\s/g, '-'); - // create proper URI from react-router - var path = this.context.makePath('bonfires', { bonfireName: bonfireName }); - - // if html5 push state exists, update URI - // else we are using hash location and should just cause a re render - if (his) { - his.replaceState({ path: path }, '', path); - } else { - this.context.replaceWith('bonfires', { bonfireName: bonfireName}); - } - }, - - _onTestBonfire: function() { - BonfireActions.testUserCode({ - userCode: this.state.userCode, - tests: this.state.tests - }); - }, - - render: function() { - var { - name, - userCode, - difficulty, - description, - results, - display - } = this.state; - var brief = description.slice(0, 1).pop(); - - // convert bonfire difficulty from floating point string - // to integer. - var difficultyInt = Math.floor(+difficulty); - - return ( - - - - 1 ? description : [] }/> - - - - - - - - - ); - } -}); - -module.exports = Bonfire; diff --git a/common/app/routes/Bonfires/components/Results.jsx b/common/app/routes/Bonfires/components/Results.jsx deleted file mode 100644 index 8e0e11b449..0000000000 --- a/common/app/routes/Bonfires/components/Results.jsx +++ /dev/null @@ -1,62 +0,0 @@ -var React = require('react'), - classNames = require('classnames'), - { Grid, Row, Col } = require('react-bootstrap'); - -var Results = React.createClass({ - - propTypes: { - results: React.PropTypes.array - }, - - _renderText: function(text, textClass) { - return ( - - { text } - - ); - }, - - _renderResult: function(results) { - return results.map(function(result, idx) { - var err = result.err; - var iconClass = { - 'ion-close-circled big-error-icon': err, - 'ion-checkmark-circled big-success-icon': !err - }; - var textClass = { - 'test-output wrappable': true, - 'test-vertical-center': !err - }; - return ( -
- - - - - { this._renderText(result.text, textClass) } - { err ? this._renderText(err, textClass) : null } - -
-
- ); - }.bind(this)); - }, - - render: function() { - var results = this.props.results; - if (!results || results.length && results.length === 0) { - return null; - } - return ( - - { this._renderResult(this.props.results) } - - ); - } -}); - -module.exports = Results; diff --git a/common/app/routes/Bonfires/components/SidePanel.jsx b/common/app/routes/Bonfires/components/SidePanel.jsx deleted file mode 100644 index 078e9fe72b..0000000000 --- a/common/app/routes/Bonfires/components/SidePanel.jsx +++ /dev/null @@ -1,129 +0,0 @@ -var React = require('react'), - - // ## components - { - Well, - Row, - Col, - Button, - } = require('react-bootstrap'); - -var SidePanel = React.createClass({ - - propTypes: { - name: React.PropTypes.string, - brief: React.PropTypes.string, - description: React.PropTypes.array, - difficulty: React.PropTypes.number, - onTestBonfire: React.PropTypes.func - }, - - getDefaultProps: function() { - return { - name: 'Welcome to Bonfires!', - difficulty: 5, - brief: 'This is a brief description' - }; - }, - - getInitialState: function() { - return { - isMoreInfoOpen: false - }; - }, - - _toggleMoreInfo: function() { - this.setState({ - isMoreInfoOpen: !this.state.isMoreInfoOpen - }); - }, - - _renderFlames: function() { - var difficulty = this.props.difficulty; - - return [1, 2, 3, 4, 5].map(num => { - var className = 'ion-ios-flame'; - if (num > difficulty) { - className += '-outline'; - } - return ( - - ); - }); - }, - - _renderMoreInfo: function(isDescription) { - var description = this.props.description.map((sentance, index) => { - return

{ sentance }

; - }); - - if (isDescription && this.state.isMoreInfoOpen) { - return ( - - - { description } - - - ); - } - return null; - }, - - _renderMoreInfoButton: function(isDescription) { - if (isDescription) { - return ( - - ); - } - return null; - }, - - render: function() { - var isDescription = this.props.description && - this.props.description.length > 1; - - return ( -
-

{ this.props.name }

-

-
- Difficulty:  - { this._renderFlames() } -
-

- - - -
-

{ this.props.brief }

-
- { this._renderMoreInfo(isDescription) } - { this._renderMoreInfoButton(isDescription) } -
-
- -
-
- -
-
- ); - } -}); - -module.exports = SidePanel; diff --git a/common/app/routes/Bonfires/executeBonfire.js b/common/app/routes/Bonfires/executeBonfire.js deleted file mode 100644 index 0d1795894a..0000000000 --- a/common/app/routes/Bonfires/executeBonfire.js +++ /dev/null @@ -1,27 +0,0 @@ -var debug = require('debug')('freecc:executebonfire'); -var { - addTests, - runTests, - testCode -} = require('../../utils'); - -module.exports = executeBonfire; - -function executeBonfire(userCode, tests, cb) { - - // TODO: move this into componentDidMount - // ga('send', 'event', 'Bonfire', 'ran-code', bonfireName); - var testSalt = Math.random(); - var { preppedCode, userTests } = addTests(userCode, tests, testSalt); - - debug('sending code to web worker for testing'); - testCode(preppedCode, function(err, data) { - if (err) { return cb(err); } - var results = runTests(userTests, data, testSalt); - debug('testing complete', results); - cb(null, { - output: data.output, - results - }); - }); -} diff --git a/common/app/routes/Bonfires/index.js b/common/app/routes/Bonfires/index.js deleted file mode 100644 index edcb26c104..0000000000 --- a/common/app/routes/Bonfires/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - path: 'bonfires/(:bonfireName)', - getComponents(cb) { - // TODO(berks): add bonfire component - } -}; diff --git a/common/app/routes/Hikes/components/Lecture.jsx b/common/app/routes/Hikes/components/Lecture.jsx index ea3c85191d..e47dc0fc98 100644 --- a/common/app/routes/Hikes/components/Lecture.jsx +++ b/common/app/routes/Hikes/components/Lecture.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Button, Col, Row, Panel } from 'react-bootstrap'; -import { Navigation } from 'react-router'; +import { History } from 'react-router'; import Vimeo from 'react-vimeo'; import debugFactory from 'debug'; @@ -8,7 +8,7 @@ const debug = debugFactory('freecc:hikes'); export default React.createClass({ displayName: 'Lecture', - mixins: [Navigation], + mixins: [History], propTypes: { currentHike: PropTypes.object, @@ -20,7 +20,7 @@ export default React.createClass({ handleFinish() { debug('loading questions'); const { dashedName } = this.props.params; - this.transitionTo(`/hikes/${dashedName}/questions/1`); + this.history.pushState(null, `/hikes/${dashedName}/questions/1`); }, renderTranscript(transcript, dashedName) { diff --git a/common/app/routes/Hikes/components/Question.jsx b/common/app/routes/Hikes/components/Question.jsx index e7fe7662d9..4f7e97f149 100644 --- a/common/app/routes/Hikes/components/Question.jsx +++ b/common/app/routes/Hikes/components/Question.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; import { Spring } from 'react-motion'; -import { Navigation, TransitionHook } from 'react-router'; +import { History, Lifecycle } from 'react-router'; import debugFactory from 'debug'; import { Button, @@ -19,8 +19,8 @@ export default React.createClass({ displayName: 'Question', mixins: [ - Navigation, - TransitionHook + History, + Lifecycle ], propTypes: { @@ -150,7 +150,8 @@ export default React.createClass({ postJSON$('/completed-challenge', { id, name }).subscribeOnCompleted(() => { if (tests[nextQuestionIndex]) { - return this.transitionTo( + return this.history.pushState( + null, `/hikes/${ dashedName }/questions/${ nextQuestionIndex + 1 }` ); } @@ -168,13 +169,13 @@ export default React.createClass({ }, null); if (nextHike) { - return this.transitionTo(`/hikes/${ nextHike.dashedName }`); + return this.history.pushState(null, `/hikes/${ nextHike.dashedName }`); } debug( 'next Hike was not found, currentHike %s', currentHike.dashedName ); - this.transitionTo('/hikes'); + this.history.pushState(null, '/hikes'); }); }, diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index 2d9d44a4fd..f6ed407fae 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -3,7 +3,7 @@ import debugFactory from 'debug'; const debug = debugFactory('freecc:hikes:actions'); -function getCurrentHike(hikes =[{}], dashedName, currentHike) { +function getCurrentHike(hikes = [{}], dashedName, currentHike) { if (!dashedName) { debug('no dashedName'); return hikes[0]; diff --git a/common/app/routes/Hikes/flux/Store.js b/common/app/routes/Hikes/flux/Store.js index 4c7080b1c2..9755ed679a 100644 --- a/common/app/routes/Hikes/flux/Store.js +++ b/common/app/routes/Hikes/flux/Store.js @@ -5,12 +5,16 @@ const initialValue = { currentHike: {} }; -export default Store(initialValue) - .refs({ displayName: 'HikesStore'}) - .init(({ instance: hikeStore, args: [cat] }) => { +export default Store({ + refs: { + displayName: 'HikesStore', + value: initialValue + }, + init({ instance: hikeStore, args: [cat] }) { let { setHikes } = cat.getActions('hikesActions'); hikeStore.register(setHikes); return hikeStore; - }); + } +}); diff --git a/common/app/routes/Jobs/components/GoToPayPal.jsx b/common/app/routes/Jobs/components/GoToPayPal.jsx new file mode 100644 index 0000000000..8b2710b2a8 --- /dev/null +++ b/common/app/routes/Jobs/components/GoToPayPal.jsx @@ -0,0 +1,272 @@ +import React, { PropTypes } from 'react'; +import { Button, Input, Col, Panel, Row, Well } from 'react-bootstrap'; +import { contain } from 'thundercats-react'; + +// real paypal buttons +// will take your money +const paypalIds = { + regular: 'Q8Z82ZLAX3Q8N', + highlighted: 'VC8QPSKCYMZLN' +}; + +export default contain( + { + store: 'JobsStore', + actions: [ + 'jobActions', + 'appActions' + ], + map({ + job: { id, isHighlighted } = {}, + buttonId = isHighlighted ? + paypalIds.highlighted : + paypalIds.regular, + price = 1000, + discountAmount = 0, + promoCode = '', + promoApplied = false, + promoName + }) { + return { + id, + isHighlighted, + buttonId, + price, + discountAmount, + promoName, + promoCode, + promoApplied + }; + } + }, + React.createClass({ + displayName: 'GoToPayPal', + + propTypes: { + appActions: PropTypes.object, + id: PropTypes.string, + isHighlighted: PropTypes.bool, + buttonId: PropTypes.string, + price: PropTypes.number, + discountAmount: PropTypes.number, + promoName: PropTypes.string, + promoCode: PropTypes.string, + promoApplied: PropTypes.bool, + jobActions: PropTypes.object + }, + + goToJobBoard() { + const { appActions } = this.props; + appActions.updateRoute('/jobs'); + }, + + renderDiscount(discountAmount) { + if (!discountAmount) { + return null; + } + return ( + + +

Promo Discount

+ + +

-{ discountAmount }

+ +
+ ); + }, + + renderHighlightPrice(isHighlighted) { + if (!isHighlighted) { + return null; + } + return ( + + +

Highlighting

+ + +

+ 250

+ +
+ ); + }, + + renderPromo() { + const { + id, + promoApplied, + promoCode, + promoName, + isHighlighted, + jobActions + } = this.props; + if (promoApplied) { + return ( +
+
+ + + { promoName } applied + + +
+ ); + } + return ( +
+
+ + + Have a promo code? + + + + + + + + + + +
+ ); + }, + + render() { + const { + id, + isHighlighted, + buttonId, + price, + discountAmount + } = this.props; + + return ( +
+ + + + + +

+ One more step +

+
+ You're Awesome! just one more step to go. + Clicking on the link below will redirect to paypal. + + +
+ + + +

Job Posting

+ + +

+ { price }

+ +
+ { this.renderHighlightPrice(isHighlighted) } + { this.renderDiscount(discountAmount) } + + +

Total

+ + +

${ + price - discountAmount + (isHighlighted ? 250 : 0) + }

+ +
+
+ { this.renderPromo() } +
+ + +
+ + + + +
+ An array of credit cards + + + +
+ + + +
+ ); + } + }) +); diff --git a/common/app/routes/Jobs/components/JobNotFound.jsx b/common/app/routes/Jobs/components/JobNotFound.jsx new file mode 100644 index 0000000000..c4981d22f8 --- /dev/null +++ b/common/app/routes/Jobs/components/JobNotFound.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import { LinkContainer } from 'react-router-bootstrap'; +import { Button, Row, Col, Panel } from 'react-bootstrap'; + +export default React.createClass({ + displayName: 'NoJobFound', + + render() { + return ( +
+ + + + No job found... + + + + + + +
+ ); + } +}); diff --git a/common/app/routes/Jobs/components/Jobs.jsx b/common/app/routes/Jobs/components/Jobs.jsx index a4d8354b3f..6b9e387134 100644 --- a/common/app/routes/Jobs/components/Jobs.jsx +++ b/common/app/routes/Jobs/components/Jobs.jsx @@ -1,36 +1,42 @@ import React, { cloneElement, PropTypes } from 'react'; import { contain } from 'thundercats-react'; -import { History } from 'react-router'; -import { Button, Jumbotron, Row } from 'react-bootstrap'; +import { Button, Panel, Row, Col } from 'react-bootstrap'; -import CreateJobModal from './CreateJobModal.jsx'; import ListJobs from './List.jsx'; export default contain( { store: 'jobsStore', fetchAction: 'jobActions.getJobs', - actions: 'jobActions' + actions: [ + 'appActions', + 'jobActions' + ] }, React.createClass({ displayName: 'Jobs', - mixins: [History], - propTypes: { children: PropTypes.element, + numOfFollowers: PropTypes.number, + appActions: PropTypes.object, jobActions: PropTypes.object, jobs: PropTypes.array, showModal: PropTypes.bool }, - handleJobClick(id) { + componentDidMount() { const { jobActions } = this.props; + jobActions.getFollowers(); + }, + + handleJobClick(id) { + const { appActions, jobActions } = this.props; if (!id) { return null; } jobActions.findJob(id); - this.history.pushState(null, `/jobs/${id}`); + appActions.updateRoute(`/jobs/${id}`); }, renderList(handleJobClick, jobs) { @@ -55,36 +61,73 @@ export default contain( const { children, jobs, - showModal, - jobActions + appActions } = this.props; return ( -
+ - -

Free Code Camps' Job Board

-

- Need to find the best junior developers? - Want to find dedicated developers eager to join your company? - Sign up now to post your job! -

- -
+ +

+ Hire a JavaScript engineer who's experienced in HTML5, + Node.js, MongoDB, and Agile Development. +

+
+ + + +
+ + +
+ + + {` + + +
+

+ We hired our last developer out of Free Code Camp + and couldn't be happier. Free Code Camp is now + our go-to way to bring on pre-screened candidates + who are enthusiastic about learning quickly and + becoming immediately productive in their new career. +

+
+ Michael Gai, CEO at CoNarrative +
+
+ +
+ + { this.renderChild(children, jobs) || + this.renderList(this.handleJobClick, jobs) } + + - - { this.renderChild(children, jobs) || - this.renderList(this.handleJobClick, jobs) } - - -
+ ); } }) diff --git a/common/app/routes/Jobs/components/List.jsx b/common/app/routes/Jobs/components/List.jsx index 2457bcb7f0..4edc4cd5ad 100644 --- a/common/app/routes/Jobs/components/List.jsx +++ b/common/app/routes/Jobs/components/List.jsx @@ -1,6 +1,6 @@ import React, { PropTypes } from 'react'; -import { PanelGroup, Thumbnail, Panel, Well } from 'react-bootstrap'; -import moment from 'moment'; +import classnames from 'classnames'; +import { ListGroup, ListGroupItem } from 'react-bootstrap'; export default React.createClass({ displayName: 'ListJobs', @@ -10,64 +10,58 @@ export default React.createClass({ jobs: PropTypes.array }, - renderJobs(handleClick, jobs =[]) { - const thumbnailStyle = { - backgroundColor: 'white', - maxHeight: '100px', - maxWidth: '100px' - }; + addLocation(locale) { + if (!locale) { + return null; + } + return ( + + { locale } + + ); + }, - return jobs.map(( - { + renderJobs(handleClick, jobs = []) { + return jobs + .filter(({ isPaid, isApproved, isFilled }) => { + return isPaid && isApproved && !isFilled; + }) + .map(({ id, company, position, isHighlighted, - description, - logo, - city, - state, - email, - phone, - postedOn - }, - index - ) => { - const header = ( -
-

{ company }

-
- { position } -
-
- ); - return ( - - - - - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } -
-

handleClick(id) }>{ description }

-
-
- ); - }); + locale + }) => { + + const className = classnames({ + 'jobs-list': true, + 'col-xs-12': true, + 'jobs-list-highlight': isHighlighted + }); + + return ( + handleClick(id) }> +
+

+ { company } + {' '} + + - { position } + +

+

+ { this.addLocation(locale) } +

+
+
+ ); + }); }, render() { @@ -77,9 +71,9 @@ export default React.createClass({ } = this.props; return ( - + { this.renderJobs(handleClick, jobs) } - + ); } }); diff --git a/common/app/routes/Jobs/components/NewJob.jsx b/common/app/routes/Jobs/components/NewJob.jsx index 4c369411f0..c482c77094 100644 --- a/common/app/routes/Jobs/components/NewJob.jsx +++ b/common/app/routes/Jobs/components/NewJob.jsx @@ -1,7 +1,11 @@ +import { helpers } from 'rx'; import React, { PropTypes } from 'react'; import { History } from 'react-router'; import { contain } from 'thundercats-react'; import debugFactory from 'debug'; +import dedent from 'dedent'; +import normalizeUrl from 'normalize-url'; + import { getDefaults } from '../utils'; import { @@ -14,13 +18,13 @@ import { Col, Input, Row, + Panel, Well } from 'react-bootstrap'; import { isAscii, isEmail, - isMobilePhone, isURL } from 'validator'; @@ -31,12 +35,32 @@ const checkValidity = [ 'locale', 'description', 'email', - 'phone', 'url', 'logo', - 'name', - 'highlight' + 'company', + 'isHighlighted', + 'howToApply' ]; +const hightlightCopy = ` +Highlight my post to make it stand out. (+$250) +`; + + +const isRemoteCopy = ` +This job can be performed remotely. +`; + +const howToApplyCopy = dedent` + Examples: click here to apply yourcompany.com/jobs/33 + Or email jobs@yourcompany.com +`; + +const checkboxClass = dedent` + text-left + jobs-checkbox-spacer + col-sm-offset-2 + col-sm-6 col-md-offset-3 +`; function formatValue(value, validator, type = 'string') { const formated = getDefaults(type); @@ -50,12 +74,32 @@ function formatValue(value, validator, type = 'string') { return formated; } +const normalizeOptions = { + stripWWW: false +}; + +function formatUrl(url, shouldKeepTrailingSlash = true) { + if ( + typeof url === 'string' && + url.length > 4 && + url.indexOf('.') !== -1 + ) { + // prevent trailing / from being stripped during typing + let lastChar = ''; + if (shouldKeepTrailingSlash && url.substring(url.length - 1) === '/') { + lastChar = '/'; + } + return normalizeUrl(url, normalizeOptions) + lastChar; + } + return url; +} + function isValidURL(data) { return isURL(data, { 'require_protocol': true }); } -function isValidPhone(data) { - return isMobilePhone(data, 'en-US'); +function makeRequired(validator) { + return (val) => !!val && validator(val); } export default contain({ @@ -67,22 +111,28 @@ export default contain({ locale, description, email, - phone, url, logo, - name, - highlight + company, + isFrontEndCert = true, + isFullStackCert, + isHighlighted, + isRemoteOk, + howToApply } = form; return { - position: formatValue(position, isAscii), - locale: formatValue(locale, isAscii), - description: formatValue(description, isAscii), - email: formatValue(email, isEmail), - phone: formatValue(phone, isValidPhone), - url: formatValue(url, isValidURL), - logo: formatValue(logo, isValidURL), - name: formatValue(name, isAscii), - highlight: formatValue(highlight, null, 'bool') + position: formatValue(position, makeRequired(isAscii)), + locale: formatValue(locale, makeRequired(isAscii)), + description: formatValue(description, makeRequired(helpers.identity)), + email: formatValue(email, makeRequired(isEmail)), + url: formatValue(formatUrl(url), isValidURL), + logo: formatValue(formatUrl(logo), isValidURL), + company: formatValue(company, makeRequired(isAscii)), + isHighlighted: formatValue(isHighlighted, null, 'bool'), + isRemoteOk: formatValue(isRemoteOk, null, 'bool'), + howToApply: formatValue(howToApply, makeRequired(isAscii)), + isFrontEndCert, + isFullStackCert }; }, subscribeOnWillMount() { @@ -98,55 +148,66 @@ export default contain({ locale: PropTypes.object, description: PropTypes.object, email: PropTypes.object, - phone: PropTypes.object, url: PropTypes.object, logo: PropTypes.object, - name: PropTypes.object, - highlight: PropTypes.object + company: PropTypes.object, + isHighlighted: PropTypes.object, + isRemoteOk: PropTypes.object, + isFrontEndCert: PropTypes.bool, + isFullStackCert: PropTypes.bool, + howToApply: PropTypes.object }, mixins: [History], handleSubmit(e) { e.preventDefault(); - const props = this.props; + const pros = this.props; let valid = true; checkValidity.forEach((prop) => { // if value exist, check if it is valid - if (props[prop].value && props[prop].type !== 'boolean') { - valid = valid && !!props[prop].valid; + if (pros[prop].value && pros[prop].type !== 'boolean') { + valid = valid && !!pros[prop].valid; } }); - if (!valid) { + if (!valid || !pros.isFrontEndCert && !pros.isFullStackCert ) { debug('form not valid'); return; } const { + jobActions, + + // form values position, locale, description, email, - phone, url, logo, - name, - highlight, - jobActions + company, + isFrontEndCert, + isFullStackCert, + isHighlighted, + isRemoteOk, + howToApply } = this.props; // sanitize user output const jobValues = { position: inHTMLData(position.value), - location: inHTMLData(locale.value), + locale: inHTMLData(locale.value), description: inHTMLData(description.value), email: inHTMLData(email.value), - phone: inHTMLData(phone.value), - url: uriInSingleQuotedAttr(url.value), - logo: uriInSingleQuotedAttr(logo.value), - name: inHTMLData(name.value), - highlight: !!highlight.value + url: formatUrl(uriInSingleQuotedAttr(url.value), false), + logo: formatUrl(uriInSingleQuotedAttr(logo.value), false), + company: inHTMLData(company.value), + isHighlighted: !!isHighlighted.value, + isRemoteOk: !!isRemoteOk.value, + howToApply: inHTMLData(howToApply.value), + isFrontEndCert, + isFullStackCert }; const job = Object.keys(jobValues).reduce((accu, prop) => { @@ -173,19 +234,35 @@ export default contain({ handleForm({ [name]: value }); }, + handleCertClick(name) { + const { jobActions: { handleForm } } = this.props; + const otherButton = name === 'isFrontEndCert' ? + 'isFullStackCert' : + 'isFrontEndCert'; + + handleForm({ + [name]: true, + [otherButton]: false + }); + }, + render() { const { position, locale, description, email, - phone, url, logo, - name, - highlight, + company, + isHighlighted, + isRemoteOk, + howToApply, + isFrontEndCert, + isFullStackCert, jobActions: { handleForm } } = this.props; + const { handleChange } = this; const labelClass = 'col-sm-offset-1 col-sm-2'; const inputClass = 'col-sm-6'; @@ -193,22 +270,71 @@ export default contain({ return (
- - -

Create Your Job Post

+ +
-

Job Information

+

First, select your ideal applicant:

+
+ + + + + + +
+ + + + + +
+

Tell us about the position

handleChange('position', e) } - placeholder='Position' + placeholder={ + 'e.g. Full Stack Developer, Front End Developer, etc.' + } + required={ true } type='text' value={ position.value } wrapperClassName={ inputClass } /> @@ -217,7 +343,8 @@ export default contain({ label='Location' labelClassName={ labelClass } onChange={ (e) => handleChange('locale', e) } - placeholder='Location' + placeholder='e.g. San Francisco, Remote, etc.' + required={ true } type='text' value={ locale.value } wrapperClassName={ inputClass } /> @@ -226,48 +353,67 @@ export default contain({ label='Description' labelClassName={ labelClass } onChange={ (e) => handleChange('description', e) } - placeholder='Description' + required={ true } rows='10' type='textarea' value={ description.value } wrapperClassName={ inputClass } /> + handleForm({ + isRemoteOk: !!checked + }) + } + type='checkbox' + wrapperClassName={ checkboxClass } /> +
+ +
+

How should they apply?

+
+ handleChange('howToApply', e) } + placeholder={ howToApplyCopy } + required={ true } + rows='2' + type='textarea' + value={ howToApply.value } + wrapperClassName={ inputClass } /> +
-
-

Company Information

+
+
+

Tell us about your organization

handleChange('name', e) } - placeholder='Foo, INC' + onChange={ (e) => handleChange('company', e) } type='text' - value={ name.value } + value={ company.value } wrapperClassName={ inputClass } /> handleChange('email', e) } - placeholder='Email' + placeholder='This is how we will contact you' + required={ true } type='email' value={ email.value } wrapperClassName={ inputClass } /> - handleChange('phone', e) } - placeholder='555-123-1234' - type='tel' - value={ phone.value } - wrapperClassName={ inputClass } /> handleChange('url', e) } - placeholder='http://freecatphotoapp.com' + placeholder='http://yourcompany.com' type='url' value={ url.value } wrapperClassName={ inputClass } /> @@ -276,27 +422,48 @@ export default contain({ label='Logo' labelClassName={ labelClass } onChange={ (e) => handleChange('logo', e) } - placeholder='http://freecatphotoapp.com/logo.png' + placeholder='http://yourcompany.com/logo.png' type='url' value={ logo.value } wrapperClassName={ inputClass } /> -
-

Make it stand out

-
- handleForm({ - highlight: !!checked - }) - } - type='checkbox' />
+ +
+

Make it stand out

+
+
+ + + Highlight this ad to give it extra attention. +
+ Featured listings receive more clicks and more applications. + +
+
+ + handleForm({ + isHighlighted: !!checked + }) + } + type='checkbox' + wrapperClassName={ + checkboxClass.replace('text-left', '') + } /> + + +
diff --git a/common/app/routes/Jobs/components/NewJobCompleted.jsx b/common/app/routes/Jobs/components/NewJobCompleted.jsx new file mode 100644 index 0000000000..8e4272bec3 --- /dev/null +++ b/common/app/routes/Jobs/components/NewJobCompleted.jsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { LinkContainer } from 'react-router-bootstrap'; +import { Button, Panel, Col, Row } from 'react-bootstrap'; + +export default React.createClass({ + displayName: 'NewJobCompleted', + + render() { + return ( +
+ + +

+ Your Position has Been Submitted +

+
+ + + We’ll review your listing and email you when it’s live. +
+ Thank you for listing this job with Free Code Camp. + +
+
+ + + + +
+ ); + } +}); diff --git a/common/app/routes/Jobs/components/Preview.jsx b/common/app/routes/Jobs/components/Preview.jsx index 5b6081be5c..c3cb67c170 100644 --- a/common/app/routes/Jobs/components/Preview.jsx +++ b/common/app/routes/Jobs/components/Preview.jsx @@ -1,14 +1,85 @@ -// import React, { PropTypes } from 'react'; +import React, { PropTypes } from 'react'; +import { Lifecycle } from 'react-router'; +import { Panel, Button, Row, Col } from 'react-bootstrap'; import { contain } from 'thundercats-react'; + import ShowJob from './ShowJob.jsx'; +import JobNotFound from './JobNotFound.jsx'; export default contain( { store: 'JobsStore', - actions: 'JobActions', + actions: [ + 'appActions', + 'jobActions' + ], map({ form: job = {} }) { return { job }; } }, - ShowJob + React.createClass({ + displayName: 'Preview', + + propTypes: { + appActions: PropTypes.object, + job: PropTypes.object, + jobActions: PropTypes.object + }, + + mixins: [Lifecycle], + + componentDidMount() { + const { appActions, job } = this.props; + // redirect user in client + if (!job || !job.position || !job.description) { + appActions.updateRoute('/jobs/new'); + } + }, + + routerWillLeave() { + const { jobActions } = this.props; + jobActions.clearPromo(); + }, + + render() { + const { appActions, job, jobActions } = this.props; + + if (!job || !job.position || !job.description) { + return ; + } + + return ( +
+ + + + + + + + + +
+ ); + } + }) ); diff --git a/common/app/routes/Jobs/components/Show.jsx b/common/app/routes/Jobs/components/Show.jsx index ce2512c27d..85034e3242 100644 --- a/common/app/routes/Jobs/components/Show.jsx +++ b/common/app/routes/Jobs/components/Show.jsx @@ -1,12 +1,71 @@ +import React, { PropTypes } from 'react'; +import { History } from 'react-router'; import { contain } from 'thundercats-react'; + import ShowJob from './ShowJob.jsx'; +import JobNotFound from './JobNotFound.jsx'; +import { isJobValid } from '../utils'; + +function shouldShowApply( + { + isFrontEndCert: isFrontEndCertReq = false, + isFullStackCert: isFullStackCertReq = false + }, { + isFrontEndCert = false, + isFullStackCert = false + } +) { + return (!isFrontEndCertReq && !isFullStackCertReq) || + (isFullStackCertReq && isFullStackCert) || + (isFrontEndCertReq && isFrontEndCert); +} + +function generateMessage( + { + isFrontEndCert: isFrontEndCertReq = false, + isFullStackCert: isFullStackCertReq = false + }, + { + isFrontEndCert = false, + isFullStackCert = false, + isSignedIn = false + } +) { + + if (!isSignedIn) { + return 'Must be signed in to apply'; + } + if (isFrontEndCertReq && !isFrontEndCert) { + return 'This employer requires Free Code Camp’s Front ' + + 'End Development Certification in order to apply'; + } + if (isFullStackCertReq && !isFullStackCert) { + return 'This employer requires Free Code Camp’s Full ' + + 'Stack Development Certification in order to apply'; + } + if (isFrontEndCertReq && isFrontEndCertReq) { + return 'This employer requires the Front End Development Certification. ' + + "You've earned it, so feel free to apply."; + } + return 'This employer requires the Full Stack Development Certification. ' + + "You've earned it, so feel free to apply."; +} export default contain( { - store: 'jobsStore', + stores: ['appStore', 'jobsStore'], + fetchWaitFor: 'jobsStore', fetchAction: 'jobActions.getJob', - map({ currentJob }) { - return { job: currentJob }; + combineLatest( + { username, isFrontEndCert, isFullStackCert }, + { currentJob } + ) { + return { + username, + job: currentJob, + isFrontEndCert, + isFullStackCert + }; }, getPayload({ params: { id }, job = {} }) { return { @@ -20,5 +79,57 @@ export default contain( return job.id !== id; } }, - ShowJob + React.createClass({ + displayName: 'Show', + + propTypes: { + job: PropTypes.object, + isFullStackCert: PropTypes.bool, + isFrontEndCert: PropTypes.bool, + username: PropTypes.string + }, + + mixins: [History], + + componentDidMount() { + const { job } = this.props; + // redirect user in client + if (!isJobValid(job)) { + this.history.pushState(null, '/jobs'); + } + }, + + render() { + const { + isFullStackCert, + isFrontEndCert, + job, + username + } = this.props; + + if (!isJobValid(job)) { + return ; + } + + const isSignedIn = !!username; + + const showApply = shouldShowApply( + job, + { isFrontEndCert, isFullStackCert } + ); + + const message = generateMessage( + job, + { isFrontEndCert, isFullStackCert, isSignedIn } + ); + + return ( + + ); + } + }) ); diff --git a/common/app/routes/Jobs/components/ShowJob.jsx b/common/app/routes/Jobs/components/ShowJob.jsx index 1a048a3fff..db9cf511e5 100644 --- a/common/app/routes/Jobs/components/ShowJob.jsx +++ b/common/app/routes/Jobs/components/ShowJob.jsx @@ -1,6 +1,10 @@ import React, { PropTypes } from 'react'; -import { Row, Thumbnail, Panel, Well } from 'react-bootstrap'; -import moment from 'moment'; +import { Well, Row, Col, Thumbnail, Panel } from 'react-bootstrap'; +import urlRegexFactory from 'url-regex'; + +const urlRegex = urlRegexFactory(); +const defaultImage = + 'https://s3.amazonaws.com/freecodecamp/camper-image-placeholder.png'; const thumbnailStyle = { backgroundColor: 'white', @@ -8,11 +12,20 @@ const thumbnailStyle = { maxWidth: '100px' }; +function addATags(text) { + return text.replace(urlRegex, function(match) { + return `${match}`; + }); +} + export default React.createClass({ displayName: 'ShowJob', propTypes: { job: PropTypes.object, - params: PropTypes.object + params: PropTypes.object, + showApply: PropTypes.bool, + preview: PropTypes.bool, + message: PropTypes.string }, renderHeader({ company, position }) { @@ -28,38 +41,101 @@ export default React.createClass({ ); }, + renderHowToApply(showApply, preview, message, howToApply) { + if (!showApply) { + return ( + + +

{ message }

+ +
+ ); + } + + return ( + + + + { preview ? 'How do I apply?' : message } +
+
+ +
+ +
+ ); + }, + render() { - const { job = {} } = this.props; + const { + showApply = true, + message, + preview = true, + job = {} + } = this.props; + const { logo, position, city, company, state, - email, - phone, - postedOn, - description + locale, + description, + howToApply } = job; return (
- - + - Position: { position } - Location: { city }, { state } -
- Contact: { email || phone || 'N/A' } -
- Posted On: { moment(postedOn).format('MMMM Do, YYYY') } + +

+ { company } +

+
+
+ + + + + + + Position: { position || 'N/A' } +
+ Location: + { locale ? locale : `${city}, ${state}` } + +
+
+ + +

{ description }

+ +
+ { this.renderHowToApply(showApply, preview, message, howToApply) } -

{ description }

- +
); diff --git a/common/app/routes/Jobs/components/TwitterBtn.jsx b/common/app/routes/Jobs/components/TwitterBtn.jsx new file mode 100644 index 0000000000..7998353ffc --- /dev/null +++ b/common/app/routes/Jobs/components/TwitterBtn.jsx @@ -0,0 +1,33 @@ +import React, { PropTypes } from 'react'; +import { Button } from 'react-bootstrap'; + +const followLink = 'https://twitter.com/intent/follow?' + + 'ref_src=twsrc%5Etfw&region=follow_link&screen_name=CamperJobs&' + + 'amp;tw_p=followbutton'; + +function commify(count) { + return Number(count).toLocaleString('en'); +} + +export default React.createClass({ + + displayName: 'FollowButton', + + propTypes: { + count: PropTypes.number + }, + + render() { + const { count } = this.props; + return ( + + ); + } +}); diff --git a/common/app/routes/Jobs/flux/Actions.js b/common/app/routes/Jobs/flux/Actions.js index 5900e6dda1..08675048b7 100644 --- a/common/app/routes/Jobs/flux/Actions.js +++ b/common/app/routes/Jobs/flux/Actions.js @@ -1,6 +1,8 @@ import { Actions } from 'thundercats'; import store from 'store'; import debugFactory from 'debug'; +import { jsonp$ } from '../../../../utils/jsonp$'; +import { postJSON$ } from '../../../../utils/ajax-stream'; const debug = debugFactory('freecc:jobs:actions'); const assign = Object.assign; @@ -31,6 +33,7 @@ export default Actions({ }, setError: null, getJob: null, + saveJobToDb: null, getJobs(params) { return { params }; }, @@ -56,12 +59,47 @@ export default Actions({ }, saveForm: null, getSavedForm: null, + clearSavedForm: null, setForm(form) { return { form }; + }, + getFollowers: null, + setFollowersCount(numOfFollowers) { + return { numOfFollowers }; + }, + setPromoCode({ target: { value = '' }} = {}) { + return { promoCode: value.replace(/[^\d\w\s]/, '') }; + }, + applyCode: null, + clearPromo(foo, undef) { + return { + price: undef, + buttonId: undef, + discountAmount: undef, + promoCode: undef, + promoApplied: false, + promoName: undef + }; + }, + applyPromo({ + fullPrice: price, + buttonId, + discountAmount, + code: promoCode, + name: promoName + } = {}) { + return { + price, + buttonId, + discountAmount, + promoCode, + promoApplied: true, + promoName + }; } }) .refs({ displayName: 'JobActions' }) - .init(({ instance: jobActions, args: [services] }) => { + .init(({ instance: jobActions, args: [cat, services] }) => { jobActions.getJobs.subscribe(() => { services.read('jobs', null, null, (err, jobs) => { if (err) { @@ -100,5 +138,58 @@ export default Actions({ jobActions.setForm(job); } }); + + jobActions.clearSavedForm.subscribe(() => { + store.remove('newJob'); + }); + + jobActions.saveJobToDb.subscribe(({ goTo, job }) => { + const appActions = cat.getActions('appActions'); + services.create('jobs', { job }, null, (err, job) => { + if (err) { + debug('job services experienced an issue', err); + return jobActions.setError(err); + } + jobActions.setJobs({ job }); + appActions.updateRoute(goTo); + }); + }); + + jobActions.getFollowers.subscribe(() => { + const url = 'https://cdn.syndication.twimg.com/widgets/followbutton/' + + 'info.json?lang=en&screen_names=CamperJobs' + + '&callback=JSONPCallback'; + + jsonp$(url) + .map(({ response }) => { + return response[0]['followers_count']; + }) + .subscribe( + count => jobActions.setFollowersCount(count), + err => jobActions.setError(err) + ); + }); + + jobActions.applyCode.subscribe(({ id, code = '', type = null}) => { + const body = { + id, + code: code.replace(/[^\d\w\s]/, '') + }; + if (type) { + body.type = type; + } + postJSON$('/api/promos/getButton', body) + .pluck('response') + .subscribe( + ({ promo }) => { + if (promo && promo.buttonId) { + jobActions.applyPromo(promo); + } + jobActions.setError(new Error('no promo found')); + }, + jobActions.setError + ); + }); + return jobActions; - }); + }); diff --git a/common/app/routes/Jobs/flux/Store.js b/common/app/routes/Jobs/flux/Store.js index b2f5132013..b7aa5cda3d 100644 --- a/common/app/routes/Jobs/flux/Store.js +++ b/common/app/routes/Jobs/flux/Store.js @@ -6,9 +6,12 @@ const { transformer } = Store; -export default Store({ showModal: false }) - .refs({ displayName: 'JobsStore' }) - .init(({ instance: jobsStore, args: [cat] }) => { +export default Store({ + refs: { + displayName: 'JobsStore', + value: { showModal: false } + }, + init({ instance: jobsStore, args: [cat] }) { const { setJobs, findJob, @@ -16,7 +19,11 @@ export default Store({ showModal: false }) openModal, closeModal, handleForm, - setForm + setForm, + setFollowersCount, + setPromoCode, + applyPromo, + clearPromo } = cat.getActions('JobActions'); const register = createRegistrar(jobsStore); register(setter(setJobs)); @@ -24,7 +31,12 @@ export default Store({ showModal: false }) register(setter(openModal)); register(setter(closeModal)); register(setter(setForm)); + register(setter(setPromoCode)); + register(setter(applyPromo)); + register(setter(clearPromo)); + register(setter(setFollowersCount)); register(transformer(findJob)); register(handleForm); - }); + } +}); diff --git a/common/app/routes/Jobs/index.js b/common/app/routes/Jobs/index.js index 6c556c994e..9d8ee8c2dc 100644 --- a/common/app/routes/Jobs/index.js +++ b/common/app/routes/Jobs/index.js @@ -2,6 +2,8 @@ import Jobs from './components/Jobs.jsx'; import NewJob from './components/NewJob.jsx'; import Show from './components/Show.jsx'; import Preview from './components/Preview.jsx'; +import GoToPayPal from './components/GoToPayPal.jsx'; +import NewJobCompleted from './components/NewJobCompleted.jsx'; /* * index: /jobs list jobs @@ -19,6 +21,12 @@ export default { }, { path: 'jobs/new/preview', component: Preview + }, { + path: 'jobs/new/check-out', + component: GoToPayPal + }, { + path: 'jobs/new/completed', + component: NewJobCompleted }, { path: 'jobs/:id', component: Show diff --git a/common/app/routes/Jobs/utils.js b/common/app/routes/Jobs/utils.js index aeb0396c12..f2fc37d593 100644 --- a/common/app/routes/Jobs/utils.js +++ b/common/app/routes/Jobs/utils.js @@ -20,3 +20,10 @@ export function getDefaults(type, value) { } return Object.assign({}, defaults[type]); } + +export function isJobValid(job) { + return job && + !job.isFilled && + job.isApproved && + job.isPaid; +} diff --git a/common/app/routes/index.js b/common/app/routes/index.js index 1f14733f82..1dd1b704fd 100644 --- a/common/app/routes/index.js +++ b/common/app/routes/index.js @@ -1,10 +1,15 @@ import Jobs from './Jobs'; import Hikes from './Hikes'; +import NotFound from '../components/NotFound/index.jsx'; export default { path: '/', childRoutes: [ Jobs, - Hikes + Hikes, + { + path: '*', + component: NotFound + } ] }; diff --git a/common/app/shared/displayCode/Display.jsx b/common/app/shared/displayCode/Display.jsx deleted file mode 100644 index 9eb60db8da..0000000000 --- a/common/app/shared/displayCode/Display.jsx +++ /dev/null @@ -1,51 +0,0 @@ -var React = require('react'), - Tailspin = require('tailspin'); - -var Editor = React.createClass({ - - propTypes: { - value: React.PropTypes.string - }, - - getDefaultProps: function() { - return { - value: [ - '/**', - '* Your output will go here.', - '* Console.log() -type statements', - '* will appear in your browser\'s', - '* DevTools JavaScript console.', - '**/' - ].join('\n') - }; - }, - - render: function() { - var value = this.props.value; - var options = { - lineNumbers: false, - lineWrapping: true, - mode: 'text', - readOnly: 'noCursor', - textAreaClassName: 'hide-textarea', - theme: 'monokai', - value: value - }; - - var config = { - setSize: ['100%', '100%'] - }; - - return ( -
-
- -
-
- ); - } -}); - -module.exports = Editor; diff --git a/common/app/shared/displayCode/index.js b/common/app/shared/displayCode/index.js deleted file mode 100644 index d3e3560174..0000000000 --- a/common/app/shared/displayCode/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./Display.jsx'); diff --git a/common/app/shared/editor/Editor.jsx b/common/app/shared/editor/Editor.jsx deleted file mode 100644 index 8e72f5d4a0..0000000000 --- a/common/app/shared/editor/Editor.jsx +++ /dev/null @@ -1,91 +0,0 @@ -var React = require('react'), - debug = require('debug')('freecc:comp:editor'), - jshint = require('jshint').JSHINT, - Tailspin = require('tailspin'); - -var Editor = React.createClass({ - - propTypes: { - onValueChange: React.PropTypes.func, - value: React.PropTypes.string - }, - - getDefaultProps: function() { - return { - value: 'console.log(\'freeCodeCamp is awesome\')' - }; - }, - - getInitialState: function() { - return { - value: this.props.value - }; - }, - - render: function() { - var options = { - autoCloseBrackets: true, - gutters: ['CodeMirror-lint-markers'], - lint: true, - linter: jshint, - lineNumbers: true, - lineWrapping: true, - mode: 'javascript', - matchBrackets: true, - runnable: true, - scrollbarStyle: 'null', - theme: 'monokai', - textAreaClassName: 'hide-textarea', - value: this.state.value, - onChange: e => { - this.setState({ value: e.target.value}); - if (typeof this.props.onValueChange === 'function') { - this.props.onValueChange(e.target.value); - } - } - }; - - var config = { - setSize: ['100%', 'auto'], - extraKeys: { - Tab: function(cm) { - debug('tab pressed'); - if (cm.somethingSelected()) { - cm.indentSelection('add'); - } else { - var spaces = new Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Shift-Tab': function(cm) { - debug('shift-tab pressed'); - if (cm.somethingSelected()) { - cm.indentSelection('subtract'); - } else { - var spaces = new Array(cm.getOption('indentUnit') + 1).join(' '); - cm.replaceSelection(spaces); - } - }, - 'Ctrl-Enter': function() { - debug('C-enter pressed'); - // execute bonfire action - return false; - } - } - }; - - return ( -
-
-
- -
-
-
- ); - } -}); - -module.exports = Editor; diff --git a/common/app/shared/editor/index.js b/common/app/shared/editor/index.js deleted file mode 100644 index 5e431fabff..0000000000 --- a/common/app/shared/editor/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./Editor.jsx'); diff --git a/common/models/challenge.json b/common/models/challenge.json index 84cf3a4c6b..10ce49a058 100644 --- a/common/models/challenge.json +++ b/common/models/challenge.json @@ -44,6 +44,20 @@ "tests": { "type": "array" }, + "head": { + "type": "array", + "default": [], + "description": "appended to user code" + }, + "tail": { + "type": "array", + "default": [], + "description": "prepended to user code" + }, + "fileName": { + "type": "string", + "description": "filename challenge comes from. Used in dev mode" + }, "challengeSeed": { "type": "array" }, @@ -82,6 +96,13 @@ }, "descriptionPt": { "type": "array" + }, + "solutions": { + "type": "array", + "default": [] + }, + "releasedOn": { + "type": "string" } }, "validations": [], diff --git a/common/models/job.json b/common/models/job.json index f77fc6defa..04776dc5d0 100644 --- a/common/models/job.json +++ b/common/models/job.json @@ -22,7 +22,8 @@ "type": "string" }, "email": { - "type": "string" + "type": "string", + "required": true }, "phone": { "type": "string" @@ -36,24 +37,61 @@ "country": { "type": "string" }, + "locale": { + "type": "string", + "required": true, + "description": "format: city, state" + }, "location": { - "type": "geopoint" + "type": "geopoint", + "description": "location in lat, long" }, "description": { "type": "string" }, "isApproved": { - "type": "boolean" + "type": "boolean", + "default": false }, "isHighlighted": { - "type": "boolean" + "type": "boolean", + "default": false }, "isPaid": { - "type": "boolean" + "type": "boolean", + "default": false + }, + "isFilled": { + "type": "boolean", + "default": false }, "postedOn": { "type": "date", "defaultFn": "now" + }, + "isFrontEndCert": { + "type": "boolean", + "defaut": false, + "description": "Camper must be front end certified to apply" + }, + "isFullStackCert": { + "type": "boolean", + "default": false, + "description": "Camper must be full stack certified to apply" + }, + "isRemoteOk": { + "type": "boolean", + "default": false, + "description": "Camper may work remotely" + }, + "howToApply": { + "type": "string", + "required": true, + "description": "How do campers apply to job" + }, + "promoCodeUsed": { + "type": "string", + "description": "the promocode, if any, that the job uses" } }, "validations": [], diff --git a/common/models/pledge.json b/common/models/pledge.json new file mode 100644 index 0000000000..08e46df414 --- /dev/null +++ b/common/models/pledge.json @@ -0,0 +1,55 @@ +{ + "name": "pledge", + "base": "PersistedModel", + "idInjection": true, + "trackChanges": false, + "properties": { + "nonprofit": { + "type": "string", + "index": true + }, + "amount": { + "type": "number" + }, + "dateStarted": { + "type": "date", + "defaultFn": "now" + }, + "dateEnded": { + "type": "date" + }, + "formerUserId": { + "type": "string" + }, + "isOrphaned": { + "type": "boolean" + }, + "isCompleted": { + "type": "boolean", + "default": "false" + } + }, + "validations": [], + "relations": { + "user": { + "type": "hasMany", + "model": "user", + "foreignKey": "userId" + } + }, + "acls": [ + { + "accessType": "*", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "DENY" + }, + { + "accessType": "READ", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW" + } + ], + "methods": [] +} diff --git a/common/models/promo.js b/common/models/promo.js new file mode 100644 index 0000000000..9a423bdd6b --- /dev/null +++ b/common/models/promo.js @@ -0,0 +1,81 @@ +import { isAlphanumeric, isHexadecimal } from 'validator'; +import debug from 'debug'; + +const log = debug('freecc:models:promo'); + +export default function promo(Promo) { + Promo.getButton = function getButton(id, code, type = 'isNot') { + const Job = Promo.app.models.Job; + if (!id || !isHexadecimal(id)) { + return Promise.reject(new Error( + 'Must include job id' + )); + } + + if ( + !isAlphanumeric(code) && + type && + !isAlphanumeric(type) + ) { + return Promise.reject(new Error( + 'Code or Type should be an alphanumeric' + )); + } + + const query = { + where: { + and: [{ + code: type === 'isNot' ? type : 'isHighlighted' + }, + { + type: type.replace(/^\$/g, '') + }] + } + }; + + return Promo.findOne(query) + .then(function(promo) { + // turn promo model to plain js object; + promo = promo.toJSON(); + return Job.updateAll({ id: id }, { promoCodeUsed: code }) + .then(function({ count = 0 } = {}) { + log('job', count); + if (count) { + return Object.assign({}, promo, { name: `${code} Discount` }); + } + return Promise.reject(new Error( + `Job ${id} not found` + )); + }); + }); + }; + + Promo.remoteMethod( + 'getButton', + { + description: 'Get button id for promocode', + accepts: [ + { + arg: 'id', + type: 'string', + required: true + }, + { + arg: 'code', + type: 'string', + required: true + }, + { + arg: 'type', + type: 'string' + } + ], + returns: [ + { + arg: 'promo', + type: 'object' + } + ] + } + ); +} diff --git a/common/models/promo.json b/common/models/promo.json new file mode 100644 index 0000000000..8cab2c27c4 --- /dev/null +++ b/common/models/promo.json @@ -0,0 +1,59 @@ +{ + "name": "promo", + "base": "PersistedModel", + "strict": true, + "idInjection": true, + "trackChanges": false, + "properties": { + "code": { + "type": "string", + "required": true, + "description": "The code to unlock the promotional discount" + }, + "name": { + "type": "string", + "required": true, + "description": "The name of the discount" + }, + "buttonId": { + "type": "string", + "required": true, + "description": "The id of paypal button" + }, + "type": { + "type": "string", + "description": "A selector of different types of buttons for the same discount" + }, + "fullPrice": { + "type": "number", + "required": true, + "description": "The original amount" + }, + "discountAmount": { + "type": "number", + "description": "The amount of the discount if applicable" + }, + "discountPercent": { + "type": "number", + "description": "The amount of discount as a percentage if applicable" + } + }, + "validations": [], + "relations": {}, + "acls": [ + { + "accessType": "*", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "DENY" + }, + { + "accessType": "EXECUTE", + "principalType": "ROLE", + "principalId": "$everyone", + "permission": "ALLOW", + "property": "getButton" + } + ], + "methods": [] +} diff --git a/common/models/user.js b/common/models/user.js index e9dbecb0df..f2e49ae252 100644 --- a/common/models/user.js +++ b/common/models/user.js @@ -322,7 +322,7 @@ module.exports = function(User) { }) // no results means this is the first brownie point given by giver // so return -1 to indicate receiver should receive point - .firstOrDefault(null, -1) + .first({ defaultValue: -1 }) .flatMap((browniePointsFromGiver) => { if (browniePointsFromGiver === -1) { diff --git a/common/models/user.json b/common/models/user.json index 776c2c500e..f85cc1023c 100644 --- a/common/models/user.json +++ b/common/models/user.json @@ -166,6 +166,11 @@ "type": "hasMany", "model": "userIdentity", "foreignKey": "" + }, + "pledge": { + "type": "hasOne", + "model": "pledge", + "foreignKey": "" } }, "acls": [ diff --git a/common/utils/ajax-stream.js b/common/utils/ajax-stream.js index 63b55e76ce..3bdcc2abdc 100644 --- a/common/utils/ajax-stream.js +++ b/common/utils/ajax-stream.js @@ -255,6 +255,7 @@ export function postJSON$(url, body) { url, body: JSON.stringify(body), method: 'POST', + responseType: 'json', headers: { 'Content-Type': 'application/json' } }); } @@ -277,10 +278,7 @@ export function get$(url) { * @returns {Observable} The observable sequence which contains the parsed JSON */ export function getJSON$(url) { - if (!root.JSON && typeof root.JSON.parse !== 'function') { - throw new TypeError('JSON is not supported in your runtime.'); - } - return ajax$({url: url, responseType: 'json'}).map(function(x) { + return ajax$({ url: url, responseType: 'json' }).map(function(x) { return x.response; }); } diff --git a/common/utils/jsonp$.js b/common/utils/jsonp$.js new file mode 100644 index 0000000000..99dbbe2eb4 --- /dev/null +++ b/common/utils/jsonp$.js @@ -0,0 +1,77 @@ +import { AnonymousObservable, Disposable } from 'rx'; + +const root = typeof window !== 'undefined' ? window : {}; +const trash = 'document' in root && root.document.createElement('div'); + +function destroy(element) { + trash.appendChild(element); + trash.innerHTML = ''; +} + +export function jsonp$(options) { + let id = 0; + if (typeof options === 'string') { + options = { url: options }; + } + + return new AnonymousObservable(function(o) { + const settings = Object.assign( + {}, + { + jsonp: 'JSONPCallback', + async: true, + jsonpCallback: 'rxjsjsonpCallbackscallback_' + (id++).toString(36) + }, + options + ); + + let script = root.document.createElement('script'); + script.type = 'text/javascript'; + script.async = settings.async; + script.src = settings.url.replace(settings.jsonp, settings.jsonpCallback); + + root[settings.jsonpCallback] = function(data) { + root[settings.jsonpCallback].called = true; + root[settings.jsonpCallback].data = data; + }; + + const handler = function(e) { + if (e.type === 'load' && !root[settings.jsonpCallback].called) { + e = { type: 'error' }; + } + const status = e.type === 'error' ? 400 : 200; + const data = root[settings.jsonpCallback].data; + + if (status === 200) { + o.onNext({ + status: status, + responseType: 'jsonp', + response: data, + originalEvent: e + }); + + o.onCompleted(); + } else { + o.onError({ + type: 'error', + status: status, + originalEvent: e + }); + } + }; + + script.onload = script.onreadystatechanged = script.onerror = handler; + + const head = root.document.getElementsByTagName('head')[0] || + root.document.documentElement; + + head.insertBefore(script, head.firstChild); + + return Disposable.create(() => { + script.onload = script.onreadystatechanged = script.onerror = null; + + destroy(script); + script = null; + }); + }); +} diff --git a/gulpfile.js b/gulpfile.js index fd5fdda38d..b58517ee9d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -13,9 +13,15 @@ var Rx = require('rx'), reduce = require('gulp-reduce-file'), sortKeys = require('sort-keys'), debug = require('debug')('freecc:gulp'), + yargs = require('yargs'), + concat = require('gulp-concat'), + uglify = require('gulp-uglify'), + merge = require('merge-stream'), + babel = require('gulp-babel'), + sourcemaps = require('gulp-sourcemaps'), // react app - webpack = require('gulp-webpack'), + webpack = require('webpack-stream'), webpackConfig = require('./webpack.config.js'), webpackConfigNode = require('./webpack.config.node.js'), @@ -38,7 +44,7 @@ var Rx = require('rx'), Rx.config.longStackSupport = true; -var __DEV__ = process.env.NODE_ENV !== 'production'; +var __DEV__ = !yargs.argv.p; var reloadDelay = 1000; var reload = sync.reload; var paths = { @@ -68,17 +74,64 @@ var paths = { dest: 'public/js' }, + vendorChallenges: [ + 'public/bower_components/jshint/dist/jshint.js', + 'public/bower_components/chai/chai.js', + 'public/bower_components/CodeMirror/lib/codemirror.js', + 'public/bower_components/CodeMirror/addon/edit/closebrackets.js', + 'public/bower_components/CodeMirror/addon/edit/matchbrackets.js', + 'public/bower_components/CodeMirror/addon/lint/lint.js', + 'public/bower_components/CodeMirror/addon/lint/javascript-lint.js', + 'public/bower_components/CodeMirror/mode/javascript/javascript.js', + 'public/bower_components/CodeMirror/mode/xml/xml.js', + 'public/bower_components/CodeMirror/mode/css/css.js', + 'public/bower_components/CodeMirror/mode/htmlmixed/htmlmixed.js', + 'node_modules/emmet-codemirror/dist/emmet.js', + 'public/js/lib/loop-protect/loop-protect.js' + ], + + vendorMain: [ + 'public/bower_components/jquery/dist/jquery.min.js', + 'public/bower_components/bootstrap/dist/js/bootstrap.min.js', + 'public/bower_components/angular/angular.min.js', + 'public/bower_components/angular-bootstrap/ui-bootstrap.min.js', + 'public/bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js', + 'public/bower_components/d3/d3.min.js', + 'public/bower_components/moment/min/moment.min.js', + 'public/bower_components/lightbox2/dist/js/lightbox.min.js', + 'public/bower_components/rxjs/dist/rx.all.min.js' + ], + js: [ 'client/main.js', 'client/iFrameScripts.js', 'client/plugin.js' ], - dependents: [ - 'client/commonFramework.js' + commonFramework: [ + 'init', + 'bindings', + 'add-test-to-string', + 'code-storage', + 'code-uri', + 'add-loop-protect', + 'get-iframe', + 'update-preview', + 'create-editor', + 'detect-unsafe-code-stream', + 'display-test-results', + 'execute-challenge-stream', + 'output-display', + 'phone-scroll-lock', + 'report-issue', + 'run-tests-stream', + 'show-completion', + 'step-challenge', + 'end' ], less: './client/less/main.less', + lessFiles: './client/less/*.less', manifest: 'server/manifests/', @@ -92,8 +145,7 @@ var paths = { ], challenges: [ - 'seed/challenges/*.json', - 'seed/under-construction/*.json' + 'seed/challenges/*.json' ] }; @@ -101,6 +153,12 @@ var webpackOptions = { devtool: 'inline-source-map' }; +function formatCommonFrameworkPaths() { + return this.map(function(script) { + return 'client/commonFramework/' + script + '.js'; + }); +} + function errorHandler() { var args = Array.prototype.slice.call(arguments); @@ -177,7 +235,14 @@ gulp.task('sync', syncDepenedents, function() { }); gulp.task('lint-js', function() { - return gulp.src(['public/js/lib/**/*']) + return gulp.src([ + 'common/**/*.js', + 'common/**/*.jsx', + 'client/**/*.js', + 'client/**/*.jsx', + 'server/**/*.js', + 'config/**/*.js' + ]) .pipe(eslint()) .pipe(eslint.format()); }); @@ -191,6 +256,8 @@ gulp.task('lint-json', function() { gulp.task('test-challenges', ['lint-json']); gulp.task('pack-client', function() { + if (!__DEV__) { console.log('\n\nbundling production\n\n'); } + var manifestName = 'react-manifest.json'; var dest = webpackConfig.output.path; @@ -201,6 +268,7 @@ gulp.task('pack-client', function() { webpackConfig, webpackOptions ))) + .pipe(__DEV__ ? gutil.noop() : uglify()) .pipe(gulp.dest(dest)) .pipe(rev()) // copy files to public @@ -231,8 +299,12 @@ var defaultStatsOptions = { errorDetails: false }; +var webpackCalled = false; gulp.task('pack-watch', function(cb) { - var called = false; + if (webpackCalled) { + console.log('webpack watching already runnning'); + return cb(); + } gulp.src(webpackConfig.entry) .pipe(plumber({ errorHandler: errorHandler })) .pipe(webpack(Object.assign( @@ -245,9 +317,9 @@ gulp.task('pack-watch', function(cb) { gutil.log(stats.toString(defaultStatsOptions)); } - if (!called) { - debug('webpack watch completed'); - called = true; + if (!webpackCalled) { + debug('webpack init completed'); + webpackCalled = true; cb(); } @@ -255,7 +327,7 @@ gulp.task('pack-watch', function(cb) { .pipe(gulp.dest(webpackConfig.output.path)); }); -gulp.task('pack-watch-manifest', function() { +gulp.task('pack-watch-manifest', ['pack-watch'], function() { var manifestName = 'react-manifest.json'; var dest = webpackConfig.output.path; return gulp.src(dest + '/bundle.js') @@ -285,7 +357,7 @@ gulp.task('less', function() { var dest = paths.css; return gulp.src(paths.less) .pipe(plumber({ errorHandler: errorHandler })) - // copile + // compile .pipe(less({ paths: [ path.join(__dirname, 'less', 'includes') ] })) @@ -303,12 +375,50 @@ gulp.task('less', function() { .pipe(gulp.dest(paths.manifest)); }); +function getFilesGlob(files) { + if (!__DEV__) { + return files; + } + return files.map(function(file) { + return file + .replace('.min.', '.') + // moment breaks the pattern + .replace('/min/', '/'); + }); +} + gulp.task('js', function() { var manifestName = 'js-manifest.json'; var dest = paths.publicJs; - return gulp.src(paths.js) - .pipe(plumber({ errorHandler: errorHandler })) + var jsFiles = merge( + + gulp.src(getFilesGlob(paths.vendorMain)) + .pipe(__DEV__ ? sourcemaps.init() : gutil.noop()) + .pipe(concat('vendor-main.js')) + .pipe( + __DEV__ ? + sourcemaps.write({ sourceRoot: '/vendor' }) : + gutil.noop() + ), + + gulp.src(paths.vendorChallenges) + .pipe(__DEV__ ? sourcemaps.init() : gutil.noop()) + .pipe(__DEV__ ? gutil.noop() : uglify()) + .pipe(concat('vendor-challenges.js')) + .pipe( + __DEV__ ? + sourcemaps.write({ sourceRoot: '/vendor' }) : + gutil.noop() + ), + + gulp.src(paths.js) + .pipe(plumber({ errorHandler: errorHandler })) + .pipe(babel()) + .pipe(__DEV__ ? gutil.noop() : uglify()) + ); + + return jsFiles .pipe(gulp.dest(dest)) // create registry file .pipe(rev()) @@ -325,7 +435,7 @@ gulp.task('js', function() { }); // commonFramework depend on iFrameScripts -// sandbox depends on plugin +// and faux.js gulp.task('dependents', ['js'], function() { var manifestName = 'dependents-manifest.json'; var dest = paths.publicJs; @@ -334,8 +444,17 @@ gulp.task('dependents', ['js'], function() { path.join(__dirname, paths.manifest, 'js-manifest.json') ); - return gulp.src(paths.dependents) + return gulp.src(formatCommonFrameworkPaths.call(paths.commonFramework)) .pipe(plumber({ errorHandler: errorHandler })) + .pipe(babel()) + .pipe(__DEV__ ? sourcemaps.init() : gutil.noop()) + .pipe(concat('commonFramework.js')) + .pipe( + __DEV__ ? + sourcemaps.write({ sourceRoot: '/commonFramework' }) : + gutil.noop() + ) + .pipe(__DEV__ ? gutil.noop() : uglify()) .pipe(revReplace({ manifest: manifest })) .pipe(gulp.dest(dest)) .pipe(rev()) @@ -391,12 +510,20 @@ var watchDependents = [ 'pack-watch-manifest' ]; +gulp.task('reload', function() { + notify({ message: 'test changed' }); + reload(); +}); + gulp.task('watch', watchDependents, function() { - gulp.watch(paths.less, ['less']); + gulp.watch(paths.lessFiles, ['less']); gulp.watch(paths.js, ['js']); - gulp.watch(paths.challenges, ['test-challenges']); + gulp.watch(paths.challenges, ['test-challenges', 'reload']); gulp.watch(paths.js, ['js', 'dependents']); - gulp.watch(paths.dependents, ['dependents']); + gulp.watch( + formatCommonFrameworkPaths.call(paths.commonFramework), + ['dependents'] + ); gulp.watch(paths.manifest + '/*.json', ['build-manifest-watch']); gulp.watch(webpackConfig.output.path + '/bundle.js', ['pack-watch-manifest']); }); @@ -409,4 +536,3 @@ gulp.task('default', [ 'watch', 'sync' ]); - diff --git a/index.html b/index.html deleted file mode 100644 index cd3ee82b9c..0000000000 --- a/index.html +++ /dev/null @@ -1,8 +0,0 @@ - - - Free Code Camp - - - - - diff --git a/package.json b/package.json index c3f7c0bebb..01be061ba0 100644 --- a/package.json +++ b/package.json @@ -6,127 +6,141 @@ "url": "https://github.com/freecodecamp/freecodecamp.git" }, "scripts": { - "build": "gulp build", + "first-time": "npm run create-rev && echo '\n\nseeding database\n\n' && node seed && node seed/nonprofits", + "create-rev": "test ! -e server/rev-manifest.json && echo '\n\ncreating manifest\n\n' && touch server/rev-manifest.json && echo '{}' >> server/rev-manifest.json", + "build": "NODE_ENV=production gulp build -p", "start": "babel-node server/server.js", - "prestart-production": "bower cache clean && bower install && gulp build", + "prestart-production": "bower cache clean && bower install && gulp build -p", "start-production": "node pm2Start", - "lint": "eslint --ext=.js,.jsx .", - "test": "gulp test-challenges" + "lint": "npm run lint-js && npm run lint-json", + "lint-challenges": "jsonlint -q seed/challenges/*.json", + "lint-nonprofits": "jsonlint -q seed/nonprofits.json", + "lint-server": "jsonlint -q server/*.json", + "lint-resources": "jsonlint -q server/resources/*.json", + "lint-utils": "jsonlint -q server/utils/*.json", + "lint-js": "eslint --ext=.js,.jsx server/ common/ config/ client/", + "lint-json": "npm run lint-server && npm run lint-nonprofits && npm run lint-challenges && npm run lint-resources && npm run lint-utils", + "test-challenges": "babel-node seed/test-challenges.js | tnyan", + "pretest": "npm run lint", + "test": "npm run test-challenges" }, "license": "(BSD-3-Clause AND CC-BY-SA-4.0)", "dependencies": { - "accepts": "~1.2.5", - "async": "~0.9.0", - "babel": "5.8.23", - "babel-core": "5.8.23", - "babel-eslint": "4.1.1", - "babel-loader": "5.2.2", - "bcrypt-nodejs": "~0.0.3", + "accepts": "^1.3.0", + "async": "^1.5.0", + "babel": "5.8.34", + "babel-core": "5.8.34", + "babel-eslint": "^4.1.4", + "babel-loader": "5.4.0", "body-parser": "^1.13.2", - "chai-jquery": "~2.0.0", - "cheerio": "~0.18.0", + "chai-jquery": "^2.0.0", + "cheerio": "~0.19.0", "classnames": "^2.1.2", - "clockwork": "~0.1.1", - "compression": "~1.2.1", - "connect-mongo": "~0.7.0", - "cookie-parser": "~1.3.3", - "csso": "~1.3.11", - "dateformat": "~1.0.11", - "debug": "~2.1.0", - "dedent": "^0.4.0", - "dotenv": "~0.4.0", - "errorhandler": "~1.3.0", - "es6-map": "^0.1.1", - "eslint": "^1.1.0", - "eslint-plugin-react": "^3.2.1", - "express": "~4.10.4", + "compression": "^1.6.0", + "connect-mongo": "~0.8.2", + "cookie-parser": "^1.4.0", + "csso": "^1.4.1", + "dateformat": "^1.0.11", + "debug": "^2.2.0", + "dedent": "~0.6.0", + "dotenv": "^1.2.0", + "emmet-codemirror": "^1.2.5", + "errorhandler": "^1.4.2", + "es6-map": "~0.1.1", + "eslint": "~1.10.2", + "eslint-plugin-react": "^3.7.1", + "express": "^4.13.3", "express-flash": "~0.0.2", - "express-session": "~1.9.2", + "express-session": "^1.12.1", "express-state": "^1.2.0", - "express-validator": "~2.8.0", - "fetchr": "^0.5.12", - "font-awesome": "~4.3.0", - "forever": "~0.14.1", - "frameguard": "^0.2.2", - "github-api": "~0.7.0", + "express-validator": "^2.18.0", + "fetchr": "~0.5.12", + "forever": "~0.15.1", + "frameguard": "~0.2.2", + "gulp": "^3.9.0", + "gulp-babel": "^5.3.0", + "gulp-concat": "^2.6.0", + "gulp-eslint": "^1.1.0", + "gulp-inject": "^3.0.0", + "gulp-jsonlint": "^1.1.0", "gulp-less": "^3.0.3", - "gulp-minify-css": "~0.5.1", + "gulp-minify-css": "^1.2.1", + "gulp-nodemon": "^2.0.3", + "gulp-notify": "^2.2.0", + "gulp-order": "^1.1.1", + "gulp-plumber": "^1.0.1", "gulp-reduce-file": "0.0.1", "gulp-rev": "^6.0.1", - "gulp-rev-replace": "^0.4.2", + "gulp-rev-replace": "~0.4.2", + "gulp-uglify": "^1.5.1", "gulp-util": "^3.0.6", "gulp-webpack": "^1.5.0", - "helmet": "~0.9.0", - "helmet-csp": "^0.2.3", + "helmet": "~0.15.0", + "helmet-csp": "~0.3.0", "history": "^1.9.0", - "jade": "~1.8.0", - "json-loader": "^0.5.2", - "less": "~2.5.1", + "jade": "^1.11.0", + "json-loader": "~0.5.2", + "less": "^2.5.1", "lodash": "^3.9.3", - "loopback": "https://github.com/FreeCodeCamp/loopback.git#fix/no-password", - "loopback-boot": "2.8.2", - "loopback-component-passport": "https://github.com/FreeCodeCamp/loopback-component-passport.git#feature/flashfailure", + "loopback": "^2.22.0", + "loopback-boot": "^2.13.0", + "loopback-component-passport": "^1.6.0", "loopback-connector-mongodb": "^1.10.0", - "lusca": "~1.0.2", - "method-override": "~2.3.0", - "moment": "~2.10.2", + "merge-stream": "^1.0.0", + "method-override": "^2.3.0", + "moment": "^2.10.2", "mongodb": "^2.0.33", - "morgan": "~1.5.0", - "node-libs-browser": "^0.5.2", - "node-slack": "0.0.7", + "morgan": "^1.6.1", "node-uuid": "^1.4.3", - "nodemailer": "~1.3.0", - "object.assign": "^3.0.0", + "nodemailer": "^1.9.0", + "normalize-url": "^1.3.1", + "object.assign": "^4.0.3", "passport-facebook": "^2.0.0", - "passport-github": "^0.1.5", - "passport-google-oauth2": "^0.1.6", + "passport-github": "^1.0.0", + "passport-google-oauth2": "~0.1.6", "passport-linkedin-oauth2": "^1.2.1", "passport-local": "^1.0.0", "passport-oauth": "^1.0.0", "passport-twitter": "^1.0.3", - "pmx": "^0.3.16", - "ramda": "~0.10.0", - "react": "^0.13.3", - "react-bootstrap": "~0.23.7", - "react-motion": "~0.1.0", - "react-router": "^1.0.0-rc1", - "react-vimeo": "^0.0.3", - "request": "~2.53.0", + "pmx": "~0.5.5", + "ramda": "~0.18.0", + "react": "~0.14.3", + "react-bootstrap": "~0.28.1", + "react-dom": "~0.14.3", + "react-motion": "~0.3.1", + "react-router": "^1.0.0", + "react-router-bootstrap": "https://github.com/FreeCodeCamp/react-router-bootstrap.git#freecodecamp", + "react-vimeo": "~0.0.3", + "request": "^2.65.0", "rev-del": "^1.0.5", - "rx": "^2.5.3", - "sanitize-html": "~1.6.1", + "rx": "^4.0.0", + "sanitize-html": "^1.11.1", "sort-keys": "^1.1.1", - "source-map-support": "^0.3.2", "store": "https://github.com/berkeleytrue/store.js.git#feature/noop-server", - "thundercats": "^2.1.0", - "thundercats-react": "^0.1.0", - "twit": "~1.1.20", - "uglify-js": "~2.4.15", - "validator": "^3.22.1", + "thundercats": "^3.0.0", + "thundercats-react": "~0.4.0", + "twit": "^2.1.1", + "uglify-js": "^2.5.0", + "url-regex": "^3.0.0", + "validator": "^4.2.1", "webpack": "^1.9.12", + "webpack-stream": "^2.1.1", "xss-filters": "^1.2.6", - "yui": "~3.18.1" + "yargs": "^3.30.0" }, "devDependencies": { - "blessed": "~0.0.37", + "blessed": "~0.1.81", "bower-main-files": "~0.0.4", - "browser-sync": "~1.8.1", - "browserify": "^10.2.4", - "chai": "~1.10.0", + "browser-sync": "^2.9.12", + "chai": "^3.4.0", "envify": "^3.4.0", - "gulp": "~3.8.8", - "gulp-eslint": "~0.9.0", - "gulp-inject": "~1.0.2", - "gulp-jsonlint": "^1.1.0", - "gulp-nodemon": "^2.0.3", - "gulp-notify": "^2.2.0", - "gulp-plumber": "^1.0.1", - "istanbul": "^0.3.15", - "loopback-explorer": "^1.7.2", + "gulp-sourcemaps": "^1.6.0", + "istanbul": "~0.4.0", + "jsonlint": "^1.6.2", + "loopback-component-explorer": "^2.1.1", "loopback-testing": "^1.1.0", - "mocha": "~2.0.1", - "multiline": "~1.0.1", - "supertest": "~0.15.0", - "vinyl-source-stream": "^1.1.0" + "mocha": "^2.3.3", + "tap-nyan": "0.0.2", + "tape": "^4.2.2" } } diff --git a/public/css/lato.css b/public/css/lato.css index 57941353cc..149eab749f 100644 --- a/public/css/lato.css +++ b/public/css/lato.css @@ -7,3 +7,8 @@ font-family: "Lato Light"; src: url(/fonts/Lato-Light.ttf) format("truetype"); } + +@font-face { + font-family: "Lato Bold"; + src: url(/fonts/Lato-Bold.ttf) format("truetype"); +} diff --git a/public/fonts/Lato-Bold.ttf b/public/fonts/Lato-Bold.ttf new file mode 100755 index 0000000000..74343694e2 Binary files /dev/null and b/public/fonts/Lato-Bold.ttf differ diff --git a/public/js/lib/bootstrap.min.js b/public/js/lib/bootstrap.min.js deleted file mode 100644 index 1d4a4ed370..0000000000 --- a/public/js/lib/bootstrap.min.js +++ /dev/null @@ -1,6 +0,0 @@ -/*! - * Bootstrap v3.1.0 (http://getbootstrap.com) - * Copyright 2011-2014 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.isLoading=!1};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",f.resetText||d.data("resetText",d[e]()),d[e](f[b]||this.options[b]),setTimeout(a.proxy(function(){"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},b.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?a=!1:b.find(".active").removeClass("active")),a&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}a&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}if(e.hasClass("active"))return this.sliding=!1;var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});return this.$element.trigger(j),j.isDefaultPrevented()?void 0:(this.sliding=!0,f&&this.pause(),this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")?(e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(1e3*d.css("transition-duration").slice(0,-1))):(d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")),f&&this.cycle(),this)};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("collapse in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?void this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);!e&&f.toggle&&"show"==c&&(c=!c),e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(b){a(d).remove(),a(e).each(function(){var d=c(a(this)),e={relatedTarget:this};d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown",e)),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown",e))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;(e||"destroy"!=c)&&(e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]())})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(a(c).is("body")?window:c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);{var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})}},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);if(g&&b<=e[0])return g!=(a=f[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parentsUntil(this.options.target,".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=this.pinnedOffset=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(b.RESET).addClass("affix");var a=this.$window.scrollTop(),c=this.$element.offset();return this.pinnedOffset=c.top-a},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"top"==this.affixed&&(e.top+=d),"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top(this.$element)),"function"==typeof h&&(h=f.bottom(this.$element));var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;if(this.affixed!==i){this.unpin&&this.$element.css("top","");var j="affix"+(i?"-"+i:""),k=a.Event(j+".bs.affix");this.$element.trigger(k),k.isDefaultPrevented()||(this.affixed=i,this.unpin="bottom"==i?this.getPinnedOffset():null,this.$element.removeClass(b.RESET).addClass(j).trigger(a.Event(j.replace("affix","affixed"))),"bottom"==i&&this.$element.offset({top:c-h-this.$element.height()}))}}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); \ No newline at end of file diff --git a/public/js/lib/chai/chai-jquery.js b/public/js/lib/chai/chai-jquery.js deleted file mode 100644 index 52d8a72282..0000000000 --- a/public/js/lib/chai/chai-jquery.js +++ /dev/null @@ -1,231 +0,0 @@ -(function (chaiJquery) { - // Module systems magic dance. - if (typeof require === "function" && typeof exports === "object" && typeof module === "object") { - // NodeJS - module.exports = chaiJquery; - } else if (typeof define === "function" && define.amd) { - // AMD - define(['jquery'], function ($) { - return function (chai, utils) { - return chaiJquery(chai, utils, $); - }; - }); - } else { - // Other environment (usually - - - - - - - - - - -

CodeMirror Accessibility

- -
-

- CodeMirror does not currently fully support screen reader software. Since CodeMirror does not use contentEditable internally, an extra effort must be made to provide this accessibility. -

- -

Analysis of Current Functionality

-

- CodeMirror uses a hidden textarea to accept input events, track user selections, provide copy/paste, etc. This has the benefit of some native input working correctly, but since the textarea is not a mirror of the content being entered, other things do not work. -

-

- Using NVDA, a screen reader for Windows, this is the behavior seen today with version 3.15 downloaded from http://codemirror.net/: -

-
    -
  • Text based input works, as the input is being added directly into the hidden textarea
  • -
  • Selection works some of the time, as it populates the textarea with the current selection. It fails when the selection is over 100 lines or 1000 characters. In this case it replaces the character with a - and marks cm.display.inaccurateSelection for cleanup later
  • -
  • Cursor movement does not work. This is because the textarea is usually left empty, cleared out on regular intervals
  • -
  • - Entering / exiting the editor does not work. Although you can generally tab into the editor without a mouse, leaving the editor is tricky and I've had to resort to mouse usage to do this. Compare the following behavior with the normal textarea at the bottom of the page. - - The tab button's behavior is overriden as you would expect with a code editor, which makes it hard to leave the field. Perhaps a key like escape could be used to blur the editor? - - Here are some recommendataions from WAI-ARIA 1.0 Authoring Practices - Expected keyboard interaction with a rich text editor: - -
    -Optionally, if the developer wishes to provide the ability to insert a tab into the document, it is recommended one of the following methods be used. - -Provide indent and outdent buttons in the menu. Keyboard shortcuts to the buttons should be Control+M for indent and Control+Shift+M for outdent. -Provide a button in the menu to toggle the use of Tab between the two modes. If this button is used, then Control+M is recommended as a keyboard shortcut to toggle the button. -
    - - Please comment with suggestions on how to deal with this. Send me a message at bgrinstead@mozilla.com or @bgrins if you have any ideas. -
  • -
- -

Possible Solution

-

- A solution involves resetting the input value to the current line when the cursor location moves with an empty selection. Then, by calling input.setSelectionRange(from.ch, to.ch) the actual selection location is being moved, and NVDA is able to detect the changes. This value must stay in the textarea long enough for the screenreader to detect the change, but has to be cleared out quick enough for the next cursor movement to be read properly. The quick clearing out could be made unnecessary by setting the value of the textarea to CodeMirror.getValue(), however this causes some major performance bottlenecks (see below). -

-

- In addition, we do not clip the selection at any maximum limit to ensure that the selection is always consistent in the actual textarea. -

-

- Still not fixed: Entering / exiting the editor is still buggy. -

- - -

Implementation Considerations

-

- I had hoped that this could be structured as a plugin, and could avoid touching the CodeMirror internals, but that proved to not be possible. That said, it avoids large modifications to the readInput and resetInput functions, and could easily be hidden behind a property. View the changes in codemirror-accessible.js. -

-

- This still needs to be tested with more advanced CodeMirror features, like atomic markers and linked documents, and with other screen readers. Please let me know if you'd be able to help with testing these features or can give feedback with different screen readers. -

- -

Performance Considerations

-

- The downside is that this could potentially slow things down. The selection was intentionally clipped with a low limit, and every time the selection changes, we need to set the textarea value to the current line of the CodeMirror. Performance on very long lines with CodeMirror are a known issue, and copying / selecting this data into a textarea is an extra step (though probably not significant compared to text measuring). -

-

- Originally, I was setting the textarea's value to the entire content (which seems to be best for the screenreader), but there is a major impact is caused by the computation of the selectionRange in this case. Textareas setSelectionRange method takes an absolute start and end value, while CodeMirror operates on individual lines. So we need to count from 0 to currently selected line and add the length of each line to arrive there. -

- -

Demos

-

- This is working in the demo below. Compare the first CodeMirror (with the fix applied), the second CodeMirror (using the original version), and a normal textarea (at the bottom). -

-

- You can also see a video of the three options here: -

- -
- - - - - - - - - - - -
-
-

- -
- makes it easier to debug and see what is going on -
- -
- -

CodeMirror With Accessibility Fix direct link

-
- -
- -
- -

CodeMirror Original direct link

-
- -
- -

Normal Textarea direct link

-
- -
- - - \ No newline at end of file diff --git a/public/js/lib/codemirror-accessible/javascript.js b/public/js/lib/codemirror-accessible/javascript.js deleted file mode 100755 index 905e76d953..0000000000 --- a/public/js/lib/codemirror-accessible/javascript.js +++ /dev/null @@ -1,482 +0,0 @@ -// TODO actually recognize syntax of TypeScript constructs - - -var opts= function(config, parserConfig) { - var indentUnit = config.indentUnit; - var statementIndent = parserConfig.statementIndent; - var jsonMode = parserConfig.json; - var isTS = parserConfig.typescript; - - // Tokenizer - - var keywords = function(){ - function kw(type) {return {type: type, style: "keyword"};} - var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); - var operator = kw("operator"), atom = {type: "atom", style: "atom"}; - - var jsKeywords = { - "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, - "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C, - "var": kw("var"), "const": kw("var"), "let": kw("var"), - "function": kw("function"), "catch": kw("catch"), - "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), - "in": operator, "typeof": operator, "instanceof": operator, - "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, - "this": kw("this") - }; - - // Extend the 'normal' keywords with the TypeScript language extensions - if (isTS) { - var type = {type: "variable", style: "variable-3"}; - var tsKeywords = { - // object-like things - "interface": kw("interface"), - "class": kw("class"), - "extends": kw("extends"), - "constructor": kw("constructor"), - - // scope modifiers - "public": kw("public"), - "private": kw("private"), - "protected": kw("protected"), - "static": kw("static"), - - "super": kw("super"), - - // types - "string": type, "number": type, "bool": type, "any": type - }; - - for (var attr in tsKeywords) { - jsKeywords[attr] = tsKeywords[attr]; - } - } - - return jsKeywords; - }(); - - var isOperatorChar = /[+\-*&%=<>!?|~^]/; - - function chain(stream, state, f) { - state.tokenize = f; - return f(stream, state); - } - - function nextUntilUnescaped(stream, end) { - var escaped = false, next; - while ((next = stream.next()) != null) { - if (next == end && !escaped) - return false; - escaped = !escaped && next == "\\"; - } - return escaped; - } - - // Used as scratch variables to communicate multiple values without - // consing up tons of objects. - var type, content; - function ret(tp, style, cont) { - type = tp; content = cont; - return style; - } - - function jsTokenBase(stream, state) { - var ch = stream.next(); - if (ch == '"' || ch == "'") - return chain(stream, state, jsTokenString(ch)); - else if (/[\[\]{}\(\),;\:\.]/.test(ch)) - return ret(ch); - else if (ch == "0" && stream.eat(/x/i)) { - stream.eatWhile(/[\da-f]/i); - return ret("number", "number"); - } - else if (/\d/.test(ch) || ch == "-" && stream.eat(/\d/)) { - stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); - return ret("number", "number"); - } - else if (ch == "/") { - if (stream.eat("*")) { - return chain(stream, state, jsTokenComment); - } - else if (stream.eat("/")) { - stream.skipToEnd(); - return ret("comment", "comment"); - } - else if (state.lastType == "operator" || state.lastType == "keyword c" || - /^[\[{}\(,;:]$/.test(state.lastType)) { - nextUntilUnescaped(stream, "/"); - stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla - return ret("regexp", "string-2"); - } - else { - stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); - } - } - else if (ch == "#") { - stream.skipToEnd(); - return ret("error", "error"); - } - else if (isOperatorChar.test(ch)) { - stream.eatWhile(isOperatorChar); - return ret("operator", null, stream.current()); - } - else { - stream.eatWhile(/[\w\$_]/); - var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word]; - return (known && state.lastType != ".") ? ret(known.type, known.style, word) : - ret("variable", "variable", word); - } - } - - function jsTokenString(quote) { - return function(stream, state) { - if (!nextUntilUnescaped(stream, quote)) - state.tokenize = jsTokenBase; - return ret("string", "string"); - }; - } - - function jsTokenComment(stream, state) { - var maybeEnd = false, ch; - while (ch = stream.next()) { - if (ch == "/" && maybeEnd) { - state.tokenize = jsTokenBase; - break; - } - maybeEnd = (ch == "*"); - } - return ret("comment", "comment"); - } - - // Parser - - var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true}; - - function JSLexical(indented, column, type, align, prev, info) { - this.indented = indented; - this.column = column; - this.type = type; - this.prev = prev; - this.info = info; - if (align != null) this.align = align; - } - - function inScope(state, varname) { - for (var v = state.localVars; v; v = v.next) - if (v.name == varname) return true; - } - - function parseJS(state, style, type, content, stream) { - var cc = state.cc; - // Communicate our context to the combinators. - // (Less wasteful than consing up a hundred closures on every call.) - cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; - - if (!state.lexical.hasOwnProperty("align")) - state.lexical.align = true; - - while(true) { - var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; - if (combinator(type, content)) { - while(cc.length && cc[cc.length - 1].lex) - cc.pop()(); - if (cx.marked) return cx.marked; - if (type == "variable" && inScope(state, content)) return "variable-2"; - return style; - } - } - } - - // Combinator utils - - var cx = {state: null, column: null, marked: null, cc: null}; - function pass() { - for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); - } - function cont() { - pass.apply(null, arguments); - return true; - } - function register(varname) { - function inList(list) { - for (var v = list; v; v = v.next) - if (v.name == varname) return true; - return false; - } - var state = cx.state; - if (state.context) { - cx.marked = "def"; - if (inList(state.localVars)) return; - state.localVars = {name: varname, next: state.localVars}; - } else { - if (inList(state.globalVars)) return; - state.globalVars = {name: varname, next: state.globalVars}; - } - } - - // Combinators - - var defaultVars = {name: "this", next: {name: "arguments"}}; - function pushcontext() { - cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; - cx.state.localVars = defaultVars; - } - function popcontext() { - cx.state.localVars = cx.state.context.vars; - cx.state.context = cx.state.context.prev; - } - function pushlex(type, info) { - var result = function() { - var state = cx.state, indent = state.indented; - if (state.lexical.type == "stat") indent = state.lexical.indented; - state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); - }; - result.lex = true; - return result; - } - function poplex() { - var state = cx.state; - if (state.lexical.prev) { - if (state.lexical.type == ")") - state.indented = state.lexical.indented; - state.lexical = state.lexical.prev; - } - } - poplex.lex = true; - - function expect(wanted) { - return function(type) { - if (type == wanted) return cont(); - else if (wanted == ";") return pass(); - else return cont(arguments.callee); - }; - } - - function statement(type) { - if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex); - if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex); - if (type == "keyword b") return cont(pushlex("form"), statement, poplex); - if (type == "{") return cont(pushlex("}"), block, poplex); - if (type == ";") return cont(); - if (type == "if") return cont(pushlex("form"), expression, statement, poplex, maybeelse); - if (type == "function") return cont(functiondef); - if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), - poplex, statement, poplex); - if (type == "variable") return cont(pushlex("stat"), maybelabel); - if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"), - block, poplex, poplex); - if (type == "case") return cont(expression, expect(":")); - if (type == "default") return cont(expect(":")); - if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), - statement, poplex, popcontext); - return pass(pushlex("stat"), expression, expect(";"), poplex); - } - function expression(type) { - return expressionInner(type, false); - } - function expressionNoComma(type) { - return expressionInner(type, true); - } - function expressionInner(type, noComma) { - var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; - if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); - if (type == "function") return cont(functiondef); - if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression); - if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); - if (type == "operator") return cont(noComma ? expressionNoComma : expression); - if (type == "[") return cont(pushlex("]"), commasep(expressionNoComma, "]"), poplex, maybeop); - if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeop); - return cont(); - } - function maybeexpression(type) { - if (type.match(/[;\}\)\],]/)) return pass(); - return pass(expression); - } - function maybeexpressionNoComma(type) { - if (type.match(/[;\}\)\],]/)) return pass(); - return pass(expressionNoComma); - } - - function maybeoperatorComma(type, value) { - if (type == ",") return cont(expression); - return maybeoperatorNoComma(type, value, false); - } - function maybeoperatorNoComma(type, value, noComma) { - var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; - var expr = noComma == false ? expression : expressionNoComma; - if (type == "operator") { - if (/\+\+|--/.test(value)) return cont(me); - if (value == "?") return cont(expression, expect(":"), expr); - return cont(expr); - } - if (type == ";") return; - if (type == "(") return cont(pushlex(")", "call"), commasep(expressionNoComma, ")"), poplex, me); - if (type == ".") return cont(property, me); - if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); - } - function maybelabel(type) { - if (type == ":") return cont(poplex, statement); - return pass(maybeoperatorComma, expect(";"), poplex); - } - function property(type) { - if (type == "variable") {cx.marked = "property"; return cont();} - } - function objprop(type, value) { - if (type == "variable") { - cx.marked = "property"; - if (value == "get" || value == "set") return cont(getterSetter); - } else if (type == "number" || type == "string") { - cx.marked = type + " property"; - } - if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expressionNoComma); - } - function getterSetter(type) { - if (type == ":") return cont(expression); - if (type != "variable") return cont(expect(":"), expression); - cx.marked = "property"; - return cont(functiondef); - } - function commasep(what, end) { - function proceed(type) { - if (type == ",") { - var lex = cx.state.lexical; - if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; - return cont(what, proceed); - } - if (type == end) return cont(); - return cont(expect(end)); - } - return function(type) { - if (type == end) return cont(); - else return pass(what, proceed); - }; - } - function block(type) { - if (type == "}") return cont(); - return pass(statement, block); - } - function maybetype(type) { - if (type == ":") return cont(typedef); - return pass(); - } - function typedef(type) { - if (type == "variable"){cx.marked = "variable-3"; return cont();} - return pass(); - } - function vardef1(type, value) { - if (type == "variable") { - register(value); - return isTS ? cont(maybetype, vardef2) : cont(vardef2); - } - return pass(); - } - function vardef2(type, value) { - if (value == "=") return cont(expressionNoComma, vardef2); - if (type == ",") return cont(vardef1); - } - function maybeelse(type, value) { - if (type == "keyword b" && value == "else") return cont(pushlex("form"), statement, poplex); - } - function forspec1(type) { - if (type == "var") return cont(vardef1, expect(";"), forspec2); - if (type == ";") return cont(forspec2); - if (type == "variable") return cont(formaybein); - return pass(expression, expect(";"), forspec2); - } - function formaybein(_type, value) { - if (value == "in") return cont(expression); - return cont(maybeoperatorComma, forspec2); - } - function forspec2(type, value) { - if (type == ";") return cont(forspec3); - if (value == "in") return cont(expression); - return pass(expression, expect(";"), forspec3); - } - function forspec3(type) { - if (type != ")") cont(expression); - } - function functiondef(type, value) { - if (type == "variable") {register(value); return cont(functiondef);} - if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext); - } - function funarg(type, value) { - if (type == "variable") {register(value); return isTS ? cont(maybetype) : cont();} - } - - // Interface - - return { - startState: function(basecolumn) { - return { - tokenize: jsTokenBase, - lastType: null, - cc: [], - lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), - localVars: parserConfig.localVars, - globalVars: parserConfig.globalVars, - context: parserConfig.localVars && {vars: parserConfig.localVars}, - indented: 0 - }; - }, - - token: function(stream, state) { - if (stream.sol()) { - if (!state.lexical.hasOwnProperty("align")) - state.lexical.align = false; - state.indented = stream.indentation(); - } - if (state.tokenize != jsTokenComment && stream.eatSpace()) return null; - var style = state.tokenize(stream, state); - if (type == "comment") return style; - state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; - return parseJS(state, style, type, content, stream); - }, - - indent: function(state, textAfter) { - if (state.tokenize == jsTokenComment) return CodeMirror.Pass; - if (state.tokenize != jsTokenBase) return 0; - var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical; - // Kludge to prevent 'maybelse' from blocking lexical scope pops - for (var i = state.cc.length - 1; i >= 0; --i) { - var c = state.cc[i]; - if (c == poplex) lexical = lexical.prev; - else if (c != maybeelse || /^else\b/.test(textAfter)) break; - } - if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev; - if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") - lexical = lexical.prev; - var type = lexical.type, closing = firstChar == type; - - if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? 4 : 0); - else if (type == "form" && firstChar == "{") return lexical.indented; - else if (type == "form") return lexical.indented + indentUnit; - else if (type == "stat") - return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? statementIndent || indentUnit : 0); - else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) - return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); - else if (lexical.align) return lexical.column + (closing ? 0 : 1); - else return lexical.indented + (closing ? 0 : indentUnit); - }, - - electricChars: ":{}", - blockCommentStart: jsonMode ? null : "/*", - blockCommentEnd: jsonMode ? null : "*/", - lineComment: jsonMode ? null : "//", - fold: "brace", - - helperType: jsonMode ? "json" : "javascript", - jsonMode: jsonMode - }; -}; - -CodeMirror.defineMode("javascript", opts ); -CodeMirrorOriginal.defineMode("javascript", opts ); -CodeMirror.defineMIME("text/javascript", "javascript"); -CodeMirror.defineMIME("text/ecmascript", "javascript"); -CodeMirror.defineMIME("application/javascript", "javascript"); -CodeMirror.defineMIME("application/ecmascript", "javascript"); -CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); -CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); -CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); -CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); diff --git a/public/js/lib/codemirror-accessible/stress.html b/public/js/lib/codemirror-accessible/stress.html deleted file mode 100755 index fba041a163..0000000000 --- a/public/js/lib/codemirror-accessible/stress.html +++ /dev/null @@ -1,749 +0,0 @@ - - - - - CodeMirror Accessible - - - - - - - - - - - - -

CodeMirror Accessibility

- -
- -
- This page is just an experiment. When you are are ready, you can click . Careful - this may be very slow. -
- -
- makes it easier to debug and see what is going on -
- -
- -

CodeMirror With Accessibility Fix

- - -
- -

CodeMirror Original

-
- -
- -

Normal Textarea

-
- -
- - - \ No newline at end of file diff --git a/public/js/lib/codemirror/.gitattributes b/public/js/lib/codemirror/.gitattributes deleted file mode 100644 index f8bdd60f49..0000000000 --- a/public/js/lib/codemirror/.gitattributes +++ /dev/null @@ -1,8 +0,0 @@ -*.txt text -*.js text -*.html text -*.md text -*.json text -*.yml text -*.css text -*.svg text diff --git a/public/js/lib/codemirror/.gitignore b/public/js/lib/codemirror/.gitignore deleted file mode 100644 index b720b9be07..0000000000 --- a/public/js/lib/codemirror/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -/node_modules -/npm-debug.log -test.html -.tern-* -*~ -*.swp -.idea -*.iml diff --git a/public/js/lib/codemirror/.travis.yml b/public/js/lib/codemirror/.travis.yml deleted file mode 100644 index baa0031d50..0000000000 --- a/public/js/lib/codemirror/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: node_js -node_js: - - 0.8 diff --git a/public/js/lib/codemirror/AUTHORS b/public/js/lib/codemirror/AUTHORS deleted file mode 100644 index afa3cc083a..0000000000 --- a/public/js/lib/codemirror/AUTHORS +++ /dev/null @@ -1,423 +0,0 @@ -List of CodeMirror contributors. Updated before every release. - -4r2r -Aaron Brooks -Abdelouahab -Abe Fettig -Adam Ahmed -Adam King -adanlobato -Adán Lobato -Adrian Aichner -aeroson -Ahmad Amireh -Ahmad M. Zawawi -ahoward -Akeksandr Motsjonov -Alberto González Palomo -Alberto Pose -Albert Xing -Alexander Pavlov -Alexander Schepanovski -Alexander Shvets -Alexander Solovyov -Alexandre Bique -alexey-k -Alex Piggott -Amsul -amuntean -Amy -Ananya Sen -anaran -AndersMad -Anders Nawroth -Anderson Mesquita -Andrea G -Andreas Reischuck -Andre von Houck -Andrey Fedorov -Andrey Klyuchnikov -Andrey Lushnikov -Andy Joslin -Andy Kimball -Andy Li -angelozerr -angelo.zerr@gmail.com -Ankit Ahuja -Ansel Santosa -Anthony Grimes -Anton Kovalyov -areos -as3boyan -AtomicPages LLC -Atul Bhouraskar -Aurelian Oancea -Bastian Müller -Bem Jones-Bey -benbro -Beni Cherniavsky-Paskin -Benjamin DeCoste -Ben Keen -Bernhard Sirlinger -Bert Chang -Billy Moon -binny -B Krishna Chaitanya -Blaine G -blukat29 -boomyjee -borawjm -Brandon Frohs -Brandon Wamboldt -Brett Zamir -Brian Grinstead -Brian Sletten -Bruce Mitchener -Chandra Sekhar Pydi -Charles Skelton -Cheah Chu Yeow -Chris Coyier -Chris Granger -Chris Houseknecht -Chris Morgan -Christian Oyarzun -Christopher Brown -ciaranj -CodeAnimal -ComFreek -Curtis Gagliardi -dagsta -daines -Dale Jung -Dan Bentley -Dan Heberden -Daniel, Dao Quang Minh -Daniele Di Sarli -Daniel Faust -Daniel Huigens -Daniel KJ -Daniel Neel -Daniel Parnell -Danny Yoo -darealshinji -Darius Roberts -Dave Myers -David Mignot -David Pathakjee -David Vázquez -deebugger -Deep Thought -Devon Carew -dignifiedquire -Dimage Sapelkin -domagoj412 -Dominator008 -Domizio Demichelis -Doug Wikle -Drew Bratcher -Drew Hintz -Drew Khoury -Dror BG -duralog -eborden -edsharp -ekhaled -Enam Mijbah Noor -Eric Allam -eustas -Fabien O'Carroll -Fabio Zendhi Nagao -Faiza Alsaied -Fauntleroy -fbuchinger -feizhang365 -Felipe Lalanne -Felix Raab -Filip Noetzel -flack -ForbesLindesay -Forbes Lindesay -Ford_Lawnmower -Frank Wiegand -Gabriel Gheorghian -Gabriel Horner -Gabriel Nahmias -galambalazs -Gautam Mehta -gekkoe -Gerard Braad -Gergely Hegykozi -Glenn Jorde -Glenn Ruehle -Golevka -Gordon Smith -Grant Skinner -greengiant -Gregory Koberger -Guillaume Massé -Guillaume Massé -Gustavo Rodrigues -Hakan Tunc -Hans Engel -Hardest -Hasan Karahan -Herculano Campos -Hiroyuki Makino -hitsthings -Hocdoc -Ian Beck -Ian Dickinson -Ian Wehrman -Ian Wetherbee -Ice White -ICHIKAWA, Yuji -ilvalle -Ingo Richter -Irakli Gozalishvili -Ivan Kurnosov -Jacob Lee -Jakob Miland -Jakub Vrana -Jakub Vrána -James Campos -James Thorne -Jamie Hill -Jan Jongboom -jankeromnes -Jan Keromnes -Jan Odvarko -Jan T. Sott -Jared Forsyth -Jason -Jason Barnabe -Jason Grout -Jason Johnston -Jason San Jose -Jason Siefken -Jaydeep Solanki -Jean Boussier -jeffkenton -Jeff Pickhardt -jem (graphite) -Jeremy Parmenter -Jochen Berger -Johan Ask -John Connor -John Lees-Miller -John Snelson -John Van Der Loo -Jonathan Malmaud -jongalloway -Jon Malmaud -Jon Sangster -Joost-Wim Boekesteijn -Joseph Pecoraro -Joshua Newman -Josh Watzman -jots -jsoojeon -Juan Benavides Romero -Jucovschi Constantin -Juho Vuori -Justin Hileman -jwallers@gmail.com -kaniga -Ken Newman -Ken Rockot -Kevin Sawicki -Kevin Ushey -Klaus Silveira -Koh Zi Han, Cliff -komakino -Konstantin Lopuhin -koops -ks-ifware -kubelsmieci -Lanfei -Lanny -Laszlo Vidacs -leaf corcoran -Leonid Khachaturov -Leon Sorokin -Leonya Khachaturov -Liam Newman -LM -lochel -Lorenzo Stoakes -Luciano Longo -Luke Stagner -lynschinzer -Maksim Lin -Maksym Taran -Malay Majithia -Manuel Rego Casasnovas -Marat Dreizin -Marcel Gerber -Marco Aurélio -Marco Munizaga -Marcus Bointon -Marijn Haverbeke -Mário Gonçalves -Mario Pietsch -Mark Lentczner -Marko Bonaci -Martin Balek -Martín Gaitán -Martin Hasoň -Mason Malone -Mateusz Paprocki -Mathias Bynens -mats cronqvist -Matthew Beale -Matthias Bussonnier -Matthias BUSSONNIER -Matt McDonald -Matt Pass -Matt Sacks -mauricio -Maximilian Hils -Maxim Kraev -Max Kirsch -Max Xiantu -mbarkhau -Metatheos -Micah Dubinko -Michael Lehenbauer -Michael Zhou -Mighty Guava -Miguel Castillo -Mike -Mike Brevoort -Mike Diaz -Mike Ivanov -Mike Kadin -MinRK -Miraculix87 -misfo -mloginov -Moritz Schwörer -mps -mtaran-google -Narciso Jaramillo -Nathan Williams -ndr -nerbert -nextrevision -nguillaumin -Ng Zhi An -Nicholas Bollweg -Nicholas Bollweg (Nick) -Nick Small -Niels van Groningen -nightwing -Nikita Beloglazov -Nikita Vasilyev -Nikolay Kostov -nilp0inter -Nisarg Jhaveri -nlwillia -Norman Rzepka -pablo -Page -Panupong Pasupat -paris -Patil Arpith -Patrick Stoica -Patrick Strawderman -Paul Garvin -Paul Ivanov -Pavel Feldman -Pavel Strashkin -Paweł Bartkiewicz -peteguhl -Peter Flynn -peterkroon -Peter Kroon -prasanthj -Prasanth J -Radek Piórkowski -Rahul -Randall Mason -Randy Burden -Randy Edmunds -Rasmus Erik Voel Jensen -Richard van der Meer -Richard Z.H. Wang -Robert Crossfield -Roberto Abdelkader Martínez Pérez -robertop23 -Robert Plummer -Ruslan Osmanov -Ryan Prior -sabaca -Samuel Ainsworth -sandeepshetty -Sander AKA Redsandro -santec -Sascha Peilicke -satchmorun -sathyamoorthi -SCLINIC\jdecker -Scott Aikin -Scott Goodhew -Sebastian Zaha -shaund -shaun gilchrist -Shawn A -sheopory -Shiv Deepak -Shmuel Englard -Shubham Jain -silverwind -snasa -soliton4 -sonson -spastorelli -srajanpaliwal -Stanislav Oaserele -Stas Kobzar -Stefan Borsje -Steffen Beyer -Steve O'Hara -stoskov -Taha Jahangir -Takuji Shimokawa -Tarmil -tfjgeorge -Thaddee Tyl -TheHowl -think -Thomas Dvornik -Thomas Schmid -Tim Alby -Tim Baumann -Timothy Farrell -Timothy Hatcher -TobiasBg -Tomas-A -Tomas Varaneckas -Tom Erik Støwer -Tom MacWright -Tony Jian -Travis Heppe -Triangle717 -twifkak -Vestimir Markov -vf -Vincent Woo -Volker Mische -wenli -Wesley Wiser -Will Binns-Smith -William Jamieson -William Stein -Willy -Wojtek Ptak -Xavier Mendez -Yassin N. Hassan -YNH Webdev -Yunchi Luo -Yuvi Panda -Zachary Dremann -zziuni -魏鹏刚 diff --git a/public/js/lib/codemirror/CONTRIBUTING.md b/public/js/lib/codemirror/CONTRIBUTING.md deleted file mode 100644 index c4296ce4d2..0000000000 --- a/public/js/lib/codemirror/CONTRIBUTING.md +++ /dev/null @@ -1,76 +0,0 @@ -# How to contribute - -- [Getting help](#getting-help-) -- [Submitting bug reports](#submitting-bug-reports-) -- [Contributing code](#contributing-code-) - -## Getting help - -Community discussion, questions, and informal bug reporting is done on the -[discuss.CodeMirror forum](http://discuss.codemirror.net). - -## Submitting bug reports - -The preferred way to report bugs is to use the -[GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before -reporting a bug, read these pointers. - -**Note:** The issue tracker is for *bugs*, not requests for help. Questions -should be asked on the -[discuss.CodeMirror forum](http://discuss.codemirror.net) instead. - -### Reporting bugs effectively - -- CodeMirror is maintained by volunteers. They don't owe you anything, so be - polite. Reports with an indignant or belligerent tone tend to be moved to the - bottom of the pile. - -- Include information about **the browser in which the problem occurred**. Even - if you tested several browsers, and the problem occurred in all of them, - mention this fact in the bug report. Also include browser version numbers and - the operating system that you're on. - -- Mention which release of CodeMirror you're using. Preferably, try also with - the current development snapshot, to ensure the problem has not already been - fixed. - -- Mention very precisely what went wrong. "X is broken" is not a good bug - report. What did you expect to happen? What happened instead? Describe the - exact steps a maintainer has to take to make the problem occur. We can not - fix something that we can not observe. - -- If the problem can not be reproduced in any of the demos included in the - CodeMirror distribution, please provide an HTML document that demonstrates - the problem. The best way to do this is to go to - [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and - include the resulting link in your bug report. - -## Contributing code - -- Make sure you have a [GitHub Account](https://github.com/signup/free) -- Fork [CodeMirror](https://github.com/codemirror/CodeMirror/) - ([how to fork a repo](https://help.github.com/articles/fork-a-repo)) -- Make your changes -- If your changes are easy to test or likely to regress, add tests. - Tests for the core go into `test/test.js`, some modes have their own - test suite under `mode/XXX/test.js`. Feel free to add new test - suites to modes that don't have one yet (be sure to link the new - tests into `test/index.html`). -- Follow the general code style of the rest of the project (see - below). Run `bin/lint` to verify that the linter is happy. -- Make sure all tests pass. Visit `test/index.html` in your browser to - run them. -- Submit a pull request -([how to create a pull request](https://help.github.com/articles/fork-a-repo)) - -### Coding standards - -- 2 spaces per indentation level, no tabs. -- Include semicolons after statements. -- Note that the linter (`bin/lint`) which is run after each commit - complains about unused variables and functions. Prefix their names - with an underscore to muffle it. - -- CodeMirror does *not* follow JSHint or JSLint prescribed style. - Patches that try to 'fix' code to pass one of these linters will be - unceremoniously discarded. diff --git a/public/js/lib/codemirror/README.md b/public/js/lib/codemirror/README.md deleted file mode 100644 index 42b06f7474..0000000000 --- a/public/js/lib/codemirror/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# CodeMirror -[![Build Status](https://travis-ci.org/codemirror/CodeMirror.svg)](https://travis-ci.org/codemirror/CodeMirror) -[![NPM version](https://img.shields.io/npm/v/codemirror.svg)](https://www.npmjs.org/package/codemirror) - -CodeMirror is a JavaScript component that provides a code editor in -the browser. When a mode is available for the language you are coding -in, it will color your code, and optionally help with indentation. - -The project page is http://codemirror.net -The manual is at http://codemirror.net/doc/manual.html -The contributing guidelines are in [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md) diff --git a/public/js/lib/codemirror/addon/comment/comment.js b/public/js/lib/codemirror/addon/comment/comment.js deleted file mode 100644 index 2dd114d332..0000000000 --- a/public/js/lib/codemirror/addon/comment/comment.js +++ /dev/null @@ -1,183 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - var noOptions = {}; - var nonWS = /[^\s\u00a0]/; - var Pos = CodeMirror.Pos; - - function firstNonWS(str) { - var found = str.search(nonWS); - return found == -1 ? 0 : found; - } - - CodeMirror.commands.toggleComment = function(cm) { - var minLine = Infinity, ranges = cm.listSelections(), mode = null; - for (var i = ranges.length - 1; i >= 0; i--) { - var from = ranges[i].from(), to = ranges[i].to(); - if (from.line >= minLine) continue; - if (to.line >= minLine) to = Pos(minLine, 0); - minLine = from.line; - if (mode == null) { - if (cm.uncomment(from, to)) mode = "un"; - else { cm.lineComment(from, to); mode = "line"; } - } else if (mode == "un") { - cm.uncomment(from, to); - } else { - cm.lineComment(from, to); - } - } - }; - - CodeMirror.defineExtension("lineComment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var commentString = options.lineComment || mode.lineComment; - if (!commentString) { - if (options.blockCommentStart || mode.blockCommentStart) { - options.fullLines = true; - self.blockComment(from, to, options); - } - return; - } - var firstLine = self.getLine(from.line); - if (firstLine == null) return; - var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1); - var pad = options.padding == null ? " " : options.padding; - var blankLines = options.commentBlankLines || from.line == to.line; - - self.operation(function() { - if (options.indent) { - var baseString = firstLine.slice(0, firstNonWS(firstLine)); - for (var i = from.line; i < end; ++i) { - var line = self.getLine(i), cut = baseString.length; - if (!blankLines && !nonWS.test(line)) continue; - if (line.slice(0, cut) != baseString) cut = firstNonWS(line); - self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut)); - } - } else { - for (var i = from.line; i < end; ++i) { - if (blankLines || nonWS.test(self.getLine(i))) - self.replaceRange(commentString + pad, Pos(i, 0)); - } - } - }); - }); - - CodeMirror.defineExtension("blockComment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var startString = options.blockCommentStart || mode.blockCommentStart; - var endString = options.blockCommentEnd || mode.blockCommentEnd; - if (!startString || !endString) { - if ((options.lineComment || mode.lineComment) && options.fullLines != false) - self.lineComment(from, to, options); - return; - } - - var end = Math.min(to.line, self.lastLine()); - if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end; - - var pad = options.padding == null ? " " : options.padding; - if (from.line > end) return; - - self.operation(function() { - if (options.fullLines != false) { - var lastLineHasText = nonWS.test(self.getLine(end)); - self.replaceRange(pad + endString, Pos(end)); - self.replaceRange(startString + pad, Pos(from.line, 0)); - var lead = options.blockCommentLead || mode.blockCommentLead; - if (lead != null) for (var i = from.line + 1; i <= end; ++i) - if (i != end || lastLineHasText) - self.replaceRange(lead + pad, Pos(i, 0)); - } else { - self.replaceRange(endString, to); - self.replaceRange(startString, from); - } - }); - }); - - CodeMirror.defineExtension("uncomment", function(from, to, options) { - if (!options) options = noOptions; - var self = this, mode = self.getModeAt(from); - var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end); - - // Try finding line comments - var lineString = options.lineComment || mode.lineComment, lines = []; - var pad = options.padding == null ? " " : options.padding, didSomething; - lineComment: { - if (!lineString) break lineComment; - for (var i = start; i <= end; ++i) { - var line = self.getLine(i); - var found = line.indexOf(lineString); - if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1; - if (found == -1 && (i != end || i == start) && nonWS.test(line)) break lineComment; - if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment; - lines.push(line); - } - self.operation(function() { - for (var i = start; i <= end; ++i) { - var line = lines[i - start]; - var pos = line.indexOf(lineString), endPos = pos + lineString.length; - if (pos < 0) continue; - if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length; - didSomething = true; - self.replaceRange("", Pos(i, pos), Pos(i, endPos)); - } - }); - if (didSomething) return true; - } - - // Try block comments - var startString = options.blockCommentStart || mode.blockCommentStart; - var endString = options.blockCommentEnd || mode.blockCommentEnd; - if (!startString || !endString) return false; - var lead = options.blockCommentLead || mode.blockCommentLead; - var startLine = self.getLine(start), endLine = end == start ? startLine : self.getLine(end); - var open = startLine.indexOf(startString), close = endLine.lastIndexOf(endString); - if (close == -1 && start != end) { - endLine = self.getLine(--end); - close = endLine.lastIndexOf(endString); - } - if (open == -1 || close == -1 || - !/comment/.test(self.getTokenTypeAt(Pos(start, open + 1))) || - !/comment/.test(self.getTokenTypeAt(Pos(end, close + 1)))) - return false; - - // Avoid killing block comments completely outside the selection. - // Positions of the last startString before the start of the selection, and the first endString after it. - var lastStart = startLine.lastIndexOf(startString, from.ch); - var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length); - if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false; - // Positions of the first endString after the end of the selection, and the last startString before it. - firstEnd = endLine.indexOf(endString, to.ch); - var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch); - lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart; - if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false; - - self.operation(function() { - self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)), - Pos(end, close + endString.length)); - var openEnd = open + startString.length; - if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length; - self.replaceRange("", Pos(start, open), Pos(start, openEnd)); - if (lead) for (var i = start + 1; i <= end; ++i) { - var line = self.getLine(i), found = line.indexOf(lead); - if (found == -1 || nonWS.test(line.slice(0, found))) continue; - var foundEnd = found + lead.length; - if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length; - self.replaceRange("", Pos(i, found), Pos(i, foundEnd)); - } - }); - return true; - }); -}); diff --git a/public/js/lib/codemirror/addon/comment/continuecomment.js b/public/js/lib/codemirror/addon/comment/continuecomment.js deleted file mode 100644 index b11d51e6ca..0000000000 --- a/public/js/lib/codemirror/addon/comment/continuecomment.js +++ /dev/null @@ -1,85 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - var modes = ["clike", "css", "javascript"]; - - for (var i = 0; i < modes.length; ++i) - CodeMirror.extendMode(modes[i], {blockCommentContinue: " * "}); - - function continueComment(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), mode, inserts = []; - for (var i = 0; i < ranges.length; i++) { - var pos = ranges[i].head, token = cm.getTokenAt(pos); - if (token.type != "comment") return CodeMirror.Pass; - var modeHere = CodeMirror.innerMode(cm.getMode(), token.state).mode; - if (!mode) mode = modeHere; - else if (mode != modeHere) return CodeMirror.Pass; - - var insert = null; - if (mode.blockCommentStart && mode.blockCommentContinue) { - var end = token.string.indexOf(mode.blockCommentEnd); - var full = cm.getRange(CodeMirror.Pos(pos.line, 0), CodeMirror.Pos(pos.line, token.end)), found; - if (end != -1 && end == token.string.length - mode.blockCommentEnd.length && pos.ch >= end) { - // Comment ended, don't continue it - } else if (token.string.indexOf(mode.blockCommentStart) == 0) { - insert = full.slice(0, token.start); - if (!/^\s*$/.test(insert)) { - insert = ""; - for (var j = 0; j < token.start; ++j) insert += " "; - } - } else if ((found = full.indexOf(mode.blockCommentContinue)) != -1 && - found + mode.blockCommentContinue.length > token.start && - /^\s*$/.test(full.slice(0, found))) { - insert = full.slice(0, found); - } - if (insert != null) insert += mode.blockCommentContinue; - } - if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) { - var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment); - if (found > -1) { - insert = line.slice(0, found); - if (/\S/.test(insert)) insert = null; - else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0]; - } - } - if (insert == null) return CodeMirror.Pass; - inserts[i] = "\n" + insert; - } - - cm.operation(function() { - for (var i = ranges.length - 1; i >= 0; i--) - cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert"); - }); - } - - function continueLineCommentEnabled(cm) { - var opt = cm.getOption("continueComments"); - if (opt && typeof opt == "object") - return opt.continueLineComment !== false; - return true; - } - - CodeMirror.defineOption("continueComments", null, function(cm, val, prev) { - if (prev && prev != CodeMirror.Init) - cm.removeKeyMap("continueComment"); - if (val) { - var key = "Enter"; - if (typeof val == "string") - key = val; - else if (typeof val == "object" && val.key) - key = val.key; - var map = {name: "continueComment"}; - map[key] = continueComment; - cm.addKeyMap(map); - } - }); -}); diff --git a/public/js/lib/codemirror/addon/dialog/dialog.css b/public/js/lib/codemirror/addon/dialog/dialog.css deleted file mode 100644 index 2e7c0fc9b8..0000000000 --- a/public/js/lib/codemirror/addon/dialog/dialog.css +++ /dev/null @@ -1,32 +0,0 @@ -.CodeMirror-dialog { - position: absolute; - left: 0; right: 0; - background: white; - z-index: 15; - padding: .1em .8em; - overflow: hidden; - color: #333; -} - -.CodeMirror-dialog-top { - border-bottom: 1px solid #eee; - top: 0; -} - -.CodeMirror-dialog-bottom { - border-top: 1px solid #eee; - bottom: 0; -} - -.CodeMirror-dialog input { - border: none; - outline: none; - background: transparent; - width: 20em; - color: inherit; - font-family: monospace; -} - -.CodeMirror-dialog button { - font-size: 70%; -} diff --git a/public/js/lib/codemirror/addon/dialog/dialog.js b/public/js/lib/codemirror/addon/dialog/dialog.js deleted file mode 100644 index e0e8ad4eb7..0000000000 --- a/public/js/lib/codemirror/addon/dialog/dialog.js +++ /dev/null @@ -1,155 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -// Open simple dialogs on top of an editor. Relies on dialog.css. - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - function dialogDiv(cm, template, bottom) { - var wrap = cm.getWrapperElement(); - var dialog; - dialog = wrap.appendChild(document.createElement("div")); - if (bottom) - dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom"; - else - dialog.className = "CodeMirror-dialog CodeMirror-dialog-top"; - - if (typeof template == "string") { - dialog.innerHTML = template; - } else { // Assuming it's a detached DOM element. - dialog.appendChild(template); - } - return dialog; - } - - function closeNotification(cm, newVal) { - if (cm.state.currentNotificationClose) - cm.state.currentNotificationClose(); - cm.state.currentNotificationClose = newVal; - } - - CodeMirror.defineExtension("openDialog", function(template, callback, options) { - if (!options) options = {}; - - closeNotification(this, null); - - var dialog = dialogDiv(this, template, options.bottom); - var closed = false, me = this; - function close(newVal) { - if (typeof newVal == 'string') { - inp.value = newVal; - } else { - if (closed) return; - closed = true; - dialog.parentNode.removeChild(dialog); - me.focus(); - - if (options.onClose) options.onClose(dialog); - } - } - - var inp = dialog.getElementsByTagName("input")[0], button; - if (inp) { - if (options.value) { - inp.value = options.value; - inp.select(); - } - - if (options.onInput) - CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);}); - if (options.onKeyUp) - CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);}); - - CodeMirror.on(inp, "keydown", function(e) { - if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; } - if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) { - inp.blur(); - CodeMirror.e_stop(e); - close(); - } - if (e.keyCode == 13) callback(inp.value, e); - }); - - if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close); - - inp.focus(); - } else if (button = dialog.getElementsByTagName("button")[0]) { - CodeMirror.on(button, "click", function() { - close(); - me.focus(); - }); - - if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close); - - button.focus(); - } - return close; - }); - - CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) { - closeNotification(this, null); - var dialog = dialogDiv(this, template, options && options.bottom); - var buttons = dialog.getElementsByTagName("button"); - var closed = false, me = this, blurring = 1; - function close() { - if (closed) return; - closed = true; - dialog.parentNode.removeChild(dialog); - me.focus(); - } - buttons[0].focus(); - for (var i = 0; i < buttons.length; ++i) { - var b = buttons[i]; - (function(callback) { - CodeMirror.on(b, "click", function(e) { - CodeMirror.e_preventDefault(e); - close(); - if (callback) callback(me); - }); - })(callbacks[i]); - CodeMirror.on(b, "blur", function() { - --blurring; - setTimeout(function() { if (blurring <= 0) close(); }, 200); - }); - CodeMirror.on(b, "focus", function() { ++blurring; }); - } - }); - - /* - * openNotification - * Opens a notification, that can be closed with an optional timer - * (default 5000ms timer) and always closes on click. - * - * If a notification is opened while another is opened, it will close the - * currently opened one and open the new one immediately. - */ - CodeMirror.defineExtension("openNotification", function(template, options) { - closeNotification(this, close); - var dialog = dialogDiv(this, template, options && options.bottom); - var closed = false, doneTimer; - var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000; - - function close() { - if (closed) return; - closed = true; - clearTimeout(doneTimer); - dialog.parentNode.removeChild(dialog); - } - - CodeMirror.on(dialog, 'click', function(e) { - CodeMirror.e_preventDefault(e); - close(); - }); - - if (duration) - doneTimer = setTimeout(close, duration); - - return close; - }); -}); diff --git a/public/js/lib/codemirror/addon/display/fullscreen.css b/public/js/lib/codemirror/addon/display/fullscreen.css deleted file mode 100644 index 437acd89be..0000000000 --- a/public/js/lib/codemirror/addon/display/fullscreen.css +++ /dev/null @@ -1,6 +0,0 @@ -.CodeMirror-fullscreen { - position: fixed; - top: 0; left: 0; right: 0; bottom: 0; - height: auto; - z-index: 9; -} diff --git a/public/js/lib/codemirror/addon/display/fullscreen.js b/public/js/lib/codemirror/addon/display/fullscreen.js deleted file mode 100644 index cd3673b96c..0000000000 --- a/public/js/lib/codemirror/addon/display/fullscreen.js +++ /dev/null @@ -1,41 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { - if (old == CodeMirror.Init) old = false; - if (!old == !val) return; - if (val) setFullscreen(cm); - else setNormal(cm); - }); - - function setFullscreen(cm) { - var wrap = cm.getWrapperElement(); - cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, - width: wrap.style.width, height: wrap.style.height}; - wrap.style.width = ""; - wrap.style.height = "auto"; - wrap.className += " CodeMirror-fullscreen"; - document.documentElement.style.overflow = "hidden"; - cm.refresh(); - } - - function setNormal(cm) { - var wrap = cm.getWrapperElement(); - wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); - document.documentElement.style.overflow = ""; - var info = cm.state.fullScreenRestore; - wrap.style.width = info.width; wrap.style.height = info.height; - window.scrollTo(info.scrollLeft, info.scrollTop); - cm.refresh(); - } -}); diff --git a/public/js/lib/codemirror/addon/display/panel.js b/public/js/lib/codemirror/addon/display/panel.js deleted file mode 100644 index 22c0453e8f..0000000000 --- a/public/js/lib/codemirror/addon/display/panel.js +++ /dev/null @@ -1,94 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - CodeMirror.defineExtension("addPanel", function(node, options) { - if (!this.state.panels) initPanels(this); - - var info = this.state.panels; - if (options && options.position == "bottom") - info.wrapper.appendChild(node); - else - info.wrapper.insertBefore(node, info.wrapper.firstChild); - var height = (options && options.height) || node.offsetHeight; - this._setSize(null, info.heightLeft -= height); - info.panels++; - return new Panel(this, node, options, height); - }); - - function Panel(cm, node, options, height) { - this.cm = cm; - this.node = node; - this.options = options; - this.height = height; - this.cleared = false; - } - - Panel.prototype.clear = function() { - if (this.cleared) return; - this.cleared = true; - var info = this.cm.state.panels; - this.cm._setSize(null, info.heightLeft += this.height); - info.wrapper.removeChild(this.node); - if (--info.panels == 0) removePanels(this.cm); - }; - - Panel.prototype.changed = function(height) { - var newHeight = height == null ? this.node.offsetHeight : height; - var info = this.cm.state.panels; - this.cm._setSize(null, info.height += (newHeight - this.height)); - this.height = newHeight; - }; - - function initPanels(cm) { - var wrap = cm.getWrapperElement(); - var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle; - var height = parseInt(style.height); - var info = cm.state.panels = { - setHeight: wrap.style.height, - heightLeft: height, - panels: 0, - wrapper: document.createElement("div") - }; - wrap.parentNode.insertBefore(info.wrapper, wrap); - var hasFocus = cm.hasFocus(); - info.wrapper.appendChild(wrap); - if (hasFocus) cm.focus(); - - cm._setSize = cm.setSize; - if (height != null) cm.setSize = function(width, newHeight) { - if (newHeight == null) return this._setSize(width, newHeight); - info.setHeight = newHeight; - if (typeof newHeight != "number") { - var px = /^(\d+\.?\d*)px$/.exec(newHeight); - if (px) { - newHeight = Number(px[1]); - } else { - info.wrapper.style.height = newHeight; - newHeight = info.wrapper.offsetHeight; - info.wrapper.style.height = ""; - } - } - cm._setSize(width, info.heightLeft += (newHeight - height)); - height = newHeight; - }; - } - - function removePanels(cm) { - var info = cm.state.panels; - cm.state.panels = null; - - var wrap = cm.getWrapperElement(); - info.wrapper.parentNode.replaceChild(wrap, info.wrapper); - wrap.style.height = info.setHeight; - cm.setSize = cm._setSize; - cm.setSize(); - } -}); diff --git a/public/js/lib/codemirror/addon/display/placeholder.js b/public/js/lib/codemirror/addon/display/placeholder.js deleted file mode 100644 index bb0c3931e4..0000000000 --- a/public/js/lib/codemirror/addon/display/placeholder.js +++ /dev/null @@ -1,58 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - CodeMirror.defineOption("placeholder", "", function(cm, val, old) { - var prev = old && old != CodeMirror.Init; - if (val && !prev) { - cm.on("blur", onBlur); - cm.on("change", onChange); - onChange(cm); - } else if (!val && prev) { - cm.off("blur", onBlur); - cm.off("change", onChange); - clearPlaceholder(cm); - var wrapper = cm.getWrapperElement(); - wrapper.className = wrapper.className.replace(" CodeMirror-empty", ""); - } - - if (val && !cm.hasFocus()) onBlur(cm); - }); - - function clearPlaceholder(cm) { - if (cm.state.placeholder) { - cm.state.placeholder.parentNode.removeChild(cm.state.placeholder); - cm.state.placeholder = null; - } - } - function setPlaceholder(cm) { - clearPlaceholder(cm); - var elt = cm.state.placeholder = document.createElement("pre"); - elt.style.cssText = "height: 0; overflow: visible"; - elt.className = "CodeMirror-placeholder"; - elt.appendChild(document.createTextNode(cm.getOption("placeholder"))); - cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild); - } - - function onBlur(cm) { - if (isEmpty(cm)) setPlaceholder(cm); - } - function onChange(cm) { - var wrapper = cm.getWrapperElement(), empty = isEmpty(cm); - wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : ""); - - if (empty) setPlaceholder(cm); - else clearPlaceholder(cm); - } - - function isEmpty(cm) { - return (cm.lineCount() === 1) && (cm.getLine(0) === ""); - } -}); diff --git a/public/js/lib/codemirror/addon/display/rulers.js b/public/js/lib/codemirror/addon/display/rulers.js deleted file mode 100644 index 13185d30b4..0000000000 --- a/public/js/lib/codemirror/addon/display/rulers.js +++ /dev/null @@ -1,64 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - CodeMirror.defineOption("rulers", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) { - clearRulers(cm); - cm.off("refresh", refreshRulers); - } - if (val && val.length) { - setRulers(cm); - cm.on("refresh", refreshRulers); - } - }); - - function clearRulers(cm) { - for (var i = cm.display.lineSpace.childNodes.length - 1; i >= 0; i--) { - var node = cm.display.lineSpace.childNodes[i]; - if (/(^|\s)CodeMirror-ruler($|\s)/.test(node.className)) - node.parentNode.removeChild(node); - } - } - - function setRulers(cm) { - var val = cm.getOption("rulers"); - var cw = cm.defaultCharWidth(); - var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left; - var minH = cm.display.scroller.offsetHeight + 30; - for (var i = 0; i < val.length; i++) { - var elt = document.createElement("div"); - elt.className = "CodeMirror-ruler"; - var col, cls = null, conf = val[i]; - if (typeof conf == "number") { - col = conf; - } else { - col = conf.column; - if (conf.className) elt.className += " " + conf.className; - if (conf.color) elt.style.borderColor = conf.color; - if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle; - if (conf.width) elt.style.borderLeftWidth = conf.width; - cls = val[i].className; - } - elt.style.left = (left + col * cw) + "px"; - elt.style.top = "-50px"; - elt.style.bottom = "-20px"; - elt.style.minHeight = minH + "px"; - cm.display.lineSpace.insertBefore(elt, cm.display.cursorDiv); - } - } - - function refreshRulers(cm) { - clearRulers(cm); - setRulers(cm); - } -}); diff --git a/public/js/lib/codemirror/addon/edit/closebrackets.js b/public/js/lib/codemirror/addon/edit/closebrackets.js deleted file mode 100644 index f6b42f02d3..0000000000 --- a/public/js/lib/codemirror/addon/edit/closebrackets.js +++ /dev/null @@ -1,159 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - var DEFAULT_BRACKETS = "()[]{}''\"\""; - var DEFAULT_EXPLODE_ON_ENTER = "[]{}"; - var SPACE_CHAR_REGEX = /\s/; - - var Pos = CodeMirror.Pos; - - CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) { - if (old != CodeMirror.Init && old) - cm.removeKeyMap("autoCloseBrackets"); - if (!val) return; - var pairs = DEFAULT_BRACKETS, explode = DEFAULT_EXPLODE_ON_ENTER; - if (typeof val == "string") pairs = val; - else if (typeof val == "object") { - if (val.pairs != null) pairs = val.pairs; - if (val.explode != null) explode = val.explode; - } - var map = buildKeymap(pairs); - if (explode) map.Enter = buildExplodeHandler(explode); - cm.addKeyMap(map); - }); - - function charsAround(cm, pos) { - var str = cm.getRange(Pos(pos.line, pos.ch - 1), - Pos(pos.line, pos.ch + 1)); - return str.length == 2 ? str : null; - } - - // Project the token type that will exists after the given char is - // typed, and use it to determine whether it would cause the start - // of a string token. - function enteringString(cm, pos, ch) { - var line = cm.getLine(pos.line); - var token = cm.getTokenAt(pos); - if (/\bstring2?\b/.test(token.type)) return false; - var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4); - stream.pos = stream.start = token.start; - for (;;) { - var type1 = cm.getMode().token(stream, token.state); - if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1); - stream.start = stream.pos; - } - } - - function buildKeymap(pairs) { - var map = { - name : "autoCloseBrackets", - Backspace: function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var around = charsAround(cm, ranges[i].head); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; - } - for (var i = ranges.length - 1; i >= 0; i--) { - var cur = ranges[i].head; - cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1)); - } - } - }; - var closingBrackets = ""; - for (var i = 0; i < pairs.length; i += 2) (function(left, right) { - closingBrackets += right; - map["'" + left + "'"] = function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), type, next; - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i], cur = range.head, curType; - var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1)); - if (!range.empty()) { - curType = "surround"; - } else if (left == right && next == right) { - if (cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == left + left + left) - curType = "skipThree"; - else - curType = "skip"; - } else if (left == right && cur.ch > 1 && - cm.getRange(Pos(cur.line, cur.ch - 2), cur) == left + left && - (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != left)) { - curType = "addFour"; - } else if (left == '"' || left == "'") { - if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, left)) curType = "both"; - else return CodeMirror.Pass; - } else if (cm.getLine(cur.line).length == cur.ch || closingBrackets.indexOf(next) >= 0 || SPACE_CHAR_REGEX.test(next)) { - curType = "both"; - } else { - return CodeMirror.Pass; - } - if (!type) type = curType; - else if (type != curType) return CodeMirror.Pass; - } - - cm.operation(function() { - if (type == "skip") { - cm.execCommand("goCharRight"); - } else if (type == "skipThree") { - for (var i = 0; i < 3; i++) - cm.execCommand("goCharRight"); - } else if (type == "surround") { - var sels = cm.getSelections(); - for (var i = 0; i < sels.length; i++) - sels[i] = left + sels[i] + right; - cm.replaceSelections(sels, "around"); - } else if (type == "both") { - cm.replaceSelection(left + right, null); - cm.execCommand("goCharLeft"); - } else if (type == "addFour") { - cm.replaceSelection(left + left + left + left, "before"); - cm.execCommand("goCharRight"); - } - }); - }; - if (left != right) map["'" + right + "'"] = function(cm) { - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var range = ranges[i]; - if (!range.empty() || - cm.getRange(range.head, Pos(range.head.line, range.head.ch + 1)) != right) - return CodeMirror.Pass; - } - cm.execCommand("goCharRight"); - }; - })(pairs.charAt(i), pairs.charAt(i + 1)); - return map; - } - - function buildExplodeHandler(pairs) { - return function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var around = charsAround(cm, ranges[i].head); - if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass; - } - cm.operation(function() { - cm.replaceSelection("\n\n", null); - cm.execCommand("goCharLeft"); - ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var line = ranges[i].head.line; - cm.indentLine(line, null, true); - cm.indentLine(line + 1, null, true); - } - }); - }; - } -}); diff --git a/public/js/lib/codemirror/addon/edit/closetag.js b/public/js/lib/codemirror/addon/edit/closetag.js deleted file mode 100644 index a0bec7dd43..0000000000 --- a/public/js/lib/codemirror/addon/edit/closetag.js +++ /dev/null @@ -1,159 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -/** - * Tag-closer extension for CodeMirror. - * - * This extension adds an "autoCloseTags" option that can be set to - * either true to get the default behavior, or an object to further - * configure its behavior. - * - * These are supported options: - * - * `whenClosing` (default true) - * Whether to autoclose when the '/' of a closing tag is typed. - * `whenOpening` (default true) - * Whether to autoclose the tag when the final '>' of an opening - * tag is typed. - * `dontCloseTags` (default is empty tags for HTML, none for XML) - * An array of tag names that should not be autoclosed. - * `indentTags` (default is block tags for HTML, none for XML) - * An array of tag names that should, when opened, cause a - * blank line to be added inside the tag, and the blank line and - * closing line to be indented. - * - * See demos/closetag.html for a usage example. - */ - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror"), require("../fold/xml-fold")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror", "../fold/xml-fold"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) { - if (old != CodeMirror.Init && old) - cm.removeKeyMap("autoCloseTags"); - if (!val) return; - var map = {name: "autoCloseTags"}; - if (typeof val != "object" || val.whenClosing) - map["'/'"] = function(cm) { return autoCloseSlash(cm); }; - if (typeof val != "object" || val.whenOpening) - map["'>'"] = function(cm) { return autoCloseGT(cm); }; - cm.addKeyMap(map); - }); - - var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", - "source", "track", "wbr"]; - var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4", - "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"]; - - function autoCloseGT(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), replacements = []; - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var pos = ranges[i].head, tok = cm.getTokenAt(pos); - var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (inner.mode.name != "xml" || !state.tagName) return CodeMirror.Pass; - - var opt = cm.getOption("autoCloseTags"), html = inner.mode.configuration == "html"; - var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose); - var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent); - - var tagName = state.tagName; - if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch); - var lowerTagName = tagName.toLowerCase(); - // Don't process the '>' at the end of an end-tag or self-closing tag - if (!tagName || - tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) || - tok.type == "tag" && state.type == "closeTag" || - tok.string.indexOf("/") == (tok.string.length - 1) || // match something like - dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 || - closingTagExists(cm, tagName, pos, state, true)) - return CodeMirror.Pass; - - var indent = indentTags && indexOf(indentTags, lowerTagName) > -1; - replacements[i] = {indent: indent, - text: ">" + (indent ? "\n\n" : "") + "", - newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)}; - } - - for (var i = ranges.length - 1; i >= 0; i--) { - var info = replacements[i]; - cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert"); - var sel = cm.listSelections().slice(0); - sel[i] = {head: info.newPos, anchor: info.newPos}; - cm.setSelections(sel); - if (info.indent) { - cm.indentLine(info.newPos.line, null, true); - cm.indentLine(info.newPos.line + 1, null, true); - } - } - } - - function autoCloseSlash(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), replacements = []; - for (var i = 0; i < ranges.length; i++) { - if (!ranges[i].empty()) return CodeMirror.Pass; - var pos = ranges[i].head, tok = cm.getTokenAt(pos); - var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state; - if (tok.type == "string" || tok.string.charAt(0) != "<" || - tok.start != pos.ch - 1) - return CodeMirror.Pass; - // Kludge to get around the fact that we are not in XML mode - // when completing in JS/CSS snippet in htmlmixed mode. Does not - // work for other XML embedded languages (there is no general - // way to go from a mixed mode to its current XML state). - if (inner.mode.name != "xml") { - if (cm.getMode().name == "htmlmixed" && inner.mode.name == "javascript") - replacements[i] = "/script>"; - else if (cm.getMode().name == "htmlmixed" && inner.mode.name == "css") - replacements[i] = "/style>"; - else - return CodeMirror.Pass; - } else { - if (!state.context || !state.context.tagName || - closingTagExists(cm, state.context.tagName, pos, state)) - return CodeMirror.Pass; - replacements[i] = "/" + state.context.tagName + ">"; - } - } - cm.replaceSelections(replacements); - ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) - if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line) - cm.indentLine(ranges[i].head.line); - } - - function indexOf(collection, elt) { - if (collection.indexOf) return collection.indexOf(elt); - for (var i = 0, e = collection.length; i < e; ++i) - if (collection[i] == elt) return i; - return -1; - } - - // If xml-fold is loaded, we use its functionality to try and verify - // whether a given tag is actually unclosed. - function closingTagExists(cm, tagName, pos, state, newTag) { - if (!CodeMirror.scanForClosingTag) return false; - var end = Math.min(cm.lastLine() + 1, pos.line + 500); - var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end); - if (!nextClose || nextClose.tag != tagName) return false; - var cx = state.context; - // If the immediate wrapping context contains onCx instances of - // the same tag, a closing tag only exists if there are at least - // that many closing tags of that type following. - for (var onCx = newTag ? 1 : 0; cx && cx.tagName == tagName; cx = cx.prev) ++onCx; - pos = nextClose.to; - for (var i = 1; i < onCx; i++) { - var next = CodeMirror.scanForClosingTag(cm, pos, null, end); - if (!next || next.tag != tagName) return false; - pos = next.to; - } - return true; - } -}); diff --git a/public/js/lib/codemirror/addon/edit/continuelist.js b/public/js/lib/codemirror/addon/edit/continuelist.js deleted file mode 100644 index ca8d26751a..0000000000 --- a/public/js/lib/codemirror/addon/edit/continuelist.js +++ /dev/null @@ -1,51 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - var listRE = /^(\s*)(>[> ]*|[*+-]\s|(\d+)\.)(\s*)/, - emptyListRE = /^(\s*)(>[> ]*|[*+-]|(\d+)\.)(\s*)$/, - unorderedListRE = /[*+-]\s/; - - CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) { - if (cm.getOption("disableInput")) return CodeMirror.Pass; - var ranges = cm.listSelections(), replacements = []; - for (var i = 0; i < ranges.length; i++) { - var pos = ranges[i].head, match; - var eolState = cm.getStateAfter(pos.line); - var inList = eolState.list !== false; - var inQuote = eolState.quote !== false; - - if (!ranges[i].empty() || (!inList && !inQuote) || !(match = cm.getLine(pos.line).match(listRE))) { - cm.execCommand("newlineAndIndent"); - return; - } - if (cm.getLine(pos.line).match(emptyListRE)) { - cm.replaceRange("", { - line: pos.line, ch: 0 - }, { - line: pos.line, ch: pos.ch + 1 - }); - replacements[i] = "\n"; - - } else { - var indent = match[1], after = match[4]; - var bullet = unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0 - ? match[2] - : (parseInt(match[3], 10) + 1) + "."; - - replacements[i] = "\n" + indent + bullet + after; - } - } - - cm.replaceSelections(replacements); - }; -}); diff --git a/public/js/lib/codemirror/addon/edit/matchbrackets.js b/public/js/lib/codemirror/addon/edit/matchbrackets.js deleted file mode 100644 index fa1ae030a5..0000000000 --- a/public/js/lib/codemirror/addon/edit/matchbrackets.js +++ /dev/null @@ -1,120 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - var ie_lt8 = /MSIE \d/.test(navigator.userAgent) && - (document.documentMode == null || document.documentMode < 8); - - var Pos = CodeMirror.Pos; - - var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; - - function findMatchingBracket(cm, where, strict, config) { - var line = cm.getLineHandle(where.line), pos = where.ch - 1; - var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; - if (!match) return null; - var dir = match.charAt(1) == ">" ? 1 : -1; - if (strict && (dir > 0) != (pos == where.ch)) return null; - var style = cm.getTokenTypeAt(Pos(where.line, pos + 1)); - - var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config); - if (found == null) return null; - return {from: Pos(where.line, pos), to: found && found.pos, - match: found && found.ch == match.charAt(0), forward: dir > 0}; - } - - // bracketRegex is used to specify which type of bracket to scan - // should be a regexp, e.g. /[[\]]/ - // - // Note: If "where" is on an open bracket, then this bracket is ignored. - // - // Returns false when no bracket was found, null when it reached - // maxScanLines and gave up - function scanForBracket(cm, where, dir, style, config) { - var maxScanLen = (config && config.maxScanLineLength) || 10000; - var maxScanLines = (config && config.maxScanLines) || 1000; - - var stack = []; - var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/; - var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1) - : Math.max(cm.firstLine() - 1, where.line - maxScanLines); - for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) { - var line = cm.getLine(lineNo); - if (!line) continue; - var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1; - if (line.length > maxScanLen) continue; - if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0); - for (; pos != end; pos += dir) { - var ch = line.charAt(pos); - if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) { - var match = matching[ch]; - if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch); - else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch}; - else stack.pop(); - } - } - } - return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null; - } - - function matchBrackets(cm, autoclear, config) { - // Disable brace matching in long lines, since it'll cause hugely slow updates - var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000; - var marks = [], ranges = cm.listSelections(); - for (var i = 0; i < ranges.length; i++) { - var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config); - if (match && cm.getLine(match.from.line).length <= maxHighlightLen) { - var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; - marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style})); - if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen) - marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style})); - } - } - - if (marks.length) { - // Kludge to work around the IE bug from issue #1193, where text - // input stops going to the textare whever this fires. - if (ie_lt8 && cm.state.focused) cm.display.input.focus(); - - var clear = function() { - cm.operation(function() { - for (var i = 0; i < marks.length; i++) marks[i].clear(); - }); - }; - if (autoclear) setTimeout(clear, 800); - else return clear; - } - } - - var currentlyHighlighted = null; - function doMatchBrackets(cm) { - cm.operation(function() { - if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;} - currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets); - }); - } - - CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) - cm.off("cursorActivity", doMatchBrackets); - if (val) { - cm.state.matchBrackets = typeof val == "object" ? val : {}; - cm.on("cursorActivity", doMatchBrackets); - } - }); - - CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);}); - CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){ - return findMatchingBracket(this, pos, strict, config); - }); - CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){ - return scanForBracket(this, pos, dir, style, config); - }); -}); diff --git a/public/js/lib/codemirror/addon/edit/matchtags.js b/public/js/lib/codemirror/addon/edit/matchtags.js deleted file mode 100644 index fb1911a8db..0000000000 --- a/public/js/lib/codemirror/addon/edit/matchtags.js +++ /dev/null @@ -1,66 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror"), require("../fold/xml-fold")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror", "../fold/xml-fold"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - CodeMirror.defineOption("matchTags", false, function(cm, val, old) { - if (old && old != CodeMirror.Init) { - cm.off("cursorActivity", doMatchTags); - cm.off("viewportChange", maybeUpdateMatch); - clear(cm); - } - if (val) { - cm.state.matchBothTags = typeof val == "object" && val.bothTags; - cm.on("cursorActivity", doMatchTags); - cm.on("viewportChange", maybeUpdateMatch); - doMatchTags(cm); - } - }); - - function clear(cm) { - if (cm.state.tagHit) cm.state.tagHit.clear(); - if (cm.state.tagOther) cm.state.tagOther.clear(); - cm.state.tagHit = cm.state.tagOther = null; - } - - function doMatchTags(cm) { - cm.state.failedTagMatch = false; - cm.operation(function() { - clear(cm); - if (cm.somethingSelected()) return; - var cur = cm.getCursor(), range = cm.getViewport(); - range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to); - var match = CodeMirror.findMatchingTag(cm, cur, range); - if (!match) return; - if (cm.state.matchBothTags) { - var hit = match.at == "open" ? match.open : match.close; - if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"}); - } - var other = match.at == "close" ? match.open : match.close; - if (other) - cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"}); - else - cm.state.failedTagMatch = true; - }); - } - - function maybeUpdateMatch(cm) { - if (cm.state.failedTagMatch) doMatchTags(cm); - } - - CodeMirror.commands.toMatchingTag = function(cm) { - var found = CodeMirror.findMatchingTag(cm, cm.getCursor()); - if (found) { - var other = found.at == "close" ? found.open : found.close; - if (other) cm.extendSelection(other.to, other.from); - } - }; -}); diff --git a/public/js/lib/codemirror/addon/edit/trailingspace.js b/public/js/lib/codemirror/addon/edit/trailingspace.js deleted file mode 100644 index fa7b56be51..0000000000 --- a/public/js/lib/codemirror/addon/edit/trailingspace.js +++ /dev/null @@ -1,27 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) { - if (prev == CodeMirror.Init) prev = false; - if (prev && !val) - cm.removeOverlay("trailingspace"); - else if (!prev && val) - cm.addOverlay({ - token: function(stream) { - for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {} - if (i > stream.pos) { stream.pos = i; return null; } - stream.pos = l; - return "trailingspace"; - }, - name: "trailingspace" - }); - }); -}); diff --git a/public/js/lib/codemirror/addon/emmet/emmet.js b/public/js/lib/codemirror/addon/emmet/emmet.js deleted file mode 100644 index a5e8042fe8..0000000000 --- a/public/js/lib/codemirror/addon/emmet/emmet.js +++ /dev/null @@ -1,43080 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.emmetCodeMirror=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 2 && typeof pos !== "object") { - pos = { line: arguments[1], ch: arguments[2] }; - } - return cm.indexFromPos(pos); -} - -/** - * Converts charater index in text to CM’s internal object representation - * @param {CodeMirror} cm CodeMirror instance - * @param {Number} ix Character index in CM document - * @return {Object} - */ -function indexToPos(cm, ix) { - return cm.posFromIndex(ix); -} -},{"./emmet":2}],2:[function(require,module,exports){ -"use strict"; - -var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; }; - -var emmet = _interopRequire(require("emmet")); - -require("emmet/bundles/snippets"); - -require("emmet/bundles/caniuse"); - -module.exports = emmet; -},{"emmet":39,"emmet/bundles/caniuse":3,"emmet/bundles/snippets":4}],3:[function(require,module,exports){ -/** - * Bundler, used in builder script to statically - * include optimized caniuse.json into bundle - */ -var ciu = require('../lib/assets/caniuse'); -var db = require('../lib/caniuse.json'); -ciu.load(db, true); -},{"../lib/assets/caniuse":23,"../lib/caniuse.json":35}],4:[function(require,module,exports){ -/** - * Bundler, used in builder script to statically - * include snippets.json into bundle - */ -var res = require('../lib/assets/resources'); -var snippets = require('../lib/snippets.json'); -res.setVocabulary(snippets, 'system'); - -},{"../lib/assets/resources":31,"../lib/snippets.json":68}],5:[function(require,module,exports){ -/** - * HTML pair matching (balancing) actions - * @constructor - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var htmlMatcher = require('../assets/htmlMatcher'); - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var range = require('../assets/range'); - var cssEditTree = require('../editTree/css'); - var cssSections = require('../utils/cssSections'); - var lastMatch = null; - - function last(arr) { - return arr[arr.length - 1]; - } - - function balanceHTML(editor, direction) { - var info = editorUtils.outputInfo(editor); - var content = info.content; - var sel = range(editor.getSelectionRange()); - - // validate previous match - if (lastMatch && !lastMatch.range.equal(sel)) { - lastMatch = null; - } - - if (lastMatch && sel.length()) { - if (direction == 'in') { - // user has previously selected tag and wants to move inward - if (lastMatch.type == 'tag' && !lastMatch.close) { - // unary tag was selected, can't move inward - return false; - } else { - if (lastMatch.range.equal(lastMatch.outerRange)) { - lastMatch.range = lastMatch.innerRange; - } else { - var narrowed = utils.narrowToNonSpace(content, lastMatch.innerRange); - lastMatch = htmlMatcher.find(content, narrowed.start + 1); - if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) { - lastMatch.range = lastMatch.innerRange; - } - } - } - } else { - if ( - !lastMatch.innerRange.equal(lastMatch.outerRange) - && lastMatch.range.equal(lastMatch.innerRange) - && sel.equal(lastMatch.range)) { - lastMatch.range = lastMatch.outerRange; - } else { - lastMatch = htmlMatcher.find(content, sel.start); - if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) { - lastMatch.range = lastMatch.outerRange; - } - } - } - } else { - lastMatch = htmlMatcher.find(content, sel.start); - } - - if (lastMatch) { - if (lastMatch.innerRange.equal(sel)) { - lastMatch.range = lastMatch.outerRange; - } - - if (!lastMatch.range.equal(sel)) { - editor.createSelection(lastMatch.range.start, lastMatch.range.end); - return true; - } - } - - lastMatch = null; - return false; - } - - function rangesForCSSRule(rule, pos) { - // find all possible ranges - var ranges = [rule.range(true)]; - - // braces content - ranges.push(rule.valueRange(true)); - - // find nested sections - var nestedSections = cssSections.nestedSectionsInRule(rule); - - // real content, e.g. from first property name to - // last property value - var items = rule.list(); - if (items.length || nestedSections.length) { - var start = Number.POSITIVE_INFINITY, end = -1; - if (items.length) { - start = items[0].namePosition(true); - end = last(items).range(true).end; - } - - if (nestedSections.length) { - if (nestedSections[0].start < start) { - start = nestedSections[0].start; - } - - if (last(nestedSections).end > end) { - end = last(nestedSections).end; - } - } - - ranges.push(range.create2(start, end)); - } - - ranges = ranges.concat(nestedSections); - - var prop = cssEditTree.propertyFromPosition(rule, pos) || items[0]; - if (prop) { - ranges.push(prop.range(true)); - var valueRange = prop.valueRange(true); - if (!prop.end()) { - valueRange._unterminated = true; - } - ranges.push(valueRange); - } - - return ranges; - } - - /** - * Returns all possible selection ranges for given caret position - * @param {String} content CSS content - * @param {Number} pos Caret position(where to start searching) - * @return {Array} - */ - function getCSSRanges(content, pos) { - var rule; - if (typeof content === 'string') { - var ruleRange = cssSections.matchEnclosingRule(content, pos); - if (ruleRange) { - rule = cssEditTree.parse(ruleRange.substring(content), { - offset: ruleRange.start - }); - } - } else { - // passed parsed CSS rule - rule = content; - } - - if (!rule) { - return null; - } - - // find all possible ranges - var ranges = rangesForCSSRule(rule, pos); - - // remove empty ranges - ranges = ranges.filter(function(item) { - return !!item.length; - }); - - return utils.unique(ranges, function(item) { - return item.valueOf(); - }); - } - - function balanceCSS(editor, direction) { - var info = editorUtils.outputInfo(editor); - var content = info.content; - var sel = range(editor.getSelectionRange()); - - var ranges = getCSSRanges(info.content, sel.start); - if (!ranges && sel.length()) { - // possible reason: user has already selected - // CSS rule from last match - try { - var rule = cssEditTree.parse(sel.substring(info.content), { - offset: sel.start - }); - ranges = getCSSRanges(rule, sel.start); - } catch(e) {} - } - - if (!ranges) { - return false; - } - - ranges = range.sort(ranges, true); - - // edge case: find match that equals current selection, - // in case if user moves inward after selecting full CSS rule - var bestMatch = utils.find(ranges, function(r) { - return r.equal(sel); - }); - - if (!bestMatch) { - bestMatch = utils.find(ranges, function(r) { - // Check for edge case: caret right after CSS value - // but it doesn‘t contains terminating semicolon. - // In this case we have to check full value range - return r._unterminated ? r.include(sel.start) : r.inside(sel.start); - }); - } - - if (!bestMatch) { - return false; - } - - // if best match equals to current selection, move index - // one position up or down, depending on direction - var bestMatchIx = ranges.indexOf(bestMatch); - if (bestMatch.equal(sel)) { - bestMatchIx += direction == 'out' ? 1 : -1; - } - - if (bestMatchIx < 0 || bestMatchIx >= ranges.length) { - if (bestMatchIx >= ranges.length && direction == 'out') { - pos = bestMatch.start - 1; - - var outerRanges = getCSSRanges(content, pos); - if (outerRanges) { - bestMatch = last(outerRanges.filter(function(r) { - return r.inside(pos); - })); - } - } else if (bestMatchIx < 0 && direction == 'in') { - bestMatch = null; - } else { - bestMatch = null; - } - } else { - bestMatch = ranges[bestMatchIx]; - } - - if (bestMatch) { - editor.createSelection(bestMatch.start, bestMatch.end); - return true; - } - - return false; - } - - return { - /** - * Find and select HTML tag pair - * @param {IEmmetEditor} editor Editor instance - * @param {String} direction Direction of pair matching: 'in' or 'out'. - * Default is 'out' - */ - balance: function(editor, direction) { - direction = String((direction || 'out').toLowerCase()); - var info = editorUtils.outputInfo(editor); - if (actionUtils.isSupportedCSS(info.syntax)) { - return balanceCSS(editor, direction); - } - - return balanceHTML(editor, direction); - }, - - balanceInwardAction: function(editor) { - return this.balance(editor, 'in'); - }, - - balanceOutwardAction: function(editor) { - return this.balance(editor, 'out'); - }, - - /** - * Moves caret to matching opening or closing tag - * @param {IEmmetEditor} editor - */ - goToMatchingPairAction: function(editor) { - var content = String(editor.getContent()); - var caretPos = editor.getCaretPos(); - - if (content.charAt(caretPos) == '<') - // looks like caret is outside of tag pair - caretPos++; - - var tag = htmlMatcher.tag(content, caretPos); - if (tag && tag.close) { // exclude unary tags - if (tag.open.range.inside(caretPos)) { - editor.setCaretPos(tag.close.range.start); - } else { - editor.setCaretPos(tag.open.range.start); - } - - return true; - } - - return false; - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/range":30,"../editTree/css":37,"../utils/action":70,"../utils/common":73,"../utils/cssSections":74,"../utils/editor":75}],6:[function(require,module,exports){ -/** - * Encodes/decodes image under cursor to/from base64 - * @param {IEmmetEditor} editor - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var file = require('../plugin/file'); - var base64 = require('../utils/base64'); - var actionUtils = require('../utils/action'); - var editorUtils = require('../utils/editor'); - - /** - * Test if text starts with token at pos - * position. If pos is omitted, search from beginning of text - * @param {String} token Token to test - * @param {String} text Where to search - * @param {Number} pos Position where to start search - * @return {Boolean} - * @since 0.65 - */ - function startsWith(token, text, pos) { - pos = pos || 0; - return text.charAt(pos) == token.charAt(0) && text.substr(pos, token.length) == token; - } - - /** - * Encodes image to base64 - * - * @param {IEmmetEditor} editor - * @param {String} imgPath Path to image - * @param {Number} pos Caret position where image is located in the editor - * @return {Boolean} - */ - function encodeToBase64(editor, imgPath, pos) { - var editorFile = editor.getFilePath(); - var defaultMimeType = 'application/octet-stream'; - - if (editorFile === null) { - throw "You should save your file before using this action"; - } - - // locate real image path - var realImgPath = file.locateFile(editorFile, imgPath); - if (realImgPath === null) { - throw "Can't find " + imgPath + ' file'; - } - - file.read(realImgPath, function(err, content) { - if (err) { - throw 'Unable to read ' + realImgPath + ': ' + err; - } - - var b64 = base64.encode(String(content)); - if (!b64) { - throw "Can't encode file content to base64"; - } - - b64 = 'data:' + (actionUtils.mimeTypes[String(file.getExt(realImgPath))] || defaultMimeType) + - ';base64,' + b64; - - editor.replaceContent('$0' + b64, pos, pos + imgPath.length); - }); - - return true; - } - - /** - * Decodes base64 string back to file. - * @param {IEmmetEditor} editor - * @param {String} data Base64-encoded file content - * @param {Number} pos Caret position where image is located in the editor - */ - function decodeFromBase64(editor, data, pos) { - // ask user to enter path to file - var filePath = String(editor.prompt('Enter path to file (absolute or relative)')); - if (!filePath) - return false; - - var absPath = file.createPath(editor.getFilePath(), filePath); - if (!absPath) { - throw "Can't save file"; - } - - file.save(absPath, base64.decode( data.replace(/^data\:.+?;.+?,/, '') )); - editor.replaceContent('$0' + filePath, pos, pos + data.length); - return true; - } - - return { - /** - * Action to encode or decode file to data:url - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Current document syntax - * @param {String} profile Output profile name - * @return {Boolean} - */ - encodeDecodeDataUrlAction: function(editor) { - var data = String(editor.getSelection()); - var caretPos = editor.getCaretPos(); - var info = editorUtils.outputInfo(editor); - - if (!data) { - // no selection, try to find image bounds from current caret position - var text = info.content, m; - while (caretPos-- >= 0) { - if (startsWith('src=', text, caretPos)) { // found - if ((m = text.substr(caretPos).match(/^(src=(["'])?)([^'"<>\s]+)\1?/))) { - data = m[3]; - caretPos += m[1].length; - } - break; - } else if (startsWith('url(', text, caretPos)) { // found CSS url() pattern - if ((m = text.substr(caretPos).match(/^(url\((['"])?)([^'"\)\s]+)\1?/))) { - data = m[3]; - caretPos += m[1].length; - } - break; - } - } - } - - if (data) { - if (startsWith('data:', data)) { - return decodeFromBase64(editor, data, caretPos); - } else { - return encodeToBase64(editor, data, caretPos); - } - } - - return false; - } - }; -}); - -},{"../plugin/file":63,"../utils/action":70,"../utils/base64":71,"../utils/editor":75}],7:[function(require,module,exports){ -/** - * Move between next/prev edit points. 'Edit points' are places between tags - * and quotes of empty attributes in html - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - /** - * Search for new caret insertion point - * @param {IEmmetEditor} editor Editor instance - * @param {Number} inc Search increment: -1 — search left, 1 — search right - * @param {Number} offset Initial offset relative to current caret position - * @return {Number} Returns -1 if insertion point wasn't found - */ - function findNewEditPoint(editor, inc, offset) { - inc = inc || 1; - offset = offset || 0; - - var curPoint = editor.getCaretPos() + offset; - var content = String(editor.getContent()); - var maxLen = content.length; - var nextPoint = -1; - var reEmptyLine = /^\s+$/; - - function getLine(ix) { - var start = ix; - while (start >= 0) { - var c = content.charAt(start); - if (c == '\n' || c == '\r') - break; - start--; - } - - return content.substring(start, ix); - } - - while (curPoint <= maxLen && curPoint >= 0) { - curPoint += inc; - var curChar = content.charAt(curPoint); - var nextChar = content.charAt(curPoint + 1); - var prevChar = content.charAt(curPoint - 1); - - switch (curChar) { - case '"': - case '\'': - if (nextChar == curChar && prevChar == '=') { - // empty attribute - nextPoint = curPoint + 1; - } - break; - case '>': - if (nextChar == '<') { - // between tags - nextPoint = curPoint + 1; - } - break; - case '\n': - case '\r': - // empty line - if (reEmptyLine.test(getLine(curPoint - 1))) { - nextPoint = curPoint; - } - break; - } - - if (nextPoint != -1) - break; - } - - return nextPoint; - } - - return { - /** - * Move to previous edit point - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Current document syntax - * @param {String} profile Output profile name - * @return {Boolean} - */ - previousEditPointAction: function(editor, syntax, profile) { - var curPos = editor.getCaretPos(); - var newPoint = findNewEditPoint(editor, -1); - - if (newPoint == curPos) - // we're still in the same point, try searching from the other place - newPoint = findNewEditPoint(editor, -1, -2); - - if (newPoint != -1) { - editor.setCaretPos(newPoint); - return true; - } - - return false; - }, - - /** - * Move to next edit point - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Current document syntax - * @param {String} profile Output profile name - * @return {Boolean} - */ - nextEditPointAction: function(editor, syntax, profile) { - var newPoint = findNewEditPoint(editor, 1); - if (newPoint != -1) { - editor.setCaretPos(newPoint); - return true; - } - - return false; - } - }; -}); -},{}],8:[function(require,module,exports){ -/** - * Evaluates simple math expression under caret - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var actionUtils = require('../utils/action'); - var utils = require('../utils/common'); - var math = require('../utils/math'); - var range = require('../assets/range'); - - return { - /** - * Evaluates math expression under the caret - * @param {IEmmetEditor} editor - * @return {Boolean} - */ - evaluateMathAction: function(editor) { - var content = editor.getContent(); - var chars = '.+-*/\\'; - - /** @type Range */ - var sel = range(editor.getSelectionRange()); - if (!sel.length()) { - sel = actionUtils.findExpressionBounds(editor, function(ch) { - return utils.isNumeric(ch) || chars.indexOf(ch) != -1; - }); - } - - if (sel && sel.length()) { - var expr = sel.substring(content); - - // replace integral division: 11\2 => Math.round(11/2) - expr = expr.replace(/([\d\.\-]+)\\([\d\.\-]+)/g, 'round($1/$2)'); - - try { - var result = utils.prettifyNumber(math.evaluate(expr)); - editor.replaceContent(result, sel.start, sel.end); - editor.setCaretPos(sel.start + result.length); - return true; - } catch (e) {} - } - - return false; - } - }; -}); - -},{"../assets/range":30,"../utils/action":70,"../utils/common":73,"../utils/math":76}],9:[function(require,module,exports){ -/** - * 'Expand abbreviation' editor action: extracts abbreviation from current caret - * position and replaces it with formatted output. - *

- * This behavior can be overridden with custom handlers which can perform - * different actions when 'Expand Abbreviation' action is called. - * For example, a CSS gradient handler that produces vendor-prefixed gradient - * definitions registers its own expand abbreviation handler. - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var handlerList = require('../assets/handlerList'); - var range = require('../assets/range'); - var prefs = require('../assets/preferences'); - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var cssGradient = require('../resolver/cssGradient'); - var parser = require('../parser/abbreviation'); - - /** - * Search for abbreviation in editor from current caret position - * @param {IEmmetEditor} editor Editor instance - * @return {String} - */ - function findAbbreviation(editor) { - var r = range(editor.getSelectionRange()); - var content = String(editor.getContent()); - if (r.length()) { - // abbreviation is selected by user - return r.substring(content); - } - - // search for new abbreviation from current caret position - var curLine = editor.getCurrentLineRange(); - return actionUtils.extractAbbreviation(content.substring(curLine.start, r.start)); - } - - /** - * @type HandlerList List of registered handlers - */ - var handlers = handlerList.create(); - - // XXX setup default expand handlers - - /** - * Extracts abbreviation from current caret - * position and replaces it with formatted output - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Syntax type (html, css, etc.) - * @param {String} profile Output profile name (html, xml, xhtml) - * @return {Boolean} Returns true if abbreviation was expanded - * successfully - */ - handlers.add(function(editor, syntax, profile) { - var caretPos = editor.getSelectionRange().end; - var abbr = findAbbreviation(editor); - - if (abbr) { - var content = parser.expand(abbr, { - syntax: syntax, - profile: profile, - contextNode: actionUtils.captureContext(editor) - }); - - if (content) { - var replaceFrom = caretPos - abbr.length; - var replaceTo = caretPos; - - // a special case for CSS: if editor already contains - // semicolon right after current caret position — replace it too - var cssSyntaxes = prefs.getArray('css.syntaxes'); - if (cssSyntaxes && ~cssSyntaxes.indexOf(syntax)) { - var curContent = editor.getContent(); - if (curContent.charAt(caretPos) == ';' && content.charAt(content.length - 1) == ';') { - replaceTo++; - } - } - - editor.replaceContent(content, replaceFrom, replaceTo); - return true; - } - } - - return false; - }, {order: -1}); - handlers.add(cssGradient.expandAbbreviationHandler.bind(cssGradient)); - - return { - /** - * The actual “Expand Abbreviation“ action routine - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Current document syntax - * @param {String} profile Output profile name - * @return {Boolean} - */ - expandAbbreviationAction: function(editor, syntax, profile) { - var args = utils.toArray(arguments); - - // normalize incoming arguments - var info = editorUtils.outputInfo(editor, syntax, profile); - args[1] = info.syntax; - args[2] = info.profile; - - return handlers.exec(false, args); - }, - - /** - * A special case of “Expand Abbreviation“ action, invoked by Tab key. - * In this case if abbreviation wasn’t expanded successfully or there’s a selecetion, - * the current line/selection will be indented. - * @param {IEmmetEditor} editor Editor instance - * @param {String} syntax Current document syntax - * @param {String} profile Output profile name - * @return {Boolean} - */ - expandAbbreviationWithTabAction: function(editor, syntax, profile) { - var sel = editor.getSelection(); - var indent = '\t'; - - // if something is selected in editor, - // we should indent the selected content - if (sel) { - var selRange = range(editor.getSelectionRange()); - var content = utils.padString(sel, indent); - - editor.replaceContent(indent + '${0}', editor.getCaretPos()); - var replaceRange = range(editor.getCaretPos(), selRange.length()); - editor.replaceContent(content, replaceRange.start, replaceRange.end, true); - editor.createSelection(replaceRange.start, replaceRange.start + content.length); - return true; - } - - // nothing selected, try to expand - if (!this.expandAbbreviationAction(editor, syntax, profile)) { - editor.replaceContent(indent, editor.getCaretPos()); - } - - return true; - }, - - - _defaultHandler: function(editor, syntax, profile) { - var caretPos = editor.getSelectionRange().end; - var abbr = this.findAbbreviation(editor); - - if (abbr) { - var ctx = actionUtils.captureContext(editor); - var content = parser.expand(abbr, syntax, profile, ctx); - if (content) { - editor.replaceContent(content, caretPos - abbr.length, caretPos); - return true; - } - } - - return false; - }, - - /** - * Adds custom expand abbreviation handler. The passed function should - * return true if it was performed successfully, - * false otherwise. - * - * Added handlers will be called when 'Expand Abbreviation' is called - * in order they were added - * @memberOf expandAbbreviation - * @param {Function} fn - * @param {Object} options - */ - addHandler: function(fn, options) { - handlers.add(fn, options); - }, - - /** - * Removes registered handler - * @returns - */ - removeHandler: function(fn) { - handlers.remove(fn); - }, - - findAbbreviation: findAbbreviation - }; -}); -},{"../assets/handlerList":25,"../assets/preferences":28,"../assets/range":30,"../parser/abbreviation":55,"../resolver/cssGradient":65,"../utils/action":70,"../utils/common":73,"../utils/editor":75}],10:[function(require,module,exports){ -/** - * Increment/decrement number under cursor - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var actionUtils = require('../utils/action'); - - /** - * Returns length of integer part of number - * @param {String} num - */ - function intLength(num) { - num = num.replace(/^\-/, ''); - if (~num.indexOf('.')) { - return num.split('.')[0].length; - } - - return num.length; - } - - return { - increment01Action: function(editor) { - return this.incrementNumber(editor, .1); - }, - - increment1Action: function(editor) { - return this.incrementNumber(editor, 1); - }, - - increment10Action: function(editor) { - return this.incrementNumber(editor, 10); - }, - - decrement01Action: function(editor) { - return this.incrementNumber(editor, -.1); - }, - - decrement1Action: function(editor) { - return this.incrementNumber(editor, -1); - }, - - decrement10Action: function(editor) { - return this.incrementNumber(editor, -10); - }, - - /** - * Default method to increment/decrement number under - * caret with given step - * @param {IEmmetEditor} editor - * @param {Number} step - * @return {Boolean} - */ - incrementNumber: function(editor, step) { - var hasSign = false; - var hasDecimal = false; - - var r = actionUtils.findExpressionBounds(editor, function(ch, pos, content) { - if (utils.isNumeric(ch)) - return true; - if (ch == '.') { - // make sure that next character is numeric too - if (!utils.isNumeric(content.charAt(pos + 1))) - return false; - - return hasDecimal ? false : hasDecimal = true; - } - if (ch == '-') - return hasSign ? false : hasSign = true; - - return false; - }); - - if (r && r.length()) { - var strNum = r.substring(String(editor.getContent())); - var num = parseFloat(strNum); - if (!isNaN(num)) { - num = utils.prettifyNumber(num + step); - - // do we have zero-padded number? - if (/^(\-?)0+[1-9]/.test(strNum)) { - var minus = ''; - if (RegExp.$1) { - minus = '-'; - num = num.substring(1); - } - - var parts = num.split('.'); - parts[0] = utils.zeroPadString(parts[0], intLength(strNum)); - num = minus + parts.join('.'); - } - - editor.replaceContent(num, r.start, r.end); - editor.createSelection(r.start, r.start + num.length); - return true; - } - } - - return false; - } - }; -}); -},{"../utils/action":70,"../utils/common":73}],11:[function(require,module,exports){ -/** - * Actions to insert line breaks. Some simple editors (like browser's - * <textarea>, for example) do not provide such simple things - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var prefs = require('../assets/preferences'); - var utils = require('../utils/common'); - var resources = require('../assets/resources'); - var htmlMatcher = require('../assets/htmlMatcher'); - var editorUtils = require('../utils/editor'); - - var xmlSyntaxes = ['html', 'xml', 'xsl']; - - // setup default preferences - prefs.define('css.closeBraceIndentation', '\n', - 'Indentation before closing brace of CSS rule. Some users prefere ' - + 'indented closing brace of CSS rule for better readability. ' - + 'This preference’s value will be automatically inserted before ' - + 'closing brace when user adds newline in newly created CSS rule ' - + '(e.g. when “Insert formatted linebreak” action will be performed ' - + 'in CSS file). If you’re such user, you may want to write put a value ' - + 'like \\n\\t in this preference.'); - - return { - /** - * Inserts newline character with proper indentation. This action is used in - * editors that doesn't have indentation control (like textarea element) to - * provide proper indentation for inserted newlines - * @param {IEmmetEditor} editor Editor instance - */ - insertLineBreakAction: function(editor) { - if (!this.insertLineBreakOnlyAction(editor)) { - var curPadding = editorUtils.getCurrentLinePadding(editor); - var content = String(editor.getContent()); - var caretPos = editor.getCaretPos(); - var len = content.length; - var nl = '\n'; - - // check out next line padding - var lineRange = editor.getCurrentLineRange(); - var nextPadding = ''; - - for (var i = lineRange.end + 1, ch; i < len; i++) { - ch = content.charAt(i); - if (ch == ' ' || ch == '\t') - nextPadding += ch; - else - break; - } - - if (nextPadding.length > curPadding.length) { - editor.replaceContent(nl + nextPadding, caretPos, caretPos, true); - } else { - editor.replaceContent(nl, caretPos); - } - } - - return true; - }, - - /** - * Inserts newline character with proper indentation in specific positions only. - * @param {IEmmetEditor} editor - * @return {Boolean} Returns true if line break was inserted - */ - insertLineBreakOnlyAction: function(editor) { - var info = editorUtils.outputInfo(editor); - var caretPos = editor.getCaretPos(); - var nl = '\n'; - var pad = '\t'; - - if (~xmlSyntaxes.indexOf(info.syntax)) { - // let's see if we're breaking newly created tag - var tag = htmlMatcher.tag(info.content, caretPos); - if (tag && !tag.innerRange.length()) { - editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos); - return true; - } - } else if (info.syntax == 'css') { - /** @type String */ - var content = info.content; - if (caretPos && content.charAt(caretPos - 1) == '{') { - var append = prefs.get('css.closeBraceIndentation'); - - var hasCloseBrace = content.charAt(caretPos) == '}'; - if (!hasCloseBrace) { - // do we really need special formatting here? - // check if this is really a newly created rule, - // look ahead for a closing brace - for (var i = caretPos, il = content.length, ch; i < il; i++) { - ch = content.charAt(i); - if (ch == '{') { - // ok, this is a new rule without closing brace - break; - } - - if (ch == '}') { - // not a new rule, just add indentation - append = ''; - hasCloseBrace = true; - break; - } - } - } - - if (!hasCloseBrace) { - append += '}'; - } - - // defining rule set - var insValue = nl + pad + utils.getCaretPlaceholder() + append; - editor.replaceContent(insValue, caretPos); - return true; - } - } - - return false; - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/preferences":28,"../assets/resources":31,"../utils/common":73,"../utils/editor":75}],12:[function(require,module,exports){ -/** - * Module describes and performs Emmet actions. The actions themselves are - * defined in actions folder - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - - // all registered actions - var actions = {}; - - // load all default actions - var actionModules = { - base64: require('./base64'), - editPoints: require('./editPoints'), - evaluateMath: require('./evaluateMath'), - expandAbbreviation: require('./expandAbbreviation'), - incrementDecrement: require('./incrementDecrement'), - lineBreaks: require('./lineBreaks'), - balance: require('./balance'), - mergeLines: require('./mergeLines'), - reflectCSSValue: require('./reflectCSSValue'), - removeTag: require('./removeTag'), - selectItem: require('./selectItem'), - selectLine: require('./selectLine'), - splitJoinTag: require('./splitJoinTag'), - toggleComment: require('./toggleComment'), - updateImageSize: require('./updateImageSize'), - wrapWithAbbreviation: require('./wrapWithAbbreviation'), - updateTag: require('./updateTag') - }; - - function addAction(name, fn, options) { - name = name.toLowerCase(); - options = options || {}; - - if (typeof options === 'string') { - options = {label: options}; - } - - if (!options.label) { - options.label = humanizeActionName(name); - } - - actions[name] = { - name: name, - fn: fn, - options: options - }; - } - - /** - * “Humanizes” action name, makes it more readable for people - * @param {String} name Action name (like 'expand_abbreviation') - * @return Humanized name (like 'Expand Abbreviation') - */ - function humanizeActionName(name) { - return utils.trim(name.charAt(0).toUpperCase() - + name.substring(1).replace(/_[a-z]/g, function(str) { - return ' ' + str.charAt(1).toUpperCase(); - })); - } - - var bind = function(name, method) { - var m = actionModules[name]; - return m[method].bind(m); - }; - - // XXX register default actions - addAction('encode_decode_data_url', bind('base64', 'encodeDecodeDataUrlAction'), 'Encode\\Decode data:URL image'); - addAction('prev_edit_point', bind('editPoints', 'previousEditPointAction'), 'Previous Edit Point'); - addAction('next_edit_point', bind('editPoints', 'nextEditPointAction'), 'Next Edit Point'); - addAction('evaluate_math_expression', bind('evaluateMath', 'evaluateMathAction'), 'Numbers/Evaluate Math Expression'); - addAction('expand_abbreviation_with_tab', bind('expandAbbreviation', 'expandAbbreviationWithTabAction'), {hidden: true}); - addAction('expand_abbreviation', bind('expandAbbreviation', 'expandAbbreviationAction'), 'Expand Abbreviation'); - addAction('insert_formatted_line_break_only', bind('lineBreaks', 'insertLineBreakOnlyAction'), {hidden: true}); - addAction('insert_formatted_line_break', bind('lineBreaks', 'insertLineBreakAction'), {hidden: true}); - addAction('balance_inward', bind('balance', 'balanceInwardAction'), 'Balance (inward)'); - addAction('balance_outward', bind('balance', 'balanceOutwardAction'), 'Balance (outward)'); - addAction('matching_pair', bind('balance', 'goToMatchingPairAction'), 'HTML/Go To Matching Tag Pair'); - addAction('merge_lines', bind('mergeLines', 'mergeLinesAction'), 'Merge Lines'); - addAction('reflect_css_value', bind('reflectCSSValue', 'reflectCSSValueAction'), 'CSS/Reflect Value'); - addAction('remove_tag', bind('removeTag', 'removeTagAction'), 'HTML/Remove Tag'); - addAction('select_next_item', bind('selectItem', 'selectNextItemAction'), 'Select Next Item'); - addAction('select_previous_item', bind('selectItem', 'selectPreviousItemAction'), 'Select Previous Item'); - addAction('split_join_tag', bind('splitJoinTag', 'splitJoinTagAction'), 'HTML/Split\\Join Tag Declaration'); - addAction('toggle_comment', bind('toggleComment', 'toggleCommentAction'), 'Toggle Comment'); - addAction('update_image_size', bind('updateImageSize', 'updateImageSizeAction'), 'Update Image Size'); - addAction('wrap_with_abbreviation', bind('wrapWithAbbreviation', 'wrapWithAbbreviationAction'), 'Wrap With Abbreviation'); - addAction('update_tag', bind('updateTag', 'updateTagAction'), 'HTML/Update Tag'); - - [1, -1, 10, -10, 0.1, -0.1].forEach(function(num) { - var prefix = num > 0 ? 'increment' : 'decrement'; - var suffix = String(Math.abs(num)).replace('.', '').substring(0, 2); - var actionId = prefix + '_number_by_' + suffix; - var actionMethod = prefix + suffix + 'Action'; - var actionLabel = 'Numbers/' + prefix.charAt(0).toUpperCase() + prefix.substring(1) + ' number by ' + Math.abs(num); - addAction(actionId, bind('incrementDecrement', actionMethod), actionLabel); - }); - - return { - /** - * Registers new action - * @param {String} name Action name - * @param {Function} fn Action function - * @param {Object} options Custom action options:
- * label : (String) – Human-readable action name. - * May contain '/' symbols as submenu separators
- * hidden : (Boolean) – Indicates whether action - * should be displayed in menu (getMenu() method) - */ - add: addAction, - - /** - * Returns action object - * @param {String} name Action name - * @returns {Object} - */ - get: function(name) { - return actions[name.toLowerCase()]; - }, - - /** - * Runs Emmet action. For list of available actions and their - * arguments see actions folder. - * @param {String} name Action name - * @param {Array} args Additional arguments. It may be array of arguments - * or inline arguments. The first argument should be IEmmetEditor instance - * @returns {Boolean} Status of performed operation, true - * means action was performed successfully. - * @example - * require('action/main').run('expand_abbreviation', editor); - * require('action/main').run('wrap_with_abbreviation', [editor, 'div']); - */ - run: function(name, args) { - if (!Array.isArray(args)) { - args = utils.toArray(arguments, 1); - } - - var action = this.get(name); - if (!action) { - throw new Error('Action "' + name + '" is not defined'); - } - - return action.fn.apply(action, args); - }, - - /** - * Returns all registered actions as object - * @returns {Object} - */ - getAll: function() { - return actions; - }, - - /** - * Returns all registered actions as array - * @returns {Array} - */ - getList: function() { - var all = this.getAll(); - return Object.keys(all).map(function(key) { - return all[key]; - }); - }, - - /** - * Returns actions list as structured menu. If action has label, - * it will be splitted by '/' symbol into submenus (for example: - * CSS/Reflect Value) and grouped with other items - * @param {Array} skipActions List of action identifiers that should be - * skipped from menu - * @returns {Array} - */ - getMenu: function(skipActions) { - var result = []; - skipActions = skipActions || []; - this.getList().forEach(function(action) { - if (action.options.hidden || ~skipActions.indexOf(action.name)) - return; - - var actionName = humanizeActionName(action.name); - var ctx = result; - if (action.options.label) { - var parts = action.options.label.split('/'); - actionName = parts.pop(); - - // create submenus, if needed - var menuName, submenu; - while ((menuName = parts.shift())) { - submenu = utils.find(ctx, function(item) { - return item.type == 'submenu' && item.name == menuName; - }); - - if (!submenu) { - submenu = { - name: menuName, - type: 'submenu', - items: [] - }; - ctx.push(submenu); - } - - ctx = submenu.items; - } - } - - ctx.push({ - type: 'action', - name: action.name, - label: actionName - }); - }); - - return result; - }, - - /** - * Returns action name associated with menu item title - * @param {String} title - * @returns {String} - */ - getActionNameForMenuTitle: function(title, menu) { - return utils.find(menu || this.getMenu(), function(val) { - if (val.type == 'action') { - if (val.label == title || val.name == title) { - return val.name; - } - } else { - return this.getActionNameForMenuTitle(title, val.items); - } - }, this); - } - }; -}); -},{"../utils/common":73,"./balance":5,"./base64":6,"./editPoints":7,"./evaluateMath":8,"./expandAbbreviation":9,"./incrementDecrement":10,"./lineBreaks":11,"./mergeLines":13,"./reflectCSSValue":14,"./removeTag":15,"./selectItem":16,"./selectLine":17,"./splitJoinTag":18,"./toggleComment":19,"./updateImageSize":20,"./updateTag":21,"./wrapWithAbbreviation":22}],13:[function(require,module,exports){ -/** - * Merges selected lines or lines between XHTML tag pairs - * @param {Function} require - * @param {Underscore} _ - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var htmlMatcher = require('../assets/htmlMatcher'); - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var range = require('../assets/range'); - - return { - mergeLinesAction: function(editor) { - var info = editorUtils.outputInfo(editor); - - var selection = range(editor.getSelectionRange()); - if (!selection.length()) { - // find matching tag - var pair = htmlMatcher.find(info.content, editor.getCaretPos()); - if (pair) { - selection = pair.outerRange; - } - } - - if (selection.length()) { - // got range, merge lines - var text = selection.substring(info.content); - var lines = utils.splitByLines(text); - - for (var i = 1; i < lines.length; i++) { - lines[i] = lines[i].replace(/^\s+/, ''); - } - - text = lines.join('').replace(/\s{2,}/, ' '); - var textLen = text.length; - text = utils.escapeText(text); - editor.replaceContent(text, selection.start, selection.end); - editor.createSelection(selection.start, selection.start + textLen); - - return true; - } - - return false; - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/range":30,"../utils/common":73,"../utils/editor":75}],14:[function(require,module,exports){ -/** - * Reflect CSS value: takes rule's value under caret and pastes it for the same - * rules with vendor prefixes - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var handlerList = require('../assets/handlerList'); - var prefs = require('../assets/preferences'); - var cssResolver = require('../resolver/css'); - var cssEditTree = require('../editTree/css'); - var utils = require('../utils/common'); - var actionUtils = require('../utils/action'); - var editorUtils = require('../utils/editor'); - var cssGradient = require('../resolver/cssGradient'); - - prefs.define('css.reflect.oldIEOpacity', false, 'Support IE6/7/8 opacity notation, e.g. filter:alpha(opacity=...).\ - Note that CSS3 and SVG also provides filter property so this option is disabled by default.') - - /** - * @type HandlerList List of registered handlers - */ - var handlers = handlerList.create(); - - function doCSSReflection(editor) { - var outputInfo = editorUtils.outputInfo(editor); - var caretPos = editor.getCaretPos(); - - var cssRule = cssEditTree.parseFromPosition(outputInfo.content, caretPos); - if (!cssRule) return; - - var property = cssRule.itemFromPosition(caretPos, true); - // no property under cursor, nothing to reflect - if (!property) return; - - var oldRule = cssRule.source; - var offset = cssRule.options.offset; - var caretDelta = caretPos - offset - property.range().start; - - handlers.exec(false, [property]); - - if (oldRule !== cssRule.source) { - return { - data: cssRule.source, - start: offset, - end: offset + oldRule.length, - caret: offset + property.range().start + caretDelta - }; - } - } - - /** - * Returns regexp that should match reflected CSS property names - * @param {String} name Current CSS property name - * @return {RegExp} - */ - function getReflectedCSSName(name) { - name = cssEditTree.baseName(name); - var vendorPrefix = '^(?:\\-\\w+\\-)?', m; - - if ((name == 'opacity' || name == 'filter') && prefs.get('css.reflect.oldIEOpacity')) { - return new RegExp(vendorPrefix + '(?:opacity|filter)$'); - } else if ((m = name.match(/^border-radius-(top|bottom)(left|right)/))) { - // Mozilla-style border radius - return new RegExp(vendorPrefix + '(?:' + name + '|border-' + m[1] + '-' + m[2] + '-radius)$'); - } else if ((m = name.match(/^border-(top|bottom)-(left|right)-radius/))) { - return new RegExp(vendorPrefix + '(?:' + name + '|border-radius-' + m[1] + m[2] + ')$'); - } - - return new RegExp(vendorPrefix + name + '$'); - } - - /** - * Reflects inner CSS properites in given value - * agains name‘s vendor prefix. In other words, it tries - * to modify `transform 0.2s linear` value for `-webkit-transition` - * property - * @param {String} name Reciever CSS property name - * @param {String} value New property value - * @return {String} - */ - function reflectValueParts(name, value) { - // detects and updates vendor-specific properties in value, - // e.g. -webkit-transition: -webkit-transform - - var reVendor = /^\-(\w+)\-/; - var propPrefix = reVendor.test(name) ? RegExp.$1.toLowerCase() : ''; - var parts = cssEditTree.findParts(value); - - parts.reverse(); - parts.forEach(function(part) { - var partValue = part.substring(value).replace(reVendor, ''); - var prefixes = cssResolver.vendorPrefixes(partValue); - if (prefixes) { - // if prefixes are not null then given value can - // be resolved against Can I Use database and may or - // may not contain prefixed variant - if (propPrefix && ~prefixes.indexOf(propPrefix)) { - partValue = '-' + propPrefix + '-' + partValue; - } - - value = utils.replaceSubstring(value, partValue, part); - } - }); - - return value; - } - - /** - * Reflects value from donor into receiver - * @param {CSSProperty} donor Donor CSS property from which value should - * be reflected - * @param {CSSProperty} receiver Property that should receive reflected - * value from donor - */ - function reflectValue(donor, receiver) { - var value = getReflectedValue(donor.name(), donor.value(), - receiver.name(), receiver.value()); - - value = reflectValueParts(receiver.name(), value); - receiver.value(value); - } - - /** - * Returns value that should be reflected for refName CSS property - * from curName property. This function is used for special cases, - * when the same result must be achieved with different properties for different - * browsers. For example: opаcity:0.5; → filter:alpha(opacity=50);

- * - * This function does value conversion between different CSS properties - * - * @param {String} curName Current CSS property name - * @param {String} curValue Current CSS property value - * @param {String} refName Receiver CSS property's name - * @param {String} refValue Receiver CSS property's value - * @return {String} New value for receiver property - */ - function getReflectedValue(curName, curValue, refName, refValue) { - curName = cssEditTree.baseName(curName); - refName = cssEditTree.baseName(refName); - - if (curName == 'opacity' && refName == 'filter') { - return refValue.replace(/opacity=[^)]*/i, 'opacity=' + Math.floor(parseFloat(curValue) * 100)); - } else if (curName == 'filter' && refName == 'opacity') { - var m = curValue.match(/opacity=([^)]*)/i); - return m ? utils.prettifyNumber(parseInt(m[1], 10) / 100) : refValue; - } - - return curValue; - } - - module = module || {}; - module.exports = { - reflectCSSValueAction: function(editor) { - if (editor.getSyntax() != 'css') { - return false; - } - - return actionUtils.compoundUpdate(editor, doCSSReflection(editor)); - }, - - _defaultHandler: function(property) { - var reName = getReflectedCSSName(property.name()); - property.parent.list().forEach(function(p) { - if (reName.test(p.name())) { - reflectValue(property, p); - } - }); - }, - - /** - * Adds custom reflect handler. The passed function will receive matched - * CSS property (as CSSEditElement object) and should - * return true if it was performed successfully (handled - * reflection), false otherwise. - * @param {Function} fn - * @param {Object} options - */ - addHandler: function(fn, options) { - handlers.add(fn, options); - }, - - /** - * Removes registered handler - * @returns - */ - removeHandler: function(fn) { - handlers.remove(fn); - } - }; - - // XXX add default handlers - handlers.add(module.exports._defaultHandler.bind(module.exports), {order: -1}); - handlers.add(cssGradient.reflectValueHandler.bind(cssGradient)); - - return module.exports; -}); -},{"../assets/handlerList":25,"../assets/preferences":28,"../editTree/css":37,"../resolver/css":64,"../resolver/cssGradient":65,"../utils/action":70,"../utils/common":73,"../utils/editor":75}],15:[function(require,module,exports){ -/** - * Gracefully removes tag under cursor - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var htmlMatcher = require('../assets/htmlMatcher'); - - return { - removeTagAction: function(editor) { - var info = editorUtils.outputInfo(editor); - - // search for tag - var tag = htmlMatcher.tag(info.content, editor.getCaretPos()); - if (tag) { - if (!tag.close) { - // simply remove unary tag - editor.replaceContent(utils.getCaretPlaceholder(), tag.range.start, tag.range.end); - } else { - // remove tag and its newlines - /** @type Range */ - var tagContentRange = utils.narrowToNonSpace(info.content, tag.innerRange); - /** @type Range */ - var startLineBounds = utils.findNewlineBounds(info.content, tagContentRange.start); - var startLinePad = utils.getLinePadding(startLineBounds.substring(info.content)); - var tagContent = tagContentRange.substring(info.content); - - tagContent = utils.unindentString(tagContent, startLinePad); - editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), tag.outerRange.start, tag.outerRange.end); - } - - return true; - } - - return false; - } - }; -}); - -},{"../assets/htmlMatcher":26,"../utils/common":73,"../utils/editor":75}],16:[function(require,module,exports){ -/** - * Actions that use stream parsers and tokenizers for traversing: - * -- Search for next/previous items in HTML - * -- Search for next/previous items in CSS - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var range = require('../assets/range'); - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var stringStream = require('../assets/stringStream'); - var xmlParser = require('../parser/xml'); - var cssEditTree = require('../editTree/css'); - var cssSections = require('../utils/cssSections'); - - var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; - - /** - * Generic function for searching for items to select - * @param {IEmmetEditor} editor - * @param {Boolean} isBackward Search backward (search forward otherwise) - * @param {Function} extractFn Function that extracts item content - * @param {Function} rangeFn Function that search for next token range - */ - function findItem(editor, isBackward, extractFn, rangeFn) { - var content = editorUtils.outputInfo(editor).content; - - var contentLength = content.length; - var itemRange, rng; - /** @type Range */ - var prevRange = range(-1, 0); - /** @type Range */ - var sel = range(editor.getSelectionRange()); - - var searchPos = sel.start, loop = 100000; // endless loop protection - while (searchPos >= 0 && searchPos < contentLength && --loop > 0) { - if ( (itemRange = extractFn(content, searchPos, isBackward)) ) { - if (prevRange.equal(itemRange)) { - break; - } - - prevRange = itemRange.clone(); - rng = rangeFn(itemRange.substring(content), itemRange.start, sel.clone()); - - if (rng) { - editor.createSelection(rng.start, rng.end); - return true; - } else { - searchPos = isBackward ? itemRange.start : itemRange.end - 1; - } - } - - searchPos += isBackward ? -1 : 1; - } - - return false; - } - - // XXX HTML section - - /** - * Find next HTML item - * @param {IEmmetEditor} editor - */ - function findNextHTMLItem(editor) { - var isFirst = true; - return findItem(editor, false, function(content, searchPos){ - if (isFirst) { - isFirst = false; - return findOpeningTagFromPosition(content, searchPos); - } else { - return getOpeningTagFromPosition(content, searchPos); - } - }, function(tag, offset, selRange) { - return getRangeForHTMLItem(tag, offset, selRange, false); - }); - } - - /** - * Find previous HTML item - * @param {IEmmetEditor} editor - */ - function findPrevHTMLItem(editor) { - return findItem(editor, true, getOpeningTagFromPosition, function (tag, offset, selRange) { - return getRangeForHTMLItem(tag, offset, selRange, true); - }); - } - - /** - * Creates possible selection ranges for HTML tag - * @param {String} source Original HTML source for tokens - * @param {Array} tokens List of HTML tokens - * @returns {Array} - */ - function makePossibleRangesHTML(source, tokens, offset) { - offset = offset || 0; - var result = []; - var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName; - tokens.forEach(function(tok) { - switch (tok.type) { - case 'tag': - tagName = source.substring(tok.start, tok.end); - if (/^<[\w\:\-]/.test(tagName)) { - // add tag name - result.push(range({ - start: tok.start + 1, - end: tok.end - })); - } - break; - case 'attribute': - attrStart = tok.start; - attrName = source.substring(tok.start, tok.end); - break; - - case 'string': - // attribute value - // push full attribute first - result.push(range(attrStart, tok.end - attrStart)); - - attrValueRange = range(tok); - attrValue = attrValueRange.substring(source); - - // is this a quoted attribute? - if (isQuote(attrValue.charAt(0))) - attrValueRange.start++; - - if (isQuote(attrValue.charAt(attrValue.length - 1))) - attrValueRange.end--; - - result.push(attrValueRange); - - if (attrName == 'class') { - result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start)); - } - - break; - } - }); - - // offset ranges - result = result.filter(function(item) { - if (item.length()) { - item.shift(offset); - return true; - } - }); - - // remove duplicates - return utils.unique(result, function(item) { - return item.toString(); - }); - } - - /** - * Returns ranges of class names in "class" attribute value - * @param {String} className - * @returns {Array} - */ - function classNameRanges(className, offset) { - offset = offset || 0; - var result = []; - /** @type StringStream */ - var stream = stringStream.create(className); - - // skip whitespace - stream.eatSpace(); - stream.start = stream.pos; - - var ch; - while ((ch = stream.next())) { - if (/[\s\u00a0]/.test(ch)) { - result.push(range(stream.start + offset, stream.pos - stream.start - 1)); - stream.eatSpace(); - stream.start = stream.pos; - } - } - - result.push(range(stream.start + offset, stream.pos - stream.start)); - return result; - } - - /** - * Returns best HTML tag range match for current selection - * @param {String} tag Tag declaration - * @param {Number} offset Tag's position index inside content - * @param {Range} selRange Selection range - * @return {Range} Returns range if next item was found, null otherwise - */ - function getRangeForHTMLItem(tag, offset, selRange, isBackward) { - var ranges = makePossibleRangesHTML(tag, xmlParser.parse(tag), offset); - - if (isBackward) - ranges.reverse(); - - // try to find selected range - var curRange = utils.find(ranges, function(r) { - return r.equal(selRange); - }); - - if (curRange) { - var ix = ranges.indexOf(curRange); - if (ix < ranges.length - 1) - return ranges[ix + 1]; - - return null; - } - - // no selected range, find nearest one - if (isBackward) - // search backward - return utils.find(ranges, function(r) { - return r.start < selRange.start; - }); - - // search forward - // to deal with overlapping ranges (like full attribute definition - // and attribute value) let's find range under caret first - if (!curRange) { - var matchedRanges = ranges.filter(function(r) { - return r.inside(selRange.end); - }); - - if (matchedRanges.length > 1) - return matchedRanges[1]; - } - - - return utils.find(ranges, function(r) { - return r.end > selRange.end; - }); - } - - /** - * Search for opening tag in content, starting at specified position - * @param {String} html Where to search tag - * @param {Number} pos Character index where to start searching - * @return {Range} Returns range if valid opening tag was found, - * null otherwise - */ - function findOpeningTagFromPosition(html, pos) { - var tag; - while (pos >= 0) { - if ((tag = getOpeningTagFromPosition(html, pos))) - return tag; - pos--; - } - - return null; - } - - /** - * @param {String} html Where to search tag - * @param {Number} pos Character index where to start searching - * @return {Range} Returns range if valid opening tag was found, - * null otherwise - */ - function getOpeningTagFromPosition(html, pos) { - var m; - if (html.charAt(pos) == '<' && (m = html.substring(pos, html.length).match(startTag))) { - return range(pos, m[0]); - } - } - - function isQuote(ch) { - return ch == '"' || ch == "'"; - } - - /** - * Returns all ranges inside given rule, available for selection - * @param {CSSEditContainer} rule - * @return {Array} - */ - function findInnerRanges(rule) { - // rule selector - var ranges = [rule.nameRange(true)]; - - // find nested sections, keep selectors only - var nestedSections = cssSections.nestedSectionsInRule(rule); - nestedSections.forEach(function(section) { - ranges.push(range.create2(section.start, section._selectorEnd)); - }); - - // add full property ranges and values - rule.list().forEach(function(property) { - ranges = ranges.concat(makePossibleRangesCSS(property)); - }); - - ranges = range.sort(ranges); - - // optimize result: remove empty ranges and duplicates - ranges = ranges.filter(function(item) { - return !!item.length(); - }); - return utils.unique(ranges, function(item) { - return item.toString(); - }); - } - - /** - * Makes all possible selection ranges for specified CSS property - * @param {CSSProperty} property - * @returns {Array} - */ - function makePossibleRangesCSS(property) { - // find all possible ranges, sorted by position and size - var valueRange = property.valueRange(true); - var result = [property.range(true), valueRange]; - - // locate parts of complex values. - // some examples: - // – 1px solid red: 3 parts - // – arial, sans-serif: enumeration, 2 parts - // – url(image.png): function value part - var value = property.value(); - property.valueParts().forEach(function(r) { - // add absolute range - var clone = r.clone(); - result.push(clone.shift(valueRange.start)); - - /** @type StringStream */ - var stream = stringStream.create(r.substring(value)); - if (stream.match(/^[\w\-]+\(/, true)) { - // we have a function, find values in it. - // but first add function contents - stream.start = stream.pos; - stream.backUp(1); - stream.skipToPair('(', ')'); - stream.backUp(1); - var fnBody = stream.current(); - result.push(range(clone.start + stream.start, fnBody)); - - // find parts - cssEditTree.findParts(fnBody).forEach(function(part) { - result.push(range(clone.start + stream.start + part.start, part.substring(fnBody))); - }); - } - }); - - return result; - } - - /** - * Tries to find matched CSS property and nearest range for selection - * @param {CSSRule} rule - * @param {Range} selRange - * @param {Boolean} isBackward - * @returns {Range} - */ - function matchedRangeForCSSProperty(rule, selRange, isBackward) { - var ranges = findInnerRanges(rule); - if (isBackward) { - ranges.reverse(); - } - - // return next to selected range, if possible - var r = utils.find(ranges, function(item) { - return item.equal(selRange); - }); - - if (r) { - return ranges[ranges.indexOf(r) + 1]; - } - - // find matched and (possibly) overlapping ranges - var nested = ranges.filter(function(item) { - return item.inside(selRange.end); - }); - - if (nested.length) { - return nested.sort(function(a, b) { - return a.length() - b.length(); - })[0]; - } - - // return range next to caret - var test = - r = utils.find(ranges, isBackward - ? function(item) {return item.end < selRange.start;} - : function(item) {return item.end > selRange.start;} - ); - - if (!r) { - // can’t find anything, just pick first one - r = ranges[0]; - } - - return r; - } - - function findNextCSSItem(editor) { - return findItem(editor, false, cssSections.locateRule.bind(cssSections), getRangeForNextItemInCSS); - } - - function findPrevCSSItem(editor) { - return findItem(editor, true, cssSections.locateRule.bind(cssSections), getRangeForPrevItemInCSS); - } - - /** - * Returns range for item to be selected in CSS after current caret - * (selection) position - * @param {String} rule CSS rule declaration - * @param {Number} offset Rule's position index inside content - * @param {Range} selRange Selection range - * @return {Range} Returns range if next item was found, null otherwise - */ - function getRangeForNextItemInCSS(rule, offset, selRange) { - var tree = cssEditTree.parse(rule, { - offset: offset - }); - - return matchedRangeForCSSProperty(tree, selRange, false); - } - - /** - * Returns range for item to be selected in CSS before current caret - * (selection) position - * @param {String} rule CSS rule declaration - * @param {Number} offset Rule's position index inside content - * @param {Range} selRange Selection range - * @return {Range} Returns range if previous item was found, null otherwise - */ - function getRangeForPrevItemInCSS(rule, offset, selRange) { - var tree = cssEditTree.parse(rule, { - offset: offset - }); - - return matchedRangeForCSSProperty(tree, selRange, true); - } - - return { - selectNextItemAction: function(editor) { - if (actionUtils.isSupportedCSS(editor.getSyntax())) { - return findNextCSSItem(editor); - } else { - return findNextHTMLItem(editor); - } - }, - - selectPreviousItemAction: function(editor) { - if (actionUtils.isSupportedCSS(editor.getSyntax())) { - return findPrevCSSItem(editor); - } else { - return findPrevHTMLItem(editor); - } - } - }; -}); -},{"../assets/range":30,"../assets/stringStream":32,"../editTree/css":37,"../parser/xml":62,"../utils/action":70,"../utils/common":73,"../utils/cssSections":74,"../utils/editor":75}],17:[function(require,module,exports){ -/** - * Select current line (for simple editors like browser's <textarea>) - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - return { - selectLineAction: function(editor) { - var range = editor.getCurrentLineRange(); - editor.createSelection(range.start, range.end); - return true; - } - }; -}); -},{}],18:[function(require,module,exports){ -/** - * Splits or joins tag, e.g. transforms it into a short notation and vice versa:
- * <div></div> → <div /> : join
- * <div /> → <div></div> : split - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var resources = require('../assets/resources'); - var matcher = require('../assets/htmlMatcher'); - var editorUtils = require('../utils/editor'); - var profile = require('../assets/profile'); - - /** - * @param {IEmmetEditor} editor - * @param {Object} profile - * @param {Object} tag - */ - function joinTag(editor, profile, tag) { - // empty closing slash is a nonsense for this action - var slash = profile.selfClosing() || ' /'; - var content = tag.open.range.substring(tag.source).replace(/\s*>$/, slash + '>'); - - var caretPos = editor.getCaretPos(); - - // update caret position - if (content.length + tag.outerRange.start < caretPos) { - caretPos = content.length + tag.outerRange.start; - } - - content = utils.escapeText(content); - editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end); - editor.setCaretPos(caretPos); - return true; - } - - function splitTag(editor, profile, tag) { - var caretPos = editor.getCaretPos(); - - // define tag content depending on profile - var tagContent = (profile.tag_nl === true) ? '\n\t\n' : ''; - var content = tag.outerContent().replace(/\s*\/>$/, '>'); - caretPos = tag.outerRange.start + content.length; - content += tagContent + ''; - - content = utils.escapeText(content); - editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end); - editor.setCaretPos(caretPos); - return true; - } - - return { - splitJoinTagAction: function(editor, profileName) { - var info = editorUtils.outputInfo(editor, null, profileName); - var curProfile = profile.get(info.profile); - - // find tag at current position - var tag = matcher.tag(info.content, editor.getCaretPos()); - if (tag) { - return tag.close - ? joinTag(editor, curProfile, tag) - : splitTag(editor, curProfile, tag); - } - - return false; - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/profile":29,"../assets/resources":31,"../utils/common":73,"../utils/editor":75}],19:[function(require,module,exports){ -/** - * Toggles HTML and CSS comments depending on current caret context. Unlike - * the same action in most editors, this action toggles comment on currently - * matched item—HTML tag or CSS selector—when nothing is selected. - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var prefs = require('../assets/preferences'); - var range = require('../assets/range'); - var utils = require('../utils/common'); - var actionUtils = require('../utils/action'); - var editorUtils = require('../utils/editor'); - var htmlMatcher = require('../assets/htmlMatcher'); - var cssEditTree = require('../editTree/css'); - - /** - * Toggle HTML comment on current selection or tag - * @param {IEmmetEditor} editor - * @return {Boolean} Returns true if comment was toggled - */ - function toggleHTMLComment(editor) { - /** @type Range */ - var r = range(editor.getSelectionRange()); - var info = editorUtils.outputInfo(editor); - - if (!r.length()) { - // no selection, find matching tag - var tag = htmlMatcher.tag(info.content, editor.getCaretPos()); - if (tag) { // found pair - r = tag.outerRange; - } - } - - return genericCommentToggle(editor, '', r); - } - - /** - * Simple CSS commenting - * @param {IEmmetEditor} editor - * @return {Boolean} Returns true if comment was toggled - */ - function toggleCSSComment(editor) { - /** @type Range */ - var rng = range(editor.getSelectionRange()); - var info = editorUtils.outputInfo(editor); - - if (!rng.length()) { - // no selection, try to get current rule - /** @type CSSRule */ - var rule = cssEditTree.parseFromPosition(info.content, editor.getCaretPos()); - if (rule) { - var property = cssItemFromPosition(rule, editor.getCaretPos()); - rng = property - ? property.range(true) - : range(rule.nameRange(true).start, rule.source); - } - } - - if (!rng.length()) { - // still no selection, get current line - rng = range(editor.getCurrentLineRange()); - utils.narrowToNonSpace(info.content, rng); - } - - return genericCommentToggle(editor, '/*', '*/', rng); - } - - /** - * Returns CSS property from rule that matches passed position - * @param {EditContainer} rule - * @param {Number} absPos - * @returns {EditElement} - */ - function cssItemFromPosition(rule, absPos) { - // do not use default EditContainer.itemFromPosition() here, because - // we need to make a few assumptions to make CSS commenting more reliable - var relPos = absPos - (rule.options.offset || 0); - var reSafeChar = /^[\s\n\r]/; - return utils.find(rule.list(), function(item) { - if (item.range().end === relPos) { - // at the end of property, but outside of it - // if there’s a space character at current position, - // use current property - return reSafeChar.test(rule.source.charAt(relPos)); - } - - return item.range().inside(relPos); - }); - } - - /** - * Search for nearest comment in str, starting from index from - * @param {String} text Where to search - * @param {Number} from Search start index - * @param {String} start_token Comment start string - * @param {String} end_token Comment end string - * @return {Range} Returns null if comment wasn't found - */ - function searchComment(text, from, startToken, endToken) { - var commentStart = -1; - var commentEnd = -1; - - var hasMatch = function(str, start) { - return text.substr(start, str.length) == str; - }; - - // search for comment start - while (from--) { - if (hasMatch(startToken, from)) { - commentStart = from; - break; - } - } - - if (commentStart != -1) { - // search for comment end - from = commentStart; - var contentLen = text.length; - while (contentLen >= from++) { - if (hasMatch(endToken, from)) { - commentEnd = from + endToken.length; - break; - } - } - } - - return (commentStart != -1 && commentEnd != -1) - ? range(commentStart, commentEnd - commentStart) - : null; - } - - /** - * Generic comment toggling routine - * @param {IEmmetEditor} editor - * @param {String} commentStart Comment start token - * @param {String} commentEnd Comment end token - * @param {Range} range Selection range - * @return {Boolean} - */ - function genericCommentToggle(editor, commentStart, commentEnd, range) { - var content = editorUtils.outputInfo(editor).content; - var caretPos = editor.getCaretPos(); - var newContent = null; - - /** - * Remove comment markers from string - * @param {Sting} str - * @return {String} - */ - function removeComment(str) { - return str - .replace(new RegExp('^' + utils.escapeForRegexp(commentStart) + '\\s*'), function(str){ - caretPos -= str.length; - return ''; - }).replace(new RegExp('\\s*' + utils.escapeForRegexp(commentEnd) + '$'), ''); - } - - // first, we need to make sure that this substring is not inside - // comment - var commentRange = searchComment(content, caretPos, commentStart, commentEnd); - if (commentRange && commentRange.overlap(range)) { - // we're inside comment, remove it - range = commentRange; - newContent = removeComment(range.substring(content)); - } else { - // should add comment - // make sure that there's no comment inside selection - newContent = commentStart + ' ' + - range.substring(content) - .replace(new RegExp(utils.escapeForRegexp(commentStart) + '\\s*|\\s*' + utils.escapeForRegexp(commentEnd), 'g'), '') + - ' ' + commentEnd; - - // adjust caret position - caretPos += commentStart.length + 1; - } - - // replace editor content - if (newContent !== null) { - newContent = utils.escapeText(newContent); - editor.setCaretPos(range.start); - editor.replaceContent(editorUtils.unindent(editor, newContent), range.start, range.end); - editor.setCaretPos(caretPos); - return true; - } - - return false; - } - - return { - /** - * Toggle comment on current editor's selection or HTML tag/CSS rule - * @param {IEmmetEditor} editor - */ - toggleCommentAction: function(editor) { - var info = editorUtils.outputInfo(editor); - if (actionUtils.isSupportedCSS(info.syntax)) { - // in case our editor is good enough and can recognize syntax from - // current token, we have to make sure that cursor is not inside - // 'style' attribute of html element - var caretPos = editor.getCaretPos(); - var tag = htmlMatcher.tag(info.content, caretPos); - if (tag && tag.open.range.inside(caretPos)) { - info.syntax = 'html'; - } - } - - var cssSyntaxes = prefs.getArray('css.syntaxes'); - if (~cssSyntaxes.indexOf(info.syntax)) { - return toggleCSSComment(editor); - } - - return toggleHTMLComment(editor); - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/preferences":28,"../assets/range":30,"../editTree/css":37,"../utils/action":70,"../utils/common":73,"../utils/editor":75}],20:[function(require,module,exports){ -/** - * Automatically updates image size attributes in HTML's <img> element or - * CSS rule - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var xmlEditTree = require('../editTree/xml'); - var cssEditTree = require('../editTree/css'); - var base64 = require('../utils/base64'); - var file = require('../plugin/file'); - - /** - * Updates image size of <img src=""> tag - * @param {IEmmetEditor} editor - */ - function updateImageSizeHTML(editor) { - var offset = editor.getCaretPos(); - - // find tag from current caret position - var info = editorUtils.outputInfo(editor); - var xmlElem = xmlEditTree.parseFromPosition(info.content, offset, true); - if (xmlElem && (xmlElem.name() || '').toLowerCase() == 'img') { - getImageSizeForSource(editor, xmlElem.value('src'), function(size) { - if (size) { - var compoundData = xmlElem.range(true); - xmlElem.value('width', size.width); - xmlElem.value('height', size.height, xmlElem.indexOf('width') + 1); - - actionUtils.compoundUpdate(editor, utils.extend(compoundData, { - data: xmlElem.toString(), - caret: offset - })); - } - }); - } - } - - /** - * Updates image size of CSS property - * @param {IEmmetEditor} editor - */ - function updateImageSizeCSS(editor) { - var offset = editor.getCaretPos(); - - // find tag from current caret position - var info = editorUtils.outputInfo(editor); - var cssRule = cssEditTree.parseFromPosition(info.content, offset, true); - if (cssRule) { - // check if there is property with image under caret - var prop = cssRule.itemFromPosition(offset, true), m; - if (prop && (m = /url\((["']?)(.+?)\1\)/i.exec(prop.value() || ''))) { - getImageSizeForSource(editor, m[2], function(size) { - if (size) { - var compoundData = cssRule.range(true); - cssRule.value('width', size.width + 'px'); - cssRule.value('height', size.height + 'px', cssRule.indexOf('width') + 1); - - actionUtils.compoundUpdate(editor, utils.extend(compoundData, { - data: cssRule.toString(), - caret: offset - })); - } - }); - } - } - } - - /** - * Returns image dimensions for source - * @param {IEmmetEditor} editor - * @param {String} src Image source (path or data:url) - */ - function getImageSizeForSource(editor, src, callback) { - var fileContent; - if (src) { - // check if it is data:url - if (/^data:/.test(src)) { - fileContent = base64.decode( src.replace(/^data\:.+?;.+?,/, '') ); - return callback(actionUtils.getImageSize(fileContent)); - } - - var absPath = file.locateFile(editor.getFilePath(), src); - if (absPath === null) { - throw "Can't find " + src + ' file'; - } - - file.read(absPath, function(err, content) { - if (err) { - throw 'Unable to read ' + absPath + ': ' + err; - } - - content = String(content); - callback(actionUtils.getImageSize(content)); - }); - } - } - - return { - updateImageSizeAction: function(editor) { - // this action will definitely won’t work in SASS dialect, - // but may work in SCSS or LESS - if (actionUtils.isSupportedCSS(editor.getSyntax())) { - updateImageSizeCSS(editor); - } else { - updateImageSizeHTML(editor); - } - - return true; - } - }; -}); -},{"../editTree/css":37,"../editTree/xml":38,"../plugin/file":63,"../utils/action":70,"../utils/base64":71,"../utils/common":73,"../utils/editor":75}],21:[function(require,module,exports){ -/** - * Update Tag action: allows users to update existing HTML tags and add/remove - * attributes or even tag name - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var xmlEditTree = require('../editTree/xml'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var utils = require('../utils/common'); - var parser = require('../parser/abbreviation'); - - function updateAttributes(tag, abbrNode, ix) { - var classNames = (abbrNode.attribute('class') || '').split(/\s+/g); - if (ix) { - classNames.push('+' + abbrNode.name()); - } - - var r = function(str) { - return utils.replaceCounter(str, abbrNode.counter); - }; - - // update class - classNames.forEach(function(className) { - if (!className) { - return; - } - - className = r(className); - var ch = className.charAt(0); - if (ch == '+') { - tag.addClass(className.substr(1)); - } else if (ch == '-') { - tag.removeClass(className.substr(1)); - } else { - tag.value('class', className); - } - }); - - // update attributes - abbrNode.attributeList().forEach(function(attr) { - if (attr.name.toLowerCase() == 'class') { - return; - } - - var ch = attr.name.charAt(0); - if (ch == '+') { - var attrName = attr.name.substr(1); - var tagAttr = tag.get(attrName); - if (tagAttr) { - tagAttr.value(tagAttr.value() + r(attr.value)); - } else { - tag.value(attrName, r(attr.value)); - } - } else if (ch == '-') { - tag.remove(attr.name.substr(1)); - } else { - tag.value(attr.name, r(attr.value)); - } - }); - } - - return { - /** - * Matches HTML tag under caret and updates its definition - * according to given abbreviation - * @param {IEmmetEditor} Editor instance - * @param {String} abbr Abbreviation to update with - */ - updateTagAction: function(editor, abbr) { - abbr = abbr || editor.prompt("Enter abbreviation"); - - if (!abbr) { - return false; - } - - var content = editor.getContent(); - var ctx = actionUtils.captureContext(editor); - var tag = this.getUpdatedTag(abbr, ctx, content); - - if (!tag) { - // nothing to update - return false; - } - - // check if tag name was updated - if (tag.name() != ctx.name && ctx.match.close) { - editor.replaceContent('', ctx.match.close.range.start, ctx.match.close.range.end, true); - } - - editor.replaceContent(tag.source, ctx.match.open.range.start, ctx.match.open.range.end, true); - return true; - }, - - /** - * Returns XMLEditContainer node with updated tag structure - * of existing tag context. - * This data can be used to modify existing tag - * @param {String} abbr Abbreviation - * @param {Object} ctx Tag to be updated (captured with `htmlMatcher`) - * @param {String} content Original editor content - * @return {XMLEditContainer} - */ - getUpdatedTag: function(abbr, ctx, content, options) { - if (!ctx) { - // nothing to update - return null; - } - - var tree = parser.parse(abbr, options || {}); - - // for this action some characters in abbreviation has special - // meaning. For example, `.-c2` means “remove `c2` class from - // element” and `.+c3` means “append class `c3` to exising one. - // - // But `.+c3` abbreviation will actually produce two elements: - //
and . Thus, we have to walk on each element - // of parsed tree and use their definitions to update current element - var tag = xmlEditTree.parse(ctx.match.open.range.substring(content), { - offset: ctx.match.outerRange.start - }); - - tree.children.forEach(function(node, i) { - updateAttributes(tag, node, i); - }); - - // if tag name was resolved by implicit tag name resolver, - // then user omitted it in abbreviation and wants to keep - // original tag name - var el = tree.children[0]; - if (!el.data('nameResolved')) { - tag.name(el.name()); - } - - return tag; - } - }; -}); -},{"../editTree/xml":38,"../parser/abbreviation":55,"../utils/action":70,"../utils/common":73,"../utils/editor":75}],22:[function(require,module,exports){ -/** - * Action that wraps content with abbreviation. For convenience, action is - * defined as reusable module - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var range = require('../assets/range'); - var htmlMatcher = require('../assets/htmlMatcher'); - var utils = require('../utils/common'); - var editorUtils = require('../utils/editor'); - var actionUtils = require('../utils/action'); - var parser = require('../parser/abbreviation'); - - return { - /** - * Wraps content with abbreviation - * @param {IEmmetEditor} Editor instance - * @param {String} abbr Abbreviation to wrap with - * @param {String} syntax Syntax type (html, css, etc.) - * @param {String} profile Output profile name (html, xml, xhtml) - */ - wrapWithAbbreviationAction: function(editor, abbr, syntax, profile) { - var info = editorUtils.outputInfo(editor, syntax, profile); - abbr = abbr || editor.prompt("Enter abbreviation"); - - if (!abbr) { - return null; - } - - abbr = String(abbr); - - var r = range(editor.getSelectionRange()); - - if (!r.length()) { - // no selection, find tag pair - var match = htmlMatcher.tag(info.content, r.start); - if (!match) { // nothing to wrap - return false; - } - - r = utils.narrowToNonSpace(info.content, match.range); - } - - var newContent = utils.escapeText(r.substring(info.content)); - var result = parser.expand(abbr, { - pastedContent: editorUtils.unindent(editor, newContent), - syntax: info.syntax, - profile: info.profile, - contextNode: actionUtils.captureContext(editor) - }); - - if (result) { - editor.replaceContent(result, r.start, r.end); - return true; - } - - return false; - } - }; -}); -},{"../assets/htmlMatcher":26,"../assets/range":30,"../parser/abbreviation":55,"../utils/action":70,"../utils/common":73,"../utils/editor":75}],23:[function(require,module,exports){ -/** - * Parsed resources (snippets, abbreviations, variables, etc.) for Emmet. - * Contains convenient method to get access for snippets with respect of - * inheritance. Also provides ability to store data in different vocabularies - * ('system' and 'user') for fast and safe resource update - * @author Sergey Chikuyonok (serge.che@gmail.com) - * @link http://chikuyonok.ru - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var prefs = require('./preferences'); - var utils = require('../utils/common'); - - prefs.define('caniuse.enabled', true, 'Enable support of Can I Use database. When enabled,\ - CSS abbreviation resolver will look at Can I Use database first before detecting\ - CSS properties that should be resolved'); - - prefs.define('caniuse.vendors', 'all', 'A comma-separated list vendor identifiers\ - (as described in Can I Use database) that should be supported\ - when resolving vendor-prefixed properties. Set value to all\ - to support all available properties'); - - prefs.define('caniuse.era', 'e-2', 'Browser era, as defined in Can I Use database.\ - Examples: e0 (current version), e1 (near future)\ - e-2 (2 versions back) and so on.'); - - var cssSections = { - 'border-image': ['border-image'], - 'css-boxshadow': ['box-shadow'], - 'css3-boxsizing': ['box-sizing'], - 'multicolumn': ['column-width', 'column-count', 'columns', 'column-gap', 'column-rule-color', 'column-rule-style', 'column-rule-width', 'column-rule', 'column-span', 'column-fill'], - 'border-radius': ['border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius'], - 'transforms2d': ['transform'], - 'css-hyphens': ['hyphens'], - 'css-transitions': ['transition', 'transition-property', 'transition-duration', 'transition-timing-function', 'transition-delay'], - 'font-feature': ['font-feature-settings'], - 'css-animation': ['animation', 'animation-name', 'animation-duration', 'animation-timing-function', 'animation-iteration-count', 'animation-direction', 'animation-play-state', 'animation-delay', 'animation-fill-mode', '@keyframes'], - 'css-gradients': ['linear-gradient'], - 'css-masks': ['mask-image', 'mask-source-type', 'mask-repeat', 'mask-position', 'mask-clip', 'mask-origin', 'mask-size', 'mask', 'mask-type', 'mask-box-image-source', 'mask-box-image-slice', 'mask-box-image-width', 'mask-box-image-outset', 'mask-box-image-repeat', 'mask-box-image', 'clip-path', 'clip-rule'], - 'css-featurequeries': ['@supports'], - 'flexbox': ['flex', 'inline-flex', 'flex-direction', 'flex-wrap', 'flex-flow', 'order', 'flex'], - 'calc': ['calc'], - 'object-fit': ['object-fit', 'object-position'], - 'css-grid': ['grid', 'inline-grid', 'grid-template-rows', 'grid-template-columns', 'grid-template-areas', 'grid-template', 'grid-auto-rows', 'grid-auto-columns', ' grid-auto-flow', 'grid-auto-position', 'grid', ' grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end', 'grid-column', 'grid-row', 'grid-area', 'justify-self', 'justify-items', 'align-self', 'align-items'], - 'css-repeating-gradients': ['repeating-linear-gradient'], - 'css-filters': ['filter'], - 'user-select-none': ['user-select'], - 'intrinsic-width': ['min-content', 'max-content', 'fit-content', 'fill-available'], - 'css3-tabsize': ['tab-size'] - }; - - /** @type {Object} The Can I Use database for CSS */ - var cssDB = null; - /** @type {Object} A list of available vendors (browsers) and their prefixes */ - var vendorsDB = null; - var erasDB = null; - - function intersection(arr1, arr2) { - var result = []; - var smaller = arr1, larger = arr2; - if (smaller.length > larger.length) { - smaller = arr2; - larger = arr1; - } - larger.forEach(function(item) { - if (~smaller.indexOf(item)) { - result.push(item); - } - }); - return result; - } - - /** - * Parses raw Can I Use database for better lookups - * @param {String} data Raw database - * @param {Boolean} optimized Pass `true` if given `data` is already optimized - * @return {Object} - */ - function parseDB(data, optimized) { - if (typeof data == 'string') { - data = JSON.parse(data); - } - - if (!optimized) { - data = optimize(data); - } - - vendorsDB = data.vendors; - cssDB = data.css; - erasDB = data.era; - } - - /** - * Extract required data only from CIU database - * @param {Object} data Raw Can I Use database - * @return {Object} Optimized database - */ - function optimize(data) { - if (typeof data == 'string') { - data = JSON.parse(data); - } - - return { - vendors: parseVendors(data), - css: parseCSS(data), - era: parseEra(data) - }; - } - - /** - * Parses vendor data - * @param {Object} data - * @return {Object} - */ - function parseVendors(data) { - var out = {}; - Object.keys(data.agents).forEach(function(name) { - var agent = data.agents[name]; - out[name] = { - prefix: agent.prefix, - versions: agent.versions - }; - }); - return out; - } - - /** - * Parses CSS data from Can I Use raw database - * @param {Object} data - * @return {Object} - */ - function parseCSS(data) { - var out = {}; - var cssCategories = data.cats.CSS; - Object.keys(data.data).forEach(function(name) { - var section = data.data[name]; - if (name in cssSections) { - cssSections[name].forEach(function(kw) { - out[kw] = section.stats; - }); - } - }); - - return out; - } - - /** - * Parses era data from Can I Use raw database - * @param {Object} data - * @return {Array} - */ - function parseEra(data) { - // some runtimes (like Mozilla Rhino) does not preserves - // key order so we have to sort values manually - return Object.keys(data.eras).sort(function(a, b) { - return parseInt(a.substr(1)) - parseInt(b.substr(1)); - }); - } - - /** - * Returs list of supported vendors, depending on user preferences - * @return {Array} - */ - function getVendorsList() { - var allVendors = Object.keys(vendorsDB); - var vendors = prefs.getArray('caniuse.vendors'); - if (!vendors || vendors[0] == 'all') { - return allVendors; - } - - return intersection(allVendors, vendors); - } - - /** - * Returns size of version slice as defined by era identifier - * @return {Number} - */ - function getVersionSlice() { - var era = prefs.get('caniuse.era'); - var ix = erasDB.indexOf(era); - if (!~ix) { - ix = erasDB.indexOf('e-2'); - } - - return ix; - } - - // try to load caniuse database - // hide it from Require.JS parser - var db = null; - (function(r) { - if (typeof define === 'undefined' || !define.amd) { - try { - var fs = r('fs'); - var path = r('path'); - db = fs.readFileSync(path.join(__dirname, '../caniuse.json'), {encoding: 'utf8'}); - } catch(e) {} - } - })(require); - - if (db) { - parseDB(db); - } - - return { - load: parseDB, - optimize: optimize, - - /** - * Resolves prefixes for given property - * @param {String} property A property to resolve. It can start with `@` symbol - * (CSS section, like `@keyframes`) or `:` (CSS value, like `flex`) - * @return {Array} Array of resolved prefixes or null - * if prefixes can't be resolved. Empty array means property has no vendor - * prefixes - */ - resolvePrefixes: function(property) { - if (!prefs.get('caniuse.enabled') || !cssDB || !(property in cssDB)) { - return null; - } - - var prefixes = []; - var propStats = cssDB[property]; - var versions = getVersionSlice(); - - getVendorsList().forEach(function(vendor) { - var vendorVesions = vendorsDB[vendor].versions.slice(versions); - for (var i = 0, v; i < vendorVesions.length; i++) { - v = vendorVesions[i]; - if (!v) { - continue; - } - - if (~propStats[vendor][v].indexOf('x')) { - prefixes.push(vendorsDB[vendor].prefix); - break; - } - } - }); - - return utils.unique(prefixes).sort(function(a, b) { - return b.length - a.length; - }); - } - }; -}); -},{"../utils/common":73,"./preferences":28}],24:[function(require,module,exports){ -/** - * Module that contains factories for element types used by Emmet - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var factories = {}; - var reAttrs = /([@\!]?)([\w\-:]+)\s*=\s*(['"])(.*?)\3/g; - - // register resource references - function commonFactory(value) { - return {data: value}; - } - - module = module || {}; - module.exports = { - /** - * Create new element factory - * @param {String} name Element identifier - * @param {Function} factory Function that produces element of specified - * type. The object generated by this factory is automatically - * augmented with type property pointing to element - * name - * @memberOf elements - */ - add: function(name, factory) { - var that = this; - factories[name] = function() { - var elem = factory.apply(that, arguments); - if (elem) - elem.type = name; - - return elem; - }; - }, - - /** - * Returns factory for specified name - * @param {String} name - * @returns {Function} - */ - get: function(name) { - return factories[name]; - }, - - /** - * Creates new element with specified type - * @param {String} name - * @returns {Object} - */ - create: function(name) { - var args = [].slice.call(arguments, 1); - var factory = this.get(name); - return factory ? factory.apply(this, args) : null; - }, - - /** - * Check if passed element is of specified type - * @param {Object} elem - * @param {String} type - * @returns {Boolean} - */ - is: function(elem, type) { - return this.type(elem) === type; - }, - - /** - * Returns type of element - * @param {Object} elem - * @return {String} - */ - type: function(elem) { - return elem && elem.type; - } - }; - - /** - * Element factory - * @param {String} elementName Name of output element - * @param {String} attrs Attributes definition. You may also pass - * Array where each contains object with name - * and value properties, or Object - * @param {Boolean} isEmpty Is expanded element should be empty - */ - module.exports.add('element', function(elementName, attrs, isEmpty) { - var ret = { - name: elementName, - is_empty: !!isEmpty - }; - - if (attrs) { - ret.attributes = []; - if (Array.isArray(attrs)) { - ret.attributes = attrs; - } else if (typeof attrs === 'string') { - var m; - while ((m = reAttrs.exec(attrs))) { - ret.attributes.push({ - name: m[2], - value: m[4], - isDefault: m[1] == '@', - isImplied: m[1] == '!' - }); - } - } else { - ret.attributes = Object.keys(attrs).map(function(name) { - return { - name: name, - value: attrs[name] - }; - }); - } - } - - return ret; - }); - - module.exports.add('snippet', commonFactory); - module.exports.add('reference', commonFactory); - module.exports.add('empty', function() { - return {}; - }); - - return module.exports; -}); -},{}],25:[function(require,module,exports){ -/** - * Utility module that provides ordered storage of function handlers. - * Many Emmet modules' functionality can be extended/overridden by custom - * function. This modules provides unified storage of handler functions, their - * management and execution - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - - /** - * @type HandlerList - * @constructor - */ - function HandlerList() { - this._list = []; - } - - HandlerList.prototype = { - /** - * Adds function handler - * @param {Function} fn Handler - * @param {Object} options Handler options. Possible values are:

- * order : (Number) – order in handler list. Handlers - * with higher order value will be executed earlier. - */ - add: function(fn, options) { - // TODO hack for stable sort, remove after fixing `list()` - var order = this._list.length; - if (options && 'order' in options) { - order = options.order * 10000; - } - this._list.push(utils.extend({}, options, {order: order, fn: fn})); - }, - - /** - * Removes handler from list - * @param {Function} fn - */ - remove: function(fn) { - var item = utils.find(this._list, function(item) { - return item.fn === fn; - }); - if (item) { - this._list.splice(this._list.indexOf(item), 1); - } - }, - - /** - * Returns ordered list of handlers. By default, handlers - * with the same order option returned in reverse order, - * i.e. the latter function was added into the handlers list, the higher - * it will be in the returned array - * @returns {Array} - */ - list: function() { - // TODO make stable sort - return this._list.sort(function(a, b) { - return b.order - a.order; - }); - }, - - /** - * Returns ordered list of handler functions - * @returns {Array} - */ - listFn: function() { - return this.list().map(function(item) { - return item.fn; - }); - }, - - /** - * Executes handler functions in their designated order. If function - * returns skipVal, meaning that function was unable to - * handle passed args, the next function will be executed - * and so on. - * @param {Object} skipValue If function returns this value, execute - * next handler. - * @param {Array} args Arguments to pass to handler function - * @returns {Boolean} Whether any of registered handlers performed - * successfully - */ - exec: function(skipValue, args) { - args = args || []; - var result = null; - utils.find(this.list(), function(h) { - result = h.fn.apply(h, args); - if (result !== skipValue) { - return true; - } - }); - - return result; - } - }; - - return { - /** - * Factory method that produces HandlerList instance - * @returns {HandlerList} - * @memberOf handlerList - */ - create: function() { - return new HandlerList(); - } - }; -}); -},{"../utils/common":73}],26:[function(require,module,exports){ -/** - * HTML matcher: takes string and searches for HTML tag pairs for given position - * - * Unlike “classic” matchers, it parses content from the specified - * position, not from the start, so it may work even outside HTML documents - * (for example, inside strings of programming languages like JavaScript, Python - * etc.) - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var range = require('./range'); - - // Regular Expressions for parsing tags and attributes - var reOpenTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; - var reCloseTag = /^<\/([\w\:\-]+)[^>]*>/; - - function openTag(i, match) { - return { - name: match[1], - selfClose: !!match[3], - /** @type Range */ - range: range(i, match[0]), - type: 'open' - }; - } - - function closeTag(i, match) { - return { - name: match[1], - /** @type Range */ - range: range(i, match[0]), - type: 'close' - }; - } - - function comment(i, match) { - return { - /** @type Range */ - range: range(i, typeof match == 'number' ? match - i : match[0]), - type: 'comment' - }; - } - - /** - * Creates new tag matcher session - * @param {String} text - */ - function createMatcher(text) { - var memo = {}, m; - return { - /** - * Test if given position matches opening tag - * @param {Number} i - * @returns {Object} Matched tag object - */ - open: function(i) { - var m = this.matches(i); - return m && m.type == 'open' ? m : null; - }, - - /** - * Test if given position matches closing tag - * @param {Number} i - * @returns {Object} Matched tag object - */ - close: function(i) { - var m = this.matches(i); - return m && m.type == 'close' ? m : null; - }, - - /** - * Matches either opening or closing tag for given position - * @param i - * @returns - */ - matches: function(i) { - var key = 'p' + i; - - if (!(key in memo)) { - memo[key] = false; - if (text.charAt(i) == '<') { - var substr = text.slice(i); - if ((m = substr.match(reOpenTag))) { - memo[key] = openTag(i, m); - } else if ((m = substr.match(reCloseTag))) { - memo[key] = closeTag(i, m); - } - } - } - - return memo[key]; - }, - - /** - * Returns original text - * @returns {String} - */ - text: function() { - return text; - }, - - clean: function() { - memo = text = m = null; - } - }; - } - - function matches(text, pos, pattern) { - return text.substring(pos, pos + pattern.length) == pattern; - } - - /** - * Search for closing pair of opening tag - * @param {Object} open Open tag instance - * @param {Object} matcher Matcher instance - */ - function findClosingPair(open, matcher) { - var stack = [], tag = null; - var text = matcher.text(); - - for (var pos = open.range.end, len = text.length; pos < len; pos++) { - if (matches(text, pos, '')) { - pos = j + 3; - break; - } - } - } - - if ((tag = matcher.matches(pos))) { - if (tag.type == 'open' && !tag.selfClose) { - stack.push(tag.name); - } else if (tag.type == 'close') { - if (!stack.length) { // found valid pair? - return tag.name == open.name ? tag : null; - } - - // check if current closing tag matches previously opened one - if (stack[stack.length - 1] == tag.name) { - stack.pop(); - } else { - var found = false; - while (stack.length && !found) { - var last = stack.pop(); - if (last == tag.name) { - found = true; - } - } - - if (!stack.length && !found) { - return tag.name == open.name ? tag : null; - } - } - } - - pos = tag.range.end - 1; - } - } - } - - return { - /** - * Main function: search for tag pair in text for given - * position - * @memberOf htmlMatcher - * @param {String} text - * @param {Number} pos - * @returns {Object} - */ - find: function(text, pos) { - var matcher = createMatcher(text); - var open = null, close = null; - var j, jl; - - for (var i = pos; i >= 0; i--) { - if ((open = matcher.open(i))) { - // found opening tag - if (open.selfClose) { - if (open.range.cmp(pos, 'lt', 'gt')) { - // inside self-closing tag, found match - break; - } - - // outside self-closing tag, continue - continue; - } - - close = findClosingPair(open, matcher); - if (close) { - // found closing tag. - var r = range.create2(open.range.start, close.range.end); - if (r.contains(pos)) { - break; - } - } else if (open.range.contains(pos)) { - // we inside empty HTML tag like
- break; - } - - open = null; - } else if (matches(text, i, '-->')) { - // skip back to comment start - for (j = i - 1; j >= 0; j--) { - if (matches(text, j, '-->')) { - // found another comment end, do nothing - break; - } else if (matches(text, j, '')) { - j += 3; - break; - } - } - - open = comment(i, j); - break; - } - } - - matcher.clean(); - - if (open) { - var outerRange = null; - var innerRange = null; - - if (close) { - outerRange = range.create2(open.range.start, close.range.end); - innerRange = range.create2(open.range.end, close.range.start); - } else { - outerRange = innerRange = range.create2(open.range.start, open.range.end); - } - - if (open.type == 'comment') { - // adjust positions of inner range for comment - var _c = outerRange.substring(text); - innerRange.start += _c.length - _c.replace(/^<\!--\s*/, '').length; - innerRange.end -= _c.length - _c.replace(/\s*-->$/, '').length; - } - - return { - open: open, - close: close, - type: open.type == 'comment' ? 'comment' : 'tag', - innerRange: innerRange, - innerContent: function() { - return this.innerRange.substring(text); - }, - outerRange: outerRange, - outerContent: function() { - return this.outerRange.substring(text); - }, - range: !innerRange.length() || !innerRange.cmp(pos, 'lte', 'gte') ? outerRange : innerRange, - content: function() { - return this.range.substring(text); - }, - source: text - }; - } - }, - - /** - * The same as find() method, but restricts matched result - * to tag type - * @param {String} text - * @param {Number} pos - * @returns {Object} - */ - tag: function(text, pos) { - var result = this.find(text, pos); - if (result && result.type == 'tag') { - return result; - } - } - }; -}); -},{"./range":30}],27:[function(require,module,exports){ -/** - * Simple logger for Emmet - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - return { - log: function() { - if (typeof console != 'undefined' && console.log) { - console.log.apply(console, arguments); - } - } - } -}) -},{}],28:[function(require,module,exports){ -/** - * Common module's preferences storage. This module - * provides general storage for all module preferences, their description and - * default values.

- * - * This module can also be used to list all available properties to create - * UI for updating properties - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - - var preferences = {}; - var defaults = {}; - var _dbgDefaults = null; - var _dbgPreferences = null; - - function toBoolean(val) { - if (typeof val === 'string') { - val = val.toLowerCase(); - return val == 'yes' || val == 'true' || val == '1'; - } - - return !!val; - } - - function isValueObj(obj) { - return typeof obj === 'object' - && !Array.isArray(obj) - && 'value' in obj - && Object.keys(obj).length < 3; - } - - return { - /** - * Creates new preference item with default value - * @param {String} name Preference name. You can also pass object - * with many options - * @param {Object} value Preference default value - * @param {String} description Item textual description - * @memberOf preferences - */ - define: function(name, value, description) { - var prefs = name; - if (typeof name === 'string') { - prefs = {}; - prefs[name] = { - value: value, - description: description - }; - } - - Object.keys(prefs).forEach(function(k) { - var v = prefs[k]; - defaults[k] = isValueObj(v) ? v : {value: v}; - }); - }, - - /** - * Updates preference item value. Preference value should be defined - * first with define method. - * @param {String} name Preference name. You can also pass object - * with many options - * @param {Object} value Preference default value - * @memberOf preferences - */ - set: function(name, value) { - var prefs = name; - if (typeof name === 'string') { - prefs = {}; - prefs[name] = value; - } - - Object.keys(prefs).forEach(function(k) { - var v = prefs[k]; - if (!(k in defaults)) { - throw new Error('Property "' + k + '" is not defined. You should define it first with `define` method of current module'); - } - - // do not set value if it equals to default value - if (v !== defaults[k].value) { - // make sure we have value of correct type - switch (typeof defaults[k].value) { - case 'boolean': - v = toBoolean(v); - break; - case 'number': - v = parseInt(v + '', 10) || 0; - break; - default: // convert to string - if (v !== null) { - v += ''; - } - } - - preferences[k] = v; - } else if (k in preferences) { - delete preferences[k]; - } - }); - }, - - /** - * Returns preference value - * @param {String} name - * @returns {String} Returns undefined if preference is - * not defined - */ - get: function(name) { - if (name in preferences) { - return preferences[name]; - } - - if (name in defaults) { - return defaults[name].value; - } - - return void 0; - }, - - /** - * Returns comma-separated preference value as array of values - * @param {String} name - * @returns {Array} Returns undefined if preference is - * not defined, null if string cannot be converted to array - */ - getArray: function(name) { - var val = this.get(name); - if (typeof val === 'undefined' || val === null || val === '') { - return null; - } - - val = val.split(',').map(utils.trim); - if (!val.length) { - return null; - } - - return val; - }, - - /** - * Returns comma and colon-separated preference value as dictionary - * @param {String} name - * @returns {Object} - */ - getDict: function(name) { - var result = {}; - this.getArray(name).forEach(function(val) { - var parts = val.split(':'); - result[parts[0]] = parts[1]; - }); - - return result; - }, - - /** - * Returns description of preference item - * @param {String} name Preference name - * @returns {Object} - */ - description: function(name) { - return name in defaults ? defaults[name].description : void 0; - }, - - /** - * Completely removes specified preference(s) - * @param {String} name Preference name (or array of names) - */ - remove: function(name) { - if (!Array.isArray(name)) { - name = [name]; - } - - name.forEach(function(key) { - if (key in preferences) { - delete preferences[key]; - } - - if (key in defaults) { - delete defaults[key]; - } - }); - }, - - /** - * Returns sorted list of all available properties - * @returns {Array} - */ - list: function() { - return Object.keys(defaults).sort().map(function(key) { - return { - name: key, - value: this.get(key), - type: typeof defaults[key].value, - description: defaults[key].description - }; - }, this); - }, - - /** - * Loads user-defined preferences from JSON - * @param {Object} json - * @returns - */ - load: function(json) { - Object.keys(json).forEach(function(key) { - this.set(key, json[key]); - }, this); - }, - - /** - * Returns hash of user-modified preferences - * @returns {Object} - */ - exportModified: function() { - return utils.extend({}, preferences); - }, - - /** - * Reset to defaults - * @returns - */ - reset: function() { - preferences = {}; - }, - - /** - * For unit testing: use empty storage - */ - _startTest: function() { - _dbgDefaults = defaults; - _dbgPreferences = preferences; - defaults = {}; - preferences = {}; - }, - - /** - * For unit testing: restore original storage - */ - _stopTest: function() { - defaults = _dbgDefaults; - preferences = _dbgPreferences; - } - }; -}); -},{"../utils/common":73}],29:[function(require,module,exports){ -/** - * Output profile module. - * Profile defines how XHTML output data should look like - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var resources = require('./resources'); - var prefs = require('./preferences'); - - prefs.define('profile.allowCompactBoolean', true, - 'This option can be used to globally disable compact form of boolean ' + - 'attribues (attributes where name and value are equal). With compact' + - 'form enabled, HTML tags can be outputted as <div contenteditable> ' + - 'instead of <div contenteditable="contenteditable">'); - - prefs.define('profile.booleanAttributes', '^contenteditable|seamless$', - 'A regular expression for attributes that should be boolean by default.' + - 'If attribute name matches this expression, you don’t have to write dot ' + - 'after attribute name in Emmet abbreviation to mark it as boolean.'); - - var profiles = {}; - - var defaultProfile = { - tag_case: 'asis', - attr_case: 'asis', - attr_quotes: 'double', - - // Each tag on new line - tag_nl: 'decide', - - // With tag_nl === true, defines if leaf node (e.g. node with no children) - // should have formatted line breaks - tag_nl_leaf: false, - - place_cursor: true, - - // Indent tags - indent: true, - - // How many inline elements should be to force line break - // (set to 0 to disable) - inline_break: 3, - - // Produce compact notation of boolean attribues: - // attributes where name and value are equal. - // With this option enabled, HTML filter will - // produce
instead of
- compact_bool: false, - - // Use self-closing style for writing empty elements, e.g.
or
- self_closing_tag: 'xhtml', - - // Profile-level output filters, re-defines syntax filters - filters: '', - - // Additional filters applied to abbreviation. - // Unlike "filters", this preference doesn't override default filters - // but add the instead every time given profile is chosen - extraFilters: '' - }; - - /** - * @constructor - * @type OutputProfile - * @param {Object} options - */ - function OutputProfile(options) { - utils.extend(this, defaultProfile, options); - } - - OutputProfile.prototype = { - /** - * Transforms tag name case depending on current profile settings - * @param {String} name String to transform - * @returns {String} - */ - tagName: function(name) { - return stringCase(name, this.tag_case); - }, - - /** - * Transforms attribute name case depending on current profile settings - * @param {String} name String to transform - * @returns {String} - */ - attributeName: function(name) { - return stringCase(name, this.attr_case); - }, - - /** - * Returns quote character for current profile - * @returns {String} - */ - attributeQuote: function() { - return this.attr_quotes == 'single' ? "'" : '"'; - }, - - /** - * Returns self-closing tag symbol for current profile - * @returns {String} - */ - selfClosing: function() { - if (this.self_closing_tag == 'xhtml') - return ' /'; - - if (this.self_closing_tag === true) - return '/'; - - return ''; - }, - - /** - * Returns cursor token based on current profile settings - * @returns {String} - */ - cursor: function() { - return this.place_cursor ? utils.getCaretPlaceholder() : ''; - }, - - /** - * Check if attribute with given name is boolean, - * e.g. written as `contenteditable` instead of - * `contenteditable="contenteditable"` - * @param {String} name Attribute name - * @return {Boolean} - */ - isBoolean: function(name, value) { - if (name == value) { - return true; - } - - var boolAttrs = prefs.get('profile.booleanAttributes'); - if (!value && boolAttrs) { - boolAttrs = new RegExp(boolAttrs, 'i'); - return boolAttrs.test(name); - } - - return false; - }, - - /** - * Check if compact boolean attribute record is - * allowed for current profile - * @return {Boolean} - */ - allowCompactBoolean: function() { - return this.compact_bool && prefs.get('profile.allowCompactBoolean'); - } - }; - - /** - * Helper function that converts string case depending on - * caseValue - * @param {String} str String to transform - * @param {String} caseValue Case value: can be lower, - * upper and leave - * @returns {String} - */ - function stringCase(str, caseValue) { - switch (String(caseValue || '').toLowerCase()) { - case 'lower': - return str.toLowerCase(); - case 'upper': - return str.toUpperCase(); - } - - return str; - } - - /** - * Creates new output profile - * @param {String} name Profile name - * @param {Object} options Profile options - */ - function createProfile(name, options) { - return profiles[name.toLowerCase()] = new OutputProfile(options); - } - - function createDefaultProfiles() { - createProfile('xhtml'); - createProfile('html', {self_closing_tag: false, compact_bool: true}); - createProfile('xml', {self_closing_tag: true, tag_nl: true}); - createProfile('plain', {tag_nl: false, indent: false, place_cursor: false}); - createProfile('line', {tag_nl: false, indent: false, extraFilters: 's'}); - createProfile('css', {tag_nl: true}); - createProfile('css_line', {tag_nl: false}); - } - - createDefaultProfiles(); - - return { - /** - * Creates new output profile and adds it into internal dictionary - * @param {String} name Profile name - * @param {Object} options Profile options - * @memberOf emmet.profile - * @returns {Object} New profile - */ - create: function(name, options) { - if (arguments.length == 2) - return createProfile(name, options); - else - // create profile object only - return new OutputProfile(utils.defaults(name || {}, defaultProfile)); - }, - - /** - * Returns profile by its name. If profile wasn't found, returns - * 'plain' profile - * @param {String} name Profile name. Might be profile itself - * @param {String} syntax. Optional. Current editor syntax. If defined, - * profile is searched in resources first, then in predefined profiles - * @returns {Object} - */ - get: function(name, syntax) { - if (!name && syntax) { - // search in user resources first - var profile = resources.findItem(syntax, 'profile'); - if (profile) { - name = profile; - } - } - - if (!name) { - return profiles.plain; - } - - if (name instanceof OutputProfile) { - return name; - } - - if (typeof name === 'string' && name.toLowerCase() in profiles) { - return profiles[name.toLowerCase()]; - } - - return this.create(name); - }, - - /** - * Deletes profile with specified name - * @param {String} name Profile name - */ - remove: function(name) { - name = (name || '').toLowerCase(); - if (name in profiles) - delete profiles[name]; - }, - - /** - * Resets all user-defined profiles - */ - reset: function() { - profiles = {}; - createDefaultProfiles(); - }, - - /** - * Helper function that converts string case depending on - * caseValue - * @param {String} str String to transform - * @param {String} caseValue Case value: can be lower, - * upper and leave - * @returns {String} - */ - stringCase: stringCase - }; -}); -},{"../utils/common":73,"./preferences":28,"./resources":31}],30:[function(require,module,exports){ -/** - * Helper module to work with ranges - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - function cmp(a, b, op) { - switch (op) { - case 'eq': - case '==': - return a === b; - case 'lt': - case '<': - return a < b; - case 'lte': - case '<=': - return a <= b; - case 'gt': - case '>': - return a > b; - case 'gte': - case '>=': - return a >= b; - } - } - - - /** - * @type Range - * @constructor - * @param {Object} start - * @param {Number} len - */ - function Range(start, len) { - if (typeof start === 'object' && 'start' in start) { - // create range from object stub - this.start = Math.min(start.start, start.end); - this.end = Math.max(start.start, start.end); - } else if (Array.isArray(start)) { - this.start = start[0]; - this.end = start[1]; - } else { - len = typeof len === 'string' ? len.length : +len; - this.start = start; - this.end = start + len; - } - } - - Range.prototype = { - length: function() { - return Math.abs(this.end - this.start); - }, - - /** - * Returns true if passed range is equals to current one - * @param {Range} range - * @returns {Boolean} - */ - equal: function(range) { - return this.cmp(range, 'eq', 'eq'); -// return this.start === range.start && this.end === range.end; - }, - - /** - * Shifts indexes position with passed delta - * @param {Number} delta - * @returns {Range} range itself - */ - shift: function(delta) { - this.start += delta; - this.end += delta; - return this; - }, - - /** - * Check if two ranges are overlapped - * @param {Range} range - * @returns {Boolean} - */ - overlap: function(range) { - return range.start <= this.end && range.end >= this.start; - }, - - /** - * Finds intersection of two ranges - * @param {Range} range - * @returns {Range} null if ranges does not overlap - */ - intersection: function(range) { - if (this.overlap(range)) { - var start = Math.max(range.start, this.start); - var end = Math.min(range.end, this.end); - return new Range(start, end - start); - } - - return null; - }, - - /** - * Returns the union of the thow ranges. - * @param {Range} range - * @returns {Range} null if ranges are not overlapped - */ - union: function(range) { - if (this.overlap(range)) { - var start = Math.min(range.start, this.start); - var end = Math.max(range.end, this.end); - return new Range(start, end - start); - } - - return null; - }, - - /** - * Returns a Boolean value that indicates whether a specified position - * is in a given range. - * @param {Number} loc - */ - inside: function(loc) { - return this.cmp(loc, 'lte', 'gt'); -// return this.start <= loc && this.end > loc; - }, - - /** - * Returns a Boolean value that indicates whether a specified position - * is in a given range, but not equals bounds. - * @param {Number} loc - */ - contains: function(loc) { - return this.cmp(loc, 'lt', 'gt'); - }, - - /** - * Check if current range completely includes specified one - * @param {Range} r - * @returns {Boolean} - */ - include: function(r) { - return this.cmp(r, 'lte', 'gte'); -// return this.start <= r.start && this.end >= r.end; - }, - - /** - * Low-level comparision method - * @param {Number} loc - * @param {String} left Left comparison operator - * @param {String} right Right comaprison operator - */ - cmp: function(loc, left, right) { - var a, b; - if (loc instanceof Range) { - a = loc.start; - b = loc.end; - } else { - a = b = loc; - } - - return cmp(this.start, a, left || '<=') && cmp(this.end, b, right || '>'); - }, - - /** - * Returns substring of specified str for current range - * @param {String} str - * @returns {String} - */ - substring: function(str) { - return this.length() > 0 - ? str.substring(this.start, this.end) - : ''; - }, - - /** - * Creates copy of current range - * @returns {Range} - */ - clone: function() { - return new Range(this.start, this.length()); - }, - - /** - * @returns {Array} - */ - toArray: function() { - return [this.start, this.end]; - }, - - toString: function() { - return this.valueOf(); - }, - - valueOf: function() { - return '{' + this.start + ', ' + this.length() + '}'; - } - }; - - /** - * Creates new range object instance - * @param {Object} start Range start or array with 'start' and 'end' - * as two first indexes or object with 'start' and 'end' properties - * @param {Number} len Range length or string to produce range from - * @returns {Range} - */ - module.exports = function(start, len) { - if (typeof start == 'undefined' || start === null) - return null; - - if (start instanceof Range) - return start; - - if (typeof start == 'object' && 'start' in start && 'end' in start) { - len = start.end - start.start; - start = start.start; - } - - return new Range(start, len); - }; - - module.exports.create = module.exports; - - module.exports.isRange = function(val) { - return val instanceof Range; - }; - - /** - * Range object factory, the same as this.create() - * but last argument represents end of range, not length - * @returns {Range} - */ - module.exports.create2 = function(start, end) { - if (typeof start === 'number' && typeof end === 'number') { - end -= start; - } - - return this.create(start, end); - }; - - /** - * Helper function that sorts ranges in order as they - * appear in text - * @param {Array} ranges - * @return {Array} - */ - module.exports.sort = function(ranges, reverse) { - ranges = ranges.sort(function(a, b) { - if (a.start === b.start) { - return b.end - a.end; - } - - return a.start - b.start; - }); - - reverse && ranges.reverse(); - return ranges; - }; - - return module.exports; -}); -},{}],31:[function(require,module,exports){ -/** - * Parsed resources (snippets, abbreviations, variables, etc.) for Emmet. - * Contains convenient method to get access for snippets with respect of - * inheritance. Also provides ability to store data in different vocabularies - * ('system' and 'user') for fast and safe resource update - * @author Sergey Chikuyonok (serge.che@gmail.com) - * @link http://chikuyonok.ru - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var handlerList = require('./handlerList'); - var utils = require('../utils/common'); - var elements = require('./elements'); - var logger = require('../assets/logger'); - var stringScore = require('../vendor/stringScore'); - var cssResolver = require('../resolver/css'); - - var VOC_SYSTEM = 'system'; - var VOC_USER = 'user'; - - var cache = {}; - - /** Regular expression for XML tag matching */ - var reTag = /^<(\w+\:?[\w\-]*)((?:\s+[@\!]?[\w\:\-]+\s*=\s*(['"]).*?\3)*)\s*(\/?)>/; - - var systemSettings = {}; - var userSettings = {}; - - /** @type HandlerList List of registered abbreviation resolvers */ - var resolvers = handlerList.create(); - - function each(obj, fn) { - if (!obj) { - return; - } - - Object.keys(obj).forEach(function(key) { - fn(obj[key], key); - }); - } - - /** - * Normalizes caret plceholder in passed text: replaces | character with - * default caret placeholder - * @param {String} text - * @returns {String} - */ - function normalizeCaretPlaceholder(text) { - return utils.replaceUnescapedSymbol(text, '|', utils.getCaretPlaceholder()); - } - - function parseItem(name, value, type) { - value = normalizeCaretPlaceholder(value); - - if (type == 'snippets') { - return elements.create('snippet', value); - } - - if (type == 'abbreviations') { - return parseAbbreviation(name, value); - } - } - - /** - * Parses single abbreviation - * @param {String} key Abbreviation name - * @param {String} value Abbreviation value - * @return {Object} - */ - function parseAbbreviation(key, value) { - key = utils.trim(key); - var m; - if ((m = reTag.exec(value))) { - return elements.create('element', m[1], m[2], m[4] == '/'); - } else { - // assume it's reference to another abbreviation - return elements.create('reference', value); - } - } - - /** - * Normalizes snippet key name for better fuzzy search - * @param {String} str - * @returns {String} - */ - function normalizeName(str) { - return str.replace(/:$/, '').replace(/:/g, '-'); - } - - function expandSnippetsDefinition(snippets) { - var out = {}; - each(snippets, function(val, key) { - var items = key.split('|'); - // do not use iterators for better performance - for (var i = items.length - 1; i >= 0; i--) { - out[items[i]] = val; - } - }); - - return out; - } - - utils.extend(exports, { - /** - * Sets new unparsed data for specified settings vocabulary - * @param {Object} data - * @param {String} type Vocabulary type ('system' or 'user') - * @memberOf resources - */ - setVocabulary: function(data, type) { - cache = {}; - - // sections like "snippets" and "abbreviations" could have - // definitions like `"f|fs": "fieldset"` which is the same as distinct - // "f" and "fs" keys both equals to "fieldset". - // We should parse these definitions first - var voc = {}; - each(data, function(section, syntax) { - var _section = {}; - each(section, function(subsection, name) { - if (name == 'abbreviations' || name == 'snippets') { - subsection = expandSnippetsDefinition(subsection); - } - _section[name] = subsection; - }); - - voc[syntax] = _section; - }); - - - if (type == VOC_SYSTEM) { - systemSettings = voc; - } else { - userSettings = voc; - } - }, - - /** - * Returns resource vocabulary by its name - * @param {String} name Vocabulary name ('system' or 'user') - * @return {Object} - */ - getVocabulary: function(name) { - return name == VOC_SYSTEM ? systemSettings : userSettings; - }, - - /** - * Returns resource (abbreviation, snippet, etc.) matched for passed - * abbreviation - * @param {AbbreviationNode} node - * @param {String} syntax - * @returns {Object} - */ - getMatchedResource: function(node, syntax) { - return resolvers.exec(null, utils.toArray(arguments)) - || this.findSnippet(syntax, node.name()); - }, - - /** - * Returns variable value - * @return {String} - */ - getVariable: function(name) { - return (this.getSection('variables') || {})[name]; - }, - - /** - * Store runtime variable in user storage - * @param {String} name Variable name - * @param {String} value Variable value - */ - setVariable: function(name, value){ - var voc = this.getVocabulary('user') || {}; - if (!('variables' in voc)) - voc.variables = {}; - - voc.variables[name] = value; - this.setVocabulary(voc, 'user'); - }, - - /** - * Check if there are resources for specified syntax - * @param {String} syntax - * @return {Boolean} - */ - hasSyntax: function(syntax) { - return syntax in this.getVocabulary(VOC_USER) - || syntax in this.getVocabulary(VOC_SYSTEM); - }, - - /** - * Registers new abbreviation resolver. - * @param {Function} fn Abbreviation resolver which will receive - * abbreviation as first argument and should return parsed abbreviation - * object if abbreviation has handled successfully, null - * otherwise - * @param {Object} options Options list as described in - * {@link HandlerList#add()} method - */ - addResolver: function(fn, options) { - resolvers.add(fn, options); - }, - - removeResolver: function(fn) { - resolvers.remove(fn); - }, - - /** - * Returns actual section data, merged from both - * system and user data - * @param {String} name Section name (syntax) - * @param {String} ...args Subsections - * @returns - */ - getSection: function(name) { - if (!name) - return null; - - if (!(name in cache)) { - cache[name] = utils.deepMerge({}, systemSettings[name], userSettings[name]); - } - - var data = cache[name], subsections = utils.toArray(arguments, 1), key; - while (data && (key = subsections.shift())) { - if (key in data) { - data = data[key]; - } else { - return null; - } - } - - return data; - }, - - /** - * Recursively searches for a item inside top level sections (syntaxes) - * with respect of `extends` attribute - * @param {String} topSection Top section name (syntax) - * @param {String} subsection Inner section name - * @returns {Object} - */ - findItem: function(topSection, subsection) { - var data = this.getSection(topSection); - while (data) { - if (subsection in data) - return data[subsection]; - - data = this.getSection(data['extends']); - } - }, - - /** - * Recursively searches for a snippet definition inside syntax section. - * Definition is searched inside `snippets` and `abbreviations` - * subsections - * @param {String} syntax Top-level section name (syntax) - * @param {String} name Snippet name - * @returns {Object} - */ - findSnippet: function(syntax, name, memo) { - if (!syntax || !name) - return null; - - memo = memo || []; - - var names = [name]; - // create automatic aliases to properties with colons, - // e.g. pos-a == pos:a - if (~name.indexOf('-')) { - names.push(name.replace(/\-/g, ':')); - } - - var data = this.getSection(syntax), matchedItem = null; - ['snippets', 'abbreviations'].some(function(sectionName) { - var data = this.getSection(syntax, sectionName); - if (data) { - return names.some(function(n) { - if (data[n]) { - return matchedItem = parseItem(n, data[n], sectionName); - } - }); - } - }, this); - - memo.push(syntax); - if (!matchedItem && data['extends'] && !~memo.indexOf(data['extends'])) { - // try to find item in parent syntax section - return this.findSnippet(data['extends'], name, memo); - } - - return matchedItem; - }, - - /** - * Performs fuzzy search of snippet definition - * @param {String} syntax Top-level section name (syntax) - * @param {String} name Snippet name - * @returns - */ - fuzzyFindSnippet: function(syntax, name, minScore) { - var result = this.fuzzyFindMatches(syntax, name, minScore)[0]; - if (result) { - return result.value.parsedValue; - } - }, - - fuzzyFindMatches: function(syntax, name, minScore) { - minScore = minScore || 0.3; - name = normalizeName(name); - var snippets = this.getAllSnippets(syntax); - - return Object.keys(snippets) - .map(function(key) { - var value = snippets[key]; - return { - key: key, - score: stringScore.score(value.nk, name, 0.1), - value: value - }; - }) - .filter(function(item) { - return item.score >= minScore; - }) - .sort(function(a, b) { - return a.score - b.score; - }) - .reverse(); - }, - - /** - * Returns plain dictionary of all available abbreviations and snippets - * for specified syntax with respect of inheritance - * @param {String} syntax - * @returns {Object} - */ - getAllSnippets: function(syntax) { - var cacheKey = 'all-' + syntax; - if (!cache[cacheKey]) { - var stack = [], sectionKey = syntax; - var memo = []; - - do { - var section = this.getSection(sectionKey); - if (!section) - break; - - ['snippets', 'abbreviations'].forEach(function(sectionName) { - var stackItem = {}; - each(section[sectionName] || null, function(v, k) { - stackItem[k] = { - nk: normalizeName(k), - value: v, - parsedValue: parseItem(k, v, sectionName), - type: sectionName - }; - }); - - stack.push(stackItem); - }); - - memo.push(sectionKey); - sectionKey = section['extends']; - } while (sectionKey && !~memo.indexOf(sectionKey)); - - - cache[cacheKey] = utils.extend.apply(utils, stack.reverse()); - } - - return cache[cacheKey]; - }, - - /** - * Returns newline character - * @returns {String} - */ - getNewline: function() { - var nl = this.getVariable('newline'); - return typeof nl === 'string' ? nl : '\n'; - }, - - /** - * Sets new newline character that will be used in output - * @param {String} str - */ - setNewline: function(str) { - this.setVariable('newline', str); - this.setVariable('nl', str); - } - }); - - // XXX add default resolvers - exports.addResolver(cssResolver.resolve.bind(cssResolver)); - - // try to load snippets - // hide it from Require.JS parser - (function(r) { - if (typeof define === 'undefined' || !define.amd) { - try { - var fs = r('fs'); - var path = r('path'); - - var defaultSnippets = fs.readFileSync(path.join(__dirname, '../snippets.json'), {encoding: 'utf8'}); - exports.setVocabulary(JSON.parse(defaultSnippets), VOC_SYSTEM); - } catch (e) {} - } - })(require); - - - return exports; -}); -},{"../assets/logger":27,"../resolver/css":64,"../utils/common":73,"../vendor/stringScore":79,"./elements":24,"./handlerList":25}],32:[function(require,module,exports){ -/** - * A trimmed version of CodeMirror's StringStream module for string parsing - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - /** - * @type StringStream - * @constructor - * @param {String} string Assuming that bound string should be - * immutable - */ - function StringStream(string) { - this.pos = this.start = 0; - this.string = string; - this._length = string.length; - } - - StringStream.prototype = { - /** - * Returns true only if the stream is at the end of the line. - * @returns {Boolean} - */ - eol: function() { - return this.pos >= this._length; - }, - - /** - * Returns true only if the stream is at the start of the line - * @returns {Boolean} - */ - sol: function() { - return this.pos === 0; - }, - - /** - * Returns the next character in the stream without advancing it. - * Will return undefined at the end of the line. - * @returns {String} - */ - peek: function() { - return this.string.charAt(this.pos); - }, - - /** - * Returns the next character in the stream and advances it. - * Also returns undefined when no more characters are available. - * @returns {String} - */ - next: function() { - if (this.pos < this._length) - return this.string.charAt(this.pos++); - }, - - /** - * match can be a character, a regular expression, or a function that - * takes a character and returns a boolean. If the next character in the - * stream 'matches' the given argument, it is consumed and returned. - * Otherwise, undefined is returned. - * @param {Object} match - * @returns {String} - */ - eat: function(match) { - var ch = this.string.charAt(this.pos), ok; - if (typeof match == "string") - ok = ch == match; - else - ok = ch && (match.test ? match.test(ch) : match(ch)); - - if (ok) { - ++this.pos; - return ch; - } - }, - - /** - * Repeatedly calls eat with the given argument, until it - * fails. Returns true if any characters were eaten. - * @param {Object} match - * @returns {Boolean} - */ - eatWhile: function(match) { - var start = this.pos; - while (this.eat(match)) {} - return this.pos > start; - }, - - /** - * Shortcut for eatWhile when matching white-space. - * @returns {Boolean} - */ - eatSpace: function() { - var start = this.pos; - while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) - ++this.pos; - return this.pos > start; - }, - - /** - * Moves the position to the end of the line. - */ - skipToEnd: function() { - this.pos = this._length; - }, - - /** - * Skips to the next occurrence of the given character, if found on the - * current line (doesn't advance the stream if the character does not - * occur on the line). Returns true if the character was found. - * @param {String} ch - * @returns {Boolean} - */ - skipTo: function(ch) { - var found = this.string.indexOf(ch, this.pos); - if (found > -1) { - this.pos = found; - return true; - } - }, - - /** - * Skips to close character which is pair to open - * character, considering possible pair nesting. This function is used - * to consume pair of characters, like opening and closing braces - * @param {String} open - * @param {String} close - * @returns {Boolean} Returns true if pair was successfully - * consumed - */ - skipToPair: function(open, close, skipString) { - var braceCount = 0, ch; - var pos = this.pos, len = this._length; - while (pos < len) { - ch = this.string.charAt(pos++); - if (ch == open) { - braceCount++; - } else if (ch == close) { - braceCount--; - if (braceCount < 1) { - this.pos = pos; - return true; - } - } else if (skipString && (ch == '"' || ch == "'")) { - this.skipString(ch); - } - } - - return false; - }, - - /** - * A helper function which, in case of either single or - * double quote was found in current position, skips entire - * string (quoted value) - * @return {Boolean} Wether quoted string was skipped - */ - skipQuoted: function(noBackup) { - var ch = this.string.charAt(noBackup ? this.pos : this.pos - 1); - if (ch === '"' || ch === "'") { - if (noBackup) { - this.pos++; - } - return this.skipString(ch); - } - }, - - /** - * A custom function to skip string literal, e.g. a "double-quoted" - * or 'single-quoted' value - * @param {String} quote An opening quote - * @return {Boolean} - */ - skipString: function(quote) { - var pos = this.pos, len = this._length, ch; - while (pos < len) { - ch = this.string.charAt(pos++); - if (ch == '\\') { - continue; - } else if (ch == quote) { - this.pos = pos; - return true; - } - } - - return false; - }, - - /** - * Backs up the stream n characters. Backing it up further than the - * start of the current token will cause things to break, so be careful. - * @param {Number} n - */ - backUp : function(n) { - this.pos -= n; - }, - - /** - * Act like a multi-character eat—if consume is true or - * not given—or a look-ahead that doesn't update the stream position—if - * it is false. pattern can be either a string or a - * regular expression starting with ^. When it is a string, - * caseInsensitive can be set to true to make the match - * case-insensitive. When successfully matching a regular expression, - * the returned value will be the array returned by match, - * in case you need to extract matched groups. - * - * @param {RegExp} pattern - * @param {Boolean} consume - * @param {Boolean} caseInsensitive - * @returns - */ - match: function(pattern, consume, caseInsensitive) { - if (typeof pattern == "string") { - var cased = caseInsensitive - ? function(str) {return str.toLowerCase();} - : function(str) {return str;}; - - if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { - if (consume !== false) - this.pos += pattern.length; - return true; - } - } else { - var match = this.string.slice(this.pos).match(pattern); - if (match && consume !== false) - this.pos += match[0].length; - return match; - } - }, - - /** - * Get the string between the start of the current token and the - * current stream position. - * @returns {String} - */ - current: function(backUp) { - return this.string.slice(this.start, this.pos - (backUp ? 1 : 0)); - } - }; - - module.exports = function(string) { - return new StringStream(string); - }; - - /** @deprecated */ - module.exports.create = module.exports; - return module.exports; -}); -},{}],33:[function(require,module,exports){ -/** - * Utility module for handling tabstops tokens generated by Emmet's - * "Expand Abbreviation" action. The main extract method will take - * raw text (for example: ${0} some ${1:text}), find all tabstops - * occurrences, replace them with tokens suitable for your editor of choice and - * return object with processed text and list of found tabstops and their ranges. - * For sake of portability (Objective-C/Java) the tabstops list is a plain - * sorted array with plain objects. - * - * Placeholders with the same are meant to be linked in your editor. - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - var utils = require('../utils/common'); - var stringStream = require('./stringStream'); - var resources = require('./resources'); - - /** - * Global placeholder value, automatically incremented by - * variablesResolver() function - */ - var startPlaceholderNum = 100; - var tabstopIndex = 0; - - var defaultOptions = { - replaceCarets: false, - escape: function(ch) { - return '\\' + ch; - }, - tabstop: function(data) { - return data.token; - }, - variable: function(data) { - return data.token; - } - }; - - return { - /** - * Main function that looks for a tabstops in provided text - * and returns a processed version of text with expanded - * placeholders and list of tabstops found. - * @param {String} text Text to process - * @param {Object} options List of processor options:
- * - * replaceCarets : Boolean — replace all default - * caret placeholders (like {%::emmet-caret::%}) with ${0:caret}
- * - * escape : Function — function that handle escaped - * characters (mostly '$'). By default, it returns the character itself - * to be displayed as is in output, but sometimes you will use - * extract method as intermediate solution for further - * processing and want to keep character escaped. Thus, you should override - * escape method to return escaped symbol (e.g. '\\$')
- * - * tabstop : Function – a tabstop handler. Receives - * a single argument – an object describing token: its position, number - * group, placeholder and token itself. Should return a replacement - * string that will appear in final output - * - * variable : Function – variable handler. Receives - * a single argument – an object describing token: its position, name - * and original token itself. Should return a replacement - * string that will appear in final output - * - * @returns {Object} Object with processed text property - * and array of tabstops found - * @memberOf tabStops - */ - extract: function(text, options) { - // prepare defaults - var placeholders = {carets: ''}; - var marks = []; - - options = utils.extend({}, defaultOptions, options, { - tabstop: function(data) { - var token = data.token; - var ret = ''; - if (data.placeholder == 'cursor') { - marks.push({ - start: data.start, - end: data.start + token.length, - group: 'carets', - value: '' - }); - } else { - // unify placeholder value for single group - if ('placeholder' in data) - placeholders[data.group] = data.placeholder; - - if (data.group in placeholders) - ret = placeholders[data.group]; - - marks.push({ - start: data.start, - end: data.start + token.length, - group: data.group, - value: ret - }); - } - - return token; - } - }); - - if (options.replaceCarets) { - text = text.replace(new RegExp( utils.escapeForRegexp( utils.getCaretPlaceholder() ), 'g'), '${0:cursor}'); - } - - // locate tabstops and unify group's placeholders - text = this.processText(text, options); - - // now, replace all tabstops with placeholders - var buf = '', lastIx = 0; - var tabStops = marks.map(function(mark) { - buf += text.substring(lastIx, mark.start); - - var pos = buf.length; - var ph = placeholders[mark.group] || ''; - - buf += ph; - lastIx = mark.end; - - return { - group: mark.group, - start: pos, - end: pos + ph.length - }; - }); - - buf += text.substring(lastIx); - - return { - text: buf, - tabstops: tabStops.sort(function(a, b) { - return a.start - b.start; - }) - }; - }, - - /** - * Text processing routine. Locates escaped characters and tabstops and - * replaces them with values returned by handlers defined in - * options - * @param {String} text - * @param {Object} options See extract method options - * description - * @returns {String} - */ - processText: function(text, options) { - options = utils.extend({}, defaultOptions, options); - - var buf = ''; - /** @type StringStream */ - var stream = stringStream.create(text); - var ch, m, a; - - while ((ch = stream.next())) { - if (ch == '\\' && !stream.eol()) { - // handle escaped character - buf += options.escape(stream.next()); - continue; - } - - a = ch; - - if (ch == '$') { - // looks like a tabstop - stream.start = stream.pos - 1; - - if ((m = stream.match(/^[0-9]+/))) { - // it's $N - a = options.tabstop({ - start: buf.length, - group: stream.current().substr(1), - token: stream.current() - }); - } else if ((m = stream.match(/^\{([a-z_\-][\w\-]*)\}/))) { - // ${variable} - a = options.variable({ - start: buf.length, - name: m[1], - token: stream.current() - }); - } else if ((m = stream.match(/^\{([0-9]+)(:.+?)?\}/, false))) { - // ${N:value} or ${N} placeholder - // parse placeholder, including nested ones - stream.skipToPair('{', '}'); - - var obj = { - start: buf.length, - group: m[1], - token: stream.current() - }; - - var placeholder = obj.token.substring(obj.group.length + 2, obj.token.length - 1); - - if (placeholder) { - obj.placeholder = placeholder.substr(1); - } - - a = options.tabstop(obj); - } - } - - buf += a; - } - - return buf; - }, - - /** - * Upgrades tabstops in output node in order to prevent naming conflicts - * @param {AbbreviationNode} node - * @param {Number} offset Tab index offset - * @returns {Number} Maximum tabstop index in element - */ - upgrade: function(node, offset) { - var maxNum = 0; - var options = { - tabstop: function(data) { - var group = parseInt(data.group, 10); - if (group > maxNum) maxNum = group; - - if (data.placeholder) - return '${' + (group + offset) + ':' + data.placeholder + '}'; - else - return '${' + (group + offset) + '}'; - } - }; - - ['start', 'end', 'content'].forEach(function(p) { - node[p] = this.processText(node[p], options); - }, this); - - return maxNum; - }, - - /** - * Helper function that produces a callback function for - * replaceVariables() method from {@link utils} - * module. This callback will replace variable definitions (like - * ${var_name}) with their value defined in resource module, - * or outputs tabstop with variable name otherwise. - * @param {AbbreviationNode} node Context node - * @returns {Function} - */ - variablesResolver: function(node) { - var placeholderMemo = {}; - return function(str, varName) { - // do not mark `child` variable as placeholder – it‘s a reserved - // variable name - if (varName == 'child') { - return str; - } - - if (varName == 'cursor') { - return utils.getCaretPlaceholder(); - } - - var attr = node.attribute(varName); - if (typeof attr !== 'undefined' && attr !== str) { - return attr; - } - - var varValue = resources.getVariable(varName); - if (varValue) { - return varValue; - } - - // output as placeholder - if (!placeholderMemo[varName]) { - placeholderMemo[varName] = startPlaceholderNum++; - } - - return '${' + placeholderMemo[varName] + ':' + varName + '}'; - }; - }, - - /** - * Replace variables like ${var} in string - * @param {String} str - * @param {Object} vars Variable set (defaults to variables defined in - * snippets.json) or variable resolver (Function) - * @return {String} - */ - replaceVariables: function(str, vars) { - vars = vars || {}; - var resolver = typeof vars === 'function' ? vars : function(str, p1) { - return p1 in vars ? vars[p1] : null; - }; - - return this.processText(str, { - variable: function(data) { - var newValue = resolver(data.token, data.name, data); - if (newValue === null) { - // try to find variable in resources - newValue = resources.getVariable(data.name); - } - - if (newValue === null || typeof newValue === 'undefined') - // nothing found, return token itself - newValue = data.token; - return newValue; - } - }); - }, - - /** - * Resets global tabstop index. When parsed tree is converted to output - * string (AbbreviationNode.toString()), all tabstops - * defined in snippets and elements are upgraded in order to prevent - * naming conflicts of nested. For example, ${1} of a node - * should not be linked with the same placehilder of the child node. - * By default, AbbreviationNode.toString() automatically - * upgrades tabstops of the same index for each node and writes maximum - * tabstop index into the tabstopIndex variable. To keep - * this variable at reasonable value, it is recommended to call - * resetTabstopIndex() method each time you expand variable - * @returns - */ - resetTabstopIndex: function() { - tabstopIndex = 0; - startPlaceholderNum = 100; - }, - - /** - * Output processor for abbreviation parser that will upgrade tabstops - * of parsed node in order to prevent tabstop index conflicts - */ - abbrOutputProcessor: function(text, node, type) { - var maxNum = 0; - var that = this; - - var tsOptions = { - tabstop: function(data) { - var group = parseInt(data.group, 10); - if (group === 0) - return '${0}'; - - if (group > maxNum) maxNum = group; - if (data.placeholder) { - // respect nested placeholders - var ix = group + tabstopIndex; - var placeholder = that.processText(data.placeholder, tsOptions); - return '${' + ix + ':' + placeholder + '}'; - } else { - return '${' + (group + tabstopIndex) + '}'; - } - } - }; - - // upgrade tabstops - text = this.processText(text, tsOptions); - - // resolve variables - text = this.replaceVariables(text, this.variablesResolver(node)); - - tabstopIndex += maxNum + 1; - return text; - } - }; -}); -},{"../utils/common":73,"./resources":31,"./stringStream":32}],34:[function(require,module,exports){ -/** - * Helper class for convenient token iteration - */ -if (typeof module === 'object' && typeof define !== 'function') { - var define = function (factory) { - module.exports = factory(require, exports, module); - }; -} - -define(function(require, exports, module) { - /** - * @type TokenIterator - * @param {Array} tokens - * @type TokenIterator - * @constructor - */ - function TokenIterator(tokens) { - /** @type Array */ - this.tokens = tokens; - this._position = 0; - this.reset(); - } - - TokenIterator.prototype = { - next: function() { - if (this.hasNext()) { - var token = this.tokens[++this._i]; - this._position = token.start; - return token; - } else { - this._i = this._il; - } - - return null; - }, - - current: function() { - return this.tokens[this._i]; - }, - - peek: function() { - return this.tokens[this._i + i]; - }, - - position: function() { - return this._position; - }, - - hasNext: function() { - return this._i < this._il - 1; - }, - - reset: function() { - this._i = 0; - this._il = this.tokens.length; - }, - - item: function() { - return this.tokens[this._i]; - }, - - itemNext: function() { - return this.tokens[this._i + 1]; - }, - - itemPrev: function() { - return this.tokens[this._i - 1]; - }, - - nextUntil: function(type, callback) { - var token; - var test = typeof type == 'string' - ? function(t){return t.type == type;} - : type; - - while ((token = this.next())) { - if (callback) - callback.call(this, token); - if (test.call(this, token)) - break; - } - } - }; - - return { - create: function(tokens) { - return new TokenIterator(tokens); - } - }; -}); -},{}],35:[function(require,module,exports){ -module.exports={ - "eras": { - "e-26": "26 versions back", - "e-25": "25 versions back", - "e-24": "24 versions back", - "e-23": "23 versions back", - "e-22": "22 versions back", - "e-21": "21 versions back", - "e-20": "20 versions back", - "e-19": "19 versions back", - "e-18": "18 versions back", - "e-17": "17 versions back", - "e-16": "16 versions back", - "e-15": "15 versions back", - "e-14": "14 versions back", - "e-13": "13 versions back", - "e-12": "12 versions back", - "e-11": "11 versions back", - "e-10": "10 versions back", - "e-9": "9 versions back", - "e-8": "8 versions back", - "e-7": "7 versions back", - "e-6": "6 versions back", - "e-5": "5 versions back", - "e-4": "4 versions back", - "e-3": "3 versions back", - "e-2": "2 versions back", - "e-1": "Previous version", - "e0": "Current", - "e1": "Near future", - "e2": "Farther future" - }, - "agents": { - "ie": { - "browser": "IE", - "abbr": "IE", - "prefix": "ms", - "type": "desktop", - "usage_global": { - "10": 10.7866, - "11": 0.114751, - "5.5": 0.009298, - "6": 0.204912, - "7": 0.508182, - "8": 8.31124, - "9": 5.21297 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "5.5", "6", "7", "8", "9", "10", "11", null, null], - "current_version": "" - }, - "firefox": { - "browser": "Firefox", - "abbr": "FF", - "prefix": "moz", - "type": "desktop", - "usage_global": { - "10": 0.112406, - "11": 0.088319, - "12": 0.208754, - "13": 0.096348, - "14": 0.096348, - "15": 0.136493, - "16": 0.264957, - "17": 0.192696, - "18": 0.112406, - "19": 0.128464, - "2": 0.016058, - "20": 0.16058, - "21": 0.216783, - "22": 0.256928, - "23": 0.907277, - "24": 11.0318, - "25": 0.529914, - "26": 0.016058, - "27": 0.016058, - "3": 0.088319, - "3.5": 0.040145, - "3.6": 0.305102, - "4": 0.072261, - "5": 0.048174, - "6": 0.048174, - "7": 0.040145, - "8": 0.072261, - "9": 0.056203 - }, - "versions": [null, "2", "3", "3.5", "3.6", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27"], - "current_version": "" - }, - "chrome": { - "browser": "Chrome", - "abbr": "Chr.", - "prefix": "webkit", - "type": "desktop", - "usage_global": { - "10": 0.048174, - "11": 0.112406, - "12": 0.064232, - "13": 0.056203, - "14": 0.056203, - "15": 0.072261, - "16": 0.048174, - "17": 0.040145, - "18": 0.08029, - "19": 0.040145, - "20": 0.040145, - "21": 0.48174, - "22": 0.248899, - "23": 0.216783, - "24": 0.200725, - "25": 0.361305, - "26": 0.353276, - "27": 0.369334, - "28": 0.610204, - "29": 5.08236, - "30": 24.6089, - "31": 0.16058, - "32": 0.064232, - "4": 0.024087, - "5": 0.024087, - "6": 0.032116, - "7": 0.024087, - "8": 0.032116, - "9": 0.024087 - }, - "versions": ["4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"], - "current_version": "" - }, - "safari": { - "browser": "Safari", - "abbr": "Saf.", - "prefix": "webkit", - "type": "desktop", - "usage_global": { - "3.1": 0, - "3.2": 0.008692, - "4": 0.104377, - "5": 0.305102, - "5.1": 1.28464, - "6": 2.04739, - "6.1": 0.064232, - "7": 0.16058 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "3.1", "3.2", "4", "5", "5.1", "6", "6.1", "7", null, null], - "current_version": "" - }, - "opera": { - "browser": "Opera", - "abbr": "Op.", - "prefix": "o", - "type": "desktop", - "usage_global": { - "10.0-10.1": 0.016058, - "10.5": 0.008392, - "10.6": 0.008029, - "11": 0.008219, - "11.1": 0.008219, - "11.5": 0.016058, - "11.6": 0.032116, - "12": 0.040145, - "12.1": 0.48174, - "15": 0.032116, - "16": 0.104377, - "17": 0.16058, - "18": 0, - "9.5-9.6": 0.008219 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, "9.5-9.6", "10.0-10.1", "10.5", "10.6", "11", "11.1", "11.5", "11.6", "12", "12.1", "15", "16", "17", "18", null], - "current_version": "", - "prefix_exceptions": { - "15": "webkit", - "16": "webkit", - "17": "webkit", - "18": "webkit" - } - }, - "ios_saf": { - "browser": "iOS Safari", - "abbr": "iOS", - "prefix": "webkit", - "type": "mobile", - "usage_global": { - "3.2": 0.00400113, - "4.0-4.1": 0.00800226, - "4.2-4.3": 0.0280079, - "5.0-5.1": 0.28408, - "6.0-6.1": 1.15633, - "7.0": 2.52071 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "3.2", "4.0-4.1", "4.2-4.3", "5.0-5.1", "6.0-6.1", "7.0", null, null], - "current_version": "" - }, - "op_mini": { - "browser": "Opera Mini", - "abbr": "O.Mini", - "prefix": "o", - "type": "mobile", - "usage_global": { - "5.0-7.0": 4.58374 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "5.0-7.0", null, null], - "current_version": "" - }, - "android": { - "browser": "Android Browser", - "abbr": "And.", - "prefix": "webkit", - "type": "mobile", - "usage_global": { - "2.1": 0.0251229, - "2.2": 0.0854178, - "2.3": 1.32146, - "3": 0.00502458, - "4": 0.994867, - "4.1": 1.87417, - "4.2-4.3": 0.743638, - "4.4": 0 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "2.1", "2.2", "2.3", "3", "4", "4.1", "4.2-4.3", "4.4", null], - "current_version": "" - }, - "op_mob": { - "browser": "Opera Mobile", - "abbr": "O.Mob", - "prefix": "o", - "type": "mobile", - "usage_global": { - "0": 0, - "10": 0, - "11.5": 0.00726525, - "12": 0.0363263, - "12.1": 0.101714 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "10", null, null, "11.5", "12", "12.1", "0", null, null], - "current_version": "16", - "prefix_exceptions": { - "0": "webkit" - } - }, - "bb": { - "browser": "Blackberry Browser", - "abbr": "BB", - "prefix": "webkit", - "type": "mobile", - "usage_global": { - "10": 0, - "7": 0.141419 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "7", "10", null, null], - "current_version": "" - }, - "and_chr": { - "browser": "Chrome for Android", - "abbr": "Chr/And.", - "prefix": "webkit", - "type": "mobile", - "usage_global": { - "0": 1.38176 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "0", null, null], - "current_version": "30" - }, - "and_ff": { - "browser": "Firefox for Android", - "abbr": "FF/And.", - "prefix": "moz", - "type": "mobile", - "usage_global": { - "0": 0.070956 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "0", null, null], - "current_version": "25" - }, - "ie_mob": { - "browser": "IE Mobile", - "abbr": "IE.Mob", - "prefix": "ms", - "type": "mobile", - "usage_global": { - "10": 0.205595 - }, - "versions": [null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "10", null, null], - "current_version": "" - } - }, - "statuses": { - "rec": "Recommendation", - "pr": "Proposed Recommendation", - "cr": "Candidate Recommendation", - "wd": "Working Draft", - "other": "Other", - "unoff": "Unofficial / Note" - }, - "cats": { - "CSS": ["CSS", "CSS2", "CSS3"], - "HTML5": ["Canvas", "HTML5"], - "JS API": ["JS API"], - "Other": ["PNG", "Other", "DOM"], - "SVG": ["SVG"] - }, - "updated": 1383587152, - "data": { - "png-alpha": { - "title": "PNG alpha transparency", - "description": "Semi-transparent areas in PNG files", - "spec": "http://www.w3.org/TR/PNG/", - "status": "rec", - "links": [{ - "url": "http://dillerdesign.com/experiment/DD_belatedPNG/", - "title": "Workaround for IE6" - }, { - "url": "http://en.wikipedia.org/wiki/Portable_Network_Graphics", - "title": "Wikipedia" - }], - "categories": ["PNG"], - "stats": { - "ie": { - "5.5": "n", - "6": "p", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y" - }, - "firefox": { - "2": "y", - "3": "y", - "3.5": "y", - "3.6": "y", - "4": "y", - "5": "y", - "6": "y", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y", - "12": "y", - "13": "y", - "14": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y", - "19": "y", - "20": "y", - "21": "y", - "22": "y", - "23": "y", - "24": "y", - "25": "y", - "26": "y", - "27": "y" - }, - "chrome": { - "4": "y", - "5": "y", - "6": "y", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y", - "12": "y", - "13": "y", - "14": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y", - "19": "y", - "20": "y", - "21": "y", - "22": "y", - "23": "y", - "24": "y", - "25": "y", - "26": "y", - "27": "y", - "28": "y", - "29": "y", - "30": "y", - "31": "y", - "32": "y" - }, - "safari": { - "3.1": "y", - "3.2": "y", - "4": "y", - "5": "y", - "5.1": "y", - "6": "y", - "6.1": "y", - "7": "y" - }, - "opera": { - "9": "y", - "9.5-9.6": "y", - "10.0-10.1": "y", - "10.5": "y", - "10.6": "y", - "11": "y", - "11.1": "y", - "11.5": "y", - "11.6": "y", - "12": "y", - "12.1": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y" - }, - "ios_saf": { - "3.2": "y", - "4.0-4.1": "y", - "4.2-4.3": "y", - "5.0-5.1": "y", - "6.0-6.1": "y", - "7.0": "y" - }, - "op_mini": { - "5.0-7.0": "y" - }, - "android": { - "2.1": "y", - "2.2": "y", - "2.3": "y", - "3": "y", - "4": "y", - "4.1": "y", - "4.2-4.3": "y", - "4.4": "y" - }, - "bb": { - "7": "y", - "10": "y" - }, - "op_mob": { - "10": "y", - "11": "y", - "11.1": "y", - "11.5": "y", - "12": "y", - "12.1": "y", - "0": "y" - }, - "and_chr": { - "0": "y" - }, - "and_ff": { - "0": "y" - }, - "ie_mob": { - "10": "y" - } - }, - "notes": "IE6 does support full transparency in 8-bit PNGs, which can sometimes be an alternative to 24-bit PNGs.", - "usage_perc_y": 94.36, - "usage_perc_a": 0, - "ucprefix": false, - "parent": "", - "keywords": "" - }, - "apng": { - "title": "Animated PNG (APNG)", - "description": "Like animated GIFs, but allowing 24-bit colors and alpha transparency", - "spec": "https://wiki.mozilla.org/APNG_Specification", - "status": "unoff", - "links": [{ - "url": "http://en.wikipedia.org/wiki/APNG", - "title": "Wikipedia" - }, { - "url": "https://github.com/davidmz/apng-canvas", - "title": "Polyfill using canvas" - }, { - "url": "https://chrome.google.com/webstore/detail/ehkepjiconegkhpodgoaeamnpckdbblp", - "title": "Chrome extension providing support" - }, { - "url": "http://www.truekolor.net/learn-how-to-create-an-animated-png/", - "title": "APNG tutorial" - }], - "categories": ["PNG"], - "stats": { - "ie": { - "5.5": "n", - "6": "n", - "7": "n", - "8": "n", - "9": "n", - "10": "n", - "11": "n" - }, - "firefox": { - "2": "n", - "3": "y", - "3.5": "y", - "3.6": "y", - "4": "y", - "5": "y", - "6": "y", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y", - "12": "y", - "13": "y", - "14": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y", - "19": "y", - "20": "y", - "21": "y", - "22": "y", - "23": "y", - "24": "y", - "25": "y", - "26": "y", - "27": "y" - }, - "chrome": { - "4": "n", - "5": "n", - "6": "n", - "7": "n", - "8": "n", - "9": "n", - "10": "n", - "11": "n", - "12": "n", - "13": "n", - "14": "n", - "15": "n", - "16": "n", - "17": "n", - "18": "n", - "19": "n", - "20": "n", - "21": "n", - "22": "n", - "23": "n", - "24": "n", - "25": "n", - "26": "n", - "27": "n", - "28": "n", - "29": "n", - "30": "n", - "31": "n", - "32": "n" - }, - "safari": { - "3.1": "n", - "3.2": "n", - "4": "n", - "5": "n", - "5.1": "n", - "6": "n", - "6.1": "n", - "7": "n" - }, - "opera": { - "9": "n", - "9.5-9.6": "y", - "10.0-10.1": "y", - "10.5": "y", - "10.6": "y", - "11": "y", - "11.1": "y", - "11.5": "y", - "11.6": "y", - "12": "y", - "12.1": "y", - "15": "n", - "16": "n", - "17": "n", - "18": "n" - }, - "ios_saf": { - "3.2": "n", - "4.0-4.1": "n", - "4.2-4.3": "n", - "5.0-5.1": "n", - "6.0-6.1": "n", - "7.0": "n" - }, - "op_mini": { - "5.0-7.0": "n" - }, - "android": { - "2.1": "n", - "2.2": "n", - "2.3": "n", - "3": "n", - "4": "n", - "4.1": "n", - "4.2-4.3": "n", - "4.4": "n" - }, - "bb": { - "7": "n", - "10": "n" - }, - "op_mob": { - "10": "y", - "11": "y", - "11.1": "y", - "11.5": "y", - "12": "y", - "12.1": "y", - "0": "n" - }, - "and_chr": { - "0": "n" - }, - "and_ff": { - "0": "y" - }, - "ie_mob": { - "10": "n" - } - }, - "notes": "Where support for APNG is missing, only the first frame is displayed", - "usage_perc_y": 16.19, - "usage_perc_a": 0, - "ucprefix": false, - "parent": "", - "keywords": "" - }, - "video": { - "title": "Video element", - "description": "Method of playing videos on webpages (without requiring a plug-in)", - "spec": "http://www.whatwg.org/specs/web-apps/current-work/multipage/video.html#video", - "status": "wd", - "links": [{ - "url": "https://raw.github.com/phiggins42/has.js/master/detect/video.js#video", - "title": "has.js test" - }, { - "url": "http://webmproject.org", - "title": "WebM format information" - }, { - "url": "http://docs.webplatform.org/wiki/html/elements/video", - "title": "WebPlatform Docs" - }, { - "url": "http://camendesign.co.uk/code/video_for_everybody", - "title": "Video for Everybody" - }, { - "url": "http://diveinto.org/html5/video.html", - "title": "Video on the Web - includes info on Android support" - }, { - "url": "http://dev.opera.com/articles/view/everything-you-need-to-know-about-html5-video-and-audio/", - "title": "Detailed article on video/audio elements" - }], - "categories": ["HTML5"], - "stats": { - "ie": { - "5.5": "n", - "6": "n", - "7": "n", - "8": "n", - "9": "y", - "10": "y", - "11": "y" - }, - "firefox": { - "2": "n", - "3": "n", - "3.5": "y", - "3.6": "y", - "4": "y", - "5": "y", - "6": "y", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y", - "12": "y", - "13": "y", - "14": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y", - "19": "y", - "20": "y", - "21": "y", - "22": "y", - "23": "y", - "24": "y", - "25": "y", - "26": "y", - "27": "y" - }, - "chrome": { - "4": "y", - "5": "y", - "6": "y", - "7": "y", - "8": "y", - "9": "y", - "10": "y", - "11": "y", - "12": "y", - "13": "y", - "14": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y", - "19": "y", - "20": "y", - "21": "y", - "22": "y", - "23": "y", - "24": "y", - "25": "y", - "26": "y", - "27": "y", - "28": "y", - "29": "y", - "30": "y", - "31": "y", - "32": "y" - }, - "safari": { - "3.1": "n", - "3.2": "n", - "4": "y", - "5": "y", - "5.1": "y", - "6": "y", - "6.1": "y", - "7": "y" - }, - "opera": { - "9": "n", - "9.5-9.6": "n", - "10.0-10.1": "n", - "10.5": "y", - "10.6": "y", - "11": "y", - "11.1": "y", - "11.5": "y", - "11.6": "y", - "12": "y", - "12.1": "y", - "15": "y", - "16": "y", - "17": "y", - "18": "y" - }, - "ios_saf": { - "3.2": "y", - "4.0-4.1": "y", - "4.2-4.3": "y", - "5.0-5.1": "y", - "6.0-6.1": "y", - "7.0": "y" - }, - "op_mini": { - "5.0-7.0": "n" - }, - "android": { - "2.1": "a", - "2.2": "a", - "2.3": "y", - "3": "y", - "4": "y", - "4.1": "y", - "4.2-4.3": "y", - "4.4": "y" - }, - "bb": { - "7": "y", - "10": "y" - }, - "op_mob": { - "10": "n", - "11": "y", - "11.1": "y", - "11.5": "y", - "12": "y", - "12.1": "y", - "0": "y" - }, - "and_chr": { - "0": "y" - }, - "and_ff": { - "0": "y" - }, - "ie_mob": { - "10": "y" - } - }, - "notes": "Different browsers have support for different video formats, see sub-features for details. \r\n\r\nThe Android browser (before 2.3) requires specific handling to run the video element.", - "usage_perc_y": 80.71, - "usage_perc_a": 0.11, - "ucprefix": false, - "parent": "", - "keywords": "
]", - "[link ]", - "[tag&bracket <][tag div][tag&bracket >]", - "[tag&bracket ]"); - -})(); diff --git a/public/js/lib/codemirror/mode/meta.js b/public/js/lib/codemirror/mode/meta.js deleted file mode 100644 index fcaed98e2d..0000000000 --- a/public/js/lib/codemirror/mode/meta.js +++ /dev/null @@ -1,165 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { - "use strict"; - - CodeMirror.modeInfo = [ - {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]}, - {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk"}, - {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]}, - {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "hpp", "h++"], alias: ["cpp"]}, - {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]}, - {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]}, - {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj"]}, - {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]}, - {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]}, - {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher"}, - {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]}, - {name: "CSS", mime: "text/css", mode: "css", ext: ["css"]}, - {name: "CQL", mime: "text/x-cassandra", mode: "sql", ext: ["cql"]}, - {name: "D", mime: "text/x-d", mode: "d", ext: ["d"]}, - {name: "Dart", mimes: ["application/dart", "text/x-dart"], mode: "dart", ext: ["dart"]}, - {name: "diff", mime: "text/x-diff", mode: "diff", ext: ["diff", "patch"]}, - {name: "Django", mime: "text/x-django", mode: "django"}, - {name: "Dockerfile", mime: "text/x-dockerfile", mode: "dockerfile"}, - {name: "DTD", mime: "application/xml-dtd", mode: "dtd", ext: ["dtd"]}, - {name: "Dylan", mime: "text/x-dylan", mode: "dylan", ext: ["dylan", "dyl", "intr"]}, - {name: "EBNF", mime: "text/x-ebnf", mode: "ebnf"}, - {name: "ECL", mime: "text/x-ecl", mode: "ecl", ext: ["ecl"]}, - {name: "Eiffel", mime: "text/x-eiffel", mode: "eiffel", ext: ["e"]}, - {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]}, - {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]}, - {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]}, - {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]}, - {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]}, - {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]}, - {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]}, - {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm"}, - {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]}, - {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy"]}, - {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]}, - {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]}, - {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]}, - {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]}, - {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]}, - {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]}, - {name: "HTTP", mime: "message/http", mode: "http"}, - {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]}, - {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]}, - {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]}, - {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]}, - {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"], - mode: "javascript", ext: ["js"], alias: ["ecmascript", "js", "node"]}, - {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]}, - {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", alias: ["jsonld"]}, - {name: "Jinja2", mime: "null", mode: "jinja2"}, - {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]}, - {name: "Kotlin", mime: "text/x-kotlin", mode: "kotlin", ext: ["kt"]}, - {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]}, - {name: "LiveScript", mime: "text/x-livescript", mode: "livescript", ext: ["ls"], alias: ["ls"]}, - {name: "Lua", mime: "text/x-lua", mode: "lua", ext: ["lua"]}, - {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]}, - {name: "mIRC", mime: "text/mirc", mode: "mirc"}, - {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"}, - {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]}, - {name: "MS SQL", mime: "text/x-mssql", mode: "sql"}, - {name: "MySQL", mime: "text/x-mysql", mode: "sql"}, - {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx"}, - {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]}, - {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"]}, - {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]}, - {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]}, - {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]}, - {name: "PEG.js", mime: "null", mode: "pegjs"}, - {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]}, - {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]}, - {name: "Pig", mime: "text/x-pig", mode: "pig"}, - {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]}, - {name: "PLSQL", mime: "text/x-plsql", mode: "sql"}, - {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]}, - {name: "Python", mime: "text/x-python", mode: "python", ext: ["py", "pyw"]}, - {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]}, - {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]}, - {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]}, - {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]}, - {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"}, - {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]}, - {name: "Ruby", mime: "text/x-ruby", mode: "ruby", ext: ["rb"], alias: ["jruby", "macruby", "rake", "rb", "rbx"]}, - {name: "Rust", mime: "text/x-rustsrc", mode: "rust", ext: ["rs"]}, - {name: "Sass", mime: "text/x-sass", mode: "sass", ext: ["sass"]}, - {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]}, - {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]}, - {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]}, - {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"]}, - {name: "Sieve", mime: "application/sieve", mode: "sieve"}, - {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim"}, - {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]}, - {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]}, - {name: "SmartyMixed", mime: "text/x-smarty", mode: "smartymixed"}, - {name: "Solr", mime: "text/x-solr", mode: "solr"}, - {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]}, - {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]}, - {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]}, - {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]}, - {name: "MariaDB", mime: "text/x-mariadb", mode: "sql"}, - {name: "sTeX", mime: "text/x-stex", mode: "stex"}, - {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]}, - {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]}, - {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]}, - {name: "Textile", mime: "text/x-textile", mode: "textile"}, - {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"}, - {name: "Tiki wiki", mime: "text/tiki", mode: "tiki"}, - {name: "TOML", mime: "text/x-toml", mode: "toml"}, - {name: "Tornado", mime: "text/x-tornado", mode: "tornado"}, - {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]}, - {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]}, - {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]}, - {name: "VBScript", mime: "text/vbscript", mode: "vbscript"}, - {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]}, - {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]}, - {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]}, - {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]}, - {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml"], alias: ["yml"]}, - {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]} - ]; - // Ensure all modes have a mime property for backwards compatibility - for (var i = 0; i < CodeMirror.modeInfo.length; i++) { - var info = CodeMirror.modeInfo[i]; - if (info.mimes) info.mime = info.mimes[0]; - } - - CodeMirror.findModeByMIME = function(mime) { - for (var i = 0; i < CodeMirror.modeInfo.length; i++) { - var info = CodeMirror.modeInfo[i]; - if (info.mime == mime) return info; - if (info.mimes) for (var j = 0; j < info.mimes.length; j++) - if (info.mimes[j] == mime) return info; - } - }; - - CodeMirror.findModeByExtension = function(ext) { - for (var i = 0; i < CodeMirror.modeInfo.length; i++) { - var info = CodeMirror.modeInfo[i]; - if (info.ext) for (var j = 0; j < info.ext.length; j++) - if (info.ext[j] == ext) return info; - } - }; - - CodeMirror.findModeByName = function(name) { - name = name.toLowerCase(); - for (var i = 0; i < CodeMirror.modeInfo.length; i++) { - var info = CodeMirror.modeInfo[i]; - if (info.name.toLowerCase() == name) return info; - if (info.alias) for (var j = 0; j < info.alias.length; j++) - if (info.alias[j].toLowerCase() == name) return info; - } - }; -}); diff --git a/public/js/lib/codemirror/mode/xml/index.html b/public/js/lib/codemirror/mode/xml/index.html deleted file mode 100644 index 7149f06b2a..0000000000 --- a/public/js/lib/codemirror/mode/xml/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - -CodeMirror: XML mode - - - - - - - - - -
-

XML mode

-
- -

The XML mode supports two configuration parameters:

-
-
htmlMode (boolean)
-
This switches the mode to parse HTML instead of XML. This - means attributes do not have to be quoted, and some elements - (such as br) do not require a closing tag.
-
alignCDATA (boolean)
-
Setting this to true will force the opening tag of CDATA - blocks to not be indented.
-
- -

MIME types defined: application/xml, text/html.

-
diff --git a/public/js/lib/codemirror/mode/xml/test.js b/public/js/lib/codemirror/mode/xml/test.js deleted file mode 100644 index f48156b517..0000000000 --- a/public/js/lib/codemirror/mode/xml/test.js +++ /dev/null @@ -1,51 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function() { - var mode = CodeMirror.getMode({indentUnit: 2}, "xml"), mname = "xml"; - function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), mname); } - - MT("matching", - "[tag&bracket <][tag top][tag&bracket >]", - " text", - " [tag&bracket <][tag inner][tag&bracket />]", - "[tag&bracket ]"); - - MT("nonmatching", - "[tag&bracket <][tag top][tag&bracket >]", - " [tag&bracket <][tag inner][tag&bracket />]", - " [tag&bracket ]"); - - MT("doctype", - "[meta ]", - "[tag&bracket <][tag top][tag&bracket />]"); - - MT("cdata", - "[tag&bracket <][tag top][tag&bracket >]", - " [atom ]", - "[tag&bracket ]"); - - // HTML tests - mode = CodeMirror.getMode({indentUnit: 2}, "text/html"); - - MT("selfclose", - "[tag&bracket <][tag html][tag&bracket >]", - " [tag&bracket <][tag link] [attribute rel]=[string stylesheet] [attribute href]=[string \"/foobar\"][tag&bracket >]", - "[tag&bracket ]"); - - MT("list", - "[tag&bracket <][tag ol][tag&bracket >]", - " [tag&bracket <][tag li][tag&bracket >]one", - " [tag&bracket <][tag li][tag&bracket >]two", - "[tag&bracket ]"); - - MT("valueless", - "[tag&bracket <][tag input] [attribute type]=[string checkbox] [attribute checked][tag&bracket />]"); - - MT("pThenArticle", - "[tag&bracket <][tag p][tag&bracket >]", - " foo", - "[tag&bracket <][tag article][tag&bracket >]bar"); - -})(); diff --git a/public/js/lib/codemirror/mode/xml/xml.js b/public/js/lib/codemirror/mode/xml/xml.js deleted file mode 100644 index 2f3b8f87a0..0000000000 --- a/public/js/lib/codemirror/mode/xml/xml.js +++ /dev/null @@ -1,384 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { -"use strict"; - -CodeMirror.defineMode("xml", function(config, parserConfig) { - var indentUnit = config.indentUnit; - var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1; - var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag; - if (multilineTagIndentPastTag == null) multilineTagIndentPastTag = true; - - var Kludges = parserConfig.htmlMode ? { - autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, - 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, - 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, - 'track': true, 'wbr': true, 'menuitem': true}, - implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, - 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, - 'th': true, 'tr': true}, - contextGrabbers: { - 'dd': {'dd': true, 'dt': true}, - 'dt': {'dd': true, 'dt': true}, - 'li': {'li': true}, - 'option': {'option': true, 'optgroup': true}, - 'optgroup': {'optgroup': true}, - 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, - 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, - 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, - 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, - 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, - 'rp': {'rp': true, 'rt': true}, - 'rt': {'rp': true, 'rt': true}, - 'tbody': {'tbody': true, 'tfoot': true}, - 'td': {'td': true, 'th': true}, - 'tfoot': {'tbody': true}, - 'th': {'td': true, 'th': true}, - 'thead': {'tbody': true, 'tfoot': true}, - 'tr': {'tr': true} - }, - doNotIndent: {"pre": true}, - allowUnquoted: true, - allowMissing: true, - caseFold: true - } : { - autoSelfClosers: {}, - implicitlyClosed: {}, - contextGrabbers: {}, - doNotIndent: {}, - allowUnquoted: false, - allowMissing: false, - caseFold: false - }; - var alignCDATA = parserConfig.alignCDATA; - - // Return variables for tokenizers - var type, setStyle; - - function inText(stream, state) { - function chain(parser) { - state.tokenize = parser; - return parser(stream, state); - } - - var ch = stream.next(); - if (ch == "<") { - if (stream.eat("!")) { - if (stream.eat("[")) { - if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); - else return null; - } else if (stream.match("--")) { - return chain(inBlock("comment", "-->")); - } else if (stream.match("DOCTYPE", true, true)) { - stream.eatWhile(/[\w\._\-]/); - return chain(doctype(1)); - } else { - return null; - } - } else if (stream.eat("?")) { - stream.eatWhile(/[\w\._\-]/); - state.tokenize = inBlock("meta", "?>"); - return "meta"; - } else { - type = stream.eat("/") ? "closeTag" : "openTag"; - state.tokenize = inTag; - return "tag bracket"; - } - } else if (ch == "&") { - var ok; - if (stream.eat("#")) { - if (stream.eat("x")) { - ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); - } else { - ok = stream.eatWhile(/[\d]/) && stream.eat(";"); - } - } else { - ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); - } - return ok ? "atom" : "error"; - } else { - stream.eatWhile(/[^&<]/); - return null; - } - } - - function inTag(stream, state) { - var ch = stream.next(); - if (ch == ">" || (ch == "/" && stream.eat(">"))) { - state.tokenize = inText; - type = ch == ">" ? "endTag" : "selfcloseTag"; - return "tag bracket"; - } else if (ch == "=") { - type = "equals"; - return null; - } else if (ch == "<") { - state.tokenize = inText; - state.state = baseState; - state.tagName = state.tagStart = null; - var next = state.tokenize(stream, state); - return next ? next + " tag error" : "tag error"; - } else if (/[\'\"]/.test(ch)) { - state.tokenize = inAttribute(ch); - state.stringStartCol = stream.column(); - return state.tokenize(stream, state); - } else { - stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); - return "word"; - } - } - - function inAttribute(quote) { - var closure = function(stream, state) { - while (!stream.eol()) { - if (stream.next() == quote) { - state.tokenize = inTag; - break; - } - } - return "string"; - }; - closure.isInAttribute = true; - return closure; - } - - function inBlock(style, terminator) { - return function(stream, state) { - while (!stream.eol()) { - if (stream.match(terminator)) { - state.tokenize = inText; - break; - } - stream.next(); - } - return style; - }; - } - function doctype(depth) { - return function(stream, state) { - var ch; - while ((ch = stream.next()) != null) { - if (ch == "<") { - state.tokenize = doctype(depth + 1); - return state.tokenize(stream, state); - } else if (ch == ">") { - if (depth == 1) { - state.tokenize = inText; - break; - } else { - state.tokenize = doctype(depth - 1); - return state.tokenize(stream, state); - } - } - } - return "meta"; - }; - } - - function Context(state, tagName, startOfLine) { - this.prev = state.context; - this.tagName = tagName; - this.indent = state.indented; - this.startOfLine = startOfLine; - if (Kludges.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) - this.noIndent = true; - } - function popContext(state) { - if (state.context) state.context = state.context.prev; - } - function maybePopContext(state, nextTagName) { - var parentTagName; - while (true) { - if (!state.context) { - return; - } - parentTagName = state.context.tagName; - if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || - !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { - return; - } - popContext(state); - } - } - - function baseState(type, stream, state) { - if (type == "openTag") { - state.tagStart = stream.column(); - return tagNameState; - } else if (type == "closeTag") { - return closeTagNameState; - } else { - return baseState; - } - } - function tagNameState(type, stream, state) { - if (type == "word") { - state.tagName = stream.current(); - setStyle = "tag"; - return attrState; - } else { - setStyle = "error"; - return tagNameState; - } - } - function closeTagNameState(type, stream, state) { - if (type == "word") { - var tagName = stream.current(); - if (state.context && state.context.tagName != tagName && - Kludges.implicitlyClosed.hasOwnProperty(state.context.tagName)) - popContext(state); - if (state.context && state.context.tagName == tagName) { - setStyle = "tag"; - return closeState; - } else { - setStyle = "tag error"; - return closeStateErr; - } - } else { - setStyle = "error"; - return closeStateErr; - } - } - - function closeState(type, _stream, state) { - if (type != "endTag") { - setStyle = "error"; - return closeState; - } - popContext(state); - return baseState; - } - function closeStateErr(type, stream, state) { - setStyle = "error"; - return closeState(type, stream, state); - } - - function attrState(type, _stream, state) { - if (type == "word") { - setStyle = "attribute"; - return attrEqState; - } else if (type == "endTag" || type == "selfcloseTag") { - var tagName = state.tagName, tagStart = state.tagStart; - state.tagName = state.tagStart = null; - if (type == "selfcloseTag" || - Kludges.autoSelfClosers.hasOwnProperty(tagName)) { - maybePopContext(state, tagName); - } else { - maybePopContext(state, tagName); - state.context = new Context(state, tagName, tagStart == state.indented); - } - return baseState; - } - setStyle = "error"; - return attrState; - } - function attrEqState(type, stream, state) { - if (type == "equals") return attrValueState; - if (!Kludges.allowMissing) setStyle = "error"; - return attrState(type, stream, state); - } - function attrValueState(type, stream, state) { - if (type == "string") return attrContinuedState; - if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return attrState;} - setStyle = "error"; - return attrState(type, stream, state); - } - function attrContinuedState(type, stream, state) { - if (type == "string") return attrContinuedState; - return attrState(type, stream, state); - } - - return { - startState: function() { - return {tokenize: inText, - state: baseState, - indented: 0, - tagName: null, tagStart: null, - context: null}; - }, - - token: function(stream, state) { - if (!state.tagName && stream.sol()) - state.indented = stream.indentation(); - - if (stream.eatSpace()) return null; - type = null; - var style = state.tokenize(stream, state); - if ((style || type) && style != "comment") { - setStyle = null; - state.state = state.state(type || style, stream, state); - if (setStyle) - style = setStyle == "error" ? style + " error" : setStyle; - } - return style; - }, - - indent: function(state, textAfter, fullLine) { - var context = state.context; - // Indent multi-line strings (e.g. css). - if (state.tokenize.isInAttribute) { - if (state.tagStart == state.indented) - return state.stringStartCol + 1; - else - return state.indented + indentUnit; - } - if (context && context.noIndent) return CodeMirror.Pass; - if (state.tokenize != inTag && state.tokenize != inText) - return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; - // Indent the starts of attribute names. - if (state.tagName) { - if (multilineTagIndentPastTag) - return state.tagStart + state.tagName.length + 2; - else - return state.tagStart + indentUnit * multilineTagIndentFactor; - } - if (alignCDATA && /$/, - blockCommentStart: "", - - configuration: parserConfig.htmlMode ? "html" : "xml", - helperType: parserConfig.htmlMode ? "html" : "xml" - }; -}); - -CodeMirror.defineMIME("text/xml", "xml"); -CodeMirror.defineMIME("application/xml", "xml"); -if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) - CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); - -}); diff --git a/public/js/lib/codemirror/mode/yaml/index.html b/public/js/lib/codemirror/mode/yaml/index.html deleted file mode 100644 index be9b632368..0000000000 --- a/public/js/lib/codemirror/mode/yaml/index.html +++ /dev/null @@ -1,80 +0,0 @@ - - -CodeMirror: YAML mode - - - - - - - - - -
-

YAML mode

-
- - -

MIME types defined: text/x-yaml.

- -
diff --git a/public/js/lib/codemirror/mode/yaml/yaml.js b/public/js/lib/codemirror/mode/yaml/yaml.js deleted file mode 100644 index b7015e599c..0000000000 --- a/public/js/lib/codemirror/mode/yaml/yaml.js +++ /dev/null @@ -1,117 +0,0 @@ -// CodeMirror, copyright (c) by Marijn Haverbeke and others -// Distributed under an MIT license: http://codemirror.net/LICENSE - -(function(mod) { - if (typeof exports == "object" && typeof module == "object") // CommonJS - mod(require("../../lib/codemirror")); - else if (typeof define == "function" && define.amd) // AMD - define(["../../lib/codemirror"], mod); - else // Plain browser env - mod(CodeMirror); -})(function(CodeMirror) { -"use strict"; - -CodeMirror.defineMode("yaml", function() { - - var cons = ['true', 'false', 'on', 'off', 'yes', 'no']; - var keywordRegex = new RegExp("\\b(("+cons.join(")|(")+"))$", 'i'); - - return { - token: function(stream, state) { - var ch = stream.peek(); - var esc = state.escaped; - state.escaped = false; - /* comments */ - if (ch == "#" && (stream.pos == 0 || /\s/.test(stream.string.charAt(stream.pos - 1)))) { - stream.skipToEnd(); - return "comment"; - } - - if (stream.match(/^('([^']|\\.)*'?|"([^"]|\\.)*"?)/)) - return "string"; - - if (state.literal && stream.indentation() > state.keyCol) { - stream.skipToEnd(); return "string"; - } else if (state.literal) { state.literal = false; } - if (stream.sol()) { - state.keyCol = 0; - state.pair = false; - state.pairStart = false; - /* document start */ - if(stream.match(/---/)) { return "def"; } - /* document end */ - if (stream.match(/\.\.\./)) { return "def"; } - /* array list item */ - if (stream.match(/\s*-\s+/)) { return 'meta'; } - } - /* inline pairs/lists */ - if (stream.match(/^(\{|\}|\[|\])/)) { - if (ch == '{') - state.inlinePairs++; - else if (ch == '}') - state.inlinePairs--; - else if (ch == '[') - state.inlineList++; - else - state.inlineList--; - return 'meta'; - } - - /* list seperator */ - if (state.inlineList > 0 && !esc && ch == ',') { - stream.next(); - return 'meta'; - } - /* pairs seperator */ - if (state.inlinePairs > 0 && !esc && ch == ',') { - state.keyCol = 0; - state.pair = false; - state.pairStart = false; - stream.next(); - return 'meta'; - } - - /* start of value of a pair */ - if (state.pairStart) { - /* block literals */ - if (stream.match(/^\s*(\||\>)\s*/)) { state.literal = true; return 'meta'; }; - /* references */ - if (stream.match(/^\s*(\&|\*)[a-z0-9\._-]+\b/i)) { return 'variable-2'; } - /* numbers */ - if (state.inlinePairs == 0 && stream.match(/^\s*-?[0-9\.\,]+\s?$/)) { return 'number'; } - if (state.inlinePairs > 0 && stream.match(/^\s*-?[0-9\.\,]+\s?(?=(,|}))/)) { return 'number'; } - /* keywords */ - if (stream.match(keywordRegex)) { return 'keyword'; } - } - - /* pairs (associative arrays) -> key */ - if (!state.pair && stream.match(/^\s*(?:[,\[\]{}&*!|>'"%@`][^\s'":]|[^,\[\]{}#&*!|>'"%@`])[^#]*?(?=\s*:($|\s))/)) { - state.pair = true; - state.keyCol = stream.indentation(); - return "atom"; - } - if (state.pair && stream.match(/^:\s*/)) { state.pairStart = true; return 'meta'; } - - /* nothing found, continue */ - state.pairStart = false; - state.escaped = (ch == '\\'); - stream.next(); - return null; - }, - startState: function() { - return { - pair: false, - pairStart: false, - keyCol: 0, - inlinePairs: 0, - inlineList: 0, - literal: false, - escaped: false - }; - } - }; -}); - -CodeMirror.defineMIME("text/x-yaml", "yaml"); - -}); diff --git a/public/js/lib/codemirror/package.json b/public/js/lib/codemirror/package.json deleted file mode 100644 index f934a08e02..0000000000 --- a/public/js/lib/codemirror/package.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "name": "codemirror", - "version":"4.10.0", - "main": "lib/codemirror.js", - "description": "In-browser code editing made bearable", - "licenses": [{"type": "MIT", - "url": "http://codemirror.net/LICENSE"}], - "directories": {"lib": "./lib"}, - "scripts": {"test": "node ./test/run.js"}, - "devDependencies": {"node-static": "0.6.0", - "phantomjs": "1.9.2-5"}, - "bugs": "http://github.com/codemirror/CodeMirror/issues", - "keywords": ["JavaScript", "CodeMirror", "Editor"], - "homepage": "http://codemirror.net", - "maintainers":[{"name": "Marijn Haverbeke", - "email": "marijnh@gmail.com", - "web": "http://marijnhaverbeke.nl"}], - "repository": {"type": "git", - "url": "https://github.com/codemirror/CodeMirror.git"} -} diff --git a/public/js/lib/codemirror/test/comment_test.js b/public/js/lib/codemirror/test/comment_test.js deleted file mode 100644 index 8bd3959ee9..0000000000 --- a/public/js/lib/codemirror/test/comment_test.js +++ /dev/null @@ -1,100 +0,0 @@ -namespace = "comment_"; - -(function() { - function test(name, mode, run, before, after) { - return testCM(name, function(cm) { - run(cm); - eq(cm.getValue(), after); - }, {value: before, mode: mode}); - } - - var simpleProg = "function foo() {\n return bar;\n}"; - var inlineBlock = "foo(/* bar */ true);"; - var inlineBlocks = "foo(/* bar */ true, /* baz */ false);"; - var multiLineInlineBlock = ["above();", "foo(/* bar */ true);", "below();"]; - - test("block", "javascript", function(cm) { - cm.blockComment(Pos(0, 3), Pos(3, 0), {blockCommentLead: " *"}); - }, simpleProg + "\n", "/* function foo() {\n * return bar;\n * }\n */"); - - test("blockToggle", "javascript", function(cm) { - cm.blockComment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); - cm.uncomment(Pos(0, 3), Pos(2, 0), {blockCommentLead: " *"}); - }, simpleProg, simpleProg); - - test("blockToggle2", "javascript", function(cm) { - cm.setCursor({line: 0, ch: 7 /* inside the block comment */}); - cm.execCommand("toggleComment"); - }, inlineBlock, "foo(bar true);"); - - // This test should work but currently fails. - // test("blockToggle3", "javascript", function(cm) { - // cm.setCursor({line: 0, ch: 7 /* inside the first block comment */}); - // cm.execCommand("toggleComment"); - // }, inlineBlocks, "foo(bar true, /* baz */ false);"); - - test("line", "javascript", function(cm) { - cm.lineComment(Pos(1, 1), Pos(1, 1)); - }, simpleProg, "function foo() {\n// return bar;\n}"); - - test("lineToggle", "javascript", function(cm) { - cm.lineComment(Pos(0, 0), Pos(2, 1)); - cm.uncomment(Pos(0, 0), Pos(2, 1)); - }, simpleProg, simpleProg); - - test("fallbackToBlock", "css", function(cm) { - cm.lineComment(Pos(0, 0), Pos(2, 1)); - }, "html {\n border: none;\n}", "/* html {\n border: none;\n} */"); - - test("fallbackToLine", "ruby", function(cm) { - cm.blockComment(Pos(0, 0), Pos(1)); - }, "def blah()\n return hah\n", "# def blah()\n# return hah\n"); - - test("ignoreExternalBlockComments", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, inlineBlocks, "// " + inlineBlocks); - - test("ignoreExternalBlockComments2", "javascript", function(cm) { - cm.setCursor({line: 0, ch: null /* eol */}); - cm.execCommand("toggleComment"); - }, inlineBlocks, "// " + inlineBlocks); - - test("ignoreExternalBlockCommentsMultiLineAbove", "javascript", function(cm) { - cm.setSelection({line: 0, ch: 0}, {line: 1, ch: 1}); - cm.execCommand("toggleComment"); - }, multiLineInlineBlock.join("\n"), ["// " + multiLineInlineBlock[0], - "// " + multiLineInlineBlock[1], - multiLineInlineBlock[2]].join("\n")); - - test("ignoreExternalBlockCommentsMultiLineBelow", "javascript", function(cm) { - cm.setSelection({line: 1, ch: 13 /* after end of block comment */}, {line: 2, ch: 1}); - cm.execCommand("toggleComment"); - }, multiLineInlineBlock.join("\n"), [multiLineInlineBlock[0], - "// " + multiLineInlineBlock[1], - "// " + multiLineInlineBlock[2]].join("\n")); - - test("commentRange", "javascript", function(cm) { - cm.blockComment(Pos(1, 2), Pos(1, 13), {fullLines: false}); - }, simpleProg, "function foo() {\n /*return bar;*/\n}"); - - test("indented", "javascript", function(cm) { - cm.lineComment(Pos(1, 0), Pos(2), {indent: true}); - }, simpleProg, "function foo() {\n // return bar;\n // }"); - - test("singleEmptyLine", "javascript", function(cm) { - cm.setCursor(1); - cm.execCommand("toggleComment"); - }, "a;\n\nb;", "a;\n// \nb;"); - - test("dontMessWithStrings", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "console.log(\"/*string*/\");", "// console.log(\"/*string*/\");"); - - test("dontMessWithStrings2", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "console.log(\"// string\");", "// console.log(\"// string\");"); - - test("dontMessWithStrings3", "javascript", function(cm) { - cm.execCommand("toggleComment"); - }, "// console.log(\"// string\");", "console.log(\"// string\");"); -})(); diff --git a/public/js/lib/codemirror/test/doc_test.js b/public/js/lib/codemirror/test/doc_test.js deleted file mode 100644 index 5f242f658d..0000000000 --- a/public/js/lib/codemirror/test/doc_test.js +++ /dev/null @@ -1,371 +0,0 @@ -(function() { - // A minilanguage for instantiating linked CodeMirror instances and Docs - function instantiateSpec(spec, place, opts) { - var names = {}, pos = 0, l = spec.length, editors = []; - while (spec) { - var m = spec.match(/^(\w+)(\*?)(?:='([^\']*)'|<(~?)(\w+)(?:\/(\d+)-(\d+))?)\s*/); - var name = m[1], isDoc = m[2], cur; - if (m[3]) { - cur = isDoc ? CodeMirror.Doc(m[3]) : CodeMirror(place, clone(opts, {value: m[3]})); - } else { - var other = m[5]; - if (!names.hasOwnProperty(other)) { - names[other] = editors.length; - editors.push(CodeMirror(place, opts)); - } - var doc = editors[names[other]].linkedDoc({ - sharedHist: !m[4], - from: m[6] ? Number(m[6]) : null, - to: m[7] ? Number(m[7]) : null - }); - cur = isDoc ? doc : CodeMirror(place, clone(opts, {value: doc})); - } - names[name] = editors.length; - editors.push(cur); - spec = spec.slice(m[0].length); - } - return editors; - } - - function clone(obj, props) { - if (!obj) return; - clone.prototype = obj; - var inst = new clone(); - if (props) for (var n in props) if (props.hasOwnProperty(n)) - inst[n] = props[n]; - return inst; - } - - function eqAll(val) { - var end = arguments.length, msg = null; - if (typeof arguments[end-1] == "string") - msg = arguments[--end]; - if (i == end) throw new Error("No editors provided to eqAll"); - for (var i = 1; i < end; ++i) - eq(arguments[i].getValue(), val, msg) - } - - function testDoc(name, spec, run, opts, expectFail) { - if (!opts) opts = {}; - - return test("doc_" + name, function() { - var place = document.getElementById("testground"); - var editors = instantiateSpec(spec, place, opts); - var successful = false; - - try { - run.apply(null, editors); - successful = true; - } finally { - if (!successful || verbose) { - place.style.visibility = "visible"; - } else { - for (var i = 0; i < editors.length; ++i) - if (editors[i] instanceof CodeMirror) - place.removeChild(editors[i].getWrapperElement()); - } - } - }, expectFail); - } - - var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent); - - function testBasic(a, b) { - eqAll("x", a, b); - a.setValue("hey"); - eqAll("hey", a, b); - b.setValue("wow"); - eqAll("wow", a, b); - a.replaceRange("u\nv\nw", Pos(0, 3)); - b.replaceRange("i", Pos(0, 4)); - b.replaceRange("j", Pos(2, 1)); - eqAll("wowui\nv\nwj", a, b); - } - - testDoc("basic", "A='x' B 0, "not at left"); - is(pos.top > 0, "not at top"); - }); - - testDoc("copyDoc", "A='u'", function(a) { - var copy = a.getDoc().copy(true); - a.setValue("foo"); - copy.setValue("bar"); - var old = a.swapDoc(copy); - eq(a.getValue(), "bar"); - a.undo(); - eq(a.getValue(), "u"); - a.swapDoc(old); - eq(a.getValue(), "foo"); - eq(old.historySize().undo, 1); - eq(old.copy(false).historySize().undo, 0); - }); - - testDoc("docKeepsMode", "A='1+1'", function(a) { - var other = CodeMirror.Doc("hi", "text/x-markdown"); - a.setOption("mode", "text/javascript"); - var old = a.swapDoc(other); - eq(a.getOption("mode"), "text/x-markdown"); - eq(a.getMode().name, "markdown"); - a.swapDoc(old); - eq(a.getOption("mode"), "text/javascript"); - eq(a.getMode().name, "javascript"); - }); - - testDoc("subview", "A='1\n2\n3\n4\n5' B<~A/1-3", function(a, b) { - eq(b.getValue(), "2\n3"); - eq(b.firstLine(), 1); - b.setCursor(Pos(4)); - eqPos(b.getCursor(), Pos(2, 1)); - a.replaceRange("-1\n0\n", Pos(0, 0)); - eq(b.firstLine(), 3); - eqPos(b.getCursor(), Pos(4, 1)); - a.undo(); - eqPos(b.getCursor(), Pos(2, 1)); - b.replaceRange("oyoy\n", Pos(2, 0)); - eq(a.getValue(), "1\n2\noyoy\n3\n4\n5"); - b.undo(); - eq(a.getValue(), "1\n2\n3\n4\n5"); - }); - - testDoc("subviewEditOnBoundary", "A='11\n22\n33\n44\n55' B<~A/1-4", function(a, b) { - a.replaceRange("x\nyy\nz", Pos(0, 1), Pos(2, 1)); - eq(b.firstLine(), 2); - eq(b.lineCount(), 2); - eq(b.getValue(), "z3\n44"); - a.replaceRange("q\nrr\ns", Pos(3, 1), Pos(4, 1)); - eq(b.firstLine(), 2); - eq(b.getValue(), "z3\n4q"); - eq(a.getValue(), "1x\nyy\nz3\n4q\nrr\ns5"); - a.execCommand("selectAll"); - a.replaceSelection("!"); - eqAll("!", a, b); - }); - - - testDoc("sharedMarker", "A='ab\ncd\nef\ngh' B 500){ - totalTime = 0; - delay = 50; - } - setTimeout(function(){step(i + 1);}, delay); - } else { // Quit tests - running = false; - return null; - } - } - step(0); -} - -function label(str, msg) { - if (msg) return str + " (" + msg + ")"; - return str; -} -function eq(a, b, msg) { - if (a != b) throw new Failure(label(a + " != " + b, msg)); -} -function near(a, b, margin, msg) { - if (Math.abs(a - b) > margin) - throw new Failure(label(a + " is not close to " + b + " (" + margin + ")", msg)); -} -function eqPos(a, b, msg) { - function str(p) { return "{line:" + p.line + ",ch:" + p.ch + "}"; } - if (a == b) return; - if (a == null) throw new Failure(label("comparing null to " + str(b), msg)); - if (b == null) throw new Failure(label("comparing " + str(a) + " to null", msg)); - if (a.line != b.line || a.ch != b.ch) throw new Failure(label(str(a) + " != " + str(b), msg)); -} -function is(a, msg) { - if (!a) throw new Failure(label("assertion failed", msg)); -} - -function countTests() { - if (!filters.length) return tests.length; - var sum = 0; - for (var i = 0; i < tests.length; ++i) { - var name = tests[i].name; - for (var j = 0; j < filters.length; j++) { - if (name.match(filters[j])) { - ++sum; - break; - } - } - } - return sum; -} - -function parseTestFilter(s) { - if (/_\*$/.test(s)) return new RegExp("^" + s.slice(0, s.length - 2), "i"); - else return new RegExp(s, "i"); -} diff --git a/public/js/lib/codemirror/test/emacs_test.js b/public/js/lib/codemirror/test/emacs_test.js deleted file mode 100644 index ccbd6ce491..0000000000 --- a/public/js/lib/codemirror/test/emacs_test.js +++ /dev/null @@ -1,141 +0,0 @@ -(function() { - "use strict"; - - var Pos = CodeMirror.Pos; - namespace = "emacs_"; - - var eventCache = {}; - function fakeEvent(keyName) { - var event = eventCache[key]; - if (event) return event; - - var ctrl, shift, alt; - var key = keyName.replace(/\w+-/g, function(type) { - if (type == "Ctrl-") ctrl = true; - else if (type == "Alt-") alt = true; - else if (type == "Shift-") shift = true; - return ""; - }); - var code; - for (var c in CodeMirror.keyNames) - if (CodeMirror.keyNames[c] == key) { code = c; break; } - if (c == null) throw new Error("Unknown key: " + key); - - return eventCache[keyName] = { - type: "keydown", keyCode: code, ctrlKey: ctrl, shiftKey: shift, altKey: alt, - preventDefault: function(){}, stopPropagation: function(){} - }; - } - - function sim(name, start /*, actions... */) { - var keys = Array.prototype.slice.call(arguments, 2); - testCM(name, function(cm) { - for (var i = 0; i < keys.length; ++i) { - var cur = keys[i]; - if (cur instanceof Pos) cm.setCursor(cur); - else if (cur.call) cur(cm); - else cm.triggerOnKeyDown(fakeEvent(cur)); - } - }, {keyMap: "emacs", value: start, mode: "javascript"}); - } - - function at(line, ch) { return function(cm) { eqPos(cm.getCursor(), Pos(line, ch)); }; } - function txt(str) { return function(cm) { eq(cm.getValue(), str); }; } - - sim("motionHSimple", "abc", "Ctrl-F", "Ctrl-F", "Ctrl-B", at(0, 1)); - sim("motionHMulti", "abcde", - "Ctrl-4", "Ctrl-F", at(0, 4), "Ctrl--", "Ctrl-2", "Ctrl-F", at(0, 2), - "Ctrl-5", "Ctrl-B", at(0, 0)); - - sim("motionHWord", "abc. def ghi", - "Alt-F", at(0, 3), "Alt-F", at(0, 8), - "Ctrl-B", "Alt-B", at(0, 5), "Alt-B", at(0, 0)); - sim("motionHWordMulti", "abc. def ghi ", - "Ctrl-3", "Alt-F", at(0, 12), "Ctrl-2", "Alt-B", at(0, 5), - "Ctrl--", "Alt-B", at(0, 8)); - - sim("motionVSimple", "a\nb\nc\n", "Ctrl-N", "Ctrl-N", "Ctrl-P", at(1, 0)); - sim("motionVMulti", "a\nb\nc\nd\ne\n", - "Ctrl-2", "Ctrl-N", at(2, 0), "Ctrl-F", "Ctrl--", "Ctrl-N", at(1, 1), - "Ctrl--", "Ctrl-3", "Ctrl-P", at(4, 1)); - - sim("killYank", "abc\ndef\nghi", - "Ctrl-F", "Ctrl-Space", "Ctrl-N", "Ctrl-N", "Ctrl-W", "Ctrl-E", "Ctrl-Y", - txt("ahibc\ndef\ng")); - sim("killRing", "abcdef", - "Ctrl-Space", "Ctrl-F", "Ctrl-W", "Ctrl-Space", "Ctrl-F", "Ctrl-W", - "Ctrl-Y", "Alt-Y", - txt("acdef")); - sim("copyYank", "abcd", - "Ctrl-Space", "Ctrl-E", "Alt-W", "Ctrl-Y", - txt("abcdabcd")); - - sim("killLineSimple", "foo\nbar", "Ctrl-F", "Ctrl-K", txt("f\nbar")); - sim("killLineEmptyLine", "foo\n \nbar", "Ctrl-N", "Ctrl-K", txt("foo\nbar")); - sim("killLineMulti", "foo\nbar\nbaz", - "Ctrl-F", "Ctrl-F", "Ctrl-K", "Ctrl-K", "Ctrl-K", "Ctrl-A", "Ctrl-Y", - txt("o\nbarfo\nbaz")); - - sim("moveByParagraph", "abc\ndef\n\n\nhij\nklm\n\n", - "Ctrl-F", "Ctrl-Down", at(2, 0), "Ctrl-Down", at(6, 0), - "Ctrl-N", "Ctrl-Up", at(3, 0), "Ctrl-Up", at(0, 0), - Pos(1, 2), "Ctrl-Down", at(2, 0), Pos(4, 2), "Ctrl-Up", at(3, 0)); - sim("moveByParagraphMulti", "abc\n\ndef\n\nhij\n\nklm", - "Ctrl-U", "2", "Ctrl-Down", at(3, 0), - "Shift-Alt-.", "Ctrl-3", "Ctrl-Up", at(1, 0)); - - sim("moveBySentence", "sentence one! sentence\ntwo\n\nparagraph two", - "Alt-E", at(0, 13), "Alt-E", at(1, 3), "Ctrl-F", "Alt-A", at(0, 13)); - - sim("moveByExpr", "function foo(a, b) {}", - "Ctrl-Alt-F", at(0, 8), "Ctrl-Alt-F", at(0, 12), "Ctrl-Alt-F", at(0, 18), - "Ctrl-Alt-B", at(0, 12), "Ctrl-Alt-B", at(0, 9)); - sim("moveByExprMulti", "foo bar baz bug", - "Ctrl-2", "Ctrl-Alt-F", at(0, 7), - "Ctrl--", "Ctrl-Alt-F", at(0, 4), - "Ctrl--", "Ctrl-2", "Ctrl-Alt-B", at(0, 11)); - sim("delExpr", "var x = [\n a,\n b\n c\n];", - Pos(0, 8), "Ctrl-Alt-K", txt("var x = ;"), "Ctrl-/", - Pos(4, 1), "Ctrl-Alt-Backspace", txt("var x = ;")); - sim("delExprMulti", "foo bar baz", - "Ctrl-2", "Ctrl-Alt-K", txt(" baz"), - "Ctrl-/", "Ctrl-E", "Ctrl-2", "Ctrl-Alt-Backspace", txt("foo ")); - - sim("justOneSpace", "hi bye ", - Pos(0, 4), "Alt-Space", txt("hi bye "), - Pos(0, 4), "Alt-Space", txt("hi b ye "), - "Ctrl-A", "Alt-Space", "Ctrl-E", "Alt-Space", txt(" hi b ye ")); - - sim("openLine", "foo bar", "Alt-F", "Ctrl-O", txt("foo\n bar")) - - sim("transposeChar", "abcd\ne", - "Ctrl-F", "Ctrl-T", "Ctrl-T", txt("bcad\ne"), at(0, 3), - "Ctrl-F", "Ctrl-T", "Ctrl-T", "Ctrl-T", txt("bcda\ne"), at(0, 4), - "Ctrl-F", "Ctrl-T", txt("bcde\na"), at(1, 0)); - - sim("manipWordCase", "foo BAR bAZ", - "Alt-C", "Alt-L", "Alt-U", txt("Foo bar BAZ"), - "Ctrl-A", "Alt-U", "Alt-L", "Alt-C", txt("FOO bar Baz")); - sim("manipWordCaseMulti", "foo Bar bAz", - "Ctrl-2", "Alt-U", txt("FOO BAR bAz"), - "Ctrl-A", "Ctrl-3", "Alt-C", txt("Foo Bar Baz")); - - sim("upExpr", "foo {\n bar[];\n baz(blah);\n}", - Pos(2, 7), "Ctrl-Alt-U", at(2, 5), "Ctrl-Alt-U", at(0, 4)); - sim("transposeExpr", "do foo[bar] dah", - Pos(0, 6), "Ctrl-Alt-T", txt("do [bar]foo dah")); - - sim("clearMark", "abcde", Pos(0, 2), "Ctrl-Space", "Ctrl-F", "Ctrl-F", - "Ctrl-G", "Ctrl-W", txt("abcde")); - - sim("delRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Delete", txt("cde")); - sim("backspaceRegion", "abcde", "Ctrl-Space", "Ctrl-F", "Ctrl-F", "Backspace", txt("cde")); - - testCM("save", function(cm) { - var saved = false; - CodeMirror.commands.save = function(cm) { saved = cm.getValue(); }; - cm.triggerOnKeyDown(fakeEvent("Ctrl-X")); - cm.triggerOnKeyDown(fakeEvent("Ctrl-S")); - is(saved, "hi"); - }, {value: "hi", keyMap: "emacs"}); -})(); diff --git a/public/js/lib/codemirror/test/index.html b/public/js/lib/codemirror/test/index.html deleted file mode 100644 index 89f4e378c3..0000000000 --- a/public/js/lib/codemirror/test/index.html +++ /dev/null @@ -1,236 +0,0 @@ - - -CodeMirror: Test Suite - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Test Suite

- -

A limited set of programmatic sanity tests for CodeMirror.

- -
-
Ran 0 of 0 tests
-
-

Please enable JavaScript...

-
- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/public/js/lib/codemirror/test/lint/acorn.js b/public/js/lib/codemirror/test/lint/acorn.js deleted file mode 100644 index 0f11931752..0000000000 --- a/public/js/lib/codemirror/test/lint/acorn.js +++ /dev/null @@ -1,1782 +0,0 @@ -// Acorn is a tiny, fast JavaScript parser written in JavaScript. -// -// Acorn was written by Marijn Haverbeke and released under an MIT -// license. The Unicode regexps (for identifiers and whitespace) were -// taken from [Esprima](http://esprima.org) by Ariya Hidayat. -// -// Git repositories for Acorn are available at -// -// http://marijnhaverbeke.nl/git/acorn -// https://github.com/marijnh/acorn.git -// -// Please use the [github bug tracker][ghbt] to report issues. -// -// [ghbt]: https://github.com/marijnh/acorn/issues -// -// This file defines the main parser interface. The library also comes -// with a [error-tolerant parser][dammit] and an -// [abstract syntax tree walker][walk], defined in other files. -// -// [dammit]: acorn_loose.js -// [walk]: util/walk.js - -(function(root, mod) { - if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS - if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD - mod(root.acorn || (root.acorn = {})); // Plain browser env -})(this, function(exports) { - "use strict"; - - exports.version = "0.4.1"; - - // The main exported interface (under `self.acorn` when in the - // browser) is a `parse` function that takes a code string and - // returns an abstract syntax tree as specified by [Mozilla parser - // API][api], with the caveat that the SpiderMonkey-specific syntax - // (`let`, `yield`, inline XML, etc) is not recognized. - // - // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API - - var options, input, inputLen, sourceFile; - - exports.parse = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - return parseTopLevel(options.program); - }; - - // A second optional argument can be given to further configure - // the parser process. These options are recognized: - - var defaultOptions = exports.defaultOptions = { - // `ecmaVersion` indicates the ECMAScript version to parse. Must - // be either 3 or 5. This - // influences support for strict mode, the set of reserved words, and - // support for getters and setter. - ecmaVersion: 5, - // Turn on `strictSemicolons` to prevent the parser from doing - // automatic semicolon insertion. - strictSemicolons: false, - // When `allowTrailingCommas` is false, the parser will not allow - // trailing commas in array and object literals. - allowTrailingCommas: true, - // By default, reserved words are not enforced. Enable - // `forbidReserved` to enforce them. When this option has the - // value "everywhere", reserved words and keywords can also not be - // used as property names. - forbidReserved: false, - // When enabled, a return at the top level is not considered an - // error. - allowReturnOutsideFunction: false, - // When `locations` is on, `loc` properties holding objects with - // `start` and `end` properties in `{line, column}` form (with - // line being 1-based and column 0-based) will be attached to the - // nodes. - locations: false, - // A function can be passed as `onComment` option, which will - // cause Acorn to call that function with `(block, text, start, - // end)` parameters whenever a comment is skipped. `block` is a - // boolean indicating whether this is a block (`/* */`) comment, - // `text` is the content of the comment, and `start` and `end` are - // character offsets that denote the start and end of the comment. - // When the `locations` option is on, two more parameters are - // passed, the full `{line, column}` locations of the start and - // end of the comments. Note that you are not allowed to call the - // parser from the callback—that will corrupt its internal state. - onComment: null, - // Nodes have their start and end characters offsets recorded in - // `start` and `end` properties (directly on the node, rather than - // the `loc` object, which holds line/column data. To also add a - // [semi-standardized][range] `range` property holding a `[start, - // end]` array with the same numbers, set the `ranges` option to - // `true`. - // - // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678 - ranges: false, - // It is possible to parse multiple files into a single AST by - // passing the tree produced by parsing the first file as - // `program` option in subsequent parses. This will add the - // toplevel forms of the parsed file to the `Program` (top) node - // of an existing parse tree. - program: null, - // When `locations` is on, you can pass this to record the source - // file in every node's `loc` object. - sourceFile: null, - // This value, if given, is stored in every node, whether - // `locations` is on or off. - directSourceFile: null - }; - - function setOptions(opts) { - options = opts || {}; - for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt)) - options[opt] = defaultOptions[opt]; - sourceFile = options.sourceFile || null; - } - - // The `getLineInfo` function is mostly useful when the - // `locations` option is off (for performance reasons) and you - // want to find the line/column position for a given character - // offset. `input` should be the code string that the offset refers - // into. - - var getLineInfo = exports.getLineInfo = function(input, offset) { - for (var line = 1, cur = 0;;) { - lineBreak.lastIndex = cur; - var match = lineBreak.exec(input); - if (match && match.index < offset) { - ++line; - cur = match.index + match[0].length; - } else break; - } - return {line: line, column: offset - cur}; - }; - - // Acorn is organized as a tokenizer and a recursive-descent parser. - // The `tokenize` export provides an interface to the tokenizer. - // Because the tokenizer is optimized for being efficiently used by - // the Acorn parser itself, this interface is somewhat crude and not - // very modular. Performing another parse or call to `tokenize` will - // reset the internal state, and invalidate existing tokenizers. - - exports.tokenize = function(inpt, opts) { - input = String(inpt); inputLen = input.length; - setOptions(opts); - initTokenState(); - - var t = {}; - function getToken(forceRegexp) { - lastEnd = tokEnd; - readToken(forceRegexp); - t.start = tokStart; t.end = tokEnd; - t.startLoc = tokStartLoc; t.endLoc = tokEndLoc; - t.type = tokType; t.value = tokVal; - return t; - } - getToken.jumpTo = function(pos, reAllowed) { - tokPos = pos; - if (options.locations) { - tokCurLine = 1; - tokLineStart = lineBreak.lastIndex = 0; - var match; - while ((match = lineBreak.exec(input)) && match.index < pos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - tokRegexpAllowed = reAllowed; - skipSpace(); - }; - return getToken; - }; - - // State is kept in (closure-)global variables. We already saw the - // `options`, `input`, and `inputLen` variables above. - - // The current position of the tokenizer in the input. - - var tokPos; - - // The start and end offsets of the current token. - - var tokStart, tokEnd; - - // When `options.locations` is true, these hold objects - // containing the tokens start and end line/column pairs. - - var tokStartLoc, tokEndLoc; - - // The type and value of the current token. Token types are objects, - // named by variables against which they can be compared, and - // holding properties that describe them (indicating, for example, - // the precedence of an infix operator, and the original name of a - // keyword token). The kind of value that's held in `tokVal` depends - // on the type of the token. For literals, it is the literal value, - // for operators, the operator name, and so on. - - var tokType, tokVal; - - // Interal state for the tokenizer. To distinguish between division - // operators and regular expressions, it remembers whether the last - // token was one that is allowed to be followed by an expression. - // (If it is, a slash is probably a regexp, if it isn't it's a - // division operator. See the `parseStatement` function for a - // caveat.) - - var tokRegexpAllowed; - - // When `options.locations` is true, these are used to keep - // track of the current line, and know when a new line has been - // entered. - - var tokCurLine, tokLineStart; - - // These store the position of the previous token, which is useful - // when finishing a node and assigning its `end` position. - - var lastStart, lastEnd, lastEndLoc; - - // This is the parser's state. `inFunction` is used to reject - // `return` statements outside of functions, `labels` to verify that - // `break` and `continue` have somewhere to jump to, and `strict` - // indicates whether strict mode is on. - - var inFunction, labels, strict; - - // This function is used to raise exceptions on parse errors. It - // takes an offset integer (into the current `input`) to indicate - // the location of the error, attaches the position to the end - // of the error message, and then raises a `SyntaxError` with that - // message. - - function raise(pos, message) { - var loc = getLineInfo(input, pos); - message += " (" + loc.line + ":" + loc.column + ")"; - var err = new SyntaxError(message); - err.pos = pos; err.loc = loc; err.raisedAt = tokPos; - throw err; - } - - // Reused empty array added for node fields that are always empty. - - var empty = []; - - // ## Token types - - // The assignment of fine-grained, information-carrying type objects - // allows the tokenizer to store the information it has about a - // token in a way that is very cheap for the parser to look up. - - // All token type variables start with an underscore, to make them - // easy to recognize. - - // These are the general types. The `type` property is only used to - // make them recognizeable when debugging. - - var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"}; - var _name = {type: "name"}, _eof = {type: "eof"}; - - // Keyword tokens. The `keyword` property (also used in keyword-like - // operators) indicates that the token originated from an - // identifier-like word, which is used when parsing property names. - // - // The `beforeExpr` property is used to disambiguate between regular - // expressions and divisions. It is set on all token types that can - // be followed by an expression (thus, a slash after them would be a - // regular expression). - // - // `isLoop` marks a keyword as starting a loop, which is important - // to know when parsing a label, in order to allow or disallow - // continue jumps to that label. - - var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"}; - var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"}; - var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true}; - var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"}; - var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"}; - var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"}; - var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true}; - var _this = {keyword: "this"}; - - // The keywords that denote values. - - var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true}; - var _false = {keyword: "false", atomValue: false}; - - // Some keywords are treated as regular operators. `in` sometimes - // (when parsing `for`) needs to be tested against specifically, so - // we assign a variable name to it for quick comparing. - - var _in = {keyword: "in", binop: 7, beforeExpr: true}; - - // Map keyword names to token types. - - var keywordTypes = {"break": _break, "case": _case, "catch": _catch, - "continue": _continue, "debugger": _debugger, "default": _default, - "do": _do, "else": _else, "finally": _finally, "for": _for, - "function": _function, "if": _if, "return": _return, "switch": _switch, - "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with, - "null": _null, "true": _true, "false": _false, "new": _new, "in": _in, - "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this, - "typeof": {keyword: "typeof", prefix: true, beforeExpr: true}, - "void": {keyword: "void", prefix: true, beforeExpr: true}, - "delete": {keyword: "delete", prefix: true, beforeExpr: true}}; - - // Punctuation token types. Again, the `type` property is purely for debugging. - - var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true}; - var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"}; - var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true}; - var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true}; - - // Operators. These carry several kinds of properties to help the - // parser use them properly (the presence of these properties is - // what categorizes them as operators). - // - // `binop`, when present, specifies that this operator is a binary - // operator, and will refer to its precedence. - // - // `prefix` and `postfix` mark the operator as a prefix or postfix - // unary operator. `isUpdate` specifies that the node produced by - // the operator should be of type UpdateExpression rather than - // simply UnaryExpression (`++` and `--`). - // - // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as - // binary operators with a very low precedence, that should result - // in AssignmentExpression nodes. - - var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true}; - var _assign = {isAssign: true, beforeExpr: true}; - var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true}; - var _logicalOR = {binop: 1, beforeExpr: true}; - var _logicalAND = {binop: 2, beforeExpr: true}; - var _bitwiseOR = {binop: 3, beforeExpr: true}; - var _bitwiseXOR = {binop: 4, beforeExpr: true}; - var _bitwiseAND = {binop: 5, beforeExpr: true}; - var _equality = {binop: 6, beforeExpr: true}; - var _relational = {binop: 7, beforeExpr: true}; - var _bitShift = {binop: 8, beforeExpr: true}; - var _plusMin = {binop: 9, prefix: true, beforeExpr: true}; - var _multiplyModulo = {binop: 10, beforeExpr: true}; - - // Provide access to the token types for external users of the - // tokenizer. - - exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR, - parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon, - dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof, - num: _num, regexp: _regexp, string: _string}; - for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw]; - - // This is a trick taken from Esprima. It turns out that, on - // non-Chrome browsers, to check whether a string is in a set, a - // predicate containing a big ugly `switch` statement is faster than - // a regular expression, and on Chrome the two are about on par. - // This function uses `eval` (non-lexical) to produce such a - // predicate from a space-separated string of words. - // - // It starts by sorting the words by length. - - function makePredicate(words) { - words = words.split(" "); - var f = "", cats = []; - out: for (var i = 0; i < words.length; ++i) { - for (var j = 0; j < cats.length; ++j) - if (cats[j][0].length == words[i].length) { - cats[j].push(words[i]); - continue out; - } - cats.push([words[i]]); - } - function compareTo(arr) { - if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";"; - f += "switch(str){"; - for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":"; - f += "return true}return false;"; - } - - // When there are more than three length categories, an outer - // switch first dispatches on the lengths, to save on comparisons. - - if (cats.length > 3) { - cats.sort(function(a, b) {return b.length - a.length;}); - f += "switch(str.length){"; - for (var i = 0; i < cats.length; ++i) { - var cat = cats[i]; - f += "case " + cat[0].length + ":"; - compareTo(cat); - } - f += "}"; - - // Otherwise, simply generate a flat `switch` statement. - - } else { - compareTo(words); - } - return new Function("str", f); - } - - // The ECMAScript 3 reserved word list. - - var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile"); - - // ECMAScript 5 reserved words. - - var isReservedWord5 = makePredicate("class enum extends super const export import"); - - // The additional reserved words in strict mode. - - var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield"); - - // The forbidden variable names in strict mode. - - var isStrictBadIdWord = makePredicate("eval arguments"); - - // And the keywords. - - var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this"); - - // ## Character categories - - // Big ugly regular expressions that match characters in the - // whitespace, identifier, and identifier-start categories. These - // are only applied when a character is found to actually have a - // code point above 128. - - var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/; - var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc"; - var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f"; - var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]"); - var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]"); - - // Whether a single character denotes a newline. - - var newline = /[\n\r\u2028\u2029]/; - - // Matches a whole line break (where CRLF is considered a single - // line break). Used to count lines. - - var lineBreak = /\r\n|[\n\r\u2028\u2029]/g; - - // Test whether a given character code starts an identifier. - - var isIdentifierStart = exports.isIdentifierStart = function(code) { - if (code < 65) return code === 36; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code)); - }; - - // Test whether a given character is part of an identifier. - - var isIdentifierChar = exports.isIdentifierChar = function(code) { - if (code < 48) return code === 36; - if (code < 58) return true; - if (code < 65) return false; - if (code < 91) return true; - if (code < 97) return code === 95; - if (code < 123)return true; - return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code)); - }; - - // ## Tokenizer - - // These are used when `options.locations` is on, for the - // `tokStartLoc` and `tokEndLoc` properties. - - function line_loc_t() { - this.line = tokCurLine; - this.column = tokPos - tokLineStart; - } - - // Reset the token state. Used at the start of a parse. - - function initTokenState() { - tokCurLine = 1; - tokPos = tokLineStart = 0; - tokRegexpAllowed = true; - skipSpace(); - } - - // Called at the end of every token. Sets `tokEnd`, `tokVal`, and - // `tokRegexpAllowed`, and skips the space after the token, so that - // the next one's `tokStart` will point at the right position. - - function finishToken(type, val) { - tokEnd = tokPos; - if (options.locations) tokEndLoc = new line_loc_t; - tokType = type; - skipSpace(); - tokVal = val; - tokRegexpAllowed = type.beforeExpr; - } - - function skipBlockComment() { - var startLoc = options.onComment && options.locations && new line_loc_t; - var start = tokPos, end = input.indexOf("*/", tokPos += 2); - if (end === -1) raise(tokPos - 2, "Unterminated comment"); - tokPos = end + 2; - if (options.locations) { - lineBreak.lastIndex = start; - var match; - while ((match = lineBreak.exec(input)) && match.index < tokPos) { - ++tokCurLine; - tokLineStart = match.index + match[0].length; - } - } - if (options.onComment) - options.onComment(true, input.slice(start + 2, end), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - function skipLineComment() { - var start = tokPos; - var startLoc = options.onComment && options.locations && new line_loc_t; - var ch = input.charCodeAt(tokPos+=2); - while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) { - ++tokPos; - ch = input.charCodeAt(tokPos); - } - if (options.onComment) - options.onComment(false, input.slice(start + 2, tokPos), start, tokPos, - startLoc, options.locations && new line_loc_t); - } - - // Called at the start of the parse and after every token. Skips - // whitespace and comments, and. - - function skipSpace() { - while (tokPos < inputLen) { - var ch = input.charCodeAt(tokPos); - if (ch === 32) { // ' ' - ++tokPos; - } else if (ch === 13) { - ++tokPos; - var next = input.charCodeAt(tokPos); - if (next === 10) { - ++tokPos; - } - if (options.locations) { - ++tokCurLine; - tokLineStart = tokPos; - } - } else if (ch === 10 || ch === 8232 || ch === 8233) { - ++tokPos; - if (options.locations) { - ++tokCurLine; - tokLineStart = tokPos; - } - } else if (ch > 8 && ch < 14) { - ++tokPos; - } else if (ch === 47) { // '/' - var next = input.charCodeAt(tokPos + 1); - if (next === 42) { // '*' - skipBlockComment(); - } else if (next === 47) { // '/' - skipLineComment(); - } else break; - } else if (ch === 160) { // '\xa0' - ++tokPos; - } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) { - ++tokPos; - } else { - break; - } - } - } - - // ### Token reading - - // This is the function that is called to fetch the next token. It - // is somewhat obscure, because it works in character codes rather - // than characters, and because operator parsing has been inlined - // into it. - // - // All in the name of speed. - // - // The `forceRegexp` parameter is used in the one case where the - // `tokRegexpAllowed` trick does not work. See `parseStatement`. - - function readToken_dot() { - var next = input.charCodeAt(tokPos + 1); - if (next >= 48 && next <= 57) return readNumber(true); - ++tokPos; - return finishToken(_dot); - } - - function readToken_slash() { // '/' - var next = input.charCodeAt(tokPos + 1); - if (tokRegexpAllowed) {++tokPos; return readRegexp();} - if (next === 61) return finishOp(_assign, 2); - return finishOp(_slash, 1); - } - - function readToken_mult_modulo() { // '%*' - var next = input.charCodeAt(tokPos + 1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_multiplyModulo, 1); - } - - function readToken_pipe_amp(code) { // '|&' - var next = input.charCodeAt(tokPos + 1); - if (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2); - if (next === 61) return finishOp(_assign, 2); - return finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1); - } - - function readToken_caret() { // '^' - var next = input.charCodeAt(tokPos + 1); - if (next === 61) return finishOp(_assign, 2); - return finishOp(_bitwiseXOR, 1); - } - - function readToken_plus_min(code) { // '+-' - var next = input.charCodeAt(tokPos + 1); - if (next === code) { - if (next == 45 && input.charCodeAt(tokPos + 2) == 62 && - newline.test(input.slice(lastEnd, tokPos))) { - // A `-->` line comment - tokPos += 3; - skipLineComment(); - skipSpace(); - return readToken(); - } - return finishOp(_incDec, 2); - } - if (next === 61) return finishOp(_assign, 2); - return finishOp(_plusMin, 1); - } - - function readToken_lt_gt(code) { // '<>' - var next = input.charCodeAt(tokPos + 1); - var size = 1; - if (next === code) { - size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2; - if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1); - return finishOp(_bitShift, size); - } - if (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 && - input.charCodeAt(tokPos + 3) == 45) { - // `", lineWrapping: true}, ie_lt8 || opera_lt10); - -testCM("scrollVerticallyAndHorizontally", function(cm) { - cm.setSize(100, 100); - addDoc(cm, 40, 40); - cm.setCursor(39); - var wrap = cm.getWrapperElement(), bar = byClassName(wrap, "CodeMirror-vscrollbar")[0]; - is(bar.offsetHeight < wrap.offsetHeight, "vertical scrollbar limited by horizontal one"); - var cursorBox = byClassName(wrap, "CodeMirror-cursor")[0].getBoundingClientRect(); - var editorBox = wrap.getBoundingClientRect(); - is(cursorBox.bottom < editorBox.top + cm.getScrollerElement().clientHeight, - "bottom line visible"); -}, {lineNumbers: true}); - -testCM("moveVstuck", function(cm) { - var lines = byClassName(cm.getWrapperElement(), "CodeMirror-lines")[0].firstChild, h0 = lines.offsetHeight; - var val = "fooooooooooooooooooooooooo baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaar\n"; - cm.setValue(val); - for (var w = cm.charCoords(Pos(0, 26), "div").right * 2.8;; w += 5) { - cm.setSize(w); - if (lines.offsetHeight <= 3.5 * h0) break; - } - cm.setCursor(Pos(0, val.length - 1)); - cm.moveV(-1, "line"); - eqPos(cm.getCursor(), Pos(0, 26)); -}, {lineWrapping: true}, ie_lt8 || opera_lt10); - -testCM("collapseOnMove", function(cm) { - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goLineUp"); - is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(0, 1)); - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goPageDown"); - is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(2, 4)); - cm.execCommand("goLineUp"); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 4)); - cm.setSelection(Pos(0, 1), Pos(2, 4)); - cm.execCommand("goCharLeft"); - is(!cm.somethingSelected()); - eqPos(cm.getCursor(), Pos(0, 1)); -}, {value: "aaaaa\nb\nccccc"}); - -testCM("clickTab", function(cm) { - var p0 = cm.charCoords(Pos(0, 0)); - eqPos(cm.coordsChar({left: p0.left + 5, top: p0.top + 5}), Pos(0, 0)); - eqPos(cm.coordsChar({left: p0.right - 5, top: p0.top + 5}), Pos(0, 1)); -}, {value: "\t\n\n", lineWrapping: true, tabSize: 8}); - -testCM("verticalScroll", function(cm) { - cm.setSize(100, 200); - cm.setValue("foo\nbar\nbaz\n"); - var sc = cm.getScrollerElement(), baseWidth = sc.scrollWidth; - cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); - is(sc.scrollWidth > baseWidth, "scrollbar present"); - cm.replaceRange("foo", Pos(0, 0), Pos(0)); - if (!phantom) eq(sc.scrollWidth, baseWidth, "scrollbar gone"); - cm.replaceRange("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah", Pos(0, 0), Pos(0)); - cm.replaceRange("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbh", Pos(1, 0), Pos(1)); - is(sc.scrollWidth > baseWidth, "present again"); - var curWidth = sc.scrollWidth; - cm.replaceRange("foo", Pos(0, 0), Pos(0)); - is(sc.scrollWidth < curWidth, "scrollbar smaller"); - is(sc.scrollWidth > baseWidth, "but still present"); -}); - -testCM("extraKeys", function(cm) { - var outcome; - function fakeKey(expected, code, props) { - if (typeof code == "string") code = code.charCodeAt(0); - var e = {type: "keydown", keyCode: code, preventDefault: function(){}, stopPropagation: function(){}}; - if (props) for (var n in props) e[n] = props[n]; - outcome = null; - cm.triggerOnKeyDown(e); - eq(outcome, expected); - } - CodeMirror.commands.testCommand = function() {outcome = "tc";}; - CodeMirror.commands.goTestCommand = function() {outcome = "gtc";}; - cm.setOption("extraKeys", {"Shift-X": function() {outcome = "sx";}, - "X": function() {outcome = "x";}, - "Ctrl-Alt-U": function() {outcome = "cau";}, - "End": "testCommand", - "Home": "goTestCommand", - "Tab": false}); - fakeKey(null, "U"); - fakeKey("cau", "U", {ctrlKey: true, altKey: true}); - fakeKey(null, "U", {shiftKey: true, ctrlKey: true, altKey: true}); - fakeKey("x", "X"); - fakeKey("sx", "X", {shiftKey: true}); - fakeKey("tc", 35); - fakeKey(null, 35, {shiftKey: true}); - fakeKey("gtc", 36); - fakeKey("gtc", 36, {shiftKey: true}); - fakeKey(null, 9); -}, null, window.opera && mac); - -testCM("wordMovementCommands", function(cm) { - cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 7)); - cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 12)); - cm.execCommand("goWordLeft"); - eqPos(cm.getCursor(), Pos(0, 9)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(0, 24)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(1, 9)); - cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(1, 13)); - cm.execCommand("goWordRight"); cm.execCommand("goWordRight"); - eqPos(cm.getCursor(), Pos(2, 0)); -}, {value: "this is (the) firstline.\na foo12\u00e9\u00f8\u00d7bar\n"}); - -testCM("groupMovementCommands", function(cm) { - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 4)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 7)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 10)); - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 7)); - cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 15)); - cm.setCursor(Pos(0, 17)); - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 16)); - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 14)); - cm.execCommand("goGroupRight"); cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(0, 20)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 2)); - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), Pos(1, 5)); - cm.execCommand("goGroupLeft"); cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 20)); - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), Pos(0, 16)); -}, {value: "booo ba---quux. ffff\n abc d"}); - -testCM("groupsAndWhitespace", function(cm) { - var positions = [Pos(0, 0), Pos(0, 2), Pos(0, 5), Pos(0, 9), Pos(0, 11), - Pos(1, 0), Pos(1, 2), Pos(1, 5)]; - for (var i = 1; i < positions.length; i++) { - cm.execCommand("goGroupRight"); - eqPos(cm.getCursor(), positions[i]); - } - for (var i = positions.length - 2; i >= 0; i--) { - cm.execCommand("goGroupLeft"); - eqPos(cm.getCursor(), i == 2 ? Pos(0, 6) : positions[i]); - } -}, {value: " foo +++ \n bar"}); - -testCM("charMovementCommands", function(cm) { - cm.execCommand("goCharLeft"); cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 2)); - cm.setCursor(Pos(1, 0)); - cm.execCommand("goColumnLeft"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); - cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), Pos(0, 5)); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(1, 5)); - cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineStartSmart"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.setCursor(Pos(2, 0)); - cm.execCommand("goCharRight"); cm.execCommand("goColumnRight"); - eqPos(cm.getCursor(), Pos(2, 0)); -}, {value: "line1\n ine2\n"}); - -testCM("verticalMovementCommands", function(cm) { - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goLineDown"); - if (!phantom) // This fails in PhantomJS, though not in a real Webkit - eqPos(cm.getCursor(), Pos(1, 0)); - cm.setCursor(Pos(1, 12)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 5)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(3, 0)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(2, 5)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 12)); - cm.execCommand("goPageDown"); - eqPos(cm.getCursor(), Pos(5, 0)); - cm.execCommand("goPageDown"); cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(5, 0)); - cm.execCommand("goPageUp"); - eqPos(cm.getCursor(), Pos(0, 0)); -}, {value: "line1\nlong long line2\nline3\n\nline5\n"}); - -testCM("verticalMovementCommandsWrapping", function(cm) { - cm.setSize(120); - cm.setCursor(Pos(0, 5)); - cm.execCommand("goLineDown"); - eq(cm.getCursor().line, 0); - is(cm.getCursor().ch > 5, "moved beyond wrap"); - for (var i = 0; ; ++i) { - is(i < 20, "no endless loop"); - cm.execCommand("goLineDown"); - var cur = cm.getCursor(); - if (cur.line == 1) eq(cur.ch, 5); - if (cur.line == 2) { eq(cur.ch, 1); break; } - } -}, {value: "a very long line that wraps around somehow so that we can test cursor movement\nshortone\nk", - lineWrapping: true}); - -testCM("rtlMovement", function(cm) { - forEach(["خحج", "خحabcخحج", "abخحخحجcd", "abخde", "abخح2342خ1حج", "خ1ح2خح3حxج", - "خحcd", "1خحcd", "abcdeح1ج", "خمرحبها مها!", "foobarر", "خ ة ق", - ""], function(line) { - var inv = line.charAt(0) == "خ"; - cm.setValue(line + "\n"); cm.execCommand(inv ? "goLineEnd" : "goLineStart"); - var cursors = byClassName(cm.getWrapperElement(), "CodeMirror-cursors")[0]; - var cursor = cursors.firstChild; - var prevX = cursor.offsetLeft, prevY = cursor.offsetTop; - for (var i = 0; i <= line.length; ++i) { - cm.execCommand("goCharRight"); - cursor = cursors.firstChild; - if (i == line.length) is(cursor.offsetTop > prevY, "next line"); - else is(cursor.offsetLeft > prevX, "moved right"); - prevX = cursor.offsetLeft; prevY = cursor.offsetTop; - } - cm.setCursor(0, 0); cm.execCommand(inv ? "goLineStart" : "goLineEnd"); - prevX = cursors.firstChild.offsetLeft; - for (var i = 0; i < line.length; ++i) { - cm.execCommand("goCharLeft"); - cursor = cursors.firstChild; - is(cursor.offsetLeft < prevX, "moved left"); - prevX = cursor.offsetLeft; - } - }); -}, null, ie_lt9); - -// Verify that updating a line clears its bidi ordering -testCM("bidiUpdate", function(cm) { - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("خحج", "start"); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 4)); -}, {value: "abcd\n"}); - -testCM("movebyTextUnit", function(cm) { - cm.setValue("בְּרֵאשִ\nééé́\n"); - cm.execCommand("goLineEnd"); - for (var i = 0; i < 4; ++i) cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 0)); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 0)); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 4)); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(1, 7)); -}); - -testCM("lineChangeEvents", function(cm) { - addDoc(cm, 3, 5); - var log = [], want = ["ch 0", "ch 1", "del 2", "ch 0", "ch 0", "del 1", "del 3", "del 4"]; - for (var i = 0; i < 5; ++i) { - CodeMirror.on(cm.getLineHandle(i), "delete", function(i) { - return function() {log.push("del " + i);}; - }(i)); - CodeMirror.on(cm.getLineHandle(i), "change", function(i) { - return function() {log.push("ch " + i);}; - }(i)); - } - cm.replaceRange("x", Pos(0, 1)); - cm.replaceRange("xy", Pos(1, 1), Pos(2)); - cm.replaceRange("foo\nbar", Pos(0, 1)); - cm.replaceRange("", Pos(0, 0), Pos(cm.lineCount())); - eq(log.length, want.length, "same length"); - for (var i = 0; i < log.length; ++i) - eq(log[i], want[i]); -}); - -testCM("scrollEntirelyToRight", function(cm) { - if (phantom) return; - addDoc(cm, 500, 2); - cm.setCursor(Pos(0, 500)); - var wrap = cm.getWrapperElement(), cur = byClassName(wrap, "CodeMirror-cursor")[0]; - is(wrap.getBoundingClientRect().right > cur.getBoundingClientRect().left); -}); - -testCM("lineWidgets", function(cm) { - addDoc(cm, 500, 3); - var last = cm.charCoords(Pos(2, 0)); - var node = document.createElement("div"); - node.innerHTML = "hi"; - var widget = cm.addLineWidget(1, node); - is(last.top < cm.charCoords(Pos(2, 0)).top, "took up space"); - cm.setCursor(Pos(1, 1)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); -}); - -testCM("lineWidgetFocus", function(cm) { - var place = document.getElementById("testground"); - place.className = "offscreen"; - try { - addDoc(cm, 500, 10); - var node = document.createElement("input"); - var widget = cm.addLineWidget(1, node); - node.focus(); - eq(document.activeElement, node); - cm.replaceRange("new stuff", Pos(1, 0)); - eq(document.activeElement, node); - } finally { - place.className = ""; - } -}); - -testCM("lineWidgetCautiousRedraw", function(cm) { - var node = document.createElement("div"); - node.innerHTML = "hahah"; - var w = cm.addLineWidget(0, node); - var redrawn = false; - w.on("redraw", function() { redrawn = true; }); - cm.replaceSelection("0"); - is(!redrawn); -}, {value: "123\n456"}); - - -var knownScrollbarWidth; -function scrollbarWidth(measure) { - if (knownScrollbarWidth != null) return knownScrollbarWidth; - var div = document.createElement('div'); - div.style.cssText = "width: 50px; height: 50px; overflow-x: scroll"; - document.body.appendChild(div); - knownScrollbarWidth = div.offsetHeight - div.clientHeight; - document.body.removeChild(div); - return knownScrollbarWidth || 0; -} - -testCM("lineWidgetChanged", function(cm) { - addDoc(cm, 2, 300); - var halfScrollbarWidth = scrollbarWidth(cm.display.measure)/2; - cm.setOption('lineNumbers', true); - cm.setSize(600, cm.defaultTextHeight() * 50); - cm.scrollTo(null, cm.heightAtLine(125, "local")); - - var expectedWidgetHeight = 60; - var expectedLinesInWidget = 3; - function w() { - var node = document.createElement("div"); - // we use these children with just under half width of the line to check measurements are made with correct width - // when placed in the measure div. - // If the widget is measured at a width much narrower than it is displayed at, the underHalf children will span two lines and break the test. - // If the widget is measured at a width much wider than it is displayed at, the overHalf children will combine and break the test. - // Note that this test only checks widgets where coverGutter is true, because these require extra styling to get the width right. - // It may also be worthwhile to check this for non-coverGutter widgets. - // Visually: - // Good: - // | ------------- display width ------------- | - // | ------- widget-width when measured ------ | - // | | -- under-half -- | | -- under-half -- | | - // | | --- over-half --- | | - // | | --- over-half --- | | - // Height: measured as 3 lines, same as it will be when actually displayed - - // Bad (too narrow): - // | ------------- display width ------------- | - // | ------ widget-width when measured ----- | < -- uh oh - // | | -- under-half -- | | - // | | -- under-half -- | | < -- when measured, shoved to next line - // | | --- over-half --- | | - // | | --- over-half --- | | - // Height: measured as 4 lines, more than expected . Will be displayed as 3 lines! - - // Bad (too wide): - // | ------------- display width ------------- | - // | -------- widget-width when measured ------- | < -- uh oh - // | | -- under-half -- | | -- under-half -- | | - // | | --- over-half --- | | --- over-half --- | | < -- when measured, combined on one line - // Height: measured as 2 lines, less than expected. Will be displayed as 3 lines! - - var barelyUnderHalfWidthHtml = '
'; - var barelyOverHalfWidthHtml = '
'; - node.innerHTML = new Array(3).join(barelyUnderHalfWidthHtml) + new Array(3).join(barelyOverHalfWidthHtml); - node.style.cssText = "background: yellow;font-size:0;line-height: " + (expectedWidgetHeight/expectedLinesInWidget) + "px;"; - return node; - } - var info0 = cm.getScrollInfo(); - var w0 = cm.addLineWidget(0, w(), { coverGutter: true }); - var w150 = cm.addLineWidget(150, w(), { coverGutter: true }); - var w300 = cm.addLineWidget(300, w(), { coverGutter: true }); - var info1 = cm.getScrollInfo(); - eq(info0.height + (3 * expectedWidgetHeight), info1.height); - eq(info0.top + expectedWidgetHeight, info1.top); - expectedWidgetHeight = 12; - w0.node.style.lineHeight = w150.node.style.lineHeight = w300.node.style.lineHeight = (expectedWidgetHeight/expectedLinesInWidget) + "px"; - w0.changed(); w150.changed(); w300.changed(); - var info2 = cm.getScrollInfo(); - eq(info0.height + (3 * expectedWidgetHeight), info2.height); - eq(info0.top + expectedWidgetHeight, info2.top); -}); - -testCM("getLineNumber", function(cm) { - addDoc(cm, 2, 20); - var h1 = cm.getLineHandle(1); - eq(cm.getLineNumber(h1), 1); - cm.replaceRange("hi\nbye\n", Pos(0, 0)); - eq(cm.getLineNumber(h1), 3); - cm.setValue(""); - eq(cm.getLineNumber(h1), null); -}); - -testCM("jumpTheGap", function(cm) { - if (phantom) return; - var longLine = "abcdef ghiklmnop qrstuvw xyz "; - longLine += longLine; longLine += longLine; longLine += longLine; - cm.replaceRange(longLine, Pos(2, 0), Pos(2)); - cm.setSize("200px", null); - cm.getWrapperElement().style.lineHeight = 2; - cm.refresh(); - cm.setCursor(Pos(0, 1)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineDown"); - eq(cm.getCursor().line, 2); - is(cm.getCursor().ch > 1); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(2, 1)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); - var node = document.createElement("div"); - node.innerHTML = "hi"; node.style.height = "30px"; - cm.addLineWidget(0, node); - cm.addLineWidget(1, node.cloneNode(true), {above: true}); - cm.setCursor(Pos(0, 2)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(1, 2)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(0, 2)); -}, {lineWrapping: true, value: "abc\ndef\nghi\njkl\n"}); - -testCM("addLineClass", function(cm) { - function cls(line, text, bg, wrap, gutter) { - var i = cm.lineInfo(line); - eq(i.textClass, text); - eq(i.bgClass, bg); - eq(i.wrapClass, wrap); - if (typeof i.handle.gutterClass !== 'undefined') { - eq(i.handle.gutterClass, gutter); - } - } - cm.addLineClass(0, "text", "foo"); - cm.addLineClass(0, "text", "bar"); - cm.addLineClass(1, "background", "baz"); - cm.addLineClass(1, "wrap", "foo"); - cm.addLineClass(1, "gutter", "gutter-class"); - cls(0, "foo bar", null, null, null); - cls(1, null, "baz", "foo", "gutter-class"); - var lines = cm.display.lineDiv; - eq(byClassName(lines, "foo").length, 2); - eq(byClassName(lines, "bar").length, 1); - eq(byClassName(lines, "baz").length, 1); - eq(byClassName(lines, "gutter-class").length, 1); - cm.removeLineClass(0, "text", "foo"); - cls(0, "bar", null, null, null); - cm.removeLineClass(0, "text", "foo"); - cls(0, "bar", null, null, null); - cm.removeLineClass(0, "text", "bar"); - cls(0, null, null, null); - - cm.addLineClass(1, "wrap", "quux"); - cls(1, null, "baz", "foo quux", "gutter-class"); - cm.removeLineClass(1, "wrap"); - cls(1, null, "baz", null, "gutter-class"); - cm.removeLineClass(1, "gutter", "gutter-class"); - eq(byClassName(lines, "gutter-class").length, 0); - cls(1, null, "baz", null, null); - - cm.addLineClass(1, "gutter", "gutter-class"); - cls(1, null, "baz", null, "gutter-class"); - cm.removeLineClass(1, "gutter", "gutter-class"); - cls(1, null, "baz", null, null); - -}, {value: "hohoho\n", lineNumbers: true}); - -testCM("atomicMarker", function(cm) { - addDoc(cm, 10, 10); - function atom(ll, cl, lr, cr, li, ri) { - return cm.markText(Pos(ll, cl), Pos(lr, cr), - {atomic: true, inclusiveLeft: li, inclusiveRight: ri}); - } - var m = atom(0, 1, 0, 5); - cm.setCursor(Pos(0, 1)); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(0, 5)); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 1)); - m.clear(); - m = atom(0, 0, 0, 5, true); - eqPos(cm.getCursor(), Pos(0, 5), "pushed out"); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 5)); - m.clear(); - m = atom(8, 4, 9, 10, false, true); - cm.setCursor(Pos(9, 8)); - eqPos(cm.getCursor(), Pos(8, 4), "set"); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(8, 4), "char right"); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(8, 4), "line down"); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(8, 3)); - m.clear(); - m = atom(1, 1, 3, 8); - cm.setCursor(Pos(0, 0)); - cm.setCursor(Pos(2, 0)); - eqPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goCharRight"); - eqPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("goLineUp"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineDown"); - eqPos(cm.getCursor(), Pos(3, 8)); - cm.execCommand("delCharBefore"); - eq(cm.getValue().length, 80, "del chunk"); - m = atom(3, 0, 5, 5); - cm.setCursor(Pos(3, 0)); - cm.execCommand("delWordAfter"); - eq(cm.getValue().length, 53, "del chunk"); -}); - -testCM("selectionBias", function(cm) { - cm.markText(Pos(0, 1), Pos(0, 3), {atomic: true}); - cm.setCursor(Pos(0, 2)); - eqPos(cm.getCursor(), Pos(0, 3)); - cm.setCursor(Pos(0, 2)); - eqPos(cm.getCursor(), Pos(0, 1)); - cm.setCursor(Pos(0, 2), null, {bias: -1}); - eqPos(cm.getCursor(), Pos(0, 1)); - cm.setCursor(Pos(0, 4)); - cm.setCursor(Pos(0, 2), null, {bias: 1}); - eqPos(cm.getCursor(), Pos(0, 3)); -}, {value: "12345"}); - -testCM("selectionHomeEnd", function(cm) { - cm.markText(Pos(1, 0), Pos(1, 1), {atomic: true, inclusiveLeft: true}); - cm.markText(Pos(1, 3), Pos(1, 4), {atomic: true, inclusiveRight: true}); - cm.setCursor(Pos(1, 2)); - cm.execCommand("goLineStart"); - eqPos(cm.getCursor(), Pos(1, 1)); - cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(1, 3)); -}, {value: "ab\ncdef\ngh"}); - -testCM("readOnlyMarker", function(cm) { - function mark(ll, cl, lr, cr, at) { - return cm.markText(Pos(ll, cl), Pos(lr, cr), - {readOnly: true, atomic: at}); - } - var m = mark(0, 1, 0, 4); - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi", "end"); - eqPos(cm.getCursor(), Pos(0, 2)); - eq(cm.getLine(0), "abcde"); - cm.execCommand("selectAll"); - cm.replaceSelection("oops", "around"); - eq(cm.getValue(), "oopsbcd"); - cm.undo(); - eqPos(m.find().from, Pos(0, 1)); - eqPos(m.find().to, Pos(0, 4)); - m.clear(); - cm.setCursor(Pos(0, 2)); - cm.replaceSelection("hi", "around"); - eq(cm.getLine(0), "abhicde"); - eqPos(cm.getCursor(), Pos(0, 4)); - m = mark(0, 2, 2, 2, true); - cm.setSelection(Pos(1, 1), Pos(2, 4)); - cm.replaceSelection("t", "end"); - eqPos(cm.getCursor(), Pos(2, 3)); - eq(cm.getLine(2), "klto"); - cm.execCommand("goCharLeft"); - cm.execCommand("goCharLeft"); - eqPos(cm.getCursor(), Pos(0, 2)); - cm.setSelection(Pos(0, 1), Pos(0, 3)); - cm.replaceSelection("xx", "around"); - eqPos(cm.getCursor(), Pos(0, 3)); - eq(cm.getLine(0), "axxhicde"); -}, {value: "abcde\nfghij\nklmno\n"}); - -testCM("dirtyBit", function(cm) { - eq(cm.isClean(), true); - cm.replaceSelection("boo", null, "test"); - eq(cm.isClean(), false); - cm.undo(); - eq(cm.isClean(), true); - cm.replaceSelection("boo", null, "test"); - cm.replaceSelection("baz", null, "test"); - cm.undo(); - eq(cm.isClean(), false); - cm.markClean(); - eq(cm.isClean(), true); - cm.undo(); - eq(cm.isClean(), false); - cm.redo(); - eq(cm.isClean(), true); -}); - -testCM("changeGeneration", function(cm) { - cm.replaceSelection("x"); - var softGen = cm.changeGeneration(); - cm.replaceSelection("x"); - cm.undo(); - eq(cm.getValue(), ""); - is(!cm.isClean(softGen)); - cm.replaceSelection("x"); - var hardGen = cm.changeGeneration(true); - cm.replaceSelection("x"); - cm.undo(); - eq(cm.getValue(), "x"); - is(cm.isClean(hardGen)); -}); - -testCM("addKeyMap", function(cm) { - function sendKey(code) { - cm.triggerOnKeyDown({type: "keydown", keyCode: code, - preventDefault: function(){}, stopPropagation: function(){}}); - } - - sendKey(39); - eqPos(cm.getCursor(), Pos(0, 1)); - var test = 0; - var map1 = {Right: function() { ++test; }}, map2 = {Right: function() { test += 10; }} - cm.addKeyMap(map1); - sendKey(39); - eqPos(cm.getCursor(), Pos(0, 1)); - eq(test, 1); - cm.addKeyMap(map2, true); - sendKey(39); - eq(test, 2); - cm.removeKeyMap(map1); - sendKey(39); - eq(test, 12); - cm.removeKeyMap(map2); - sendKey(39); - eq(test, 12); - eqPos(cm.getCursor(), Pos(0, 2)); - cm.addKeyMap({Right: function() { test = 55; }, name: "mymap"}); - sendKey(39); - eq(test, 55); - cm.removeKeyMap("mymap"); - sendKey(39); - eqPos(cm.getCursor(), Pos(0, 3)); -}, {value: "abc"}); - -testCM("findPosH", function(cm) { - forEach([{from: Pos(0, 0), to: Pos(0, 1), by: 1}, - {from: Pos(0, 0), to: Pos(0, 0), by: -1, hitSide: true}, - {from: Pos(0, 0), to: Pos(0, 4), by: 1, unit: "word"}, - {from: Pos(0, 0), to: Pos(0, 8), by: 2, unit: "word"}, - {from: Pos(0, 0), to: Pos(2, 0), by: 20, unit: "word", hitSide: true}, - {from: Pos(0, 7), to: Pos(0, 5), by: -1, unit: "word"}, - {from: Pos(0, 4), to: Pos(0, 8), by: 1, unit: "word"}, - {from: Pos(1, 0), to: Pos(1, 18), by: 3, unit: "word"}, - {from: Pos(1, 22), to: Pos(1, 5), by: -3, unit: "word"}, - {from: Pos(1, 15), to: Pos(1, 10), by: -5}, - {from: Pos(1, 15), to: Pos(1, 10), by: -5, unit: "column"}, - {from: Pos(1, 15), to: Pos(1, 0), by: -50, unit: "column", hitSide: true}, - {from: Pos(1, 15), to: Pos(1, 24), by: 50, unit: "column", hitSide: true}, - {from: Pos(1, 15), to: Pos(2, 0), by: 50, hitSide: true}], function(t) { - var r = cm.findPosH(t.from, t.by, t.unit || "char"); - eqPos(r, t.to); - eq(!!r.hitSide, !!t.hitSide); - }); -}, {value: "line one\nline two.something.other\n"}); - -testCM("beforeChange", function(cm) { - cm.on("beforeChange", function(cm, change) { - var text = []; - for (var i = 0; i < change.text.length; ++i) - text.push(change.text[i].replace(/\s/g, "_")); - change.update(null, null, text); - }); - cm.setValue("hello, i am a\nnew document\n"); - eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); - CodeMirror.on(cm.getDoc(), "beforeChange", function(doc, change) { - if (change.from.line == 0) change.cancel(); - }); - cm.setValue("oops"); // Canceled - eq(cm.getValue(), "hello,_i_am_a\nnew_document\n"); - cm.replaceRange("hey hey hey", Pos(1, 0), Pos(2, 0)); - eq(cm.getValue(), "hello,_i_am_a\nhey_hey_hey"); -}, {value: "abcdefghijk"}); - -testCM("beforeChangeUndo", function(cm) { - cm.replaceRange("hi", Pos(0, 0), Pos(0)); - cm.replaceRange("bye", Pos(0, 0), Pos(0)); - eq(cm.historySize().undo, 2); - cm.on("beforeChange", function(cm, change) { - is(!change.update); - change.cancel(); - }); - cm.undo(); - eq(cm.historySize().undo, 0); - eq(cm.getValue(), "bye\ntwo"); -}, {value: "one\ntwo"}); - -testCM("beforeSelectionChange", function(cm) { - function notAtEnd(cm, pos) { - var len = cm.getLine(pos.line).length; - if (!len || pos.ch == len) return Pos(pos.line, pos.ch - 1); - return pos; - } - cm.on("beforeSelectionChange", function(cm, obj) { - obj.update([{anchor: notAtEnd(cm, obj.ranges[0].anchor), - head: notAtEnd(cm, obj.ranges[0].head)}]); - }); - - addDoc(cm, 10, 10); - cm.execCommand("goLineEnd"); - eqPos(cm.getCursor(), Pos(0, 9)); - cm.execCommand("selectAll"); - eqPos(cm.getCursor("start"), Pos(0, 0)); - eqPos(cm.getCursor("end"), Pos(9, 9)); -}); - -testCM("change_removedText", function(cm) { - cm.setValue("abc\ndef"); - - var removedText = []; - cm.on("change", function(cm, change) { - removedText.push(change.removed); - }); - - cm.operation(function() { - cm.replaceRange("xyz", Pos(0, 0), Pos(1,1)); - cm.replaceRange("123", Pos(0,0)); - }); - - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "abc\nd"); - eq(removedText[1].join("\n"), ""); - - var removedText = []; - cm.undo(); - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "123"); - eq(removedText[1].join("\n"), "xyz"); - - var removedText = []; - cm.redo(); - eq(removedText.length, 2); - eq(removedText[0].join("\n"), "abc\nd"); - eq(removedText[1].join("\n"), ""); -}); - -testCM("lineStyleFromMode", function(cm) { - CodeMirror.defineMode("test_mode", function() { - return {token: function(stream) { - if (stream.match(/^\[[^\]]*\]/)) return " line-brackets "; - if (stream.match(/^\([^\)]*\)/)) return " line-background-parens "; - if (stream.match(/^<[^>]*>/)) return " span line-line line-background-bg "; - stream.match(/^\s+|^\S+/); - }}; - }); - cm.setOption("mode", "test_mode"); - var bracketElts = byClassName(cm.getWrapperElement(), "brackets"); - eq(bracketElts.length, 1, "brackets count"); - eq(bracketElts[0].nodeName, "PRE"); - is(!/brackets.*brackets/.test(bracketElts[0].className)); - var parenElts = byClassName(cm.getWrapperElement(), "parens"); - eq(parenElts.length, 1, "parens count"); - eq(parenElts[0].nodeName, "DIV"); - is(!/parens.*parens/.test(parenElts[0].className)); - eq(parenElts[0].parentElement.nodeName, "DIV"); - - eq(byClassName(cm.getWrapperElement(), "bg").length, 1); - eq(byClassName(cm.getWrapperElement(), "line").length, 1); - var spanElts = byClassName(cm.getWrapperElement(), "cm-span"); - eq(spanElts.length, 2); - is(/^\s*cm-span\s*$/.test(spanElts[0].className)); -}, {value: "line1: [br] [br]\nline2: (par) (par)\nline3: "}); - -testCM("lineStyleFromBlankLine", function(cm) { - CodeMirror.defineMode("lineStyleFromBlankLine_mode", function() { - return {token: function(stream) { stream.skipToEnd(); return "comment"; }, - blankLine: function() { return "line-blank"; }}; - }); - cm.setOption("mode", "lineStyleFromBlankLine_mode"); - var blankElts = byClassName(cm.getWrapperElement(), "blank"); - eq(blankElts.length, 1); - eq(blankElts[0].nodeName, "PRE"); - cm.replaceRange("x", Pos(1, 0)); - blankElts = byClassName(cm.getWrapperElement(), "blank"); - eq(blankElts.length, 0); -}, {value: "foo\n\nbar"}); - -CodeMirror.registerHelper("xxx", "a", "A"); -CodeMirror.registerHelper("xxx", "b", "B"); -CodeMirror.defineMode("yyy", function() { - return { - token: function(stream) { stream.skipToEnd(); }, - xxx: ["a", "b", "q"] - }; -}); -CodeMirror.registerGlobalHelper("xxx", "c", function(m) { return m.enableC; }, "C"); - -testCM("helpers", function(cm) { - cm.setOption("mode", "yyy"); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "A/B"); - cm.setOption("mode", {name: "yyy", modeProps: {xxx: "b", enableC: true}}); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), "B/C"); - cm.setOption("mode", "javascript"); - eq(cm.getHelpers(Pos(0, 0), "xxx").join("/"), ""); -}); - -testCM("selectionHistory", function(cm) { - for (var i = 0; i < 3; i++) { - cm.setExtending(true); - cm.execCommand("goCharRight"); - cm.setExtending(false); - cm.execCommand("goCharRight"); - cm.execCommand("goCharRight"); - } - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "c"); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 4)); - cm.execCommand("undoSelection"); - eq(cm.getSelection(), "b"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 4)); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), "c"); - cm.execCommand("redoSelection"); - eq(cm.getSelection(), ""); - eqPos(cm.getCursor(), Pos(0, 6)); -}, {value: "a b c d"}); - -testCM("selectionChangeReducesRedo", function(cm) { - cm.replaceSelection("X"); - cm.execCommand("goCharRight"); - cm.undoSelection(); - cm.execCommand("selectAll"); - cm.undoSelection(); - eq(cm.getValue(), "Xabc"); - eqPos(cm.getCursor(), Pos(0, 1)); - cm.undoSelection(); - eq(cm.getValue(), "abc"); -}, {value: "abc"}); - -testCM("selectionHistoryNonOverlapping", function(cm) { - cm.setSelection(Pos(0, 0), Pos(0, 1)); - cm.setSelection(Pos(0, 2), Pos(0, 3)); - cm.execCommand("undoSelection"); - eqPos(cm.getCursor("anchor"), Pos(0, 0)); - eqPos(cm.getCursor("head"), Pos(0, 1)); -}, {value: "1234"}); - -testCM("cursorMotionSplitsHistory", function(cm) { - cm.replaceSelection("a"); - cm.execCommand("goCharRight"); - cm.replaceSelection("b"); - cm.replaceSelection("c"); - cm.undo(); - eq(cm.getValue(), "a1234"); - eqPos(cm.getCursor(), Pos(0, 2)); - cm.undo(); - eq(cm.getValue(), "1234"); - eqPos(cm.getCursor(), Pos(0, 0)); -}, {value: "1234"}); - -testCM("selChangeInOperationDoesNotSplit", function(cm) { - for (var i = 0; i < 4; i++) { - cm.operation(function() { - cm.replaceSelection("x"); - cm.setCursor(Pos(0, cm.getCursor().ch - 1)); - }); - } - eqPos(cm.getCursor(), Pos(0, 0)); - eq(cm.getValue(), "xxxxa"); - cm.undo(); - eq(cm.getValue(), "a"); -}, {value: "a"}); - -testCM("alwaysMergeSelEventWithChangeOrigin", function(cm) { - cm.replaceSelection("U", null, "foo"); - cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "foo"}); - cm.undoSelection(); - eq(cm.getValue(), "a"); - cm.replaceSelection("V", null, "foo"); - cm.setSelection(Pos(0, 0), Pos(0, 1), {origin: "bar"}); - cm.undoSelection(); - eq(cm.getValue(), "Va"); -}, {value: "a"}); - -testCM("getTokenAt", function(cm) { - var tokPlus = cm.getTokenAt(Pos(0, 2)); - eq(tokPlus.type, "operator"); - eq(tokPlus.string, "+"); - var toks = cm.getLineTokens(0); - eq(toks.length, 3); - forEach([["number", "1"], ["operator", "+"], ["number", "2"]], function(expect, i) { - eq(toks[i].type, expect[0]); - eq(toks[i].string, expect[1]); - }); -}, {value: "1+2", mode: "javascript"}); - -testCM("getTokenTypeAt", function(cm) { - eq(cm.getTokenTypeAt(Pos(0, 0)), "number"); - eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); - cm.addOverlay({ - token: function(stream) { - if (stream.match("foo")) return "foo"; - else stream.next(); - } - }); - eq(byClassName(cm.getWrapperElement(), "cm-foo").length, 1); - eq(cm.getTokenTypeAt(Pos(0, 6)), "string"); -}, {value: "1 + 'foo'", mode: "javascript"}); - -testCM("resizeLineWidget", function(cm) { - addDoc(cm, 200, 3); - var widget = document.createElement("pre"); - widget.innerHTML = "imwidget"; - widget.style.background = "yellow"; - cm.addLineWidget(1, widget, {noHScroll: true}); - cm.setSize(40); - is(widget.parentNode.offsetWidth < 42); -}); - -testCM("combinedOperations", function(cm) { - var place = document.getElementById("testground"); - var other = CodeMirror(place, {value: "123"}); - try { - cm.operation(function() { - cm.addLineClass(0, "wrap", "foo"); - other.addLineClass(0, "wrap", "foo"); - }); - eq(byClassName(cm.getWrapperElement(), "foo").length, 1); - eq(byClassName(other.getWrapperElement(), "foo").length, 1); - cm.operation(function() { - cm.removeLineClass(0, "wrap", "foo"); - other.removeLineClass(0, "wrap", "foo"); - }); - eq(byClassName(cm.getWrapperElement(), "foo").length, 0); - eq(byClassName(other.getWrapperElement(), "foo").length, 0); - } finally { - place.removeChild(other.getWrapperElement()); - } -}, {value: "abc"}); - -testCM("eventOrder", function(cm) { - var seen = []; - cm.on("change", function() { - if (!seen.length) cm.replaceSelection("."); - seen.push("change"); - }); - cm.on("cursorActivity", function() { - cm.replaceSelection("!"); - seen.push("activity"); - }); - cm.replaceSelection("/"); - eq(seen.join(","), "change,change,activity,change"); -}); - -test("core_rmClass", function() { - var node = document.createElement("div"); - node.className = "foo-bar baz-quux yadda"; - CodeMirror.rmClass(node, "quux"); - eq(node.className, "foo-bar baz-quux yadda"); - CodeMirror.rmClass(node, "baz-quux"); - eq(node.className, "foo-bar yadda"); - CodeMirror.rmClass(node, "yadda"); - eq(node.className, "foo-bar"); - CodeMirror.rmClass(node, "foo-bar"); - eq(node.className, ""); - node.className = " foo "; - CodeMirror.rmClass(node, "foo"); - eq(node.className, ""); -}); - -test("core_addClass", function() { - var node = document.createElement("div"); - CodeMirror.addClass(node, "a"); - eq(node.className, "a"); - CodeMirror.addClass(node, "a"); - eq(node.className, "a"); - CodeMirror.addClass(node, "b"); - eq(node.className, "a b"); - CodeMirror.addClass(node, "a"); - CodeMirror.addClass(node, "b"); - eq(node.className, "a b"); -}); diff --git a/public/js/lib/codemirror/test/vim_test.js b/public/js/lib/codemirror/test/vim_test.js deleted file mode 100644 index 6262dab0c1..0000000000 --- a/public/js/lib/codemirror/test/vim_test.js +++ /dev/null @@ -1,3768 +0,0 @@ -CodeMirror.Vim.suppressErrorLogging = true; - -var code = '' + -' wOrd1 (#%\n' + -' word3] \n' + -'aopop pop 0 1 2 3 4\n' + -' (a) [b] {c} \n' + -'int getchar(void) {\n' + -' static char buf[BUFSIZ];\n' + -' static char *bufp = buf;\n' + -' if (n == 0) { /* buffer is empty */\n' + -' n = read(0, buf, sizeof buf);\n' + -' bufp = buf;\n' + -' }\n' + -'\n' + -' return (--n >= 0) ? (unsigned char) *bufp++ : EOF;\n' + -' \n' + -'}\n'; - -var lines = (function() { - lineText = code.split('\n'); - var ret = []; - for (var i = 0; i < lineText.length; i++) { - ret[i] = { - line: i, - length: lineText[i].length, - lineText: lineText[i], - textStart: /^\s*/.exec(lineText[i])[0].length - }; - } - return ret; -})(); -var endOfDocument = makeCursor(lines.length - 1, - lines[lines.length - 1].length); -var wordLine = lines[0]; -var bigWordLine = lines[1]; -var charLine = lines[2]; -var bracesLine = lines[3]; -var seekBraceLine = lines[4]; - -var word1 = { - start: { line: wordLine.line, ch: 1 }, - end: { line: wordLine.line, ch: 5 } -}; -var word2 = { - start: { line: wordLine.line, ch: word1.end.ch + 2 }, - end: { line: wordLine.line, ch: word1.end.ch + 4 } -}; -var word3 = { - start: { line: bigWordLine.line, ch: 1 }, - end: { line: bigWordLine.line, ch: 5 } -}; -var bigWord1 = word1; -var bigWord2 = word2; -var bigWord3 = { - start: { line: bigWordLine.line, ch: 1 }, - end: { line: bigWordLine.line, ch: 7 } -}; -var bigWord4 = { - start: { line: bigWordLine.line, ch: bigWord1.end.ch + 3 }, - end: { line: bigWordLine.line, ch: bigWord1.end.ch + 7 } -}; - -var oChars = [ { line: charLine.line, ch: 1 }, - { line: charLine.line, ch: 3 }, - { line: charLine.line, ch: 7 } ]; -var pChars = [ { line: charLine.line, ch: 2 }, - { line: charLine.line, ch: 4 }, - { line: charLine.line, ch: 6 }, - { line: charLine.line, ch: 8 } ]; -var numChars = [ { line: charLine.line, ch: 10 }, - { line: charLine.line, ch: 12 }, - { line: charLine.line, ch: 14 }, - { line: charLine.line, ch: 16 }, - { line: charLine.line, ch: 18 }]; -var parens1 = { - start: { line: bracesLine.line, ch: 1 }, - end: { line: bracesLine.line, ch: 3 } -}; -var squares1 = { - start: { line: bracesLine.line, ch: 5 }, - end: { line: bracesLine.line, ch: 7 } -}; -var curlys1 = { - start: { line: bracesLine.line, ch: 9 }, - end: { line: bracesLine.line, ch: 11 } -}; -var seekOutside = { - start: { line: seekBraceLine.line, ch: 1 }, - end: { line: seekBraceLine.line, ch: 16 } -}; -var seekInside = { - start: { line: seekBraceLine.line, ch: 14 }, - end: { line: seekBraceLine.line, ch: 11 } -}; - -function copyCursor(cur) { - return { ch: cur.ch, line: cur.line }; -} - -function forEach(arr, func) { - for (var i = 0; i < arr.length; i++) { - func(arr[i], i, arr); - } -} - -function testVim(name, run, opts, expectedFail) { - var vimOpts = { - lineNumbers: true, - vimMode: true, - showCursorWhenSelecting: true, - value: code - }; - for (var prop in opts) { - if (opts.hasOwnProperty(prop)) { - vimOpts[prop] = opts[prop]; - } - } - return test('vim_' + name, function() { - var place = document.getElementById("testground"); - var cm = CodeMirror(place, vimOpts); - var vim = CodeMirror.Vim.maybeInitVimState_(cm); - - function doKeysFn(cm) { - return function(args) { - if (args instanceof Array) { - arguments = args; - } - for (var i = 0; i < arguments.length; i++) { - CodeMirror.Vim.handleKey(cm, arguments[i]); - } - } - } - function doInsertModeKeysFn(cm) { - return function(args) { - if (args instanceof Array) { arguments = args; } - function executeHandler(handler) { - if (typeof handler == 'string') { - CodeMirror.commands[handler](cm); - } else { - handler(cm); - } - return true; - } - for (var i = 0; i < arguments.length; i++) { - var key = arguments[i]; - // Find key in keymap and handle. - var handled = CodeMirror.lookupKey(key, 'vim-insert', executeHandler); - // Record for insert mode. - if (handled == "handled" && cm.state.vim.insertMode && arguments[i] != 'Esc') { - var lastChange = CodeMirror.Vim.getVimGlobalState_().macroModeState.lastInsertModeChanges; - if (lastChange) { - lastChange.changes.push(new CodeMirror.Vim.InsertModeKey(key)); - } - } - } - } - } - function doExFn(cm) { - return function(command) { - cm.openDialog = helpers.fakeOpenDialog(command); - helpers.doKeys(':'); - } - } - function assertCursorAtFn(cm) { - return function(line, ch) { - var pos; - if (ch == null && typeof line.line == 'number') { - pos = line; - } else { - pos = makeCursor(line, ch); - } - eqPos(pos, cm.getCursor()); - } - } - function fakeOpenDialog(result) { - return function(text, callback) { - return callback(result); - } - } - function fakeOpenNotification(matcher) { - return function(text) { - matcher(text); - } - } - var helpers = { - doKeys: doKeysFn(cm), - // Warning: Only emulates keymap events, not character insertions. Use - // replaceRange to simulate character insertions. - // Keys are in CodeMirror format, NOT vim format. - doInsertModeKeys: doInsertModeKeysFn(cm), - doEx: doExFn(cm), - assertCursorAt: assertCursorAtFn(cm), - fakeOpenDialog: fakeOpenDialog, - fakeOpenNotification: fakeOpenNotification, - getRegisterController: function() { - return CodeMirror.Vim.getRegisterController(); - } - } - CodeMirror.Vim.resetVimGlobalState_(); - var successful = false; - var savedOpenNotification = cm.openNotification; - try { - run(cm, vim, helpers); - successful = true; - } finally { - cm.openNotification = savedOpenNotification; - if (!successful || verbose) { - place.style.visibility = "visible"; - } else { - place.removeChild(cm.getWrapperElement()); - } - } - }, expectedFail); -}; -testVim('qq@q', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'q', 'l', 'l', 'q'); - helpers.assertCursorAt(0,2); - helpers.doKeys('@', 'q'); - helpers.assertCursorAt(0,4); -}, { value: ' '}); -testVim('@@', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'q', 'l', 'l', 'q'); - helpers.assertCursorAt(0,2); - helpers.doKeys('@', 'q'); - helpers.assertCursorAt(0,4); - helpers.doKeys('@', '@'); - helpers.assertCursorAt(0,6); -}, { value: ' '}); -var jumplistScene = ''+ - 'word\n'+ - '(word)\n'+ - '{word\n'+ - 'word.\n'+ - '\n'+ - 'word search\n'+ - '}word\n'+ - 'word\n'+ - 'word\n'; -function testJumplist(name, keys, endPos, startPos, dialog) { - endPos = makeCursor(endPos[0], endPos[1]); - startPos = makeCursor(startPos[0], startPos[1]); - testVim(name, function(cm, vim, helpers) { - CodeMirror.Vim.resetVimGlobalState_(); - if(dialog)cm.openDialog = helpers.fakeOpenDialog('word'); - cm.setCursor(startPos); - helpers.doKeys.apply(null, keys); - helpers.assertCursorAt(endPos); - }, {value: jumplistScene}); -}; -testJumplist('jumplist_H', ['H', ''], [5,2], [5,2]); -testJumplist('jumplist_M', ['M', ''], [2,2], [2,2]); -testJumplist('jumplist_L', ['L', ''], [2,2], [2,2]); -testJumplist('jumplist_[[', ['[', '[', ''], [5,2], [5,2]); -testJumplist('jumplist_]]', [']', ']', ''], [2,2], [2,2]); -testJumplist('jumplist_G', ['G', ''], [5,2], [5,2]); -testJumplist('jumplist_gg', ['g', 'g', ''], [5,2], [5,2]); -testJumplist('jumplist_%', ['%', ''], [1,5], [1,5]); -testJumplist('jumplist_{', ['{', ''], [1,5], [1,5]); -testJumplist('jumplist_}', ['}', ''], [1,5], [1,5]); -testJumplist('jumplist_\'', ['m', 'a', 'h', '\'', 'a', 'h', ''], [1,0], [1,5]); -testJumplist('jumplist_`', ['m', 'a', 'h', '`', 'a', 'h', ''], [1,5], [1,5]); -testJumplist('jumplist_*_cachedCursor', ['*', ''], [1,3], [1,3]); -testJumplist('jumplist_#_cachedCursor', ['#', ''], [1,3], [1,3]); -testJumplist('jumplist_n', ['#', 'n', ''], [1,1], [2,3]); -testJumplist('jumplist_N', ['#', 'N', ''], [1,1], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', ''], [2,3], [2,3]); -testJumplist('jumplist_repeat_', ['*', '*', '*', '3', '', '2', ''], [5,0], [2,3]); -testJumplist('jumplist_repeated_motion', ['3', '*', ''], [2,3], [2,3]); -testJumplist('jumplist_/', ['/', ''], [2,3], [2,3], 'dialog'); -testJumplist('jumplist_?', ['?', ''], [2,3], [2,3], 'dialog'); -testJumplist('jumplist_skip_delted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], - [0,2], [0,2]); -testJumplist('jumplist_skip_delted_mark', - ['*', 'n', 'n', 'k', 'd', 'k', '', '', ''], - [1,0], [0,2]); - -/** - * @param name Name of the test - * @param keys An array of keys or a string with a single key to simulate. - * @param endPos The expected end position of the cursor. - * @param startPos The position the cursor should start at, defaults to 0, 0. - */ -function testMotion(name, keys, endPos, startPos) { - testVim(name, function(cm, vim, helpers) { - if (!startPos) { - startPos = { line: 0, ch: 0 }; - } - cm.setCursor(startPos); - helpers.doKeys(keys); - helpers.assertCursorAt(endPos); - }); -}; - -function makeCursor(line, ch) { - return { line: line, ch: ch }; -}; - -function offsetCursor(cur, offsetLine, offsetCh) { - return { line: cur.line + offsetLine, ch: cur.ch + offsetCh }; -}; - -// Motion tests -testMotion('|', '|', makeCursor(0, 0), makeCursor(0,4)); -testMotion('|_repeat', ['3', '|'], makeCursor(0, 2), makeCursor(0,4)); -testMotion('h', 'h', makeCursor(0, 0), word1.start); -testMotion('h_repeat', ['3', 'h'], offsetCursor(word1.end, 0, -3), word1.end); -testMotion('l', 'l', makeCursor(0, 1)); -testMotion('l_repeat', ['2', 'l'], makeCursor(0, 2)); -testMotion('j', 'j', offsetCursor(word1.end, 1, 0), word1.end); -testMotion('j_repeat', ['2', 'j'], offsetCursor(word1.end, 2, 0), word1.end); -testMotion('j_repeat_clip', ['1000', 'j'], endOfDocument); -testMotion('k', 'k', offsetCursor(word3.end, -1, 0), word3.end); -testMotion('k_repeat', ['2', 'k'], makeCursor(0, 4), makeCursor(2, 4)); -testMotion('k_repeat_clip', ['1000', 'k'], makeCursor(0, 4), makeCursor(2, 4)); -testMotion('w', 'w', word1.start); -testMotion('w_multiple_newlines_no_space', 'w', makeCursor(12, 2), makeCursor(11, 2)); -testMotion('w_multiple_newlines_with_space', 'w', makeCursor(14, 0), makeCursor(12, 51)); -testMotion('w_repeat', ['2', 'w'], word2.start); -testMotion('w_wrap', ['w'], word3.start, word2.start); -testMotion('w_endOfDocument', 'w', endOfDocument, endOfDocument); -testMotion('w_start_to_end', ['1000', 'w'], endOfDocument, makeCursor(0, 0)); -testMotion('W', 'W', bigWord1.start); -testMotion('W_repeat', ['2', 'W'], bigWord3.start, bigWord1.start); -testMotion('e', 'e', word1.end); -testMotion('e_repeat', ['2', 'e'], word2.end); -testMotion('e_wrap', 'e', word3.end, word2.end); -testMotion('e_endOfDocument', 'e', endOfDocument, endOfDocument); -testMotion('e_start_to_end', ['1000', 'e'], endOfDocument, makeCursor(0, 0)); -testMotion('b', 'b', word3.start, word3.end); -testMotion('b_repeat', ['2', 'b'], word2.start, word3.end); -testMotion('b_wrap', 'b', word2.start, word3.start); -testMotion('b_startOfDocument', 'b', makeCursor(0, 0), makeCursor(0, 0)); -testMotion('b_end_to_start', ['1000', 'b'], makeCursor(0, 0), endOfDocument); -testMotion('ge', ['g', 'e'], word2.end, word3.end); -testMotion('ge_repeat', ['2', 'g', 'e'], word1.end, word3.start); -testMotion('ge_wrap', ['g', 'e'], word2.end, word3.start); -testMotion('ge_startOfDocument', ['g', 'e'], makeCursor(0, 0), - makeCursor(0, 0)); -testMotion('ge_end_to_start', ['1000', 'g', 'e'], makeCursor(0, 0), endOfDocument); -testMotion('gg', ['g', 'g'], makeCursor(lines[0].line, lines[0].textStart), - makeCursor(3, 1)); -testMotion('gg_repeat', ['3', 'g', 'g'], - makeCursor(lines[2].line, lines[2].textStart)); -testMotion('G', 'G', - makeCursor(lines[lines.length - 1].line, lines[lines.length - 1].textStart), - makeCursor(3, 1)); -testMotion('G_repeat', ['3', 'G'], makeCursor(lines[2].line, - lines[2].textStart)); -// TODO: Make the test code long enough to test Ctrl-F and Ctrl-B. -testMotion('0', '0', makeCursor(0, 0), makeCursor(0, 8)); -testMotion('^', '^', makeCursor(0, lines[0].textStart), makeCursor(0, 8)); -testMotion('+', '+', makeCursor(1, lines[1].textStart), makeCursor(0, 8)); -testMotion('-', '-', makeCursor(0, lines[0].textStart), makeCursor(1, 4)); -testMotion('_', ['6','_'], makeCursor(5, lines[5].textStart), makeCursor(0, 8)); -testMotion('$', '$', makeCursor(0, lines[0].length - 1), makeCursor(0, 1)); -testMotion('$_repeat', ['2', '$'], makeCursor(1, lines[1].length - 1), - makeCursor(0, 3)); -testMotion('f', ['f', 'p'], pChars[0], makeCursor(charLine.line, 0)); -testMotion('f_repeat', ['2', 'f', 'p'], pChars[2], pChars[0]); -testMotion('f_num', ['f', '2'], numChars[2], makeCursor(charLine.line, 0)); -testMotion('t', ['t','p'], offsetCursor(pChars[0], 0, -1), - makeCursor(charLine.line, 0)); -testMotion('t_repeat', ['2', 't', 'p'], offsetCursor(pChars[2], 0, -1), - pChars[0]); -testMotion('F', ['F', 'p'], pChars[0], pChars[1]); -testMotion('F_repeat', ['2', 'F', 'p'], pChars[0], pChars[2]); -testMotion('T', ['T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[1]); -testMotion('T_repeat', ['2', 'T', 'p'], offsetCursor(pChars[0], 0, 1), pChars[2]); -testMotion('%_parens', ['%'], parens1.end, parens1.start); -testMotion('%_squares', ['%'], squares1.end, squares1.start); -testMotion('%_braces', ['%'], curlys1.end, curlys1.start); -testMotion('%_seek_outside', ['%'], seekOutside.end, seekOutside.start); -testMotion('%_seek_inside', ['%'], seekInside.end, seekInside.start); -testVim('%_seek_skip', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,9); -}, {value:'01234"("()'}); -testVim('%_skip_string', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,4); - cm.setCursor(0,2); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,0); -}, {value:'(")")'}); -(')') -testVim('%_skip_comment', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,6); - cm.setCursor(0,3); - helpers.doKeys(['%']); - helpers.assertCursorAt(0,0); -}, {value:'(/*)*/)'}); -// Make sure that moving down after going to the end of a line always leaves you -// at the end of a line, but preserves the offset in other cases -testVim('Changing lines after Eol operation', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys(['$']); - helpers.doKeys(['j']); - // After moving to Eol and then down, we should be at Eol of line 2 - helpers.assertCursorAt({ line: 1, ch: lines[1].length - 1 }); - helpers.doKeys(['j']); - // After moving down, we should be at Eol of line 3 - helpers.assertCursorAt({ line: 2, ch: lines[2].length - 1 }); - helpers.doKeys(['h']); - helpers.doKeys(['j']); - // After moving back one space and then down, since line 4 is shorter than line 2, we should - // be at Eol of line 2 - 1 - helpers.assertCursorAt({ line: 3, ch: lines[3].length - 1 }); - helpers.doKeys(['j']); - helpers.doKeys(['j']); - // After moving down again, since line 3 has enough characters, we should be back to the - // same place we were at on line 1 - helpers.assertCursorAt({ line: 5, ch: lines[2].length - 2 }); -}); -//making sure gj and gk recover from clipping -testVim('gj_gk_clipping', function(cm,vim,helpers){ - cm.setCursor(0, 1); - helpers.doKeys('g','j','g','j'); - helpers.assertCursorAt(2, 1); - helpers.doKeys('g','k','g','k'); - helpers.assertCursorAt(0, 1); -},{value: 'line 1\n\nline 2'}); -//testing a mix of j/k and gj/gk -testVim('j_k_and_gj_gk', function(cm,vim,helpers){ - cm.setSize(120); - cm.setCursor(0, 0); - //go to the last character on the first line - helpers.doKeys('$'); - //move up/down on the column within the wrapped line - //side-effect: cursor is not locked to eol anymore - helpers.doKeys('g','k'); - var cur=cm.getCursor(); - eq(cur.line,0); - is((cur.ch<176),'gk didn\'t move cursor back (1)'); - helpers.doKeys('g','j'); - helpers.assertCursorAt(0, 176); - //should move to character 177 on line 2 (j/k preserve character index within line) - helpers.doKeys('j'); - //due to different line wrapping, the cursor can be on a different screen-x now - //gj and gk preserve screen-x on movement, much like moveV - helpers.doKeys('3','g','k'); - cur=cm.getCursor(); - eq(cur.line,1); - is((cur.ch<176),'gk didn\'t move cursor back (2)'); - helpers.doKeys('g','j','2','g','j'); - //should return to the same character-index - helpers.doKeys('k'); - helpers.assertCursorAt(0, 176); -},{ lineWrapping:true, value: 'This line is intentially long to test movement of gj and gk over wrapped lines. I will start on the end of this line, then make a step up and back to set the origin for j and k.\nThis line is supposed to be even longer than the previous. I will jump here and make another wiggle with gj and gk, before I jump back to the line above. Both wiggles should not change my cursor\'s target character but both j/k and gj/gk change each other\'s reference position.'}); -testVim('gj_gk', function(cm, vim, helpers) { - if (phantom) return; - cm.setSize(120); - // Test top of document edge case. - cm.setCursor(0, 4); - helpers.doKeys('g', 'j'); - helpers.doKeys('10', 'g', 'k'); - helpers.assertCursorAt(0, 4); - - // Test moving down preserves column position. - helpers.doKeys('g', 'j'); - var pos1 = cm.getCursor(); - var expectedPos2 = { line: 0, ch: (pos1.ch - 4) * 2 + 4}; - helpers.doKeys('g', 'j'); - helpers.assertCursorAt(expectedPos2); - - // Move to the last character - cm.setCursor(0, 0); - // Move left to reset HSPos - helpers.doKeys('h'); - // Test bottom of document edge case. - helpers.doKeys('100', 'g', 'j'); - var endingPos = cm.getCursor(); - is(endingPos != 0, 'gj should not be on wrapped line 0'); - var topLeftCharCoords = cm.charCoords(makeCursor(0, 0)); - var endingCharCoords = cm.charCoords(endingPos); - is(topLeftCharCoords.left == endingCharCoords.left, 'gj should end up on column 0'); -},{ lineNumbers: false, lineWrapping:true, value: 'Thislineisintentiallylongtotestmovementofgjandgkoverwrappedlines.' }); -testVim('}', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(1, 0); - cm.setCursor(0, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(4, 0); - cm.setCursor(0, 0); - helpers.doKeys('6', '}'); - helpers.assertCursorAt(5, 0); -}, { value: 'a\n\nb\nc\n\nd' }); -testVim('{', function(cm, vim, helpers) { - cm.setCursor(5, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(4, 0); - cm.setCursor(5, 0); - helpers.doKeys('2', '{'); - helpers.assertCursorAt(1, 0); - cm.setCursor(5, 0); - helpers.doKeys('6', '{'); - helpers.assertCursorAt(0, 0); -}, { value: 'a\n\nb\nc\n\nd' }); -testVim('paragraph motions', function(cm, vim, helpers) { - cm.setCursor(10, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(4, 0); - helpers.doKeys('{'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(7, 0); - helpers.doKeys('2', '}'); - helpers.assertCursorAt(16, 0); - - cm.setCursor(9, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(14, 0); - - cm.setCursor(6, 0); - helpers.doKeys('}'); - helpers.assertCursorAt(7, 0); - - // ip inside empty space - cm.setCursor(10, 0); - helpers.doKeys('v', 'i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(12, 0), cm.getCursor('head')); - helpers.doKeys('i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(13, 1), cm.getCursor('head')); - helpers.doKeys('2', 'i', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); - - // should switch to visualLine mode - cm.setCursor(14, 0); - helpers.doKeys('', 'v', 'i', 'p'); - helpers.assertCursorAt(14, 0); - - cm.setCursor(14, 0); - helpers.doKeys('', 'V', 'i', 'p'); - eqPos(Pos(16, 1), cm.getCursor('head')); - - // ap inside empty space - cm.setCursor(10, 0); - helpers.doKeys('', 'v', 'a', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(13, 1), cm.getCursor('head')); - helpers.doKeys('a', 'p'); - eqPos(Pos(7, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); - - cm.setCursor(13, 0); - helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(13, 0), cm.getCursor('anchor')); - eqPos(Pos(14, 0), cm.getCursor('head')); - - cm.setCursor(16, 0); - helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(14, 0), cm.getCursor('anchor')); - eqPos(Pos(16, 1), cm.getCursor('head')); - - cm.setCursor(0, 0); - helpers.doKeys('v', 'a', 'p'); - eqPos(Pos(0, 0), cm.getCursor('anchor')); - eqPos(Pos(4, 0), cm.getCursor('head')); - - cm.setCursor(0, 0); - helpers.doKeys('d', 'i', 'p'); - var register = helpers.getRegisterController().getRegister(); - eq('a\na\n', register.toString()); - is(register.linewise); - helpers.doKeys('3', 'j', 'p'); - helpers.doKeys('y', 'i', 'p'); - is(register.linewise); - eq('b\na\na\nc\n', register.toString()); -}, { value: 'a\na\n\n\n\nb\nc\n\n\n\n\n\n\nd\n\ne\nf' }); - -// Operator tests -testVim('dl', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('d', 'l'); - eq('word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dl_eol', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('d', 'l'); - eq(' word1', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 6); -}, { value: ' word1 ' }); -testVim('dl_repeat', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('2', 'd', 'l'); - eq('ord1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' w', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dh', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'h'); - eq(' wrd1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('o', register.toString()); - is(!register.linewise); - eqPos(offsetCursor(curStart, 0 , -1), cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dj', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'j'); - eq(' word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1\nword2\n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2\n word3' }); -testVim('dj_end_of_document', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'j'); - eq(' word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1 ' }); -testVim('dk', function(cm, vim, helpers) { - var curStart = makeCursor(1, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'k'); - eq(' word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' word1\nword2\n', register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2\n word3' }); -testVim('dk_start_of_document', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('d', 'k'); - eq(' word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1 ' }); -testVim('dw_space', function(cm, vim, helpers) { - var curStart = makeCursor(0, 0); - cm.setCursor(curStart); - helpers.doKeys('d', 'w'); - eq('word1 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(' ', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('dw_word', function(cm, vim, helpers) { - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('d', 'w'); - eq(' word2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1 ', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1 word2' }); -testVim('dw_only_word', function(cm, vim, helpers) { - // Test that if there is only 1 word left, dw deletes till the end of the - // line. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1 ', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1 ' }); -testVim('dw_eol', function(cm, vim, helpers) { - // Assert that dw does not delete the newline if last word to delete is at end - // of line. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' \nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2' }); -testVim('dw_eol_with_multiple_newlines', function(cm, vim, helpers) { - // Assert that dw does not delete the newline if last word to delete is at end - // of line and it is followed by multiple newlines. - cm.setCursor(0, 1); - helpers.doKeys('d', 'w'); - eq(' \n\nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\n\nword2' }); -testVim('dw_empty_line_followed_by_whitespace', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq(' \nword', cm.getValue()); -}, { value: '\n \nword' }); -testVim('dw_empty_line_followed_by_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('word', cm.getValue()); -}, { value: '\nword' }); -testVim('dw_empty_line_followed_by_empty_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n', cm.getValue()); -}, { value: '\n\n' }); -testVim('dw_whitespace_followed_by_whitespace', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n \n', cm.getValue()); -}, { value: ' \n \n' }); -testVim('dw_whitespace_followed_by_empty_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n\n', cm.getValue()); -}, { value: ' \n\n' }); -testVim('dw_word_whitespace_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'w'); - eq('\n \nword2', cm.getValue()); -}, { value: 'word1\n \nword2'}) -testVim('dw_end_of_document', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('d', 'w'); - eq('\nab', cm.getValue()); -}, { value: '\nabc' }); -testVim('dw_repeat', function(cm, vim, helpers) { - // Assert that dw does delete newline if it should go to the next line, and - // that repeat works properly. - cm.setCursor(0, 1); - helpers.doKeys('d', '2', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2' }); -testVim('de_word_start_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'e'); - eq('\n\n', cm.getValue()); -}, { value: 'word\n\n' }); -testVim('de_word_end_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('d', 'e'); - eq('wor', cm.getValue()); -}, { value: 'word\n\n\n' }); -testVim('de_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'e'); - eq('', cm.getValue()); -}, { value: ' \n\n\n' }); -testVim('de_end_of_document', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('d', 'e'); - eq('\nab', cm.getValue()); -}, { value: '\nabc' }); -testVim('db_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('\n\n', cm.getValue()); -}, { value: '\n\n\n' }); -testVim('db_word_start_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('\nword', cm.getValue()); -}, { value: '\n\nword' }); -testVim('db_word_end_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 3); - helpers.doKeys('d', 'b'); - eq('\n\nd', cm.getValue()); -}, { value: '\n\nword' }); -testVim('db_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'b'); - eq('', cm.getValue()); -}, { value: '\n \n' }); -testVim('db_start_of_document', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'b'); - eq('abc\n', cm.getValue()); -}, { value: 'abc\n' }); -testVim('dge_empty_lines', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doKeys('d', 'g', 'e'); - // Note: In real VIM the result should be '', but it's not quite consistent, - // since 2 newlines are deleted. But in the similar case of word\n\n, only - // 1 newline is deleted. We'll diverge from VIM's behavior since it's much - // easier this way. - eq('\n', cm.getValue()); -}, { value: '\n\n' }); -testVim('dge_word_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doKeys('d', 'g', 'e'); - eq('wor\n', cm.getValue()); -}, { value: 'word\n\n'}); -testVim('dge_whitespace_and_empty_lines', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('d', 'g', 'e'); - eq('', cm.getValue()); -}, { value: '\n \n' }); -testVim('dge_start_of_document', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('d', 'g', 'e'); - eq('bc\n', cm.getValue()); -}, { value: 'abc\n' }); -testVim('d_inclusive', function(cm, vim, helpers) { - // Assert that when inclusive is set, the character the cursor is on gets - // deleted too. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('d', 'e'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1 ' }); -testVim('d_reverse', function(cm, vim, helpers) { - // Test that deleting in reverse works. - cm.setCursor(1, 0); - helpers.doKeys('d', 'b'); - eq(' word2 ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\n', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\nword2 ' }); -testVim('dd', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 1, ch: 0 }); - var expectedLineCount = cm.lineCount() - 1; - helpers.doKeys('d', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[1].textStart); -}); -testVim('dd_prefix_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 2, ch: 0 }); - var expectedLineCount = cm.lineCount() - 2; - helpers.doKeys('2', 'd', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[2].textStart); -}); -testVim('dd_motion_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 2, ch: 0 }); - var expectedLineCount = cm.lineCount() - 2; - helpers.doKeys('d', '2', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[2].textStart); -}); -testVim('dd_multiply_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); - var expectedLineCount = cm.lineCount() - 6; - helpers.doKeys('2', 'd', '3', 'd'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - helpers.assertCursorAt(0, lines[6].textStart); -}); -testVim('dd_lastline', function(cm, vim, helpers) { - cm.setCursor(cm.lineCount(), 0); - var expectedLineCount = cm.lineCount() - 1; - helpers.doKeys('d', 'd'); - eq(expectedLineCount, cm.lineCount()); - helpers.assertCursorAt(cm.lineCount() - 1, 0); -}); -testVim('dd_only_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - var expectedRegister = cm.getValue() + "\n"; - helpers.doKeys('d','d'); - eq(1, cm.lineCount()); - eq('', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedRegister, register.toString()); -}, { value: "thisistheonlyline" }); -// Yank commands should behave the exact same as d commands, expect that nothing -// gets deleted. -testVim('yw_repeat', function(cm, vim, helpers) { - // Assert that yw does yank newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('y', '2', 'w'); - eq(' word1\nword2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2' }); -testVim('yy_multiply_repeat', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); - var expectedLineCount = cm.lineCount(); - helpers.doKeys('2', 'y', '3', 'y'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - eqPos(curStart, cm.getCursor()); -}); -// Change commands behave like d commands except that it also enters insert -// mode. In addition, when the change is linewise, an additional newline is -// inserted so that insert mode starts on that line. -testVim('cw', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('c', '2', 'w'); - eq(' word3', cm.getValue()); - helpers.assertCursorAt(0, 0); -}, { value: 'word1 word2 word3'}); -testVim('cw_repeat', function(cm, vim, helpers) { - // Assert that cw does delete newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('c', '2', 'w'); - eq(' ', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('word1\nword2', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: ' word1\nword2' }); -testVim('cc_multiply_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedBuffer = cm.getRange({ line: 0, ch: 0 }, - { line: 6, ch: 0 }); - var expectedLineCount = cm.lineCount() - 5; - helpers.doKeys('2', 'c', '3', 'c'); - eq(expectedLineCount, cm.lineCount()); - var register = helpers.getRegisterController().getRegister(); - eq(expectedBuffer, register.toString()); - is(register.linewise); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('cc_should_not_append_to_document', function(cm, vim, helpers) { - var expectedLineCount = cm.lineCount(); - cm.setCursor(cm.lastLine(), 0); - helpers.doKeys('c', 'c'); - eq(expectedLineCount, cm.lineCount()); -}); -function fillArray(val, times) { - var arr = []; - for (var i = 0; i < times; i++) { - arr.push(val); - } - return arr; -} -testVim('c_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'c'); - var replacement = fillArray('hello', 3); - cm.replaceSelections(replacement); - eq('1hello\n5hello\nahellofg', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(2, 3); - helpers.doKeys('', '2', 'k', 'h', 'C'); - replacement = fillArray('world', 3); - cm.replaceSelections(replacement); - eq('1hworld\n5hworld\nahworld', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('c_visual_block_replay', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'c'); - var replacement = fillArray('fo', 3); - cm.replaceSelections(replacement); - eq('1fo4\n5fo8\nafodefg', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(0, 0); - helpers.doKeys('.'); - eq('foo4\nfoo8\nfoodefg', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); - -testVim('d_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'l', 'd'); - eq('1\n5\nafg', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('D_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'D'); - eq('1\n5\na', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); - -// Swapcase commands edit in place and do not modify registers. -testVim('g~w_repeat', function(cm, vim, helpers) { - // Assert that dw does delete newline if it should go to the next line, and - // that repeat works properly. - var curStart = makeCursor(0, 1); - cm.setCursor(curStart); - helpers.doKeys('g', '~', '2', 'w'); - eq(' WORD1\nWORD2', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2' }); -testVim('g~g~', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - var expectedLineCount = cm.lineCount(); - var expectedValue = cm.getValue().toUpperCase(); - helpers.doKeys('2', 'g', '~', '3', 'g', '~'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); -}, { value: ' word1\nword2\nword3\nword4\nword5\nword6' }); -testVim('gu_and_gU', function(cm, vim, helpers) { - var curStart = makeCursor(0, 7); - var value = cm.getValue(); - cm.setCursor(curStart); - helpers.doKeys('2', 'g', 'U', 'w'); - eq(cm.getValue(), 'wa wb xX WC wd'); - eqPos(curStart, cm.getCursor()); - helpers.doKeys('2', 'g', 'u', 'w'); - eq(cm.getValue(), value); - - helpers.doKeys('2', 'g', 'U', 'B'); - eq(cm.getValue(), 'wa WB Xx wc wd'); - eqPos(makeCursor(0, 3), cm.getCursor()); - - cm.setCursor(makeCursor(0, 4)); - helpers.doKeys('g', 'u', 'i', 'w'); - eq(cm.getValue(), 'wa wb Xx wc wd'); - eqPos(makeCursor(0, 3), cm.getCursor()); - - // TODO: support gUgU guu - // eqPos(makeCursor(0, 0), cm.getCursor()); - - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); -}, { value: 'wa wb xx wc wd' }); -testVim('visual_block_~', function(cm, vim, helpers) { - cm.setCursor(1, 1); - helpers.doKeys('', 'l', 'l', 'j', '~'); - helpers.assertCursorAt(1, 1); - eq('hello\nwoRLd\naBCDe', cm.getValue()); - cm.setCursor(2, 0); - helpers.doKeys('v', 'l', 'l', '~'); - helpers.assertCursorAt(2, 0); - eq('hello\nwoRLd\nAbcDe', cm.getValue()); -},{value: 'hello\nwOrld\nabcde' }); -testVim('._swapCase_visualBlock', function(cm, vim, helpers) { - helpers.doKeys('', 'j', 'j', 'l', '~'); - cm.setCursor(0, 3); - helpers.doKeys('.'); - eq('HelLO\nWorLd\nAbcdE', cm.getValue()); -},{value: 'hEllo\nwOrlD\naBcDe' }); -testVim('._delete_visualBlock', function(cm, vim, helpers) { - helpers.doKeys('', 'j', 'x'); - eq('ive\ne\nsome\nsugar', cm.getValue()); - helpers.doKeys('.'); - eq('ve\n\nsome\nsugar', cm.getValue()); - helpers.doKeys('j', 'j', '.'); - eq('ve\n\nome\nugar', cm.getValue()); - helpers.doKeys('u', '', '.'); - eq('ve\n\nme\ngar', cm.getValue()); -},{value: 'give\nme\nsome\nsugar' }); -testVim('>{motion}', function(cm, vim, helpers) { - cm.setCursor(1, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\n word2\nword3 '; - helpers.doKeys('>', 'k'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); -testVim('>>', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\n word2\nword3 '; - helpers.doKeys('2', '>', '>'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\nword3 ', indentUnit: 2 }); -testVim('<{motion}', function(cm, vim, helpers) { - cm.setCursor(1, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('<', 'k'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); -testVim('<<', function(cm, vim, helpers) { - cm.setCursor(0, 3); - var expectedLineCount = cm.lineCount(); - var expectedValue = ' word1\nword2\nword3 '; - helpers.doKeys('2', '<', '<'); - eq(expectedValue, cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 1); -}, { value: ' word1\n word2\nword3 ', indentUnit: 2 }); - -// Edit tests -function testEdit(name, before, pos, edit, after) { - return testVim(name, function(cm, vim, helpers) { - var ch = before.search(pos) - var line = before.substring(0, ch).split('\n').length - 1; - if (line) { - ch = before.substring(0, ch).split('\n').pop().length; - } - cm.setCursor(line, ch); - helpers.doKeys.apply(this, edit.split('')); - eq(after, cm.getValue()); - }, {value: before}); -} - -// These Delete tests effectively cover word-wise Change, Visual & Yank. -// Tabs are used as differentiated whitespace to catch edge cases. -// Normal word: -testEdit('diw_mid_spc', 'foo \tbAr\t baz', /A/, 'diw', 'foo \t\t baz'); -testEdit('daw_mid_spc', 'foo \tbAr\t baz', /A/, 'daw', 'foo \tbaz'); -testEdit('diw_mid_punct', 'foo \tbAr.\t baz', /A/, 'diw', 'foo \t.\t baz'); -testEdit('daw_mid_punct', 'foo \tbAr.\t baz', /A/, 'daw', 'foo.\t baz'); -testEdit('diw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diw', 'foo \t,.\t baz'); -testEdit('daw_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daw', 'foo \t,.\t baz'); -testEdit('diw_start_spc', 'bAr \tbaz', /A/, 'diw', ' \tbaz'); -testEdit('daw_start_spc', 'bAr \tbaz', /A/, 'daw', 'baz'); -testEdit('diw_start_punct', 'bAr. \tbaz', /A/, 'diw', '. \tbaz'); -testEdit('daw_start_punct', 'bAr. \tbaz', /A/, 'daw', '. \tbaz'); -testEdit('diw_end_spc', 'foo \tbAr', /A/, 'diw', 'foo \t'); -testEdit('daw_end_spc', 'foo \tbAr', /A/, 'daw', 'foo'); -testEdit('diw_end_punct', 'foo \tbAr.', /A/, 'diw', 'foo \t.'); -testEdit('daw_end_punct', 'foo \tbAr.', /A/, 'daw', 'foo.'); -// Big word: -testEdit('diW_mid_spc', 'foo \tbAr\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_spc', 'foo \tbAr\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_mid_punct', 'foo \tbAr.\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_punct', 'foo \tbAr.\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'diW', 'foo \t\t baz'); -testEdit('daW_mid_punct2', 'foo \t,bAr.\t baz', /A/, 'daW', 'foo \tbaz'); -testEdit('diW_start_spc', 'bAr\t baz', /A/, 'diW', '\t baz'); -testEdit('daW_start_spc', 'bAr\t baz', /A/, 'daW', 'baz'); -testEdit('diW_start_punct', 'bAr.\t baz', /A/, 'diW', '\t baz'); -testEdit('daW_start_punct', 'bAr.\t baz', /A/, 'daW', 'baz'); -testEdit('diW_end_spc', 'foo \tbAr', /A/, 'diW', 'foo \t'); -testEdit('daW_end_spc', 'foo \tbAr', /A/, 'daW', 'foo'); -testEdit('diW_end_punct', 'foo \tbAr.', /A/, 'diW', 'foo \t'); -testEdit('daW_end_punct', 'foo \tbAr.', /A/, 'daW', 'foo'); -// Deleting text objects -// Open and close on same line -testEdit('di(_open_spc', 'foo (bAr) baz', /\(/, 'di(', 'foo () baz'); -testEdit('di)_open_spc', 'foo (bAr) baz', /\(/, 'di)', 'foo () baz'); -testEdit('dib_open_spc', 'foo (bAr) baz', /\(/, 'dib', 'foo () baz'); -testEdit('da(_open_spc', 'foo (bAr) baz', /\(/, 'da(', 'foo baz'); -testEdit('da)_open_spc', 'foo (bAr) baz', /\(/, 'da)', 'foo baz'); - -testEdit('di(_middle_spc', 'foo (bAr) baz', /A/, 'di(', 'foo () baz'); -testEdit('di)_middle_spc', 'foo (bAr) baz', /A/, 'di)', 'foo () baz'); -testEdit('da(_middle_spc', 'foo (bAr) baz', /A/, 'da(', 'foo baz'); -testEdit('da)_middle_spc', 'foo (bAr) baz', /A/, 'da)', 'foo baz'); - -testEdit('di(_close_spc', 'foo (bAr) baz', /\)/, 'di(', 'foo () baz'); -testEdit('di)_close_spc', 'foo (bAr) baz', /\)/, 'di)', 'foo () baz'); -testEdit('da(_close_spc', 'foo (bAr) baz', /\)/, 'da(', 'foo baz'); -testEdit('da)_close_spc', 'foo (bAr) baz', /\)/, 'da)', 'foo baz'); - -// delete around and inner b. -testEdit('dab_on_(_should_delete_around_()block', 'o( in(abc) )', /\(a/, 'dab', 'o( in )'); - -// delete around and inner B. -testEdit('daB_on_{_should_delete_around_{}block', 'o{ in{abc} }', /{a/, 'daB', 'o{ in }'); -testEdit('diB_on_{_should_delete_inner_{}block', 'o{ in{abc} }', /{a/, 'diB', 'o{ in{} }'); - -testEdit('da{_on_{_should_delete_inner_block', 'o{ in{abc} }', /{a/, 'da{', 'o{ in }'); -testEdit('di[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'di[', 'foo (bAr) baz'); -testEdit('di[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'di[', 'foo (bAr) baz'); -testEdit('da[_on_(_should_not_delete', 'foo (bAr) baz', /\(/, 'da[', 'foo (bAr) baz'); -testEdit('da[_on_)_should_not_delete', 'foo (bAr) baz', /\)/, 'da[', 'foo (bAr) baz'); -testMotion('di(_outside_should_stay', ['d', 'i', '('], { line: 0, ch: 0}, { line: 0, ch: 0}); - -// Open and close on different lines, equally indented -testEdit('di{_middle_spc', 'a{\n\tbar\n}b', /r/, 'di{', 'a{}b'); -testEdit('di}_middle_spc', 'a{\n\tbar\n}b', /r/, 'di}', 'a{}b'); -testEdit('da{_middle_spc', 'a{\n\tbar\n}b', /r/, 'da{', 'ab'); -testEdit('da}_middle_spc', 'a{\n\tbar\n}b', /r/, 'da}', 'ab'); -testEdit('daB_middle_spc', 'a{\n\tbar\n}b', /r/, 'daB', 'ab'); - -// open and close on diff lines, open indented less than close -testEdit('di{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di{', 'a{}b'); -testEdit('di}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'di}', 'a{}b'); -testEdit('da{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da{', 'ab'); -testEdit('da}_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'da}', 'ab'); - -// open and close on diff lines, open indented more than close -testEdit('di[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di[', 'a\t[]b'); -testEdit('di]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'di]', 'a\t[]b'); -testEdit('da[_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da[', 'a\tb'); -testEdit('da]_middle_spc', 'a\t[\n\tbar\n]b', /r/, 'da]', 'a\tb'); - -function testSelection(name, before, pos, keys, sel) { - return testVim(name, function(cm, vim, helpers) { - var ch = before.search(pos) - var line = before.substring(0, ch).split('\n').length - 1; - if (line) { - ch = before.substring(0, ch).split('\n').pop().length; - } - cm.setCursor(line, ch); - helpers.doKeys.apply(this, keys.split('')); - eq(sel, cm.getSelection()); - }, {value: before}); -} -testSelection('viw_middle_spc', 'foo \tbAr\t baz', /A/, 'viw', 'bAr'); -testSelection('vaw_middle_spc', 'foo \tbAr\t baz', /A/, 'vaw', 'bAr\t '); -testSelection('viw_middle_punct', 'foo \tbAr,\t baz', /A/, 'viw', 'bAr'); -testSelection('vaW_middle_punct', 'foo \tbAr,\t baz', /A/, 'vaW', 'bAr,\t '); -testSelection('viw_start_spc', 'foo \tbAr\t baz', /b/, 'viw', 'bAr'); -testSelection('viw_end_spc', 'foo \tbAr\t baz', /r/, 'viw', 'bAr'); -testSelection('viw_eol', 'foo \tbAr', /r/, 'viw', 'bAr'); -testSelection('vi{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'vi{', '\n\tbar\n\t'); -testSelection('va{_middle_spc', 'a{\n\tbar\n\t}b', /r/, 'va{', '{\n\tbar\n\t}'); - -testVim('mouse_select', function(cm, vim, helpers) { - cm.setSelection(Pos(0, 2), Pos(0, 4), {origin: '*mouse'}); - is(cm.state.vim.visualMode); - is(!cm.state.vim.visualLine); - is(!cm.state.vim.visualBlock); - helpers.doKeys(''); - is(!cm.somethingSelected()); - helpers.doKeys('g', 'v'); - eq('cd', cm.getSelection()); -}, {value: 'abcdef'}); - -// Operator-motion tests -testVim('D', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('D'); - eq(' wo\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('rd1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\n word3' }); -testVim('C', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('C'); - eq(' wo\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('rd1', register.toString()); - is(!register.linewise); - eqPos(curStart, cm.getCursor()); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: ' word1\nword2\n word3' }); -testVim('Y', function(cm, vim, helpers) { - var curStart = makeCursor(0, 3); - cm.setCursor(curStart); - helpers.doKeys('Y'); - eq(' word1\nword2\n word3', cm.getValue()); - var register = helpers.getRegisterController().getRegister(); - eq('rd1', register.toString()); - is(!register.linewise); - helpers.assertCursorAt(0, 3); -}, { value: ' word1\nword2\n word3' }); -testVim('~', function(cm, vim, helpers) { - helpers.doKeys('3', '~'); - eq('ABCdefg', cm.getValue()); - helpers.assertCursorAt(0, 3); -}, { value: 'abcdefg' }); - -// Action tests -testVim('ctrl-a', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('-9', cm.getValue()); - helpers.assertCursorAt(0, 1); - helpers.doKeys('2',''); - eq('-7', cm.getValue()); -}, {value: '-10'}); -testVim('ctrl-x', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(''); - eq('-1', cm.getValue()); - helpers.assertCursorAt(0, 1); - helpers.doKeys('2',''); - eq('-3', cm.getValue()); -}, {value: '0'}); -testVim('/ search forward', function(cm, vim, helpers) { - forEach(['', ''], function(key) { - cm.setCursor(0, 0); - helpers.doKeys(key); - helpers.assertCursorAt(0, 5); - helpers.doKeys('l'); - helpers.doKeys(key); - helpers.assertCursorAt(0, 10); - cm.setCursor(0, 11); - helpers.doKeys(key); - helpers.assertCursorAt(0, 11); - }); -}, {value: '__jmp1 jmp2 jmp'}); -testVim('a', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('a'); - helpers.assertCursorAt(0, 2); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('a_eol', function(cm, vim, helpers) { - cm.setCursor(0, lines[0].length - 1); - helpers.doKeys('a'); - helpers.assertCursorAt(0, lines[0].length); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('A_endOfSelectedArea', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'j', 'l'); - helpers.doKeys('A'); - helpers.assertCursorAt(1, 2); - eq('vim-insert', cm.getOption('keyMap')); -}, {value: 'foo\nbar'}); -testVim('i', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('i'); - helpers.assertCursorAt(0, 1); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('i_repeat', function(cm, vim, helpers) { - helpers.doKeys('3', 'i'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - eq('testtesttest', cm.getValue()); - helpers.assertCursorAt(0, 11); -}, { value: '' }); -testVim('i_repeat_delete', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('2', 'i'); - cm.replaceRange('z', cm.getCursor()); - helpers.doInsertModeKeys('Backspace', 'Backspace'); - helpers.doKeys(''); - eq('abe', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'abcde' }); -testVim('A', function(cm, vim, helpers) { - helpers.doKeys('A'); - helpers.assertCursorAt(0, lines[0].length); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('A_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', '2', 'j', 'l', 'l', 'A'); - var replacement = new Array(cm.listSelections().length+1).join('hello ').split(' '); - replacement.pop(); - cm.replaceSelections(replacement); - eq('testhello\nmehello\npleahellose', cm.getValue()); - helpers.doKeys(''); - cm.setCursor(0, 0); - helpers.doKeys('.'); - // TODO this doesn't work yet - // eq('teshellothello\nme hello hello\nplehelloahellose', cm.getValue()); -}, {value: 'test\nme\nplease'}); -testVim('I', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('I'); - helpers.assertCursorAt(0, lines[0].textStart); - eq('vim-insert', cm.getOption('keyMap')); -}); -testVim('I_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('3', 'I'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - eq('testtesttestblah', cm.getValue()); - helpers.assertCursorAt(0, 11); -}, { value: 'blah' }); -testVim('I_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'l', 'l', 'I'); - var replacement = new Array(cm.listSelections().length+1).join('hello ').split(' '); - replacement.pop(); - cm.replaceSelections(replacement); - eq('hellotest\nhellome\nhelloplease', cm.getValue()); -}, {value: 'test\nme\nplease'}); -testVim('o', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('o'); - eq('word1\n\nword2', cm.getValue()); - helpers.assertCursorAt(1, 0); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'word1\nword2' }); -testVim('o_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('3', 'o'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - eq('\ntest\ntest\ntest', cm.getValue()); - helpers.assertCursorAt(3, 3); -}, { value: '' }); -testVim('O', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('O'); - eq('\nword1\nword2', cm.getValue()); - helpers.assertCursorAt(0, 0); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'word1\nword2' }); -testVim('J', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('J'); - var expectedValue = 'word1 word2\nword3\n word4'; - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, expectedValue.indexOf('word2') - 1); -}, { value: 'word1 \n word2\nword3\n word4' }); -testVim('J_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('3', 'J'); - var expectedValue = 'word1 word2 word3\n word4'; - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(0, expectedValue.indexOf('word3') - 1); -}, { value: 'word1 \n word2\nword3\n word4' }); -testVim('p', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); - helpers.doKeys('p'); - eq('__abc\ndef_', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_register', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().getRegister('a').setText('abc\ndef', false); - helpers.doKeys('"', 'a', 'p'); - eq('__abc\ndef_', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_wrong_register', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().getRegister('a').setText('abc\ndef', false); - helpers.doKeys('p'); - eq('___', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: '___' }); -testVim('p_line', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); - helpers.doKeys('2', 'p'); - eq('___\n a\nd\n a\nd', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim('p_lastline', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd', true); - helpers.doKeys('2', 'p'); - eq('___\n a\nd\n a\nd', cm.getValue()); - helpers.assertCursorAt(1, 2); -}, { value: '___' }); -testVim(']p_first_indent_is_smaller', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq(' ___\n abc\n def', cm.getValue()); -}, { value: ' ___' }); -testVim(']p_first_indent_is_larger', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq(' ___\n abc\ndef', cm.getValue()); -}, { value: ' ___' }); -testVim(']p_with_tab_indents', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', '\t\tabc\n\t\t\tdef\n', true); - helpers.doKeys(']', 'p'); - eq('\t___\n\tabc\n\t\tdef', cm.getValue()); -}, { value: '\t___', indentWithTabs: true}); -testVim(']p_with_spaces_translated_to_tabs', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys(']', 'p'); - eq('\t___\n\tabc\n\t\tdef', cm.getValue()); -}, { value: '\t___', indentWithTabs: true, tabSize: 2 }); -testVim('[p', function(cm, vim, helpers) { - helpers.getRegisterController().pushText('"', 'yank', ' abc\n def\n', true); - helpers.doKeys('[', 'p'); - eq(' abc\n def\n ___', cm.getValue()); -}, { value: ' ___' }); -testVim('P', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', 'abc\ndef', false); - helpers.doKeys('P'); - eq('_abc\ndef__', cm.getValue()); - helpers.assertCursorAt(1, 3); -}, { value: '___' }); -testVim('P_line', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.getRegisterController().pushText('"', 'yank', ' a\nd\n', true); - helpers.doKeys('2', 'P'); - eq(' a\nd\n a\nd\n___', cm.getValue()); - helpers.assertCursorAt(0, 2); -}, { value: '___' }); -testVim('r', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('3', 'r', 'u'); - eq('wuuuet\nanother', cm.getValue(),'3r failed'); - helpers.assertCursorAt(0, 3); - cm.setCursor(0, 4); - helpers.doKeys('v', 'j', 'h', 'r', ''); - eq('wuuu \n her', cm.getValue(),'Replacing selection by space-characters failed'); -}, { value: 'wordet\nanother' }); -testVim('r_visual_block', function(cm, vim, helpers) { - cm.setCursor(2, 3); - helpers.doKeys('', 'k', 'k', 'h', 'h', 'r', 'l'); - eq('1lll\n5lll\nalllefg', cm.getValue()); - helpers.doKeys('', 'l', 'j', 'r', ''); - eq('1 l\n5 l\nalllefg', cm.getValue()); - cm.setCursor(2, 0); - helpers.doKeys('o'); - helpers.doKeys(''); - cm.replaceRange('\t\t', cm.getCursor()); - helpers.doKeys('', 'h', 'h', 'r', 'r'); - eq('1 l\n5 l\nalllefg\nrrrrrrrr', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('R', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('R'); - helpers.assertCursorAt(0, 1); - eq('vim-replace', cm.getOption('keyMap')); - is(cm.state.overwrite, 'Setting overwrite state failed'); -}); -testVim('mark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('`', 't'); - helpers.assertCursorAt(2, 2); - cm.setCursor(2, 0); - cm.replaceRange(' h', cm.getCursor()); - cm.setCursor(0, 0); - helpers.doKeys('\'', 't'); - helpers.assertCursorAt(2, 3); -}); -testVim('jumpToMark_next', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 2); - cm.setCursor(0, 0); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_next_repeat', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(0, 0); - helpers.doKeys('2', ']', '`'); - helpers.assertCursorAt(3, 2); - cm.setCursor(0, 0); - helpers.doKeys('2', ']', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_next_sameline', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 2); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 4); -}); -testVim('jumpToMark_next_onlyprev', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(4, 0); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(4, 0); -}); -testVim('jumpToMark_next_nomark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys(']', '`'); - helpers.assertCursorAt(2, 2); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_next_linewise_over', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 1); - helpers.doKeys(']', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_next_action', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('d', ']', '`'); - helpers.assertCursorAt(0, 0); - var actual = cm.getLine(0); - var expected = 'pop pop 0 1 2 3 4'; - eq(actual, expected, "Deleting while jumping to the next mark failed."); -}); -testVim('jumpToMark_next_line_action', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(0, 0); - helpers.doKeys('d', ']', '\''); - helpers.assertCursorAt(0, 1); - var actual = cm.getLine(0); - var expected = ' (a) [b] {c} ' - eq(actual, expected, "Deleting while jumping to the next mark line failed."); -}); -testVim('jumpToMark_prev', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 't'); - cm.setCursor(4, 0); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 2); - cm.setCursor(4, 0); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_repeat', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(5, 0); - helpers.doKeys('2', '[', '`'); - helpers.assertCursorAt(3, 2); - cm.setCursor(5, 0); - helpers.doKeys('2', '[', '\''); - helpers.assertCursorAt(3, 1); -}); -testVim('jumpToMark_prev_sameline', function(cm, vim, helpers) { - cm.setCursor(2, 0); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(2, 2); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_onlynext', function(cm, vim, helpers) { - cm.setCursor(4, 4); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 0); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_nomark', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('[', '`'); - helpers.assertCursorAt(2, 2); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('jumpToMark_prev_linewise_over', function(cm, vim, helpers) { - cm.setCursor(2, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(3, 4); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 6); - helpers.doKeys('[', '\''); - helpers.assertCursorAt(2, 0); -}); -testVim('delmark_single', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 't'); - helpers.doEx('delmarks t'); - cm.setCursor(0, 0); - helpers.doKeys('`', 't'); - helpers.assertCursorAt(0, 0); -}); -testVim('delmark_range', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks b-d'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_multi', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks bcd'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_multi_space', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks b c d'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(1, 2); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(5, 2); -}); -testVim('delmark_all', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('m', 'a'); - cm.setCursor(2, 2); - helpers.doKeys('m', 'b'); - cm.setCursor(3, 2); - helpers.doKeys('m', 'c'); - cm.setCursor(4, 2); - helpers.doKeys('m', 'd'); - cm.setCursor(5, 2); - helpers.doKeys('m', 'e'); - helpers.doEx('delmarks a b-de'); - cm.setCursor(0, 0); - helpers.doKeys('`', 'a'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'b'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'c'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'd'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('`', 'e'); - helpers.assertCursorAt(0, 0); -}); -testVim('visual', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l'); - helpers.assertCursorAt(0, 4); - eqPos(makeCursor(0, 1), cm.getCursor('anchor')); - helpers.doKeys('d'); - eq('15', cm.getValue()); -}, { value: '12345' }); -testVim('visual_yank', function(cm, vim, helpers) { - helpers.doKeys('v', '3', 'l', 'y'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('p'); - eq('aa te test for yank', cm.getValue()); -}, { value: 'a test for yank' }) -testVim('visual_w', function(cm, vim, helpers) { - helpers.doKeys('v', 'w'); - eq(cm.getSelection(), 'motion t'); -}, { value: 'motion test'}); -testVim('visual_initial_selection', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v'); - cm.getSelection('n'); -}, { value: 'init'}); -testVim('visual_crossover_left', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys('v', 'l', 'h', 'h'); - cm.getSelection('ro'); -}, { value: 'cross'}); -testVim('visual_crossover_left', function(cm, vim, helpers) { - cm.setCursor(0, 2); - helpers.doKeys('v', 'h', 'l', 'l'); - cm.getSelection('os'); -}, { value: 'cross'}); -testVim('visual_crossover_up', function(cm, vim, helpers) { - cm.setCursor(3, 2); - helpers.doKeys('v', 'j', 'k', 'k'); - eqPos(Pos(2, 2), cm.getCursor('head')); - eqPos(Pos(3, 3), cm.getCursor('anchor')); - helpers.doKeys('k'); - eqPos(Pos(1, 2), cm.getCursor('head')); - eqPos(Pos(3, 3), cm.getCursor('anchor')); -}, { value: 'cross\ncross\ncross\ncross\ncross\n'}); -testVim('visual_crossover_down', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('v', 'k', 'j', 'j'); - eqPos(Pos(2, 3), cm.getCursor('head')); - eqPos(Pos(1, 2), cm.getCursor('anchor')); - helpers.doKeys('j'); - eqPos(Pos(3, 3), cm.getCursor('head')); - eqPos(Pos(1, 2), cm.getCursor('anchor')); -}, { value: 'cross\ncross\ncross\ncross\ncross\n'}); -testVim('visual_exit', function(cm, vim, helpers) { - helpers.doKeys('', 'l', 'j', 'j', ''); - eqPos(cm.getCursor('anchor'), cm.getCursor('head')); - eq(vim.visualMode, false); -}, { value: 'hello\nworld\nfoo' }); -testVim('visual_line', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'l', 'j', 'j', 'd'); - eq(' 4\n 5', cm.getValue()); -}, { value: ' 1\n 2\n 3\n 4\n 5' }); -testVim('visual_block_move_to_eol', function(cm, vim, helpers) { - // moveToEol should move all block cursors to end of line - cm.setCursor(0, 0); - helpers.doKeys('', 'G', '$'); - var selections = cm.getSelections().join(); - eq("123,45,6", selections); -}, {value: '123\n45\n6'}); -testVim('visual_block_different_line_lengths', function(cm, vim, helpers) { - // test the block selection with lines of different length - // i.e. extending the selection - // till the end of the longest line. - helpers.doKeys('', 'l', 'j', 'j', '6', 'l', 'd'); - helpers.doKeys('d', 'd', 'd', 'd'); - eq('', cm.getValue()); -}, {value: '1234\n5678\nabcdefg'}); -testVim('visual_block_truncate_on_short_line', function(cm, vim, helpers) { - // check for left side selection in case - // of moving up to a shorter line. - cm.replaceRange('', cm.getCursor()); - cm.setCursor(3, 4); - helpers.doKeys('', 'l', 'k', 'k', 'd'); - eq('hello world\n{\ntis\nsa!', cm.getValue()); -}, {value: 'hello world\n{\nthis is\nsparta!'}); -testVim('visual_block_corners', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('', '2', 'l', 'k'); - // circle around the anchor - // and check the selections - var selections = cm.getSelections(); - eq('345891', selections.join('')); - helpers.doKeys('4', 'h'); - selections = cm.getSelections(); - eq('123678', selections.join('')); - helpers.doKeys('j', 'j'); - selections = cm.getSelections(); - eq('678abc', selections.join('')); - helpers.doKeys('4', 'l'); - selections = cm.getSelections(); - eq('891cde', selections.join('')); -}, {value: '12345\n67891\nabcde'}); -testVim('visual_block_mode_switch', function(cm, vim, helpers) { - // switch between visual modes - cm.setCursor(1, 1); - // blockwise to characterwise visual - helpers.doKeys('', 'j', 'l', 'v'); - selections = cm.getSelections(); - eq('7891\nabc', selections.join('')); - // characterwise to blockwise - helpers.doKeys(''); - selections = cm.getSelections(); - eq('78bc', selections.join('')); - // blockwise to linewise visual - helpers.doKeys('V'); - selections = cm.getSelections(); - eq('67891\nabcde', selections.join('')); -}, {value: '12345\n67891\nabcde'}); -testVim('visual_block_crossing_short_line', function(cm, vim, helpers) { - // visual block with long and short lines - cm.setCursor(0, 3); - helpers.doKeys('', 'j', 'j', 'j'); - var selections = cm.getSelections().join(); - eq('4,,d,b', selections); - helpers.doKeys('3', 'k'); - selections = cm.getSelections().join(); - eq('4', selections); - helpers.doKeys('5', 'j', 'k'); - selections = cm.getSelections().join(""); - eq(10, selections.length); -}, {value: '123456\n78\nabcdefg\nfoobar\n}\n'}); -testVim('visual_block_curPos_on_exit', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '3' , 'l', ''); - eqPos(makeCursor(0, 3), cm.getCursor()); - helpers.doKeys('h', '', '2' , 'j' ,'3' , 'l'); - eq(cm.getSelections().join(), "3456,,cdef"); - helpers.doKeys('4' , 'h'); - eq(cm.getSelections().join(), "23,8,bc"); - helpers.doKeys('2' , 'l'); - eq(cm.getSelections().join(), "34,,cd"); -}, {value: '123456\n78\nabcdefg\nfoobar'}); - -testVim('visual_marks', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l', 'j', 'j', 'v'); - // Test visual mode marks - cm.setCursor(2, 1); - helpers.doKeys('\'', '<'); - helpers.assertCursorAt(0, 1); - helpers.doKeys('\'', '>'); - helpers.assertCursorAt(2, 0); -}); -testVim('visual_join', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'l', 'j', 'j', 'J'); - eq(' 1 2 3\n 4\n 5', cm.getValue()); - is(!vim.visualMode); -}, { value: ' 1\n 2\n 3\n 4\n 5' }); -testVim('visual_blank', function(cm, vim, helpers) { - helpers.doKeys('v', 'k'); - eq(vim.visualMode, true); -}, { value: '\n' }); -testVim('reselect_visual', function(cm, vim, helpers) { - helpers.doKeys('l', 'v', 'l', 'l', 'l', 'y', 'g', 'v'); - helpers.assertCursorAt(0, 5); - eqPos(makeCursor(0, 1), cm.getCursor('anchor')); - helpers.doKeys('v'); - cm.setCursor(1, 0); - helpers.doKeys('v', 'l', 'l', 'p'); - eq('123456\n2345\nbar', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys('g', 'v'); - // here the fake cursor is at (1, 3) - helpers.assertCursorAt(1, 4); - eqPos(makeCursor(1, 0), cm.getCursor('anchor')); - helpers.doKeys('v'); - cm.setCursor(2, 0); - helpers.doKeys('v', 'l', 'l', 'g', 'v'); - helpers.assertCursorAt(1, 4); - eqPos(makeCursor(1, 0), cm.getCursor('anchor')); - helpers.doKeys('g', 'v'); - helpers.assertCursorAt(2, 3); - eqPos(makeCursor(2, 0), cm.getCursor('anchor')); - eq('123456\n2345\nbar', cm.getValue()); -}, { value: '123456\nfoo\nbar' }); -testVim('reselect_visual_line', function(cm, vim, helpers) { - helpers.doKeys('l', 'V', 'j', 'j', 'V', 'g', 'v', 'd'); - eq('foo\nand\nbar', cm.getValue()); - cm.setCursor(1, 0); - helpers.doKeys('V', 'y', 'j'); - helpers.doKeys('V', 'p' , 'g', 'v', 'd'); - eq('foo\nand', cm.getValue()); -}, { value: 'hello\nthis\nis\nfoo\nand\nbar' }); -testVim('reselect_visual_block', function(cm, vim, helpers) { - cm.setCursor(1, 2); - helpers.doKeys('', 'k', 'h', ''); - cm.setCursor(2, 1); - helpers.doKeys('v', 'l', 'g', 'v'); - eqPos(Pos(1, 2), vim.sel.anchor); - eqPos(Pos(0, 1), vim.sel.head); - // Ensure selection is done with visual block mode rather than one - // continuous range. - eq(cm.getSelections().join(''), '23oo') - helpers.doKeys('g', 'v'); - eqPos(Pos(2, 1), vim.sel.anchor); - eqPos(Pos(2, 2), vim.sel.head); - helpers.doKeys(''); - // Ensure selection of deleted range - cm.setCursor(1, 1); - helpers.doKeys('v', '', 'j', 'd', 'g', 'v'); - eq(cm.getSelections().join(''), 'or'); -}, { value: '123456\nfoo\nbar' }); -testVim('s_normal', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('s'); - helpers.doKeys(''); - eq('ac', cm.getValue()); -}, { value: 'abc'}); -testVim('s_visual', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v', 's'); - helpers.doKeys(''); - helpers.assertCursorAt(0, 0); - eq('ac', cm.getValue()); -}, { value: 'abc'}); -testVim('o_visual', function(cm, vim, helpers) { - cm.setCursor(0,0); - helpers.doKeys('v','l','l','l','o'); - helpers.assertCursorAt(0,0); - helpers.doKeys('v','v','j','j','j','o'); - helpers.assertCursorAt(0,0); - helpers.doKeys('O'); - helpers.doKeys('l','l') - helpers.assertCursorAt(3, 3); - helpers.doKeys('d'); - eq('p',cm.getValue()); -}, { value: 'abcd\nefgh\nijkl\nmnop'}); -testVim('o_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('','3','j','l','l', 'o'); - eqPos(Pos(3, 3), vim.sel.anchor); - eqPos(Pos(0, 1), vim.sel.head); - helpers.doKeys('O'); - eqPos(Pos(3, 1), vim.sel.anchor); - eqPos(Pos(0, 3), vim.sel.head); - helpers.doKeys('o'); - eqPos(Pos(0, 3), vim.sel.anchor); - eqPos(Pos(3, 1), vim.sel.head); -}, { value: 'abcd\nefgh\nijkl\nmnop'}); -testVim('changeCase_visual', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'l', 'l'); - helpers.doKeys('U'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('v', 'l', 'l'); - helpers.doKeys('u'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('l', 'l', 'l', '.'); - helpers.assertCursorAt(0, 3); - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'v', 'j', 'U', 'q'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('j', '@', 'a'); - helpers.assertCursorAt(1, 0); - cm.setCursor(3, 0); - helpers.doKeys('V', 'U', 'j', '.'); - eq('ABCDEF\nGHIJKL\nMnopq\nSHORT LINE\nLONG LINE OF TEXT', cm.getValue()); -}, { value: 'abcdef\nghijkl\nmnopq\nshort line\nlong line of text'}); -testVim('changeCase_visual_block', function(cm, vim, helpers) { - cm.setCursor(2, 1); - helpers.doKeys('', 'k', 'k', 'h', 'U'); - eq('ABcdef\nGHijkl\nMNopq\nfoo', cm.getValue()); - cm.setCursor(0, 2); - helpers.doKeys('.'); - eq('ABCDef\nGHIJkl\nMNOPq\nfoo', cm.getValue()); - // check when last line is shorter. - cm.setCursor(2, 2); - helpers.doKeys('.'); - eq('ABCDef\nGHIJkl\nMNOPq\nfoO', cm.getValue()); -}, { value: 'abcdef\nghijkl\nmnopq\nfoo'}); -testVim('visual_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('v', 'l', 'l', 'y'); - helpers.assertCursorAt(0, 0); - helpers.doKeys('3', 'l', 'j', 'v', 'l', 'p'); - helpers.assertCursorAt(1, 5); - eq('this is a\nunithitest for visual paste', cm.getValue()); - cm.setCursor(0, 0); - // in case of pasting whole line - helpers.doKeys('y', 'y'); - cm.setCursor(1, 6); - helpers.doKeys('v', 'l', 'l', 'l', 'p'); - helpers.assertCursorAt(2, 0); - eq('this is a\nunithi\nthis is a\n for visual paste', cm.getValue()); -}, { value: 'this is a\nunit test for visual paste'}); - -// This checks the contents of the register used to paste the text -testVim('v_paste_from_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - cm.setCursor(1, 0); - helpers.doKeys('v', 'p'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+register/.test(text)); - }); -}, { value: 'register contents\nare not erased'}); -testVim('S_normal', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('j', 'S'); - helpers.doKeys(''); - helpers.assertCursorAt(1, 0); - eq('aa\n\ncc', cm.getValue()); -}, { value: 'aa\nbb\ncc'}); -testVim('blockwise_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '3', 'j', 'l', 'y'); - cm.setCursor(0, 2); - // paste one char after the current cursor position - helpers.doKeys('p'); - eq('helhelo\nworwold\nfoofo\nbarba', cm.getValue()); - cm.setCursor(0, 0); - helpers.doKeys('v', '4', 'l', 'y'); - cm.setCursor(0, 0); - helpers.doKeys('', '3', 'j', 'p'); - eq('helheelhelo\norwold\noofo\narba', cm.getValue()); -}, { value: 'hello\nworld\nfoo\nbar'}); -testVim('blockwise_paste_long/short_line', function(cm, vim, helpers) { - // extend short lines in case of different line lengths. - cm.setCursor(0, 0); - helpers.doKeys('', 'j', 'j', 'y'); - cm.setCursor(0, 3); - helpers.doKeys('p'); - eq('hellho\nfoo f\nbar b', cm.getValue()); -}, { value: 'hello\nfoo\nbar'}); -testVim('blockwise_paste_cut_paste', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'x'); - cm.setCursor(0, 0); - helpers.doKeys('P'); - eq('cut\nand\npaste\nme', cm.getValue()); -}, { value: 'cut\nand\npaste\nme'}); -testVim('blockwise_paste_from_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', '"', 'a', 'y'); - cm.setCursor(0, 3); - helpers.doKeys('"', 'a', 'p'); - eq('foobfar\nhellho\nworlwd', cm.getValue()); -}, { value: 'foobar\nhello\nworld'}); -testVim('blockwise_paste_last_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', '2', 'j', 'l', 'y'); - cm.setCursor(3, 0); - helpers.doKeys('p'); - eq('cut\nand\npaste\nmcue\n an\n pa', cm.getValue()); -}, { value: 'cut\nand\npaste\nme'}); - -testVim('S_visual', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('v', 'j', 'S'); - helpers.doKeys(''); - helpers.assertCursorAt(0, 0); - eq('\ncc', cm.getValue()); -}, { value: 'aa\nbb\ncc'}); - -testVim('/ and n/N', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 6); - helpers.doKeys('N'); - helpers.assertCursorAt(0, 11); - - cm.setCursor(0, 0); - helpers.doKeys('2', '/'); - helpers.assertCursorAt(1, 6); -}, { value: 'match nope match \n nope Match' }); -testVim('/_case', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('Match'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 6); -}, { value: 'match nope match \n nope Match' }); -testVim('/_2_pcre', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', true); - cm.openDialog = helpers.fakeOpenDialog('(word){2}'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 9); - helpers.doKeys('n'); - helpers.assertCursorAt(2, 1); -}, { value: 'word\n another wordword\n wordwordword\n' }); -testVim('/_2_nopcre', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', false); - cm.openDialog = helpers.fakeOpenDialog('\\(word\\)\\{2}'); - helpers.doKeys('/'); - helpers.assertCursorAt(1, 9); - helpers.doKeys('n'); - helpers.assertCursorAt(2, 1); -}, { value: 'word\n another wordword\n wordwordword\n' }); -testVim('/_nongreedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('aa'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('?_nongreedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('aa'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('/_greedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a+'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('?_greedy', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a+'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa \n a aa'}); -testVim('/_greedy_0_or_more', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a*'); - helpers.doKeys('/'); - helpers.assertCursorAt(0, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 5); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 0); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa\n aa'}); -testVim('?_greedy_0_or_more', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('a*'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 1); - helpers.doKeys('n'); - helpers.assertCursorAt(1, 0); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 5); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 3); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 0); -}, { value: 'aaa aa\n aa'}); -testVim('? and n/N', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?'); - helpers.assertCursorAt(1, 6); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 11); - helpers.doKeys('N'); - helpers.assertCursorAt(1, 6); - - cm.setCursor(0, 0); - helpers.doKeys('2', '?'); - helpers.assertCursorAt(0, 11); -}, { value: 'match nope match \n nope Match' }); -testVim('*', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 22); - - cm.setCursor(0, 9); - helpers.doKeys('2', '*'); - helpers.assertCursorAt(1, 8); -}, { value: 'nomatch match nomatch match \nnomatch Match' }); -testVim('*_no_word', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 0); -}, { value: ' \n match \n' }); -testVim('*_symbol', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('*'); - helpers.assertCursorAt(1, 0); -}, { value: ' /}\n/} match \n' }); -testVim('#', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('#'); - helpers.assertCursorAt(1, 8); - - cm.setCursor(0, 9); - helpers.doKeys('2', '#'); - helpers.assertCursorAt(0, 22); -}, { value: 'nomatch match nomatch match \nnomatch Match' }); -testVim('*_seek', function(cm, vim, helpers) { - // Should skip over space and symbols. - cm.setCursor(0, 3); - helpers.doKeys('*'); - helpers.assertCursorAt(0, 22); -}, { value: ' := match nomatch match \nnomatch Match' }); -testVim('#', function(cm, vim, helpers) { - // Should skip over space and symbols. - cm.setCursor(0, 3); - helpers.doKeys('#'); - helpers.assertCursorAt(1, 8); -}, { value: ' := match nomatch match \nnomatch Match' }); -testVim('g*', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('g', '*'); - helpers.assertCursorAt(0, 18); - cm.setCursor(0, 8); - helpers.doKeys('3', 'g', '*'); - helpers.assertCursorAt(1, 8); -}, { value: 'matches match alsoMatch\nmatchme matching' }); -testVim('g#', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('g', '#'); - helpers.assertCursorAt(0, 0); - cm.setCursor(0, 8); - helpers.doKeys('3', 'g', '#'); - helpers.assertCursorAt(1, 0); -}, { value: 'matches match alsoMatch\nmatchme matching' }); -testVim('macro_insert', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '0', 'i'); - cm.replaceRange('foo', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q', '@', 'a'); - eq('foofoo', cm.getValue()); -}, { value: ''}); -testVim('macro_insert_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '$', 'a'); - cm.replaceRange('larry.', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('a'); - cm.replaceRange('curly.', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('a'); - cm.replaceRange('moe.', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('@', 'a'); - // At this point, the most recent edit should be the 2nd insert change - // inside the macro, i.e. "curly.". - helpers.doKeys('.'); - eq('larry.curly.moe.larry.curly.curly.', cm.getValue()); -}, { value: ''}); -testVim('macro_space', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('', ''); - helpers.assertCursorAt(0, 2); - helpers.doKeys('q', 'a', '', '', 'q'); - helpers.assertCursorAt(0, 4); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0, 6); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0, 8); -}, { value: 'one line of text.'}); -testVim('macro_t_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 't', 'e', 'q'); - helpers.assertCursorAt(0, 1); - helpers.doKeys('l', '@', 'a'); - helpers.assertCursorAt(0, 6); - helpers.doKeys('l', ';'); - helpers.assertCursorAt(0, 12); -}, { value: 'one line of text.'}); -testVim('macro_f_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'b', 'f', 'e', 'q'); - helpers.assertCursorAt(0, 2); - helpers.doKeys('@', 'b'); - helpers.assertCursorAt(0, 7); - helpers.doKeys(';'); - helpers.assertCursorAt(0, 13); -}, { value: 'one line of text.'}); -testVim('macro_slash_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'c'); - cm.openDialog = helpers.fakeOpenDialog('e'); - helpers.doKeys('/', 'q'); - helpers.assertCursorAt(0, 2); - helpers.doKeys('@', 'c'); - helpers.assertCursorAt(0, 7); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 13); -}, { value: 'one line of text.'}); -testVim('macro_multislash_search', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'd'); - cm.openDialog = helpers.fakeOpenDialog('e'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('t'); - helpers.doKeys('/', 'q'); - helpers.assertCursorAt(0, 12); - helpers.doKeys('@', 'd'); - helpers.assertCursorAt(0, 15); -}, { value: 'one line of text to rule them all.'}); -testVim('macro_parens', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'z', 'i'); - cm.replaceRange('(', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('e', 'a'); - cm.replaceRange(')', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('w', '@', 'z'); - helpers.doKeys('w', '@', 'z'); - eq('(see) (spot) (run)', cm.getValue()); -}, { value: 'see spot run'}); -testVim('macro_overwrite', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'z', '0', 'i'); - cm.replaceRange('I ', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('e'); - // Now replace the macro with something else. - helpers.doKeys('q', 'z', 'a'); - cm.replaceRange('.', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('e', '@', 'z'); - helpers.doKeys('e', '@', 'z'); - eq('I see. spot. run.', cm.getValue()); -}, { value: 'see spot run'}); -testVim('macro_search_f', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'f', ' '); - helpers.assertCursorAt(0,3); - helpers.doKeys('q', '0'); - helpers.assertCursorAt(0,0); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0,3); -}, { value: 'The quick brown fox jumped over the lazy dog.'}); -testVim('macro_search_2f', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', '2', 'f', ' '); - helpers.assertCursorAt(0,9); - helpers.doKeys('q', '0'); - helpers.assertCursorAt(0,0); - helpers.doKeys('@', 'a'); - helpers.assertCursorAt(0,9); -}, { value: 'The quick brown fox jumped over the lazy dog.'}); -testVim('yank_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'b', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo/.test(text)); - is(/b\s+bar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_visual_block', function(cm, vim, helpers) { - cm.setCursor(0, 1); - helpers.doKeys('', 'l', 'j', '"', 'a', 'y'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+oo\nar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_line_to_line_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'A', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_word_to_word_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - helpers.doKeys('j', '"', 'A', 'y', 'w'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foobar/.test(text)); - is(/"\s+foobar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_line_to_word_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'w'); - helpers.doKeys('j', '"', 'A', 'y', 'y'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('yank_append_word_to_line_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('"', 'a', 'y', 'y'); - helpers.doKeys('j', '"', 'A', 'y', 'w'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+foo\nbar/.test(text)); - is(/"\s+foo\nbar/.test(text)); - }); - helpers.doKeys(':'); -}, { value: 'foo\nbar'}); -testVim('macro_register', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('q', 'a', 'i'); - cm.replaceRange('gangnam', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - helpers.doKeys('q', 'b', 'o'); - cm.replaceRange('style', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('q'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/a\s+i/.test(text)); - is(/b\s+o/.test(text)); - }); - helpers.doKeys(':'); -}, { value: ''}); -testVim('._register', function(cm,vim,helpers) { - cm.setCursor(0,0); - helpers.doKeys('i'); - cm.replaceRange('foo',cm.getCursor()); - helpers.doKeys(''); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/\.\s+foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim(':_register', function(cm,vim,helpers) { - helpers.doEx('bar'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/:\s+bar/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_register_escape', function(cm, vim, helpers) { - // Check that the register is restored if the user escapes rather than confirms. - cm.openDialog = helpers.fakeOpenDialog('waldo'); - helpers.doKeys('/'); - var onKeyDown; - var onKeyUp; - var KEYCODES = { - f: 70, - o: 79, - Esc: 27 - }; - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - onKeyUp = options.onKeyUp; - }; - var close = function() {}; - helpers.doKeys('/'); - // Fake some keyboard events coming in. - onKeyDown({keyCode: KEYCODES.f}, '', close); - onKeyUp({keyCode: KEYCODES.f}, '', close); - onKeyDown({keyCode: KEYCODES.o}, 'f', close); - onKeyUp({keyCode: KEYCODES.o}, 'f', close); - onKeyDown({keyCode: KEYCODES.o}, 'fo', close); - onKeyUp({keyCode: KEYCODES.o}, 'fo', close); - onKeyDown({keyCode: KEYCODES.Esc}, 'foo', close); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/waldo/.test(text)); - is(!/foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_register', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('foo'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('registers'); - cm.openNotification = helpers.fakeOpenNotification(function(text) { - is(/\/\s+foo/.test(text)); - }); - helpers.doKeys(':'); -}, {value: ''}); -testVim('search_history', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('this'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('checks'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('search'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('history'); - helpers.doKeys('/'); - cm.openDialog = helpers.fakeOpenDialog('checks'); - helpers.doKeys('/'); - var onKeyDown; - var onKeyUp; - var query = ''; - var keyCodes = { - Up: 38, - Down: 40 - }; - cm.openDialog = function(template, callback, options) { - onKeyUp = options.onKeyUp; - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') query = newVal; - } - helpers.doKeys('/'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'checks'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'history'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'search'); - onKeyDown({keyCode: keyCodes.Up}, query, close); - onKeyUp({keyCode: keyCodes.Up}, query, close); - eq(query, 'this'); - onKeyDown({keyCode: keyCodes.Down}, query, close); - onKeyUp({keyCode: keyCodes.Down}, query, close); - eq(query, 'search'); -}, {value: ''}); -testVim('exCommand_history', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('registers'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('sort'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('map'); - helpers.doKeys(':'); - cm.openDialog = helpers.fakeOpenDialog('invalid'); - helpers.doKeys(':'); - var onKeyDown; - var onKeyUp; - var input = ''; - var keyCodes = { - Up: 38, - Down: 40, - s: 115 - }; - cm.openDialog = function(template, callback, options) { - onKeyUp = options.onKeyUp; - onKeyDown = options.onKeyDown; - }; - var close = function(newVal) { - if (typeof newVal == 'string') input = newVal; - } - helpers.doKeys(':'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'invalid'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'map'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'sort'); - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'registers'); - onKeyDown({keyCode: keyCodes.s}, '', close); - input = 's'; - onKeyDown({keyCode: keyCodes.Up}, input, close); - eq(input, 'sort'); -}, {value: ''}); -testVim('.', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('2', 'd', 'w'); - helpers.doKeys('.'); - eq('5 6', cm.getValue()); -}, { value: '1 2 3 4 5 6'}); -testVim('._repeat', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('2', 'd', 'w'); - helpers.doKeys('3', '.'); - eq('6', cm.getValue()); -}, { value: '1 2 3 4 5 6'}); -testVim('._insert', function(cm, vim, helpers) { - helpers.doKeys('i'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('testestt', cm.getValue()); - helpers.assertCursorAt(0, 6); -}, { value: ''}); -testVim('._insert_repeat', function(cm, vim, helpers) { - helpers.doKeys('i'); - cm.replaceRange('test', cm.getCursor()); - cm.setCursor(0, 4); - helpers.doKeys(''); - helpers.doKeys('2', '.'); - eq('testesttestt', cm.getValue()); - helpers.assertCursorAt(0, 10); -}, { value: ''}); -testVim('._repeat_insert', function(cm, vim, helpers) { - helpers.doKeys('3', 'i'); - cm.replaceRange('te', cm.getCursor()); - cm.setCursor(0, 2); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('tetettetetee', cm.getValue()); - helpers.assertCursorAt(0, 10); -}, { value: ''}); -testVim('._insert_o', function(cm, vim, helpers) { - helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); - cm.setCursor(1, 1); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('\nz\nz', cm.getValue()); - helpers.assertCursorAt(2, 0); -}, { value: ''}); -testVim('._insert_o_repeat', function(cm, vim, helpers) { - helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); - helpers.doKeys(''); - cm.setCursor(1, 0); - helpers.doKeys('2', '.'); - eq('\nz\nz\nz', cm.getValue()); - helpers.assertCursorAt(3, 0); -}, { value: ''}); -testVim('._insert_o_indent', function(cm, vim, helpers) { - helpers.doKeys('o'); - cm.replaceRange('z', cm.getCursor()); - helpers.doKeys(''); - cm.setCursor(1, 2); - helpers.doKeys('.'); - eq('{\n z\n z', cm.getValue()); - helpers.assertCursorAt(2, 2); -}, { value: '{'}); -testVim('._insert_cw', function(cm, vim, helpers) { - helpers.doKeys('c', 'w'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - cm.setCursor(0, 3); - helpers.doKeys('2', 'l'); - helpers.doKeys('.'); - eq('test test word3', cm.getValue()); - helpers.assertCursorAt(0, 8); -}, { value: 'word1 word2 word3' }); -testVim('._insert_cw_repeat', function(cm, vim, helpers) { - // For some reason, repeat cw in desktop VIM will does not repeat insert mode - // changes. Will conform to that behavior. - helpers.doKeys('c', 'w'); - cm.replaceRange('test', cm.getCursor()); - helpers.doKeys(''); - cm.setCursor(0, 4); - helpers.doKeys('l'); - helpers.doKeys('2', '.'); - eq('test test', cm.getValue()); - helpers.assertCursorAt(0, 8); -}, { value: 'word1 word2 word3' }); -testVim('._delete', function(cm, vim, helpers) { - cm.setCursor(0, 5); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Backspace'); - helpers.doKeys(''); - helpers.doKeys('.'); - eq('zace', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'zabcde'}); -testVim('._delete_repeat', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('i'); - helpers.doInsertModeKeys('Backspace'); - helpers.doKeys(''); - helpers.doKeys('2', '.'); - eq('zzce', cm.getValue()); - helpers.assertCursorAt(0, 1); -}, { value: 'zzabcde'}); -testVim('._visual_>', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('V', 'j', '>'); - cm.setCursor(2, 0) - helpers.doKeys('.'); - eq(' 1\n 2\n 3\n 4', cm.getValue()); - helpers.assertCursorAt(2, 2); -}, { value: '1\n2\n3\n4'}); -testVim('f;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(9, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('F;', function(cm, vim, helpers) { - cm.setCursor(0, 8); - helpers.doKeys('F', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(2, cm.getCursor().ch); -}, { value: '01x3xx6x8x'}); -testVim('t;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(8, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('T;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', 'x'); - helpers.doKeys(';'); - helpers.doKeys('2', ';'); - eq(2, cm.getCursor().ch); -}, { value: '0xx3xx678x'}); -testVim('f,', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('f', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(2, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('F,', function(cm, vim, helpers) { - cm.setCursor(0, 3); - helpers.doKeys('F', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(9, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('t,', function(cm, vim, helpers) { - cm.setCursor(0, 6); - helpers.doKeys('t', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(3, cm.getCursor().ch); -}, { value: '01x3xx678x'}); -testVim('T,', function(cm, vim, helpers) { - cm.setCursor(0, 4); - helpers.doKeys('T', 'x'); - helpers.doKeys(','); - helpers.doKeys('2', ','); - eq(8, cm.getCursor().ch); -}, { value: '01x3xx67xx'}); -testVim('fd,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('d', ';'); - eq('56789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('d', ','); - eq('01239', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fd,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('d', ';'); - eq('01239', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('d', ','); - eq('56789', cm.getValue()); -}, { value: '0123456789'}); -testVim('td,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('d', ';'); - eq('456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('d', ','); - eq('012349', cm.getValue()); -}, { value: '0123456789'}); -testVim('Td,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('d', ';'); - eq('012349', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('d', ','); - eq('456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('fc,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('c', ';', ''); - eq('56789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('c', ','); - eq('01239', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fc,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('c', ';', ''); - eq('01239', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('c', ','); - eq('56789', cm.getValue()); -}, { value: '0123456789'}); -testVim('tc,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('c', ';', ''); - eq('456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('c', ','); - eq('012349', cm.getValue()); -}, { value: '0123456789'}); -testVim('Tc,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('c', ';', ''); - eq('012349', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('c', ','); - eq('456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('fy,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('f', '4'); - cm.setCursor(0, 0); - helpers.doKeys('y', ';', 'P'); - eq('012340123456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('y', ',', 'P'); - eq('012345678456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('Fy,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('F', '4'); - cm.setCursor(0, 9); - helpers.doKeys('y', ';', 'p'); - eq('012345678945678', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('y', ',', 'P'); - eq('012340123456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('ty,;', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys('t', '4'); - cm.setCursor(0, 0); - helpers.doKeys('y', ';', 'P'); - eq('01230123456789', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 9); - helpers.doKeys('y', ',', 'p'); - eq('01234567895678', cm.getValue()); -}, { value: '0123456789'}); -testVim('Ty,;', function(cm, vim, helpers) { - cm.setCursor(0, 9); - helpers.doKeys('T', '4'); - cm.setCursor(0, 9); - helpers.doKeys('y', ';', 'p'); - eq('01234567895678', cm.getValue()); - helpers.doKeys('u'); - cm.setCursor(0, 0); - helpers.doKeys('y', ',', 'P'); - eq('01230123456789', cm.getValue()); -}, { value: '0123456789'}); -testVim('HML', function(cm, vim, helpers) { - var lines = 35; - var textHeight = cm.defaultTextHeight(); - cm.setSize(600, lines*textHeight); - cm.setCursor(120, 0); - helpers.doKeys('H'); - helpers.assertCursorAt(86, 2); - helpers.doKeys('L'); - helpers.assertCursorAt(120, 4); - helpers.doKeys('M'); - helpers.assertCursorAt(103,4); -}, { value: (function(){ - var lines = new Array(100); - var upper = ' xx\n'; - var lower = ' xx\n'; - upper = lines.join(upper); - lower = lines.join(lower); - return upper + lower; -})()}); - -var zVals = []; -forEach(['zb','zz','zt','z-','z.','z'], function(e, idx){ - var lineNum = 250; - var lines = 35; - testVim(e, function(cm, vim, helpers) { - var k1 = e[0]; - var k2 = e.substring(1); - var textHeight = cm.defaultTextHeight(); - cm.setSize(600, lines*textHeight); - cm.setCursor(lineNum, 0); - helpers.doKeys(k1, k2); - zVals[idx] = cm.getScrollInfo().top; - }, { value: (function(){ - return new Array(500).join('\n'); - })()}); -}); -testVim('zb', function(cm, vim, helpers){ - eq(zVals[2], zVals[5]); -}); - -var moveTillCharacterSandbox = - 'The quick brown fox \n' - 'jumped over the lazy dog.' -testVim('moveTillCharacter', function(cm, vim, helpers){ - cm.setCursor(0, 0); - // Search for the 'q'. - cm.openDialog = helpers.fakeOpenDialog('q'); - helpers.doKeys('/'); - eq(4, cm.getCursor().ch); - // Jump to just before the first o in the list. - helpers.doKeys('t'); - helpers.doKeys('o'); - eq('The quick brown fox \n', cm.getValue()); - // Delete that one character. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('o'); - eq('The quick bown fox \n', cm.getValue()); - // Delete everything until the next 'o'. - helpers.doKeys('.'); - eq('The quick box \n', cm.getValue()); - // An unmatched character should have no effect. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('q'); - eq('The quick box \n', cm.getValue()); - // Matches should only be possible on single lines. - helpers.doKeys('d'); - helpers.doKeys('t'); - helpers.doKeys('z'); - eq('The quick box \n', cm.getValue()); - // After all that, the search for 'q' should still be active, so the 'N' command - // can run it again in reverse. Use that to delete everything back to the 'q'. - helpers.doKeys('d'); - helpers.doKeys('N'); - eq('The ox \n', cm.getValue()); - eq(4, cm.getCursor().ch); -}, { value: moveTillCharacterSandbox}); -testVim('searchForPipe', function(cm, vim, helpers){ - CodeMirror.Vim.setOption('pcre', false); - cm.setCursor(0, 0); - // Search for the '|'. - cm.openDialog = helpers.fakeOpenDialog('|'); - helpers.doKeys('/'); - eq(4, cm.getCursor().ch); -}, { value: 'this|that'}); - - -var scrollMotionSandbox = - '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' - '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' - '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n' - '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'; -testVim('scrollMotion', function(cm, vim, helpers){ - var prevCursor, prevScrollInfo; - cm.setCursor(0, 0); - // ctrl-y at the top of the file should have no effect. - helpers.doKeys(''); - eq(0, cm.getCursor().line); - prevScrollInfo = cm.getScrollInfo(); - helpers.doKeys(''); - eq(1, cm.getCursor().line); - is(prevScrollInfo.top < cm.getScrollInfo().top); - // Jump to the end of the sandbox. - cm.setCursor(1000, 0); - prevCursor = cm.getCursor(); - // ctrl-e at the bottom of the file should have no effect. - helpers.doKeys(''); - eq(prevCursor.line, cm.getCursor().line); - prevScrollInfo = cm.getScrollInfo(); - helpers.doKeys(''); - eq(prevCursor.line - 1, cm.getCursor().line); - is(prevScrollInfo.top > cm.getScrollInfo().top); -}, { value: scrollMotionSandbox}); - -var squareBracketMotionSandbox = ''+ - '({\n'+//0 - ' ({\n'+//11 - ' /*comment {\n'+//2 - ' */(\n'+//3 - '#else \n'+//4 - ' /* )\n'+//5 - '#if }\n'+//6 - ' )}*/\n'+//7 - ')}\n'+//8 - '{}\n'+//9 - '#else {{\n'+//10 - '{}\n'+//11 - '}\n'+//12 - '{\n'+//13 - '#endif\n'+//14 - '}\n'+//15 - '}\n'+//16 - '#else';//17 -testVim('[[, ]]', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(']', ']'); - helpers.assertCursorAt(9,0); - helpers.doKeys('2', ']', ']'); - helpers.assertCursorAt(13,0); - helpers.doKeys(']', ']'); - helpers.assertCursorAt(17,0); - helpers.doKeys('[', '['); - helpers.assertCursorAt(13,0); - helpers.doKeys('2', '[', '['); - helpers.assertCursorAt(9,0); - helpers.doKeys('[', '['); - helpers.assertCursorAt(0,0); -}, { value: squareBracketMotionSandbox}); -testVim('[], ][', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doKeys(']', '['); - helpers.assertCursorAt(12,0); - helpers.doKeys('2', ']', '['); - helpers.assertCursorAt(16,0); - helpers.doKeys(']', '['); - helpers.assertCursorAt(17,0); - helpers.doKeys('[', ']'); - helpers.assertCursorAt(16,0); - helpers.doKeys('2', '[', ']'); - helpers.assertCursorAt(12,0); - helpers.doKeys('[', ']'); - helpers.assertCursorAt(0,0); -}, { value: squareBracketMotionSandbox}); -testVim('[{, ]}', function(cm, vim, helpers) { - cm.setCursor(4, 10); - helpers.doKeys('[', '{'); - helpers.assertCursorAt(2,12); - helpers.doKeys('2', '[', '{'); - helpers.assertCursorAt(0,1); - cm.setCursor(4, 10); - helpers.doKeys(']', '}'); - helpers.assertCursorAt(6,11); - helpers.doKeys('2', ']', '}'); - helpers.assertCursorAt(8,1); - cm.setCursor(0,1); - helpers.doKeys(']', '}'); - helpers.assertCursorAt(8,1); - helpers.doKeys('[', '{'); - helpers.assertCursorAt(0,1); -}, { value: squareBracketMotionSandbox}); -testVim('[(, ])', function(cm, vim, helpers) { - cm.setCursor(4, 10); - helpers.doKeys('[', '('); - helpers.assertCursorAt(3,14); - helpers.doKeys('2', '[', '('); - helpers.assertCursorAt(0,0); - cm.setCursor(4, 10); - helpers.doKeys(']', ')'); - helpers.assertCursorAt(5,11); - helpers.doKeys('2', ']', ')'); - helpers.assertCursorAt(8,0); - helpers.doKeys('[', '('); - helpers.assertCursorAt(0,0); - helpers.doKeys(']', ')'); - helpers.assertCursorAt(8,0); -}, { value: squareBracketMotionSandbox}); -testVim('[*, ]*, [/, ]/', function(cm, vim, helpers) { - forEach(['*', '/'], function(key){ - cm.setCursor(7, 0); - helpers.doKeys('2', '[', key); - helpers.assertCursorAt(2,2); - helpers.doKeys('2', ']', key); - helpers.assertCursorAt(7,5); - }); -}, { value: squareBracketMotionSandbox}); -testVim('[#, ]#', function(cm, vim, helpers) { - cm.setCursor(10, 3); - helpers.doKeys('2', '[', '#'); - helpers.assertCursorAt(4,0); - helpers.doKeys('5', ']', '#'); - helpers.assertCursorAt(17,0); - cm.setCursor(10, 3); - helpers.doKeys(']', '#'); - helpers.assertCursorAt(14,0); -}, { value: squareBracketMotionSandbox}); -testVim('[m, ]m, [M, ]M', function(cm, vim, helpers) { - cm.setCursor(11, 0); - helpers.doKeys('[', 'm'); - helpers.assertCursorAt(10,7); - helpers.doKeys('4', '[', 'm'); - helpers.assertCursorAt(1,3); - helpers.doKeys('5', ']', 'm'); - helpers.assertCursorAt(11,0); - helpers.doKeys('[', 'M'); - helpers.assertCursorAt(9,1); - helpers.doKeys('3', ']', 'M'); - helpers.assertCursorAt(15,0); - helpers.doKeys('5', '[', 'M'); - helpers.assertCursorAt(7,3); -}, { value: squareBracketMotionSandbox}); - -// Ex mode tests -testVim('ex_go_to_line', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('4'); - helpers.assertCursorAt(3, 0); -}, { value: 'a\nb\nc\nd\ne\n'}); -testVim('ex_write', function(cm, vim, helpers) { - var tmp = CodeMirror.commands.save; - var written; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - // Test that w, wr, wri ... write all trigger :write. - var command = 'write'; - for (var i = 1; i < command.length; i++) { - written = false; - actualCm = null; - helpers.doEx(command.substring(0, i)); - eq(written, true); - eq(actualCm, cm); - } - CodeMirror.commands.save = tmp; -}); -testVim('ex_sort', function(cm, vim, helpers) { - helpers.doEx('sort'); - eq('Z\na\nb\nc\nd', cm.getValue()); -}, { value: 'b\nZ\nd\nc\na'}); -testVim('ex_sort_reverse', function(cm, vim, helpers) { - helpers.doEx('sort!'); - eq('d\nc\nb\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_range', function(cm, vim, helpers) { - helpers.doEx('2,3sort'); - eq('b\nc\nd\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_oneline', function(cm, vim, helpers) { - helpers.doEx('2sort'); - // Expect no change. - eq('b\nd\nc\na', cm.getValue()); -}, { value: 'b\nd\nc\na'}); -testVim('ex_sort_ignoreCase', function(cm, vim, helpers) { - helpers.doEx('sort i'); - eq('a\nb\nc\nd\nZ', cm.getValue()); -}, { value: 'b\nZ\nd\nc\na'}); -testVim('ex_sort_unique', function(cm, vim, helpers) { - helpers.doEx('sort u'); - eq('Z\na\nb\nc\nd', cm.getValue()); -}, { value: 'b\nZ\na\na\nd\na\nc\na'}); -testVim('ex_sort_decimal', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('d3\n s5\n6\n.9', cm.getValue()); -}, { value: '6\nd3\n s5\n.9'}); -testVim('ex_sort_decimal_negative', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('z-9\nd3\n s5\n6\n.9', cm.getValue()); -}, { value: '6\nd3\n s5\n.9\nz-9'}); -testVim('ex_sort_decimal_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! d'); - eq('.9\n6\n s5\nd3', cm.getValue()); -}, { value: '6\nd3\n s5\n.9'}); -testVim('ex_sort_hex', function(cm, vim, helpers) { - helpers.doEx('sort x'); - eq(' s5\n6\n.9\n&0xB\nd3', cm.getValue()); -}, { value: '6\nd3\n s5\n&0xB\n.9'}); -testVim('ex_sort_octal', function(cm, vim, helpers) { - helpers.doEx('sort o'); - eq('.8\n.9\nd3\n s5\n6', cm.getValue()); -}, { value: '6\nd3\n s5\n.9\n.8'}); -testVim('ex_sort_decimal_mixed', function(cm, vim, helpers) { - helpers.doEx('sort d'); - eq('y\nz\nc1\nb2\na3', cm.getValue()); -}, { value: 'a3\nz\nc1\ny\nb2'}); -testVim('ex_sort_decimal_mixed_reverse', function(cm, vim, helpers) { - helpers.doEx('sort! d'); - eq('a3\nb2\nc1\nz\ny', cm.getValue()); -}, { value: 'a3\nz\nc1\ny\nb2'}); -// test for :global command -testVim('ex_global', function(cm, vim, helpers) { - cm.setCursor(0, 0); - helpers.doEx('g/one/s//two'); - eq('two two\n two two\n two two', cm.getValue()); - helpers.doEx('1,2g/two/s//one'); - eq('one one\n one one\n two two', cm.getValue()); -}, {value: 'one one\n one one\n one one'}); -testVim('ex_global_confirm', function(cm, vim, helpers) { - cm.setCursor(0, 0); - var onKeyDown; - var openDialogSave = cm.openDialog; - var KEYCODES = { - a: 65, - n: 78, - q: 81, - y: 89 - }; - // Intercept the ex command, 'global' - cm.openDialog = function(template, callback, options) { - // Intercept the prompt for the embedded ex command, 'substitute' - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - }; - callback('g/one/s//two/gc'); - }; - helpers.doKeys(':'); - var close = function() {}; - onKeyDown({keyCode: KEYCODES.n}, '', close); - onKeyDown({keyCode: KEYCODES.y}, '', close); - onKeyDown({keyCode: KEYCODES.a}, '', close); - onKeyDown({keyCode: KEYCODES.q}, '', close); - onKeyDown({keyCode: KEYCODES.y}, '', close); - eq('one two\n two two\n one one\n two one\n one one', cm.getValue()); -}, {value: 'one one\n one one\n one one\n one one\n one one'}); -// Basic substitute tests. -testVim('ex_substitute_same_line', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('s/one/two/g'); - eq('one one\n two two', cm.getValue()); -}, { value: 'one one\n one one'}); -testVim('ex_substitute_full_file', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('%s/one/two/g'); - eq('two two\n two two', cm.getValue()); -}, { value: 'one one\n one one'}); -testVim('ex_substitute_input_range', function(cm, vim, helpers) { - cm.setCursor(1, 0); - helpers.doEx('1,3s/\\d/0/g'); - eq('0\n0\n0\n4', cm.getValue()); -}, { value: '1\n2\n3\n4' }); -testVim('ex_substitute_visual_range', function(cm, vim, helpers) { - cm.setCursor(1, 0); - // Set last visual mode selection marks '< and '> at lines 2 and 4 - helpers.doKeys('V', '2', 'j', 'v'); - helpers.doEx('\'<,\'>s/\\d/0/g'); - eq('1\n0\n0\n0\n5', cm.getValue()); -}, { value: '1\n2\n3\n4\n5' }); -testVim('ex_substitute_empty_query', function(cm, vim, helpers) { - // If the query is empty, use last query. - cm.setCursor(1, 0); - cm.openDialog = helpers.fakeOpenDialog('1'); - helpers.doKeys('/'); - helpers.doEx('s//b/g'); - eq('abb ab2 ab3', cm.getValue()); -}, { value: 'a11 a12 a13' }); -testVim('ex_substitute_javascript', function(cm, vim, helpers) { - CodeMirror.Vim.setOption('pcre', false); - cm.setCursor(1, 0); - // Throw all the things that javascript likes to treat as special values - // into the replace part. All should be literal (this is VIM). - helpers.doEx('s/\\(\\d+\\)/$$ $\' $` $& \\1/g') - eq('a $$ $\' $` $& 0 b', cm.getValue()); -}, { value: 'a 0 b' }); -testVim('ex_substitute_empty_arguments', function(cm,vim,helpers) { - cm.setCursor(0, 0); - helpers.doEx('s/a/b/g'); - cm.setCursor(1, 0); - helpers.doEx('s'); - eq('b b\nb a', cm.getValue()); -}, {value: 'a a\na a'}); - -// More complex substitute tests that test both pcre and nopcre options. -function testSubstitute(name, options) { - testVim(name + '_pcre', function(cm, vim, helpers) { - cm.setCursor(1, 0); - CodeMirror.Vim.setOption('pcre', true); - helpers.doEx(options.expr); - eq(options.expectedValue, cm.getValue()); - }, options); - // If no noPcreExpr is defined, assume that it's the same as the expr. - var noPcreExpr = options.noPcreExpr ? options.noPcreExpr : options.expr; - testVim(name + '_nopcre', function(cm, vim, helpers) { - cm.setCursor(1, 0); - CodeMirror.Vim.setOption('pcre', false); - helpers.doEx(noPcreExpr); - eq(options.expectedValue, cm.getValue()); - }, options); -} -testSubstitute('ex_substitute_capture', { - value: 'a11 a12 a13', - expectedValue: 'a1111 a1212 a1313', - // $n is a backreference - expr: 's/(\\d+)/$1$1/g', - // \n is a backreference. - noPcreExpr: 's/\\(\\d+\\)/\\1\\1/g'}); -testSubstitute('ex_substitute_capture2', { - value: 'a 0 b', - expectedValue: 'a $00 b', - expr: 's/(\\d+)/$$$1$1/g', - noPcreExpr: 's/\\(\\d+\\)/$\\1\\1/g'}); -testSubstitute('ex_substitute_nocapture', { - value: 'a11 a12 a13', - expectedValue: 'a$1$1 a$1$1 a$1$1', - expr: 's/(\\d+)/$$1$$1/g', - noPcreExpr: 's/\\(\\d+\\)/$1$1/g'}); -testSubstitute('ex_substitute_nocapture2', { - value: 'a 0 b', - expectedValue: 'a $10 b', - expr: 's/(\\d+)/$$1$1/g', - noPcreExpr: 's/\\(\\d+\\)/\\$1\\1/g'}); -testSubstitute('ex_substitute_nocapture', { - value: 'a b c', - expectedValue: 'a $ c', - expr: 's/b/$$/', - noPcreExpr: 's/b/$/'}); -testSubstitute('ex_substitute_slash_regex', { - value: 'one/two \n three/four', - expectedValue: 'one|two \n three|four', - expr: '%s/\\//|'}); -testSubstitute('ex_substitute_pipe_regex', { - value: 'one|two \n three|four', - expectedValue: 'one,two \n three,four', - expr: '%s/\\|/,/', - noPcreExpr: '%s/|/,/'}); -testSubstitute('ex_substitute_or_regex', { - value: 'one|two \n three|four', - expectedValue: 'ana|twa \n thraa|faar', - expr: '%s/o|e|u/a/g', - noPcreExpr: '%s/o\\|e\\|u/a/g'}); -testSubstitute('ex_substitute_or_word_regex', { - value: 'one|two \n three|four', - expectedValue: 'five|five \n three|four', - expr: '%s/(one|two)/five/g', - noPcreExpr: '%s/\\(one\\|two\\)/five/g'}); -testSubstitute('ex_substitute_backslashslash_regex', { - value: 'one\\two \n three\\four', - expectedValue: 'one,two \n three,four', - expr: '%s/\\\\/,'}); -testSubstitute('ex_substitute_slash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one/two \n three/four', - expr: '%s/,/\\/'}); -testSubstitute('ex_substitute_backslash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one\\two \n three\\four', - expr: '%s/,/\\\\/g'}); -testSubstitute('ex_substitute_multibackslash_replacement', { - value: 'one,two \n three,four', - expectedValue: 'one\\\\\\\\two \n three\\\\\\\\four', // 2*8 backslashes. - expr: '%s/,/\\\\\\\\\\\\\\\\/g'}); // 16 backslashes. -testSubstitute('ex_substitute_braces_word', { - value: 'ababab abb ab{2}', - expectedValue: 'ab abb ab{2}', - expr: '%s/(ab){2}//g', - noPcreExpr: '%s/\\(ab\\)\\{2\\}//g'}); -testSubstitute('ex_substitute_braces_range', { - value: 'a aa aaa aaaa', - expectedValue: 'a a', - expr: '%s/a{2,3}//g', - noPcreExpr: '%s/a\\{2,3\\}//g'}); -testSubstitute('ex_substitute_braces_literal', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab abb ', - expr: '%s/ab\\{2\\}//g', - noPcreExpr: '%s/ab{2}//g'}); -testSubstitute('ex_substitute_braces_char', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab ab{2}', - expr: '%s/ab{2}//g', - noPcreExpr: '%s/ab\\{2\\}//g'}); -testSubstitute('ex_substitute_braces_no_escape', { - value: 'ababab abb ab{2}', - expectedValue: 'ababab ab{2}', - expr: '%s/ab{2}//g', - noPcreExpr: '%s/ab\\{2}//g'}); -testSubstitute('ex_substitute_count', { - value: '1\n2\n3\n4', - expectedValue: '1\n0\n0\n4', - expr: 's/\\d/0/i 2'}); -testSubstitute('ex_substitute_count_with_range', { - value: '1\n2\n3\n4', - expectedValue: '1\n2\n0\n0', - expr: '1,3s/\\d/0/ 3'}); -testSubstitute('ex_substitute_not_global', { - value: 'aaa\nbaa\ncaa', - expectedValue: 'xaa\nbxa\ncxa', - expr: '%s/a/x/'}); -function testSubstituteConfirm(name, command, initialValue, expectedValue, keys, finalPos) { - testVim(name, function(cm, vim, helpers) { - var savedOpenDialog = cm.openDialog; - var savedKeyName = CodeMirror.keyName; - var onKeyDown; - var recordedCallback; - var closed = true; // Start out closed, set false on second openDialog. - function close() { - closed = true; - } - // First openDialog should save callback. - cm.openDialog = function(template, callback, options) { - recordedCallback = callback; - } - // Do first openDialog. - helpers.doKeys(':'); - // Second openDialog should save keyDown handler. - cm.openDialog = function(template, callback, options) { - onKeyDown = options.onKeyDown; - closed = false; - }; - // Return the command to Vim and trigger second openDialog. - recordedCallback(command); - // The event should really use keyCode, but here just mock it out and use - // key and replace keyName to just return key. - CodeMirror.keyName = function (e) { return e.key; } - keys = keys.toUpperCase(); - for (var i = 0; i < keys.length; i++) { - is(!closed); - onKeyDown({ key: keys.charAt(i) }, '', close); - } - try { - eq(expectedValue, cm.getValue()); - helpers.assertCursorAt(finalPos); - is(closed); - } catch(e) { - throw e - } finally { - // Restore overriden functions. - CodeMirror.keyName = savedKeyName; - cm.openDialog = savedOpenDialog; - } - }, { value: initialValue }); -}; -testSubstituteConfirm('ex_substitute_confirm_emptydoc', - '%s/x/b/c', '', '', '', makeCursor(0, 0)); -testSubstituteConfirm('ex_substitute_confirm_nomatch', - '%s/x/b/c', 'ba a\nbab', 'ba a\nbab', '', makeCursor(0, 0)); -testSubstituteConfirm('ex_substitute_confirm_accept', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'yyy', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_random_keys', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ysdkywerty', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_some', - '%s/a/b/cg', 'ba a\nbab', 'bb a\nbbb', 'yny', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_all', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'a', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_accept_then_all', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbbb', 'ya', makeCursor(1, 1)); -testSubstituteConfirm('ex_substitute_confirm_quit', - '%s/a/b/cg', 'ba a\nbab', 'bb a\nbab', 'yq', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_last', - '%s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_oneline', - '1s/a/b/cg', 'ba a\nbab', 'bb b\nbab', 'yl', makeCursor(0, 3)); -testSubstituteConfirm('ex_substitute_confirm_range_accept', - '1,2s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyy', makeCursor(1, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_some', - '1,3s/a/b/cg', 'aa\na \na\na', 'ba\nb \nb\na', 'ynyy', makeCursor(2, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_all', - '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \nb\na', 'a', makeCursor(2, 0)); -testSubstituteConfirm('ex_substitute_confirm_range_last', - '1,3s/a/b/cg', 'aa\na \na\na', 'bb\nb \na\na', 'yyl', makeCursor(1, 0)); -//:noh should clear highlighting of search-results but allow to resume search through n -testVim('ex_noh_clearSearchHighlight', function(cm, vim, helpers) { - cm.openDialog = helpers.fakeOpenDialog('match'); - helpers.doKeys('?'); - helpers.doEx('noh'); - eq(vim.searchState_.getOverlay(),null,'match-highlighting wasn\'t cleared'); - helpers.doKeys('n'); - helpers.assertCursorAt(0, 11,'can\'t resume search after clearing highlighting'); -}, { value: 'match nope match \n nope Match' }); -testVim('set_boolean', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', true, 'boolean'); - // Test default value is set. - is(CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set to non-boolean - CodeMirror.Vim.setOption('testoption', '5'); - fail(); - } catch (expected) {}; - // Test setOption - CodeMirror.Vim.setOption('testoption', false); - is(!CodeMirror.Vim.getOption('testoption')); -}); -testVim('ex_set_boolean', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', true, 'boolean'); - // Test default value is set. - is(CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set to non-boolean - helpers.doEx('set testoption=22'); - fail(); - } catch (expected) {}; - // Test setOption - helpers.doEx('set notestoption'); - is(!CodeMirror.Vim.getOption('testoption')); -}); -testVim('set_string', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', 'a', 'string'); - // Test default value is set. - eq('a', CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set non-string. - CodeMirror.Vim.setOption('testoption', true); - fail(); - } catch (expected) {}; - try { - // Test fail to set 'notestoption' - CodeMirror.Vim.setOption('notestoption', 'b'); - fail(); - } catch (expected) {}; - // Test setOption - CodeMirror.Vim.setOption('testoption', 'c'); - eq('c', CodeMirror.Vim.getOption('testoption')); -}); -testVim('ex_set_string', function(cm, vim, helpers) { - CodeMirror.Vim.defineOption('testoption', 'a', 'string'); - // Test default value is set. - eq('a', CodeMirror.Vim.getOption('testoption')); - try { - // Test fail to set 'notestoption' - helpers.doEx('set notestoption=b'); - fail(); - } catch (expected) {}; - // Test setOption - helpers.doEx('set testoption=c') - eq('c', CodeMirror.Vim.getOption('testoption')); -}); -// TODO: Reset key maps after each test. -testVim('ex_map_key2key', function(cm, vim, helpers) { - helpers.doEx('map a x'); - helpers.doKeys('a'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); -}, { value: 'abc' }); -testVim('ex_unmap_key2key', function(cm, vim, helpers) { - helpers.doEx('unmap a'); - helpers.doKeys('a'); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'abc' }); -testVim('ex_unmap_key2key_does_not_remove_default', function(cm, vim, helpers) { - try { - helpers.doEx('unmap a'); - fail(); - } catch (expected) {} - helpers.doKeys('a'); - eq('vim-insert', cm.getOption('keyMap')); -}, { value: 'abc' }); -testVim('ex_map_key2key_to_colon', function(cm, vim, helpers) { - helpers.doEx('map ; :'); - var dialogOpened = false; - cm.openDialog = function() { - dialogOpened = true; - } - helpers.doKeys(';'); - eq(dialogOpened, true); -}); -testVim('ex_map_ex2key:', function(cm, vim, helpers) { - helpers.doEx('map :del x'); - helpers.doEx('del'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); -}, { value: 'abc' }); -testVim('ex_map_ex2ex', function(cm, vim, helpers) { - helpers.doEx('map :del :w'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - helpers.doEx('del'); - CodeMirror.commands.save = tmp; - eq(written, true); - eq(actualCm, cm); -}); -testVim('ex_map_key2ex', function(cm, vim, helpers) { - helpers.doEx('map a :w'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - helpers.doKeys('a'); - CodeMirror.commands.save = tmp; - eq(written, true); - eq(actualCm, cm); -}); -testVim('ex_map_key2key_visual_api', function(cm, vim, helpers) { - CodeMirror.Vim.map('b', ':w', 'visual'); - var tmp = CodeMirror.commands.save; - var written = false; - var actualCm; - CodeMirror.commands.save = function(cm) { - written = true; - actualCm = cm; - }; - // Mapping should not work in normal mode. - helpers.doKeys('b'); - eq(written, false); - // Mapping should work in visual mode. - helpers.doKeys('v', 'b'); - eq(written, true); - eq(actualCm, cm); - - CodeMirror.commands.save = tmp; -}); -testVim('ex_imap', function(cm, vim, helpers) { - CodeMirror.Vim.map('jk', '', 'insert'); - helpers.doKeys('i'); - is(vim.insertMode); - helpers.doKeys('j', 'k'); - is(!vim.insertMode); -}) - -// Testing registration of functions as ex-commands and mapping to -keys -testVim('ex_api_test', function(cm, vim, helpers) { - var res=false; - var val='from'; - CodeMirror.Vim.defineEx('extest','ext',function(cm,params){ - if(params.args)val=params.args[0]; - else res=true; - }); - helpers.doEx(':ext to'); - eq(val,'to','Defining ex-command failed'); - CodeMirror.Vim.map('',':ext'); - helpers.doKeys('',''); - is(res,'Mapping to key failed'); -}); -// For now, this test needs to be last because it messes up : for future tests. -testVim('ex_map_key2key_from_colon', function(cm, vim, helpers) { - helpers.doEx('map : x'); - helpers.doKeys(':'); - helpers.assertCursorAt(0, 0); - eq('bc', cm.getValue()); -}, { value: 'abc' }); - -// Test event handlers -testVim('beforeSelectionChange', function(cm, vim, helpers) { - cm.setCursor(0, 100); - eqPos(cm.getCursor('head'), cm.getCursor('anchor')); -}, { value: 'abc' }); - - diff --git a/public/js/lib/codemirror/theme/3024-day.css b/public/js/lib/codemirror/theme/3024-day.css deleted file mode 100644 index 3c01c2bf58..0000000000 --- a/public/js/lib/codemirror/theme/3024-day.css +++ /dev/null @@ -1,38 +0,0 @@ -/* - - Name: 3024 day - Author: Jan T. Sott (http://github.com/idleberg) - - CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) - Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) - -*/ - -.cm-s-3024-day.CodeMirror {background: #f7f7f7; color: #3a3432;} -.cm-s-3024-day div.CodeMirror-selected {background: #d6d5d4 !important;} - -.cm-s-3024-day .CodeMirror-gutters {background: #f7f7f7; border-right: 0px;} -.cm-s-3024-day .CodeMirror-guttermarker { color: #db2d20; } -.cm-s-3024-day .CodeMirror-guttermarker-subtle { color: #807d7c; } -.cm-s-3024-day .CodeMirror-linenumber {color: #807d7c;} - -.cm-s-3024-day .CodeMirror-cursor {border-left: 1px solid #5c5855 !important;} - -.cm-s-3024-day span.cm-comment {color: #cdab53;} -.cm-s-3024-day span.cm-atom {color: #a16a94;} -.cm-s-3024-day span.cm-number {color: #a16a94;} - -.cm-s-3024-day span.cm-property, .cm-s-3024-day span.cm-attribute {color: #01a252;} -.cm-s-3024-day span.cm-keyword {color: #db2d20;} -.cm-s-3024-day span.cm-string {color: #fded02;} - -.cm-s-3024-day span.cm-variable {color: #01a252;} -.cm-s-3024-day span.cm-variable-2 {color: #01a0e4;} -.cm-s-3024-day span.cm-def {color: #e8bbd0;} -.cm-s-3024-day span.cm-bracket {color: #3a3432;} -.cm-s-3024-day span.cm-tag {color: #db2d20;} -.cm-s-3024-day span.cm-link {color: #a16a94;} -.cm-s-3024-day span.cm-error {background: #db2d20; color: #5c5855;} - -.cm-s-3024-day .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-3024-day .CodeMirror-matchingbracket { text-decoration: underline; color: #a16a94 !important;} diff --git a/public/js/lib/codemirror/theme/3024-night.css b/public/js/lib/codemirror/theme/3024-night.css deleted file mode 100644 index 631757fc47..0000000000 --- a/public/js/lib/codemirror/theme/3024-night.css +++ /dev/null @@ -1,37 +0,0 @@ -/* - - Name: 3024 night - Author: Jan T. Sott (http://github.com/idleberg) - - CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) - Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) - -*/ - -.cm-s-3024-night.CodeMirror {background: #090300; color: #d6d5d4;} -.cm-s-3024-night div.CodeMirror-selected {background: #3a3432 !important;} -.cm-s-3024-night .CodeMirror-gutters {background: #090300; border-right: 0px;} -.cm-s-3024-night .CodeMirror-guttermarker { color: #db2d20; } -.cm-s-3024-night .CodeMirror-guttermarker-subtle { color: #5c5855; } -.cm-s-3024-night .CodeMirror-linenumber {color: #5c5855;} - -.cm-s-3024-night .CodeMirror-cursor {border-left: 1px solid #807d7c !important;} - -.cm-s-3024-night span.cm-comment {color: #cdab53;} -.cm-s-3024-night span.cm-atom {color: #a16a94;} -.cm-s-3024-night span.cm-number {color: #a16a94;} - -.cm-s-3024-night span.cm-property, .cm-s-3024-night span.cm-attribute {color: #01a252;} -.cm-s-3024-night span.cm-keyword {color: #db2d20;} -.cm-s-3024-night span.cm-string {color: #fded02;} - -.cm-s-3024-night span.cm-variable {color: #01a252;} -.cm-s-3024-night span.cm-variable-2 {color: #01a0e4;} -.cm-s-3024-night span.cm-def {color: #e8bbd0;} -.cm-s-3024-night span.cm-bracket {color: #d6d5d4;} -.cm-s-3024-night span.cm-tag {color: #db2d20;} -.cm-s-3024-night span.cm-link {color: #a16a94;} -.cm-s-3024-night span.cm-error {background: #db2d20; color: #807d7c;} - -.cm-s-3024-night .CodeMirror-activeline-background {background: #2F2F2F !important;} -.cm-s-3024-night .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/ambiance-mobile.css b/public/js/lib/codemirror/theme/ambiance-mobile.css deleted file mode 100644 index 88d332e1a7..0000000000 --- a/public/js/lib/codemirror/theme/ambiance-mobile.css +++ /dev/null @@ -1,5 +0,0 @@ -.cm-s-ambiance.CodeMirror { - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} diff --git a/public/js/lib/codemirror/theme/ambiance.css b/public/js/lib/codemirror/theme/ambiance.css deleted file mode 100644 index c844566eac..0000000000 --- a/public/js/lib/codemirror/theme/ambiance.css +++ /dev/null @@ -1,77 +0,0 @@ -/* ambiance theme for codemirror */ - -/* Color scheme */ - -.cm-s-ambiance .cm-keyword { color: #cda869; } -.cm-s-ambiance .cm-atom { color: #CF7EA9; } -.cm-s-ambiance .cm-number { color: #78CF8A; } -.cm-s-ambiance .cm-def { color: #aac6e3; } -.cm-s-ambiance .cm-variable { color: #ffb795; } -.cm-s-ambiance .cm-variable-2 { color: #eed1b3; } -.cm-s-ambiance .cm-variable-3 { color: #faded3; } -.cm-s-ambiance .cm-property { color: #eed1b3; } -.cm-s-ambiance .cm-operator {color: #fa8d6a;} -.cm-s-ambiance .cm-comment { color: #555; font-style:italic; } -.cm-s-ambiance .cm-string { color: #8f9d6a; } -.cm-s-ambiance .cm-string-2 { color: #9d937c; } -.cm-s-ambiance .cm-meta { color: #D2A8A1; } -.cm-s-ambiance .cm-qualifier { color: yellow; } -.cm-s-ambiance .cm-builtin { color: #9999cc; } -.cm-s-ambiance .cm-bracket { color: #24C2C7; } -.cm-s-ambiance .cm-tag { color: #fee4ff } -.cm-s-ambiance .cm-attribute { color: #9B859D; } -.cm-s-ambiance .cm-header {color: blue;} -.cm-s-ambiance .cm-quote { color: #24C2C7; } -.cm-s-ambiance .cm-hr { color: pink; } -.cm-s-ambiance .cm-link { color: #F4C20B; } -.cm-s-ambiance .cm-special { color: #FF9D00; } -.cm-s-ambiance .cm-error { color: #AF2018; } - -.cm-s-ambiance .CodeMirror-matchingbracket { color: #0f0; } -.cm-s-ambiance .CodeMirror-nonmatchingbracket { color: #f22; } - -.cm-s-ambiance .CodeMirror-selected { - background: rgba(255, 255, 255, 0.15); -} -.cm-s-ambiance.CodeMirror-focused .CodeMirror-selected { - background: rgba(255, 255, 255, 0.10); -} - -/* Editor styling */ - -.cm-s-ambiance.CodeMirror { - line-height: 1.40em; - color: #E6E1DC; - background-color: #202020; - -webkit-box-shadow: inset 0 0 10px black; - -moz-box-shadow: inset 0 0 10px black; - box-shadow: inset 0 0 10px black; -} - -.cm-s-ambiance .CodeMirror-gutters { - background: #3D3D3D; - border-right: 1px solid #4D4D4D; - box-shadow: 0 10px 20px black; -} - -.cm-s-ambiance .CodeMirror-linenumber { - text-shadow: 0px 1px 1px #4d4d4d; - color: #111; - padding: 0 5px; -} - -.cm-s-ambiance .CodeMirror-guttermarker { color: #aaa; } -.cm-s-ambiance .CodeMirror-guttermarker-subtle { color: #111; } - -.cm-s-ambiance .CodeMirror-lines .CodeMirror-cursor { - border-left: 1px solid #7991E8; -} - -.cm-s-ambiance .CodeMirror-activeline-background { - background: none repeat scroll 0% 0% rgba(255, 255, 255, 0.031); -} - -.cm-s-ambiance.CodeMirror, -.cm-s-ambiance .CodeMirror-gutters { - background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAQAAAAHUWYVAABFFUlEQVQYGbzBCeDVU/74/6fj9HIcx/FRHx9JCFmzMyGRURhLZIkUsoeRfUjS2FNDtr6WkMhO9sm+S8maJfu+Jcsg+/o/c+Z4z/t97/vezy3z+z8ekGlnYICG/o7gdk+wmSHZ1z4pJItqapjoKXWahm8NmV6eOTbWUOp6/6a/XIg6GQqmenJ2lDHyvCFZ2cBDbmtHA043VFhHwXxClWmeYAdLhV00Bd85go8VmaFCkbVkzlQENzfBDZ5gtN7HwF0KDrTwJ0dypSOzpaKCMwQHKTIreYIxlmhXTzTWkVm+LTynZhiSBT3RZQ7aGfjGEd3qyXQ1FDymqbKxpspERQN2MiRjNZlFFQXfCNFm9nM1zpAsoYjmtRTc5ajwuaXc5xrWskT97RaKzAGe5ARHhVUsDbjKklziiX5WROcJwSNCNI+9w1Jwv4Zb2r7lCMZ4oq5C0EdTx+2GzNuKpJ+iFf38JEWkHJn9DNF7mmBDITrWEg0VWL3pHU20tSZnuqWu+R3BtYa8XxV1HO7GyD32UkOpL/yDloINFTmvtId+nmAjxRw40VMwVKiwrKLE4bK5UOVntYwhOcSSXKrJHKPJedocpGjVz/ZMIbnYUPB10/eKCrs5apqpgVmWzBYWpmtKHecJPjaUuEgRDDaU0oZghCJ6zNMQ5ZhDYx05r5v2muQdM0EILtXUsaKiQX9WMEUotagQzFbUNN6NUPC2nm5pxEWGCjMc3GdJHjSU2kORLK/JGSrkfGEIjncU/CYUnOipoYemwj8tST9NsJmB7TUVXtbUtXATJVZXBMvYeTXJfobgJUPmGMP/yFaWonaa6BcFO3nqcIqCozSZoZoSr1g4zJOzuyGnxTEX3lUEJ7WcZgme8ddaWvWJo2AJR9DZU3CUIbhCSG6ybSwN6qtJVnCU2svDTP2ZInOw2cBTrqtQahtNZn9NcJ4l2NaSmSkkP1noZWnVwkLmdUPOwLZEwy2Z3S3R+4rIG9hcbpPXHFVWcQdZkn2FOta3cKWQnNRC5g1LsJah4GCzSVsKnCOY5OAFRTBekyyryeyilhFKva75r4Mc0aWanGEaThcy31s439KKxTzJYY5WTHPU1FtIHjQU3Oip4xlNzj/lBw23dYZVliQa7WAXf4shetcQfatI+jWRDBPmyNeW6A1P5kdDgyYJlba0BIM8BZu1JfrFwItyjcAMR3K0BWOIrtMEXyhyrlVEx3ui5dUBjmB/Q3CXW85R4mBD0s7B+4q5tKUjOlb9qqmhi5AZ6GFIC5HXtOobdYGlVdMVbNJ8toNTFcHxnoL+muBagcctjWnbNMuR00uI7nQESwg5q2qqrKWIfrNUmeQocY6HuyxJV02wj36w00yhpmUFenv4p6fUkZYqLyuinx2RGOjhCXYyJF84oiU00YMOOhhquNdfbOB7gU88pY4xJO8LVdp6/q2voeB4R04vIdhSE40xZObx1HGGJ/ja0LBthFInKaLPPFzuCaYaoj8JjPME8yoyxo6zlBqkiUZYgq00OYMswbWO5NGmq+xhipxHLRW29ARjNKXO0wRnear8XSg4XFPLKEPUS1GqvyLwiuBUoa7zpZ0l5xxFwWmWZC1H5h5FwU8eQ7K+g8UcVY6TMQreVQT/8uQ8Z+ALIXnSEa2pYZQneE9RZbSBNYXfWYJzW/h/4j4Dp1tYVcFIC5019Vyi4ThPqSFCzjGWaHQTBU8q6vrVwgxP9Lkm840imWKpcLCjYTtrKuwvsKSnrvHCXGkSMk9p6lhckfRpIeis+N2PiszT+mFLspyGleUhDwcLrZqmyeylxwjBcKHEapqkmyangyLZRVOijwOtCY5SsG5zL0OwlCJ4y5KznF3EUNDDrinwiyLZRzOXtlBbK5ITHFGLp8Q0R6ab6mS7enI2cFrxOyHvOCFaT1HThS1krjCwqWeurCkk+willhCC+RSZnRXBiZaC5RXRIZYKp2lyfrHwiKPKR0JDzrdU2EFgpidawlFDR6FgXUMNa+g1FY3bUQh2cLCwosRdnuQTS/S+JVrGLeWIvtQUvONJxlqSQYYKpwoN2kaocLjdVsis4Mk80ESF2YpSkzwldjHkjFCUutI/r+EHDU8oCs6yzL3PhWiEooZdFMkymlas4AcI3KmoMMNSQ3tHzjGWCrcJJdYyZC7QFGwjRL9p+MrRkAGWzIaWCn9W0F3TsK01c2ZvQw0byvxuQU0r1lM0qJO7wW0kRIMdDTtXEdzi4VIh+EoIHm0mWtAtpCixlabgn83fKTI7anJe9ST7WIK1DMGpQmYeA58ImV6ezOGOzK2Kgq01pd60cKWiUi9Lievb/0vIDPHQ05Kzt4ddPckQBQtoaurjyHnek/nKzpQLrVgKPjIkh2v4uyezpv+Xoo7fPFXaGFp1vaLKxQ4uUpQQS5VuQs7BCq4xRJv7fwpVvvFEB3j+620haOuocqMhWd6TTPAEx+mdFNGHdranFe95WrWmIvlY4F1Dle2ECgc6cto7SryuqGGGha0tFQ5V53migUKmg6XKAo4qS3mik+0OZpAhOLeZKicacgaYcyx5hypYQE02ZA4xi/pNhOQxR4klNKyqacj+mpxnLTnnGSo85++3ZCZq6lrZkXlGEX3o+C9FieccJbZWVFjC0Yo1FZnJhoYMFoI1hEZ9r6hwg75HwzBNhbZCdJEfJwTPGzJvaKImw1yYX1HDAmpXR+ZJQ/SmgqMNVQb5vgamGwLtt7VwvP7Qk1xpiM5x5Cyv93E06MZmgs0Nya2azIKOYKCGBQQW97RmhKNKF02JZqHEJ4o58qp7X5EcZmc56trXEqzjCBZ1MFGR87Ql2tSTs6CGxS05PTzRQorkbw7aKoKXFDXsYW42VJih/q+FP2BdTzDTwVqOYB13liM50vG7wy28qagyuIXMeQI/Oqq8bcn5wJI50xH00CRntyfpL1T4hydYpoXgNiFzoIUTDZnLNRzh4TBHwbYGDvZkxmlyJloyr6tRihpeUG94GnKtIznREF0tzJG/OOr73JBcrSh1k6WuTprgLU+mnSGnv6Zge0NNz+kTDdH8nuAuTdJDCNb21LCiIuqlYbqGzT3RAoZofQfjFazkqeNWdYaGvYTM001EW2oKPvVk1ldUGSgUtHFwjKM1h9jnFcmy5lChoLNaQMGGDsYbKixlaMBmmsx1QjCfflwTfO/gckW0ruZ3jugKR3R5W9hGUWqCgxuFgsuaCHorotGKzGaeZB9DMsaTnKCpMtwTvOzhYk0rdrArKCqcaWmVk1+F372ur1YkKxgatI8Qfe1gIX9wE9FgS8ESmuABIXnRUbCapcKe+nO7slClSZFzpV/LkLncEb1qiO42fS3R855Su2mCLh62t1SYZZYVmKwIHjREF2uihTzB20JOkz7dkxzYQnK0UOU494wh+VWRc6Un2kpTaVgLDFEkJ/uhzRcI0YKGgpGWOlocBU/a4fKoJ/pEaNV6jip3+Es9VXY078rGnmAdf7t9ylPXS34RBSuYPs1UecZTU78WanhBCHpZ5sAoTz0LGZKjPf9TRypqWEiTvOFglL1fCEY3wY/++rbk7C8bWebA6p6om6PgOL2kp44TFJlVNBXae2rqqdZztOJpT87GQsE9jqCPIe9VReZuQ/CIgacsyZdCpIScSYqcZk8r+nsyCzhyfhOqHGOIvrLknC8wTpFcaYiGC/RU1NRbUeUpocQOnkRpGOrIOcNRx+1uA0UrzhSSt+VyS3SJpnFWkzNDqOFGIWcfR86DnmARTQ1HKIL33ExPiemeOhYSSjzlSUZZuE4TveoJLnBUOFof6KiysCbnAEcZgcUNTDOwkqWu3RWtmGpZwlHhJENdZ3miGz0lJlsKnjbwqSHQjpxnFDlTLLwqJPMZMjd7KrzkSG7VsxXBZE+F8YZkb01Oe00yyRK9psh5SYh29ySPKBo2ylNht7ZkZnsKenjKNJu9PNEyZpaCHv4Kt6RQsLvAVp7M9kIimmCUwGeWqLMmGuIotYMmWNpSahkhZw9FqZsVnKJhsjAHvtHMsTM9fCI06Dx/u3vfUXCqfsKRc4oFY2jMsoo/7DJDwZ1CsIKnJu+J9ldkpmiCxQx1rWjI+T9FwcWWzOuaYH0Hj7klNRVWEQpmaqosakiGNTFHdjS/qnUdmf0NJW5xsL0HhimCCZZSRzmSPTXJQ4aaztAwtZnoabebJ+htCaZ7Cm535ByoqXKbX1WRc4Eh2MkRXWzImVc96Cj4VdOKVxR84VdQsIUM8Psoou2byVHyZFuq7O8otbSQ2UAoeEWTudATLGSpZzVLlXVkPU2Jc+27lsw2jmg5T5VhbeE3BT083K9WsTTkFU/Osi0rC5lRlpwRHUiesNS0sOvmqGML1aRbPAxTJD9ZKtxuob+hhl8cwYGWpJ8nub7t5p6coYbMovZ1BTdaKn1jYD6h4GFDNFyT/Kqe1XCXphXHOKLZmuRSRdBPEfVUXQzJm5YGPGGJdvAEr7hHNdGZnuBvrpciGmopOLf5N0uVMy0FfYToJk90uUCbJupaVpO53UJXR2bVpoU00V2KOo4zMFrBd0Jtz2pa0clT5Q5L8IpQ177mWQejPMEJhuQjS10ref6HHjdEhy1P1EYR7GtO0uSsKJQYLiTnG1rVScj5lyazpqWGl5uBbRWl7m6ixGOOnEsMJR7z8J0n6KMnCdxhiNYQCoZ6CmYLnO8omC3MkW3bktlPmEt/VQQHejL3+dOE5FlPdK/Mq8hZxxJtLyRrepLThYKbLZxkSb5W52vYxNOaOxUF0yxMUPwBTYqCzy01XayYK0sJyWBLqX0MwU5CzoymRzV0EjjeUeLgDpTo6ij42ZAzvD01dHUUTPLU96MdLbBME8nFBn7zJCMtJcZokn8YoqU0FS5WFKyniHobguMcmW8N0XkWZjkyN3hqOMtS08r+/xTBwpZSZ3qiVRX8SzMHHjfUNFjgHEPmY9PL3ykEzxkSre/1ZD6z/NuznuB0RcE1TWTm9zRgfUWVJiG6yrzgmWPXC8EAR4Wxhlad0ZbgQyEz3pG5RVEwwDJH2mgKpjcTiCOzn1lfUWANFbZ2BA8balnEweJC9J0iuaeZoI+ippFCztEKVvckR2iice1JvhVytrQwUAZpgsubCPaU7xUe9vWnaOpaSBEspalykhC9bUlOMpT42ZHca6hyrqKmw/wMR8H5ZmdFoBVJb03O4UL0tSNnvIeRmkrLWqrs78gcrEn2tpcboh0UPOW3UUR9PMk4T4nnNKWmCjlrefhCwxRNztfmIQVdDElvS4m1/WuOujoZCs5XVOjtKPGokJzsYCtFYoWonSPT21DheU/wWhM19FcElwqNGOsp9Q8N/cwXaiND1MmeL1Q5XROtYYgGeFq1aTMsoMmcrKjQrOFQTQ1fmBYhmW6o8Jkjc7iDJRTBIo5kgJD5yMEYA3srCg7VFKwiVJkmRCc5ohGOKhsYMn/XBLdo5taZjlb9YAlGWRimqbCsoY7HFAXLa5I1HPRxMMsQDHFkWtRNniqT9UEeNjcE7RUlrCJ4R2CSJuqlKHWvJXjAUNcITYkenuBRB84TbeepcqTj3zZyFJzgYQdHnqfgI0ddUwS6GqWpsKWhjq9cV0vBAEMN2znq+EBfIWT+pClYw5xsTlJU6GeIBsjGmmANTzJZiIYpgrM0Oa8ZMjd7NP87jxhqGOhJlnQtjuQpB+8aEE00wZFznSJPyHxgH3HkPOsJFvYk8zqCHzTs1BYOa4J3PFU+UVRZxlHDM4YavlNUuMoRveiZA2d7grMNc2g+RbSCEKzmgYsUmWmazFJyoiOZ4KnyhKOGRzWJa0+moyV4TVHDzn51Awtqaphfk/lRQ08FX1iiqxTB/kLwd0VynKfEvI6cd4XMV5bMhZ7gZUWVzYQ6Nm2BYzxJbw3bGthEUUMfgbGeorae6DxHtJoZ6alhZ0+ytiVoK1R4z5PTrOECT/SugseEOlb1MMNR4VRNcJy+V1Hg9ONClSZFZjdHlc6W6FBLdJja2MC5hhpu0DBYEY1TFGwiFAxRRCsYkiM9JRb0JNMVkW6CZYT/2EiTGWmo8k+h4FhDNE7BvppoTSFnmCV5xZKzvcCdDo7VVPnIU+I+Rc68juApC90MwcFCsJ5hDqxgScYKreruyQwTqrzoqDCmhWi4IbhB0Yrt3RGa6GfDv52rKXWhh28dyZaWUvcZeMTBaZoSGyiCtRU5J8iviioHaErs7Jkj61syVzTTgOcUOQ8buFBTYWdL5g3T4qlpe0+wvD63heAXRfCCIed9RbCsp2CiI7raUOYOTU13N8PNHvpaGvayo4a3LLT1lDrVEPT2zLUlheB1R+ZTRfKWJ+dcocLJfi11vyJ51lLqJ0WD7tRwryezjiV5W28uJO9qykzX8JDe2lHl/9oyBwa2UMfOngpXCixvKdXTk3wrsKmiVYdZIqsoWEERjbcUNDuiaQomGoIbFdEHmsyWnuR+IeriKDVLnlawlyNHKwKlSU631PKep8J4Q+ayjkSLKYLhalNHlYvttb6fHm0p6OApsZ4l2VfdqZkjuysy6ysKLlckf1KUutCTs39bmCgEyyoasIWlVaMF7mgmWtBT8Kol5xpH9IGllo8cJdopcvZ2sImlDmMIbtDk3KIpeNiS08lQw11NFPTwVFlPP6pJ2gvRfI7gQUfmNAtf6Gs0wQxDsKGlVBdF8rCa3jzdwMaGHOsItrZk7hAyOzpK9VS06j5F49b0VNGOOfKs3lDToMsMBe9ZWtHFEgxTJLs7qrygKZjUnmCYoeAqeU6jqWuLJup4WghOdvCYJnrSkSzoyRkm5M2StQwVltPkfCAk58tET/CSg+8MUecmotMEnhBKfWBIZsg2ihruMJQaoIm+tkTLKEqspMh00w95gvFCQRtDwTT1gVDDSEVdlwqZfxoQRbK0g+tbiBZxzKlpnpypejdDwTaeOvorMk/IJE10h9CqRe28hhLbe0pMsdSwv4ZbhKivo2BjDWfL8UKJgeavwlwb5KlwhyE4u4XkGE2ytZCznKLCDZZq42VzT8HLCrpruFbIfOIINmh/qCdZ1ZBc65kLHR1Bkyf5zn6pN3SvGKIlFNGplhrO9QSXanLOMQTLCa0YJCRrCZm/CZmrLTm7WzCK4GJDiWUdFeYx1LCFg3NMd0XmCuF3Y5rITLDUsYS9zoHVzwnJoYpSTQoObyEzr4cFBNqYTopoaU/wkyLZ2lPhX/5Y95ulxGTV7KjhWrOZgl8MyUUafjYraNjNU1N3IWcjT5WzWqjwtoarHSUObGYO3GCJZpsBlnJGPd6ZYLyl1GdCA2625IwwJDP8GUKymbzuyPlZlvTUsaUh5zFDhRWFzPKKZLAlWdcQbObgF9tOqOsmB1dqcqYJmWstFbZRRI9poolmqiLnU0POvxScpah2iSL5UJNzgScY5+AuIbpO0YD3NCW+dLMszFSdFCWGqG6eVq2uYVNDdICGD6W7EPRWZEY5gpsE9rUkS3mijzzJnm6UpUFXG1hCUeVoS5WfNcFpblELL2qqrCvMvRfd45oalvKU2tiQ6ePJOVMRXase9iTtLJztPxJKLWpo2CRDcJwn2sWSLKIO1WQWNTCvpVUvOZhgSC40JD0dOctaSqzkCRbXsKlb11Oip6PCJ0IwSJM31j3akRxlP7Rwn6aGaUL0qiLnJkvB3xWZ2+Q1TfCwpQH3G0o92UzmX4o/oJNQMMSQc547wVHhdk+VCw01DFYEnTxzZKAm74QmeNNR1w6WzEhNK15VJzuCdxQ53dRUDws5KvwgBMOEgpcVNe0hZI6RXT1Jd0cyj5nsaEAHgVmGaJIlWdsc5Ui2ElrRR6jrRAttNMEAIWrTDFubkZaok7/AkzfIwfuWVq0jHzuCK4QabtLUMVPB3kJ0oyHTSVFlqMALilJf2Rf8k5aaHtMfayocLBS8L89oKoxpJvnAkDPa0qp5DAUTHKWmCcnthlou8iCKaFFLHWcINd1nyIwXqrSxMNmSs6KmoL2QrKuWtlQ5V0120xQ5vRyZS1rgFkWwhiOwiuQbR0OOVhQM9iS3tiXp4RawRPMp5tDletOOBL95MpM01dZTBM9pkn5qF010rIeHFcFZhmSGpYpTsI6nwhqe5C9ynhlpp5ophuRb6WcJFldkVnVEwwxVfrVkvnWUuNLCg5bgboFHPDlDPDmnK7hUrWiIbjadDclujlZcaokOFup4Ri1kacV6jmrrK1hN9bGwpKEBQ4Q6DvIUXOmo6U5LqQM6EPyiKNjVkPnJkDPNEaxhiFay5ExW1NXVUGqcpYYdPcGiCq7z/TSlbhL4pplWXKd7NZO5QQFrefhRQW/NHOsqcIglc4UhWklR8K0QzbAw08CBDnpbgqXdeD/QUsM4RZXDFBW6WJKe/mFPdH0LtBgiq57wFLzlyQzz82qYx5D5WJP5yVJDW01BfyHnS6HKO/reZqId1WGa4Hkh2kWodJ8i6KoIPlAj2hPt76CzXsVR6koPRzWTfKqIentatYpQw2me4AA3y1Kind3SwoOKZDcFXTwl9tWU6mfgRk9d71sKtlNwrjnYw5tC5n5LdKiGry3JKNlHEd3oaMCFHrazBPMp/uNJ+V7IudcSbeOIdjUEdwl0VHCOZo5t6YluEuaC9mQeMgSfOyKnYGFHcIeQ84yQWbuJYJpZw5CzglDH7gKnWqqM9ZTaXcN0TeYhR84eQtJT76JJ1lREe7WnnvsMmRc9FQ7SBBM9mV3lCUdmHk/S2RAMt0QjFNFqQpWjDPQ01DXWUdDBkXziKPjGEP3VP+zIWU2t7im41FOloyWzn/L6dkUy3VLDaZ6appgDLHPjJEsyvJngWEPUyVBiAaHCTEXwrLvSEbV1e1gKJniicWorC1MUrVjB3uDhJE/wgSOzk1DXpk0k73qCM8xw2UvD5kJmDUfOomqMpWCkJRlvKXGmoeBm18USjVIk04SClxTB6YrgLAPLWYK9HLUt5cmc0vYES8GnTeRc6skZbQkWdxRsIcyBRzx1DbTk9FbU0caTPOgJHhJKnOGIVhQqvKmo0llRw9sabrZkDtdg3PqaKi9oatjY8B+G371paMg6+mZFNNtQ04mWBq3rYLOmtWWQp8KJnpy9DdFensyjdqZ+yY40VJlH8wcdLzC8PZnvHMFUTZUrDTkLyQaGus5X5LzpYAf3i+e/ZlhqGqWhh6Ou6xTR9Z6oi5AZZtp7Mj2EEm8oSpxiYZCHU/1fbGdNNNRRoZMhmilEb2gqHOEJDtXkHK/JnG6IrvbPCwV3NhONVdS1thBMs1T4QOBcTWa2IzhMk2nW5Kyn9tXUtpv9RsG2msxk+ZsQzRQacJncpgke0+T8y5Fzj8BiGo7XlJjaTIlpQs7KFjpqGnKuoyEPeIKnFMkZHvopgh81ySxNFWvJWcKRs70j2FOT012IllEEO1n4pD1513Yg2ssQPOThOkvyrqHUdEXOSEsihmBbTbKX1kLBPWqWkLOqJbjB3GBIZmoa8qWl4CG/iZ7oiA72ZL7TJNeZUY7kFQftDcHHluBzRbCegzMtrRjVQpX2lgoPKKLJAkcbMl01XK2p7yhL8pCBbQ3BN2avJgKvttcrWDK3CiUOVxQ8ZP+pqXKyIxnmBymCg5vJjNfkPK4+c8cIfK8ocVt7kmfd/I5SR1hKvCzUtb+lhgc00ZaO6CyhIQP1Uv4yIZjload72PXX0OIJvnFU+0Zf6MhsJwTfW0r0UwQfW4LNLZl5HK261JCZ4qnBaAreVAS3WrjV0LBnNDUNNDToCEeFfwgcb4gOEqLRhirWkexrCEYKVV711DLYEE1XBEsp5tpTGjorkomKYF9FDXv7fR3BGwbettSxnyL53MBPjsxDZjMh+VUW9NRxq1DhVk+FSxQcaGjV9Pawv6eGByw5qzoy7xk4RsOShqjJwWKe/1pEEfzkobeD/dQJmpqedcyBTy2sr4nGNRH0c0SPWTLrqAc0OQcb/gemKgqucQT7ySWKCn2EUotoCvpZct7RO2sy/QW0IWcXd7pQRQyZVwT2USRO87uhjioTLKV2brpMUcMQRbKH/N2T+UlTpaMls6cmc6CCNy3JdYYSUzzJQ4oSD3oKLncULOiJvjBEC2oqnCJkJluCYy2ZQ5so9YYlZ1VLlQU1mXEW1jZERwj/MUSRc24TdexlqLKfQBtDTScJUV8FszXBEY5ktpD5Ur9hYB4Nb1iikw3JoYpkKX+RodRKFt53MMuRnKSpY31PwYaGaILh3wxJGz9TkTPEETxoCWZrgvOlmyMzxFEwVJE5xZKzvyJ4WxEc16Gd4Xe3Weq4XH2jKRikqOkGQ87hQnC7wBmGYLAnesX3M+S87eFATauuN+Qcrh7xIxXJbUIdMw3JGE3ylCWzrieaqCn4zhGM19TQ3z1oH1AX+pWEqIc7wNGAkULBo/ZxRaV9NNyh4Br3rCHZzbzmSfawBL0dNRwpW1kK9mxPXR9povcdrGSZK9c2k0xwFGzjuniCtRSZCZ6ccZ7gaktmgAOtKbG/JnOkJrjcQTdFMsxRQ2cLY3WTIrlCw1eWKn8R6pvt4GFDso3QoL4a3nLk3G6JrtME3dSenpx7PNFTmga0EaJTLQ061sEeQoWXhSo9LTXsaSjoJQRXeZLtDclbCrYzfzHHeaKjHCVOUkQHO3JeEepr56mhiyaYYKjjNU+Fed1wS5VlhWSqI/hYUdDOkaxiKehoyOnrCV5yBHtbWFqTHCCwtpDcYolesVR5yUzTZBb3RNMd0d6WP+SvhuBmRcGxnuQzT95IC285cr41cLGQ6aJJhmi4TMGempxeimBRQw1tFKV+8jd6KuzoSTqqDxzRtpZkurvKEHxlqXKRIjjfUNNXQsNOsRScoWFLT+YeRZVD3GRN0MdQcKqQjHDMrdGGVu3iYJpQx3WGUvfbmxwFfR20WBq0oYY7LMFhhgYtr8jpaEnaOzjawWWaTP8mMr0t/EPDPoqcnxTBI5o58L7uoWnMrpoqPwgVrlAUWE+V+TQl9rawoyP6QGAlQw2TPRX+YSkxyBC8Z6jhHkXBgQL7WII3DVFnRfCrBfxewv9D6xsyjys4VkhWb9pUU627JllV0YDNHMku/ldNMMXDEo4aFnAkk4U6frNEU4XgZUPmEKHUl44KrzmYamjAbh0JFvGnaTLPu1s9jPCwjFpYiN7z1DTOk/nc07CfDFzmCf7i+bfNHXhDtLeBXzTBT5rkMvWOIxpl4EMh2LGJBu2syDnAEx2naEhHDWMMzPZEhygyS1mS5RTJr5ZkoKbEUoYqr2kqdDUE8ztK7OaIntJkFrIECwv8LJTaVx5XJE86go8dFeZ3FN3rjabCAYpoYEeC9zzJVULBbmZhDyd7ko09ydpNZ3nm2Kee4FPPXHnYEF1nqOFEC08LUVcDvYXkJHW8gTaKCk9YGOeIJhqiE4ToPEepdp7IWFjdwnWaufGMwJJCMtUTTBBK9BGCOy2tGGrJTHIwyEOzp6aPzNMOtlZkDvcEWpP5SVNhfkvDxhmSazTJXYrM9U1E0xwFVwqZQwzJxw6+kGGGUj2FglGGmnb1/G51udRSMNlTw6GGnCcUwVcOpmsqTHa06o72sw1RL02p9z0VbnMLOaIX3QKaYKSCFQzBKEUNHTSc48k53RH9wxGMtpQa5KjjW0W0n6XCCCG4yxNNdhQ4R4l1Ff+2sSd6UFHiIEOyqqFgT01mEUMD+joy75jPhOA+oVVLm309FR4yVOlp4RhLiScNmSmaYF5Pw0STrOIoWMSR2UkRXOMp+M4SHW8o8Zoi6OZgjKOaFar8zZDzkWzvKOjkKBjmCXby8JahhjXULY4KlzgKLvAwxVGhvyd4zxB1d9T0piazmKLCVZY5sKiD0y2ZSYrkUEPUbIk+dlQ4SJHTR50k1DPaUWIdTZW9NJwnJMOECgd7ou/MnppMJ02O1VT4Wsh85MnZzcFTngpXGKo84qmwgKbCL/orR/SzJ2crA+t6Mp94KvxJUeIbT3CQu1uIdlQEOzlKfS3UMcrTiFmOuroocrZrT2AcmamOKg8YomeEKm/rlT2sociMaybaUlFhuqHCM2qIJ+rg4EcDFymiDSxzaHdPcpE62pD5kyM5SBMoA1PaUtfIthS85ig1VPiPPYXgYEMNk4Qq7TXBgo7oT57gPUdwgCHzhIVFPFU6OYJzHAX9m5oNrVjeE61miDrqQ4VSa1oiURTsKHC0IfjNwU2WzK6eqK8jWln4g15TVBnqmDteCJ501PGAocJhhqjZdtBEB6lnhLreFJKxmlKbeGrqLiSThVIbCdGzloasa6lpMQXHCME2boLpJgT7yWaemu6wBONbqGNVRS0PKIL7LckbjmQtR7K8I5qtqel+T/ChJTNIKLjdUMNIRyvOEko9YYl2cwQveBikCNawJKcLBbc7+JM92mysNvd/Fqp8a0k6CNEe7cnZrxlW0wQXaXjaktnRwNOGZKYiONwS7a1JVheq3WgJHlQUGKHKmp4KAxXR/ULURcNgoa4zhKSLpZR3kxRRb0NmD0OFn+UCS7CzI1nbP6+o4x47QZE5xRCt3ZagnYcvmpYQktXdk5YKXTzBC57kKEe0VVuiSYqapssMS3C9p2CKkHOg8B8Pa8p5atrIw3qezIWanMGa5HRDNF6RM9wcacl0N+Q8Z8hsIkSnaIIdHRUOEebAPy1zbCkhM062FCJtif7PU+UtoVXzWKqM1PxXO8cfdruhFQ/a6x3JKYagvVDhQEtNiyiiSQ7OsuRsZUku0CRNDs4Sog6KKjsZgk2bYJqijgsEenoKeniinRXBn/U3lgpPdyDZynQx8IiioMnCep5Ky8mjGs6Wty0l1hUQTcNWswS3WRp2kCNZwJG8omG8JphPUaFbC8lEfabwP7VtM9yoaNCAjpR41VNhrD9LkbN722v0CoZMByFzhaW+MyzRYEWFDQwN2M4/JiT76PuljT3VU/A36eaIThb+R9oZGOAJ9tewkgGvqOMNRWYjT/Cwu99Q8LqDE4TgbLWxJ1jaDDAERsFOFrobgjUsBScaguXU8kKm2RL19tRypSHnHNlHiIZqgufs4opgQdVdwxBNNFBR6kVFqb8ogimOzB6a6HTzrlDHEpYaxjiiA4TMQobkDg2vejjfwJGWmnbVFAw3H3hq2NyQfG7hz4aC+w3BbwbesG0swYayvpAs6++Ri1Vfzx93mFChvyN5xVHTS+0p9aqCAxyZ6ZacZyw5+7uuQkFPR9DDk9NOiE7X1PCYJVjVUqq7JlrHwWALF5nfHNGjApdpqgzx5OwilDhCiDYTgnc9waGW4BdLNNUQvOtpzDOWHDH8D7TR/A/85KljEQu3NREc4Pl/6B1Hhc8Umb5CsKMmGC9EPcxoT2amwHNCmeOEnOPbklnMkbOgIvO5UMOpQrS9UGVdt6iH/fURjhI/WOpaW9OKLYRod6HCUEdOX000wpDZQ6hwg6LgZfOqo1RfT/CrJzjekXOGhpc1VW71ZLbXyyp+93ILbC1kPtIEYx0FIx1VDrLoVzXRKRYWk809yYlC9ImcrinxtabKnzRJk3lAU1OLEN1j2zrYzr2myHRXJFf4h4QKT1qSTzTB5+ZNTzTRkAxX8FcLV2uS8eoQQ2aAkFzvCM72sJIcJET3WPjRk5wi32uSS9rfZajpWEvj9hW42F4o5NytSXYy8IKHay10VYdrcl4SkqscrXpMwyGOgtkajheSxdQqmpxP1L3t4R5PqasFnrQEjytq6qgp9Y09Qx9o4S1FzhUCn1kyHSzBWLemoSGvOqLNhZyBjmCaAUYpMgt4Ck7wBBMMwWKWgjsUwTaGVsxWC1mYoKiyqqeGKYqonSIRQ3KIkHO0pmAxTdBHkbOvfllfr+AA+7gnc50huVKYK393FOyg7rbPO/izI7hE4CnHHHnJ0ogNPRUGeUpsrZZTBJcrovUcJe51BPsr6GkJdhCCsZ6aTtMEb2pqWkqeVtDXE/QVggsU/Nl86d9RMF3DxvZTA58agu810RWawCiSzzXBeU3MMW9oyJUedvNEvQyNu1f10BSMddR1vaLCYpYa/mGocLSiYDcLbQz8aMn5iyF4xBNMs1P0QEOV7o5gaWGuzSeLue4tt3ro7y4Tgm4G/mopdZgl6q0o6KzJWE3mMksNr3r+a6CbT8g5wZNzT9O7fi/zpaOmnz3BRoqos+tv9zMbdpxsqDBOEewtJLt7cg5wtKKbvldpSzRRCD43VFheCI7yZLppggMVBS/KMAdHODJvOwq2NQSbKKKPLdFWQs7Fqo+mpl01JXYRgq8dnGLhTiFzqmWsUMdpllZdbKlyvSdYxhI9YghOtxR8LgSLWHK62mGGVoxzBE8LNWzqH9CUesQzFy5RQzTc56mhi6fgXEWwpKfE5Z7M05ZgZUPmo6auiv8YKzDYwWBLMErIbKHJvOwIrvEdhOBcQ9JdU1NHQ7CXn2XIDFBKU2WAgcX9UAUzDXWd5alwuyJ41Z9rjKLCL4aCp4WarhPm2rH+SaHUYE001JDZ2ZAzXPjdMpZWvC9wmqIB2lLhQ01D5jO06hghWMndbM7yRJMsoCj1vYbnFQVrW9jak3OlEJ3s/96+p33dEPRV5GxiqaGjIthUU6FFEZyqCa5qJrpBdzSw95IUnOPIrCUUjRZQFrbw5PR0R1qiYx3cb6nrWUMrBmmiBQxVHtTew5ICP/ip6g4hed/Akob/32wvBHsIOX83cI8hGeNeNPCIkPmXe8fPKx84OMSRM1MTdXSwjCZ4S30jVGhvqTRak/OVhgGazHuOCud5onEO1lJr6ecVyaOK6H7zqlBlIaHE0oroCgfvGJIdPcmfLNGLjpz7hZwZQpUbFME0A1cIJa7VNORkgfsMBatbKgwwJM9bSvQXeNOvbIjelg6WWvo5kvbKaJJNHexkKNHL9xRyFlH8Ti2riB5wVPhUk7nGkJnoCe428LR/wRGdYIlmWebCyxou1rCk4g/ShugBDX0V0ZQWkh0dOVsagkM0yV6OoLd5ye+pRlsCr0n+KiQrGuq5yJDzrTAXHtLUMduTDBVKrSm3eHL+6ijxhFDX9Z5gVU/wliHYTMiMFpKLNMEywu80wd3meoFmt6VbRMPenhrOc6DVe4pgXU8DnnHakLOIIrlF4FZPIw6R+zxBP0dyq6OOZ4Q5sLKCcz084ok+VsMMyQhNZmmBgX5xIXOEJTmi7VsGTvMTNdHHhpzdbE8Du2oKxgvBqQKdDDnTFOylCFaxR1syz2iqrOI/FEpNc3C6f11/7+ASS6l2inq2ciTrCCzgyemrCL5SVPjQkdPZUmGy2c9Sw9FtR1sS30RmsKPCS4rkIC/2U0MduwucYolGaPjKEyhzmiPYXagyWbYz8LWBDdzRimAXzxx4z8K9hpzlhLq+NiQ97HuKorMUfK/OVvC2JfiHUPCQI/q7J2gjK+tTDNxkCc4TMssqCs4TGtLVwQihyoAWgj9bosU80XGW6Ac9TJGziaUh5+hnFcHOnlaM1iRn29NaqGENTTTSUHCH2tWTeV0osUhH6psuVLjRUmGWhm6OZEshGeNowABHcJ2Bpy2ZszRcKkRXd2QuKVEeXnbfaEq825FguqfgfE2whlChSRMdron+LATTPQ2Z369t4B9C5gs/ylzv+CMmepIDPclFQl13W0rspPd1JOcbghGOEutqCv5qacURQl3dDKyvyJlqKXGPgcM9FfawJAMVmdcspcYKOZc4GjDYkFlK05olNMHyHn4zFNykyOxt99RkHlfwmiHo60l2EKI+mhreEKp080Tbug08BVPcgoqC5zWt+NLDTZ7oNSF51N1qie7Va3uCCwyZbkINf/NED6jzOsBdZjFN8oqG3wxVunqCSYYKf3EdhJyf9YWGf7tRU2oH3VHgPr1fe5J9hOgHd7xQ0y7qBwXr23aGErP0cm64JVjZwsOGqL+mhNgZmhJLW2oY4UhedsyBgzrCKrq7BmcpNVhR6jBPq64Vgi+kn6XE68pp8J5/+0wRHGOpsKenQn9DZntPzjRLZpDAdD2fnSgkG9tmIXnUwQ6WVighs7Yi2MxQ0N3CqYaCXkJ0oyOztMDJjmSSpcpvlrk0RMMOjmArQ04PRV1DO1FwhCVaUVPpKUM03JK5SxPsIWRu8/CGHi8UHChiqGFDTbSRJWeYUDDcH6vJWUxR4k1FXbMUwV6e4AJFXS8oMqsZKqzvYQ9DDQdZckY4aGsIhtlubbd2r3j4QBMoTamdPZk7O/Bf62lacZwneNjQoGcdVU7zJOd7ghsUHOkosagic6cnWc8+4gg285R6zZP5s1/LUbCKIznTwK36PkdwlOrl4U1LwfdCCa+IrvFkmgw1PCAUXKWo0sURXWcI2muKJlgyFzhynCY4RBOsqCjoI1R5zREco0n2Vt09BQtYSizgKNHfUmUrQ5UOCh51BFcLmY7umhYqXKQomOop8bUnWNNQcIiBcYaC6xzMNOS8JQQfeqKBmmglB+97ok/lfk3ygaHSyZaCRTzRxQo6GzLfa2jWBPepw+UmT7SQEJyiyRkhBLMVOfcoMjcK0eZChfUNzFAUzCsEN5vP/X1uP/n/aoMX+K+nw/Hjr/9xOo7j7Pju61tLcgvJpTWXNbfN5jLpi6VfCOviTktKlFusQixdEKWmEBUKNaIpjZRSSOXSgzaaKLdabrm1/9nZ+/f+vd/vz/v9+Xy+zZ7PRorYoZqyLrCwQdEAixxVOEXNNnjX2nUSRlkqGmWowk8lxR50JPy9Bo6qJXaXwNvREBvnThPEPrewryLhcAnj5WE15Fqi8W7R1sAuEu86S4ENikItFN4xkv9Af4nXSnUVcLiA9xzesFpivRRVeFKtsMRaKBhuSbjOELnAUtlSQUpXgdfB4Z1oSbnFEetbQ0IrAe+Y+pqnDcEJFj6S8LDZzZHwY4e3XONNlARraomNEt2bkvGsosA3ioyHm+6jCMbI59wqt4eeara28IzEmyPgoRaUOEDhTVdEJhmCoTWfC0p8aNkCp0oYqih2iqGi4yXeMkOsn4LdLLnmKfh/YogjNsPebeFGR4m9BJHLzB61XQ3BtpISfS2FugsK9FAtLWX1dCRcrCnUp44CNzuCowUZmxSRgYaE6Za0W2u/E7CVXCiI/UOR8aAm1+OSyE3mOUcwyc1zBBeoX1kiKy0Zfxck1Gsyulti11i83QTBF5Kg3pDQThFMVHiPSlK+0cSedng/VaS8bOZbtsBcTcZAR8JP5KeqQ1OYKAi20njdNNRpgnsU//K+JnaXJaGTomr7aYIphoRn9aeShJWKEq9LcozSF7QleEfDI5LYm5bgVkFkRwVDBCVu0DDIkGupo8TZBq+/pMQURYErJQmPKGKjNDkWOLx7Jd5QizdUweIaKrlP7SwJDhZvONjLkOsBBX9UpGxnydhXkfBLQ8IxgojQbLFnJf81JytSljclYYyEFyx0kVBvKWOFJmONpshGAcsduQY5giVNCV51eOdJYo/pLhbvM0uDHSevNKRcrKZIqnCtJeEsO95RoqcgGK4ocZcho1tTYtcZvH41pNQ7vA0WrhIfOSraIIntIAi+NXWCErdbkvrWwjRLrt0NKUdL6KSOscTOdMSOUtBHwL6OLA0vNSdynaWQEnCpIvKaIrJJEbvHkmuNhn6OjM8VkSGSqn1uYJCGHnq9I3aLhNME3t6GjIkO7xrNFumpyTNX/NrwX7CrIRiqqWijI9JO4d1iieykyfiposQIQ8YjjsjlBh6oHWbwRjgYJQn2NgSnNycmJAk3NiXhx44Sxykihxm8ybUwT1OVKySc7vi3OXVkdBJ4AyXBeksDXG0IhgtYY0lY5ahCD0ehborIk5aUWRJviMA7Xt5kyRjonrXENkm8yYqgs8VzgrJmClK20uMM3jRJ0FiQICQF9hdETlLQWRIb5ki6WDfWRPobvO6a4GP5mcOrNzDFELtTkONLh9dXE8xypEg7z8A9jkhrQ6Fhjlg/QVktJXxt4WXzT/03Q8IaQWSqIuEvloQ2mqC9Jfi7wRul4RX3pSPlzpoVlmCtI2jvKHCFhjcM3sN6lqF6HxnKelLjXWbwrpR4xzuCrTUZx2qq9oAh8p6ixCUGr78g8oyjRAtB5CZFwi80VerVpI0h+IeBxa6Zg6kWvpDHaioYYuEsRbDC3eOmC2JvGYLeioxGknL2UATNJN6hmtj1DlpLvDVmocYbrGCVJKOrg4X6DgddLA203BKMFngdJJFtFd7vJLm6KEpc5yjQrkk7M80SGe34X24nSex1Ra5Omgb71JKyg8SrU3i/kARKwWpH0kOGhKkObyfd0ZGjvyXlAkVZ4xRbYJ2irFMkFY1SwyWxr2oo4zlNiV+7zmaweFpT4kR3kaDAFW6xpSqzJay05FtYR4HmZhc9UxKbbfF2V8RG1MBmSaE+kmC6JnaRXK9gsiXhJHl/U0qM0WTcbyhwkYIvFGwjSbjfwhiJt8ZSQU+Bd5+marPMOkVkD0muxYLIfEuhh60x/J92itguihJSEMySVPQnTewnEm+620rTQEMsOfo4/kP/0ARvWjitlpSX7GxBgcMEsd3EEeYWvdytd+Saawi6aCIj1CkGb6Aj9rwhx16Cf3vAwFy5pyLhVonXzy51FDpdEblbkdJbUcEPDEFzQ8qNmhzzLTmmKWKbFCXeEuRabp6rxbvAtLF442QjQ+wEA9eL1xSR7Q0JXzlSHjJ4exq89yR0laScJ/FW6z4a73pFMEfDiRZvuvijIt86RaSFOl01riV2mD1UEvxGk/Geg5aWwGki1zgKPG9J2U8PEg8qYvMsZeytiTRXBMslCU8JSlxi8EabjwUldlDNLfzTUmCgxWsjqWCOHavYAqsknKFIO0yQ61VL5AVFxk6WhEaCAkdJgt9aSkzXlKNX2jEa79waYuc7gq0N3GDJGCBhoiTXUEPsdknCUE1CK0fwsiaylSF2uiDyO4XX3pFhNd7R4itFGc0k/ElBZwWvq+GC6szVeEoS/MZ+qylwpKNKv9Z469UOjqCjwlusicyTxG6VpNxcQ8IncoR4RhLbR+NdpGGmJWOcIzJGUuKPGpQg8rrG21dOMqQssJQ4RxH5jaUqnZuQ0F4Q+cjxLwPtpZbIAk3QTJHQWBE5S1BokoVtDd6lhqr9UpHSUxMcIYl9pojsb8h4SBOsMQcqvOWC2E8EVehqiJ1hrrAEbQxeK0NGZ0Gkq+guSRgniM23bIHVkqwx4hiHd7smaOyglyIyQuM978j4VS08J/A2G1KeMBRo4fBaSNhKUEZfQewVQ/C1I+MgfbEleEzCUw7mKXI0M3hd1EESVji8x5uQ41nxs1q4RMJCCXs7Iq9acpxn22oSDnQ/sJTxsCbHIYZiLyhY05TY0ZLIOQrGaSJDDN4t8pVaIrsqqFdEegtizc1iTew5Q4ayBDMUsQMkXocaYkc0hZua412siZ1rSXlR460zRJ5SlHGe5j801RLMlJTxtaOM3Q1pvxJ45zUlWFD7rsAbpfEm1JHxG0eh8w2R7QQVzBUw28FhFp5QZzq8t2rx2joqulYTWSuJdTYfWwqMFMcovFmSyJPNyLhE4E10pHzYjOC3huArRa571ZsGajQpQx38SBP5pyZB6lMU3khDnp0MBV51BE9o2E+TY5Ml2E8S7C0o6w1xvCZjf0HkVEHCzFoyNmqC+9wdcqN+Tp7jSDheE9ws8Y5V0NJCn2bk2tqSY4okdrEhx1iDN8cSudwepWmAGXKcJXK65H9to8jYQRH7SBF01ESUJdd0TayVInaWhLkOjlXE5irKGOnI6GSWGCJa482zBI9rCr0jyTVcEuzriC1vcr6mwFGSiqy5zMwxBH/TJHwjSPhL8+01kaaSUuMFKTcLEvaUePcrSmwn8DZrgikWb7CGPxkSjhQwrRk57tctmxLsb9sZvL9LSlyuSLlWkqOjwduo8b6Uv1DkmudIeFF2dHCgxVtk8dpIvHpBxhEOdhKk7OLIUSdJ+cSRY57B+0DgGUUlNfpthTfGkauzxrvTsUUaCVhlKeteTXCoJDCa2NOKhOmC4G1H8JBd4OBZReSRGkqcb/CO1PyLJTLB4j1q8JYaIutEjSLX8YKM+a6phdMsdLFUoV5RTm9JSkuDN8WcIon0NZMNZWh1q8C7SJEwV5HxrmnnTrf3KoJBlmCYI2ilSLlfEvlE4011NNgjgthzEua0oKK7JLE7HZHlEl60BLMVFewg4EWNt0ThrVNEVkkiTwpKXSWJzdRENgvKGq4IhjsiezgSFtsfCUq8qki5S1LRQeYQQ4nemmCkImWMw3tFUoUBZk4NOeZYEp4XRKTGa6wJjrWNHBVJR4m3FCnbuD6aak2WsMTh3SZImGCIPKNgsDpVwnsa70K31lCFJZYcwwSMFcQulGTsZuEaSdBXkPGZhu0FsdUO73RHjq8MPGGIfaGIbVTk6iuI3GFgucHrIQkmWSJdBd7BBu+uOryWAhY7+Lki9rK5wtEQzWwvtbqGhIMFwWRJsElsY4m9IIg9L6lCX0VklaPAYkfkZEGDnOWowlBJjtMUkcGK4Lg6EtoZInMUBVYLgn0UsdmCyCz7gIGHFfk+k1QwTh5We7A9x+IdJ6CvIkEagms0hR50eH9UnTQJ+2oiKyVlLFUE+8gBGu8MQ3CppUHesnjTHN4QB/UGPhCTHLFPHMFrCqa73gqObUJGa03wgbhHkrCfpEpzNLE7JDS25FMKhlhKKWKfCgqstLCPu1zBXy0J2ztwjtixBu8UTRn9LVtkmCN2iyFhtME70JHRQ1KVZXqKI/KNIKYMCYs1GUMEKbM1bKOI9LDXC7zbHS+bt+1MTWS9odA9DtrYtpbImQJ2VHh/lisEwaHqUk1kjKTAKknkBEXkbkdMGwq0dnhzLJF3NJH3JVwrqOB4Sca2hti75nmJN0WzxS6UxDYoEpxpa4htVlRjkYE7DZGzJVU72uC9IyhQL4i8YfGWSYLLNcHXloyz7QhNifmKSE9JgfGmuyLhc403Xm9vqcp6gXe3xuuv8F6VJNxkyTHEkHG2g0aKXL0MsXc1bGfgas2//dCONXiNLCX+5mB7eZIl1kHh7ajwpikyzlUUWOVOsjSQlsS+M0R+pPje/dzBXRZGO0rMtgQrLLG9VSu9n6CMXS3BhwYmSoIBhsjNBmZbgusE9BCPCP5triU4VhNbJfE+swSP27aayE8tuTpYYjtrYjMVGZdp2NpS1s6aBnKSHDsbKuplKbHM4a0wMFd/5/DmGyKrJSUaW4IBrqUhx0vyfzTBBLPIUcnZdrAkNsKR0sWRspumSns6Ch0v/qqIbBYUWKvPU/CFoyrDJGwSNFhbA/MlzKqjrO80hRbpKx0Jewsi/STftwGSlKc1JZyAzx05dhLEdnfQvhZOqiHWWEAHC7+30FuRcZUgaO5gpaIK+xsiHRUsqaPElTV40xQZQ107Q9BZE1nryDVGU9ZSQ47bmhBpLcYpUt7S+xuK/FiT8qKjwXYw5ypS2iuCv7q1gtgjhuBuB8LCFY5cUuCNtsQOFcT+4Ih9JX+k8Ea6v0iCIRZOtCT0Et00JW5UeC85Cg0ScK0k411HcG1zKtre3SeITBRk7WfwDhEvaYLTHP9le0m8By0JDwn4TlLW/aJOvGHxdjYUes+ScZigCkYQdNdEOhkiezgShqkx8ueKjI8lDfK2oNiOFvrZH1hS+tk7NV7nOmLHicGWEgubkXKdwdtZknCLJXaCpkrjZBtLZFsDP9CdxWsSr05Sxl6CMmoFbCOgryX40uDtamB7SVmXW4Ihlgpmq+00tBKUUa83WbjLUNkzDmY7cow1JDygyPGlhgGKYKz4vcV7QBNbJIgM11TUqZaMdwTeSguH6rOaw1JRKzaaGyxVm2EJ/uCIrVWUcZUkcp2grMsEjK+DMwS59jQk3Kd6SEq1d0S6uVmO4Bc1lDXTUcHjluCXEq+1OlBDj1pi9zgiXxnKuE0SqTXwhqbETW6RggMEnGl/q49UT2iCzgJvRwVXS2K/d6+ZkyUl7jawSVLit46EwxVljDZwoSQ20sDBihztHfk2yA8NVZghiXwrYHQdfKAOtzsayjhY9bY0yE2CWEeJ9xfzO423xhL5syS2TFJofO2pboHob0nY4GiAgRrvGQEDa/FWSsoaaYl0syRsEt3kWoH3B01shCXhTUWe9w3Bt44SC9QCh3eShQctwbaK2ApLroGCMlZrYqvlY3qYhM0aXpFkPOuoqJ3Dm6fxXrGwVF9gCWZagjPqznfkuMKQ8DPTQRO8ZqG1hPGKEm9IgpGW4DZDgTNriTxvFiq+Lz+0cKfp4wj6OCK9JSnzNSn9LFU7UhKZZMnYwcJ8s8yRsECScK4j5UOB95HFO0CzhY4xJxuCix0lDlEUeMdS6EZBkTsUkZ4K74dugyTXS7aNgL8aqjDfkCE0ZbwkCXpaWCKhl8P7VD5jxykivSyxyZrYERbe168LYu9ZYh86IkscgVLE7tWPKmJv11CgoyJltMEbrohtVAQfO4ImltiHEroYEs7RxAarVpY8AwXMcMReFOTYWe5iiLRQxJ5Q8DtJ8LQhWOhIeFESPGsILhbNDRljNbHzNRlTFbk2S3L0NOS6V1KFJYKUbSTcIIhM0wQ/s2TM0SRMNcQmSap3jCH4yhJZKSkwyRHpYYgsFeQ4U7xoCB7VVOExhXepo9ABBsYbvGWKXPME3lyH95YioZ0gssQRWWbI+FaSMkXijZXwgiTlYdPdkNLaETxlyDVIwqeaEus0aTcYcg0RVOkpR3CSJqIddK+90JCxzsDVloyrFd5ZAr4TBKfaWa6boEA7C7s6EpYaeFPjveooY72mjIccLHJ9HUwVlDhKkmutJDJBwnp1rvulJZggKDRfbXAkvC/4l3ozQOG9a8lxjx0i7nV4jSXc7vhe3OwIxjgSHjdEhhsif9YkPGlus3iLFDnWOFhtCZbJg0UbQcIaR67JjthoCyMEZRwhiXWyxO5QxI6w5NhT4U1WsJvDO60J34fW9hwzwlKij6ZAW9ne4L0s8C6XeBMEkd/LQy1VucBRot6QMlbivaBhoBgjqGiCJNhsqVp/S2SsG6DIONCR0dXhvWbJ+MRRZJkkuEjgDXJjFQW6SSL7GXK8Z2CZg7cVsbWGoKmEpzQ5elpiy8Ryg7dMkLLUEauzeO86CuwlSOlgYLojZWeJ9xM3S1PWfEfKl5ISLQ0MEKR8YOB2QfCxJBjrKPCN4f9MkaSsqoVXJBmP7EpFZ9UQfOoOFwSzBN4MQ8LsGrymlipcJQhmy0GaQjPqCHaXRwuCZwRbqK2Fg9wlClZqYicrIgMdZfxTQ0c7TBIbrChxmuzoKG8XRaSrIhhiyNFJkrC7oIAWMEOQa5aBekPCRknCo4IKPrYkvCDI8aYmY7WFtprgekcJZ3oLIqssCSMtFbQTJKwXYy3BY5oCh2iKPCpJOE+zRdpYgi6O2KmOAgvVCYaU4ySRek1sgyFhJ403QFHiVEmJHwtybO1gs8Hr5+BETQX3War0qZngYGgtVZtoqd6vFSk/UwdZElYqyjrF4HXUeFspIi9IGKf4j92pKGAdCYMVsbcV3kRF0N+R8LUd5PCsIGWoxDtBkCI0nKofdJQxT+LtZflvuc8Q3CjwWkq8KwUpHzkK/NmSsclCL0nseQdj5FRH5CNHSgtLiW80Of5HU9Hhlsga9bnBq3fEVltKfO5IaSTmGjjc4J0otcP7QsJUSQM8pEj5/wCuUuC2DWz8AAAAAElFTkSuQmCC"); -} diff --git a/public/js/lib/codemirror/theme/base16-dark.css b/public/js/lib/codemirror/theme/base16-dark.css deleted file mode 100644 index a46abdbbbd..0000000000 --- a/public/js/lib/codemirror/theme/base16-dark.css +++ /dev/null @@ -1,36 +0,0 @@ -/* - - Name: Base16 Default Dark - Author: Chris Kempson (http://chriskempson.com) - - CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) - Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) - -*/ - -.cm-s-base16-dark.CodeMirror {background: #151515; color: #e0e0e0;} -.cm-s-base16-dark div.CodeMirror-selected {background: #303030 !important;} -.cm-s-base16-dark .CodeMirror-gutters {background: #151515; border-right: 0px;} -.cm-s-base16-dark .CodeMirror-guttermarker { color: #ac4142; } -.cm-s-base16-dark .CodeMirror-guttermarker-subtle { color: #505050; } -.cm-s-base16-dark .CodeMirror-linenumber {color: #505050;} -.cm-s-base16-dark .CodeMirror-cursor {border-left: 1px solid #b0b0b0 !important;} - -.cm-s-base16-dark span.cm-comment {color: #8f5536;} -.cm-s-base16-dark span.cm-atom {color: #aa759f;} -.cm-s-base16-dark span.cm-number {color: #aa759f;} - -.cm-s-base16-dark span.cm-property, .cm-s-base16-dark span.cm-attribute {color: #90a959;} -.cm-s-base16-dark span.cm-keyword {color: #ac4142;} -.cm-s-base16-dark span.cm-string {color: #f4bf75;} - -.cm-s-base16-dark span.cm-variable {color: #90a959;} -.cm-s-base16-dark span.cm-variable-2 {color: #6a9fb5;} -.cm-s-base16-dark span.cm-def {color: #d28445;} -.cm-s-base16-dark span.cm-bracket {color: #e0e0e0;} -.cm-s-base16-dark span.cm-tag {color: #ac4142;} -.cm-s-base16-dark span.cm-link {color: #aa759f;} -.cm-s-base16-dark span.cm-error {background: #ac4142; color: #b0b0b0;} - -.cm-s-base16-dark .CodeMirror-activeline-background {background: #202020 !important;} -.cm-s-base16-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/base16-light.css b/public/js/lib/codemirror/theme/base16-light.css deleted file mode 100644 index 12ff2eb06f..0000000000 --- a/public/js/lib/codemirror/theme/base16-light.css +++ /dev/null @@ -1,36 +0,0 @@ -/* - - Name: Base16 Default Light - Author: Chris Kempson (http://chriskempson.com) - - CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-chrome-devtools) - Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) - -*/ - -.cm-s-base16-light.CodeMirror {background: #f5f5f5; color: #202020;} -.cm-s-base16-light div.CodeMirror-selected {background: #e0e0e0 !important;} -.cm-s-base16-light .CodeMirror-gutters {background: #f5f5f5; border-right: 0px;} -.cm-s-base16-light .CodeMirror-guttermarker { color: #ac4142; } -.cm-s-base16-light .CodeMirror-guttermarker-subtle { color: #b0b0b0; } -.cm-s-base16-light .CodeMirror-linenumber {color: #b0b0b0;} -.cm-s-base16-light .CodeMirror-cursor {border-left: 1px solid #505050 !important;} - -.cm-s-base16-light span.cm-comment {color: #8f5536;} -.cm-s-base16-light span.cm-atom {color: #aa759f;} -.cm-s-base16-light span.cm-number {color: #aa759f;} - -.cm-s-base16-light span.cm-property, .cm-s-base16-light span.cm-attribute {color: #90a959;} -.cm-s-base16-light span.cm-keyword {color: #ac4142;} -.cm-s-base16-light span.cm-string {color: #f4bf75;} - -.cm-s-base16-light span.cm-variable {color: #90a959;} -.cm-s-base16-light span.cm-variable-2 {color: #6a9fb5;} -.cm-s-base16-light span.cm-def {color: #d28445;} -.cm-s-base16-light span.cm-bracket {color: #202020;} -.cm-s-base16-light span.cm-tag {color: #ac4142;} -.cm-s-base16-light span.cm-link {color: #aa759f;} -.cm-s-base16-light span.cm-error {background: #ac4142; color: #505050;} - -.cm-s-base16-light .CodeMirror-activeline-background {background: #DDDCDC !important;} -.cm-s-base16-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/blackboard.css b/public/js/lib/codemirror/theme/blackboard.css deleted file mode 100644 index d7a2dc9695..0000000000 --- a/public/js/lib/codemirror/theme/blackboard.css +++ /dev/null @@ -1,30 +0,0 @@ -/* Port of TextMate's Blackboard theme */ - -.cm-s-blackboard.CodeMirror { background: #0C1021; color: #F8F8F8; } -.cm-s-blackboard .CodeMirror-selected { background: #253B76 !important; } -.cm-s-blackboard .CodeMirror-gutters { background: #0C1021; border-right: 0; } -.cm-s-blackboard .CodeMirror-guttermarker { color: #FBDE2D; } -.cm-s-blackboard .CodeMirror-guttermarker-subtle { color: #888; } -.cm-s-blackboard .CodeMirror-linenumber { color: #888; } -.cm-s-blackboard .CodeMirror-cursor { border-left: 1px solid #A7A7A7 !important; } - -.cm-s-blackboard .cm-keyword { color: #FBDE2D; } -.cm-s-blackboard .cm-atom { color: #D8FA3C; } -.cm-s-blackboard .cm-number { color: #D8FA3C; } -.cm-s-blackboard .cm-def { color: #8DA6CE; } -.cm-s-blackboard .cm-variable { color: #FF6400; } -.cm-s-blackboard .cm-operator { color: #FBDE2D;} -.cm-s-blackboard .cm-comment { color: #AEAEAE; } -.cm-s-blackboard .cm-string { color: #61CE3C; } -.cm-s-blackboard .cm-string-2 { color: #61CE3C; } -.cm-s-blackboard .cm-meta { color: #D8FA3C; } -.cm-s-blackboard .cm-builtin { color: #8DA6CE; } -.cm-s-blackboard .cm-tag { color: #8DA6CE; } -.cm-s-blackboard .cm-attribute { color: #8DA6CE; } -.cm-s-blackboard .cm-header { color: #FF6400; } -.cm-s-blackboard .cm-hr { color: #AEAEAE; } -.cm-s-blackboard .cm-link { color: #8DA6CE; } -.cm-s-blackboard .cm-error { background: #9D1E15; color: #F8F8F8; } - -.cm-s-blackboard .CodeMirror-activeline-background {background: #3C3636 !important;} -.cm-s-blackboard .CodeMirror-matchingbracket {outline:1px solid grey;color:white !important} \ No newline at end of file diff --git a/public/js/lib/codemirror/theme/cobalt.css b/public/js/lib/codemirror/theme/cobalt.css deleted file mode 100644 index 47440531a0..0000000000 --- a/public/js/lib/codemirror/theme/cobalt.css +++ /dev/null @@ -1,23 +0,0 @@ -.cm-s-cobalt.CodeMirror { background: #002240; color: white; } -.cm-s-cobalt div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-cobalt .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } -.cm-s-cobalt .CodeMirror-guttermarker { color: #ffee80; } -.cm-s-cobalt .CodeMirror-guttermarker-subtle { color: #d0d0d0; } -.cm-s-cobalt .CodeMirror-linenumber { color: #d0d0d0; } -.cm-s-cobalt .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-cobalt span.cm-comment { color: #08f; } -.cm-s-cobalt span.cm-atom { color: #845dc4; } -.cm-s-cobalt span.cm-number, .cm-s-cobalt span.cm-attribute { color: #ff80e1; } -.cm-s-cobalt span.cm-keyword { color: #ffee80; } -.cm-s-cobalt span.cm-string { color: #3ad900; } -.cm-s-cobalt span.cm-meta { color: #ff9d00; } -.cm-s-cobalt span.cm-variable-2, .cm-s-cobalt span.cm-tag { color: #9effff; } -.cm-s-cobalt span.cm-variable-3, .cm-s-cobalt span.cm-def { color: white; } -.cm-s-cobalt span.cm-bracket { color: #d8d8d8; } -.cm-s-cobalt span.cm-builtin, .cm-s-cobalt span.cm-special { color: #ff9e59; } -.cm-s-cobalt span.cm-link { color: #845dc4; } -.cm-s-cobalt span.cm-error { color: #9d1e15; } - -.cm-s-cobalt .CodeMirror-activeline-background {background: #002D57 !important;} -.cm-s-cobalt .CodeMirror-matchingbracket {outline:1px solid grey;color:white !important} diff --git a/public/js/lib/codemirror/theme/eclipse.css b/public/js/lib/codemirror/theme/eclipse.css deleted file mode 100644 index 317218e3d2..0000000000 --- a/public/js/lib/codemirror/theme/eclipse.css +++ /dev/null @@ -1,23 +0,0 @@ -.cm-s-eclipse span.cm-meta {color: #FF1717;} -.cm-s-eclipse span.cm-keyword { line-height: 1em; font-weight: bold; color: #7F0055; } -.cm-s-eclipse span.cm-atom {color: #219;} -.cm-s-eclipse span.cm-number {color: #164;} -.cm-s-eclipse span.cm-def {color: #00f;} -.cm-s-eclipse span.cm-variable {color: black;} -.cm-s-eclipse span.cm-variable-2 {color: #0000C0;} -.cm-s-eclipse span.cm-variable-3 {color: #0000C0;} -.cm-s-eclipse span.cm-property {color: black;} -.cm-s-eclipse span.cm-operator {color: black;} -.cm-s-eclipse span.cm-comment {color: #3F7F5F;} -.cm-s-eclipse span.cm-string {color: #2A00FF;} -.cm-s-eclipse span.cm-string-2 {color: #f50;} -.cm-s-eclipse span.cm-qualifier {color: #555;} -.cm-s-eclipse span.cm-builtin {color: #30a;} -.cm-s-eclipse span.cm-bracket {color: #cc7;} -.cm-s-eclipse span.cm-tag {color: #170;} -.cm-s-eclipse span.cm-attribute {color: #00c;} -.cm-s-eclipse span.cm-link {color: #219;} -.cm-s-eclipse span.cm-error {color: #f00;} - -.cm-s-eclipse .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-eclipse .CodeMirror-matchingbracket {outline:1px solid grey; color:black !important;} diff --git a/public/js/lib/codemirror/theme/elegant.css b/public/js/lib/codemirror/theme/elegant.css deleted file mode 100644 index dd7df7b73c..0000000000 --- a/public/js/lib/codemirror/theme/elegant.css +++ /dev/null @@ -1,13 +0,0 @@ -.cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;} -.cm-s-elegant span.cm-comment {color: #262; font-style: italic; line-height: 1em;} -.cm-s-elegant span.cm-meta {color: #555; font-style: italic; line-height: 1em;} -.cm-s-elegant span.cm-variable {color: black;} -.cm-s-elegant span.cm-variable-2 {color: #b11;} -.cm-s-elegant span.cm-qualifier {color: #555;} -.cm-s-elegant span.cm-keyword {color: #730;} -.cm-s-elegant span.cm-builtin {color: #30a;} -.cm-s-elegant span.cm-link {color: #762;} -.cm-s-elegant span.cm-error {background-color: #fdd;} - -.cm-s-elegant .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-elegant .CodeMirror-matchingbracket {outline:1px solid grey; color:black !important;} diff --git a/public/js/lib/codemirror/theme/erlang-dark.css b/public/js/lib/codemirror/theme/erlang-dark.css deleted file mode 100644 index ff47d7f8de..0000000000 --- a/public/js/lib/codemirror/theme/erlang-dark.css +++ /dev/null @@ -1,32 +0,0 @@ -.cm-s-erlang-dark.CodeMirror { background: #002240; color: white; } -.cm-s-erlang-dark div.CodeMirror-selected { background: #b36539 !important; } -.cm-s-erlang-dark .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } -.cm-s-erlang-dark .CodeMirror-guttermarker { color: white; } -.cm-s-erlang-dark .CodeMirror-guttermarker-subtle { color: #d0d0d0; } -.cm-s-erlang-dark .CodeMirror-linenumber { color: #d0d0d0; } -.cm-s-erlang-dark .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-erlang-dark span.cm-atom { color: #f133f1; } -.cm-s-erlang-dark span.cm-attribute { color: #ff80e1; } -.cm-s-erlang-dark span.cm-bracket { color: #ff9d00; } -.cm-s-erlang-dark span.cm-builtin { color: #eaa; } -.cm-s-erlang-dark span.cm-comment { color: #77f; } -.cm-s-erlang-dark span.cm-def { color: #e7a; } -.cm-s-erlang-dark span.cm-keyword { color: #ffee80; } -.cm-s-erlang-dark span.cm-meta { color: #50fefe; } -.cm-s-erlang-dark span.cm-number { color: #ffd0d0; } -.cm-s-erlang-dark span.cm-operator { color: #d55; } -.cm-s-erlang-dark span.cm-property { color: #ccc; } -.cm-s-erlang-dark span.cm-qualifier { color: #ccc; } -.cm-s-erlang-dark span.cm-quote { color: #ccc; } -.cm-s-erlang-dark span.cm-special { color: #ffbbbb; } -.cm-s-erlang-dark span.cm-string { color: #3ad900; } -.cm-s-erlang-dark span.cm-string-2 { color: #ccc; } -.cm-s-erlang-dark span.cm-tag { color: #9effff; } -.cm-s-erlang-dark span.cm-variable { color: #50fe50; } -.cm-s-erlang-dark span.cm-variable-2 { color: #e0e; } -.cm-s-erlang-dark span.cm-variable-3 { color: #ccc; } -.cm-s-erlang-dark span.cm-error { color: #9d1e15; } - -.cm-s-erlang-dark .CodeMirror-activeline-background {background: #013461 !important;} -.cm-s-erlang-dark .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} diff --git a/public/js/lib/codemirror/theme/lesser-dark.css b/public/js/lib/codemirror/theme/lesser-dark.css deleted file mode 100644 index a782474739..0000000000 --- a/public/js/lib/codemirror/theme/lesser-dark.css +++ /dev/null @@ -1,45 +0,0 @@ -/* -http://lesscss.org/ dark theme -Ported to CodeMirror by Peter Kroon -*/ -.cm-s-lesser-dark { - line-height: 1.3em; -} -.cm-s-lesser-dark.CodeMirror { background: #262626; color: #EBEFE7; text-shadow: 0 -1px 1px #262626; } -.cm-s-lesser-dark div.CodeMirror-selected {background: #45443B !important;} /* 33322B*/ -.cm-s-lesser-dark .CodeMirror-cursor { border-left: 1px solid white !important; } -.cm-s-lesser-dark pre { padding: 0 8px; }/*editable code holder*/ - -.cm-s-lesser-dark.CodeMirror span.CodeMirror-matchingbracket { color: #7EFC7E; }/*65FC65*/ - -.cm-s-lesser-dark .CodeMirror-gutters { background: #262626; border-right:1px solid #aaa; } -.cm-s-lesser-dark .CodeMirror-guttermarker { color: #599eff; } -.cm-s-lesser-dark .CodeMirror-guttermarker-subtle { color: #777; } -.cm-s-lesser-dark .CodeMirror-linenumber { color: #777; } - -.cm-s-lesser-dark span.cm-keyword { color: #599eff; } -.cm-s-lesser-dark span.cm-atom { color: #C2B470; } -.cm-s-lesser-dark span.cm-number { color: #B35E4D; } -.cm-s-lesser-dark span.cm-def {color: white;} -.cm-s-lesser-dark span.cm-variable { color:#D9BF8C; } -.cm-s-lesser-dark span.cm-variable-2 { color: #669199; } -.cm-s-lesser-dark span.cm-variable-3 { color: white; } -.cm-s-lesser-dark span.cm-property {color: #92A75C;} -.cm-s-lesser-dark span.cm-operator {color: #92A75C;} -.cm-s-lesser-dark span.cm-comment { color: #666; } -.cm-s-lesser-dark span.cm-string { color: #BCD279; } -.cm-s-lesser-dark span.cm-string-2 {color: #f50;} -.cm-s-lesser-dark span.cm-meta { color: #738C73; } -.cm-s-lesser-dark span.cm-qualifier {color: #555;} -.cm-s-lesser-dark span.cm-builtin { color: #ff9e59; } -.cm-s-lesser-dark span.cm-bracket { color: #EBEFE7; } -.cm-s-lesser-dark span.cm-tag { color: #669199; } -.cm-s-lesser-dark span.cm-attribute {color: #00c;} -.cm-s-lesser-dark span.cm-header {color: #a0a;} -.cm-s-lesser-dark span.cm-quote {color: #090;} -.cm-s-lesser-dark span.cm-hr {color: #999;} -.cm-s-lesser-dark span.cm-link {color: #00c;} -.cm-s-lesser-dark span.cm-error { color: #9d1e15; } - -.cm-s-lesser-dark .CodeMirror-activeline-background {background: #3C3A3A !important;} -.cm-s-lesser-dark .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} diff --git a/public/js/lib/codemirror/theme/mbo.css b/public/js/lib/codemirror/theme/mbo.css deleted file mode 100644 index 0ad6360b50..0000000000 --- a/public/js/lib/codemirror/theme/mbo.css +++ /dev/null @@ -1,35 +0,0 @@ -/****************************************************************/ -/* Based on mbonaci's Brackets mbo theme */ -/* https://github.com/mbonaci/global/blob/master/Mbo.tmTheme */ -/* Create your own: http://tmtheme-editor.herokuapp.com */ -/****************************************************************/ - -.cm-s-mbo.CodeMirror {background: #2c2c2c; color: #ffffec;} -.cm-s-mbo div.CodeMirror-selected {background: #716C62 !important;} -.cm-s-mbo .CodeMirror-gutters {background: #4e4e4e; border-right: 0px;} -.cm-s-mbo .CodeMirror-guttermarker { color: white; } -.cm-s-mbo .CodeMirror-guttermarker-subtle { color: grey; } -.cm-s-mbo .CodeMirror-linenumber {color: #dadada;} -.cm-s-mbo .CodeMirror-cursor {border-left: 1px solid #ffffec !important;} - -.cm-s-mbo span.cm-comment {color: #95958a;} -.cm-s-mbo span.cm-atom {color: #00a8c6;} -.cm-s-mbo span.cm-number {color: #00a8c6;} - -.cm-s-mbo span.cm-property, .cm-s-mbo span.cm-attribute {color: #9ddfe9;} -.cm-s-mbo span.cm-keyword {color: #ffb928;} -.cm-s-mbo span.cm-string {color: #ffcf6c;} -.cm-s-mbo span.cm-string.cm-property {color: #ffffec;} - -.cm-s-mbo span.cm-variable {color: #ffffec;} -.cm-s-mbo span.cm-variable-2 {color: #00a8c6;} -.cm-s-mbo span.cm-def {color: #ffffec;} -.cm-s-mbo span.cm-bracket {color: #fffffc; font-weight: bold;} -.cm-s-mbo span.cm-tag {color: #9ddfe9;} -.cm-s-mbo span.cm-link {color: #f54b07;} -.cm-s-mbo span.cm-error {border-bottom: #636363; color: #ffffec;} -.cm-s-mbo span.cm-qualifier {color: #ffffec;} - -.cm-s-mbo .CodeMirror-activeline-background {background: #494b41 !important;} -.cm-s-mbo .CodeMirror-matchingbracket {color: #222 !important;} -.cm-s-mbo .CodeMirror-matchingtag {background: rgba(255, 255, 255, .37);} diff --git a/public/js/lib/codemirror/theme/mdn-like.css b/public/js/lib/codemirror/theme/mdn-like.css deleted file mode 100644 index 81b21772d2..0000000000 --- a/public/js/lib/codemirror/theme/mdn-like.css +++ /dev/null @@ -1,44 +0,0 @@ -/* - MDN-LIKE Theme - Mozilla - Ported to CodeMirror by Peter Kroon - Report bugs/issues here: https://github.com/codemirror/CodeMirror/issues - GitHub: @peterkroon - - The mdn-like theme is inspired on the displayed code examples at: https://developer.mozilla.org/en-US/docs/Web/CSS/animation - -*/ -.cm-s-mdn-like.CodeMirror { color: #999; background-color: #fff; } -.cm-s-mdn-like .CodeMirror-selected { background: #cfc !important; } - -.cm-s-mdn-like .CodeMirror-gutters { background: #f8f8f8; border-left: 6px solid rgba(0,83,159,0.65); color: #333; } -.cm-s-mdn-like .CodeMirror-linenumber { color: #aaa; margin-left: 3px; } -div.cm-s-mdn-like .CodeMirror-cursor { border-left: 2px solid #222; } - -.cm-s-mdn-like .cm-keyword { color: #6262FF; } -.cm-s-mdn-like .cm-atom { color: #F90; } -.cm-s-mdn-like .cm-number { color: #ca7841; } -.cm-s-mdn-like .cm-def { color: #8DA6CE; } -.cm-s-mdn-like span.cm-variable-2, .cm-s-mdn-like span.cm-tag { color: #690; } -.cm-s-mdn-like span.cm-variable-3, .cm-s-mdn-like span.cm-def { color: #07a; } - -.cm-s-mdn-like .cm-variable { color: #07a; } -.cm-s-mdn-like .cm-property { color: #905; } -.cm-s-mdn-like .cm-qualifier { color: #690; } - -.cm-s-mdn-like .cm-operator { color: #cda869; } -.cm-s-mdn-like .cm-comment { color:#777; font-weight:normal; } -.cm-s-mdn-like .cm-string { color:#07a; font-style:italic; } -.cm-s-mdn-like .cm-string-2 { color:#bd6b18; } /*?*/ -.cm-s-mdn-like .cm-meta { color: #000; } /*?*/ -.cm-s-mdn-like .cm-builtin { color: #9B7536; } /*?*/ -.cm-s-mdn-like .cm-tag { color: #997643; } -.cm-s-mdn-like .cm-attribute { color: #d6bb6d; } /*?*/ -.cm-s-mdn-like .cm-header { color: #FF6400; } -.cm-s-mdn-like .cm-hr { color: #AEAEAE; } -.cm-s-mdn-like .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } -.cm-s-mdn-like .cm-error { border-bottom: 1px solid red; } - -div.cm-s-mdn-like .CodeMirror-activeline-background {background: #efefff;} -div.cm-s-mdn-like span.CodeMirror-matchingbracket {outline:1px solid grey; color: inherit;} - -.cm-s-mdn-like.CodeMirror { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFcAAAAyCAYAAAAp8UeFAAAHvklEQVR42s2b63bcNgyEQZCSHCdt2vd/0tWF7I+Q6XgMXiTtuvU5Pl57ZQKkKHzEAOtF5KeIJBGJ8uvL599FRFREZhFx8DeXv8trn68RuGaC8TRfo3SNp9dlDDHedyLyTUTeRWStXKPZrjtpZxaRw5hPqozRs1N8/enzIiQRWcCgy4MUA0f+XWliDhyL8Lfyvx7ei/Ae3iQFHyw7U/59pQVIMEEPEz0G7XiwdRjzSfC3UTtz9vchIntxvry5iMgfIhJoEflOz2CQr3F5h/HfeFe+GTdLaKcu9L8LTeQb/R/7GgbsfKedyNdoHsN31uRPWrfZ5wsj/NzzRQHuToIdU3ahwnsKPxXCjJITuOsi7XLc7SG/v5GdALs7wf8JjTFiB5+QvTEfRyGOfX3Lrx8wxyQi3sNq46O7QahQiCsRFgqddjBouVEHOKDgXAQHD9gJCr5sMKkEdjwsarG/ww3BMHBU7OBjXnzdyY7SfCxf5/z6ATccrwlKuwC/jhznnPF4CgVzhhVf4xp2EixcBActO75iZ8/fM9zAs2OMzKdslgXWJ9XG8PQoOAMA5fGcsvORgv0doBXyHrCwfLJAOwo71QLNkb8n2Pl6EWiR7OCibtkPaz4Kc/0NNAze2gju3zOwekALDaCFPI5vjPFmgGY5AZqyGEvH1x7QfIb8YtxMnA/b+QQ0aQDAwc6JMFg8CbQZ4qoYEEHbRwNojuK3EHwd7VALSgq+MNDKzfT58T8qdpADrgW0GmgcAS1lhzztJmkAzcPNOQbsWEALBDSlMKUG0Eq4CLAQWvEVQ9WU57gZJwZtgPO3r9oBTQ9WO8TjqXINx8R0EYpiZEUWOF3FxkbJkgU9B2f41YBrIj5ZfsQa0M5kTgiAAqM3ShXLgu8XMqcrQBvJ0CL5pnTsfMB13oB8athpAq2XOQmcGmoACCLydx7nToa23ATaSIY2ichfOdPTGxlasXMLaL0MLZAOwAKIM+y8CmicobGdCcbbK9DzN+yYGVoNNI5iUKTMyYOjPse4A8SM1MmcXgU0toOq1yO/v8FOxlASyc7TgeYaAMBJHcY1CcCwGI/TK4AmDbDyKYBBtFUkRwto8gygiQEaByFgJ00BH2M8JWwQS1nafDXQCidWyOI8AcjDCSjCLk8ngObuAm3JAHAdubAmOaK06V8MNEsKPJOhobSprwQa6gD7DclRQdqcwL4zxqgBrQcabUiBLclRDKAlWp+etPkBaNMA0AKlrHwTdEByZAA4GM+SNluSY6wAzcMNewxmgig5Ks0nkrSpBvSaQHMdKTBAnLojOdYyGpQ254602ZILPdTD1hdlggdIm74jbTp8vDwF5ZYUeLWGJpWsh6XNyXgcYwVoJQTEhhTYkxzZjiU5npU2TaB979TQehlaAVq4kaGpiPwwwLkYUuBbQwocyQTv1tA0+1UFWoJF3iv1oq+qoSk8EQdJmwHkziIF7oOZk14EGitibAdjLYYK78H5vZOhtWpoI0ATGHs0Q8OMb4Ey+2bU2UYztCtA0wFAs7TplGLRVQCcqaFdGSPCeTI1QNIC52iWNzof6Uib7xjEp07mNNoUYmVosVItHrHzRlLgBn9LFyRHaQCtVUMbtTNhoXWiTOO9k/V8BdAc1Oq0ArSQs6/5SU0hckNy9NnXqQY0PGYo5dWJ7nINaN6o958FWin27aBaWRka1r5myvLOAm0j30eBJqCxHLReVclxhxOEN2JfDWjxBtAC7MIH1fVaGdoOp4qJYDgKtKPSFNID2gSnGldrCqkFZ+5UeQXQBIRrSwocbdZYQT/2LwRahBPBXoHrB8nxaGROST62DKUbQOMMzZIC9abkuELfQzQALWTnDNAm8KHWFOJgJ5+SHIvTPcmx1xQyZRhNL5Qci689aXMEaN/uNIWkEwDAvFpOZmgsBaaGnbs1NPa1Jm32gBZAIh1pCtG7TSH4aE0y1uVY4uqoFPisGlpP2rSA5qTecWn5agK6BzSpgAyD+wFaqhnYoSZ1Vwr8CmlTQbrcO3ZaX0NAEyMbYaAlyquFoLKK3SPby9CeVUPThrSJmkCAE0CrKUQadi4DrdSlWhmah0YL9z9vClH59YGbHx1J8VZTyAjQepJjmXwAKTDQI3omc3p1U4gDUf6RfcdYfrUp5ClAi2J3Ba6UOXGo+K+bQrjjssitG2SJzshaLwMtXgRagUNpYYoVkMSBLM+9GGiJZMvduG6DRZ4qc04DMPtQQxOjEtACmhO7K1AbNbQDEggZyJwscFpAGwENhoBeUwh3bWolhe8BTYVKxQEWrSUn/uhcM5KhvUu/+eQu0Lzhi+VrK0PrZZNDQKs9cpYUuFYgMVpD4/NxenJTiMCNqdUEUf1qZWjppLT5qSkkUZbCwkbZMSuVnu80hfSkzRbQeqCZSAh6huR4VtoM2gHAlLf72smuWgE+VV7XpE25Ab2WFDgyhnSuKbs4GuGzCjR+tIoUuMFg3kgcWKLTwRqanJQ2W00hAsenfaApRC42hbCvK1SlE0HtE9BGgneJO+ELamitD1YjjOYnNYVcraGhtKkW0EqVVeDx733I2NH581k1NNxNLG0i0IJ8/NjVaOZ0tYZ2Vtr0Xv7tPV3hkWp9EFkgS/J0vosngTaSoaG06WHi+xObQkaAdlbanP8B2+2l0f90LmUAAAAASUVORK5CYII=); } diff --git a/public/js/lib/codemirror/theme/midnight.css b/public/js/lib/codemirror/theme/midnight.css deleted file mode 100644 index 4567d29399..0000000000 --- a/public/js/lib/codemirror/theme/midnight.css +++ /dev/null @@ -1,45 +0,0 @@ -/* Based on the theme at http://bonsaiden.github.com/JavaScript-Garden */ - -/**/ -.cm-s-midnight span.CodeMirror-matchhighlight { background: #494949; } -.cm-s-midnight.CodeMirror-focused span.CodeMirror-matchhighlight { background: #314D67 !important; } - -/**/ -.cm-s-midnight .CodeMirror-activeline-background {background: #253540 !important;} - -.cm-s-midnight.CodeMirror { - background: #0F192A; - color: #D1EDFF; -} - -.cm-s-midnight.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} - -.cm-s-midnight div.CodeMirror-selected {background: #314D67 !important;} -.cm-s-midnight .CodeMirror-gutters {background: #0F192A; border-right: 1px solid;} -.cm-s-midnight .CodeMirror-guttermarker { color: white; } -.cm-s-midnight .CodeMirror-guttermarker-subtle { color: #d0d0d0; } -.cm-s-midnight .CodeMirror-linenumber {color: #D0D0D0;} -.cm-s-midnight .CodeMirror-cursor { - border-left: 1px solid #F8F8F0 !important; -} - -.cm-s-midnight span.cm-comment {color: #428BDD;} -.cm-s-midnight span.cm-atom {color: #AE81FF;} -.cm-s-midnight span.cm-number {color: #D1EDFF;} - -.cm-s-midnight span.cm-property, .cm-s-midnight span.cm-attribute {color: #A6E22E;} -.cm-s-midnight span.cm-keyword {color: #E83737;} -.cm-s-midnight span.cm-string {color: #1DC116;} - -.cm-s-midnight span.cm-variable {color: #FFAA3E;} -.cm-s-midnight span.cm-variable-2 {color: #FFAA3E;} -.cm-s-midnight span.cm-def {color: #4DD;} -.cm-s-midnight span.cm-bracket {color: #D1EDFF;} -.cm-s-midnight span.cm-tag {color: #449;} -.cm-s-midnight span.cm-link {color: #AE81FF;} -.cm-s-midnight span.cm-error {background: #F92672; color: #F8F8F0;} - -.cm-s-midnight .CodeMirror-matchingbracket { - text-decoration: underline; - color: white !important; -} diff --git a/public/js/lib/codemirror/theme/monokai.css b/public/js/lib/codemirror/theme/monokai.css deleted file mode 100644 index 8ac80e7ac2..0000000000 --- a/public/js/lib/codemirror/theme/monokai.css +++ /dev/null @@ -1,31 +0,0 @@ -/* Based on Sublime Text's Monokai theme */ - -.cm-s-monokai.CodeMirror {background: #272822; color: #f8f8f2; height: auto;} -.cm-s-monokai div.CodeMirror-selected {background: #49483E !important;} -.cm-s-monokai .CodeMirror-gutters {background: #272822; border-right: 0px;} -.cm-s-monokai .CodeMirror-guttermarker { color: white; } -.cm-s-monokai .CodeMirror-guttermarker-subtle { color: #d0d0d0; } -.cm-s-monokai .CodeMirror-linenumber {color: #d0d0d0;} -.cm-s-monokai .CodeMirror-cursor {border-left: 1px solid #f8f8f0 !important;} - -.cm-s-monokai span.cm-comment {color: #75715e;} -.cm-s-monokai span.cm-atom {color: #ae81ff;} -.cm-s-monokai span.cm-number {color: #ae81ff;} - -.cm-s-monokai span.cm-property, .cm-s-monokai span.cm-attribute {color: #a6e22e;} -.cm-s-monokai span.cm-keyword {color: #f92672;} -.cm-s-monokai span.cm-string {color: #e6db74;} - -.cm-s-monokai span.cm-variable {color: #a6e22e;} -.cm-s-monokai span.cm-variable-2 {color: #9effff;} -.cm-s-monokai span.cm-def {color: #fd971f;} -.cm-s-monokai span.cm-bracket {color: #f8f8f2;} -.cm-s-monokai span.cm-tag {color: #f92672;} -.cm-s-monokai span.cm-link {color: #ae81ff;} -.cm-s-monokai span.cm-error {background: #f92672; color: #f8f8f0;} - -.cm-s-monokai .CodeMirror-activeline-background {background: #373831 !important;} -.cm-s-monokai .CodeMirror-matchingbracket { - text-decoration: underline; - color: white !important; -} diff --git a/public/js/lib/codemirror/theme/neat.css b/public/js/lib/codemirror/theme/neat.css deleted file mode 100644 index 115083b812..0000000000 --- a/public/js/lib/codemirror/theme/neat.css +++ /dev/null @@ -1,12 +0,0 @@ -.cm-s-neat span.cm-comment { color: #a86; } -.cm-s-neat span.cm-keyword { line-height: 1em; font-weight: bold; color: blue; } -.cm-s-neat span.cm-string { color: #a22; } -.cm-s-neat span.cm-builtin { line-height: 1em; font-weight: bold; color: #077; } -.cm-s-neat span.cm-special { line-height: 1em; font-weight: bold; color: #0aa; } -.cm-s-neat span.cm-variable { color: black; } -.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; } -.cm-s-neat span.cm-meta {color: #555;} -.cm-s-neat span.cm-link { color: #3a3; } - -.cm-s-neat .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-neat .CodeMirror-matchingbracket {outline:1px solid grey; color:black !important;} diff --git a/public/js/lib/codemirror/theme/neo.css b/public/js/lib/codemirror/theme/neo.css deleted file mode 100644 index cecaaf2801..0000000000 --- a/public/js/lib/codemirror/theme/neo.css +++ /dev/null @@ -1,43 +0,0 @@ -/* neo theme for codemirror */ - -/* Color scheme */ - -.cm-s-neo.CodeMirror { - background-color:#ffffff; - color:#2e383c; - line-height:1.4375; -} -.cm-s-neo .cm-comment {color:#75787b} -.cm-s-neo .cm-keyword, .cm-s-neo .cm-property {color:#1d75b3} -.cm-s-neo .cm-atom,.cm-s-neo .cm-number {color:#75438a} -.cm-s-neo .cm-node,.cm-s-neo .cm-tag {color:#9c3328} -.cm-s-neo .cm-string {color:#b35e14} -.cm-s-neo .cm-variable,.cm-s-neo .cm-qualifier {color:#047d65} - - -/* Editor styling */ - -.cm-s-neo pre { - padding:0; -} - -.cm-s-neo .CodeMirror-gutters { - border:none; - border-right:10px solid transparent; - background-color:transparent; -} - -.cm-s-neo .CodeMirror-linenumber { - padding:0; - color:#e0e2e5; -} - -.cm-s-neo .CodeMirror-guttermarker { color: #1d75b3; } -.cm-s-neo .CodeMirror-guttermarker-subtle { color: #e0e2e5; } - -.cm-s-neo div.CodeMirror-cursor { - width: auto; - border: 0; - background: rgba(155,157,162,0.37); - z-index: 1; -} diff --git a/public/js/lib/codemirror/theme/night.css b/public/js/lib/codemirror/theme/night.css deleted file mode 100644 index a0bf8cfa78..0000000000 --- a/public/js/lib/codemirror/theme/night.css +++ /dev/null @@ -1,26 +0,0 @@ -/* Loosely based on the Midnight Textmate theme */ - -.cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; } -.cm-s-night div.CodeMirror-selected { background: #447 !important; } -.cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-night .CodeMirror-guttermarker { color: white; } -.cm-s-night .CodeMirror-guttermarker-subtle { color: #bbb; } -.cm-s-night .CodeMirror-linenumber { color: #f8f8f8; } -.cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-night span.cm-comment { color: #6900a1; } -.cm-s-night span.cm-atom { color: #845dc4; } -.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; } -.cm-s-night span.cm-keyword { color: #599eff; } -.cm-s-night span.cm-string { color: #37f14a; } -.cm-s-night span.cm-meta { color: #7678e2; } -.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; } -.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; } -.cm-s-night span.cm-bracket { color: #8da6ce; } -.cm-s-night span.cm-comment { color: #6900a1; } -.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; } -.cm-s-night span.cm-link { color: #845dc4; } -.cm-s-night span.cm-error { color: #9d1e15; } - -.cm-s-night .CodeMirror-activeline-background {background: #1C005A !important;} -.cm-s-night .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} diff --git a/public/js/lib/codemirror/theme/paraiso-dark.css b/public/js/lib/codemirror/theme/paraiso-dark.css deleted file mode 100644 index 53dcdf7a2a..0000000000 --- a/public/js/lib/codemirror/theme/paraiso-dark.css +++ /dev/null @@ -1,36 +0,0 @@ -/* - - Name: Paraíso (Dark) - Author: Jan T. Sott - - Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) - Inspired by the art of Rubens LP (http://www.rubenslp.com.br) - -*/ - -.cm-s-paraiso-dark.CodeMirror {background: #2f1e2e; color: #b9b6b0;} -.cm-s-paraiso-dark div.CodeMirror-selected {background: #41323f !important;} -.cm-s-paraiso-dark .CodeMirror-gutters {background: #2f1e2e; border-right: 0px;} -.cm-s-paraiso-dark .CodeMirror-guttermarker { color: #ef6155; } -.cm-s-paraiso-dark .CodeMirror-guttermarker-subtle { color: #776e71; } -.cm-s-paraiso-dark .CodeMirror-linenumber {color: #776e71;} -.cm-s-paraiso-dark .CodeMirror-cursor {border-left: 1px solid #8d8687 !important;} - -.cm-s-paraiso-dark span.cm-comment {color: #e96ba8;} -.cm-s-paraiso-dark span.cm-atom {color: #815ba4;} -.cm-s-paraiso-dark span.cm-number {color: #815ba4;} - -.cm-s-paraiso-dark span.cm-property, .cm-s-paraiso-dark span.cm-attribute {color: #48b685;} -.cm-s-paraiso-dark span.cm-keyword {color: #ef6155;} -.cm-s-paraiso-dark span.cm-string {color: #fec418;} - -.cm-s-paraiso-dark span.cm-variable {color: #48b685;} -.cm-s-paraiso-dark span.cm-variable-2 {color: #06b6ef;} -.cm-s-paraiso-dark span.cm-def {color: #f99b15;} -.cm-s-paraiso-dark span.cm-bracket {color: #b9b6b0;} -.cm-s-paraiso-dark span.cm-tag {color: #ef6155;} -.cm-s-paraiso-dark span.cm-link {color: #815ba4;} -.cm-s-paraiso-dark span.cm-error {background: #ef6155; color: #8d8687;} - -.cm-s-paraiso-dark .CodeMirror-activeline-background {background: #4D344A !important;} -.cm-s-paraiso-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/paraiso-light.css b/public/js/lib/codemirror/theme/paraiso-light.css deleted file mode 100644 index 07ca325978..0000000000 --- a/public/js/lib/codemirror/theme/paraiso-light.css +++ /dev/null @@ -1,36 +0,0 @@ -/* - - Name: Paraíso (Light) - Author: Jan T. Sott - - Color scheme by Jan T. Sott (https://github.com/idleberg/Paraiso-CodeMirror) - Inspired by the art of Rubens LP (http://www.rubenslp.com.br) - -*/ - -.cm-s-paraiso-light.CodeMirror {background: #e7e9db; color: #41323f;} -.cm-s-paraiso-light div.CodeMirror-selected {background: #b9b6b0 !important;} -.cm-s-paraiso-light .CodeMirror-gutters {background: #e7e9db; border-right: 0px;} -.cm-s-paraiso-light .CodeMirror-guttermarker { color: black; } -.cm-s-paraiso-light .CodeMirror-guttermarker-subtle { color: #8d8687; } -.cm-s-paraiso-light .CodeMirror-linenumber {color: #8d8687;} -.cm-s-paraiso-light .CodeMirror-cursor {border-left: 1px solid #776e71 !important;} - -.cm-s-paraiso-light span.cm-comment {color: #e96ba8;} -.cm-s-paraiso-light span.cm-atom {color: #815ba4;} -.cm-s-paraiso-light span.cm-number {color: #815ba4;} - -.cm-s-paraiso-light span.cm-property, .cm-s-paraiso-light span.cm-attribute {color: #48b685;} -.cm-s-paraiso-light span.cm-keyword {color: #ef6155;} -.cm-s-paraiso-light span.cm-string {color: #fec418;} - -.cm-s-paraiso-light span.cm-variable {color: #48b685;} -.cm-s-paraiso-light span.cm-variable-2 {color: #06b6ef;} -.cm-s-paraiso-light span.cm-def {color: #f99b15;} -.cm-s-paraiso-light span.cm-bracket {color: #41323f;} -.cm-s-paraiso-light span.cm-tag {color: #ef6155;} -.cm-s-paraiso-light span.cm-link {color: #815ba4;} -.cm-s-paraiso-light span.cm-error {background: #ef6155; color: #776e71;} - -.cm-s-paraiso-light .CodeMirror-activeline-background {background: #CFD1C4 !important;} -.cm-s-paraiso-light .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/pastel-on-dark.css b/public/js/lib/codemirror/theme/pastel-on-dark.css deleted file mode 100644 index 7992ac7d2d..0000000000 --- a/public/js/lib/codemirror/theme/pastel-on-dark.css +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Pastel On Dark theme ported from ACE editor - * @license MIT - * @copyright AtomicPages LLC 2014 - * @author Dennis Thompson, AtomicPages LLC - * @version 1.1 - * @source https://github.com/atomicpages/codemirror-pastel-on-dark-theme - */ - -.cm-s-pastel-on-dark.CodeMirror { - background: #2c2827; - color: #8F938F; - line-height: 1.5; - font-size: 14px; -} -.cm-s-pastel-on-dark div.CodeMirror-selected { background: rgba(221,240,255,0.2) !important; } -.cm-s-pastel-on-dark .CodeMirror-gutters { - background: #34302f; - border-right: 0px; - padding: 0 3px; -} -.cm-s-pastel-on-dark .CodeMirror-guttermarker { color: white; } -.cm-s-pastel-on-dark .CodeMirror-guttermarker-subtle { color: #8F938F; } -.cm-s-pastel-on-dark .CodeMirror-linenumber { color: #8F938F; } -.cm-s-pastel-on-dark .CodeMirror-cursor { border-left: 1px solid #A7A7A7 !important; } -.cm-s-pastel-on-dark span.cm-comment { color: #A6C6FF; } -.cm-s-pastel-on-dark span.cm-atom { color: #DE8E30; } -.cm-s-pastel-on-dark span.cm-number { color: #CCCCCC; } -.cm-s-pastel-on-dark span.cm-property { color: #8F938F; } -.cm-s-pastel-on-dark span.cm-attribute { color: #a6e22e; } -.cm-s-pastel-on-dark span.cm-keyword { color: #AEB2F8; } -.cm-s-pastel-on-dark span.cm-string { color: #66A968; } -.cm-s-pastel-on-dark span.cm-variable { color: #AEB2F8; } -.cm-s-pastel-on-dark span.cm-variable-2 { color: #BEBF55; } -.cm-s-pastel-on-dark span.cm-variable-3 { color: #DE8E30; } -.cm-s-pastel-on-dark span.cm-def { color: #757aD8; } -.cm-s-pastel-on-dark span.cm-bracket { color: #f8f8f2; } -.cm-s-pastel-on-dark span.cm-tag { color: #C1C144; } -.cm-s-pastel-on-dark span.cm-link { color: #ae81ff; } -.cm-s-pastel-on-dark span.cm-qualifier,.cm-s-pastel-on-dark span.cm-builtin { color: #C1C144; } -.cm-s-pastel-on-dark span.cm-error { - background: #757aD8; - color: #f8f8f0; -} -.cm-s-pastel-on-dark .CodeMirror-activeline-background { background: rgba(255, 255, 255, 0.031) !important; } -.cm-s-pastel-on-dark .CodeMirror-matchingbracket { - border: 1px solid rgba(255,255,255,0.25); - color: #8F938F !important; - margin: -1px -1px 0 -1px; -} diff --git a/public/js/lib/codemirror/theme/rubyblue.css b/public/js/lib/codemirror/theme/rubyblue.css deleted file mode 100644 index 5349838940..0000000000 --- a/public/js/lib/codemirror/theme/rubyblue.css +++ /dev/null @@ -1,23 +0,0 @@ -.cm-s-rubyblue.CodeMirror { background: #112435; color: white; } -.cm-s-rubyblue div.CodeMirror-selected { background: #38566F !important; } -.cm-s-rubyblue .CodeMirror-gutters { background: #1F4661; border-right: 7px solid #3E7087; } -.cm-s-rubyblue .CodeMirror-guttermarker { color: white; } -.cm-s-rubyblue .CodeMirror-guttermarker-subtle { color: #3E7087; } -.cm-s-rubyblue .CodeMirror-linenumber { color: white; } -.cm-s-rubyblue .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-rubyblue span.cm-comment { color: #999; font-style:italic; line-height: 1em; } -.cm-s-rubyblue span.cm-atom { color: #F4C20B; } -.cm-s-rubyblue span.cm-number, .cm-s-rubyblue span.cm-attribute { color: #82C6E0; } -.cm-s-rubyblue span.cm-keyword { color: #F0F; } -.cm-s-rubyblue span.cm-string { color: #F08047; } -.cm-s-rubyblue span.cm-meta { color: #F0F; } -.cm-s-rubyblue span.cm-variable-2, .cm-s-rubyblue span.cm-tag { color: #7BD827; } -.cm-s-rubyblue span.cm-variable-3, .cm-s-rubyblue span.cm-def { color: white; } -.cm-s-rubyblue span.cm-bracket { color: #F0F; } -.cm-s-rubyblue span.cm-link { color: #F4C20B; } -.cm-s-rubyblue span.CodeMirror-matchingbracket { color:#F0F !important; } -.cm-s-rubyblue span.cm-builtin, .cm-s-rubyblue span.cm-special { color: #FF9D00; } -.cm-s-rubyblue span.cm-error { color: #AF2018; } - -.cm-s-rubyblue .CodeMirror-activeline-background {background: #173047 !important;} diff --git a/public/js/lib/codemirror/theme/solarized.css b/public/js/lib/codemirror/theme/solarized.css deleted file mode 100644 index 07ef406804..0000000000 --- a/public/js/lib/codemirror/theme/solarized.css +++ /dev/null @@ -1,165 +0,0 @@ -/* -Solarized theme for code-mirror -http://ethanschoonover.com/solarized -*/ - -/* -Solarized color pallet -http://ethanschoonover.com/solarized/img/solarized-palette.png -*/ - -.solarized.base03 { color: #002b36; } -.solarized.base02 { color: #073642; } -.solarized.base01 { color: #586e75; } -.solarized.base00 { color: #657b83; } -.solarized.base0 { color: #839496; } -.solarized.base1 { color: #93a1a1; } -.solarized.base2 { color: #eee8d5; } -.solarized.base3 { color: #fdf6e3; } -.solarized.solar-yellow { color: #b58900; } -.solarized.solar-orange { color: #cb4b16; } -.solarized.solar-red { color: #dc322f; } -.solarized.solar-magenta { color: #d33682; } -.solarized.solar-violet { color: #6c71c4; } -.solarized.solar-blue { color: #268bd2; } -.solarized.solar-cyan { color: #2aa198; } -.solarized.solar-green { color: #859900; } - -/* Color scheme for code-mirror */ - -.cm-s-solarized { - line-height: 1.45em; - color-profile: sRGB; - rendering-intent: auto; -} -.cm-s-solarized.cm-s-dark { - color: #839496; - background-color: #002b36; - text-shadow: #002b36 0 1px; -} -.cm-s-solarized.cm-s-light { - background-color: #fdf6e3; - color: #657b83; - text-shadow: #eee8d5 0 1px; -} - -.cm-s-solarized .CodeMirror-widget { - text-shadow: none; -} - - -.cm-s-solarized .cm-keyword { color: #cb4b16 } -.cm-s-solarized .cm-atom { color: #d33682; } -.cm-s-solarized .cm-number { color: #d33682; } -.cm-s-solarized .cm-def { color: #2aa198; } - -.cm-s-solarized .cm-variable { color: #268bd2; } -.cm-s-solarized .cm-variable-2 { color: #b58900; } -.cm-s-solarized .cm-variable-3 { color: #6c71c4; } - -.cm-s-solarized .cm-property { color: #2aa198; } -.cm-s-solarized .cm-operator {color: #6c71c4;} - -.cm-s-solarized .cm-comment { color: #586e75; font-style:italic; } - -.cm-s-solarized .cm-string { color: #859900; } -.cm-s-solarized .cm-string-2 { color: #b58900; } - -.cm-s-solarized .cm-meta { color: #859900; } -.cm-s-solarized .cm-qualifier { color: #b58900; } -.cm-s-solarized .cm-builtin { color: #d33682; } -.cm-s-solarized .cm-bracket { color: #cb4b16; } -.cm-s-solarized .CodeMirror-matchingbracket { color: #859900; } -.cm-s-solarized .CodeMirror-nonmatchingbracket { color: #dc322f; } -.cm-s-solarized .cm-tag { color: #93a1a1 } -.cm-s-solarized .cm-attribute { color: #2aa198; } -.cm-s-solarized .cm-header { color: #586e75; } -.cm-s-solarized .cm-quote { color: #93a1a1; } -.cm-s-solarized .cm-hr { - color: transparent; - border-top: 1px solid #586e75; - display: block; -} -.cm-s-solarized .cm-link { color: #93a1a1; cursor: pointer; } -.cm-s-solarized .cm-special { color: #6c71c4; } -.cm-s-solarized .cm-em { - color: #999; - text-decoration: underline; - text-decoration-style: dotted; -} -.cm-s-solarized .cm-strong { color: #eee; } -.cm-s-solarized .cm-error, -.cm-s-solarized .cm-invalidchar { - color: #586e75; - border-bottom: 1px dotted #dc322f; -} - -.cm-s-solarized.cm-s-dark .CodeMirror-selected { - background: #073642; -} - -.cm-s-solarized.cm-s-light .CodeMirror-selected { - background: #eee8d5; -} - -/* Editor styling */ - - - -/* Little shadow on the view-port of the buffer view */ -.cm-s-solarized.CodeMirror { - -moz-box-shadow: inset 7px 0 12px -6px #000; - -webkit-box-shadow: inset 7px 0 12px -6px #000; - box-shadow: inset 7px 0 12px -6px #000; -} - -/* Gutter border and some shadow from it */ -.cm-s-solarized .CodeMirror-gutters { - border-right: 1px solid; -} - -/* Gutter colors and line number styling based of color scheme (dark / light) */ - -/* Dark */ -.cm-s-solarized.cm-s-dark .CodeMirror-gutters { - background-color: #002b36; - border-color: #00232c; -} - -.cm-s-solarized.cm-s-dark .CodeMirror-linenumber { - text-shadow: #021014 0 -1px; -} - -/* Light */ -.cm-s-solarized.cm-s-light .CodeMirror-gutters { - background-color: #fdf6e3; - border-color: #eee8d5; -} - -/* Common */ -.cm-s-solarized .CodeMirror-linenumber { - color: #586e75; - padding: 0 5px; -} -.cm-s-solarized .CodeMirror-guttermarker-subtle { color: #586e75; } -.cm-s-solarized.cm-s-dark .CodeMirror-guttermarker { color: #ddd; } -.cm-s-solarized.cm-s-light .CodeMirror-guttermarker { color: #cb4b16; } - -.cm-s-solarized .CodeMirror-gutter .CodeMirror-gutter-text { - color: #586e75; -} - -.cm-s-solarized .CodeMirror-lines .CodeMirror-cursor { - border-left: 1px solid #819090; -} - -/* -Active line. Negative margin compensates left padding of the text in the -view-port -*/ -.cm-s-solarized.cm-s-dark .CodeMirror-activeline-background { - background: rgba(255, 255, 255, 0.10); -} -.cm-s-solarized.cm-s-light .CodeMirror-activeline-background { - background: rgba(0, 0, 0, 0.10); -} diff --git a/public/js/lib/codemirror/theme/the-matrix.css b/public/js/lib/codemirror/theme/the-matrix.css deleted file mode 100644 index 01474ca94d..0000000000 --- a/public/js/lib/codemirror/theme/the-matrix.css +++ /dev/null @@ -1,28 +0,0 @@ -.cm-s-the-matrix.CodeMirror { background: #000000; color: #00FF00; } -.cm-s-the-matrix div.CodeMirror-selected { background: #2D2D2D !important; } -.cm-s-the-matrix .CodeMirror-gutters { background: #060; border-right: 2px solid #00FF00; } -.cm-s-the-matrix .CodeMirror-guttermarker { color: #0f0; } -.cm-s-the-matrix .CodeMirror-guttermarker-subtle { color: white; } -.cm-s-the-matrix .CodeMirror-linenumber { color: #FFFFFF; } -.cm-s-the-matrix .CodeMirror-cursor { border-left: 1px solid #00FF00 !important; } - -.cm-s-the-matrix span.cm-keyword {color: #008803; font-weight: bold;} -.cm-s-the-matrix span.cm-atom {color: #3FF;} -.cm-s-the-matrix span.cm-number {color: #FFB94F;} -.cm-s-the-matrix span.cm-def {color: #99C;} -.cm-s-the-matrix span.cm-variable {color: #F6C;} -.cm-s-the-matrix span.cm-variable-2 {color: #C6F;} -.cm-s-the-matrix span.cm-variable-3 {color: #96F;} -.cm-s-the-matrix span.cm-property {color: #62FFA0;} -.cm-s-the-matrix span.cm-operator {color: #999} -.cm-s-the-matrix span.cm-comment {color: #CCCCCC;} -.cm-s-the-matrix span.cm-string {color: #39C;} -.cm-s-the-matrix span.cm-meta {color: #C9F;} -.cm-s-the-matrix span.cm-qualifier {color: #FFF700;} -.cm-s-the-matrix span.cm-builtin {color: #30a;} -.cm-s-the-matrix span.cm-bracket {color: #cc7;} -.cm-s-the-matrix span.cm-tag {color: #FFBD40;} -.cm-s-the-matrix span.cm-attribute {color: #FFF700;} -.cm-s-the-matrix span.cm-error {color: #FF0000;} - -.cm-s-the-matrix .CodeMirror-activeline-background {background: #040;} diff --git a/public/js/lib/codemirror/theme/tomorrow-night-bright.css b/public/js/lib/codemirror/theme/tomorrow-night-bright.css deleted file mode 100644 index decb82d3e0..0000000000 --- a/public/js/lib/codemirror/theme/tomorrow-night-bright.css +++ /dev/null @@ -1,35 +0,0 @@ -/* - - Name: Tomorrow Night - Bright - Author: Chris Kempson - - Port done by Gerard Braad - -*/ - -.cm-s-tomorrow-night-bright.CodeMirror {background: #000000; color: #eaeaea;} -.cm-s-tomorrow-night-bright div.CodeMirror-selected {background: #424242 !important;} -.cm-s-tomorrow-night-bright .CodeMirror-gutters {background: #000000; border-right: 0px;} -.cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; } -.cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; } -.cm-s-tomorrow-night-bright .CodeMirror-linenumber {color: #424242;} -.cm-s-tomorrow-night-bright .CodeMirror-cursor {border-left: 1px solid #6A6A6A !important;} - -.cm-s-tomorrow-night-bright span.cm-comment {color: #d27b53;} -.cm-s-tomorrow-night-bright span.cm-atom {color: #a16a94;} -.cm-s-tomorrow-night-bright span.cm-number {color: #a16a94;} - -.cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute {color: #99cc99;} -.cm-s-tomorrow-night-bright span.cm-keyword {color: #d54e53;} -.cm-s-tomorrow-night-bright span.cm-string {color: #e7c547;} - -.cm-s-tomorrow-night-bright span.cm-variable {color: #b9ca4a;} -.cm-s-tomorrow-night-bright span.cm-variable-2 {color: #7aa6da;} -.cm-s-tomorrow-night-bright span.cm-def {color: #e78c45;} -.cm-s-tomorrow-night-bright span.cm-bracket {color: #eaeaea;} -.cm-s-tomorrow-night-bright span.cm-tag {color: #d54e53;} -.cm-s-tomorrow-night-bright span.cm-link {color: #a16a94;} -.cm-s-tomorrow-night-bright span.cm-error {background: #d54e53; color: #6A6A6A;} - -.cm-s-tomorrow-night-bright .CodeMirror-activeline-background {background: #2a2a2a !important;} -.cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/tomorrow-night-eighties.css b/public/js/lib/codemirror/theme/tomorrow-night-eighties.css deleted file mode 100644 index 841413546c..0000000000 --- a/public/js/lib/codemirror/theme/tomorrow-night-eighties.css +++ /dev/null @@ -1,36 +0,0 @@ -/* - - Name: Tomorrow Night - Eighties - Author: Chris Kempson - - CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) - Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) - -*/ - -.cm-s-tomorrow-night-eighties.CodeMirror {background: #000000; color: #CCCCCC;} -.cm-s-tomorrow-night-eighties div.CodeMirror-selected {background: #2D2D2D !important;} -.cm-s-tomorrow-night-eighties .CodeMirror-gutters {background: #000000; border-right: 0px;} -.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker { color: #f2777a; } -.cm-s-tomorrow-night-eighties .CodeMirror-guttermarker-subtle { color: #777; } -.cm-s-tomorrow-night-eighties .CodeMirror-linenumber {color: #515151;} -.cm-s-tomorrow-night-eighties .CodeMirror-cursor {border-left: 1px solid #6A6A6A !important;} - -.cm-s-tomorrow-night-eighties span.cm-comment {color: #d27b53;} -.cm-s-tomorrow-night-eighties span.cm-atom {color: #a16a94;} -.cm-s-tomorrow-night-eighties span.cm-number {color: #a16a94;} - -.cm-s-tomorrow-night-eighties span.cm-property, .cm-s-tomorrow-night-eighties span.cm-attribute {color: #99cc99;} -.cm-s-tomorrow-night-eighties span.cm-keyword {color: #f2777a;} -.cm-s-tomorrow-night-eighties span.cm-string {color: #ffcc66;} - -.cm-s-tomorrow-night-eighties span.cm-variable {color: #99cc99;} -.cm-s-tomorrow-night-eighties span.cm-variable-2 {color: #6699cc;} -.cm-s-tomorrow-night-eighties span.cm-def {color: #f99157;} -.cm-s-tomorrow-night-eighties span.cm-bracket {color: #CCCCCC;} -.cm-s-tomorrow-night-eighties span.cm-tag {color: #f2777a;} -.cm-s-tomorrow-night-eighties span.cm-link {color: #a16a94;} -.cm-s-tomorrow-night-eighties span.cm-error {background: #f2777a; color: #6A6A6A;} - -.cm-s-tomorrow-night-eighties .CodeMirror-activeline-background {background: #343600 !important;} -.cm-s-tomorrow-night-eighties .CodeMirror-matchingbracket { text-decoration: underline; color: white !important;} diff --git a/public/js/lib/codemirror/theme/twilight.css b/public/js/lib/codemirror/theme/twilight.css deleted file mode 100644 index 9ca50576d8..0000000000 --- a/public/js/lib/codemirror/theme/twilight.css +++ /dev/null @@ -1,30 +0,0 @@ -.cm-s-twilight.CodeMirror { background: #141414; color: #f7f7f7; } /**/ -.cm-s-twilight .CodeMirror-selected { background: #323232 !important; } /**/ - -.cm-s-twilight .CodeMirror-gutters { background: #222; border-right: 1px solid #aaa; } -.cm-s-twilight .CodeMirror-guttermarker { color: white; } -.cm-s-twilight .CodeMirror-guttermarker-subtle { color: #aaa; } -.cm-s-twilight .CodeMirror-linenumber { color: #aaa; } -.cm-s-twilight .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-twilight .cm-keyword { color: #f9ee98; } /**/ -.cm-s-twilight .cm-atom { color: #FC0; } -.cm-s-twilight .cm-number { color: #ca7841; } /**/ -.cm-s-twilight .cm-def { color: #8DA6CE; } -.cm-s-twilight span.cm-variable-2, .cm-s-twilight span.cm-tag { color: #607392; } /**/ -.cm-s-twilight span.cm-variable-3, .cm-s-twilight span.cm-def { color: #607392; } /**/ -.cm-s-twilight .cm-operator { color: #cda869; } /**/ -.cm-s-twilight .cm-comment { color:#777; font-style:italic; font-weight:normal; } /**/ -.cm-s-twilight .cm-string { color:#8f9d6a; font-style:italic; } /**/ -.cm-s-twilight .cm-string-2 { color:#bd6b18 } /*?*/ -.cm-s-twilight .cm-meta { background-color:#141414; color:#f7f7f7; } /*?*/ -.cm-s-twilight .cm-builtin { color: #cda869; } /*?*/ -.cm-s-twilight .cm-tag { color: #997643; } /**/ -.cm-s-twilight .cm-attribute { color: #d6bb6d; } /*?*/ -.cm-s-twilight .cm-header { color: #FF6400; } -.cm-s-twilight .cm-hr { color: #AEAEAE; } -.cm-s-twilight .cm-link { color:#ad9361; font-style:italic; text-decoration:none; } /**/ -.cm-s-twilight .cm-error { border-bottom: 1px solid red; } - -.cm-s-twilight .CodeMirror-activeline-background {background: #27282E !important;} -.cm-s-twilight .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} diff --git a/public/js/lib/codemirror/theme/vibrant-ink.css b/public/js/lib/codemirror/theme/vibrant-ink.css deleted file mode 100644 index 5177282325..0000000000 --- a/public/js/lib/codemirror/theme/vibrant-ink.css +++ /dev/null @@ -1,32 +0,0 @@ -/* Taken from the popular Visual Studio Vibrant Ink Schema */ - -.cm-s-vibrant-ink.CodeMirror { background: black; color: white; } -.cm-s-vibrant-ink .CodeMirror-selected { background: #35493c !important; } - -.cm-s-vibrant-ink .CodeMirror-gutters { background: #002240; border-right: 1px solid #aaa; } -.cm-s-vibrant-ink .CodeMirror-guttermarker { color: white; } -.cm-s-vibrant-ink .CodeMirror-guttermarker-subtle { color: #d0d0d0; } -.cm-s-vibrant-ink .CodeMirror-linenumber { color: #d0d0d0; } -.cm-s-vibrant-ink .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-vibrant-ink .cm-keyword { color: #CC7832; } -.cm-s-vibrant-ink .cm-atom { color: #FC0; } -.cm-s-vibrant-ink .cm-number { color: #FFEE98; } -.cm-s-vibrant-ink .cm-def { color: #8DA6CE; } -.cm-s-vibrant-ink span.cm-variable-2, .cm-s-vibrant span.cm-tag { color: #FFC66D } -.cm-s-vibrant-ink span.cm-variable-3, .cm-s-vibrant span.cm-def { color: #FFC66D } -.cm-s-vibrant-ink .cm-operator { color: #888; } -.cm-s-vibrant-ink .cm-comment { color: gray; font-weight: bold; } -.cm-s-vibrant-ink .cm-string { color: #A5C25C } -.cm-s-vibrant-ink .cm-string-2 { color: red } -.cm-s-vibrant-ink .cm-meta { color: #D8FA3C; } -.cm-s-vibrant-ink .cm-builtin { color: #8DA6CE; } -.cm-s-vibrant-ink .cm-tag { color: #8DA6CE; } -.cm-s-vibrant-ink .cm-attribute { color: #8DA6CE; } -.cm-s-vibrant-ink .cm-header { color: #FF6400; } -.cm-s-vibrant-ink .cm-hr { color: #AEAEAE; } -.cm-s-vibrant-ink .cm-link { color: blue; } -.cm-s-vibrant-ink .cm-error { border-bottom: 1px solid red; } - -.cm-s-vibrant-ink .CodeMirror-activeline-background {background: #27282E !important;} -.cm-s-vibrant-ink .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} diff --git a/public/js/lib/codemirror/theme/xq-dark.css b/public/js/lib/codemirror/theme/xq-dark.css deleted file mode 100644 index 116eccf21b..0000000000 --- a/public/js/lib/codemirror/theme/xq-dark.css +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright (C) 2011 by MarkLogic Corporation -Author: Mike Brevoort - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -.cm-s-xq-dark.CodeMirror { background: #0a001f; color: #f8f8f8; } -.cm-s-xq-dark .CodeMirror-selected { background: #27007A !important; } -.cm-s-xq-dark .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; } -.cm-s-xq-dark .CodeMirror-guttermarker { color: #FFBD40; } -.cm-s-xq-dark .CodeMirror-guttermarker-subtle { color: #f8f8f8; } -.cm-s-xq-dark .CodeMirror-linenumber { color: #f8f8f8; } -.cm-s-xq-dark .CodeMirror-cursor { border-left: 1px solid white !important; } - -.cm-s-xq-dark span.cm-keyword {color: #FFBD40;} -.cm-s-xq-dark span.cm-atom {color: #6C8CD5;} -.cm-s-xq-dark span.cm-number {color: #164;} -.cm-s-xq-dark span.cm-def {color: #FFF; text-decoration:underline;} -.cm-s-xq-dark span.cm-variable {color: #FFF;} -.cm-s-xq-dark span.cm-variable-2 {color: #EEE;} -.cm-s-xq-dark span.cm-variable-3 {color: #DDD;} -.cm-s-xq-dark span.cm-property {} -.cm-s-xq-dark span.cm-operator {} -.cm-s-xq-dark span.cm-comment {color: gray;} -.cm-s-xq-dark span.cm-string {color: #9FEE00;} -.cm-s-xq-dark span.cm-meta {color: yellow;} -.cm-s-xq-dark span.cm-qualifier {color: #FFF700;} -.cm-s-xq-dark span.cm-builtin {color: #30a;} -.cm-s-xq-dark span.cm-bracket {color: #cc7;} -.cm-s-xq-dark span.cm-tag {color: #FFBD40;} -.cm-s-xq-dark span.cm-attribute {color: #FFF700;} -.cm-s-xq-dark span.cm-error {color: #f00;} - -.cm-s-xq-dark .CodeMirror-activeline-background {background: #27282E !important;} -.cm-s-xq-dark .CodeMirror-matchingbracket {outline:1px solid grey; color:white !important;} \ No newline at end of file diff --git a/public/js/lib/codemirror/theme/xq-light.css b/public/js/lib/codemirror/theme/xq-light.css deleted file mode 100644 index 20b5c79614..0000000000 --- a/public/js/lib/codemirror/theme/xq-light.css +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright (C) 2011 by MarkLogic Corporation -Author: Mike Brevoort - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -.cm-s-xq-light span.cm-keyword {line-height: 1em; font-weight: bold; color: #5A5CAD; } -.cm-s-xq-light span.cm-atom {color: #6C8CD5;} -.cm-s-xq-light span.cm-number {color: #164;} -.cm-s-xq-light span.cm-def {text-decoration:underline;} -.cm-s-xq-light span.cm-variable {color: black; } -.cm-s-xq-light span.cm-variable-2 {color:black;} -.cm-s-xq-light span.cm-variable-3 {color: black; } -.cm-s-xq-light span.cm-property {} -.cm-s-xq-light span.cm-operator {} -.cm-s-xq-light span.cm-comment {color: #0080FF; font-style: italic;} -.cm-s-xq-light span.cm-string {color: red;} -.cm-s-xq-light span.cm-meta {color: yellow;} -.cm-s-xq-light span.cm-qualifier {color: grey} -.cm-s-xq-light span.cm-builtin {color: #7EA656;} -.cm-s-xq-light span.cm-bracket {color: #cc7;} -.cm-s-xq-light span.cm-tag {color: #3F7F7F;} -.cm-s-xq-light span.cm-attribute {color: #7F007F;} -.cm-s-xq-light span.cm-error {color: #f00;} - -.cm-s-xq-light .CodeMirror-activeline-background {background: #e8f2ff !important;} -.cm-s-xq-light .CodeMirror-matchingbracket {outline:1px solid grey;color:black !important;background:yellow;} \ No newline at end of file diff --git a/public/js/lib/codemirror/theme/zenburn.css b/public/js/lib/codemirror/theme/zenburn.css deleted file mode 100644 index f817198af0..0000000000 --- a/public/js/lib/codemirror/theme/zenburn.css +++ /dev/null @@ -1,37 +0,0 @@ -/** - * " - * Using Zenburn color palette from the Emacs Zenburn Theme - * https://github.com/bbatsov/zenburn-emacs/blob/master/zenburn-theme.el - * - * Also using parts of https://github.com/xavi/coderay-lighttable-theme - * " - * From: https://github.com/wisenomad/zenburn-lighttable-theme/blob/master/zenburn.css - */ - -.cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; } -.cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; } -.cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white !important; } -.cm-s-zenburn { background-color: #3f3f3f; color: #dcdccc; } -.cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; } -.cm-s-zenburn span.cm-comment { color: #7f9f7f; } -.cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; } -.cm-s-zenburn span.cm-atom { color: #bfebbf; } -.cm-s-zenburn span.cm-def { color: #dcdccc; } -.cm-s-zenburn span.cm-variable { color: #dfaf8f; } -.cm-s-zenburn span.cm-variable-2 { color: #dcdccc; } -.cm-s-zenburn span.cm-string { color: #cc9393; } -.cm-s-zenburn span.cm-string-2 { color: #cc9393; } -.cm-s-zenburn span.cm-number { color: #dcdccc; } -.cm-s-zenburn span.cm-tag { color: #93e0e3; } -.cm-s-zenburn span.cm-property { color: #dfaf8f; } -.cm-s-zenburn span.cm-attribute { color: #dfaf8f; } -.cm-s-zenburn span.cm-qualifier { color: #7cb8bb; } -.cm-s-zenburn span.cm-meta { color: #f0dfaf; } -.cm-s-zenburn span.cm-header { color: #f0efd0; } -.cm-s-zenburn span.cm-operator { color: #f0efd0; } -.cm-s-zenburn span.CodeMirror-matchingbracket { box-sizing: border-box; background: transparent; border-bottom: 1px solid; } -.cm-s-zenburn span.CodeMirror-nonmatchingbracket { border-bottom: 1px solid; background: none; } -.cm-s-zenburn .CodeMirror-activeline { background: #000000; } -.cm-s-zenburn .CodeMirror-activeline-background { background: #000000; } -.cm-s-zenburn .CodeMirror-selected { background: #545454; } -.cm-s-zenburn .CodeMirror-focused .CodeMirror-selected { background: #4f4f4f; } diff --git a/public/js/lib/jquery-2.1.1.min.js b/public/js/lib/jquery-2.1.1.min.js deleted file mode 100644 index e5ace116b6..0000000000 --- a/public/js/lib/jquery-2.1.1.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v2.1.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ -!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l=a.document,m="2.1.1",n=function(a,b){return new n.fn.init(a,b)},o=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,p=/^-ms-/,q=/-([\da-z])/gi,r=function(a,b){return b.toUpperCase()};n.fn=n.prototype={jquery:m,constructor:n,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=n.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return n.each(this,a,b)},map:function(a){return this.pushStack(n.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},n.extend=n.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||n.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(a=arguments[h]))for(b in a)c=g[b],d=a[b],g!==d&&(j&&d&&(n.isPlainObject(d)||(e=n.isArray(d)))?(e?(e=!1,f=c&&n.isArray(c)?c:[]):f=c&&n.isPlainObject(c)?c:{},g[b]=n.extend(j,f,d)):void 0!==d&&(g[b]=d));return g},n.extend({expando:"jQuery"+(m+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===n.type(a)},isArray:Array.isArray,isWindow:function(a){return null!=a&&a===a.window},isNumeric:function(a){return!n.isArray(a)&&a-parseFloat(a)>=0},isPlainObject:function(a){return"object"!==n.type(a)||a.nodeType||n.isWindow(a)?!1:a.constructor&&!j.call(a.constructor.prototype,"isPrototypeOf")?!1:!0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(a){var b,c=eval;a=n.trim(a),a&&(1===a.indexOf("use strict")?(b=l.createElement("script"),b.text=a,l.head.appendChild(b).parentNode.removeChild(b)):c(a))},camelCase:function(a){return a.replace(p,"ms-").replace(q,r)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=s(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(o,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(s(Object(a))?n.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){return null==b?-1:g.call(b,a,c)},merge:function(a,b){for(var c=+b.length,d=0,e=a.length;c>d;d++)a[e++]=b[d];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=s(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(c=a[b],b=a,a=c),n.isFunction(a)?(e=d.call(arguments,2),f=function(){return a.apply(b||this,e.concat(d.call(arguments)))},f.guid=a.guid=a.guid||n.guid++,f):void 0},now:Date.now,support:k}),n.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function s(a){var b=a.length,c=n.type(a);return"function"===c||n.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var t=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);n.find=t,n.expr=t.selectors,n.expr[":"]=n.expr.pseudos,n.unique=t.uniqueSort,n.text=t.getText,n.isXMLDoc=t.isXML,n.contains=t.contains;var u=n.expr.match.needsContext,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function x(a,b,c){if(n.isFunction(b))return n.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return n.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(w.test(b))return n.filter(b,a,c);b=n.filter(b,a)}return n.grep(a,function(a){return g.call(b,a)>=0!==c})}n.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?n.find.matchesSelector(d,a)?[d]:[]:n.find.matches(a,n.grep(b,function(a){return 1===a.nodeType}))},n.fn.extend({find:function(a){var b,c=this.length,d=[],e=this;if("string"!=typeof a)return this.pushStack(n(a).filter(function(){for(b=0;c>b;b++)if(n.contains(e[b],this))return!0}));for(b=0;c>b;b++)n.find(a,e[b],d);return d=this.pushStack(c>1?n.unique(d):d),d.selector=this.selector?this.selector+" "+a:a,d},filter:function(a){return this.pushStack(x(this,a||[],!1))},not:function(a){return this.pushStack(x(this,a||[],!0))},is:function(a){return!!x(this,"string"==typeof a&&u.test(a)?n(a):a||[],!1).length}});var y,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=n.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||y).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof n?b[0]:b,n.merge(this,n.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:l,!0)),v.test(c[1])&&n.isPlainObject(b))for(c in b)n.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}return d=l.getElementById(c[2]),d&&d.parentNode&&(this.length=1,this[0]=d),this.context=l,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):n.isFunction(a)?"undefined"!=typeof y.ready?y.ready(a):a(n):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),n.makeArray(a,this))};A.prototype=n.fn,y=n(l);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};n.extend({dir:function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&n(a).is(c))break;d.push(a)}return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),n.fn.extend({has:function(a){var b=n(a,this),c=b.length;return this.filter(function(){for(var a=0;c>a;a++)if(n.contains(this,b[a]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=u.test(a)||"string"!=typeof a?n(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&n.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?n.unique(f):f)},index:function(a){return a?"string"==typeof a?g.call(n(a),this[0]):g.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(n.unique(n.merge(this.get(),n(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){while((a=a[b])&&1!==a.nodeType);return a}n.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return n.dir(a,"parentNode")},parentsUntil:function(a,b,c){return n.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return n.dir(a,"nextSibling")},prevAll:function(a){return n.dir(a,"previousSibling")},nextUntil:function(a,b,c){return n.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return n.dir(a,"previousSibling",c)},siblings:function(a){return n.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return n.sibling(a.firstChild)},contents:function(a){return a.contentDocument||n.merge([],a.childNodes)}},function(a,b){n.fn[a]=function(c,d){var e=n.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=n.filter(d,e)),this.length>1&&(C[a]||n.unique(e),B.test(a)&&e.reverse()),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return n.each(a.match(E)||[],function(a,c){b[c]=!0}),b}n.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):n.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(b=a.memory&&l,c=!0,g=e||0,e=0,f=h.length,d=!0;h&&f>g;g++)if(h[g].apply(l[0],l[1])===!1&&a.stopOnFalse){b=!1;break}d=!1,h&&(i?i.length&&j(i.shift()):b?h=[]:k.disable())},k={add:function(){if(h){var c=h.length;!function g(b){n.each(b,function(b,c){var d=n.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&g(c)})}(arguments),d?f=h.length:b&&(e=c,j(b))}return this},remove:function(){return h&&n.each(arguments,function(a,b){var c;while((c=n.inArray(b,h,c))>-1)h.splice(c,1),d&&(f>=c&&f--,g>=c&&g--)}),this},has:function(a){return a?n.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],f=0,this},disable:function(){return h=i=b=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,b||k.disable(),this},locked:function(){return!i},fireWith:function(a,b){return!h||c&&!i||(b=b||[],b=[a,b.slice?b.slice():b],d?i.push(b):j(b)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!c}};return k},n.extend({Deferred:function(a){var b=[["resolve","done",n.Callbacks("once memory"),"resolved"],["reject","fail",n.Callbacks("once memory"),"rejected"],["notify","progress",n.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return n.Deferred(function(c){n.each(b,function(b,f){var g=n.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&n.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?n.extend(a,d):d}},e={};return d.pipe=d.then,n.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&n.isFunction(a.promise)?e:0,g=1===f?a:n.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&n.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;n.fn.ready=function(a){return n.ready.promise().done(a),this},n.extend({isReady:!1,readyWait:1,holdReady:function(a){a?n.readyWait++:n.ready(!0)},ready:function(a){(a===!0?--n.readyWait:n.isReady)||(n.isReady=!0,a!==!0&&--n.readyWait>0||(H.resolveWith(l,[n]),n.fn.triggerHandler&&(n(l).triggerHandler("ready"),n(l).off("ready"))))}});function I(){l.removeEventListener("DOMContentLoaded",I,!1),a.removeEventListener("load",I,!1),n.ready()}n.ready.promise=function(b){return H||(H=n.Deferred(),"complete"===l.readyState?setTimeout(n.ready):(l.addEventListener("DOMContentLoaded",I,!1),a.addEventListener("load",I,!1))),H.promise(b)},n.ready.promise();var J=n.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===n.type(c)){e=!0;for(h in c)n.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,n.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(n(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f};n.acceptData=function(a){return 1===a.nodeType||9===a.nodeType||!+a.nodeType};function K(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=n.expando+Math.random()}K.uid=1,K.accepts=n.acceptData,K.prototype={key:function(a){if(!K.accepts(a))return 0;var b={},c=a[this.expando];if(!c){c=K.uid++;try{b[this.expando]={value:c},Object.defineProperties(a,b)}catch(d){b[this.expando]=c,n.extend(a,b)}}return this.cache[c]||(this.cache[c]={}),c},set:function(a,b,c){var d,e=this.key(a),f=this.cache[e];if("string"==typeof b)f[b]=c;else if(n.isEmptyObject(f))n.extend(this.cache[e],b);else for(d in b)f[d]=b[d];return f},get:function(a,b){var c=this.cache[this.key(a)];return void 0===b?c:c[b]},access:function(a,b,c){var d;return void 0===b||b&&"string"==typeof b&&void 0===c?(d=this.get(a,b),void 0!==d?d:this.get(a,n.camelCase(b))):(this.set(a,b,c),void 0!==c?c:b)},remove:function(a,b){var c,d,e,f=this.key(a),g=this.cache[f];if(void 0===b)this.cache[f]={};else{n.isArray(b)?d=b.concat(b.map(n.camelCase)):(e=n.camelCase(b),b in g?d=[b,e]:(d=e,d=d in g?[d]:d.match(E)||[])),c=d.length;while(c--)delete g[d[c]]}},hasData:function(a){return!n.isEmptyObject(this.cache[a[this.expando]]||{})},discard:function(a){a[this.expando]&&delete this.cache[a[this.expando]]}};var L=new K,M=new K,N=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,O=/([A-Z])/g;function P(a,b,c){var d;if(void 0===c&&1===a.nodeType)if(d="data-"+b.replace(O,"-$1").toLowerCase(),c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:N.test(c)?n.parseJSON(c):c}catch(e){}M.set(a,b,c)}else c=void 0;return c}n.extend({hasData:function(a){return M.hasData(a)||L.hasData(a)},data:function(a,b,c){return M.access(a,b,c)},removeData:function(a,b){M.remove(a,b) -},_data:function(a,b,c){return L.access(a,b,c)},_removeData:function(a,b){L.remove(a,b)}}),n.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=M.get(f),1===f.nodeType&&!L.get(f,"hasDataAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=n.camelCase(d.slice(5)),P(f,d,e[d])));L.set(f,"hasDataAttrs",!0)}return e}return"object"==typeof a?this.each(function(){M.set(this,a)}):J(this,function(b){var c,d=n.camelCase(a);if(f&&void 0===b){if(c=M.get(f,a),void 0!==c)return c;if(c=M.get(f,d),void 0!==c)return c;if(c=P(f,d,void 0),void 0!==c)return c}else this.each(function(){var c=M.get(this,d);M.set(this,d,b),-1!==a.indexOf("-")&&void 0!==c&&M.set(this,a,b)})},null,b,arguments.length>1,null,!0)},removeData:function(a){return this.each(function(){M.remove(this,a)})}}),n.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=L.get(a,b),c&&(!d||n.isArray(c)?d=L.access(a,b,n.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=n.queue(a,b),d=c.length,e=c.shift(),f=n._queueHooks(a,b),g=function(){n.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return L.get(a,c)||L.access(a,c,{empty:n.Callbacks("once memory").add(function(){L.remove(a,[b+"queue",c])})})}}),n.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthx",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var U="undefined";k.focusinBubbles="onfocusin"in a;var V=/^key/,W=/^(?:mouse|pointer|contextmenu)|click/,X=/^(?:focusinfocus|focusoutblur)$/,Y=/^([^.]*)(?:\.(.+)|)$/;function Z(){return!0}function $(){return!1}function _(){try{return l.activeElement}catch(a){}}n.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.get(a);if(r){c.handler&&(f=c,c=f.handler,e=f.selector),c.guid||(c.guid=n.guid++),(i=r.events)||(i=r.events={}),(g=r.handle)||(g=r.handle=function(b){return typeof n!==U&&n.event.triggered!==b.type?n.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(E)||[""],j=b.length;while(j--)h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o&&(l=n.event.special[o]||{},o=(e?l.delegateType:l.bindType)||o,l=n.event.special[o]||{},k=n.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&n.expr.match.needsContext.test(e),namespace:p.join(".")},f),(m=i[o])||(m=i[o]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,p,g)!==!1||a.addEventListener&&a.addEventListener(o,g,!1)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),n.event.global[o]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,o,p,q,r=L.hasData(a)&&L.get(a);if(r&&(i=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=Y.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=n.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,m=i[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&q!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||n.removeEvent(a,o,r.handle),delete i[o])}else for(o in i)n.event.remove(a,o+b[j],c,d,!0);n.isEmptyObject(i)&&(delete r.handle,L.remove(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,m,o,p=[d||l],q=j.call(b,"type")?b.type:b,r=j.call(b,"namespace")?b.namespace.split("."):[];if(g=h=d=d||l,3!==d.nodeType&&8!==d.nodeType&&!X.test(q+n.event.triggered)&&(q.indexOf(".")>=0&&(r=q.split("."),q=r.shift(),r.sort()),k=q.indexOf(":")<0&&"on"+q,b=b[n.expando]?b:new n.Event(q,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=r.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+r.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:n.makeArray(c,[b]),o=n.event.special[q]||{},e||!o.trigger||o.trigger.apply(d,c)!==!1)){if(!e&&!o.noBubble&&!n.isWindow(d)){for(i=o.delegateType||q,X.test(i+q)||(g=g.parentNode);g;g=g.parentNode)p.push(g),h=g;h===(d.ownerDocument||l)&&p.push(h.defaultView||h.parentWindow||a)}f=0;while((g=p[f++])&&!b.isPropagationStopped())b.type=f>1?i:o.bindType||q,m=(L.get(g,"events")||{})[b.type]&&L.get(g,"handle"),m&&m.apply(g,c),m=k&&g[k],m&&m.apply&&n.acceptData(g)&&(b.result=m.apply(g,c),b.result===!1&&b.preventDefault());return b.type=q,e||b.isDefaultPrevented()||o._default&&o._default.apply(p.pop(),c)!==!1||!n.acceptData(d)||k&&n.isFunction(d[q])&&!n.isWindow(d)&&(h=d[k],h&&(d[k]=null),n.event.triggered=q,d[q](),n.event.triggered=void 0,h&&(d[k]=h)),b.result}},dispatch:function(a){a=n.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(L.get(this,"events")||{})[a.type]||[],k=n.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=n.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,c=0;while((g=f.handlers[c++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(g.namespace))&&(a.handleObj=g,a.data=g.data,e=((n.event.special[g.origType]||{}).handle||g.handler).apply(f.elem,i),void 0!==e&&(a.result=e)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!==this;i=i.parentNode||this)if(i.disabled!==!0||"click"!==a.type){for(d=[],c=0;h>c;c++)f=b[c],e=f.selector+" ",void 0===d[e]&&(d[e]=f.needsContext?n(e,this).index(i)>=0:n.find(e,this,null,[i]).length),d[e]&&d.push(f);d.length&&g.push({elem:i,handlers:d})}return h]*)\/>/gi,bb=/<([\w:]+)/,cb=/<|&#?\w+;/,db=/<(?:script|style|link)/i,eb=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/^$|\/(?:java|ecma)script/i,gb=/^true\/(.*)/,hb=/^\s*\s*$/g,ib={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ib.optgroup=ib.option,ib.tbody=ib.tfoot=ib.colgroup=ib.caption=ib.thead,ib.th=ib.td;function jb(a,b){return n.nodeName(a,"table")&&n.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function kb(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function lb(a){var b=gb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function mb(a,b){for(var c=0,d=a.length;d>c;c++)L.set(a[c],"globalEval",!b||L.get(b[c],"globalEval"))}function nb(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(L.hasData(a)&&(f=L.access(a),g=L.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;d>c;c++)n.event.add(b,e,j[e][c])}M.hasData(a)&&(h=M.access(a),i=n.extend({},h),M.set(b,i))}}function ob(a,b){var c=a.getElementsByTagName?a.getElementsByTagName(b||"*"):a.querySelectorAll?a.querySelectorAll(b||"*"):[];return void 0===b||b&&n.nodeName(a,b)?n.merge([a],c):c}function pb(a,b){var c=b.nodeName.toLowerCase();"input"===c&&T.test(a.type)?b.checked=a.checked:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}n.extend({clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=n.contains(a.ownerDocument,a);if(!(k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||n.isXMLDoc(a)))for(g=ob(h),f=ob(a),d=0,e=f.length;e>d;d++)pb(f[d],g[d]);if(b)if(c)for(f=f||ob(a),g=g||ob(h),d=0,e=f.length;e>d;d++)nb(f[d],g[d]);else nb(a,h);return g=ob(h,"script"),g.length>0&&mb(g,!i&&ob(a,"script")),h},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,k=b.createDocumentFragment(),l=[],m=0,o=a.length;o>m;m++)if(e=a[m],e||0===e)if("object"===n.type(e))n.merge(l,e.nodeType?[e]:e);else if(cb.test(e)){f=f||k.appendChild(b.createElement("div")),g=(bb.exec(e)||["",""])[1].toLowerCase(),h=ib[g]||ib._default,f.innerHTML=h[1]+e.replace(ab,"<$1>")+h[2],j=h[0];while(j--)f=f.lastChild;n.merge(l,f.childNodes),f=k.firstChild,f.textContent=""}else l.push(b.createTextNode(e));k.textContent="",m=0;while(e=l[m++])if((!d||-1===n.inArray(e,d))&&(i=n.contains(e.ownerDocument,e),f=ob(k.appendChild(e),"script"),i&&mb(f),c)){j=0;while(e=f[j++])fb.test(e.type||"")&&c.push(e)}return k},cleanData:function(a){for(var b,c,d,e,f=n.event.special,g=0;void 0!==(c=a[g]);g++){if(n.acceptData(c)&&(e=c[L.expando],e&&(b=L.cache[e]))){if(b.events)for(d in b.events)f[d]?n.event.remove(c,d):n.removeEvent(c,d,b.handle);L.cache[e]&&delete L.cache[e]}delete M.cache[c[M.expando]]}}}),n.fn.extend({text:function(a){return J(this,function(a){return void 0===a?n.text(this):this.empty().each(function(){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&(this.textContent=a)})},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=jb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?n.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||n.cleanData(ob(c)),c.parentNode&&(b&&n.contains(c.ownerDocument,c)&&mb(ob(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(n.cleanData(ob(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return n.clone(this,a,b)})},html:function(a){return J(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!db.test(a)&&!ib[(bb.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(ab,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(n.cleanData(ob(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,n.cleanData(ob(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,m=this,o=l-1,p=a[0],q=n.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&eb.test(p))return this.each(function(c){var d=m.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(c=n.buildFragment(a,this[0].ownerDocument,!1,this),d=c.firstChild,1===c.childNodes.length&&(c=d),d)){for(f=n.map(ob(c,"script"),kb),g=f.length;l>j;j++)h=c,j!==o&&(h=n.clone(h,!0,!0),g&&n.merge(f,ob(h,"script"))),b.call(this[j],h,j);if(g)for(i=f[f.length-1].ownerDocument,n.map(f,lb),j=0;g>j;j++)h=f[j],fb.test(h.type||"")&&!L.access(h,"globalEval")&&n.contains(i,h)&&(h.src?n._evalUrl&&n._evalUrl(h.src):n.globalEval(h.textContent.replace(hb,"")))}return this}}),n.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){n.fn[a]=function(a){for(var c,d=[],e=n(a),g=e.length-1,h=0;g>=h;h++)c=h===g?this:this.clone(!0),n(e[h])[b](c),f.apply(d,c.get());return this.pushStack(d)}});var qb,rb={};function sb(b,c){var d,e=n(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:n.css(e[0],"display");return e.detach(),f}function tb(a){var b=l,c=rb[a];return c||(c=sb(a,b),"none"!==c&&c||(qb=(qb||n("