* text=auto |
*.wav | |
*.pyc | |
/nbproject/private/ | |
/output.txt | |
bin | |
gen | |
target | |
.settings | |
.classpath | |
.project | |
*.keystore | |
*.swp | |
*.orig | |
*.log | |
*.properties | |
seed.txt | |
map.txt | |
[submodule "pynma"] | |
path = pynma | |
url = git://github.com/uskr/pynma.git | |
[submodule "js/flotr2"] | |
path = js/flotr2 | |
url = https://github.com/HumbleSoftware/Flotr2.git | |
# Apache configuration file | |
# httpd.apache.org/docs/2.2/mod/quickreference.html | |
# Note .htaccess files are an overhead, this logic should be in your Apache | |
# config if possible: httpd.apache.org/docs/2.2/howto/htaccess.html | |
# Techniques in here adapted from all over, including: | |
# Kroc Camen: camendesign.com/.htaccess | |
# perishablepress.com/press/2006/01/10/stupid-htaccess-tricks/ | |
# Sample .htaccess file of CMS MODx: modxcms.com | |
# ---------------------------------------------------------------------- | |
# Better website experience for IE users | |
# ---------------------------------------------------------------------- | |
# Force the latest IE version, in various cases when it may fall back to IE7 mode | |
# github.com/rails/rails/commit/123eb25#commitcomment-118920 | |
# Use ChromeFrame if it's installed for a better experience for the poor IE folk | |
<IfModule mod_headers.c> | |
Header set X-UA-Compatible "IE=Edge,chrome=1" | |
# mod_headers can't match by content-type, but we don't want to send this header on *everything*... | |
<FilesMatch "\.(js|css|gif|png|jpe?g|pdf|xml|oga|ogg|m4a|ogv|mp4|m4v|webm|svg|svgz|eot|ttf|otf|woff|ico|webp|appcache|manifest|htc|crx|oex|xpi|safariextz|vcf)$" > | |
Header unset X-UA-Compatible | |
</FilesMatch> | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Cross-domain AJAX requests | |
# ---------------------------------------------------------------------- | |
# Serve cross-domain Ajax requests, disabled by default. | |
# enable-cors.org | |
# code.google.com/p/html5security/wiki/CrossOriginRequestSecurity | |
# <IfModule mod_headers.c> | |
# Header set Access-Control-Allow-Origin "*" | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# CORS-enabled images (@crossorigin) | |
# ---------------------------------------------------------------------- | |
# Send CORS headers if browsers request them; enabled by default for images. | |
# developer.mozilla.org/en/CORS_Enabled_Image | |
# blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html | |
# hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ | |
# wiki.mozilla.org/Security/Reviews/crossoriginAttribute | |
<IfModule mod_setenvif.c> | |
<IfModule mod_headers.c> | |
# mod_headers, y u no match by Content-Type?! | |
<FilesMatch "\.(gif|png|jpe?g|svg|svgz|ico|webp)$"> | |
SetEnvIf Origin ":" IS_CORS | |
Header set Access-Control-Allow-Origin "*" env=IS_CORS | |
</FilesMatch> | |
</IfModule> | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Webfont access | |
# ---------------------------------------------------------------------- | |
# Allow access from all domains for webfonts. | |
# Alternatively you could only whitelist your | |
# subdomains like "subdomain.example.com". | |
<IfModule mod_headers.c> | |
<FilesMatch "\.(ttf|ttc|otf|eot|woff|font.css)$"> | |
Header set Access-Control-Allow-Origin "*" | |
</FilesMatch> | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Proper MIME type for all files | |
# ---------------------------------------------------------------------- | |
# JavaScript | |
# Normalize to standard type (it's sniffed in IE anyways) | |
# tools.ietf.org/html/rfc4329#section-7.2 | |
AddType application/javascript js jsonp | |
AddType application/json json | |
# Audio | |
AddType audio/ogg oga ogg | |
AddType audio/mp4 m4a f4a f4b | |
# Video | |
AddType video/ogg ogv | |
AddType video/mp4 mp4 m4v f4v f4p | |
AddType video/webm webm | |
AddType video/x-flv flv | |
# SVG | |
# Required for svg webfonts on iPad | |
# twitter.com/FontSquirrel/status/14855840545 | |
AddType image/svg+xml svg svgz | |
AddEncoding gzip svgz | |
# Webfonts | |
AddType application/vnd.ms-fontobject eot | |
AddType application/x-font-ttf ttf ttc | |
AddType font/opentype otf | |
AddType application/x-font-woff woff | |
# Assorted types | |
AddType image/x-icon ico | |
AddType image/webp webp | |
AddType text/cache-manifest appcache manifest | |
AddType text/x-component htc | |
AddType application/xml rss atom xml rdf | |
AddType application/x-chrome-extension crx | |
AddType application/x-opera-extension oex | |
AddType application/x-xpinstall xpi | |
AddType application/octet-stream safariextz | |
AddType application/x-web-app-manifest+json webapp | |
AddType text/x-vcard vcf | |
AddType application/x-shockwave-flash swf | |
AddType text/vtt vtt | |
# ---------------------------------------------------------------------- | |
# Allow concatenation from within specific js and css files | |
# ---------------------------------------------------------------------- | |
# e.g. Inside of script.combined.js you could have | |
# <!--#include file="libs/jquery-1.5.0.min.js" --> | |
# <!--#include file="plugins/jquery.idletimer.js" --> | |
# and they would be included into this single file. | |
# This is not in use in the boilerplate as it stands. You may | |
# choose to use this technique if you do not have a build process. | |
#<FilesMatch "\.combined\.js$"> | |
# Options +Includes | |
# AddOutputFilterByType INCLUDES application/javascript application/json | |
# SetOutputFilter INCLUDES | |
#</FilesMatch> | |
#<FilesMatch "\.combined\.css$"> | |
# Options +Includes | |
# AddOutputFilterByType INCLUDES text/css | |
# SetOutputFilter INCLUDES | |
#</FilesMatch> | |
# ---------------------------------------------------------------------- | |
# Gzip compression | |
# ---------------------------------------------------------------------- | |
<IfModule mod_deflate.c> | |
# Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/ | |
<IfModule mod_setenvif.c> | |
<IfModule mod_headers.c> | |
SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding | |
RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding | |
</IfModule> | |
</IfModule> | |
# Compress all output labeled with one of the following MIME-types | |
<IfModule mod_filter.c> | |
AddOutputFilterByType DEFLATE application/atom+xml \ | |
application/javascript \ | |
application/json \ | |
application/rss+xml \ | |
application/vnd.ms-fontobject \ | |
application/x-font-ttf \ | |
application/xhtml+xml \ | |
application/xml \ | |
font/opentype \ | |
image/svg+xml \ | |
image/x-icon \ | |
text/css \ | |
text/html \ | |
text/plain \ | |
text/x-component \ | |
text/xml | |
</IfModule> | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Expires headers (for better cache control) | |
# ---------------------------------------------------------------------- | |
# These are pretty far-future expires headers. | |
# They assume you control versioning with filename-based cache busting | |
# Additionally, consider that outdated proxies may miscache | |
# www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/ | |
# If you don't use filenames to version, lower the CSS and JS to something like | |
# "access plus 1 week". | |
<IfModule mod_expires.c> | |
ExpiresActive on | |
# Perhaps better to whitelist expires rules? Perhaps. | |
ExpiresDefault "access plus 1 month" | |
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5) | |
ExpiresByType text/cache-manifest "access plus 0 seconds" | |
# Your document html | |
ExpiresByType text/html "access plus 0 seconds" | |
# Data | |
ExpiresByType text/xml "access plus 0 seconds" | |
ExpiresByType application/xml "access plus 0 seconds" | |
ExpiresByType application/json "access plus 0 seconds" | |
# Feed | |
ExpiresByType application/rss+xml "access plus 1 hour" | |
ExpiresByType application/atom+xml "access plus 1 hour" | |
# Favicon (cannot be renamed) | |
ExpiresByType image/x-icon "access plus 1 week" | |
# Media: images, video, audio | |
ExpiresByType image/gif "access plus 1 month" | |
ExpiresByType image/png "access plus 1 month" | |
ExpiresByType image/jpeg "access plus 1 month" | |
ExpiresByType video/ogg "access plus 1 month" | |
ExpiresByType audio/ogg "access plus 1 month" | |
ExpiresByType video/mp4 "access plus 1 month" | |
ExpiresByType video/webm "access plus 1 month" | |
# HTC files (css3pie) | |
ExpiresByType text/x-component "access plus 1 month" | |
# Webfonts | |
ExpiresByType application/x-font-ttf "access plus 1 month" | |
ExpiresByType font/opentype "access plus 1 month" | |
ExpiresByType application/x-font-woff "access plus 1 month" | |
ExpiresByType image/svg+xml "access plus 1 month" | |
ExpiresByType application/vnd.ms-fontobject "access plus 1 month" | |
# CSS and JavaScript | |
ExpiresByType text/css "access plus 1 year" | |
ExpiresByType application/javascript "access plus 1 year" | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Prevent mobile network providers from modifying your site | |
# ---------------------------------------------------------------------- | |
# The following header prevents modification of your code over 3G on some | |
# European providers. | |
# This is the official 'bypass' suggested by O2 in the UK. | |
# <IfModule mod_headers.c> | |
# Header set Cache-Control "no-transform" | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# ETag removal | |
# ---------------------------------------------------------------------- | |
# FileETag None is not enough for every server. | |
<IfModule mod_headers.c> | |
Header unset ETag | |
</IfModule> | |
# Since we're sending far-future expires, we don't need ETags for | |
# static content. | |
# developer.yahoo.com/performance/rules.html#etags | |
FileETag None | |
# ---------------------------------------------------------------------- | |
# Stop screen flicker in IE on CSS rollovers | |
# ---------------------------------------------------------------------- | |
# The following directives stop screen flicker in IE on CSS rollovers - in | |
# combination with the "ExpiresByType" rules for images (see above). | |
# BrowserMatch "MSIE" brokenvary=1 | |
# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 | |
# BrowserMatch "Opera" !brokenvary | |
# SetEnvIf brokenvary 1 force-no-vary | |
# ---------------------------------------------------------------------- | |
# Set Keep-Alive Header | |
# ---------------------------------------------------------------------- | |
# Keep-Alive allows the server to send multiple requests through one | |
# TCP-connection. Be aware of possible disadvantages of this setting. Turn on | |
# if you serve a lot of static content. | |
# <IfModule mod_headers.c> | |
# Header set Connection Keep-Alive | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# Cookie setting from iframes | |
# ---------------------------------------------------------------------- | |
# Allow cookies to be set from iframes (for IE only) | |
# If needed, specify a path or regex in the Location directive. | |
# <IfModule mod_headers.c> | |
# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# Start rewrite engine | |
# ---------------------------------------------------------------------- | |
# Turning on the rewrite engine is necessary for the following rules and | |
# features. FollowSymLinks must be enabled for this to work. | |
# Some cloud hosting services require RewriteBase to be set: goo.gl/HOcPN | |
# If using the h5bp in a subdirectory, use `RewriteBase /foo` instead where | |
# 'foo' is your directory. | |
# If your web host doesn't allow the FollowSymlinks option, you may need to | |
# comment it out and use `Options +SymLinksOfOwnerMatch`, but be aware of the | |
# performance impact: http://goo.gl/Mluzd | |
<IfModule mod_rewrite.c> | |
Options +FollowSymlinks | |
# Options +SymLinksIfOwnerMatch | |
RewriteEngine On | |
# RewriteBase / | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Suppress or force the "www." at the beginning of URLs | |
# ---------------------------------------------------------------------- | |
# The same content should never be available under two different URLs - | |
# especially not with and without "www." at the beginning, since this can cause | |
# SEO problems (duplicate content). That's why you should choose one of the | |
# alternatives and redirect the other one. | |
# By default option 1 (no "www.") is activated. | |
# no-www.org/faq.php?q=class_b | |
# If you'd prefer to use option 2, just comment out all option 1 lines | |
# and uncomment option 2. | |
# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! | |
# ---------------------------------------------------------------------- | |
# Option 1: | |
# Rewrite "www.example.com -> example.com". | |
<IfModule mod_rewrite.c> | |
RewriteCond %{HTTPS} !=on | |
RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] | |
RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] | |
</IfModule> | |
# ---------------------------------------------------------------------- | |
# Option 2: | |
# Rewrite "example.com -> www.example.com". | |
# Be aware that the following rule might not be a good idea if you use "real" | |
# subdomains for certain parts of your website. | |
# <IfModule mod_rewrite.c> | |
# RewriteCond %{HTTPS} !=on | |
# RewriteCond %{HTTP_HOST} !^www\..+$ [NC] | |
# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# Built-in filename-based cache busting | |
# ---------------------------------------------------------------------- | |
# If you're not using the build script to manage your filename version revving, | |
# you might want to consider enabling this, which will route requests for | |
# /css/style.20110203.css to /css/style.css | |
# To understand why this is important and a better idea than all.css?v1231, | |
# read: github.com/h5bp/html5-boilerplate/wiki/cachebusting | |
# <IfModule mod_rewrite.c> | |
# RewriteCond %{REQUEST_FILENAME} !-f | |
# RewriteCond %{REQUEST_FILENAME} !-d | |
# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# Prevent SSL cert warnings | |
# ---------------------------------------------------------------------- | |
# Rewrite secure requests properly to prevent SSL cert warnings, e.g. prevent | |
# https://www.example.com when your cert only allows https://secure.example.com | |
# <IfModule mod_rewrite.c> | |
# RewriteCond %{SERVER_PORT} !^443 | |
# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] | |
# </IfModule> | |
# ---------------------------------------------------------------------- | |
# Prevent 404 errors for non-existing redirected folders | |
# ---------------------------------------------------------------------- | |
# without -MultiViews, Apache will give a 404 for a rewrite if a folder of the | |
# same name does not exist. | |
# webmasterworld.com/apache/3808792.htm | |
Options -MultiViews | |
# ---------------------------------------------------------------------- | |
# Custom 404 page | |
# ---------------------------------------------------------------------- | |
# You can add custom pages to handle 500 or 403 pretty easily, if you like. | |
# If you are hosting your site in subdirectory, adjust this accordingly | |
# e.g. ErrorDocument 404 /subdir/404.html | |
ErrorDocument 404 /404.html | |
# ---------------------------------------------------------------------- | |
# UTF-8 encoding | |
# ---------------------------------------------------------------------- | |
# Use UTF-8 encoding for anything served text/plain or text/html | |
AddDefaultCharset utf-8 | |
# Force UTF-8 for a number of file formats | |
AddCharset utf-8 .atom .css .js .json .rss .vtt .xml | |
# ---------------------------------------------------------------------- | |
# A little more security | |
# ---------------------------------------------------------------------- | |
# To avoid displaying the exact version number of Apache being used, add the | |
# following to httpd.conf (it will not work in .htaccess): | |
# ServerTokens Prod | |
# "-Indexes" will have Apache block users from browsing folders without a | |
# default document Usually you should leave this activated, because you | |
# shouldn't allow everybody to surf through every folder on your server (which | |
# includes rather private places like CMS system folders). | |
<IfModule mod_autoindex.c> | |
Options -Indexes | |
</IfModule> | |
# Block access to "hidden" directories or files whose names begin with a | |
# period. This includes directories used by version control systems such as | |
# Subversion or Git. | |
<IfModule mod_rewrite.c> | |
RewriteCond %{SCRIPT_FILENAME} -d [OR] | |
RewriteCond %{SCRIPT_FILENAME} -f | |
RewriteRule "(^|/)\." - [F] | |
</IfModule> | |
# Block access to backup and source files. These files may be left by some | |
# text/html editors and pose a great security danger, when anyone can access | |
# them. | |
<FilesMatch "(\.(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)|~)$"> | |
Order allow,deny | |
Deny from all | |
Satisfy All | |
</FilesMatch> | |
# If your server is not already configured as such, the following directive | |
# should be uncommented in order to set PHP's register_globals option to OFF. | |
# This closes a major security hole that is abused by most XSS (cross-site | |
# scripting) attacks. For more information: http://php.net/register_globals | |
# | |
# IF REGISTER_GLOBALS DIRECTIVE CAUSES 500 INTERNAL SERVER ERRORS: | |
# | |
# Your server does not allow PHP directives to be set via .htaccess. In that | |
# case you must make this change in your php.ini file instead. If you are | |
# using a commercial web host, contact the administrators for assistance in | |
# doing this. Not all servers allow local php.ini files, and they should | |
# include all PHP configurations (not just this one), or you will effectively | |
# reset everything to PHP defaults. Consult www.php.net for more detailed | |
# information about setting PHP directives. | |
# php_flag register_globals Off | |
# Rename session cookie to something else, than PHPSESSID | |
# php_value session.name sid | |
# Disable magic quotes (This feature has been DEPRECATED as of PHP 5.3.0 and REMOVED as of PHP 5.4.0.) | |
# php_flag magic_quotes_gpc Off | |
# Do not show you are using PHP | |
# Note: Move this line to php.ini since it won't work in .htaccess | |
# php_flag expose_php Off | |
# Level of log detail - log all errors | |
# php_value error_reporting -1 | |
# Write errors to log file | |
# php_flag log_errors On | |
# Do not display errors in browser (production - Off, development - On) | |
# php_flag display_errors Off | |
# Do not display startup errors (production - Off, development - On) | |
# php_flag display_startup_errors Off | |
# Format errors in plain text | |
# Note: Leave this setting 'On' for xdebug's var_dump() output | |
# php_flag html_errors Off | |
# Show multiple occurrence of error | |
# php_flag ignore_repeated_errors Off | |
# Show same errors from different sources | |
# php_flag ignore_repeated_source Off | |
# Size limit for error messages | |
# php_value log_errors_max_len 1024 | |
# Don't precede error with string (doesn't accept empty string, use whitespace if you need) | |
# php_value error_prepend_string " " | |
# Don't prepend to error (doesn't accept empty string, use whitespace if you need) | |
# php_value error_append_string " " | |
# Increase cookie security | |
<IfModule php5_module> | |
php_value session.cookie_httponly true | |
</IfModule> | |
scannr |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="CompilerConfiguration"> | |
<option name="DEFAULT_COMPILER" value="Javac" /> | |
<resourceExtensions /> | |
<wildcardResourcePatterns> | |
<entry name="!?*.java" /> | |
<entry name="!?*.form" /> | |
<entry name="!?*.class" /> | |
<entry name="!?*.groovy" /> | |
<entry name="!?*.scala" /> | |
<entry name="!?*.flex" /> | |
<entry name="!?*.kt" /> | |
<entry name="!?*.clj" /> | |
</wildcardResourcePatterns> | |
<annotationProcessing> | |
<profile default="true" name="Default" enabled="false"> | |
<processorPath useClasspath="true" /> | |
</profile> | |
</annotationProcessing> | |
</component> | |
</project> | |
<component name="CopyrightManager"> | |
<settings default=""> | |
<module2copyright /> | |
</settings> | |
</component> |
<component name="ProjectDictionaryState"> | |
<dictionary name="Madoka"> | |
<words> | |
<w>tgid</w> | |
<w>timefrom</w> | |
<w>timeto</w> | |
<w>tzoffset</w> | |
</words> | |
</dictionary> | |
</component> |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> | |
</project> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectResources"> | |
<default-html-doctype>jar:file:\C:\Program Files (x86)\JetBrains\PhpStorm 5.0.2\lib\webide.jar!\resources\html5-schema\html5.rnc</default-html-doctype> | |
</component> | |
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_3" assert-keyword="false" jdk-15="false" /> | |
</project> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ProjectModuleManager"> | |
<modules> | |
<module fileurl="file://$PROJECT_DIR$/.idea/scannr.iml" filepath="$PROJECT_DIR$/.idea/scannr.iml" /> | |
</modules> | |
</component> | |
</project> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<module type="WEB_MODULE" version="4"> | |
<component name="FacetManager"> | |
<facet type="Python" name="Python"> | |
<configuration sdkName="" /> | |
</facet> | |
</component> | |
<component name="NewModuleRootManager" inherit-compiler-output="false"> | |
<content url="file://$MODULE_DIR$" /> | |
<orderEntry type="inheritedJdk" /> | |
<orderEntry type="sourceFolder" forTests="false" /> | |
</component> | |
</module> | |
<component name="DependencyValidationManager"> | |
<state> | |
<option name="SKIP_IMPORT_STATEMENTS" value="false" /> | |
</state> | |
</component> |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="Palette2"> | |
<group name="Swing"> | |
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> | |
</item> | |
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> | |
</item> | |
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> | |
</item> | |
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> | |
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> | |
</item> | |
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> | |
<initial-values> | |
<property name="text" value="Button" /> | |
</initial-values> | |
</item> | |
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> | |
<initial-values> | |
<property name="text" value="RadioButton" /> | |
</initial-values> | |
</item> | |
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> | |
<initial-values> | |
<property name="text" value="CheckBox" /> | |
</initial-values> | |
</item> | |
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> | |
<initial-values> | |
<property name="text" value="Label" /> | |
</initial-values> | |
</item> | |
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> | |
<preferred-size width="150" height="-1" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> | |
<preferred-size width="150" height="-1" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> | |
<preferred-size width="150" height="-1" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> | |
</item> | |
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> | |
<preferred-size width="150" height="50" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> | |
<preferred-size width="200" height="200" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> | |
<preferred-size width="200" height="200" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> | |
</item> | |
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> | |
</item> | |
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> | |
</item> | |
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> | |
</item> | |
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> | |
<preferred-size width="-1" height="20" /> | |
</default-constraints> | |
</item> | |
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> | |
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> | |
</item> | |
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> | |
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> | |
</item> | |
</group> | |
</component> | |
</project> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="VcsDirectoryMappings"> | |
<mapping directory="$PROJECT_DIR$" vcs="Git" /> | |
<mapping directory="$PROJECT_DIR$/js/flotr2" vcs="Git" /> | |
<mapping directory="$PROJECT_DIR$/pynma" vcs="Git" /> | |
</component> | |
</project> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<project version="4"> | |
<component name="ChangeListManager"> | |
<list default="true" id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment=""> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Chart.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Chart.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Color.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Color.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Flotr.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Flotr.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/Graph.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/Graph.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/base64.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/base64.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/basic.json" afterPath="$PROJECT_DIR$/js/flotr2/make/basic.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/build.json" afterPath="$PROJECT_DIR$/js/flotr2/make/build.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/examples.js" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/examples.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/examples.json" afterPath="$PROJECT_DIR$/js/flotr2/make/examples.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/extending-flotr.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/extending-flotr.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/flotr2.json" afterPath="$PROJECT_DIR$/js/flotr2/make/flotr2.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/flotr2.stable.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/flotr2.stable.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/lib/codemirror/lib/util/formatting.js" afterPath="$PROJECT_DIR$/js/flotr2/examples/lib/codemirror/lib/util/formatting.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/generateHourlys.php" afterPath="$PROJECT_DIR$/generateHourlys.php" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/ie.json" afterPath="$PROJECT_DIR$/js/flotr2/make/ie.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/index.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/index.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/index.html" afterPath="$PROJECT_DIR$/js/flotr2/spec/index.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine-html.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine-html.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.css" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.css" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/jasmine/jasmine.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-data.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-data.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-real-data.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json-real-data.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json.txt" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/json.txt" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/make/lib.json" afterPath="$PROJECT_DIR$/js/flotr2/make/lib.json" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/logarithmic-scale.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/logarithmic-scale.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/mouse-zoom-preview.html" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/mouse-zoom-preview.html" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/lib/prototype.js" afterPath="$PROJECT_DIR$/js/flotr2/lib/prototype.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/examples/old_examples/style.css" afterPath="$PROJECT_DIR$/js/flotr2/examples/old_examples/style.css" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-background.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-background.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-boundaries.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-boundaries.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/js/flotr2/spec/js/test-mountain-nulls.js" afterPath="$PROJECT_DIR$/js/flotr2/spec/js/test-mountain-nulls.js" /> | |
<change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" /> | |
</list> | |
<ignored path="scannr.iws" /> | |
<ignored path=".idea/workspace.xml" /> | |
<file path="/Dummy.txt" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356959804071" ignored="false" /> | |
<file path="/calllog.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356153807482" ignored="false" /> | |
<file path="/scannr.py" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356154551131" ignored="false" /> | |
<file path="/start_script.py" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155203132" ignored="false" /> | |
<file path="/a.java" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155211924" ignored="false" /> | |
<file path="/a.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155216083" ignored="false" /> | |
<file path="/calls.json.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356155700744" ignored="false" /> | |
<file path="/generateHourlys.php" changelist="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" time="1356960167594" ignored="false" /> | |
<option name="TRACKING_ENABLED" value="true" /> | |
<option name="SHOW_DIALOG" value="false" /> | |
<option name="HIGHLIGHT_CONFLICTS" value="true" /> | |
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | |
<option name="LAST_RESOLUTION" value="IGNORE" /> | |
</component> | |
<component name="ChangesViewManager" flattened_view="true" show_ignored="false" /> | |
<component name="CreatePatchCommitExecutor"> | |
<option name="PATCH_PATH" value="" /> | |
</component> | |
<component name="DaemonCodeAnalyzer"> | |
<disable_hints /> | |
</component> | |
<component name="DebuggerManager"> | |
<breakpoint_any default_suspend_policy="SuspendAll" default_condition_enabled="true"> | |
<breakpoint> | |
<option name="NOTIFY_CAUGHT" value="true" /> | |
<option name="NOTIFY_UNCAUGHT" value="true" /> | |
<option name="ENABLED" value="false" /> | |
<option name="LOG_ENABLED" value="false" /> | |
<option name="LOG_EXPRESSION_ENABLED" value="false" /> | |
<option name="SUSPEND_POLICY" value="SuspendAll" /> | |
<option name="SUSPEND" value="true" /> | |
<option name="COUNT_FILTER_ENABLED" value="false" /> | |
<option name="COUNT_FILTER" value="0" /> | |
<option name="CONDITION_ENABLED" value="true" /> | |
<option name="CLASS_FILTERS_ENABLED" value="false" /> | |
<option name="INSTANCE_FILTERS_ENABLED" value="false" /> | |
<option name="CONDITION" value="" /> | |
<option name="LOG_MESSAGE" value="" /> | |
</breakpoint> | |
<breakpoint> | |
<option name="NOTIFY_CAUGHT" value="true" /> | |
<option name="NOTIFY_UNCAUGHT" value="true" /> | |
<option name="ENABLED" value="false" /> | |
<option name="LOG_ENABLED" value="false" /> | |
<option name="LOG_EXPRESSION_ENABLED" value="false" /> | |
<option name="SUSPEND_POLICY" value="SuspendAll" /> | |
<option name="SUSPEND" value="true" /> | |
<option name="COUNT_FILTER_ENABLED" value="false" /> | |
<option name="COUNT_FILTER" value="0" /> | |
<option name="CONDITION_ENABLED" value="true" /> | |
<option name="CLASS_FILTERS_ENABLED" value="false" /> | |
<option name="INSTANCE_FILTERS_ENABLED" value="false" /> | |
<option name="CONDITION" value="" /> | |
<option name="LOG_MESSAGE" value="" /> | |
</breakpoint> | |
</breakpoint_any> | |
<ui_properties default_suspend_policy="SuspendAll" default_condition_enabled="true" /> | |
<breakpoint_rules /> | |
<ui_properties /> | |
</component> | |
<component name="ExecutionTargetManager" SELECTED_TARGET="default_target" /> | |
<component name="FavoritesManager"> | |
<favorites_list name="scannr" /> | |
</component> | |
<component name="FileEditorManager"> | |
<leaf> | |
<file leaf-file-name="calllog.php" pinned="false" current="false" current-in-tab="false"> | |
<entry file="file://$PROJECT_DIR$/calllog.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="2" column="0" selection-start="34" selection-end="226" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</file> | |
<file leaf-file-name="calls.json.php" pinned="false" current="false" current-in-tab="false"> | |
<entry file="file://$PROJECT_DIR$/calls.json.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="13" column="53" selection-start="499" selection-end="499" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</file> | |
<file leaf-file-name="generateConvos.php" pinned="false" current="false" current-in-tab="false"> | |
<entry file="file://$PROJECT_DIR$/generateConvos.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</file> | |
<file leaf-file-name="generateHourlys.php" pinned="false" current="true" current-in-tab="true"> | |
<entry file="file://$PROJECT_DIR$/generateHourlys.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="23" column="6" selection-start="1012" selection-end="1012" vertical-scroll-proportion="0.68944097"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</file> | |
<file leaf-file-name="getfile.php" pinned="false" current="false" current-in-tab="false"> | |
<entry file="file://$PROJECT_DIR$/getfile.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="11" column="13" selection-start="471" selection-end="539" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</file> | |
</leaf> | |
</component> | |
<component name="FindManager"> | |
<FindUsagesManager> | |
<setting name="OPEN_NEW_TAB" value="false" /> | |
</FindUsagesManager> | |
</component> | |
<component name="Git.Settings"> | |
<option name="SYNC_SETTING" value="DONT" /> | |
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> | |
</component> | |
<component name="GitLogSettings"> | |
<option name="myDateState"> | |
<MyDateState /> | |
</option> | |
</component> | |
<component name="IdeDocumentHistory"> | |
<option name="changedFiles"> | |
<list> | |
<option value="$PROJECT_DIR$/common.inc.php" /> | |
<option value="$PROJECT_DIR$/viewcalls.php" /> | |
<option value="$PROJECT_DIR$/calllog.php" /> | |
<option value="$PROJECT_DIR$/scannr.py" /> | |
<option value="$PROJECT_DIR$/calls.json.php" /> | |
<option value="$PROJECT_DIR$/generateHourlys.php" /> | |
</list> | |
</option> | |
</component> | |
<component name="PhpWorkspaceProjectConfiguration" backward_compatibility_performed="true" interpreter_name="PHP" /> | |
<component name="ProjectFrameBounds"> | |
<option name="y" value="22" /> | |
<option name="width" value="1680" /> | |
<option name="height" value="936" /> | |
</component> | |
<component name="ProjectLevelVcsManager" settingsEditedManually="true"> | |
<OptionsSetting value="true" id="Add" /> | |
<OptionsSetting value="true" id="Remove" /> | |
<OptionsSetting value="true" id="Checkout" /> | |
<OptionsSetting value="true" id="Update" /> | |
<OptionsSetting value="true" id="Status" /> | |
<OptionsSetting value="true" id="Edit" /> | |
<ConfirmationsSetting value="0" id="Add" /> | |
<ConfirmationsSetting value="0" id="Remove" /> | |
</component> | |
<component name="ProjectReloadState"> | |
<option name="STATE" value="0" /> | |
</component> | |
<component name="ProjectView"> | |
<navigator currentView="ProjectPane" proportions="" version="1" splitterProportion="0.5"> | |
<flattenPackages /> | |
<showMembers /> | |
<showModules /> | |
<showLibraryContents ProjectPane="true" /> | |
<hideEmptyPackages /> | |
<abbreviatePackageNames /> | |
<autoscrollToSource /> | |
<autoscrollFromSource /> | |
<sortByType /> | |
</navigator> | |
<panes> | |
<pane id="Scope" /> | |
<pane id="PackagesPane" /> | |
<pane id="ProjectPane"> | |
<subPane> | |
<PATH> | |
<PATH_ELEMENT> | |
<option name="myItemId" value="scannr" /> | |
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> | |
</PATH_ELEMENT> | |
</PATH> | |
<PATH> | |
<PATH_ELEMENT> | |
<option name="myItemId" value="scannr" /> | |
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.ProjectViewProjectNode" /> | |
</PATH_ELEMENT> | |
<PATH_ELEMENT> | |
<option name="myItemId" value="scannr" /> | |
<option name="myItemType" value="com.intellij.ide.projectView.impl.nodes.PsiDirectoryNode" /> | |
</PATH_ELEMENT> | |
</PATH> | |
</subPane> | |
</pane> | |
</panes> | |
</component> | |
<component name="PropertiesComponent"> | |
<property name="options.splitter.main.proportions" value="0.3" /> | |
<property name="WebServerToolWindowFactoryState" value="false" /> | |
<property name="options.lastSelected" value="tasks" /> | |
<property name="last_opened_file_path" value="$PROJECT_DIR$/../disclosr" /> | |
<property name="FullScreen" value="false" /> | |
<property name="options.searchVisible" value="true" /> | |
<property name="options.splitter.details.proportions" value="0.2" /> | |
</component> | |
<component name="PyConsoleOptionsProvider"> | |
<option name="myPythonConsoleState"> | |
<PyConsoleSettings /> | |
</option> | |
<option name="myDjangoConsoleState"> | |
<PyConsoleSettings /> | |
</option> | |
</component> | |
<component name="RecentsManager"> | |
<key name="CopyFile.RECENT_KEYS"> | |
<recent name="$PROJECT_DIR$" /> | |
</key> | |
</component> | |
<component name="RunManager"> | |
<configuration default="true" type="PHPUnitRunConfigurationType" factoryName="PHPUnit"> | |
<TestRunner /> | |
<method /> | |
</configuration> | |
<configuration default="true" type="PhpLocalRunConfigurationType" factoryName="PHP Console"> | |
<method /> | |
</configuration> | |
<configuration default="true" type="tests" factoryName="Doctests"> | |
<option name="INTERPRETER_OPTIONS" value="" /> | |
<option name="PARENT_ENVS" value="true" /> | |
<envs /> | |
<option name="SDK_HOME" value="" /> | |
<option name="WORKING_DIRECTORY" value="" /> | |
<option name="IS_MODULE_SDK" value="false" /> | |
<module name="scannr" /> | |
<EXTENSION ID="PythonCoverageRunConfigurationExtension" enabled="false" sample_coverage="true" runner="coverage.py" /> | |
<option name="SCRIPT_NAME" value="" /> | |
<option name="CLASS_NAME" value="" /> | |
<option name="METHOD_NAME" value="" /> | |
<option name="FOLDER_NAME" value="" /> | |
<option name="TEST_TYPE" value="TEST_SCRIPT" /> | |
<option name="PATTERN" value="" /> | |
<option name="USE_PATTERN" value="false" /> | |
<method /> | |
</configuration> | |
<configuration default="true" type="JavascriptDebugSession" factoryName="Local" singleton="true"> | |
<JSDebuggerConfigurationSettings> | |
<option name="engineId" value="embedded" /> | |
<option name="fileUrl" /> | |
</JSDebuggerConfigurationSettings> | |
<method /> | |
</configuration> | |
<list size="0" /> | |
</component> | |
<component name="ShelveChangesManager" show_recycled="false" /> | |
<component name="SvnConfiguration" maxAnnotateRevisions="500" myUseAcceleration="nothing" myAutoUpdateAfterCommit="false" cleanupOnStartRun="false"> | |
<option name="USER" value="" /> | |
<option name="PASSWORD" value="" /> | |
<option name="mySSHConnectionTimeout" value="30000" /> | |
<option name="mySSHReadTimeout" value="30000" /> | |
<option name="LAST_MERGED_REVISION" /> | |
<option name="MERGE_DRY_RUN" value="false" /> | |
<option name="MERGE_DIFF_USE_ANCESTRY" value="true" /> | |
<option name="UPDATE_LOCK_ON_DEMAND" value="false" /> | |
<option name="IGNORE_SPACES_IN_MERGE" value="false" /> | |
<option name="DETECT_NESTED_COPIES" value="true" /> | |
<option name="CHECK_NESTED_FOR_QUICK_MERGE" value="false" /> | |
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true" /> | |
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true" /> | |
<option name="FORCE_UPDATE" value="false" /> | |
<option name="IGNORE_EXTERNALS" value="false" /> | |
<myIsUseDefaultProxy>false</myIsUseDefaultProxy> | |
</component> | |
<component name="TaskManager"> | |
<task active="true" id="Default" summary="Default task"> | |
<changelist id="f90ee5b5-97e4-47ec-9b14-d4f4e896f100" name="Default" comment="" /> | |
<created>1350026709905</created> | |
<updated>1350026709905</updated> | |
</task> | |
<servers /> | |
</component> | |
<component name="ToolWindowManager"> | |
<frame x="0" y="22" width="1680" height="936" extended-state="6" /> | |
<editor active="true" /> | |
<layout> | |
<window_info id="Changes" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> | |
<window_info id="Palette" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> | |
<window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> | |
<window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> | |
<window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.32843137" sideWeight="0.5" order="7" side_tool="true" content_ui="tabs" /> | |
<window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="true" content_ui="tabs" /> | |
<window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" /> | |
<window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" /> | |
<window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="1" side_tool="true" content_ui="tabs" /> | |
<window_info id="Maven Projects" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> | |
<window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> | |
<window_info id="Project" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" weight="0.19645043" sideWeight="0.67156863" order="0" side_tool="false" content_ui="combo" /> | |
<window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" /> | |
<window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" /> | |
<window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" /> | |
<window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" /> | |
<window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" /> | |
<window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.25" sideWeight="0.5" order="2" side_tool="false" content_ui="combo" /> | |
<window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" weight="0.4" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" /> | |
</layout> | |
</component> | |
<component name="VcsContentAnnotationSettings"> | |
<option name="myLimit" value="2678400000" /> | |
</component> | |
<component name="VcsManagerConfiguration"> | |
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true" /> | |
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true" /> | |
<option name="CHECK_NEW_TODO" value="true" /> | |
<option name="myTodoPanelSettings"> | |
<value> | |
<are-packages-shown value="false" /> | |
<are-modules-shown value="false" /> | |
<flatten-packages value="false" /> | |
<is-autoscroll-to-source value="false" /> | |
</value> | |
</option> | |
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="true" /> | |
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="true" /> | |
<option name="PERFORM_EDIT_IN_BACKGROUND" value="true" /> | |
<option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true" /> | |
<option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true" /> | |
<option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false" /> | |
<option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false" /> | |
<option name="CHANGED_ON_SERVER_INTERVAL" value="60" /> | |
<option name="SHOW_ONLY_CHANGED_IN_SELECTION_DIFF" value="true" /> | |
<option name="CHECK_COMMIT_MESSAGE_SPELLING" value="true" /> | |
<option name="DEFAULT_PATCH_EXTENSION" value="patch" /> | |
<option name="SHORT_DIFF_HORISONTALLY" value="true" /> | |
<option name="SHORT_DIFF_EXTRA_LINES" value="2" /> | |
<option name="SOFT_WRAPS_IN_SHORT_DIFF" value="true" /> | |
<option name="INCLUDE_TEXT_INTO_PATCH" value="false" /> | |
<option name="INCLUDE_TEXT_INTO_SHELF" value="false" /> | |
<option name="SHOW_FILE_HISTORY_DETAILS" value="true" /> | |
<option name="SHOW_VCS_ERROR_NOTIFICATIONS" value="true" /> | |
<option name="SHOW_DIRTY_RECURSIVELY" value="false" /> | |
<option name="LIMIT_HISTORY" value="true" /> | |
<option name="MAXIMUM_HISTORY_ROWS" value="1000" /> | |
<option name="FORCE_NON_EMPTY_COMMENT" value="false" /> | |
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="false" /> | |
<option name="LAST_COMMIT_MESSAGE" /> | |
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="false" /> | |
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false" /> | |
<option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false" /> | |
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false" /> | |
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false" /> | |
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8" /> | |
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5" /> | |
<option name="ACTIVE_VCS_NAME" /> | |
<option name="UPDATE_GROUP_BY_PACKAGES" value="false" /> | |
<option name="UPDATE_GROUP_BY_CHANGELIST" value="false" /> | |
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false" /> | |
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6" /> | |
</component> | |
<component name="XDebuggerManager"> | |
<breakpoint-manager /> | |
</component> | |
<component name="antWorkspaceConfiguration"> | |
<option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> | |
<option name="FILTER_TARGETS" value="false" /> | |
</component> | |
<component name="editorHistoryManager"> | |
<entry file="file://$PROJECT_DIR$/viewcalls.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="2" column="13" selection-start="46" selection-end="46" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/common.inc.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="34" column="0" selection-start="1179" selection-end="1179" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/calls.json.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="2" column="9" selection-start="42" selection-end="42" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/trunklog.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/../disclosr/include/template.inc.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="72" column="93" selection-start="2545" selection-end="2635" vertical-scroll-proportion="0.43404254" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/../disclosr/include/common.inc.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/../busui/myway/myway_timeliness.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="27" column="5" selection-start="1003" selection-end="1018" vertical-scroll-proportion="26.346153" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/snd.py"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/common.inc.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="60" column="117" selection-start="2262" selection-end="2262" vertical-scroll-proportion="-39.23077" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/viewcalls.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="41" column="69" selection-start="1388" selection-end="1388" vertical-scroll-proportion="1.2659575" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/scannr.py"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="80" column="64" selection-start="2271" selection-end="2271" vertical-scroll-proportion="0.0" /> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/calllog.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="2" column="0" selection-start="34" selection-end="226" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/calls.json.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="13" column="53" selection-start="499" selection-end="499" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/generateConvos.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="0" column="0" selection-start="0" selection-end="0" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/getfile.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="11" column="13" selection-start="471" selection-end="539" vertical-scroll-proportion="0.0"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
<entry file="file://$PROJECT_DIR$/generateHourlys.php"> | |
<provider selected="true" editor-type-id="text-editor"> | |
<state line="23" column="6" selection-start="1012" selection-end="1012" vertical-scroll-proportion="0.68944097"> | |
<folding /> | |
</state> | |
</provider> | |
</entry> | |
</component> | |
</project> | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="utf-8"> | |
<title>Page Not Found :(</title> | |
<style> | |
::-moz-selection { | |
background: #b3d4fc; | |
text-shadow: none; | |
} | |
::selection { | |
background: #b3d4fc; | |
text-shadow: none; | |
} | |
html { | |
padding: 30px 10px; | |
font-size: 20px; | |
line-height: 1.4; | |
color: #737373; | |
background: #f0f0f0; | |
-webkit-text-size-adjust: 100%; | |
-ms-text-size-adjust: 100%; | |
} | |
html, | |
input { | |
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
} | |
body { | |
max-width: 500px; | |
_width: 500px; | |
padding: 30px 20px 50px; | |
border: 1px solid #b3b3b3; | |
border-radius: 4px; | |
margin: 0 auto; | |
box-shadow: 0 1px 10px #a7a7a7, inset 0 1px 0 #fff; | |
background: #fcfcfc; | |
} | |
h1 { | |
margin: 0 10px; | |
font-size: 50px; | |
text-align: center; | |
} | |
h1 span { | |
color: #bbb; | |
} | |
h3 { | |
margin: 1.5em 0 0.5em; | |
} | |
p { | |
margin: 1em 0; | |
} | |
ul { | |
padding: 0 0 0 40px; | |
margin: 1em 0; | |
} | |
.container { | |
max-width: 380px; | |
_width: 380px; | |
margin: 0 auto; | |
} | |
/* google search */ | |
#goog-fixurl ul { | |
list-style: none; | |
padding: 0; | |
margin: 0; | |
} | |
#goog-fixurl form { | |
margin: 0; | |
} | |
#goog-wm-qt, | |
#goog-wm-sb { | |
border: 1px solid #bbb; | |
font-size: 16px; | |
line-height: normal; | |
vertical-align: top; | |
color: #444; | |
border-radius: 2px; | |
} | |
#goog-wm-qt { | |
width: 220px; | |
height: 20px; | |
padding: 5px; | |
margin: 5px 10px 0 0; | |
box-shadow: inset 0 1px 1px #ccc; | |
} | |
#goog-wm-sb { | |
display: inline-block; | |
height: 32px; | |
padding: 0 10px; | |
margin: 5px 0 0; | |
white-space: nowrap; | |
cursor: pointer; | |
background-color: #f5f5f5; | |
background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1); | |
background-image: -moz-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1); | |
background-image: -ms-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1); | |
background-image: -o-linear-gradient(rgba(255, 255, 255, 0), #f1f1f1); | |
-webkit-appearance: none; | |
-moz-appearance: none; | |
appearance: none; | |
*overflow: visible; | |
*display: inline; | |
*zoom: 1; | |
} | |
#goog-wm-sb:hover, | |
#goog-wm-sb:focus { | |
border-color: #aaa; | |
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1); | |
background-color: #f8f8f8; | |
} | |
#goog-wm-qt:hover, | |
#goog-wm-qt:focus { | |
border-color: #105cb6; | |
outline: 0; | |
color: #222; | |
} | |
input::-moz-focus-inner { | |
padding: 0; | |
border: 0; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="container"> | |
<h1>Not found <span>:(</span></h1> | |
<p>Sorry, but the page you were trying to view does not exist.</p> | |
<p>It looks like this was the result of either:</p> | |
<ul> | |
<li>a mistyped address</li> | |
<li>an out-of-date link</li> | |
</ul> | |
<script> | |
var GOOG_FIXURL_LANG = (navigator.language || '').slice(0, 2), GOOG_FIXURL_SITE = location.host; | |
</script> | |
<script src="http://linkhelp.clients.google.com/tbproxy/lh/wm/fixurl.js"></script> | |
</div> | |
</body> | |
</html> | |
### HEAD | |
### 4.0.0 (28 August, 2012) | |
* Improve the Apache compression configuration ([#1012](https://github.com/h5bp/html5-boilerplate/issues/1012), [#1173](https://github.com/h5bp/html5-boilerplate/issues/1173)). | |
* Add a HiDPI example media query ([#1127](https://github.com/h5bp/html5-boilerplate/issues/1127)). | |
* Add bundled docs ([#1154](https://github.com/h5bp/html5-boilerplate/issues/1154)). | |
* Add MIT license ([#1139](https://github.com/h5bp/html5-boilerplate/issues/1139)). | |
* Update to Normalize.css 1.0.1. | |
* Separate Normalize.css from the rest of the CSS ([#1160](https://github.com/h5bp/html5-boilerplate/issues/1160)). | |
* Improve `console.log` protection ([#1107](https://github.com/h5bp/html5-boilerplate/issues/1107)). | |
* Replace hot pink text selection color with a neutral color. | |
* Change image replacement technique ([#1149](https://github.com/h5bp/html5-boilerplate/issues/1149)). | |
* Code format and consistency changes ([#1112](https://github.com/h5bp/html5-boilerplate/issues/1112)). | |
* Rename CSS file and rename JS files and subdirectories. | |
* Update to jQuery 1.8 ([#1161](https://github.com/h5bp/html5-boilerplate/issues/1161)). | |
* Update to Modernizr 2.6.1 ([#1086](https://github.com/h5bp/html5-boilerplate/issues/1086)). | |
* Remove uncompressed jQuery ([#1153](https://github.com/h5bp/html5-boilerplate/issues/1153)). | |
* Remove superfluous inline comments ([#1150](https://github.com/h5bp/html5-boilerplate/issues/1150)). | |
### 3.0.2 (February 19, 2012) | |
* Update to Modernizr 2.5.3. | |
### 3.0.1 (February 08, 2012). | |
* Update to Modernizr 2.5.2 (includes html5shiv 3.3). | |
### 3.0.0 (February 06, 2012) | |
* Improvements to `.htaccess`. | |
* Improve 404 design. | |
* Simplify JS folder structure. | |
* Change `html` IE class names changed to target ranges rather than specific versions of IE. | |
* Update CSS to include latest normalize.css changes and better typographic defaults ([#825](https://github.com/h5bp/html5-boilerplate/issues/825)). | |
* Update to Modernizr 2.5 (includes yepnope 1.5 and html5shiv 3.2). | |
* Update to jQuery 1.7.1. | |
* Revert to async snippet for the Google Analytics script. | |
* Remove the ant build script ([#826](https://github.com/h5bp/html5-boilerplate/issues/826)). | |
* Remove Respond.js ([#816](https://github.com/h5bp/html5-boilerplate/issues/816)). | |
* Remove the `demo/` directory ([#808](https://github.com/h5bp/html5-boilerplate/issues/808)). | |
* Remove the `test/` directory ([#808](https://github.com/h5bp/html5-boilerplate/issues/808)). | |
* Remove Google Chrome Frame script for IE6 users; replace with links to Chrome Frame and options for alternative browsers. | |
* Remove `initial-scale=1` from the viewport `meta` ([#824](https://github.com/h5bp/html5-boilerplate/issues/824)). | |
* Remove `defer` from all scripts to avoid legacy IE bugs. | |
* Remove explicit Site Speed tracking for Google Analytics. It's now enabled by default. | |
### 2.0.0 (August 10, 2011) | |
* Change starting CSS to be based on normalize.css instead of reset.css ([#500](https://github.com/h5bp/html5-boilerplate/issues/500)). | |
* Add Respond.js media query polyfill. | |
* Add Google Chrome Frame script prompt for IE6 users. | |
* Simplify the `html` conditional comments for modern browsers and add an `oldie` class. | |
* Update clearfix to use "micro clearfix". | |
* Add placeholder CSS MQs for mobile-first approach. | |
* Add `textarea { resize: vertical; }` to only allow vertical resizing. | |
* Add `img { max-width: 100%; }` to the print styles; prevents images being truncated. | |
* Add Site Speed tracking for Google Analytics. | |
* Update to jQuery 1.6.2 (and use minified by default). | |
* Update to Modernizr 2.0 Complete, Production minified (includes yepnope, html5shiv, and Respond.js). | |
* Use `Modernizr.load()` to load the Google Analytics script. | |
* Much faster build process. | |
* Add build script options for CSSLint, JSLint, JSHint tools. | |
* Build script now compresses all images in subfolders. | |
* Build script now versions files by SHA hash. | |
* Many `.htaccess` improvements including: disable directory browsing, improved support for all versions of Apache, more robust and extensive HTTP compression rules. | |
* Remove `handheld.css` as it has very poor device support. | |
* Remove touch-icon `link` elements from the HTML and include improved touch-icon support. | |
* Remove the cache-busting query paramaters from files references in the HTML. | |
* Remove IE6 PNGFix. | |
### 1.0.0 (March 21, 2011) | |
* Rewrite build script to make it more customizable and flexible. | |
* Add a humans.txt. | |
* Numerous `.htaccess` improvements (including inline documentation). | |
* Move the alternative server configurations to the H5BP server configs repo. | |
* Use a protocol-relative url to reference jQuery and prevent mixed content warnings. | |
* Optimize the Google Analytics snippet. | |
* Use Eric Meyer's recent CSS reset update and the HTML5 Doctor reset. | |
* More robust `sub`/`sup` CSS styles. | |
* Add keyboard `.focusable` helper class that extends `.visuallyhidden`. | |
* Print styles no longer print hash or JavaScript links. | |
* Add a print reset for IE's proprietary filters. | |
* Remove IE9-specific conditional class on the `html` element. | |
* Remove margins from lists within `nav` elements. | |
* Remove YUI profiling. | |
Copyright (c) HTML5 Boilerplate | |
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. | |
# [HTML5 Boilerplate](http://html5boilerplate.com) | |
HTML5 Boilerplate is a professional front-end template for building fast, | |
robust, and adaptable web apps or sites. | |
This project is the product of many years of iterative development and combined | |
community knowledge. It does not impose a specific development philosophy or | |
framework, so you're free to architect your code in the way that you want. | |
* Source: [https://github.com/h5bp/html5-boilerplate](https://github.com/h5bp/html5-boilerplate) | |
* Homepage: [http://html5boilerplate.com](http://html5boilerplate.com) | |
* Twitter: [@h5bp](http://twitter.com/h5bp) | |
## Quick start | |
Choose one of the following options: | |
1. Download the latest stable release from | |
[html5boilerplate.com](http://html5boilerplate.com/) or a custom build from | |
[Initializr](http://www.initializr.com). | |
2. Clone the git repo — `git clone | |
https://github.com/h5bp/html5-boilerplate.git` - and checkout the tagged | |
release you'd like to use. | |
## Features | |
* HTML5 ready. Use the new elements with confidence. | |
* Cross-browser compatible (Chrome, Opera, Safari, Firefox 3.6+, IE6+). | |
* Designed with progressive enhancement in mind. | |
* Includes [Normalize.css](http://necolas.github.com/normalize.css/) for CSS | |
normalizations and common bug fixes. | |
* The latest [jQuery](http://jquery.com/) via CDN, with a local fallback. | |
* The latest [Modernizr](http://modernizr.com/) build for feature detection. | |
* IE-specific classes for easier cross-browser control. | |
* Placeholder CSS Media Queries. | |
* Useful CSS helpers. | |
* Default print CSS, performance optimized. | |
* Protection against any stray `console.log` causing JavaScript errors in | |
IE6/7. | |
* An optimized Google Analytics snippet. | |
* Apache server caching, compression, and other configuration defaults for | |
Grade-A performance. | |
* Cross-domain Ajax and Flash. | |
* "Delete-key friendly." Easy to strip out parts you don't need. | |
* Extensive inline and accompanying documentation. | |
## Documentation | |
Take a look at the [documentation table of | |
contents](/h5bp/html5-boilerplate/blob/master/doc/README.md). This | |
documentation is bundled with the project, which makes it readily available for | |
offline reading and provides a useful starting point for any documentation | |
you want to write about your project. | |
## Contributing | |
Anyone and everyone is welcome to | |
[contribute](/h5bp/html5-boilerplate/blob/master/doc/contribute.md). Hundreds | |
of developers have helped make the HTML5 Boilerplate what it is today. | |
ffmpeg from http://ffmpeg.zeranoe.com/builds/ |
Binary files /dev/null and b/apple-touch-icon-114x114-precomposed.png differ
Binary files /dev/null and b/apple-touch-icon-144x144-precomposed.png differ
Binary files /dev/null and b/apple-touch-icon-57x57-precomposed.png differ
Binary files /dev/null and b/apple-touch-icon-72x72-precomposed.png differ
Binary files /dev/null and b/apple-touch-icon-precomposed.png differ
Binary files /dev/null and b/apple-touch-icon.png differ
<?php | |
include ('common.inc.php'); | |
$sth = $conn->prepare('select * from recordings | |
order by call_timestamp desc limit 1000'); | |
$sth->execute(Array()); | |
$row = 0; | |
echo "<table>"; | |
foreach ($sth->fetchAll() as $data) { | |
echo "<tr>"; | |
for ($c = 0; $c < count($data); $c++) { | |
echo '<td>' . $data[$c] . "</td>\n"; | |
} | |
echo "</tr>"; | |
} | |
$row++; | |
echo "</table>"; | |
<?php | |
include('common.inc.php'); | |
function getTGIDValuesByHour($TGID, $timeFrom, $timeTo) | |
{ | |
global $conn; | |
$sth = $conn->prepare('select tgid, min(call_timestamp) as time, count(*), min(length), max(length), avg(length), stddev(length) from recordings | |
where call_timestamp between to_timestamp(?) and to_timestamp(?) | |
group by tgid, date_trunc(\'hour\', call_timestamp) order by time'); | |
$sth->execute(Array($timeFrom, $timeTo)); | |
return $sth->fetchAll(PDO::FETCH_ASSOC); | |
} | |
function getTGIDValuesByDay($TGID, $dayFrom, $dayTo) | |
{ | |
global $conn; | |
$sth = $conn->prepare('select min(time) as time, min(value), max(value), avg(value), stddev(value) from sensor_values where sensor_id = ? | |
group by sensor_id, date_trunc(\'day\', time) order by time'); | |
$sth->execute(Array($TGID)); | |
return $sth->fetchAll(PDO::FETCH_ASSOC); | |
} | |
function getTGIDDataYears($TGID, $timeFrom, $timeTo) | |
{ | |
global $conn; | |
$sth = $conn->prepare("select distinct extract('year' from call_timestamp) as year from recordings where tgid = ? order by year"); | |
$sth->execute(Array($TGID)); | |
return $sth->fetchAll(PDO::FETCH_ASSOC); | |
} | |
function getTGIDDataMonths($TGID, $timeFrom, $timeTo) | |
{ | |
global $conn; | |
$sth = $conn->prepare("select distinct extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year, month"); | |
$sth->execute(Array($TGID)); | |
return $sth->fetchAll(PDO::FETCH_ASSOC); | |
} | |
function getTGIDDataDays($TGID, $timeFrom, $timeTo) | |
{ | |
global $conn; | |
$sth = $conn->prepare("select distinct extract('day' from call_timestamp) as day, extract('month' from call_timestamp) as month, extract('year' from call_timestamp) as year from recordings where tgid = ? order by year,month,day"); | |
$sth->execute(Array($TGID)); | |
return $sth->fetchAll(PDO::FETCH_ASSOC); | |
} | |
$action = (isset($_REQUEST['action']) ? $_REQUEST['action'] : ''); | |
$TGID = (isset($_REQUEST['tgid']) ? $_REQUEST['tgid'] : ''); | |
$timefrom = (isset($_REQUEST['from']) ? $_REQUEST['from'] : ''); | |
$timeto = (isset($_REQUEST['to']) ? $_REQUEST['to'] : ''); | |
if ($action == "data") { | |
$sth = $conn->prepare('select * from recordings | |
order by call_timestamp desc limit 100'); | |
$sth->execute(Array()); | |
echo json_encode ($sth->fetchAll(PDO::FETCH_ASSOC)); | |
} | |
if ($action == "data_description") { | |
$timefrom = strtotime($timefrom); | |
$timeto = strtotime($timeto); | |
$years = getTGIDDataYears($TGID, $timefrom, $timeto); | |
$months = getTGIDDataMonths($TGID, $timefrom, $timeto); | |
$days = getTGIDDataDays($TGID, $timefrom, $timeto); | |
echo json_encode(Array("years" => $years, "months" => $months, "days" => $days | |
)); | |
} | |
if (strpos($action, "graph") !== false) { | |
$values = getTGIDValuesByHour($TGID, $timefrom, $timeto); | |
$label = $TGID; | |
$data = Array(); | |
$tzoffset = get_timezone_offset("UTC"); | |
foreach ($values as $value) { | |
if ($action == "graphlength") { | |
$data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['avg'])); | |
} else if ($action == "graphcount") { | |
$data[$value['tgid']][] = Array((strtotime($value['time']) + $tzoffset) * 1000, intval($value['count'])); | |
} | |
} | |
echo json_encode(Array("label" => $label, "data" => $data, | |
"previous" => Array( | |
"from" => $timefrom - (24 * 60 * 60), | |
"to" => $timefrom) | |
, | |
"next" => Array( | |
"to" => $timeto + (24 * 60 * 60), | |
"from" => $timeto) | |
) | |
); | |
} | |
?> | |
<?php | |
date_default_timezone_set("Australia/Sydney"); | |
try { | |
$conn = new PDO("pgsql:dbname=scannr;user=postgres;password=snmc;host=localhost"); | |
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); | |
} catch (PDOException $e) { | |
die('Unable to connect to database server.'); | |
} | |
catch (Exception $e) { | |
die('Unknown error in ' . __FILE__ . '.'); | |
} | |
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); | |
$basePath = ""; | |
$DATA_DIR = "./data"; | |
/** Returns the offset from the origin timezone to the remote timezone, in seconds. | |
* @param $remote_tz; | |
* @param $origin_tz; If null the servers current timezone is used as the origin. | |
* @return int; | |
*/ | |
function get_timezone_offset($remote_tz, $origin_tz = null) | |
{ | |
if ($origin_tz === null) { | |
if (!is_string($origin_tz = date_default_timezone_get())) { | |
return false; // A UTC timestamp was returned -- bail out! | |
} | |
} | |
$origin_dtz = new DateTimeZone($origin_tz); | |
$remote_dtz = new DateTimeZone($remote_tz); | |
$origin_dt = new DateTime("now", $origin_dtz); | |
$remote_dt = new DateTime("now", $remote_dtz); | |
$offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt); | |
return $offset; | |
} | |
function include_header($title) | |
{ | |
global $basePath; | |
?> | |
<!DOCTYPE html> | |
<!--[if lt IE 7]> | |
<html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]--> | |
<!--[if IE 7]> | |
<html class="no-js lt-ie9 lt-ie8"> <![endif]--> | |
<!--[if IE 8]> | |
<html class="no-js lt-ie9"> <![endif]--> | |
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]--> | |
<head> | |
<meta charset="utf-8"> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> | |
<title></title> | |
<meta name="description" content=""> | |
<meta name="viewport" content="width=device-width"> | |
<!-- Place favicon.ico and apple-touch-icon.png in the root directory --> | |
<link rel="stylesheet" href="css/normalize.css"> | |
<link rel="stylesheet" href="css/main.css"> | |
<script src="js/vendor/modernizr-2.6.1.min.js"></script> | |
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script> | |
<!--<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.8.0.min.js"><\/script>')</script>--> | |
<script type="text/javascript" src="<?php echo $basePath ?>js/flotr2/flotr2.js"></script> | |
<script src="js/plugins.js"></script> | |
<script src="js/main.js"></script> | |
</head> | |
<body> | |
<!--[if lt IE 7]> | |
<p class="chromeframe">You are using an outdated browser. <a href="http://browsehappy.com/">Upgrade your browser | |
today</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to | |
better experience this site.</p> | |
<![endif]--> | |
<!-- Add your site or application content here --> | |
<?php | |
} | |
function include_footer() | |
{ | |
global $basePath; | |
?> | |
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. --> | |
<script> | |
var _gaq = [ | |
['_setAccount', 'UA-XXXXX-X'], | |
['_trackPageview'] | |
]; | |
(function (d, t) { | |
var g = d.createElement(t), s = d.getElementsByTagName(t)[0]; | |
g.src = ('https:' == location.protocol ? '//ssl' : '//www') + '.google-analytics.com/ga.js'; | |
s.parentNode.insertBefore(g, s) | |
}(document, 'script')); | |
</script> | |
</body> | |
</html> | |
<?php | |
} | |
<?php | |
/*- scheduled tasks | |
- combine recordings into conversations | |
- using ffmpeg | |
- conversations and recordings_to_conversations tables | |
- remove all other wav files | |
- delete old uninteresting conversations*/ | |
?> | |
<?xml version="1.0"?> | |
<!DOCTYPE cross-domain-policy SYSTEM "http://www.adobe.com/xml/dtds/cross-domain-policy.dtd"> | |
<cross-domain-policy> | |
<!-- Read this: www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html --> | |
<!-- Most restrictive policy: --> | |
<site-control permitted-cross-domain-policies="none"/> | |
<!-- Least restrictive policy: --> | |
<!-- | |
<site-control permitted-cross-domain-policies="all"/> | |
<allow-access-from domain="*" to-ports="*" secure="false"/> | |
<allow-http-request-headers-from domain="*" headers="*" secure="false"/> | |
--> | |
</cross-domain-policy> | |
/* | |
* HTML5 Boilerplate | |
* | |
* What follows is the result of much research on cross-browser styling. | |
* Credit left inline and big thanks to Nicolas Gallagher, Jonathan Neal, | |
* Kroc Camen, and the H5BP dev community and team. | |
*/ | |
/* ========================================================================== | |
Base styles: opinionated defaults | |
========================================================================== */ | |
html, | |
button, | |
input, | |
select, | |
textarea { | |
color: #222; | |
} | |
body { | |
font-size: 1em; | |
line-height: 1.4; | |
} | |
/* | |
* Remove text-shadow in selection highlight: h5bp.com/i | |
* These selection declarations have to be separate. | |
* Customize the background color to match your design. | |
*/ | |
::-moz-selection { | |
background: #b3d4fc; | |
text-shadow: none; | |
} | |
::selection { | |
background: #b3d4fc; | |
text-shadow: none; | |
} | |
/* | |
* A better looking default horizontal rule | |
*/ | |
hr { | |
display: block; | |
height: 1px; | |
border: 0; | |
border-top: 1px solid #ccc; | |
margin: 1em 0; | |
padding: 0; | |
} | |
/* | |
* Remove the gap between images and the bottom of their containers: h5bp.com/i/440 | |
*/ | |
img { | |
vertical-align: middle; | |
} | |
/* | |
* Remove default fieldset styles. | |
*/ | |
fieldset { | |
border: 0; | |
margin: 0; | |
padding: 0; | |
} | |
/* | |
* Allow only vertical resizing of textareas. | |
*/ | |
textarea { | |
resize: vertical; | |
} | |
/* ========================================================================== | |
Chrome Frame prompt | |
========================================================================== */ | |
.chromeframe { | |
margin: 0.2em 0; | |
background: #ccc; | |
color: #000; | |
padding: 0.2em 0; | |
} | |
/* ========================================================================== | |
Author's custom styles | |
========================================================================== */ | |
/* ========================================================================== | |
Helper classes | |
========================================================================== */ | |
/* | |
* Image replacement | |
*/ | |
.ir { | |
background-color: transparent; | |
border: 0; | |
overflow: hidden; | |
/* IE 6/7 fallback */ | |
*text-indent: -9999px; | |
} | |
.ir:before { | |
content: ""; | |
display: block; | |
width: 0; | |
height: 100%; | |
} | |
/* | |
* Hide from both screenreaders and browsers: h5bp.com/u | |
*/ | |
.hidden { | |
display: none !important; | |
visibility: hidden; | |
} | |
/* | |
* Hide only visually, but have it available for screenreaders: h5bp.com/v | |
*/ | |
.visuallyhidden { | |
border: 0; | |
clip: rect(0 0 0 0); | |
height: 1px; | |
margin: -1px; | |
overflow: hidden; | |
padding: 0; | |
position: absolute; | |
width: 1px; | |
} | |
/* | |
* Extends the .visuallyhidden class to allow the element to be focusable | |
* when navigated to via the keyboard: h5bp.com/p | |
*/ | |
.visuallyhidden.focusable:active, | |
.visuallyhidden.focusable:focus { | |
clip: auto; | |
height: auto; | |
margin: 0; | |
overflow: visible; | |
position: static; | |
width: auto; | |
} | |
/* | |
* Hide visually and from screenreaders, but maintain layout | |
*/ | |
.invisible { | |
visibility: hidden; | |
} | |
/* | |
* Clearfix: contain floats | |
* | |
* For modern browsers | |
* 1. The space content is one way to avoid an Opera bug when the | |
* `contenteditable` attribute is included anywhere else in the document. | |
* Otherwise it causes space to appear at the top and bottom of elements | |
* that receive the `clearfix` class. | |
* 2. The use of `table` rather than `block` is only necessary if using | |
* `:before` to contain the top-margins of child elements. | |
*/ | |
.clearfix:before, | |
.clearfix:after { | |
content: " "; /* 1 */ | |
display: table; /* 2 */ | |
} | |
.clearfix:after { | |
clear: both; | |
} | |
/* | |
* For IE 6/7 only | |
* Include this rule to trigger hasLayout and contain floats. | |
*/ | |
.clearfix { | |
*zoom: 1; | |
} | |
/* ========================================================================== | |
EXAMPLE Media Queries for Responsive Design. | |
Theses examples override the primary ('mobile first') styles. | |
Modify as content requires. | |
========================================================================== */ | |
@media only screen and (min-width: 35em) { | |
/* Style adjustments for viewports that meet the condition */ | |
} | |
@media only screen and (-webkit-min-device-pixel-ratio: 1.5), | |
only screen and (min-resolution: 144dpi) { | |
/* Style adjustments for high resolution devices */ | |
} | |
/* ========================================================================== | |
Print styles. | |
Inlined to avoid required HTTP connection: h5bp.com/r | |
========================================================================== */ | |
@media print { | |
* { | |
background: transparent !important; | |
color: #000 !important; /* Black prints faster: h5bp.com/s */ | |
box-shadow:none !important; | |
text-shadow: none !important; | |
} | |
a, | |
a:visited { | |
text-decoration: underline; | |
} | |
a[href]:after { | |
content: " (" attr(href) ")"; | |
} | |
abbr[title]:after { | |
content: " (" attr(title) ")"; | |
} | |
/* | |
* Don't show links for images, or javascript/internal links | |
*/ | |
.ir a:after, | |
a[href^="javascript:"]:after, | |
a[href^="#"]:after { | |
content: ""; | |
} | |
pre, | |
blockquote { | |
border: 1px solid #999; | |
page-break-inside: avoid; | |
} | |
thead { | |
display: table-header-group; /* h5bp.com/t */ | |
} | |
tr, | |
img { | |
page-break-inside: avoid; | |
} | |
img { | |
max-width: 100% !important; | |
} | |
@page { | |
margin: 0.5cm; | |
} | |
p, | |
h2, | |
h3 { | |
orphans: 3; | |
widows: 3; | |
} | |
h2, | |
h3 { | |
page-break-after: avoid; | |
} | |
} | |
/*! normalize.css v1.0.1 | MIT License | git.io/normalize */ | |
/* ========================================================================== | |
HTML5 display definitions | |
========================================================================== */ | |
/* | |
* Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3. | |
*/ | |
article, | |
aside, | |
details, | |
figcaption, | |
figure, | |
footer, | |
header, | |
hgroup, | |
nav, | |
section, | |
summary { | |
display: block; | |
} | |
/* | |
* Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. | |
*/ | |
audio, | |
canvas, | |
video { | |
display: inline-block; | |
*display: inline; | |
*zoom: 1; | |
} | |
/* | |
* Prevents modern browsers from displaying `audio` without controls. | |
* Remove excess height in iOS 5 devices. | |
*/ | |
audio:not([controls]) { | |
display: none; | |
height: 0; | |
} | |
/* | |
* Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3, | |
* and Safari 4. | |
* Known issue: no IE 6 support. | |
*/ | |
[hidden] { | |
display: none; | |
} | |
/* ========================================================================== | |
Base | |
========================================================================== */ | |
/* | |
* 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using | |
* `em` units. | |
* 2. Prevents iOS text size adjust after orientation change, without disabling | |
* user zoom. | |
*/ | |
html { | |
font-size: 100%; /* 1 */ | |
-webkit-text-size-adjust: 100%; /* 2 */ | |
-ms-text-size-adjust: 100%; /* 2 */ | |
} | |
/* | |
* Addresses `font-family` inconsistency between `textarea` and other form | |
* elements. | |
*/ | |
html, | |
button, | |
input, | |
select, | |
textarea { | |
font-family: sans-serif; | |
} | |
/* | |
* Addresses margins handled incorrectly in IE 6/7. | |
*/ | |
body { | |
margin: 0; | |
} | |
/* ========================================================================== | |
Links | |
========================================================================== */ | |
/* | |
* Addresses `outline` inconsistency between Chrome and other browsers. | |
*/ | |
a:focus { | |
outline: thin dotted; | |
} | |
/* | |
* Improves readability when focused and also mouse hovered in all browsers. | |
*/ | |
a:active, | |
a:hover { | |
outline: 0; | |
} | |
/* ========================================================================== | |
Typography | |
========================================================================== */ | |
/* | |
* Addresses font sizes and margins set differently in IE 6/7. | |
* Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5, | |
* and Chrome. | |
*/ | |
h1 { | |
font-size: 2em; | |
margin: 0.67em 0; | |
} | |
h2 { | |
font-size: 1.5em; | |
margin: 0.83em 0; | |
} | |
h3 { | |
font-size: 1.17em; | |
margin: 1em 0; | |
} | |
h4 { | |
font-size: 1em; | |
margin: 1.33em 0; | |
} | |
h5 { | |
font-size: 0.83em; | |
margin: 1.67em 0; | |
} | |
h6 { | |
font-size: 0.75em; | |
margin: 2.33em 0; | |
} | |
/* | |
* Addresses styling not present in IE 7/8/9, Safari 5, and Chrome. | |
*/ | |
abbr[title] { | |
border-bottom: 1px dotted; | |
} | |
/* | |
* Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. | |
*/ | |
b, | |
strong { | |
font-weight: bold; | |
} | |
blockquote { | |
margin: 1em 40px; | |
} | |
/* | |
* Addresses styling not present in Safari 5 and Chrome. | |
*/ | |
dfn { | |
font-style: italic; | |
} | |
/* | |
* Addresses styling not present in IE 6/7/8/9. | |
*/ | |
mark { | |
background: #ff0; | |
color: #000; | |
} | |
/* | |
* Addresses margins set differently in IE 6/7. | |
*/ | |
p, | |
pre { | |
margin: 1em 0; | |
} | |
/* | |
* Corrects font family set oddly in IE 6, Safari 4/5, and Chrome. | |
*/ | |
code, | |
kbd, | |
pre, | |
samp { | |
font-family: monospace, serif; | |
_font-family: 'courier new', monospace; | |
font-size: 1em; | |
} | |
/* | |
* Improves readability of pre-formatted text in all browsers. | |
*/ | |
pre { | |
white-space: pre; | |
white-space: pre-wrap; | |
word-wrap: break-word; | |
} | |
/* | |
* Addresses CSS quotes not supported in IE 6/7. | |
*/ | |
q { | |
quotes: none; | |
} | |
/* | |
* Addresses `quotes` property not supported in Safari 4. | |
*/ | |
q:before, | |
q:after { | |
content: ''; | |
content: none; | |
} | |
/* | |
* Addresses inconsistent and variable font size in all browsers. | |
*/ | |
small { | |
font-size: 80%; | |
} | |
/* | |
* Prevents `sub` and `sup` affecting `line-height` in all browsers. | |
*/ | |
sub, | |
sup { | |
font-size: 75%; | |
line-height: 0; | |
position: relative; | |
vertical-align: baseline; | |
} | |
sup { | |
top: -0.5em; | |
} | |
sub { | |
bottom: -0.25em; | |
} | |
/* ========================================================================== | |
Lists | |
========================================================================== */ | |
/* | |
* Addresses margins set differently in IE 6/7. | |
*/ | |
dl, | |
menu, | |
ol, | |
ul { | |
margin: 1em 0; | |
} | |
dd { | |
margin: 0 0 0 40px; | |
} | |
/* | |
* Addresses paddings set differently in IE 6/7. | |
*/ | |
menu, | |
ol, | |
ul { | |
padding: 0 0 0 40px; | |
} | |
/* | |
* Corrects list images handled incorrectly in IE 7. | |
*/ | |
nav ul, | |
nav ol { | |
list-style: none; | |
list-style-image: none; | |
} | |
/* ========================================================================== | |
Embedded content | |
========================================================================== */ | |
/* | |
* 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3. | |
* 2. Improves image quality when scaled in IE 7. | |
*/ | |
img { | |
border: 0; /* 1 */ | |
-ms-interpolation-mode: bicubic; /* 2 */ | |
} | |
/* | |
* Corrects overflow displayed oddly in IE 9. | |
*/ | |
svg:not(:root) { | |
overflow: hidden; | |
} | |
/* ========================================================================== | |
Figures | |
========================================================================== */ | |
/* | |
* Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11. | |
*/ | |
figure { | |
margin: 0; | |
} | |
/* ========================================================================== | |
Forms | |
========================================================================== */ | |
/* | |
* Corrects margin displayed oddly in IE 6/7. | |
*/ | |
form { | |
margin: 0; | |
} | |
/* | |
* Define consistent border, margin, and padding. | |
*/ | |
fieldset { | |
border: 1px solid #c0c0c0; | |
margin: 0 2px; | |
padding: 0.35em 0.625em 0.75em; | |
} | |
/* | |
* 1. Corrects color not being inherited in IE 6/7/8/9. | |
* 2. Corrects text not wrapping in Firefox 3. | |
* 3. Corrects alignment displayed oddly in IE 6/7. | |
*/ | |
legend { | |
border: 0; /* 1 */ | |
padding: 0; | |
white-space: normal; /* 2 */ | |
*margin-left: -7px; /* 3 */ | |
} | |
/* | |
* 1. Corrects font size not being inherited in all browsers. | |
* 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5, | |
* and Chrome. | |
* 3. Improves appearance and consistency in all browsers. | |
*/ | |
button, | |
input, | |
select, | |
textarea { | |
font-size: 100%; /* 1 */ | |
margin: 0; /* 2 */ | |
vertical-align: baseline; /* 3 */ | |
*vertical-align: middle; /* 3 */ | |
} | |
/* | |
* Addresses Firefox 3+ setting `line-height` on `input` using `!important` in | |
* the UA stylesheet. | |
*/ | |
button, | |
input { | |
line-height: normal; | |
} | |
/* | |
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` | |
* and `video` controls. | |
* 2. Corrects inability to style clickable `input` types in iOS. | |
* 3. Improves usability and consistency of cursor style between image-type | |
* `input` and others. | |
* 4. Removes inner spacing in IE 7 without affecting normal text inputs. | |
* Known issue: inner spacing remains in IE 6. | |
*/ | |
button, | |
html input[type="button"], /* 1 */ | |
input[type="reset"], | |
input[type="submit"] { | |
-webkit-appearance: button; /* 2 */ | |
cursor: pointer; /* 3 */ | |
*overflow: visible; /* 4 */ | |
} | |
/* | |
* Re-set default cursor for disabled elements. | |
*/ | |
button[disabled], | |
input[disabled] { | |
cursor: default; | |
} | |
/* | |
* 1. Addresses box sizing set to content-box in IE 8/9. | |
* 2. Removes excess padding in IE 8/9. | |
* 3. Removes excess padding in IE 7. | |
* Known issue: excess padding remains in IE 6. | |
*/ | |
input[type="checkbox"], | |
input[type="radio"] { | |
box-sizing: border-box; /* 1 */ | |
padding: 0; /* 2 */ | |
*height: 13px; /* 3 */ | |
*width: 13px; /* 3 */ | |
} | |
/* | |
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. | |
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome | |
* (include `-moz` to future-proof). | |
*/ | |
input[type="search"] { | |
-webkit-appearance: textfield; /* 1 */ | |
-moz-box-sizing: content-box; | |
-webkit-box-sizing: content-box; /* 2 */ | |
box-sizing: content-box; | |
} | |
/* | |
* Removes inner padding and search cancel button in Safari 5 and Chrome | |
* on OS X. | |
*/ | |
input[type="search"]::-webkit-search-cancel-button, | |
input[type="search"]::-webkit-search-decoration { | |
-webkit-appearance: none; | |
} | |
/* | |
* Removes inner padding and border in Firefox 3+. | |
*/ | |
button::-moz-focus-inner, | |
input::-moz-focus-inner { | |
border: 0; | |
padding: 0; | |
} | |
/* | |
* 1. Removes default vertical scrollbar in IE 6/7/8/9. | |
* 2. Improves readability and alignment in all browsers. | |
*/ | |
textarea { | |
overflow: auto; /* 1 */ | |
vertical-align: top; /* 2 */ | |
} | |
/* ========================================================================== | |
Tables | |
========================================================================== */ | |
/* | |
* Remove most spacing between table cells. | |
*/ | |
table { | |
border-collapse: collapse; | |
border-spacing: 0; | |
} | |
-- /usr/pgsql-9.1/bin/pg_dump --schema-only scannr | |
-- | |
-- PostgreSQL database dump | |
-- | |
SET statement_timeout = 0; | |
SET client_encoding = 'UTF8'; | |
SET standard_conforming_strings = on; | |
SET check_function_bodies = false; | |
SET client_min_messages = warning; | |
-- | |
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: | |
-- | |
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; | |
-- | |
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: | |
-- | |
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; | |
SET search_path = public, pg_catalog; | |
SET default_tablespace = ''; | |
SET default_with_oids = false; | |
-- | |
-- Name: recordings; Type: TABLE; Schema: public; Owner: postgres; Tablespace: | |
-- | |
CREATE TABLE recordings ( | |
filename text NOT NULL, | |
tgid text, | |
tgname text, | |
sitename text, | |
call_timestamp timestamp with time zone DEFAULT now(), | |
length integer | |
); | |
ALTER TABLE public.recordings OWNER TO postgres; | |
-- | |
-- Name: tgids; Type: TABLE; Schema: public; Owner: postgres; Tablespace: | |
-- | |
CREATE TABLE tgids ( | |
tgid text NOT NULL, | |
subfleet smallint, | |
mode character(1) DEFAULT 'D'::bpchar NOT NULL, | |
alpha_tag text NOT NULL, | |
service_tag text, | |
category smallint | |
); | |
ALTER TABLE public.tgids OWNER TO postgres; | |
-- | |
-- Name: recordings_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: | |
-- | |
ALTER TABLE ONLY recordings | |
ADD CONSTRAINT recordings_pkey PRIMARY KEY (filename); | |
-- | |
-- Name: tgids_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres; Tablespace: | |
-- | |
ALTER TABLE ONLY tgids | |
ADD CONSTRAINT tgids_pkey PRIMARY KEY (tgid); | |
-- | |
-- Name: public; Type: ACL; Schema: -; Owner: postgres | |
-- | |
REVOKE ALL ON SCHEMA public FROM PUBLIC; | |
REVOKE ALL ON SCHEMA public FROM postgres; | |
GRANT ALL ON SCHEMA public TO postgres; | |
GRANT ALL ON SCHEMA public TO PUBLIC; | |
-- | |
-- PostgreSQL database dump complete | |
-- | |
Binary files /dev/null and b/favicon.ico differ
<?php | |
//select tgid, extract(hour from call_timestamp) ahour, date_trunc('day', call_timestamp) aday, count(filename), array_to_string(array_agg(filename), ' ') from recordings group by tgid, ahour, aday order by aday, ahour, tgid | |
include('common.inc.php'); | |
$sth = $conn->prepare('select * from recordings limit 100;'); | |
$sth->execute(); | |
$recordings = $sth->fetchAll(); | |
$convos = Array(); | |
$convo = Array(); | |
foreach ($recordings as $i => $recording) { | |
if (count($convo) > 0) { | |
echo "<br> " . strcasecmp($convos[count($convos) - 1][0]['call_timestamp'], $recording['call_timestamp']); | |
if (abs(strcasecmp($convos[count($convos) - 1][0]['call_timestamp'], $recording['call_timestamp'])) > 2) { | |
echo " " . $convos[count($convos) - 1][0]['call_timestamp'] . " " . $recording['call_timestamp']; | |
} | |
if (strcasecmp($convos[count($convos) - 1][0]['tgid'], $recording['tgid']) != 0) { | |
$convos[] = $convo; | |
$convo = Array(); | |
} | |
} | |
; | |
//print_r($recording); | |
$convo[] = $recording; | |
//print_r($convo); | |
//echo "<br>\n"; | |
} | |
foreach ($convos as $i => $convo) { | |
foreach ($convo as $recording) { | |
echo $recording['filename'] . " , "; | |
} | |
echo "<br><hr>\n"; | |
} | |
?> | |
<?php | |
include('common.inc.php'); | |
function processHourly($hourly) { | |
$filename = $hourly['tgid'].'-'.str_replace(' 00:00:00+1','',$hourly['aday']).'-'.$hourly['ahour'].'.3gp'; | |
if(!file_exists("hourly/".$filename)) { | |
$filenames = explode(",",$hourly['filenames']); | |
$cmd = "/usr/local/bin/ffmpeg -i data/".implode(" -i data/",$filenames)." -ar 8000 -ab 4.75k -ac 1 hourly/".$filename . ' 2>&1'; | |
//print_r($hourly); | |
exec ( $cmd,$output,$returncode ); | |
echo $cmd."<br>\n"; | |
if ($returncode != 10) { | |
//print_r($output); | |
//die(); | |
} | |
} | |
} | |
$sth = $conn->prepare("select tgid, extract(hour from call_timestamp) ahour, date_trunc('day', call_timestamp) aday, count(filename), array_to_string(array_agg(filename order by call_timestamp), ',') filenames from recordings group by tgid, ahour, aday order by aday DESC, ahour, tgid;"); | |
$sth->execute(); | |
$hourlies = $sth->fetchAll(PDO::FETCH_ASSOC); | |
foreach($hourlies as $hourly) { | |
//processHourly($hourly); | |
} | |
$sth = $conn->prepare("select 'hour' as tgid, extract(hour from call_timestamp) ahour, date_trunc('day', call_timestamp) aday, count(filename), array_to_string(array_agg(filename order by call_timestamp), ',') filenames from recordings group by ahour, aday order by aday DESC, ahour;"); | |
$sth->execute(); | |
$hourlies = $sth->fetchAll(PDO::FETCH_ASSOC); | |
foreach($hourlies as $hourly) { | |
processHourly($hourly); | |
} | |
<?php | |
$reqfile = "path/to/file.3gp"; | |
$contenttype = "audio/3gpp"; | |
if ($fn = fopen($reqfile, "rba")) { | |
header("Content-Type: " . $contenttype); | |
header("Content-Length: " . filesize($reqfile)); | |
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); | |
header("Pragma: no-cache"); | |
header("Expires: Mon, 26 Jul 1997 06:00:00 GMT"); | |
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0"); | |
passthru("ffmpeg -i 2012-09-29-1348911268.34-demo.wav -ar 8000 -ab 4.75k -"); | |
fpassthru($fn); | |
fclose($fn); | |
} else { | |
exit("error...."); | |
} | |
exit(); | |
?> | |
# humanstxt.org/ | |
# The humans responsible & technology colophon | |
# TEAM | |
<name> -- <role> -- <twitter> | |
# THANKS | |
<name> | |
# TECHNOLOGY COLOPHON | |
HTML5, CSS3 | |
jQuery, Modernizr | |
build | |
.vimrc | |
*.swp | |
*.swm | |
*.swo | |
*.vim | |
.jhw-cache | |
node_modules | |
dev | |
Copyright (c) 2012 Carl Sutherland | |
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. | |
all: test flotr2 | |
test: | |
cd spec; jasmine-headless-webkit -j jasmine.yml -c | |
libraries: | |
smoosh make/lib.json | |
cat ./build/bean.js > build/lib.js | |
cat ./build/underscore.js >> build/lib.js | |
cat ./build/bean.min.js > build/lib.min.js | |
echo ";" >> build/lib.min.js | |
cat ./build/underscore.min.js >> build/lib.min.js | |
echo ";" >> build/lib.min.js | |
ie: | |
smoosh make/ie.json | |
flotr2: libraries ie | |
smoosh make/flotr2.json | |
cat build/lib.js build/flotr2.js > flotr2.js | |
cat build/lib.min.js > flotr2.min.js | |
cat build/flotr2.min.js >> flotr2.min.js | |
echo ';' >> flotr2.min.js | |
cp build/ie.min.js flotr2.ie.min.js | |
flotr2-basic: libraries ie | |
smoosh make/basic.json | |
cat build/lib.min.js > flotr2-basic.min.js | |
cat build/flotr2-basic.min.js >> flotr2-basic.min.js | |
flotr-examples: | |
smoosh make/examples.json | |
cp build/examples.min.js flotr2.examples.min.js | |
cp build/examples-types.js flotr2.examples.types.js | |
flotr-amd: flotr2 | |
cat js/amd/pre.js > flotr2.amd.js | |
cat build/flotr2.js >> flotr2.amd.js | |
cat js/amd/post.js >> flotr2.amd.js | |
Flotr2 | |
====== | |
The Canvas graphing library. | |
![Google Groups](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) | |
http://groups.google.com/group/flotr2/ | |
Please fork http://jsfiddle.net/cesutherland/ZFBj5/ with your question or bug reproduction case. | |
API | |
--- | |
The API consists of a primary draw method which accepts a configuration object, helper methods, and several microlibs. | |
### Example | |
```javascript | |
var | |
// Container div: | |
container = document.getElementById("flotr-example-graph"), | |
// First data series: | |
d1 = [[0, 3], [4, 8], [8, 5], [9, 13]], | |
// Second data series: | |
d2 = [], | |
// A couple flotr configuration options: | |
options = { | |
xaxis: { | |
minorTickFreq: 4 | |
}, | |
grid: { | |
minorVerticalLines: true | |
} | |
}, | |
i, graph; | |
// Generated second data set: | |
for (i = 0; i < 14; i += 0.5) { | |
d2.push([i, Math.sin(i)]); | |
} | |
// Draw the graph: | |
graph = Flotr.draw( | |
container, // Container element | |
[ d1, d2 ], // Array of data series | |
options // Configuration options | |
); | |
``` | |
### Microlibs | |
* [underscore.js](http://documentcloud.github.com/underscore/) | |
* [bean.js](https://github.com/fat/bean) | |
Extending | |
--------- | |
Flotr may be extended by adding new plugins and graph types. | |
### Graph Types | |
Graph types define how a particular chart is rendered. Examples include line, bar, pie. | |
Existing graph types are found in `js/types/`. | |
### Plugins | |
Plugins extend the core of flotr with new functionality. They can add interactions, new decorations, etc. Examples | |
include titles, labels and selection. | |
The plugins included are found in `js/plugins/`. | |
Development | |
----------- | |
This project uses [smoosh](https://github.com/fat/smoosh) to build and [jasmine](http://pivotal.github.com/jasmine/) | |
with [js-imagediff](https://github.com/HumbleSoftware/js-imagediff) to test. Tests may be executed by | |
[jasmine-headless-webkit](http://johnbintz.github.com/jasmine-headless-webkit/) with | |
`cd spec; jasmine-headless-webkit -j jasmine.yml -c` or by a browser by navigating to | |
`flotr2/spec/SpecRunner.html`. | |
Shoutouts | |
--------- | |
Thanks to Bas Wenneker, Fabien Ménager and others for all the work on the original Flotr. | |
Thanks to Jochen Berger and Jordan Santell for their contributions to Flotr2. | |
Flotr 2 Architecture Notes | |
Global: | |
====== | |
Flotr.js - | |
versioning information | |
browser detection | |
extension (plugins, graph types) | |
draw | |
clone / merge | |
tick size | |
tick formatter | |
engineering notation | |
magnitude | |
rad, pixel, floor | |
drawText | |
measureText | |
getBestTextAlign | |
align map | |
compatibility | |
Graph Architecture: | |
=================== | |
Axis - | |
all series | |
orientation | |
ticks (major, minor) | |
scale (d2p, p2d, logarithmic) | |
notion of stacks | |
Series - | |
per 'data' | |
notion of range (x, y, min, max) | |
Graph - | |
DOM constructon | |
event attachment | |
options initialization | |
data range calculations | |
canvas spacing calculations | |
event normalization | |
draw methods | |
DOM cleanup | |
event cleanup | |
Utilities: | |
========== | |
Color | |
build colors | |
parse textual color data | |
convert colors | |
clone colors | |
Text | |
calculate text size | |
canvas size | |
html size | |
Date | |
formatting | |
constants | |
Spacing Calculation | |
=================== | |
Flotr | |
calculate data | |
calculate margins | |
Chart | |
calculate Data Ranges - Explicit or auto data minimum, maximums | |
calculate Data Range Extensions - By chart type, extend data range with needs of chart type (ie. stacked bars, stacked lines) | |
add Chart Padding - By chart type | |
Text | |
use explicit margins | |
calculate label margins | |
calculate title margins | |
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>Flotr Example Index Page</title> | |
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css" type="text/css" /> | |
<link rel="stylesheet" href="examples.css" type="text/css" /> | |
<link rel="stylesheet" href="editor.css" type="text/css" /> | |
</head> | |
<body> | |
<div id="body-container"> | |
<div id="content-container"> | |
<div id="content"> | |
<div id="examples"></div> | |
</div> | |
</div> | |
</div> | |
</body> | |
<script type="text/javascript" src="../lib/yepnope.js"></script> | |
<script type="text/javascript" src="js/includes.dev.js"></script> | |
</html> | |
/* Editor */ | |
.editor { | |
position: relative; | |
} | |
.editor .render { | |
height: 240px; | |
width: 320px; | |
margin: 8px auto; | |
} | |
.editor .source { | |
border: 1px solid #ddd; | |
border-radius: 3px; | |
} | |
.editor .controls { | |
position: absolute;; | |
z-index: 100; | |
right: 8px; | |
margin-top: -12px; | |
} | |
.editor .controls button { | |
float: right; | |
} | |
.editor .errors { | |
color: #ee0000; | |
padding: 8px; | |
font-size: 12px; | |
background: #fee; | |
border-bottom: 1px solid #eee; | |
} | |
.editor .errors .error { | |
font-weight: bold | |
} | |
.editor .errors .message { | |
font-style: italic; | |
} | |
.editor .errors .position { | |
display: block; | |
margin-top: 4px; | |
} | |
.editor.no-run .controls, | |
.editor.no-run .render { | |
display: none; | |
} | |
/* html type */ | |
.editor.html .render { | |
height: 400px; | |
width: 800px; | |
text-align: center; | |
} | |
.editor.html .render iframe { | |
height: 100%; | |
width: 100%; | |
border: none; | |
} | |
/* CodeMirror */ | |
.CodeMirror { | |
background: #fafafa; | |
} | |
.CodeMirror.CodeMirror-focused { | |
} | |
.CodeMirror-scroll { | |
height: auto; | |
overflow: visible; | |
overflow-x: auto; | |
} | |
.CodeMirror-lines pre, | |
.CodeMirror-gutter pre { | |
line-height: 16px; | |
} | |
body { | |
font-family : sans-serif; | |
padding: 0px; | |
margin: 0px; | |
} | |
/* Example */ | |
.flotr-example { | |
display: none; | |
margin: 0px auto 48px auto; | |
position: relative; | |
} | |
.flotr-example-label { | |
font-size: 18px; | |
padding: 14px 0px; | |
} | |
.flotr-example-editor .editor .render { | |
width: 600px; | |
height: 400px; | |
margin: 12px auto 18px auto; | |
} | |
.flotr-example-editor .editor .source { | |
width: 720px; | |
} | |
/* Chart no-select */ | |
.flotr-example-editor .editor .render, | |
.flotr-examples-thumb { | |
-webkit-user-select: none; | |
-khtml-user-select: none; | |
-moz-user-select: none; | |
-o-user-select: none; | |
user-select: none; | |
} | |
/* Examples */ | |
.flotr-examples-thumbs { | |
text-align: center; | |
} | |
.flotr-examples-reset { | |
z-index: 100; | |
cursor: pointer; | |
text-decoration: underline; | |
position: absolute; | |
top: 260px; | |
right: 24px; | |
display: none; | |
} | |
.flotr-examples-collapsed .flotr-examples-reset { | |
display: block; | |
} | |
.flotr-examples-thumb { | |
display: inline-block; | |
font-size : 11px; | |
width : 300px; | |
height : 200px; | |
margin: 10px 15px; | |
border: 2px solid transparent; | |
} | |
.flotr-examples-thumb.flotr-examples-highlight{ | |
font-size : 12px; | |
width : 330px; | |
height : 220px; | |
margin: 0px 0px; | |
} | |
.flotr-examples-thumb .flotr-legend, | |
.flotr-examples-thumb .flotr-mouse-value { | |
display : none; | |
} | |
.flotr-examples-collapsed .flotr-examples-container { | |
margin-top: 20px; | |
position: relative; | |
margin: 0px auto; | |
} | |
.flotr-examples-collapsed .flotr-examples-thumbs { | |
position: relative; | |
overflow-x: auto; | |
white-space: nowrap; | |
} | |
/* Flotr Styles */ | |
.flotr-datagrid-container { | |
border: 1px solid #999; | |
border-bottom: none; | |
background: #fff; | |
} | |
.flotr-datagrid { | |
border-collapse: collapse; | |
border-spacing: 0; | |
} | |
.flotr-datagrid td, .flotr-datagrid th { | |
border: 1px solid #ccc; | |
padding: 1px 3px; | |
min-width: 2em; | |
} | |
.flotr-datagrid tr:hover, .flotr-datagrid col.hover { | |
background: #f3f3f3; | |
} | |
.flotr-datagrid tr:hover th, .flotr-datagrid th.hover { | |
background: #999; | |
color: #fff; | |
} | |
.flotr-datagrid th { | |
text-align: left; | |
background: #e3e3e3; | |
border: 2px outset #fff; | |
} | |
.flotr-datagrid-toolbar { | |
padding: 1px; | |
border-bottom: 1px solid #ccc; | |
background: #f9f9f9; | |
} | |
.flotr-datagrid td:hover { | |
background: #ccc; | |
} | |
.flotr-datagrid .first-row th { | |
text-align: center; | |
} | |
.flotr-canvas { | |
margin-bottom: -3px; | |
padding-bottom: 1px; | |
} | |
.flotr-tabs-group { | |
border-top: 1px solid #999; | |
} | |
.flotr-tab { | |
border: 1px solid #666; | |
border-top: none; | |
margin: 0 3px; | |
padding: 1px 4px; | |
cursor: pointer; | |
-moz-border-radius: 0 0 4px 4px; | |
-webkit-border-bottom-left-radius: 4px; | |
-webkit-border-bottom-right-radius: 4px; | |
border-radius: 0 0 4px 4px; | |
opacity: 0.5; | |
-moz-opacity: 0.5; | |
} | |
.flotr-tab.selected { | |
background: #ddd; | |
opacity: 1; | |
-moz-opacity: 1; | |
} | |
.flotr-tab:hover { | |
background: #ccc; | |
} | |
/* Large */ | |
.flotr-examples-large .flotr-example { | |
width: 1360px; | |
margin: 0px auto; | |
position: relative; | |
} | |
.flotr-examples-large .flotr-example-editor .editor .render { | |
margin-left: 0px; | |
margin-right: 0px; | |
} | |
.flotr-examples-large .flotr-example-editor .controls { | |
top: 0px; | |
} | |
.flotr-examples-large .flotr-example-editor .source { | |
position: absolute; | |
top: 0px; | |
right: 0px; | |
} | |
/* Veritical Thumbs */ | |
.flotr-examples-large.flotr-examples-collapsed .flotr-examples-reset, | |
.flotr-examples-medium.flotr-examples-collapsed .flotr-examples-reset { | |
top: 16px; | |
} | |
.flotr-examples-large.flotr-examples-collapsed .flotr-examples-thumbs, | |
.flotr-examples-medium.flotr-examples-collapsed .flotr-examples-thumbs { | |
position: fixed; | |
width: 400px; | |
left: 0px; | |
top: 0px; | |
overflow-y: auto; | |
background: #fff; | |
} | |
.flotr-examples-large.flotr-examples-collapsed .flotr-examples-thumb, | |
.flotr-examples-medium.flotr-examples-collapsed .flotr-examples-thumb { | |
display: block; | |
float: center; | |
margin: 10px auto; | |
} | |
.flotr-examples-large.flotr-examples-collapsed .flotr-examples-container, | |
.flotr-examples-medium.flotr-examples-collapsed .flotr-examples-container { | |
margin-left: 400px; | |
} | |
/* Vertical Example */ | |
.flotr-examples-small .flotr-example, | |
.flotr-examples-medium .flotr-example { | |
width: 720px; | |
} | |
.flotr-examples-small .flotr-example-editor .source, | |
.flotr-examples-medium .flotr-example-editor .source { | |
position: relative; | |
} | |
Binary files /dev/null and b/js/flotr2/examples/images/butterfly.jpg differ
Binary files /dev/null and b/js/flotr2/examples/images/checkmark.png differ
Binary files /dev/null and b/js/flotr2/examples/images/xmark.png differ
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | |
<title>Flotr Example Index Page</title> | |
<link rel="stylesheet" href="lib/codemirror/lib/codemirror.css" type="text/css" /> | |
<link rel="stylesheet" href="examples.css" type="text/css" /> | |
<link rel="stylesheet" href="editor.css" type="text/css" /> | |
</head> | |
<body> | |
<div id="body-container"> | |
<div id="content-container"> | |
<div id="content"> | |
<div id="examples"></div> | |
</div> | |
</div> | |
</div> | |
</body> | |
<script type="text/javascript" src="../lib/yepnope.js"></script> | |
<script type="text/javascript" src="js/includes.min.js"></script> | |
</html> | |
(function () { | |
var | |
ONERROR = window.onerror, | |
COUNT = 0, | |
TYPES = {}, | |
T_CONTROLS = | |
'<div class="controls">' + | |
'<button class="fiddle btn large primary">Fiddle</button>' + | |
'<button class="run btn large primary">Run</button>' + | |
'</div>', | |
T_EDITOR = '<div class="editor"></div>', | |
T_SOURCE = '<div class="source"></div>', | |
T_ERRORS = '<div class="errors"></div>', | |
T_RENDER = '<div class="render"></div>', | |
T_IFRAME = '<iframe></iframe>'; | |
// Javascript type: | |
TYPES.javascript = function Javascript (o) { | |
this.onerror = o.onerror; | |
}; | |
TYPES.javascript.prototype = { | |
codeMirrorType : 'javascript', | |
example : function (o) { | |
var | |
example = o.example, | |
render = o.render, | |
renderId = $(render).attr('id'), | |
args = o.args ? ',' + o.args.toString() : ''; | |
return '(' + example + ')(document.getElementById("' + renderId+ '")' + | |
args + ');'; | |
}, | |
render : function (o) { | |
eval(o.example); | |
} | |
}; | |
// HTML Type: | |
TYPES.html = function Html (o) { | |
this.onerror = o.onerror; | |
}; | |
TYPES.html.prototype = { | |
codeMirrorType : 'htmlmixed', | |
example : function (o) { | |
return $.trim(o.example); | |
}, | |
render : function (o) { | |
var | |
example = o.example, | |
render = o.render, | |
iframe = $(T_IFRAME), | |
that = this, | |
win, doc; | |
render.html(iframe); | |
win = iframe[0].contentWindow; | |
doc = win.document; | |
doc.open(); | |
// Error | |
win.onerror = iframe.onerror = function () { | |
that.onerror.apply(null, arguments); | |
} | |
doc.write(example); | |
doc.close(); | |
} | |
}; | |
// Editor | |
function Editor (container, o) { | |
var | |
type = o.type || 'javascript', | |
example = o.example || '', | |
noRun = o.noRun || false, | |
teardown = o.teardown || false, | |
controls = $(T_CONTROLS), | |
render = $(T_RENDER), | |
errors = $(T_ERRORS), | |
source = $(T_SOURCE), | |
node = $(T_EDITOR), | |
renderId = 'editor-render-' + COUNT, | |
api, | |
render, | |
codeMirror; | |
api = new TYPES[type]({ | |
onerror : onerror | |
}); | |
if (!api) throw 'Invalid type: API not found for type `' + type + '`.'; | |
render | |
.attr('id', renderId); | |
errors | |
.hide(); | |
node | |
.append(render) | |
.append(controls) | |
.append(source) | |
.addClass(type) | |
.addClass(noRun ? 'no-run' : ''); | |
container = $(container); | |
container | |
.append(node); | |
source | |
.append(errors) | |
example = api.example({ | |
args : o.args, | |
example : example, | |
render : render | |
}); | |
codeMirror = CodeMirror(source[0], { | |
value : example, | |
readOnly : noRun, | |
lineNumbers : true, | |
mode : api.codeMirrorType | |
}); | |
if (!noRun) { | |
controls.delegate('.run', 'click', function () { | |
example = codeMirror.getValue(); | |
execute(); | |
}); | |
execute(); | |
} | |
controls.delegate('.fiddle', 'click', function () { | |
fiddle(); | |
}); | |
// Error handling: | |
window.onerror = function (message, url, line) { | |
onerror(message, url, line); | |
console.log(message); | |
if (ONERROR && $.isFunction(ONERROR)) { | |
return ONERROR(message, url, line); | |
} else { | |
return false; | |
} | |
} | |
// Helpers | |
function execute () { | |
errors.hide(); | |
if (teardown) { | |
teardown.call(); | |
} | |
api.render({ | |
example : example, | |
render : render | |
}); | |
} | |
function onerror (message, url, line) { | |
// @TODO Find some js error normalizing lib | |
var | |
doThatSexyThang = false, | |
html = '<span class="error">Error: </span>', | |
error, stack; | |
/* | |
// Native error type handling: | |
if (typeof (message) !== 'string') { | |
error = message; | |
message = error.message; | |
stack = error.stack; | |
//if (stack) { | |
console.log(stack); | |
//} | |
//console.log(message); | |
} | |
*/ | |
html += '<span class="message">' + message + '</span>'; | |
if (typeof (line) !== "undefined") { | |
html += '<span class="position">'; | |
html += 'Line <span class="line">' + line + '</span>'; | |
console.log(url); | |
if (url) { | |
html += ' of '; | |
if (url == window.location) { | |
html += '<span class="url">script</span>'; | |
if (doThatSexyThang) { | |
//codeMirror.setMarker(line, '•'); | |
} | |
} else { | |
html += '<span class="url">' + url + '</span>'; | |
} | |
} | |
html += '.</span>'; | |
} | |
errors.show(); | |
errors.html(html); | |
} | |
function fiddle () { | |
var | |
url = 'http://jsfiddle.net/api/post/jquery/1.7/', | |
form = $('<form method="post" action="' + url + '" target="_blank"></form>'), | |
input; | |
// Resources | |
resources = [ | |
'https://raw.github.com/HumbleSoftware/Flotr2/master/flotr2.min.js', | |
'https://raw.github.com/HumbleSoftware/Flotr2/master/examples/examples.css' | |
]; | |
input = $('<input type="hidden" name="resources">') | |
.attr('value', resources.join(',')); | |
form.append(input); | |
// HTML | |
input = $('<input type="hidden" name="html">') | |
.attr('value', '<div id="'+renderId+'"></div>'); | |
form.append(input); | |
// CSS | |
input = $('<input type="hidden" name="normalize_css" value="no">') | |
form.append(input); | |
input = $('<input type="hidden" name="css">') | |
.attr('value', | |
'#'+renderId+' {\n width: 340px;\n height: 220px;' + | |
'\n margin: 24px auto;\n}' | |
); | |
form.append(input); | |
// JS | |
input = $('<input type="hidden" name="js">') | |
.attr('value', '$(function () {\n' + example + '\n});'); | |
form.append(input); | |
// Submit | |
form.append(input); | |
$(document.body).append(form); | |
form.submit(); | |
} | |
COUNT++; | |
this.setExample = function (source, args) { | |
example = api.example({ | |
args : args, | |
example : source, | |
render : render | |
}); | |
codeMirror.setValue(example); | |
codeMirror.refresh(); | |
execute(); | |
} | |
} | |
if (typeof Flotr.Examples === 'undefined') Flotr.Examples = {}; | |
Flotr.Examples.Editor = Editor; | |
})(); | |
(function () { | |
var | |
_ = Flotr._, | |
DOT = '.', | |
CN_EXAMPLE = 'flotr-example', | |
CN_LABEL = 'flotr-example-label', | |
CN_TITLE = 'flotr-example-title', | |
CN_MARKUP = 'flotr-example-description', | |
CN_EDITOR = 'flotr-example-editor', | |
ID_GRAPH = 'flotr-example-graph', | |
TEMPLATE = '' + | |
'<div class="' + CN_EXAMPLE + '">' + | |
'<div class="' + CN_LABEL + ' ' + CN_TITLE + '"></div>' + | |
'<div class="' + CN_MARKUP + '"></div>' + | |
'<div class="' + CN_EDITOR + '"></div>' + | |
'</div>', | |
Example = function (o) { | |
this.options = o; | |
this.example = null; | |
this._initNodes(); | |
}; | |
Example.prototype = { | |
setExample : function (example) { | |
var | |
source = this.getSource(example), | |
editorNode = this._editorNode; | |
this.example = example; | |
Math.seedrandom(example.key); | |
this._exampleNode.css({ display: 'block' }); | |
this._titleNode.html(example.name || ''); | |
this._markupNode.html(example.description || ''); | |
if (!this._editor) { | |
this._editor = new Flotr.Examples.Editor(editorNode, { | |
args : example.args, | |
example : source, | |
teardown : function () { | |
// Unbind event listeners from previous examples | |
Flotr.EventAdapter.stopObserving($(editorNode).find('.render')[0]); | |
$(editorNode).find('canvas').each(function (index, canvas) { | |
Flotr.EventAdapter.stopObserving(canvas); | |
}); | |
} | |
}); | |
} else { | |
this._editor.setExample(source, example.args); | |
} | |
}, | |
getSource : function (example) { | |
var | |
source = example.callback.toString(); | |
// Hack for FF code style | |
if (navigator.userAgent.search(/firefox/i) !== -1) | |
source = js_beautify(source); | |
return source; | |
}, | |
executeCallback : function (example, node) { | |
if (!_.isElement(node)) node = node[0]; | |
var args = (example.args ? [node].concat(example.args) : [node]); | |
Math.seedrandom(example.key); | |
return example.callback.apply(this, args); | |
}, | |
_initNodes : function () { | |
var | |
node = this.options.node, | |
example = $(TEMPLATE); | |
this._titleNode = example.find(DOT + CN_TITLE); | |
this._markupNode = example.find(DOT + CN_MARKUP); | |
this._editorNode = example.find(DOT + CN_EDITOR); | |
this._exampleNode = example; | |
node.append(example); | |
} | |
}; | |
Flotr.Examples.Example = Example; | |
})(); | |
(function () { | |
var ExampleList = function () { | |
// Map of examples. | |
this.examples = {}; | |
}; | |
ExampleList.prototype = { | |
add : function (example) { | |
this.examples[example.key] = example; | |
}, | |
get : function (key) { | |
return key ? (this.examples[key] || null) : this.examples; | |
}, | |
getType : function (type) { | |
return Flotr._.select(this.examples, function (example) { | |
return (example.type === type); | |
}); | |
} | |
} | |
Flotr.ExampleList = new ExampleList(); | |
})(); | |
(function () { | |
var | |
E = Flotr.EventAdapter, | |
_ = Flotr._, | |
CLICK = 'click', | |
EXAMPLE = 'example', | |
MOUSEENTER = 'mouseenter', | |
MOUSELEAVE = 'mouseleave', | |
DOT = '.', | |
CN_EXAMPLES = 'flotr-examples', | |
CN_CONTAINER = 'flotr-examples-container', | |
CN_RESET = 'flotr-examples-reset', | |
CN_THUMBS = 'flotr-examples-thumbs', | |
CN_THUMB = 'flotr-examples-thumb', | |
CN_COLLAPSED = 'flotr-examples-collapsed', | |
CN_HIGHLIGHT = 'flotr-examples-highlight', | |
CN_LARGE = 'flotr-examples-large', | |
CN_MEDIUM = 'flotr-examples-medium', | |
CN_SMALL = 'flotr-examples-small', | |
CN_MOBILE = 'flotr-examples-mobile', | |
T_THUMB = '<div class="' + CN_THUMB + '"></div>', | |
TEMPLATE = '' + | |
'<div class="' + CN_EXAMPLES + '">' + | |
'<div class="' + CN_RESET + '">View All</div>' + | |
'<div class="' + CN_THUMBS + '"></div>' + | |
'<div class="' + CN_CONTAINER + '"></div>' + | |
'</div>' | |
Examples = function (o) { | |
if (_.isUndefined(Flotr.ExampleList)) throw "Flotr.ExampleList not defined."; | |
this.options = o; | |
this.list = Flotr.ExampleList; | |
this.current = null; | |
this.single = false; | |
this._initNodes(); | |
this._example = new Flotr.Examples.Example({ | |
node : this._exampleNode | |
}); | |
//console.time(EXAMPLE); | |
//console.profile(); | |
this._initExamples(); | |
//console.profileEnd(); | |
//console.timeEnd(EXAMPLE); | |
}; | |
Examples.prototype = { | |
examples : function () { | |
var | |
styles = {cursor : 'pointer'}, | |
thumbsNode = this._thumbsNode, | |
list = this.list.get(), | |
that = this; | |
var | |
order = [ | |
"basic", | |
"basic-axis", | |
"basic-bars", | |
"basic-bars-horizontal", | |
"basic-bar-stacked", | |
"basic-stacked-horizontal", | |
"basic-pie", | |
"basic-radar", | |
"basic-bubble", | |
"basic-candle", | |
"basic-legend", | |
"mouse-tracking", | |
"mouse-zoom", | |
"mouse-drag", | |
"basic-time", | |
"negative-values", | |
"click-example", | |
"download-image", | |
"download-data", | |
"advanced-titles", | |
"color-gradients", | |
"basic-timeline", | |
"advanced-markers" | |
]; | |
(function runner () { | |
var | |
key = order.shift(), | |
example = list[key]; | |
if (example.type === 'profile' || example.type === 'test') return; | |
var node = $(T_THUMB); | |
node.data('example', example); | |
thumbsNode.append(node); | |
that._example.executeCallback(example, node); | |
node.click(function () {that._loadExample(example)}); | |
if (order.length) setTimeout(runner, 20); | |
})(); | |
function zoomHandler (e) { | |
var | |
node = $(e.currentTarget), | |
example = node.data('example'), | |
orientation = e.data.orientation; | |
if (orientation ^ node.hasClass(CN_HIGHLIGHT)) { | |
node.toggleClass(CN_HIGHLIGHT).css(styles); | |
that._example.executeCallback(example, node); | |
} | |
} | |
thumbsNode.delegate(DOT + CN_THUMB, 'mouseenter', {orientation : true}, zoomHandler); | |
thumbsNode.delegate(DOT + CN_THUMB, 'mouseleave', {orientation : false}, zoomHandler); | |
if ($(window).hashchange) { | |
$(window).hashchange(function () { | |
that._loadHash(); | |
}); | |
} | |
}, | |
_loadExample : function (example) { | |
if (example) { | |
if (this._currentExample !== example) { | |
this._currentExample = example; | |
} else { | |
return; | |
} | |
window.location.hash = '!'+(this.single ? 'single/' : '')+example.key; | |
if (!scroller) { | |
this._thumbsNode.css({ | |
position: 'absolute', | |
height: '0px', | |
overflow: 'hidden', | |
width: '0px' | |
}); | |
this._resetNode.css({ | |
top: '16px' | |
}); | |
} | |
this._examplesNode.addClass(CN_COLLAPSED); | |
this._exampleNode.show(); | |
this._example.setExample(example); | |
this._resize(); | |
$(document).scrollTop(0); | |
} | |
}, | |
_reset : function () { | |
window.location.hash = ''; | |
if (!scroller) { | |
this._thumbsNode.css({ | |
position: '', | |
height: '', | |
overflow: '', | |
width: '' | |
}); | |
} | |
this._examplesNode.removeClass(CN_COLLAPSED); | |
this._thumbsNode.height(''); | |
this._exampleNode.hide(); | |
}, | |
_initNodes : function () { | |
var | |
node = $(this.options.node), | |
that = this, | |
examplesNode = $(TEMPLATE); | |
that._resetNode = examplesNode.find(DOT+CN_RESET); | |
that._exampleNode = examplesNode.find(DOT+CN_CONTAINER); | |
that._thumbsNode = examplesNode.find(DOT+CN_THUMBS); | |
that._examplesNode = examplesNode; | |
that._resetNode.click(function () { | |
that._reset(); | |
}); | |
node.append(examplesNode); | |
this._initResizer(); | |
}, | |
_initResizer : function () { | |
var | |
that = this, | |
node = that._examplesNode, | |
page = $(window), | |
currentClass; | |
$(window).resize(applySize); | |
applySize(); | |
function applySize () { | |
var | |
height = page.height() - (that.options.thumbPadding || 0), | |
width = page.width(), | |
newClass; | |
if (width > 1760) { | |
newClass = CN_LARGE; | |
that._thumbsNode.height(height); | |
} else if (width > 1140) { | |
newClass = CN_MEDIUM; | |
that._thumbsNode.height(height); | |
} else { | |
newClass = CN_SMALL; | |
that._thumbsNode.height(''); | |
} | |
if (currentClass !== newClass) { | |
if (currentClass) | |
that._examplesNode.removeClass(currentClass); | |
that._examplesNode.addClass(newClass); | |
currentClass = newClass; | |
} | |
} | |
this._resize = applySize; | |
}, | |
_initExamples : function () { | |
var | |
hash = window.location.hash, | |
example, params; | |
hash = hash.substring(2); | |
params = hash.split('/'); | |
if (params.length == 1) { | |
this.examples(); | |
if (hash) { | |
this._loadHash(); | |
} | |
} | |
else { | |
if (params[0] == 'single') { | |
this.single = true; | |
this._loadExample( | |
this.list.get(params[1]) | |
); | |
} | |
} | |
}, | |
_loadHash : function () { | |
var | |
hash = window.location.hash, | |
example; | |
hash = hash.substring(2); | |
if (hash) { | |
example = this.list.get(hash); | |
this._loadExample(example); | |
} else { | |
this._reset(); | |
} | |
} | |
} | |
var scroller = (function () { | |
var | |
mobile = !!( | |
navigator.userAgent.match(/Android/i) || | |
navigator.userAgent.match(/webOS/i) || | |
navigator.userAgent.match(/iPhone/i) || | |
navigator.userAgent.match(/iPod/i) | |
), | |
mozilla = !!$.browser.mozilla; | |
return (!mobile || mozilla); | |
})(); | |
Flotr.Examples = Examples; | |
})(); | |
(function () { | |
var | |
D = Flotr.DOM, | |
E = Flotr.EventAdapter, | |
_ = Flotr._, | |
CLICK = 'click', | |
ID_EXAMPLE_PROFILE = 'example-profile', | |
ID_EXAMPLES = 'examples', | |
Profile = function (o) { | |
if (_.isUndefined(Flotr.ExampleList)) throw "Flotr.ExampleList not defined."; | |
this.editMode = 'off'; | |
this.list = Flotr.ExampleList; | |
this.current = null; | |
this.single = false; | |
this.init(); | |
}; | |
Profile.prototype = _.extend({}, Flotr.Examples.prototype, { | |
examples : function () { | |
var | |
examplesNode = document.getElementById(ID_EXAMPLES), | |
listNode = D.node('<ul></ul>'), | |
profileNode; | |
_.each(this.list.getType('profile'), function (example) { | |
profileNode = D.node('<li>' + example.name + '</li>'); | |
D.insert(listNode, profileNode); | |
E.observe(profileNode, CLICK, _.bind(function () { | |
this.example(example); | |
}, this)); | |
}, this); | |
D.insert(examplesNode, listNode); | |
}, | |
example : function (example) { | |
this._renderSource(example); | |
this.profileStart(example); | |
setTimeout(_.bind(function () { | |
this._renderGraph(example); | |
this.profileEnd(); | |
}, this), 50); | |
}, | |
profileStart : function (example) { | |
var profileNode = document.getElementById(ID_EXAMPLE_PROFILE); | |
this._startTime = new Date(); | |
profileNode.innerHTML = '<div>Profile started for "'+example.name+'"...</div>'; | |
}, | |
profileEnd : function (example) { | |
var | |
profileNode = document.getElementById(ID_EXAMPLE_PROFILE); | |
profileTime = (new Date()) - this._startTime; | |
this._startTime = null; | |
profileNode.innerHTML += '<div>Profile complete: '+profileTime+'ms<div>'; | |
} | |
}); | |
Flotr.Profile = Profile; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'advanced-markers', | |
name : 'Advanced Markers', | |
callback : advanced_markers, | |
timeout : 150 | |
}); | |
function advanced_markers (container) { | |
var | |
xmark = new Image(), | |
checkmark = new Image(), | |
bars = { | |
data: [], | |
bars: { | |
show: true, | |
barWidth: 0.6, | |
lineWidth: 0, | |
fillOpacity: 0.8 | |
} | |
}, markers = { | |
data: [], | |
markers: { | |
show: true, | |
position: 'ct', | |
labelFormatter: function (o) { | |
return (o.y >= 5) ? checkmark : xmark; | |
} | |
} | |
}, | |
flotr = Flotr, | |
point, | |
graph, | |
i; | |
for (i = 0; i < 8; i++) { | |
point = [i, Math.ceil(Math.random() * 10)]; | |
bars.data.push(point); | |
markers.data.push(point); | |
} | |
var runner = function () { | |
if (!xmark.complete || !checkmark.complete) { | |
setTimeout(runner, 50); | |
return; | |
} | |
graph = flotr.draw( | |
container, | |
[bars, markers], { | |
yaxis: { | |
min: 0, | |
max: 11 | |
}, | |
xaxis: { | |
min: -0.5, | |
max: 7.5 | |
}, | |
grid: { | |
verticalLines: false | |
} | |
} | |
); | |
} | |
xmark.onload = runner; | |
xmark.src = 'images/xmark.png'; | |
checkmark.src = 'images/checkmark.png'; | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'advanced-titles', | |
name : 'Advanced Titles', | |
callback : advanced_titles | |
}); | |
function advanced_titles (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
d4 = [], | |
d5 = [], | |
graph, | |
i; | |
for (i = 0; i <= 10; i += 0.1) { | |
d1.push([i, 4 + Math.pow(i,1.5)]); | |
d2.push([i, Math.pow(i,3)]); | |
d3.push([i, i*5+3*Math.sin(i*4)]); | |
d4.push([i, i]); | |
if (i.toFixed(1)%1 == 0) { | |
d5.push([i, 2*i]); | |
} | |
} | |
// Draw the graph. | |
graph = Flotr.draw( | |
container,[ | |
{ data : d1, label : 'y = 4 + x^(1.5)', lines : { fill : true } }, | |
{ data : d2, label : 'y = x^3', yaxis : 2 }, | |
{ data : d3, label : 'y = 5x + 3sin(4x)' }, | |
{ data : d4, label : 'y = x' }, | |
{ data : d5, label : 'y = 2x', lines : { show : true }, points : { show : true } } | |
], { | |
title : 'Advanced Titles Example', | |
subtitle : 'You can save me as an image', | |
xaxis : { | |
noTicks : 7, | |
tickFormatter : function (n) { return '('+n+')'; }, | |
min : 1, | |
max : 7.5, | |
labelsAngle : 45, | |
title : 'x Axis' | |
}, | |
yaxis : { | |
ticks : [[0, "Lower"], 10, 20, 30, [40, "Upper"]], | |
max : 40, | |
title : 'y = f(x)' | |
}, | |
y2axis : { color : '#FF0000', max : 500, title : 'y = x^3' }, | |
grid : { | |
verticalLines : false, | |
backgroundColor : 'white' | |
}, | |
HtmlText : false, | |
legend : { | |
position : 'nw' | |
} | |
}); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-axis', | |
name : 'Basic Axis', | |
callback : basic_axis | |
}); | |
function basic_axis (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
d4 = [], | |
d5 = [], // Data | |
ticks = [[ 0, "Lower"], 10, 20, 30, [40, "Upper"]], // Ticks for the Y-Axis | |
graph; | |
for(var i = 0; i <= 10; i += 0.1){ | |
d1.push([i, 4 + Math.pow(i,1.5)]); | |
d2.push([i, Math.pow(i,3)]); | |
d3.push([i, i*5+3*Math.sin(i*4)]); | |
d4.push([i, i]); | |
if( i.toFixed(1)%1 == 0 ){ | |
d5.push([i, 2*i]); | |
} | |
} | |
d3[30][1] = null; | |
d3[31][1] = null; | |
function ticksFn (n) { return '('+n+')'; } | |
graph = Flotr.draw(container, [ | |
{ data : d1, label : 'y = 4 + x^(1.5)', lines : { fill : true } }, | |
{ data : d2, label : 'y = x^3'}, | |
{ data : d3, label : 'y = 5x + 3sin(4x)'}, | |
{ data : d4, label : 'y = x'}, | |
{ data : d5, label : 'y = 2x', lines : { show : true }, points : { show : true } } | |
], { | |
xaxis : { | |
noTicks : 7, // Display 7 ticks. | |
tickFormatter : ticksFn, // Displays tick values between brackets. | |
min : 1, // Part of the series is not displayed. | |
max : 7.5 // Part of the series is not displayed. | |
}, | |
yaxis : { | |
ticks : ticks, // Set Y-Axis ticks | |
max : 40 // Maximum value along Y-Axis | |
}, | |
grid : { | |
verticalLines : false, | |
backgroundColor : { | |
colors : [[0,'#fff'], [1,'#ccc']], | |
start : 'top', | |
end : 'bottom' | |
} | |
}, | |
legend : { | |
position : 'nw' | |
}, | |
title : 'Basic Axis example', | |
subtitle : 'This is a subtitle' | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-bar-stacked', | |
name : 'Stacked Bars', | |
callback : bars_stacked | |
}); | |
Flotr.ExampleList.add({ | |
key : 'basic-stacked-horizontal', | |
name : 'Stacked Horizontal Bars', | |
args : [true], | |
callback : bars_stacked, | |
tolerance : 5 | |
}); | |
function bars_stacked (container, horizontal) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
graph, i; | |
for (i = -10; i < 10; i++) { | |
if (horizontal) { | |
d1.push([Math.random(), i]); | |
d2.push([Math.random(), i]); | |
d3.push([Math.random(), i]); | |
} else { | |
d1.push([i, Math.random()]); | |
d2.push([i, Math.random()]); | |
d3.push([i, Math.random()]); | |
} | |
} | |
graph = Flotr.draw(container,[ | |
{ data : d1, label : 'Serie 1' }, | |
{ data : d2, label : 'Serie 2' }, | |
{ data : d3, label : 'Serie 3' } | |
], { | |
legend : { | |
backgroundColor : '#D2E8FF' // Light blue | |
}, | |
bars : { | |
show : true, | |
stacked : true, | |
horizontal : horizontal, | |
barWidth : 0.6, | |
lineWidth : 1, | |
shadowSize : 0 | |
}, | |
grid : { | |
verticalLines : horizontal, | |
horizontalLines : !horizontal | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-bars', | |
name : 'Basic Bars', | |
callback : basic_bars | |
}); | |
Flotr.ExampleList.add({ | |
key : 'basic-bars-horizontal', | |
name : 'Horizontal Bars', | |
args : [true], | |
callback : basic_bars, | |
tolerance : 5 | |
}); | |
function basic_bars (container, horizontal) { | |
var | |
horizontal = (horizontal ? true : false), // Show horizontal bars | |
d1 = [], // First data series | |
d2 = [], // Second data series | |
point, // Data point variable declaration | |
i; | |
for (i = 0; i < 4; i++) { | |
if (horizontal) { | |
point = [Math.ceil(Math.random()*10), i]; | |
} else { | |
point = [i, Math.ceil(Math.random()*10)]; | |
} | |
d1.push(point); | |
if (horizontal) { | |
point = [Math.ceil(Math.random()*10), i+0.5]; | |
} else { | |
point = [i+0.5, Math.ceil(Math.random()*10)]; | |
} | |
d2.push(point); | |
}; | |
// Draw the graph | |
Flotr.draw( | |
container, | |
[d1, d2], | |
{ | |
bars : { | |
show : true, | |
horizontal : horizontal, | |
shadowSize : 0, | |
barWidth : 0.5 | |
}, | |
mouse : { | |
track : true, | |
relative : true | |
}, | |
yaxis : { | |
min : 0, | |
autoscaleMargin : 1 | |
} | |
} | |
); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-bubble', | |
name : 'Basic Bubble', | |
callback : basic_bubble | |
}); | |
function basic_bubble (container) { | |
var | |
d1 = [], | |
d2 = [], | |
point, graph, i; | |
for (i = 0; i < 10; i++ ){ | |
point = [i, Math.ceil(Math.random()*10), Math.ceil(Math.random()*10)]; | |
d1.push(point); | |
point = [i, Math.ceil(Math.random()*10), Math.ceil(Math.random()*10)]; | |
d2.push(point); | |
} | |
// Draw the graph | |
graph = Flotr.draw(container, [d1, d2], { | |
bubbles : { show : true, baseRadius : 5 }, | |
xaxis : { min : -4, max : 14 }, | |
yaxis : { min : -4, max : 14 } | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-candle', | |
name : 'Basic Candle', | |
callback : basic_candle | |
}); | |
function basic_candle (container) { | |
var | |
d1 = [], | |
price = 3.206, | |
graph, | |
i, a, b, c; | |
for (i = 0; i < 50; i++) { | |
a = Math.random(); | |
b = Math.random(); | |
c = (Math.random() * (a + b)) - b; | |
d1.push([i, price, price + a, price - b, price + c]); | |
price = price + c; | |
} | |
// Graph | |
graph = Flotr.draw(container, [ d1 ], { | |
candles : { show : true, candleWidth : 0.6 }, | |
xaxis : { noTicks : 10 } | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-legend', | |
name : 'Basic Legend', | |
callback : basic_legend | |
}); | |
function basic_legend (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
data, | |
graph, i; | |
// Data Generation | |
for (i = 0; i < 15; i += 0.5) { | |
d1.push([i, i + Math.sin(i+Math.PI)]); | |
d2.push([i, i]); | |
d3.push([i, 15-Math.cos(i)]); | |
} | |
data = [ | |
{ data : d1, label :'x + sin(x+π)' }, | |
{ data : d2, label :'x' }, | |
{ data : d3, label :'15 - cos(x)' } | |
]; | |
// This function prepend each label with 'y = ' | |
function labelFn (label) { | |
return 'y = ' + label; | |
} | |
// Draw graph | |
graph = Flotr.draw(container, data, { | |
legend : { | |
position : 'se', // Position the legend 'south-east'. | |
labelFormatter : labelFn, // Format the labels. | |
backgroundColor : '#D2E8FF' // A light blue background color. | |
}, | |
HtmlText : false | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-pie', | |
name : 'Basic Pie', | |
callback : basic_pie | |
}); | |
function basic_pie (container) { | |
var | |
d1 = [[0, 4]], | |
d2 = [[0, 3]], | |
d3 = [[0, 1.03]], | |
d4 = [[0, 3.5]], | |
graph; | |
graph = Flotr.draw(container, [ | |
{ data : d1, label : 'Comedy' }, | |
{ data : d2, label : 'Action' }, | |
{ data : d3, label : 'Romance', | |
pie : { | |
explode : 50 | |
} | |
}, | |
{ data : d4, label : 'Drama' } | |
], { | |
HtmlText : false, | |
grid : { | |
verticalLines : false, | |
horizontalLines : false | |
}, | |
xaxis : { showLabels : false }, | |
yaxis : { showLabels : false }, | |
pie : { | |
show : true, | |
explode : 6 | |
}, | |
mouse : { track : true }, | |
legend : { | |
position : 'se', | |
backgroundColor : '#D2E8FF' | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-radar', | |
name : 'Basic Radar', | |
callback : basic_radar | |
}); | |
function basic_radar (container) { | |
// Fill series s1 and s2. | |
var | |
s1 = { label : 'Actual', data : [[0, 3], [1, 8], [2, 5], [3, 5], [4, 3], [5, 9]] }, | |
s2 = { label : 'Target', data : [[0, 8], [1, 7], [2, 8], [3, 2], [4, 4], [5, 7]] }, | |
graph, ticks; | |
// Radar Labels | |
ticks = [ | |
[0, "Statutory"], | |
[1, "External"], | |
[2, "Videos"], | |
[3, "Yippy"], | |
[4, "Management"], | |
[5, "oops"] | |
]; | |
// Draw the graph. | |
graph = Flotr.draw(container, [ s1, s2 ], { | |
radar : { show : true}, | |
grid : { circular : true, minorHorizontalLines : true}, | |
yaxis : { min : 0, max : 10, minorTickFreq : 2}, | |
xaxis : { ticks : ticks} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-stacked', | |
name : 'Basic Stacked', | |
callback : basic_stacked, | |
type : 'test' | |
}); | |
function basic_stacked (container) { | |
var | |
d1 = [[0, 3], [4, 8], [8, 2], [9, 3]], // First data series | |
d2 = [[0, 2], [4, 3], [8, 8], [9, 4]], // Second data series | |
i, graph; | |
// Draw Graph | |
graph = Flotr.draw(container, [ d1, d2 ], { | |
lines: { | |
show : true, | |
stacked: true | |
}, | |
xaxis: { | |
minorTickFreq: 4 | |
}, | |
grid: { | |
minorVerticalLines: true | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-stepped', | |
name : 'Basic Stepped', | |
callback : basic_stepped, | |
type : 'test' | |
}); | |
function basic_stepped (container) { | |
var | |
d1 = [[0, 3], [4, 8], [8, 5], [9, 13]], // First data series | |
d2 = [], // Second data series | |
i, graph; | |
// Generate first data set | |
for (i = 0; i < 14; i += 0.5) { | |
d2.push([i, Math.sin(i)]); | |
} | |
// Draw Graph | |
graph = Flotr.draw(container, [ d1, d2 ], { | |
lines: { | |
steps : true, | |
show : true | |
}, | |
xaxis: { | |
minorTickFreq: 4 | |
}, | |
yaxis: { | |
autoscale: true | |
}, | |
grid: { | |
minorVerticalLines: true | |
}, | |
mouse : { | |
track : true, | |
relative : true | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-time', | |
name : 'Basic Time', | |
callback : basic_time, | |
description : "<p>Select an area of the graph to zoom. Click to reset the chart.</p>" | |
}); | |
function basic_time (container) { | |
var | |
d1 = [], | |
start = new Date("2009/01/01 01:00").getTime(), | |
options, | |
graph, | |
i, x, o; | |
for (i = 0; i < 100; i++) { | |
x = start+(i*1000*3600*24*36.5); | |
d1.push([x, i+Math.random()*30+Math.sin(i/20+Math.random()*2)*20+Math.sin(i/10+Math.random())*10]); | |
} | |
options = { | |
xaxis : { | |
mode : 'time', | |
labelsAngle : 45 | |
}, | |
selection : { | |
mode : 'x' | |
}, | |
HtmlText : false, | |
title : 'Time' | |
}; | |
// Draw graph with default options, overwriting with passed options | |
function drawGraph (opts) { | |
// Clone the options, so the 'options' variable always keeps intact. | |
o = Flotr._.extend(Flotr._.clone(options), opts || {}); | |
// Return a new graph. | |
return Flotr.draw( | |
container, | |
[ d1 ], | |
o | |
); | |
} | |
graph = drawGraph(); | |
Flotr.EventAdapter.observe(container, 'flotr:select', function(area){ | |
// Draw selected area | |
graph = drawGraph({ | |
xaxis : { min : area.x1, max : area.x2, mode : 'time', labelsAngle : 45 }, | |
yaxis : { min : area.y1, max : area.y2 } | |
}); | |
}); | |
// When graph is clicked, draw the graph with default area. | |
Flotr.EventAdapter.observe(container, 'flotr:click', function () { graph = drawGraph(); }); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic-timeline', | |
name : 'Basic Timeline', | |
callback : basic_timeline | |
}); | |
function basic_timeline (container) { | |
var | |
d1 = [[1, 4, 5]], | |
d2 = [[3.2, 3, 4]], | |
d3 = [[1.9, 2, 2], [5, 2, 3.3]], | |
d4 = [[1.55, 1, 9]], | |
d5 = [[5, 0, 2.3]], | |
data = [], | |
timeline = { show : true, barWidth : .5 }, | |
markers = [], | |
labels = ['Obama', 'Bush', 'Clinton', 'Palin', 'McCain'], | |
i, graph, point; | |
// Timeline | |
Flotr._.each([d1, d2, d3, d4, d5], function (d) { | |
data.push({ | |
data : d, | |
timeline : Flotr._.clone(timeline) | |
}); | |
}); | |
// Markers | |
Flotr._.each([d1, d2, d3, d4, d5], function (d) { | |
point = d[0]; | |
markers.push([point[0], point[1]]); | |
}); | |
data.push({ | |
data: markers, | |
markers: { | |
show: true, | |
position: 'rm', | |
fontSize: 11, | |
labelFormatter : function (o) { return labels[o.index]; } | |
} | |
}); | |
// Draw Graph | |
graph = Flotr.draw(container, data, { | |
xaxis: { | |
noTicks: 3, | |
tickFormatter: function (x) { | |
var | |
x = parseInt(x), | |
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; | |
return months[(x-1)%12]; | |
} | |
}, | |
yaxis: { | |
showLabels : false | |
}, | |
grid: { | |
horizontalLines : false | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'basic', | |
name : 'Basic', | |
callback : basic | |
}); | |
function basic (container) { | |
var | |
d1 = [[0, 3], [4, 8], [8, 5], [9, 13]], // First data series | |
d2 = [], // Second data series | |
i, graph; | |
// Generate first data set | |
for (i = 0; i < 14; i += 0.5) { | |
d2.push([i, Math.sin(i)]); | |
} | |
// Draw Graph | |
graph = Flotr.draw(container, [ d1, d2 ], { | |
xaxis: { | |
minorTickFreq: 4 | |
}, | |
grid: { | |
minorVerticalLines: true | |
} | |
}); | |
} | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'click-example', | |
name : 'Click Example', | |
callback : click_example | |
}); | |
function click_example (container) { | |
var | |
d1 = [[0,0]], // Point at origin | |
options, | |
graph; | |
options = { | |
xaxis: {min: 0, max: 15}, | |
yaxis: {min: 0, max: 15}, | |
lines: {show: true}, | |
points: {show: true}, | |
mouse: {track:true}, | |
title: 'Click Example' | |
}; | |
graph = Flotr.draw(container, [d1], options); | |
// Add a point to the series and redraw the graph | |
Flotr.EventAdapter.observe(container, 'flotr:click', function(position){ | |
// Add a point to the series at the location of the click | |
d1.push([position.x, position.y]); | |
// Sort the series. | |
d1 = d1.sort(function (a, b) { return a[0] - b[0]; }); | |
// Redraw the graph, with the new series. | |
graph = Flotr.draw(container, [d1], options); | |
}); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'color-gradients', | |
name : 'Color Gradients', | |
callback : color_gradients | |
}); | |
function color_gradients (container) { | |
var | |
bars = { | |
data: [], | |
bars: { | |
show: true, | |
barWidth: 0.8, | |
lineWidth: 0, | |
fillColor: { | |
colors: ['#CB4B4B', '#fff'], | |
start: 'top', | |
end: 'bottom' | |
}, | |
fillOpacity: 0.8 | |
} | |
}, markers = { | |
data: [], | |
markers: { | |
show: true, | |
position: 'ct' | |
} | |
}, lines = { | |
data: [], | |
lines: { | |
show: true, | |
fillColor: ['#00A8F0', '#fff'], | |
fill: true, | |
fillOpacity: 1 | |
} | |
}, | |
point, | |
graph, | |
i; | |
for (i = 0; i < 8; i++) { | |
point = [i, Math.ceil(Math.random() * 10)]; | |
bars.data.push(point); | |
markers.data.push(point); | |
} | |
for (i = -1; i < 9; i += 0.01){ | |
lines.data.push([i, i*i/8+2]); | |
} | |
graph = Flotr.draw( | |
container, | |
[lines, bars, markers], { | |
yaxis: { | |
min: 0, | |
max: 11 | |
}, | |
xaxis: { | |
min: -0.5, | |
max: 7.5 | |
}, | |
grid: { | |
verticalLines: false, | |
backgroundColor: ['#fff', '#ccc'] | |
} | |
} | |
); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'download-data', | |
name : 'Download Data', | |
callback : download_data | |
}); | |
function download_data (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
d4 = [], | |
d5 = [], | |
graph, | |
i,x; | |
for (i = 0; i <= 100; i += 1) { | |
x = i / 10; | |
d1.push([x, 4 + Math.pow(x,1.5)]); | |
d2.push([x, Math.pow(x,3)]); | |
d3.push([x, i*5+3*Math.sin(x*4)]); | |
d4.push([x, x]); | |
if(x%1 === 0 ){ | |
d5.push([x, 2*x]); | |
} | |
} | |
// Draw the graph. | |
graph = Flotr.draw( | |
container, [ | |
{ data : d1, label : 'y = 4 + x^(1.5)', lines : { fill : true } }, | |
{ data : d2, label : 'y = x^3' }, | |
{ data : d3, label : 'y = 5x + 3sin(4x)' }, | |
{ data : d4, label : 'y = x' }, | |
{ data : d5, label : 'y = 2x', lines : { show : true }, points : { show : true } } | |
],{ | |
xaxis : { | |
noTicks : 7, | |
tickFormatter : function (n) { return '('+n+')'; }, | |
min: 1, // Part of the series is not displayed. | |
max: 7.5 | |
}, | |
yaxis : { | |
ticks : [[ 0, "Lower"], 10, 20, 30, [40, "Upper"]], | |
max : 40 | |
}, | |
grid : { | |
verticalLines : false, | |
backgroundColor : 'white' | |
}, | |
legend : { | |
position : 'nw' | |
}, | |
spreadsheet : { | |
show : true, | |
tickFormatter : function (e) { return e+''; } | |
} | |
}); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'download-image', | |
name : 'Download Image', | |
callback : download_image, | |
description : '' + | |
'<form name="image-download" id="image-download" action="" onsubmit="return false">' + | |
'<label><input type="radio" name="format" value="png" checked="checked" /> PNG</label>' + | |
'<label><input type="radio" name="format" value="jpeg" /> JPEG</label>' + | |
'<button name="to-image" onclick="CurrentExample(\'to-image\')">To Image</button>' + | |
'<button name="download" onclick="CurrentExample(\'download\')">Download</button>' + | |
'<button name="reset" onclick="CurrentExample(\'reset\')">Reset</button>' + | |
'</form>' | |
}); | |
function download_image (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
d4 = [], | |
d5 = [], | |
graph, | |
i; | |
for (i = 0; i <= 10; i += 0.1) { | |
d1.push([i, 4 + Math.pow(i,1.5)]); | |
d2.push([i, Math.pow(i,3)]); | |
d3.push([i, i*5+3*Math.sin(i*4)]); | |
d4.push([i, i]); | |
if( i.toFixed(1)%1 == 0 ){ | |
d5.push([i, 2*i]); | |
} | |
} | |
// Draw the graph | |
graph = Flotr.draw( | |
container,[ | |
{data:d1, label:'y = 4 + x^(1.5)', lines:{fill:true}}, | |
{data:d2, label:'y = x^3', yaxis:2}, | |
{data:d3, label:'y = 5x + 3sin(4x)'}, | |
{data:d4, label:'y = x'}, | |
{data:d5, label:'y = 2x', lines: {show: true}, points: {show: true}} | |
],{ | |
title: 'Download Image Example', | |
subtitle: 'You can save me as an image', | |
xaxis:{ | |
noTicks: 7, // Display 7 ticks. | |
tickFormatter: function(n){ return '('+n+')'; }, // => displays tick values between brackets. | |
min: 1, // => part of the series is not displayed. | |
max: 7.5, // => part of the series is not displayed. | |
labelsAngle: 45, | |
title: 'x Axis' | |
}, | |
yaxis:{ | |
ticks: [[0, "Lower"], 10, 20, 30, [40, "Upper"]], | |
max: 40, | |
title: 'y = f(x)' | |
}, | |
y2axis:{color:'#FF0000', max: 500, title: 'y = x^3'}, | |
grid:{ | |
verticalLines: false, | |
backgroundColor: 'white' | |
}, | |
HtmlText: false, | |
legend: { | |
position: 'nw' | |
} | |
}); | |
this.CurrentExample = function (operation) { | |
var | |
format = $('#image-download input:radio[name=format]:checked').val(); | |
if (Flotr.isIE && Flotr.isIE < 9) { | |
alert( | |
"Your browser doesn't allow you to get a bitmap image from the plot, " + | |
"you can only get a VML image that you can use in Microsoft Office.<br />" | |
); | |
} | |
if (operation == 'to-image') { | |
graph.download.saveImage(format, null, null, true) | |
} else if (operation == 'download') { | |
graph.download.saveImage(format); | |
} else if (operation == 'reset') { | |
graph.download.restoreCanvas(); | |
} | |
}; | |
return graph; | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'mouse-drag', | |
name : 'Mouse Drag', | |
callback : mouse_drag | |
}); | |
function mouse_drag (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
options, | |
graph, | |
start, | |
i; | |
for (i = -40; i < 40; i += 0.5) { | |
d1.push([i, Math.sin(i)+3*Math.cos(i)]); | |
d2.push([i, Math.pow(1.1, i)]); | |
d3.push([i, 40 - i+Math.random()*10]); | |
} | |
options = { | |
xaxis: {min: 0, max: 20}, | |
title : 'Mouse Drag' | |
}; | |
// Draw graph with default options, overwriting with passed options | |
function drawGraph (opts) { | |
// Clone the options, so the 'options' variable always keeps intact. | |
var o = Flotr._.extend(Flotr._.clone(options), opts || {}); | |
// Return a new graph. | |
return Flotr.draw( | |
container, | |
[ d1, d2, d3 ], | |
o | |
); | |
} | |
graph = drawGraph(); | |
function initializeDrag (e) { | |
start = graph.getEventPosition(e); | |
Flotr.EventAdapter.observe(document, 'mousemove', move); | |
Flotr.EventAdapter.observe(document, 'mouseup', stopDrag); | |
} | |
function move (e) { | |
var | |
end = graph.getEventPosition(e), | |
xaxis = graph.axes.x, | |
offset = start.x - end.x; | |
graph = drawGraph({ | |
xaxis : { | |
min : xaxis.min + offset, | |
max : xaxis.max + offset | |
} | |
}); | |
// @todo: refector initEvents in order not to remove other observed events | |
Flotr.EventAdapter.observe(graph.overlay, 'mousedown', initializeDrag); | |
} | |
function stopDrag () { | |
Flotr.EventAdapter.stopObserving(document, 'mousemove', move); | |
} | |
Flotr.EventAdapter.observe(graph.overlay, 'mousedown', initializeDrag); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'mouse-tracking', | |
name : 'Mouse Tracking', | |
callback : mouse_tracking | |
}); | |
function mouse_tracking (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
graph, i; | |
for (i = 0; i < 20; i += 0.5) { | |
d1.push([i, 2*i]); | |
d2.push([i, i*1.5+1.5*Math.sin(i)]); | |
d3.push([i, 3*Math.cos(i)+10]); | |
} | |
graph = Flotr.draw( | |
container, | |
[ | |
{ | |
data : d1, | |
mouse : { track : false } // Disable mouse tracking for d1 | |
}, | |
d2, | |
d3 | |
], | |
{ | |
mouse : { | |
track : true, // Enable mouse tracking | |
lineColor : 'purple', | |
relative : true, | |
position : 'ne', | |
sensibility : 1, | |
trackDecimals : 2, | |
trackFormatter : function (o) { return 'x = ' + o.x +', y = ' + o.y; } | |
}, | |
crosshair : { | |
mode : 'xy' | |
} | |
} | |
); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'mouse-zoom', | |
name : 'Mouse Zoom', | |
callback : mouse_zoom, | |
description : "<p>Select an area of the graph to zoom. Click to reset the chart.</p>" | |
}); | |
function mouse_zoom (container) { | |
var | |
d1 = [], | |
d2 = [], | |
d3 = [], | |
options, | |
graph, | |
i; | |
for (i = 0; i < 40; i += 0.5) { | |
d1.push([i, Math.sin(i)+3*Math.cos(i)]); | |
d2.push([i, Math.pow(1.1, i)]); | |
d3.push([i, 40 - i+Math.random()*10]); | |
} | |
options = { | |
selection : { mode : 'x', fps : 30 }, | |
title : 'Mouse Zoom' | |
}; | |
// Draw graph with default options, overwriting with passed options | |
function drawGraph (opts) { | |
// Clone the options, so the 'options' variable always keeps intact. | |
var o = Flotr._.extend(Flotr._.clone(options), opts || {}); | |
// Return a new graph. | |
return Flotr.draw( | |
container, | |
[ d1, d2, d3 ], | |
o | |
); | |
} | |
// Actually draw the graph. | |
graph = drawGraph(); | |
// Hook into the 'flotr:select' event. | |
Flotr.EventAdapter.observe(container, 'flotr:select', function (area) { | |
// Draw graph with new area | |
graph = drawGraph({ | |
xaxis: {min:area.x1, max:area.x2}, | |
yaxis: {min:area.y1, max:area.y2} | |
}); | |
}); | |
// When graph is clicked, draw the graph with default area. | |
Flotr.EventAdapter.observe(container, 'flotr:click', function () { drawGraph(); }); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'negative-values', | |
name : 'Negative Values', | |
callback : negative_values | |
}); | |
function negative_values (container) { | |
var | |
d0 = [], // Line through y = 0 | |
d1 = [], // Random data presented as a scatter plot. | |
d2 = [], // A regression line for the scatter. | |
sx = 0, | |
sy = 0, | |
sxy = 0, | |
sxsq = 0, | |
xmean, | |
ymean, | |
alpha, | |
beta, | |
n, x, y; | |
for (n = 0; n < 20; n++){ | |
x = n; | |
y = x + Math.random()*8 - 15; | |
d0.push([x, 0]); | |
d1.push([x, y]); | |
// Computations used for regression line | |
sx += x; | |
sy += y; | |
sxy += x*y; | |
sxsq += Math.pow(x,2); | |
} | |
xmean = sx/n; | |
ymean = sy/n; | |
beta = ((n*sxy) - (sx*sy))/((n*sxsq)-(Math.pow(sx,2))); | |
alpha = ymean - (beta * xmean); | |
// Compute the regression line. | |
for (n = 0; n < 20; n++){ | |
d2.push([n, alpha + beta*n]) | |
} | |
// Draw the graph | |
graph = Flotr.draw( | |
container, [ | |
{ data : d0, shadowSize : 0, color : '#545454' }, // Horizontal | |
{ data : d1, label : 'y = x + (Math.random() * 8) - 15', points : { show : true } }, // Scatter | |
{ data : d2, label : 'y = ' + alpha.toFixed(2) + ' + ' + beta.toFixed(2) + '*x' } // Regression | |
], | |
{ | |
legend : { position : 'se', backgroundColor : '#D2E8FF' }, | |
title : 'Negative Values' | |
} | |
); | |
}; | |
})(); | |
(function () { | |
Flotr.ExampleList.add({ | |
key : 'profile-bars', | |
name : 'Profile Bars', | |
type : 'profile', | |
callback : profile_bars | |
}); | |
/* | |
Flotr.ExampleList.add({ | |
key : 'basic-bars-horizontal', | |
name : 'Horizontal Bars', | |
args : [true], | |
callback : basic_bars | |
}); | |
*/ | |
function profile_bars (container, horizontal) { | |
var | |
horizontal = (horizontal ? true : false), // Show horizontal bars | |
d1 = [], // First data series | |
d2 = [], // Second data series | |
point, // Data point variable declaration | |
i; | |
for (i = 0; i < 5000; i++) { | |
if (horizontal) { | |
point = [Math.ceil(Math.random()*10), i]; | |
} else { | |
point = [i, Math.ceil(Math.random()*10)]; | |
} | |
d1.push(point); | |
if (horizontal) { | |
point = [Math.ceil(Math.random()*10), i+0.5]; | |
} else { | |
point = [i+0.5, Math.ceil(Math.random()*10)]; | |
} | |
d2.push(point); | |
}; | |
// Draw the graph | |
Flotr.draw( | |
container, | |
[d1, d2], | |
{ | |
bars : { | |
show : true, | |
horizontal : horizontal, | |
barWidth : 0.5 | |
}, | |
mouse : { | |
track : true, | |
relative : true | |
}, | |
yaxis : { | |
min : 0, | |
autoscaleMargin : 1 | |
} | |
} | |
); | |
} | |
})(); | |
yepnope([ | |
// Libs | |
'../lib/bean-min.js', | |
'../lib/underscore-min.js', | |
{ | |
test : (navigator.appVersion.indexOf("MSIE") != -1 && parseFloat(navigator.appVersion.split("MSIE")[1]) < 9), | |
// Load for IE < 9 | |
yep : [ | |
'../lib/excanvas.js', | |
'../lib/base64.js', | |
'../lib/canvastext.js' | |
] | |
}, | |
'lib/codemirror/lib/codemirror.js', | |
'lib/codemirror/mode/javascript/javascript.js', | |
'lib/beautify.js', | |
'lib/randomseed.js', | |
'lib/jquery-1.7.1.min.js', | |
'lib/jquery.ba-hashchange.min.js', | |
// Flotr | |
'../js/Flotr.js', | |
'../js/DefaultOptions.js', | |
'../js/Color.js', | |
'../js/Date.js', | |
'../js/DOM.js', | |
'../js/EventAdapter.js', | |
'../js/Text.js', | |
'../js/Graph.js', | |
'../js/Axis.js', | |
'../js/Series.js', | |
'../js/types/lines.js', | |
'../js/types/bars.js', | |
'../js/types/points.js', | |
'../js/types/pie.js', | |
'../js/types/candles.js', | |
'../js/types/markers.js', | |
'../js/types/radar.js', | |
'../js/types/bubbles.js', | |
'../js/types/gantt.js', | |
'../js/types/timeline.js', | |
'../js/plugins/download.js', | |
'../js/plugins/selection.js', | |
'../js/plugins/spreadsheet.js', | |
'../js/plugins/grid.js', | |
'../js/plugins/hit.js', | |
'../js/plugins/crosshair.js', | |
'../js/plugins/labels.js', | |
'../js/plugins/legend.js', | |
'../js/plugins/titles.js', | |
// Examples | |
'js/Examples.js', | |
'js/ExampleList.js', | |
'js/Example.js', | |
'js/Editor.js', | |
'js/Profile.js', | |
'js/examples/basic.js', | |
'js/examples/basic-axis.js', | |
'js/examples/basic-bars.js', | |
'js/examples/basic-bars-stacked.js', | |
'js/examples/basic-pie.js', | |
'js/examples/basic-radar.js', | |
'js/examples/basic-bubble.js', | |
'js/examples/basic-candle.js', | |
'js/examples/basic-legend.js', | |
'js/examples/mouse-tracking.js', | |
'js/examples/mouse-zoom.js', | |
'js/examples/mouse-drag.js', | |
'js/examples/basic-time.js', | |
'js/examples/negative-values.js', | |
'js/examples/click-example.js', | |
'js/examples/download-image.js', | |
'js/examples/download-data.js', | |
'js/examples/advanced-titles.js', | |
'js/examples/color-gradients.js', | |
'js/examples/profile-bars.js', | |
'js/examples/basic-timeline.js', | |
'js/examples/advanced-markers.js', | |
{ complete : function () { | |
if (Flotr.ExamplesCallback) { | |
Flotr.ExamplesCallback(); | |
} else { | |
Examples = new Flotr.Examples({ | |
node : document.getElementById('examples') | |
}); | |
} | |
} | |
} | |
]); | |
yepnope([ | |
{ | |
test : (navigator.appVersion.indexOf("MSIE") != -1 && parseFloat(navigator.appVersion.split("MSIE")[1]) < 9), | |
// Load for IE < 9 | |
yep : [ | |
'../flotr2.ie.min.js' | |
] | |
}, | |
'../flotr2.min.js', | |
'lib/codemirror/lib/codemirror.js', | |
'lib/codemirror/mode/javascript/javascript.js', | |
'lib/beautify.js', | |
'lib/randomseed.js', | |
'lib/jquery-1.7.1.min.js', | |
'lib/jquery.ba-hashchange.min.js', | |
// Examples | |
'../flotr2.examples.min.js', | |
'../flotr2.examples.types.js', | |
{ complete : function () { | |
if (Flotr.ExamplesCallback) { | |
Flotr.ExamplesCallback(); | |
} else { | |
Examples = new Flotr.Examples({ | |
node : document.getElementById('examples') | |
}); | |
} | |
} | |
} | |
]); | |
/*jslint onevar: false, plusplus: false */ | |
/* | |
JS Beautifier | |
--------------- | |
Written by Einar Lielmanis, <einar@jsbeautifier.org> | |
http://jsbeautifier.org/ | |
Originally converted to javascript by Vital, <vital76@gmail.com> | |
"End braces on own line" added by Chris J. Shull, <chrisjshull@gmail.com> | |
You are free to use this in any way you want, in case you find this useful or working for you. | |
Usage: | |
js_beautify(js_source_text); | |
js_beautify(js_source_text, options); | |
The options are: | |
indent_size (default 4) — indentation size, | |
indent_char (default space) — character to indent with, | |
preserve_newlines (default true) — whether existing line breaks should be preserved, | |
preserve_max_newlines (default unlimited) - maximum number of line breaks to be preserved in one chunk, | |
jslint_happy (default false) — if true, then jslint-stricter mode is enforced. | |
jslint_happy !jslint_happy | |
--------------------------------- | |
function () function() | |
brace_style (default "collapse") - "collapse" | "expand" | "end-expand" | |
put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line. | |
e.g | |
js_beautify(js_source_text, { | |
'indent_size': 1, | |
'indent_char': '\t' | |
}); | |
*/ | |
function js_beautify(js_source_text, options) { | |
var input, output, token_text, last_type, last_text, last_last_text, last_word, flags, flag_store, indent_string; | |
var whitespace, wordchar, punct, parser_pos, line_starters, digits; | |
var prefix, token_type, do_block_just_closed; | |
var wanted_newline, just_added_newline, n_newlines; | |
var preindent_string = ''; | |
// Some interpreters have unexpected results with foo = baz || bar; | |
options = options ? options : {}; | |
var opt_brace_style; | |
// compatibility | |
if (options.space_after_anon_function !== undefined && options.jslint_happy === undefined) { | |
options.jslint_happy = options.space_after_anon_function; | |
} | |
if (options.braces_on_own_line !== undefined) { //graceful handling of depricated option | |
opt_brace_style = options.braces_on_own_line ? "expand" : "collapse"; | |
} | |
opt_brace_style = options.brace_style ? options.brace_style : (opt_brace_style ? opt_brace_style : "collapse"); | |
var opt_indent_size = options.indent_size ? options.indent_size : 4; | |
var opt_indent_char = options.indent_char ? options.indent_char : ' '; | |
var opt_preserve_newlines = typeof options.preserve_newlines === 'undefined' ? true : options.preserve_newlines; | |
var opt_max_preserve_newlines = typeof options.max_preserve_newlines === 'undefined' ? false : options.max_preserve_newlines; | |
var opt_jslint_happy = options.jslint_happy === 'undefined' ? false : options.jslint_happy; | |
var opt_keep_array_indentation = typeof options.keep_array_indentation === 'undefined' ? false : options.keep_array_indentation; | |
just_added_newline = false; | |
// cache the source's length. | |
var input_length = js_source_text.length; | |
function trim_output(eat_newlines) { | |
eat_newlines = typeof eat_newlines === 'undefined' ? false : eat_newlines; | |
while (output.length && (output[output.length - 1] === ' ' | |
|| output[output.length - 1] === indent_string | |
|| output[output.length - 1] === preindent_string | |
|| (eat_newlines && (output[output.length - 1] === '\n' || output[output.length - 1] === '\r')))) { | |
output.pop(); | |
} | |
} | |
function trim(s) { | |
return s.replace(/^\s\s*|\s\s*$/, ''); | |
} | |
function force_newline() | |
{ | |
var old_keep_array_indentation = opt_keep_array_indentation; | |
opt_keep_array_indentation = false; | |
print_newline() | |
opt_keep_array_indentation = old_keep_array_indentation; | |
} | |
function print_newline(ignore_repeated) { | |
flags.eat_next_space = false; | |
if (opt_keep_array_indentation && is_array(flags.mode)) { | |
return; | |
} | |
ignore_repeated = typeof ignore_repeated === 'undefined' ? true : ignore_repeated; | |
flags.if_line = false; | |
trim_output(); | |
if (!output.length) { | |
return; // no newline on start of file | |
} | |
if (output[output.length - 1] !== "\n" || !ignore_repeated) { | |
just_added_newline = true; | |
output.push("\n"); | |
} | |
if (preindent_string) { | |
output.push(preindent_string); | |
} | |
for (var i = 0; i < flags.indentation_level; i += 1) { | |
output.push(indent_string); | |
} | |
if (flags.var_line && flags.var_line_reindented) { | |
output.push(indent_string); // skip space-stuffing, if indenting with a tab | |
} | |
} | |
function print_single_space() { | |
if (flags.eat_next_space) { | |
flags.eat_next_space = false; | |
return; | |
} | |
var last_output = ' '; | |
if (output.length) { | |
last_output = output[output.length - 1]; | |
} | |
if (last_output !== ' ' && last_output !== '\n' && last_output !== indent_string) { // prevent occassional duplicate space | |
output.push(' '); | |
} | |
} | |
function print_token() { | |
just_added_newline = false; | |
flags.eat_next_space = false; | |
output.push(token_text); | |
} | |
function indent() { | |
flags.indentation_level += 1; | |
} | |
function remove_indent() { | |
if (output.length && output[output.length - 1] === indent_string) { | |
output.pop(); | |
} | |
} | |
function set_mode(mode) { | |
if (flags) { | |
flag_store.push(flags); | |
} | |
flags = { | |
previous_mode: flags ? flags.mode : 'BLOCK', | |
mode: mode, | |
var_line: false, | |
var_line_tainted: false, | |
var_line_reindented: false, | |
in_html_comment: false, | |
if_line: false, | |
in_case: false, | |
eat_next_space: false, | |
indentation_baseline: -1, | |
indentation_level: (flags ? flags.indentation_level + ((flags.var_line && flags.var_line_reindented) ? 1 : 0) : 0), | |
ternary_depth: 0 | |
}; | |
} | |
function is_array(mode) { | |
return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]'; | |
} | |
function is_expression(mode) { | |
return mode === '[EXPRESSION]' || mode === '[INDENTED-EXPRESSION]' || mode === '(EXPRESSION)'; | |
} | |
function restore_mode() { | |
do_block_just_closed = flags.mode === 'DO_BLOCK'; | |
if (flag_store.length > 0) { | |
flags = flag_store.pop(); | |
} | |
} | |
function all_lines_start_with(lines, c) { | |
for (var i = 0; i < lines.length; i++) { | |
if (trim(lines[i])[0] != c) { | |
return false; | |
} | |
} | |
return true; | |
} | |
function in_array(what, arr) { | |
for (var i = 0; i < arr.length; i += 1) { | |
if (arr[i] === what) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function get_next_token() { | |
n_newlines = 0; | |
if (parser_pos >= input_length) { | |
return ['', 'TK_EOF']; | |
} | |
wanted_newline = false; | |
var c = input.charAt(parser_pos); | |
parser_pos += 1; | |
var keep_whitespace = opt_keep_array_indentation && is_array(flags.mode); | |
if (keep_whitespace) { | |
// | |
// slight mess to allow nice preservation of array indentation and reindent that correctly | |
// first time when we get to the arrays: | |
// var a = [ | |
// ....'something' | |
// we make note of whitespace_count = 4 into flags.indentation_baseline | |
// so we know that 4 whitespaces in original source match indent_level of reindented source | |
// | |
// and afterwards, when we get to | |
// 'something, | |
// .......'something else' | |
// we know that this should be indented to indent_level + (7 - indentation_baseline) spaces | |
// | |
var whitespace_count = 0; | |
while (in_array(c, whitespace)) { | |
if (c === "\n") { | |
trim_output(); | |
output.push("\n"); | |
just_added_newline = true; | |
whitespace_count = 0; | |
} else { | |
if (c === '\t') { | |
whitespace_count += 4; | |
} else if (c === '\r') { | |
// nothing | |
} else { | |
whitespace_count += 1; | |
} | |
} | |
if (parser_pos >= input_length) { | |
return ['', 'TK_EOF']; | |
} | |
c = input.charAt(parser_pos); | |
parser_pos += 1; | |
} | |
if (flags.indentation_baseline === -1) { | |
flags.indentation_baseline = whitespace_count; | |
} | |
if (just_added_newline) { | |
var i; | |
for (i = 0; i < flags.indentation_level + 1; i += 1) { | |
output.push(indent_string); | |
} | |
if (flags.indentation_baseline !== -1) { | |
for (i = 0; i < whitespace_count - flags.indentation_baseline; i++) { | |
output.push(' '); | |
} | |
} | |
} | |
} else { | |
while (in_array(c, whitespace)) { | |
if (c === "\n") { | |
n_newlines += ( (opt_max_preserve_newlines) ? (n_newlines <= opt_max_preserve_newlines) ? 1: 0: 1 ); | |
} | |
if (parser_pos >= input_length) { | |
return ['', 'TK_EOF']; | |
} | |
c = input.charAt(parser_pos); | |
parser_pos += 1; | |
} | |
if (opt_preserve_newlines) { | |
if (n_newlines > 1) { | |
for (i = 0; i < n_newlines; i += 1) { | |
print_newline(i === 0); | |
just_added_newline = true; | |
} | |
} | |
} | |
wanted_newline = n_newlines > 0; | |
} | |
if (in_array(c, wordchar)) { | |
if (parser_pos < input_length) { | |
while (in_array(input.charAt(parser_pos), wordchar)) { | |
c += input.charAt(parser_pos); | |
parser_pos += 1; | |
if (parser_pos === input_length) { | |
break; | |
} | |
} | |
} | |
// small and surprisingly unugly hack for 1E-10 representation | |
if (parser_pos !== input_length && c.match(/^[0-9]+[Ee]$/) && (input.charAt(parser_pos) === '-' || input.charAt(parser_pos) === '+')) { | |
var sign = input.charAt(parser_pos); | |
parser_pos += 1; | |
var t = get_next_token(parser_pos); | |
c += sign + t[0]; | |
return [c, 'TK_WORD']; | |
} | |
if (c === 'in') { // hack for 'in' operator | |
return [c, 'TK_OPERATOR']; | |
} | |
if (wanted_newline && last_type !== 'TK_OPERATOR' | |
&& last_type !== 'TK_EQUALS' | |
&& !flags.if_line && (opt_preserve_newlines || last_text !== 'var')) { | |
print_newline(); | |
} | |
return [c, 'TK_WORD']; | |
} | |
if (c === '(' || c === '[') { | |
return [c, 'TK_START_EXPR']; | |
} | |
if (c === ')' || c === ']') { | |
return [c, 'TK_END_EXPR']; | |
} | |
if (c === '{') { | |
return [c, 'TK_START_BLOCK']; | |
} | |
if (c === '}') { | |
return [c, 'TK_END_BLOCK']; | |
} | |
if (c === ';') { | |
return [c, 'TK_SEMICOLON']; | |
} | |
if (c === '/') { | |
var comment = ''; | |
// peek for comment /* ... */ | |
var inline_comment = true; | |
if (input.charAt(parser_pos) === '*') { | |
parser_pos += 1; | |
if (parser_pos < input_length) { | |
while (! (input.charAt(parser_pos) === '*' && input.charAt(parser_pos + 1) && input.charAt(parser_pos + 1) === '/') && parser_pos < input_length) { | |
c = input.charAt(parser_pos); | |
comment += c; | |
if (c === '\x0d' || c === '\x0a') { | |
inline_comment = false; | |
} | |
parser_pos += 1; | |
if (parser_pos >= input_length) { | |
break; | |
} | |
} | |
} | |
parser_pos += 2; | |
if (inline_comment) { | |
return ['/*' + comment + '*/', 'TK_INLINE_COMMENT']; | |
} else { | |
return ['/*' + comment + '*/', 'TK_BLOCK_COMMENT']; | |
} | |
} | |
// peek for comment // ... | |
if (input.charAt(parser_pos) === '/') { | |
comment = c; | |
while (input.charAt(parser_pos) !== '\r' && input.charAt(parser_pos) !== '\n') { | |
comment += input.charAt(parser_pos); | |
parser_pos += 1; | |
if (parser_pos >= input_length) { | |
break; | |
} | |
} | |
parser_pos += 1; | |
if (wanted_newline) { | |
print_newline(); | |
} | |
return [comment, 'TK_COMMENT']; | |
} | |
} | |
if (c === "'" || // string | |
c === '"' || // string | |
(c === '/' && | |
((last_type === 'TK_WORD' && in_array(last_text, ['return', 'do'])) || | |
(last_type === 'TK_COMMENT' || last_type === 'TK_START_EXPR' || last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_OPERATOR' || last_type === 'TK_EQUALS' || last_type === 'TK_EOF' || last_type === 'TK_SEMICOLON')))) { // regexp | |
var sep = c; | |
var esc = false; | |
var resulting_string = c; | |
if (parser_pos < input_length) { | |
if (sep === '/') { | |
// | |
// handle regexp separately... | |
// | |
var in_char_class = false; | |
while (esc || in_char_class || input.charAt(parser_pos) !== sep) { | |
resulting_string += input.charAt(parser_pos); | |
if (!esc) { | |
esc = input.charAt(parser_pos) === '\\'; | |
if (input.charAt(parser_pos) === '[') { | |
in_char_class = true; | |
} else if (input.charAt(parser_pos) === ']') { | |
in_char_class = false; | |
} | |
} else { | |
esc = false; | |
} | |
parser_pos += 1; | |
if (parser_pos >= input_length) { | |
// incomplete string/rexp when end-of-file reached. | |
// bail out with what had been received so far. | |
return [resulting_string, 'TK_STRING']; | |
} | |
} | |
} else { | |
// | |
// and handle string also separately | |
// | |
while (esc || input.charAt(parser_pos) !== sep) { | |
resulting_string += input.charAt(parser_pos); | |
if (!esc) { | |
esc = input.charAt(parser_pos) === '\\'; | |
} else { | |
esc = false; | |
} | |
parser_pos += 1; | |
if (parser_pos >= input_length) { | |
// incomplete string/rexp when end-of-file reached. | |
// bail out with what had been received so far. | |
return [resulting_string, 'TK_STRING']; | |
} | |
} | |
} | |
} | |
parser_pos += 1; | |
resulting_string += sep; | |
if (sep === '/') { | |
// regexps may have modifiers /regexp/MOD , so fetch those, too | |
while (parser_pos < input_length && in_array(input.charAt(parser_pos), wordchar)) { | |
resulting_string += input.charAt(parser_pos); | |
parser_pos += 1; | |
} | |
} | |
return [resulting_string, 'TK_STRING']; | |
} | |
if (c === '#') { | |
if (output.length === 0 && input.charAt(parser_pos) === '!') { | |
// shebang | |
resulting_string = c; | |
while (parser_pos < input_length && c != '\n') { | |
c = input.charAt(parser_pos); | |
resulting_string += c; | |
parser_pos += 1; | |
} | |
output.push(trim(resulting_string) + '\n'); | |
print_newline(); | |
return get_next_token(); | |
} | |
// Spidermonkey-specific sharp variables for circular references | |
// https://developer.mozilla.org/En/Sharp_variables_in_JavaScript | |
// http://mxr.mozilla.org/mozilla-central/source/js/src/jsscan.cpp around line 1935 | |
var sharp = '#'; | |
if (parser_pos < input_length && in_array(input.charAt(parser_pos), digits)) { | |
do { | |
c = input.charAt(parser_pos); | |
sharp += c; | |
parser_pos += 1; | |
} while (parser_pos < input_length && c !== '#' && c !== '='); | |
if (c === '#') { | |
// | |
} else if (input.charAt(parser_pos) === '[' && input.charAt(parser_pos + 1) === ']') { | |
sharp += '[]'; | |
parser_pos += 2; | |
} else if (input.charAt(parser_pos) === '{' && input.charAt(parser_pos + 1) === '}') { | |
sharp += '{}'; | |
parser_pos += 2; | |
} | |
return [sharp, 'TK_WORD']; | |
} | |
} | |
if (c === '<' && input.substring(parser_pos - 1, parser_pos + 3) === '<!--') { | |
parser_pos += 3; | |
flags.in_html_comment = true; | |
return ['<!--', 'TK_COMMENT']; | |
} | |
if (c === '-' && flags.in_html_comment && input.substring(parser_pos - 1, parser_pos + 2) === '-->') { | |
flags.in_html_comment = false; | |
parser_pos += 2; | |
if (wanted_newline) { | |
print_newline(); | |
} | |
return ['-->', 'TK_COMMENT']; | |
} | |
if (in_array(c, punct)) { | |
while (parser_pos < input_length && in_array(c + input.charAt(parser_pos), punct)) { | |
c += input.charAt(parser_pos); | |
parser_pos += 1; | |
if (parser_pos >= input_length) { | |
break; | |
} | |
} | |
if (c === '=') { | |
return [c, 'TK_EQUALS']; | |
} else { | |
return [c, 'TK_OPERATOR']; | |
} | |
} | |
return [c, 'TK_UNKNOWN']; | |
} | |
//---------------------------------- | |
indent_string = ''; | |
while (opt_indent_size > 0) { | |
indent_string += opt_indent_char; | |
opt_indent_size -= 1; | |
} | |
while (js_source_text && (js_source_text[0] === ' ' || js_source_text[0] === '\t')) { | |
preindent_string += js_source_text[0]; | |
js_source_text = js_source_text.substring(1); | |
} | |
input = js_source_text; | |
last_word = ''; // last 'TK_WORD' passed | |
last_type = 'TK_START_EXPR'; // last token type | |
last_text = ''; // last token text | |
last_last_text = ''; // pre-last token text | |
output = []; | |
do_block_just_closed = false; | |
whitespace = "\n\r\t ".split(''); | |
wordchar = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$'.split(''); | |
digits = '0123456789'.split(''); | |
punct = '+ - * / % & ++ -- = += -= *= /= %= == === != !== > < >= <= >> << >>> >>>= >>= <<= && &= | || ! !! , : ? ^ ^= |= ::'.split(' '); | |
// words which should always start on new line. | |
line_starters = 'continue,try,throw,return,var,if,switch,case,default,for,while,break,function'.split(','); | |
// states showing if we are currently in expression (i.e. "if" case) - 'EXPRESSION', or in usual block (like, procedure), 'BLOCK'. | |
// some formatting depends on that. | |
flag_store = []; | |
set_mode('BLOCK'); | |
parser_pos = 0; | |
while (true) { | |
var t = get_next_token(parser_pos); | |
token_text = t[0]; | |
token_type = t[1]; | |
if (token_type === 'TK_EOF') { | |
break; | |
} | |
switch (token_type) { | |
case 'TK_START_EXPR': | |
if (token_text === '[') { | |
if (last_type === 'TK_WORD' || last_text === ')') { | |
// this is array index specifier, break immediately | |
// a[x], fn()[x] | |
if (in_array(last_text, line_starters)) { | |
print_single_space(); | |
} | |
set_mode('(EXPRESSION)'); | |
print_token(); | |
break; | |
} | |
if (flags.mode === '[EXPRESSION]' || flags.mode === '[INDENTED-EXPRESSION]') { | |
if (last_last_text === ']' && last_text === ',') { | |
// ], [ goes to new line | |
if (flags.mode === '[EXPRESSION]') { | |
flags.mode = '[INDENTED-EXPRESSION]'; | |
if (!opt_keep_array_indentation) { | |
indent(); | |
} | |
} | |
set_mode('[EXPRESSION]'); | |
if (!opt_keep_array_indentation) { | |
print_newline(); | |
} | |
} else if (last_text === '[') { | |
if (flags.mode === '[EXPRESSION]') { | |
flags.mode = '[INDENTED-EXPRESSION]'; | |
if (!opt_keep_array_indentation) { | |
indent(); | |
} | |
} | |
set_mode('[EXPRESSION]'); | |
if (!opt_keep_array_indentation) { | |
print_newline(); | |
} | |
} else { | |
set_mode('[EXPRESSION]'); | |
} | |
} else { | |
set_mode('[EXPRESSION]'); | |
} | |
} else { | |
set_mode('(EXPRESSION)'); | |
} | |
if (last_text === ';' || last_type === 'TK_START_BLOCK') { | |
print_newline(); | |
} else if (last_type === 'TK_END_EXPR' || last_type === 'TK_START_EXPR' || last_type === 'TK_END_BLOCK' || last_text === '.') { | |
// do nothing on (( and )( and ][ and ]( and .( | |
} else if (last_type !== 'TK_WORD' && last_type !== 'TK_OPERATOR') { | |
print_single_space(); | |
} else if (last_word === 'function' || last_word === 'typeof') { | |
// function() vs function () | |
if (opt_jslint_happy) { | |
print_single_space(); | |
} | |
} else if (in_array(last_text, line_starters) || last_text === 'catch') { | |
print_single_space(); | |
} | |
print_token(); | |
break; | |
case 'TK_END_EXPR': | |
if (token_text === ']') { | |
if (opt_keep_array_indentation) { | |
if (last_text === '}') { | |
// trim_output(); | |
// print_newline(true); | |
remove_indent(); | |
print_token(); | |
restore_mode(); | |
break; | |
} | |
} else { | |
if (flags.mode === '[INDENTED-EXPRESSION]') { | |
if (last_text === ']') { | |
restore_mode(); | |
print_newline(); | |
print_token(); | |
break; | |
} | |
} | |
} | |
} | |
restore_mode(); | |
print_token(); | |
break; | |
case 'TK_START_BLOCK': | |
if (last_word === 'do') { | |
set_mode('DO_BLOCK'); | |
} else { | |
set_mode('BLOCK'); | |
} | |
if (opt_brace_style=="expand") { | |
if (last_type !== 'TK_OPERATOR') { | |
if (last_text === 'return' || last_text === '=') { | |
print_single_space(); | |
} else { | |
print_newline(true); | |
} | |
} | |
print_token(); | |
indent(); | |
} else { | |
if (last_type !== 'TK_OPERATOR' && last_type !== 'TK_START_EXPR') { | |
if (last_type === 'TK_START_BLOCK') { | |
print_newline(); | |
} else { | |
print_single_space(); | |
} | |
} else { | |
// if TK_OPERATOR or TK_START_EXPR | |
if (is_array(flags.previous_mode) && last_text === ',') { | |
if (last_last_text === '}') { | |
// }, { in array context | |
print_single_space(); | |
} else { | |
print_newline(); // [a, b, c, { | |
} | |
} | |
} | |
indent(); | |
print_token(); | |
} | |
break; | |
case 'TK_END_BLOCK': | |
restore_mode(); | |
if (opt_brace_style=="expand") { | |
if (last_text !== '{') { | |
print_newline(); | |
} | |
print_token(); | |
} else { | |
if (last_type === 'TK_START_BLOCK') { | |
// nothing | |
if (just_added_newline) { | |
remove_indent(); | |
} else { | |
// {} | |
trim_output(); | |
} | |
} else { | |
if (is_array(flags.mode) && opt_keep_array_indentation) { | |
// we REALLY need a newline here, but newliner would skip that | |
opt_keep_array_indentation = false; | |
print_newline(); | |
opt_keep_array_indentation = true; | |
} else { | |
print_newline(); | |
} | |
} | |
print_token(); | |
} | |
break; | |
case 'TK_WORD': | |
// no, it's not you. even I have problems understanding how this works | |
// and what does what. | |
if (do_block_just_closed) { | |
// do {} ## while () | |
print_single_space(); | |
print_token(); | |
print_single_space(); | |
do_block_just_closed = false; | |
break; | |
} | |
if (token_text === 'function') { | |
if (flags.var_line) { | |
flags.var_line_reindented = true; | |
} | |
if ((just_added_newline || last_text === ';') && last_text !== '{') { | |
// make sure there is a nice clean space of at least one blank line | |
// before a new function definition | |
n_newlines = just_added_newline ? n_newlines : 0; | |
if ( ! opt_preserve_newlines) { | |
n_newlines = 1; | |
} | |
for (var i = 0; i < 2 - n_newlines; i++) { | |
print_newline(false); | |
} | |
} | |
} | |
if (token_text === 'case' || token_text === 'default') { | |
if (last_text === ':') { | |
// switch cases following one another | |
remove_indent(); | |
} else { | |
// case statement starts in the same line where switch | |
flags.indentation_level--; | |
print_newline(); | |
flags.indentation_level++; | |
} | |
print_token(); | |
flags.in_case = true; | |
break; | |
} | |
prefix = 'NONE'; | |
if (last_type === 'TK_END_BLOCK') { | |
if (!in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { | |
prefix = 'NEWLINE'; | |
} else { | |
if (opt_brace_style=="expand" || opt_brace_style=="end-expand") { | |
prefix = 'NEWLINE'; | |
} else { | |
prefix = 'SPACE'; | |
print_single_space(); | |
} | |
} | |
} else if (last_type === 'TK_SEMICOLON' && (flags.mode === 'BLOCK' || flags.mode === 'DO_BLOCK')) { | |
prefix = 'NEWLINE'; | |
} else if (last_type === 'TK_SEMICOLON' && is_expression(flags.mode)) { | |
prefix = 'SPACE'; | |
} else if (last_type === 'TK_STRING') { | |
prefix = 'NEWLINE'; | |
} else if (last_type === 'TK_WORD') { | |
if (last_text === 'else') { | |
// eat newlines between ...else *** some_op... | |
// won't preserve extra newlines in this place (if any), but don't care that much | |
trim_output(true); | |
} | |
prefix = 'SPACE'; | |
} else if (last_type === 'TK_START_BLOCK') { | |
prefix = 'NEWLINE'; | |
} else if (last_type === 'TK_END_EXPR') { | |
print_single_space(); | |
prefix = 'NEWLINE'; | |
} | |
if (in_array(token_text, line_starters) && last_text !== ')') { | |
if (last_text == 'else') { | |
prefix = 'SPACE'; | |
} else { | |
prefix = 'NEWLINE'; | |
} | |
} | |
if (flags.if_line && last_type === 'TK_END_EXPR') { | |
flags.if_line = false; | |
} | |
if (in_array(token_text.toLowerCase(), ['else', 'catch', 'finally'])) { | |
if (last_type !== 'TK_END_BLOCK' || opt_brace_style=="expand" || opt_brace_style=="end-expand") { | |
print_newline(); | |
} else { | |
trim_output(true); | |
print_single_space(); | |
} | |
} else if (prefix === 'NEWLINE') { | |
if ((last_type === 'TK_START_EXPR' || last_text === '=' || last_text === ',') && token_text === 'function') { | |
// no need to force newline on 'function': (function | |
// DONOTHING | |
} else if (token_text === 'function' && last_text == 'new') { | |
print_single_space(); | |
} else if (last_text === 'return' || last_text === 'throw') { | |
// no newline between 'return nnn' | |
print_single_space(); | |
} else if (last_type !== 'TK_END_EXPR') { | |
if ((last_type !== 'TK_START_EXPR' || token_text !== 'var') && last_text !== ':') { | |
// no need to force newline on 'var': for (var x = 0...) | |
if (token_text === 'if' && last_word === 'else' && last_text !== '{') { | |
// no newline for } else if { | |
print_single_space(); | |
} else { | |
flags.var_line = false; | |
flags.var_line_reindented = false; | |
print_newline(); | |
} | |
} | |
} else if (in_array(token_text, line_starters) && last_text != ')') { | |
flags.var_line = false; | |
flags.var_line_reindented = false; | |
print_newline(); | |
} | |
} else if (is_array(flags.mode) && last_text === ',' && last_last_text === '}') { | |
print_newline(); // }, in lists get a newline treatment | |
} else if (prefix === 'SPACE') { | |
print_single_space(); | |
} | |
print_token(); | |
last_word = token_text; | |
if (token_text === 'var') { | |
flags.var_line = true; | |
flags.var_line_reindented = false; | |
flags.var_line_tainted = false; | |
} | |
if (token_text === 'if') { | |
flags.if_line = true; | |
} | |
if (token_text === 'else') { | |
flags.if_line = false; | |
} | |
break; | |
case 'TK_SEMICOLON': | |
print_token(); | |
flags.var_line = false; | |
flags.var_line_reindented = false; | |
if (flags.mode == 'OBJECT') { | |
// OBJECT mode is weird and doesn't get reset too well. | |
flags.mode = 'BLOCK'; | |
} | |
break; | |
case 'TK_STRING': | |
if (last_type === 'TK_START_BLOCK' || last_type === 'TK_END_BLOCK' || last_type === 'TK_SEMICOLON') { | |
print_newline(); | |
} else if (last_type === 'TK_WORD') { | |
print_single_space(); | |
} | |
print_token(); | |
break; | |
case 'TK_EQUALS': | |
if (flags.var_line) { | |
// just got an '=' in a var-line, different formatting/line-breaking, etc will now be done | |
flags.var_line_tainted = true; | |
} | |
print_single_space(); | |
print_token(); | |
print_single_space(); | |
break; | |
case 'TK_OPERATOR': | |
var space_before = true; | |
var space_after = true; | |
if (flags.var_line && token_text === ',' && (is_expression(flags.mode))) { | |
// do not break on comma, for(var a = 1, b = 2) | |
flags.var_line_tainted = false; | |
} | |
if (flags.var_line) { | |
if (token_text === ',') { | |
if (flags.var_line_tainted) { | |
print_token(); | |
flags.var_line_reindented = true; | |
flags.var_line_tainted = false; | |
print_newline(); | |
break; | |
} else { | |
flags.var_line_tainted = false; | |
} | |
// } else if (token_text === ':') { | |
// hmm, when does this happen? tests don't catch this | |
// flags.var_line = false; | |
} | |
} | |
if (last_text === 'return' || last_text === 'throw') { | |
// "return" had a special handling in TK_WORD. Now we need to return the favor | |
print_single_space(); | |
print_token(); | |
break; | |
} | |
if (token_text === ':' && flags.in_case) { | |
print_token(); // colon really asks for separate treatment | |
print_newline(); | |
flags.in_case = false; | |
break; | |
} | |
if (token_text === '::') { | |
// no spaces around exotic namespacing syntax operator | |
print_token(); | |
break; | |
} | |
if (token_text === ',') { | |
if (flags.var_line) { | |
if (flags.var_line_tainted) { | |
print_token(); | |
print_newline(); | |
flags.var_line_tainted = false; | |
} else { | |
print_token(); | |
print_single_space(); | |
} | |
} else if (last_type === 'TK_END_BLOCK' && flags.mode !== "(EXPRESSION)") { | |
print_token(); | |
if (flags.mode === 'OBJECT' && last_text === '}') { | |
print_newline(); | |
} else { | |
print_single_space(); | |
} | |
} else { | |
if (flags.mode === 'OBJECT') { | |
print_token(); | |
print_newline(); | |
} else { | |
// EXPR or DO_BLOCK | |
print_token(); | |
print_single_space(); | |
} | |
} | |
break; | |
// } else if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS']) || in_array(last_text, line_starters) || in_array(last_text, ['==', '!=', '+=', '-=', '*=', '/=', '+', '-'])))) { | |
} else if (in_array(token_text, ['--', '++', '!']) || (in_array(token_text, ['-', '+']) && (in_array(last_type, ['TK_START_BLOCK', 'TK_START_EXPR', 'TK_EQUALS', 'TK_OPERATOR']) || in_array(last_text, line_starters)))) { | |
// unary operators (and binary +/- pretending to be unary) special cases | |
space_before = false; | |
space_after = false; | |
if (last_text === ';' && is_expression(flags.mode)) { | |
// for (;; ++i) | |
// ^^^ | |
space_before = true; | |
} | |
if (last_type === 'TK_WORD' && in_array(last_text, line_starters)) { | |
space_before = true; | |
} | |
if (flags.mode === 'BLOCK' && (last_text === '{' || last_text === ';')) { | |
// { foo; --i } | |
// foo(); --bar; | |
print_newline(); | |
} | |
} else if (token_text === '.') { | |
// decimal digits or object.property | |
space_before = false; | |
} else if (token_text === ':') { | |
if (flags.ternary_depth == 0) { | |
flags.mode = 'OBJECT'; | |
space_before = false; | |
} else { | |
flags.ternary_depth -= 1; | |
} | |
} else if (token_text === '?') { | |
flags.ternary_depth += 1; | |
} | |
if (space_before) { | |
print_single_space(); | |
} | |
print_token(); | |
if (space_after) { | |
print_single_space(); | |
} | |
if (token_text === '!') { | |
// flags.eat_next_space = true; | |
} | |
break; | |
case 'TK_BLOCK_COMMENT': | |
var lines = token_text.split(/\x0a|\x0d\x0a/); | |
if (all_lines_start_with(lines.slice(1), '*')) { | |
// javadoc: reformat and reindent | |
print_newline(); | |
output.push(lines[0]); | |
for (i = 1; i < lines.length; i++) { | |
print_newline(); | |
output.push(' '); | |
output.push(trim(lines[i])); | |
} | |
} else { | |
// simple block comment: leave intact | |
if (lines.length > 1) { | |
// multiline comment block starts with a new line | |
print_newline(); | |
trim_output(); | |
} else { | |
// single-line /* comment */ stays where it is | |
print_single_space(); | |
} | |
for (i = 0; i < lines.length; i++) { | |
output.push(lines[i]); | |
output.push('\n'); | |
} | |
} | |
print_newline(); | |
break; | |
case 'TK_INLINE_COMMENT': | |
print_single_space(); | |
print_token(); | |
if (is_expression(flags.mode)) { | |
print_single_space(); | |
} else { | |
force_newline(); | |
} | |
break; | |
case 'TK_COMMENT': | |
// print_newline(); | |
if (wanted_newline) { | |
print_newline(); | |
} else { | |
print_single_space(); | |
} | |
print_token(); | |
force_newline(); | |
break; | |
case 'TK_UNKNOWN': | |
if (last_text === 'return' || last_text === 'throw') { | |
print_single_space(); | |
} | |
print_token(); | |
break; | |
} | |
last_last_text = last_text; | |
last_type = token_type; | |
last_text = token_text; | |
} | |
var sweet_code = preindent_string + output.join('').replace(/[\n ]+$/, ''); | |
return sweet_code; | |
} | |
// Add support for CommonJS. Just put this file somewhere on your require.paths | |
// and you will be able to `var js_beautify = require("beautify").js_beautify`. | |
if (typeof exports !== "undefined") | |
exports.js_beautify = js_beautify; | |
Copyright (C) 2011 by Marijn Haverbeke <marijnh@gmail.com> | |
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. | |
# CodeMirror 2 | |
CodeMirror 2 is a rewrite of [CodeMirror | |
1](http://github.com/marijnh/CodeMirror). The docs live | |
[here](http://codemirror.net/doc/manual.html), and the project page is | |
[http://codemirror.net/](http://codemirror.net/). | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Active Line Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/xml/xml.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} | |
.activeline {background: #f0fcff !important;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Active Line Demo</h1> | |
<form><textarea id="code" name="code"> | |
<?xml version="1.0" encoding="UTF-8"?> | |
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" | |
xmlns:georss="http://www.georss.org/georss" | |
xmlns:twitter="http://api.twitter.com"> | |
<channel> | |
<title>Twitter / codemirror</title> | |
<link>http://twitter.com/codemirror</link> | |
<atom:link type="application/rss+xml" | |
href="http://twitter.com/statuses/user_timeline/242283288.rss" rel="self"/> | |
<description>Twitter updates from CodeMirror / codemirror.</description> | |
<language>en-us</language> | |
<ttl>40</ttl> | |
<item> | |
<title>codemirror: http://cloud-ide.com — they're springing up like mushrooms. This one | |
uses CodeMirror as its editor.</title> | |
<description>codemirror: http://cloud-ide.com — they're springing up like mushrooms. This | |
one uses CodeMirror as its editor.</description> | |
<pubDate>Thu, 17 Mar 2011 23:34:47 +0000</pubDate> | |
<guid>http://twitter.com/codemirror/statuses/48527733722058752</guid> | |
<link>http://twitter.com/codemirror/statuses/48527733722058752</link> | |
<twitter:source>web</twitter:source> | |
<twitter:place/> | |
</item> | |
<item> | |
<title>codemirror: Posted a description of the CodeMirror 2 internals at | |
http://codemirror.net/2/internals.html</title> | |
<description>codemirror: Posted a description of the CodeMirror 2 internals at | |
http://codemirror.net/2/internals.html</description> | |
<pubDate>Wed, 02 Mar 2011 12:15:09 +0000</pubDate> | |
<guid>http://twitter.com/codemirror/statuses/42920879788789760</guid> | |
<link>http://twitter.com/codemirror/statuses/42920879788789760</link> | |
<twitter:source>web</twitter:source> | |
<twitter:place/> | |
</item> | |
</channel> | |
</rss></textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
mode: "application/xml", | |
lineNumbers: true, | |
onCursorActivity: function() { | |
editor.setLineClass(hlLine, null); | |
hlLine = editor.setLineClass(editor.getCursor().line, "activeline"); | |
} | |
}); | |
var hlLine = editor.setLineClass(0, "activeline"); | |
</script> | |
<p>Styling the current cursor line.</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Mode-Changing Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/javascript/javascript.js"></script> | |
<script src="../mode/scheme/scheme.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border: 1px solid black;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Mode-Changing demo</h1> | |
<form><textarea id="code" name="code"> | |
;; If there is Scheme code in here, the editor will be in Scheme mode. | |
;; If you put in JS instead, it'll switch to JS mode. | |
(define (double x) | |
(* x x)) | |
</textarea></form> | |
<p>On changes to the content of the above editor, a (crude) script | |
tries to auto-detect the language used, and switches the editor to | |
either JavaScript or Scheme mode based on that.</p> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
mode: "scheme", | |
lineNumbers: true, | |
matchBrackets: true, | |
tabMode: "indent", | |
onChange: function() { | |
clearTimeout(pending); | |
setTimeout(update, 400); | |
} | |
}); | |
var pending; | |
function looksLikeScheme(code) { | |
return !/^\s*\(\s*function\b/.test(code) && /^\s*[;\(]/.test(code); | |
} | |
function update() { | |
editor.setOption("mode", looksLikeScheme(editor.getValue()) ? "scheme" : "javascript"); | |
} | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Autocomplete Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../lib/util/simple-hint.js"></script> | |
<link rel="stylesheet" href="../lib/util/simple-hint.css"> | |
<script src="../lib/util/javascript-hint.js"></script> | |
<script src="../mode/javascript/javascript.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css">.CodeMirror {border: 1px solid #eee;}</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Autocomplete demo</h1> | |
<form><textarea id="code" name="code"> | |
function getCompletions(token, context) { | |
var found = [], start = token.string; | |
function maybeAdd(str) { | |
if (str.indexOf(start) == 0) found.push(str); | |
} | |
function gatherCompletions(obj) { | |
if (typeof obj == "string") forEach(stringProps, maybeAdd); | |
else if (obj instanceof Array) forEach(arrayProps, maybeAdd); | |
else if (obj instanceof Function) forEach(funcProps, maybeAdd); | |
for (var name in obj) maybeAdd(name); | |
} | |
if (context) { | |
// If this is a property, see if it belongs to some object we can | |
// find in the current environment. | |
var obj = context.pop(), base; | |
if (obj.className == "js-variable") | |
base = window[obj.string]; | |
else if (obj.className == "js-string") | |
base = ""; | |
else if (obj.className == "js-atom") | |
base = 1; | |
while (base != null && context.length) | |
base = base[context.pop().string]; | |
if (base != null) gatherCompletions(base); | |
} | |
else { | |
// If not, just look in the window object and any local scope | |
// (reading into JS mode internals to get at the local variables) | |
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); | |
gatherCompletions(window); | |
forEach(keywords, maybeAdd); | |
} | |
return found; | |
} | |
</textarea></form> | |
<p>Press <strong>ctrl-space</strong> to activate autocompletion. See | |
the code (<a href="../lib/util/simple-hint.js">here</a> | |
and <a href="../lib/util/javascript-hint.js">here</a>) to figure out | |
how it works.</p> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
extraKeys: {"Ctrl-Space": function(cm) {CodeMirror.simpleHint(cm, CodeMirror.javascriptHint);}} | |
}); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Emacs bindings demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/clike/clike.js"></script> | |
<script src="../keymap/emacs.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Emacs bindings demo</h1> | |
<form><textarea id="code" name="code"> | |
#include "syscalls.h" | |
/* getchar: simple buffered version */ | |
int getchar(void) | |
{ | |
static char buf[BUFSIZ]; | |
static char *bufp = buf; | |
static int n = 0; | |
if (n == 0) { /* buffer is empty */ | |
n = read(0, buf, sizeof buf); | |
bufp = buf; | |
} | |
return (--n >= 0) ? (unsigned char) *bufp++ : EOF; | |
} | |
</textarea></form> | |
<p>The emacs keybindings are enabled by | |
including <a href="../keymap/emacs.js">keymap/emacs.js</a> and setting | |
the <code>keyMap</code> option to <code>"emacs"</code>. Because | |
CodeMirror's internal API is quite different from Emacs, they are only | |
a loose approximation of actual emacs bindings, though.</p> | |
<p>Also note that a lot of browsers disallow certain keys from being | |
captured. For example, Chrome blocks both Ctrl-W and Ctrl-N, with the | |
result that idiomatic use of Emacs keys will constantly close your tab | |
or open a new window.</p> | |
<script> | |
CodeMirror.commands.save = function() { | |
var elt = editor.getWrapperElement(); | |
elt.style.background = "#def"; | |
setTimeout(function() { elt.style.background = ""; }, 300); | |
}; | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
mode: "text/x-csrc", | |
keyMap: "emacs" | |
}); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Code Folding Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../lib/util/foldcode.js"></script> | |
<script src="../mode/javascript/javascript.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} | |
.CodeMirror-gutter {min-width: 2.6em; cursor: pointer;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Code Folding Demo</h1> | |
<form><div style="max-width: 50em"><textarea id="code" name="code"></textarea></div></form> | |
<script id="script"> | |
window.onload = function() { | |
var te = document.getElementById("code"); | |
var sc = document.getElementById("script"); | |
te.value = (sc.textContent || sc.innerText || sc.innerHTML).replace(/^\s*/, ""); | |
var foldFunc = CodeMirror.newFoldFunction(CodeMirror.braceRangeFinder); | |
function keyEvent(cm, e) { | |
if (e.keyCode == 81 && e.ctrlKey) { | |
if (e.type == "keydown") { | |
e.stop(); | |
setTimeout(function() {foldFunc(cm, cm.getCursor().line);}, 50); | |
} | |
return true; | |
} | |
} | |
window.editor = CodeMirror.fromTextArea(te, { | |
mode: "javascript", | |
lineNumbers: true, | |
lineWrapping: true, | |
onGutterClick: foldFunc, | |
extraKeys: {"Ctrl-Q": function(cm){foldFunc(cm, cm.getCursor().line);}} | |
}); | |
foldFunc(editor, 6); | |
foldFunc(editor, 16); | |
}; | |
</script> | |
<p>Demonstration of code folding using the code | |
in <a href="../lib/util/foldcode.js"><code>foldcode.js</code></a>. | |
Press ctrl-q or click on the gutter to fold a block, again | |
to unfold.</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Formatting Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../lib/util/formatting.js"></script> | |
<script src="../mode/css/css.js"></script> | |
<script src="../mode/xml/xml.js"></script> | |
<script src="../mode/javascript/javascript.js"></script> | |
<script src="../mode/htmlmixed/htmlmixed.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror { | |
border: 1px solid #eee; | |
} | |
td { | |
padding-right: 20px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Formatting demo</h1> | |
<form><textarea id="code" name="code"><script> function (s,e){ for(var i=0; i < 1; i++) test("test();a=1");} </script> | |
<script> | |
function test(c){ for (var i = 0; i < 10; i++){ process("a.b();c = null;", 300);} | |
} | |
</script> | |
<table><tr><td>test 1</td></tr><tr><td>test 2</td></tr></table> | |
<script> function test() { return 1;} </script> | |
<style> .test { font-size: medium; font-family: monospace; } | |
</style></textarea></form> | |
<p>Select a piece of code and click one of the links below to apply automatic formatting to the selected text or comment/uncomment the selected text. Note that the formatting behavior depends on the current block's mode. | |
<table> | |
<tr> | |
<td> | |
<a href="javascript:autoFormatSelection()"> | |
Autoformat Selected | |
</a> | |
</td> | |
<td> | |
<a href="javascript:commentSelection(true)"> | |
Comment Selected | |
</a> | |
</td> | |
<td> | |
<a href="javascript:commentSelection(false)"> | |
Uncomment Selected | |
</a> | |
</td> | |
</tr> | |
</table> | |
</p> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
mode: "htmlmixed" | |
}); | |
CodeMirror.commands["selectAll"](editor); | |
function getSelectedRange() { | |
return { from: editor.getCursor(true), to: editor.getCursor(false) }; | |
} | |
function autoFormatSelection() { | |
var range = getSelectedRange(); | |
editor.autoFormatRange(range.from, range.to); | |
} | |
function commentSelection(isComment) { | |
var range = getSelectedRange(); | |
editor.commentRange(isComment, range.from, range.to); | |
} | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Full Screen Editing</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<link rel="stylesheet" href="../theme/night.css"> | |
<script src="../mode/xml/xml.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} | |
.fullscreen { | |
display: block; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
z-index: 9999; | |
margin: 0; | |
padding: 0; | |
border: 0px solid #BBBBBB; | |
opacity: 1; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Full Screen Editing</h1> | |
<form><textarea id="code" name="code" rows="5"> | |
<dt id="option_indentWithTabs"><code>indentWithTabs (boolean)</code></dt> | |
<dd>Whether, when indenting, the first N*8 spaces should be | |
replaced by N tabs. Default is false.</dd> | |
<dt id="option_tabMode"><code>tabMode (string)</code></dt> | |
<dd>Determines what happens when the user presses the tab key. | |
Must be one of the following: | |
<dl> | |
<dt><code>"classic" (the default)</code></dt> | |
<dd>When nothing is selected, insert a tab. Otherwise, | |
behave like the <code>"shift"</code> mode. (When shift is | |
held, this behaves like the <code>"indent"</code> mode.)</dd> | |
<dt><code>"shift"</code></dt> | |
<dd>Indent all selected lines by | |
one <a href="#option_indentUnit"><code>indentUnit</code></a>. | |
If shift was held while pressing tab, un-indent all selected | |
lines one unit.</dd> | |
<dt><code>"indent"</code></dt> | |
<dd>Indent the line the 'correctly', based on its syntactic | |
context. Only works if the | |
mode <a href="#indent">supports</a> it.</dd> | |
<dt><code>"default"</code></dt> | |
<dd>Do not capture tab presses, let the browser apply its | |
default behaviour (which usually means it skips to the next | |
control).</dd> | |
</dl></dd> | |
<dt id="option_enterMode"><code>enterMode (string)</code></dt> | |
<dd>Determines whether and how new lines are indented when the | |
enter key is pressed. The following modes are supported: | |
<dl> | |
<dt><code>"indent" (the default)</code></dt> | |
<dd>Use the mode's indentation rules to give the new line | |
the correct indentation.</dd> | |
<dt><code>"keep"</code></dt> | |
<dd>Indent the line the same as the previous line.</dd> | |
<dt><code>"flat"</code></dt> | |
<dd>Do not indent the new line.</dd> | |
</dl></dd> | |
<dt id="option_enterMode"><code>enterMode (string)</code></dt> | |
<dd>Determines whether and how new lines are indented when the | |
enter key is pressed. The following modes are supported: | |
<dl> | |
<dt><code>"indent" (the default)</code></dt> | |
<dd>Use the mode's indentation rules to give the new line | |
the correct indentation.</dd> | |
<dt><code>"keep"</code></dt> | |
<dd>Indent the line the same as the previous line.</dd> | |
<dt><code>"flat"</code></dt> | |
<dd>Do not indent the new line.</dd> | |
</dl></dd> | |
<dt id="option_enterMode"><code>enterMode (string)</code></dt> | |
<dd>Determines whether and how new lines are indented when the | |
enter key is pressed. The following modes are supported: | |
<dl> | |
<dt><code>"indent" (the default)</code></dt> | |
<dd>Use the mode's indentation rules to give the new line | |
the correct indentation.</dd> | |
<dt><code>"keep"</code></dt> | |
<dd>Indent the line the same as the previous line.</dd> | |
<dt><code>"flat"</code></dt> | |
<dd>Do not indent the new line.</dd> | |
</dl></dd> | |
<dt id="option_enterMode"><code>enterMode (string)</code></dt> | |
<dd>Determines whether and how new lines are indented when the | |
enter key is pressed. The following modes are supported: | |
<dl> | |
<dt><code>"indent" (the default)</code></dt> | |
<dd>Use the mode's indentation rules to give the new line | |
the correct indentation.</dd> | |
<dt><code>"keep"</code></dt> | |
<dd>Indent the line the same as the previous line.</dd> | |
<dt><code>"flat"</code></dt> | |
<dd>Do not indent the new line.</dd> | |
</dl></dd> | |
</textarea></form> | |
<script> | |
(function () { | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
theme: "night", | |
extraKeys: {"F11": toggleFullscreenEditing, "Esc": toggleFullscreenEditing} | |
}); | |
function toggleFullscreenEditing() | |
{ | |
var editorDiv = $('.CodeMirror-scroll'); | |
if (!editorDiv.hasClass('fullscreen')) { | |
toggleFullscreenEditing.beforeFullscreen = { height: editorDiv.height(), width: editorDiv.width() } | |
editorDiv.addClass('fullscreen'); | |
editorDiv.height('100%'); | |
editorDiv.width('100%'); | |
editor.refresh(); | |
} | |
else { | |
editorDiv.removeClass('fullscreen'); | |
editorDiv.height(toggleFullscreenEditing.beforeFullscreen.height); | |
editorDiv.width(toggleFullscreenEditing.beforeFullscreen.width); | |
editor.refresh(); | |
} | |
} | |
})(); | |
</script> | |
<p>Press <strong>F11</strong> (or <strong>ESC</strong> in Safari on Mac OS X) when cursor is in the editor to toggle full screen editing.</p> | |
<p><strong>Note:</strong> Does not currently work correctly in IE | |
6 and 7, where setting the height of something | |
to <code>100%</code> doesn't make it full-screen.</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Breakpoint Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/javascript/javascript.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror-gutter { | |
width: 3em; | |
background: white; | |
} | |
.CodeMirror { | |
border: 1px solid #aaa; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Breakpoint demo</h1> | |
<form><textarea id="code" name="code"> | |
CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
onGutterClick: function(cm, n) { | |
var info = cm.lineInfo(n); | |
if (info.markerText) | |
cm.clearMarker(n); | |
else | |
cm.setMarker(n, "<span style=\"color: #900\">●</span> %N%"); | |
} | |
}); | |
</textarea></form> | |
<p>Click the line-number gutter to add or remove 'breakpoints'.</p> | |
<script> | |
CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
onGutterClick: function(cm, n) { | |
var info = cm.lineInfo(n); | |
if (info.markerText) | |
cm.clearMarker(n); | |
else | |
cm.setMarker(n, "<span style=\"color: #900\">●</span> %N%"); | |
} | |
}); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Overlay Parser Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../lib/util/overlay.js"></script> | |
<script src="../mode/xml/xml.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border: 1px solid black;} | |
.cm-mustache {color: #0ca;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Overlay Parser Demo</h1> | |
<form><textarea id="code" name="code"> | |
<html> | |
<body> | |
<h1>{{title}}</h1> | |
<p>These are links to {{things}}:</p> | |
<ul>{{#links}} | |
<li><a href="{{url}}">{{text}}</a></li> | |
{{/links}}</ul> | |
</body> | |
</html> | |
</textarea></form> | |
<script> | |
CodeMirror.defineMode("mustache", function(config, parserConfig) { | |
var mustacheOverlay = { | |
token: function(stream, state) { | |
if (stream.match("{{")) { | |
while ((ch = stream.next()) != null) | |
if (ch == "}" && stream.next() == "}") break; | |
return "mustache"; | |
} | |
while (stream.next() != null && !stream.match("{{", false)) {} | |
return null; | |
} | |
}; | |
return CodeMirror.overlayParser(CodeMirror.getMode(config, parserConfig.backdrop || "text/html"), mustacheOverlay); | |
}); | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "mustache"}); | |
</script> | |
<p>Demonstration of a mode that parses HTML, highlighting | |
the <a href="http://mustache.github.com/">Mustache</a> templating | |
directives inside of it by using the code | |
in <a href="../lib/util/overlay.js"><code>overlay.js</code></a>. View | |
source to see the 15 lines of code needed to accomplish this.</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: HTML5 preview</title> | |
<meta charset=utf-8> | |
<script src=../lib/codemirror.js></script> | |
<script src=../mode/xml/xml.js></script> | |
<script src=../mode/javascript/javascript.js></script> | |
<script src=../mode/css/css.js></script> | |
<script src=../mode/htmlmixed/htmlmixed.js></script> | |
<link rel=stylesheet href=../lib/codemirror.css> | |
<link rel=stylesheet href=../doc/docs.css> | |
<style type=text/css> | |
.CodeMirror { | |
float: left; | |
width: 50%; | |
border: 1px solid black; | |
} | |
iframe { | |
width: 49%; | |
float: left; | |
height: 300px; | |
border: 1px solid black; | |
border-left: 0px; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: HTML5 preview</h1> | |
<textarea id=code name=code> | |
<!doctype html> | |
<html> | |
<head> | |
<meta charset=utf-8> | |
<title>HTML5 canvas demo</title> | |
<style>p {font-family: monospace;}</style> | |
</head> | |
<body> | |
<p>Canvas pane goes here:</p> | |
<canvas id=pane width=300 height=200></canvas> | |
<script> | |
var canvas = document.getElementById('pane'); | |
var context = canvas.getContext('2d'); | |
context.fillStyle = 'rgb(250,0,0)'; | |
context.fillRect(10, 10, 55, 50); | |
context.fillStyle = 'rgba(0, 0, 250, 0.5)'; | |
context.fillRect(30, 30, 55, 50); | |
</script> | |
</body> | |
</html></textarea> | |
<iframe id=preview></iframe> | |
<script> | |
var delay; | |
// Initialize CodeMirror editor with a nice html5 canvas demo. | |
var editor = CodeMirror.fromTextArea(document.getElementById('code'), { | |
mode: 'text/html', | |
tabMode: 'indent', | |
onChange: function() { | |
clearTimeout(delay); | |
delay = setTimeout(updatePreview, 300); | |
} | |
}); | |
function updatePreview() { | |
var previewFrame = document.getElementById('preview'); | |
var preview = previewFrame.contentDocument || previewFrame.contentWindow.document; | |
preview.open(); | |
preview.write(editor.getValue()); | |
preview.close(); | |
} | |
setTimeout(updatePreview, 300); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Autoresize Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/css/css.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror { | |
border: 1px solid #eee; | |
} | |
.CodeMirror-scroll { | |
height: auto; | |
overflow-y: hidden; | |
overflow-x: auto; | |
width: 100%; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Autoresize demo</h1> | |
<form><textarea id="code" name="code"> | |
.CodeMirror-scroll { | |
height: auto; | |
overflow-y: hidden; | |
overflow-x: auto; | |
width: 100% | |
}</textarea></form> | |
<p>By setting a few CSS properties, CodeMirror can be made to | |
automatically resize to fit its content.</p> | |
<script> | |
CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true | |
}); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Mode Runner Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../lib/util/runmode.js"></script> | |
<script src="../mode/xml/xml.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Mode Runner Demo</h1> | |
<textarea id="code" style="width: 90%; height: 7em; border: 1px solid black; padding: .2em .4em;"> | |
<foobar> | |
<blah>Enter your xml here and press the button below to display | |
it as highlighted by the CodeMirror XML mode</blah> | |
<tag2 foo="2" bar="&quot;bar&quot;"/> | |
</foobar></textarea><br> | |
<button onclick="doHighlight();">Highlight!</button> | |
<pre id="output" class="cm-s-default"></pre> | |
<script> | |
function doHighlight() { | |
CodeMirror.runMode(document.getElementById("code").value, "application/xml", | |
document.getElementById("output")); | |
} | |
</script> | |
<p>Running a CodeMirror mode outside of the editor. | |
The <code>CodeMirror.runMode</code> function, defined | |
in <code><a href="../lib/util/runmode.js">lib/runmode.js</a></code> takes the following arguments:</p> | |
<dl> | |
<dt><code>text (string)</code></dt> | |
<dd>The document to run through the highlighter.</dd> | |
<dt><code>mode (<a href="../doc/manual.html#option_mode">mode spec</a>)</code></dt> | |
<dd>The mode to use (must be loaded as normal).</dd> | |
<dt><code>output (function or DOM node)</code></dt> | |
<dd>If this is a function, it will be called for each token with | |
two arguments, the token's text and the token's style class (may | |
be <code>null</code> for unstyled tokens). If it is a DOM node, | |
the tokens will be converted to <code>span</code> elements as in | |
an editor, and inserted into the node | |
(through <code>innerHTML</code>).</dd> | |
</dl> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Search/Replace Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/xml/xml.js"></script> | |
<script src="../lib/util/dialog.js"></script> | |
<link rel="stylesheet" href="../lib/util/dialog.css"> | |
<script src="../lib/util/searchcursor.js"></script> | |
<script src="../lib/util/search.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;} | |
dt {font-family: monospace; color: #666;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Search/Replace Demo</h1> | |
<form><textarea id="code" name="code"> | |
<dt id="option_indentWithTabs"><code>indentWithTabs (boolean)</code></dt> | |
<dd>Whether, when indenting, the first N*8 spaces should be | |
replaced by N tabs. Default is false.</dd> | |
<dt id="option_tabMode"><code>tabMode (string)</code></dt> | |
<dd>Determines what happens when the user presses the tab key. | |
Must be one of the following: | |
<dl> | |
<dt><code>"classic" (the default)</code></dt> | |
<dd>When nothing is selected, insert a tab. Otherwise, | |
behave like the <code>"shift"</code> mode. (When shift is | |
held, this behaves like the <code>"indent"</code> mode.)</dd> | |
<dt><code>"shift"</code></dt> | |
<dd>Indent all selected lines by | |
one <a href="#option_indentUnit"><code>indentUnit</code></a>. | |
If shift was held while pressing tab, un-indent all selected | |
lines one unit.</dd> | |
<dt><code>"indent"</code></dt> | |
<dd>Indent the line the 'correctly', based on its syntactic | |
context. Only works if the | |
mode <a href="#indent">supports</a> it.</dd> | |
<dt><code>"default"</code></dt> | |
<dd>Do not capture tab presses, let the browser apply its | |
default behaviour (which usually means it skips to the next | |
control).</dd> | |
</dl></dd> | |
<dt id="option_enterMode"><code>enterMode (string)</code></dt> | |
<dd>Determines whether and how new lines are indented when the | |
enter key is pressed. The following modes are supported: | |
<dl> | |
<dt><code>"indent" (the default)</code></dt> | |
<dd>Use the mode's indentation rules to give the new line | |
the correct indentation.</dd> | |
<dt><code>"keep"</code></dt> | |
<dd>Indent the line the same as the previous line.</dd> | |
<dt><code>"flat"</code></dt> | |
<dd>Do not indent the new line.</dd> | |
</dl></dd> | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", lineNumbers: true}); | |
</script> | |
<p>Demonstration of primitive search/replace functionality. The | |
keybindings (which can be overridden by custom keymaps) are:</p> | |
<dl> | |
<dt>Ctrl-F / Cmd-F</dt><dd>Start searching</dd> | |
<dt>Ctrl-G / Cmd-G</dt><dd>Find next</dd> | |
<dt>Shift-Ctrl-G / Shift-Cmd-G</dt><dd>Find previous</dd> | |
<dt>Shift-Ctrl-F / Cmd-Option-F</dt><dd>Replace</dd> | |
<dt>Shift-Ctrl-R / Shift-Cmd-Option-F</dt><dd>Replace all</dd> | |
</dl> | |
<p>Searching is enabled by | |
including <a href="../lib/util/search.js">lib/util/search.js</a>. | |
For good-looking input dialogs, you also want to include | |
<a href="../lib/util/dialog.js">lib/util/dialog.js</a> | |
and <a href="../lib/util/dialog.css">lib/util/dialog.css</a>.</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Theme Demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<link rel="stylesheet" href="../theme/neat.css"> | |
<link rel="stylesheet" href="../theme/elegant.css"> | |
<link rel="stylesheet" href="../theme/night.css"> | |
<link rel="stylesheet" href="../theme/monokai.css"> | |
<link rel="stylesheet" href="../theme/cobalt.css"> | |
<link rel="stylesheet" href="../theme/eclipse.css"> | |
<link rel="stylesheet" href="../theme/rubyblue.css"> | |
<script src="../mode/javascript/javascript.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border: 1px solid black;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Theme demo</h1> | |
<form><textarea id="code" name="code"> | |
function findSequence(goal) { | |
function find(start, history) { | |
if (start == goal) | |
return history; | |
else if (start > goal) | |
return null; | |
else | |
return find(start + 5, "(" + history + " + 5)") || | |
find(start * 3, "(" + history + " * 3)"); | |
} | |
return find(1, "1"); | |
}</textarea></form> | |
<p>Select a theme: <select onchange="selectTheme(this)"> | |
<option selected>default</option> | |
<option>night</option> | |
<option>monokai</option> | |
<option>neat</option> | |
<option>elegant</option> | |
<option>cobalt</option> | |
<option>eclipse</option> | |
<option>rubyblue</option> | |
</select> | |
</p> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true | |
}); | |
function selectTheme(node) { | |
var theme = node.options[node.selectedIndex].innerHTML; | |
editor.setOption("theme", theme); | |
} | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Vim bindings demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/clike/clike.js"></script> | |
<script src="../keymap/vim.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Vim bindings demo</h1> | |
<form><textarea id="code" name="code"> | |
#include "syscalls.h" | |
/* getchar: simple buffered version */ | |
int getchar(void) | |
{ | |
static char buf[BUFSIZ]; | |
static char *bufp = buf; | |
static int n = 0; | |
if (n == 0) { /* buffer is empty */ | |
n = read(0, buf, sizeof buf); | |
bufp = buf; | |
} | |
return (--n >= 0) ? (unsigned char) *bufp++ : EOF; | |
} | |
</textarea></form> | |
<p>The vim keybindings are enabled by | |
including <a href="../keymap/vim.js">keymap/vim.js</a> and setting | |
the <code>keyMap</code> option to <code>"vim"</code>. Because | |
CodeMirror's internal API is quite different from Vim, they are only | |
a loose approximation of actual vim bindings, though.</p> | |
<script> | |
CodeMirror.commands.save = function(){alert("Saving");}; | |
CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
mode: "text/x-csrc", | |
keyMap: "vim" | |
}); | |
</script> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Visible tabs demo</title> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="../lib/codemirror.js"></script> | |
<script src="../mode/clike/clike.js"></script> | |
<link rel="stylesheet" href="../doc/docs.css"> | |
<style type="text/css"> | |
.CodeMirror {border-top: 1px solid #eee; border-bottom: 1px solid #eee;} | |
.cm-tab:after { | |
content: "\21e5"; | |
display: -moz-inline-block; | |
display: -webkit-inline-block; | |
display: inline-block; | |
width: 0px; | |
position: relative; | |
overflow: visible; | |
left: -1.4em; | |
color: #aaa; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Visible tabs demo</h1> | |
<form><textarea id="code" name="code"> | |
#include "syscalls.h" | |
/* getchar: simple buffered version */ | |
int getchar(void) | |
{ | |
static char buf[BUFSIZ]; | |
static char *bufp = buf; | |
static int n = 0; | |
if (n == 0) { /* buffer is empty */ | |
n = read(0, buf, sizeof buf); | |
bufp = buf; | |
} | |
return (--n >= 0) ? (unsigned char) *bufp++ : EOF; | |
} | |
</textarea></form> | |
<p>Tabs inside the editor are spans with the | |
class <code>cm-tab</code>, and can be styled. This demo uses | |
an <code>:after</code> pseudo-class CSS hack that will not work on old | |
browsers. You can use a more conservative technique like a background | |
image as an alternative.</p> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
tabSize: 4, | |
indentUnit: 4, | |
indentWithTabs: true, | |
mode: "text/x-csrc" | |
}); | |
</script> | |
</body> | |
</html> | |
Binary files /dev/null and b/js/flotr2/examples/lib/codemirror/doc/baboon.png differ
<?xml version="1.0" encoding="UTF-8" standalone="no"?> | |
<!-- Created with Inkscape (http://www.inkscape.org/) --> | |
<svg | |
xmlns:dc="http://purl.org/dc/elements/1.1/" | |
xmlns:cc="http://creativecommons.org/ns#" | |
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | |
xmlns:svg="http://www.w3.org/2000/svg" | |
xmlns="http://www.w3.org/2000/svg" | |
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | |
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | |
id="svg3181" | |
version="1.1" | |
inkscape:version="0.48.0 r9654" | |
width="1750" | |
height="960" | |
xml:space="preserve" | |
sodipodi:docname="baboon_vector.svg"><metadata | |
id="metadata3187"><rdf:RDF><cc:Work | |
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type | |
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs | |
id="defs3185"><clipPath | |
clipPathUnits="userSpaceOnUse" | |
id="clipPath3195"><path | |
d="M 0,768 1400,768 1400,0 0,0 0,768 z" | |
id="path3197" /></clipPath><clipPath | |
clipPathUnits="userSpaceOnUse" | |
id="clipPath3215"><path | |
d="M 0,768 1400,768 1400,0 0,0 0,768 z" | |
id="path3217" /></clipPath></defs><sodipodi:namedview | |
pagecolor="#ffffff" | |
bordercolor="#666666" | |
borderopacity="1" | |
objecttolerance="10" | |
gridtolerance="10" | |
guidetolerance="10" | |
inkscape:pageopacity="0" | |
inkscape:pageshadow="2" | |
inkscape:window-width="1440" | |
inkscape:window-height="851" | |
id="namedview3183" | |
showgrid="false" | |
inkscape:zoom="0.20550291" | |
inkscape:cx="1534.1667" | |
inkscape:cy="795.78156" | |
inkscape:window-x="0" | |
inkscape:window-y="0" | |
inkscape:window-maximized="1" | |
inkscape:current-layer="g3189" /><g | |
id="g3189" | |
inkscape:groupmode="layer" | |
inkscape:label="baboon_vector" | |
transform="matrix(1.25,0,0,-1.25,0,960)"><g | |
id="g3191"><g | |
id="g3193" | |
clip-path="url(#clipPath3195)"><g | |
id="g3199" | |
transform="translate(458.9561,569.9678)"><path | |
d="m 0,0 59.835,69.355 87.034,26.518 133.949,-7.479 c 0,0 74.116,-32.639 74.795,-34.678 0.68,-2.04 84.314,-59.155 84.314,-59.155 l 12.238,-74.795 5.439,-97.912 -13.598,-25.159 -4.76,-40.797 -18.358,-23.118 24.39,-5.561 0.501,-5.192 -14.012,-60.641 16.477,-93.368 7.223,-49.972 -208.295,-51.754 -18.552,4.005 -37.468,8.325 -10.036,4.036 -66.885,10.101 c 0,0 -14.959,74.793 -16.999,73.433 -2.039,-1.359 -42.836,56.437 -42.836,56.437 l -19.719,65.274 12.48,74.571 -7.961,9.643 -26.479,16.187 -12.716,38.309 4.08,48.277 8.769,38.985 L 6.608,-74.308 0,0 z" | |
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3201" /></g><g | |
id="g3203" | |
transform="translate(78.8657,682.1582)"><path | |
d="M 0,0 142.789,40.797 259.74,52.355 313.457,-232.543 204.665,-291.698 78.194,-293.738 0,0 z" | |
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3205" /></g><g | |
id="g3207" | |
transform="translate(269.5122,345.2344)"><path | |
d="M 0,0 18.801,-74.425 40.728,-85.408 59.539,-59.541 40.259,13.503 36.821,15.669 0,0 z" | |
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3209" /></g></g></g><g | |
id="g3223" | |
transform="translate(741.918,109.0332)"><path | |
d="m 0,0 -17.236,-9.401 -16.452,-22.721 -0.783,12.537 6.268,17.234 13.317,6.268 L 0,7.833 14.884,3.917 0,0 z m 172.622,-21.824 c -0.031,0.271 -0.081,0.535 -0.117,0.804 -20.85,7.653 -49.59,7.327 -66.874,10.927 -13.849,2.886 -23.047,9.119 -27.032,12.298 -9.863,-8.494 -12.025,-14.377 -12.025,-14.377 0,0 -9.816,15.309 -30.17,25.76 -7.05,3.621 -17.767,5.691 -29.341,5.691 -24.297,0 -52.384,-9.155 -58.339,-32.223 -10.458,-40.511 9.697,-76.594 49.814,-77.623 1.325,-0.034 2.623,-0.12 3.894,-0.12 36.131,0 48.855,8.572 58.323,15.478 0.027,0.021 0.104,0 0.104,0 0,0 25.126,-11.506 53.529,-11.506 4.419,0 9.156,0.415 14.249,1.063 31.641,4.018 47.989,28.124 43.985,63.828" | |
style="fill:#df0019;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3225" | |
inkscape:connector-curvature="0" /></g><g | |
id="g3227" | |
transform="translate(300.8481,270.0254)"><path | |
d="m 0,0 c -3.063,-0.691 -12.535,0.784 -12.535,0.784 l 6.267,-25.853 43.481,13.319 -9.01,27.418 C 28.203,15.668 7.867,1.777 0,0" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3229" | |
inkscape:connector-curvature="0" /></g><g | |
id="g3231" | |
transform="translate(211.66052,615.85984)"><path | |
d="m 0,0 -16.243,-2.871 -15.462,-9.4 4.323,-10.938 14.568,9.89 L 2.75,-8.771 0,0 z" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3233" | |
inkscape:connector-curvature="0" /></g><g | |
id="g3235" | |
transform="translate(274.15732,626.4084)"><path | |
d="m 0,0 -15.64,0.407 -14.279,-3.608 2.008,-9.747 14.756,4.208 L 1.111,-8.215 0,0 z" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3237" | |
inkscape:connector-curvature="0" /></g><path | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="M 436.65625 22.28125 C 436.65625 22.28125 338.18375 25.385 251 42.8125 C 163.24875 60.35375 70.40625 99.65625 70.40625 99.65625 L 175.1875 495.28125 L 327.96875 492.34375 L 337.75 527.59375 C 337.75 527.59375 365.095 523.25875 373 518.78125 C 376.31375 516.90375 383.78125 508 383.78125 508 L 377.75 484.65625 L 504.21875 407.15625 L 436.65625 22.28125 z M 410.53125 55.1875 L 465.6875 393.3125 L 346.59375 456.625 L 202.75 466.46875 L 112 114.40625 L 263 79.1875 L 410.53125 55.1875 z " | |
transform="matrix(0.8,0,0,-0.8,0,768)" | |
id="path3253" /><g | |
id="g3247" | |
transform="matrix(1.199238,-0.02879331,0.02673084,1.0520756,172.41935,498.37339)"><path | |
d="m 0,0 c 0,0 -1.861,1.481 -9.143,-1.457 9.712,18.867 9.439,39.989 9.439,39.989 0,0 -3.106,-2.465 -11.311,-8.47 9.241,23.044 5.338,72.525 5.338,72.525 0,0 -17.493,40.746 -13.657,45.799 8.841,11.65 23.834,23.968 44.295,25.594 17.935,1.424 44.606,-4.953 55.865,-15.284 4.536,-4.161 23.367,-47.493 23.367,-47.493 0,0 6.104,-35.271 11.619,-54.108 5.513,-18.839 11.054,-26.674 21.284,-34.825 17.831,-14.207 27.076,-29.938 27.076,-29.938 L 143.399,3.945 c 3.655,-17.356 14.875,-34.28 27.39,-47.672 -12.863,1.507 -19.61,8.783 -19.61,8.783 0,0 2.151,-12.664 9.109,-26.554 l 28.712,15.264 -1.762,10.805 c -5.128,9.304 -9.336,15.534 -9.336,15.534 0,0 2.089,0.956 7.385,-3.572 l -2.005,12.296 c -4.814,9.391 -11.773,16.752 -25.115,31.113 5.944,-6.087 15.438,-5.379 20.751,-4.356 l -0.572,3.512 c -2.231,1.278 -5.494,3.171 -10.241,5.957 -12.43,7.299 -22.326,21.049 -22.326,21.049 0,0 12.85,1.815 20.513,11.022 -7.316,-2.641 -18.585,0.799 -18.585,0.799 -17.086,6.772 -15.022,30.217 -17.687,50.587 -2.667,20.37 -9.299,34.125 -9.299,34.125 0,0 -0.243,2.149 11.91,-5.906 -7.744,33.215 -35.545,44.94 -35.545,44.94 0,0 2.223,2.79 22.843,0.044 -16.469,15.817 -32.303,16.896 -32.303,16.896 0,0 10.077,2.25 23.611,0.24 0,0 -3.327,3.508 -7.549,6.453 L 35.985,194.291 -77.543,167.815 -8.211,-101.17 17.481,-99.413 C 8.602,-85.114 -0.371,-63.837 -2.15,-40.857 -4.911,-5.208 0,0 0,0" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3249" | |
inkscape:connector-curvature="0" /></g><g | |
id="g3255" | |
transform="translate(204.22134,580.88353)"><path | |
d="m 0,0 c 0,-1.418 0.43,-2.736 1.168,-3.83 1.523,0.677 3.551,1.094 5.786,1.094 2.164,0 4.133,-0.39 5.639,-1.029 0.711,1.081 1.129,2.374 1.129,3.765 0,3.79 -3.072,6.861 -6.861,6.861 C 3.071,6.861 0,3.79 0,0" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3257" | |
inkscape:connector-curvature="0" /></g><g | |
id="g3259" | |
transform="translate(256.3311,595.31646)"><path | |
d="m 0,0 c 0,-1.418 0.43,-2.736 1.168,-3.83 1.524,0.677 3.552,1.094 5.787,1.094 2.163,0 4.132,-0.39 5.638,-1.029 0.712,1.081 1.129,2.373 1.129,3.765 0,3.79 -3.072,6.861 -6.861,6.861 C 3.071,6.861 0,3.79 0,0" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3261" | |
inkscape:connector-curvature="0" /></g><g | |
id="g4174" | |
transform="matrix(0.99694509,0.07810563,-0.07810563,0.99694509,47.348748,-15.348299)"><g | |
transform="translate(222.5098,610.1558)" | |
id="g3219"><path | |
inkscape:connector-curvature="0" | |
id="path3221" | |
style="fill:#df0019;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="m 0,0 4.45,2.752 5.34,3.785 7.05,-8.226 7.093,-33.359 17.801,-51.259 13.86,-30.215 26.261,-1.55 -6.685,-35.653 c 0,0 -49.98,-21.871 -49.545,-21.911 -42.657,4.001 -12.553,43.066 -8.631,47.301 L 3.666,-47.869 0,0 z" /></g><g | |
transform="translate(247.626,467.3545)" | |
id="g3239"><path | |
inkscape:connector-curvature="0" | |
id="path3241" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="M 0,0 C -3.044,-0.345 -5.232,-3.092 -4.888,-6.136 -4.543,-9.18 1.576,-2.254 13.308,-4.961 13.971,-1.97 3.044,0.344 0,0" /></g><g | |
transform="translate(279.4419,476.5762)" | |
id="g3243"><path | |
inkscape:connector-curvature="0" | |
id="path3245" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="M 0,0 C 3.271,1.08 6.798,-0.697 7.88,-3.969 8.96,-7.24 -0.55,-3.044 -11.258,-11.329 -13.345,-8.586 -3.272,-1.081 0,0" /></g><g | |
transform="translate(284.1929,525.9082)" | |
id="g3263"><path | |
inkscape:connector-curvature="0" | |
id="path3265" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="M 0,0 C 0,0 -6.972,28.671 -6.972,29.355 L 1.585,2.864 9.999,-10.564 13.634,-32.697 7.922,-11.098 0,0 z M -0.633,-15.036 -9.19,-4.86 -16.478,25.776 c -0.202,0.684 9.106,-28.811 9.106,-28.811 l 8.64,-11.642 2.469,-17.336 -4.37,16.977 z m -6.339,-6.085 -10.457,16.826 -5.444,28.646 6.614,-27.842 11.311,-18.026 1.413,-9.583 -3.437,9.979 z m -53.462,-13.246 -1.437,24.944 -2.682,28.754 5.106,-29.895 1.212,-21.677 4.139,-18.236 -6.338,16.11 z m -4.265,-19.55 -6.665,15.516 0.404,29.205 -0.882,28.169 3.104,-28.396 0.808,-26.697 4.242,-15.972 2.423,-6.617 -3.434,4.792 z m -9.695,-2.967 -7.117,16.885 1.318,32.01 0,25.223 2.115,-25.061 -0.581,-31.259 5.869,-16.429 5.056,-8.671 -6.66,7.302 z m 103.144,-7.97 -6.676,20.38 2.141,11.54 L 16.499,-9.376 4.557,13.104 -5.879,53.97 c 0,0 -8.325,-7.41 -16.781,-8.08 -8.455,-0.671 -15.09,4.018 -15.09,4.018 0,0 3.592,-17.761 8.659,-37.597 5.069,-19.836 17.528,-44.866 17.528,-44.866 0,0 21.578,-8.197 24.302,-16.587 2.724,-8.391 -3.508,-22.911 -14.102,-26.551 -10.593,-3.64 -32.284,-8.262 -32.284,-8.262 0,0 -19,1.512 -20.438,14.26 0,0 4.131,16.406 10.418,19.225 6.285,2.819 21.362,11.174 21.362,11.174 l -8.254,1.332 -7.664,-1.332 c 0,0 -4.784,11.295 -10.973,35.086 -6.19,23.79 -8.967,42.485 -8.967,42.485 0,0 -3.912,-4.391 -14.199,-4.885 -10.286,-0.494 -16.031,7.988 -16.031,7.988 l 1.027,-30.185 -1.049,-25.83 -0.15,-29.22 5.102,-15.99 19.818,-30.448 c 0,0 14.102,-9.293 31.728,-9.293 16.453,1.328 51.131,18.047 51.131,18.047 l 9.536,16.687 z" /></g></g><g | |
id="g3267" | |
transform="translate(847.2637,321.5059)"><path | |
d="m 0,0 c 2.252,3.516 6.693,15.3 6.693,15.3 0,0 3.778,-13.306 1.912,-17.213 -3.056,-6.404 -23.905,-15.3 -23.905,-15.3 0,0 12.196,12.364 15.3,17.213 m -33.514,23.16 -0.757,56.352 c 0,0 11.136,-14.028 11.843,-19.739 1.176,-9.491 -11.086,-36.613 -11.086,-36.613 m -17.575,236.921 c 0,0 12.453,-15.338 14.854,-21.39 1.424,-3.591 2.286,-15.287 2.286,-15.287 l -17.14,36.677 z M -98.574,-86.136 c -9.757,-0.906 -29.836,1.016 -38.912,4.708 -7.499,3.05 -25.734,19.656 -25.734,19.656 l 24.187,-10.86 -4.701,17.627 15.272,-22.009 41.813,-5.356 c 0,0 -8.812,-3.477 -11.925,-3.766 m -74.428,157.941 c -4.518,10.057 -1.763,44.065 -1.763,44.065 0,0 7.544,-31.093 12.338,-40.541 6.978,-13.754 37.015,-49.352 37.015,-49.352 0,0 -40.824,30.759 -47.59,45.828 m -17.833,-149.47 -40.407,24.724 1.636,-17.575 0.026,-0.035 -5.178,-29.811 -2.056,-10.701 0.383,-33.34 -4.982,36.406 6.41,41.45 -11.063,8.338 -17.532,43.159 23.502,-38.779 2.351,14.101 40.634,-25.695 11.924,-5.651 13.809,-28.871 -19.457,22.28 z m -85.522,138.863 17.212,-34.424 c 0,0 -12.972,11.185 -15.299,16.257 -1.905,4.152 -1.913,18.167 -1.913,18.167 m -2.367,66.042 c 0,0 -6.206,15.581 -6.323,21.082 -0.168,7.817 4.568,23.148 7.695,30.315 0.755,1.73 4.103,6.341 4.103,6.341 0,0 -4.654,-24.542 -5.347,-32.829 -0.518,-6.205 -0.128,-24.909 -0.128,-24.909 m -7.195,-114.809 c -0.334,3.363 1.912,13.387 1.912,13.387 l 3.825,-29.643 c 0,0 -5.313,11.967 -5.737,16.256 m -20.082,53.549 c -1.394,3.571 -0.956,15.301 -0.956,15.301 l 13.388,-30.6 c 0,0 -10.639,10.71 -12.432,15.299 m -6.03,106.795 c 0,0 -0.315,35.831 4.637,46.379 4.531,9.647 29.936,30.356 29.936,30.356 0,0 -17.824,-22.47 -21.503,-31.2 -5.089,-12.077 -10.119,-51.437 -10.119,-51.437 l -2.951,5.902 z M 50.121,205.01 c 3.335,-9.155 1.168,-38.956 1.168,-38.956 0,0 -5.451,29.987 -9.221,39.366 -4.214,10.487 -23.014,38.907 -23.014,38.907 0,0 26.78,-27.546 31.067,-39.317 M 54.506,95.624 c 0,0 6.884,-18.586 5.738,-24.861 -0.773,-4.241 -9.562,-14.345 -9.562,-14.345 0,0 2.414,12.874 2.868,17.212 0.573,5.474 0.956,21.994 0.956,21.994 M 19.125,-13.389 c 0,0 9.656,22.183 11.062,30.068 1.235,6.941 0,28.203 0,28.203 0,0 8.477,-22.819 7.106,-30.538 C 35.845,6.183 19.125,-13.389 19.125,-13.389 m 441.487,-40.965 c -3.249,8.935 -6.587,17.23 -10.01,24.928 l -1.862,28.873 -8.857,-4.876 -25.862,49.457 -4.828,-10.34 c -32.69,31.48 -70.457,34.284 -111.982,31.646 -65.568,-4.163 -91.587,-41.63 -79.098,-57.241 12.49,-15.613 18.733,-5.205 40.589,5.203 21.858,10.407 74.937,26.017 110.323,-2.082 35.386,-28.1 86.383,-109.281 50.997,-169.646 -35.386,-60.365 -105.626,-105.385 -182.135,-88.465 -86.422,19.112 -126.078,60.082 -177.675,74.811 -8.311,1.334 -18.347,2.789 -24.791,3.191 -12.671,0.792 -21.6,14.727 -21.6,14.727 l 17.181,-9.327 25.763,-2.36 c 2.331,14 9.395,49.054 9.395,49.054 l -8.688,87.29 -18.668,-27.06 -7.246,10.184 -21.349,-22.915 -15.473,-1.959 14.67,6.596 21.38,29.409 6.7,-13.754 19.485,24.691 0.004,-0.011 16.47,9.525 -3.123,68.69 10.407,-10.407 -4.163,40.59 22.173,71.502 -34.662,91.899 16.652,-4.162 -19.773,35.386 -40.591,38.509 9.368,17.693 -93.671,9.368 -20.229,-7.165 -18.437,38.292 13.22,8.813 -69.039,14.69 2.938,19.095 -80.791,-23.303 -26.147,-19.191 -116.339,0 8.814,-10.188 -42.501,-40.641 -8.911,-78.491 7.344,-1.494 8.814,-45.548 23.502,-24.978 19.096,45.533 -14.689,-4.409 41.13,48.474 30.848,26.44 -14.69,-1.469 19.096,16.158 105.763,2.938 72.917,15.799 -41.623,-14.742 -30.181,-7.285 -104.079,-1.043 1.04,-11.449 -64.526,-61.403 14.571,2.081 -27.844,-63.28 c -15.017,-13.719 -28.06,-55.016 -36.687,-75.145 -9.367,-21.856 -20.816,-39.55 -20.816,-39.55 0,0 -30.182,-6.244 -61.405,-18.734 -31.224,-12.489 -43.713,4.163 -43.713,4.163 l -3.122,-8.326 c 0,0 -18.28,-9.057 -39.303,-11.825 -16.43,-2.162 -9.967,-20.946 -9.613,-26.684 0.405,-6.57 4.294,-19.774 8.325,-24.978 3.227,-4.165 12.525,-10.425 17.694,-11.448 12.039,-2.385 28.101,5.204 45.794,17.693 74.936,-6.245 103.241,-10.321 126.974,8.326 14.572,11.448 29.142,22.897 41.631,40.59 l -15.611,42.671 -8.327,-14.569 -5.807,44.931 1.841,17.863 5.547,-51.234 7.789,9.257 35.387,-70.772 11.448,4.164 c 0,0 13.515,-18.583 23.057,-32.881 l -26.02,25.006 -10.224,-5.964 -11.076,22.152 c 0,0 -13.383,-2.353 -24.727,-18.027 -15.862,-21.915 -23.503,-24.678 -17.627,-78.735 5.876,-54.055 16.452,-54.055 64.632,-121.039 11.752,-16.452 14.601,-18.465 14.601,-18.465 l -51.03,-27.365 -22.327,-5.876 -21.384,-11.28 c 0,0 4.744,-8.174 7.495,-9.369 4.739,-2.062 20.613,1.56 20.613,1.56 0,0 15.603,-6.763 36.756,-6.763 21.152,0 32.903,8.225 47.005,8.225 14.101,0 38.78,-8.225 57.582,-5.876 18.802,2.351 22.328,12.927 22.328,12.927 l -51.706,54.057 -4.675,47.096 -56.605,75.769 -3.038,9.437 65.791,-82.24 5.107,-46.75 55.161,-61.405 37.468,-8.325 c 0,0 -0.257,1.226 -0.625,3.114 -6.146,15.664 -6.986,34.894 -1.999,54.214 6.975,27.012 38.85,36.596 64.029,36.596 12.506,0 24.179,-2.312 32.025,-6.341 12.912,-6.63 21.851,-15.076 27.029,-20.917 3.673,4.516 7.133,7.194 11.833,11.11 0,0 12.143,-11.751 45.047,-14.101 27.14,-1.939 45.048,-8.226 70.901,-19.585 53.676,-23.584 102.5,-61.785 207.618,-45.132 105.119,16.651 206.073,113.444 164.442,227.929" | |
style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
id="path3269" | |
inkscape:connector-curvature="0" /></g><path | |
inkscape:connector-curvature="0" | |
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" | |
d="m 329.26398,723.3082 -118.025,-19.2 -120.800003,-28.175 72.600003,-281.65 115.075,7.875 95.275,50.65 -44.125,270.5 z m -6.55,-10.575 40.675,-252.4 -87.85,-47.275 -106.125,-7.325 -66.95,262.8 111.4,26.275 108.85,17.925 z" | |
id="path3253-3" /></g></svg> |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Compression Helper</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* Script compression | |
helper */ | |
</pre> | |
<p>To optimize loading CodeMirror, especially when including a | |
bunch of different modes, it is recommended that you combine and | |
minify (and preferably also gzip) the scripts. This page makes | |
those first two steps very easy. Simply select the version and | |
scripts you need in the form below, and | |
click <strong>Compress</strong> to download the minified script | |
file.</p> | |
<form id="form" action="http://marijnhaverbeke.nl/uglifyjs" method="post"> | |
<input type="hidden" id="download" name="download" value="codemirror-compressed.js"/> | |
<p>Version: <select id="version" onchange="setVersion(this);" style="padding: 1px"> | |
<option value="http://codemirror.net/">HEAD</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.2;f=">2.2</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.18;f=">2.18</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.16;f=">2.16</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.15;f=">2.15</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.13;f=">2.13</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.12;f=">2.12</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.11;f=">2.11</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.1;f=">2.1</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.02;f=">2.02</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.01;f=">2.01</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=v2.0;f=">2.0</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=beta2;f=">beta2</option> | |
<option value="http://marijnhaverbeke.nl/git/codemirror2?a=blob_plain;hb=beta1;f=">beta1</option> | |
</select></p> | |
<select multiple="multiple" name="code_url" style="width: 40em;" class="field" id="files"> | |
<optgroup label="CodeMirror Library"> | |
<option value="http://codemirror.net/lib/codemirror.js" selected>codemirror.js</option> | |
</optgroup> | |
<optgroup label="Modes"> | |
<option value="http://codemirror.net/mode/clike/clike.js">clike.js</option> | |
<option value="http://codemirror.net/mode/clojure/clojure.js">clojure.js</option> | |
<option value="http://codemirror.net/mode/coffeescript/coffeescript.js">coffeescript.js</option> | |
<option value="http://codemirror.net/mode/css/css.js">css.js</option> | |
<option value="http://codemirror.net/mode/diff/diff.js">diff.js</option> | |
<option value="http://codemirror.net/mode/gfm/gfm.js">gfm.js</option> | |
<option value="http://codemirror.net/mode/groovy/groovy.js">groovy.js</option> | |
<option value="http://codemirror.net/mode/haskell/haskell.js">haskell.js</option> | |
<option value="http://codemirror.net/mode/htmlembedded/htmlembedded.js">htmlembedded.js</option> | |
<option value="http://codemirror.net/mode/htmlmixed/htmlmixed.js">htmlmixed.js</option> | |
<option value="http://codemirror.net/mode/javascript/javascript.js">javascript.js</option> | |
<option value="http://codemirror.net/mode/jinja2/jinja2.js">jinja2.js</option> | |
<option value="http://codemirror.net/mode/lua/lua.js">lua.js</option> | |
<option value="http://codemirror.net/mode/markdown/markdown.js">markdown.js</option> | |
<option value="http://codemirror.net/mode/ntriples/ntriples.js">ntriples.js</option> | |
<option value="http://codemirror.net/mode/pascal/pascal.js">pascal.js</option> | |
<option value="http://codemirror.net/mode/perl/perl.js">perl.js</option> | |
<option value="http://codemirror.net/mode/php/php.js">php.js</option> | |
<option value="http://codemirror.net/mode/plsql/plsql.js">plsql.js</option> | |
<option value="http://codemirror.net/mode/python/python.js">python.js</option> | |
<option value="http://codemirror.net/mode/r/r.js">r.js</option> | |
<option value="http://codemirror.net/mode/rpm/changes/changes.js">rpm/changes.js</option> | |
<option value="http://codemirror.net/mode/rpm/spec/spec.js">rpm/spec.js</option> | |
<option value="http://codemirror.net/mode/rst/rst.js">rst.js</option> | |
<option value="http://codemirror.net/mode/ruby/ruby.js">ruby.js</option> | |
<option value="http://codemirror.net/mode/rust/rust.js">rust.js</option> | |
<option value="http://codemirror.net/mode/scheme/scheme.js">scheme.js</option> | |
<option value="http://codemirror.net/mode/smalltalk/smalltalk.js">smalltalk.js</option> | |
<option value="http://codemirror.net/mode/sparql/sparql.js">sparql.js</option> | |
<option value="http://codemirror.net/mode/stex/stex.js">stex.js</option> | |
<option value="http://codemirror.net/mode/tiddlywiki/tiddlywiki.js">tiddlywiki.js</option> | |
<option value="http://codemirror.net/mode/velocity/velocity.js">velocity.js</option> | |
<option value="http://codemirror.net/mode/xml/xml.js">xml.js</option> | |
<option value="http://codemirror.net/mode/yaml/yaml.js">yaml.js</option> | |
</optgroup> | |
<optgroup label="Utilities and add-ons"> | |
<option value="http://codemirror.net/lib/util/overlay.js">overlay.js</option> | |
<option value="http://codemirror.net/lib/util/runmode.js">runmode.js</option> | |
<option value="http://codemirror.net/lib/util/simple-hint.js">simple-hint.js</option> | |
<option value="http://codemirror.net/lib/util/javascript-hint.js">javascript-hint.js</option> | |
<option value="http://codemirror.net/lib/util/foldcode.js">codefold.js</option> | |
<option value="http://codemirror.net/lib/util/dialog.js">dialog.js</option> | |
<option value="http://codemirror.net/lib/util/search.js">search.js</option> | |
</optgroup> | |
<optgroup label="Keymaps"> | |
<option value="http://codemirror.net/keymap/emacs.js">emacs.js</option> | |
</optgroup> | |
</select></p> | |
<p> | |
<button type="submit">Compress</button> with <a href="http://github.com/mishoo/UglifyJS/">UglifyJS</a> | |
</p> | |
<p>Custom code to add to the compressed file:<textarea name="js_code" style="width: 100%; height: 15em;" class="field"></textarea></p> | |
</form> | |
<script type="text/javascript"> | |
function setVersion(ver) { | |
var urlprefix = ver.options[ver.selectedIndex].value; | |
var select = document.getElementById("files"), m; | |
for (var optgr = select.firstChild; optgr; optgr = optgr.nextSibling) | |
for (var opt = optgr.firstChild; opt; opt = opt.nextSibling) { | |
if (opt.nodeName != "OPTION") | |
continue; | |
else if (m = opt.value.match(/^http:\/\/codemirror.net\/2\/(.*)$/)) | |
opt.value = urlprefix + m[1]; | |
else if (m = opt.value.match(/http:\/\/marijnhaverbeke.nl\/git\/codemirror\?a=blob_plain;hb=[^;]+;f=(.*)$/)) | |
opt.value = urlprefix + m[1]; | |
} | |
} | |
</script> | |
</body> | |
</html> | |
body { | |
font-family: Droid Sans, Arial, sans-serif; | |
line-height: 1.5; | |
max-width: 64.3em; | |
margin: 3em auto; | |
padding: 0 1em; | |
} | |
h1 { | |
letter-spacing: -3px; | |
font-size: 3.23em; | |
font-weight: bold; | |
margin: 0; | |
} | |
h2 { | |
font-size: 1.23em; | |
font-weight: bold; | |
margin: .5em 0; | |
letter-spacing: -1px; | |
} | |
h3 { | |
font-size: 1em; | |
font-weight: bold; | |
margin: .4em 0; | |
} | |
pre { | |
background-color: #eee; | |
-moz-border-radius: 6px; | |
-webkit-border-radius: 6px; | |
border-radius: 6px; | |
padding: 1em; | |
} | |
pre.code { | |
margin: 0 1em; | |
} | |
.grey { | |
font-size: 2.2em; | |
padding: .5em 1em; | |
line-height: 1.2em; | |
margin-top: .5em; | |
position: relative; | |
} | |
img.logo { | |
position: absolute; | |
right: -25px; | |
bottom: 4px; | |
} | |
a:link, a:visited, .quasilink { | |
color: #df0019; | |
cursor: pointer; | |
text-decoration: none; | |
} | |
a:hover, .quasilink:hover { | |
color: #800004; | |
} | |
h1 a:link, h1 a:visited, h1 a:hover { | |
color: black; | |
} | |
ul { | |
margin: 0; | |
padding-left: 1.2em; | |
} | |
a.download { | |
color: white; | |
background-color: #df0019; | |
width: 100%; | |
display: block; | |
text-align: center; | |
font-size: 1.23em; | |
font-weight: bold; | |
text-decoration: none; | |
-moz-border-radius: 6px; | |
-webkit-border-radius: 6px; | |
border-radius: 6px; | |
padding: .5em 0; | |
margin-bottom: 1em; | |
} | |
a.download:hover { | |
background-color: #bb0010; | |
} | |
.rel { | |
margin-bottom: 0; | |
} | |
.rel-note { | |
color: #777; | |
font-size: .9em; | |
margin-top: .1em; | |
} | |
.logo-braces { | |
color: #df0019; | |
position: relative; | |
top: -4px; | |
} | |
.blk { | |
float: left; | |
} | |
.left { | |
width: 37em; | |
padding-right: 6.53em; | |
padding-bottom: 1em; | |
} | |
.left1 { | |
width: 15.24em; | |
padding-right: 6.45em; | |
} | |
.left2 { | |
width: 15.24em; | |
} | |
.right { | |
width: 20.68em; | |
} | |
.leftbig { | |
width: 42.44em; | |
padding-right: 6.53em; | |
} | |
.rightsmall { | |
width: 15.24em; | |
} | |
.clear:after { | |
visibility: hidden; | |
display: block; | |
font-size: 0; | |
content: " "; | |
clear: both; | |
height: 0; | |
} | |
.clear { display: inline-block; } | |
/* start commented backslash hack \*/ | |
* html .clear { height: 1%; } | |
.clear { display: block; } | |
/* close commented backslash hack */ | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Internals</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<style>dl dl {margin: 0;} .update {color: #d40 !important}</style> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* (Re-) Implementing A Syntax- | |
Highlighting Editor in JavaScript */ | |
</pre> | |
<div class="clear"><div class="leftbig blk"> | |
<p style="font-size: 85%" id="intro"> | |
<strong>Topic:</strong> JavaScript, code editor implementation<br> | |
<strong>Author:</strong> Marijn Haverbeke<br> | |
<strong>Date:</strong> March 2nd 2011 (updated November 13th 2011) | |
</p> | |
<p>This is a followup to | |
my <a href="http://codemirror.net/story.html">Brutal Odyssey to the | |
Dark Side of the DOM Tree</a> story. That one describes the | |
mind-bending process of implementing (what would become) CodeMirror 1. | |
This one describes the internals of CodeMirror 2, a complete rewrite | |
and rethink of the old code base. I wanted to give this piece another | |
Hunter Thompson copycat subtitle, but somehow that would be out of | |
place—the process this time around was one of straightforward | |
engineering, requiring no serious mind-bending whatsoever.</p> | |
<p>So, what is wrong with CodeMirror 1? I'd estimate, by mailing list | |
activity and general search-engine presence, that it has been | |
integrated into about a thousand systems by now. The most prominent | |
one, since a few weeks, | |
being <a href="http://googlecode.blogspot.com/2011/01/make-quick-fixes-quicker-on-google.html">Google | |
code's project hosting</a>. It works, and it's being used widely.</a> | |
<p>Still, I did not start replacing it because I was bored. CodeMirror | |
1 was heavily reliant on <code>designMode</code> | |
or <code>contentEditable</code> (depending on the browser). Neither of | |
these are well specified (HTML5 tries | |
to <a href="http://www.w3.org/TR/html5/editing.html#contenteditable">specify</a> | |
their basics), and, more importantly, they tend to be one of the more | |
obscure and buggy areas of browser functionality—CodeMirror, by using | |
this functionality in a non-typical way, was constantly running up | |
against browser bugs. WebKit wouldn't show an empty line at the end of | |
the document, and in some releases would suddenly get unbearably slow. | |
Firefox would show the cursor in the wrong place. Internet Explorer | |
would insist on linkifying everything that looked like a URL or email | |
address, a behaviour that can't be turned off. Some bugs I managed to | |
work around (which was often a frustrating, painful process), others, | |
such as the Firefox cursor placement, I gave up on, and had to tell | |
user after user that they were known problems, but not something I | |
could help.</p> | |
<p>Also, there is the fact that <code>designMode</code> (which seemed | |
to be less buggy than <code>contentEditable</code> in Webkit and | |
Firefox, and was thus used by CodeMirror 1 in those browsers) requires | |
a frame. Frames are another tricky area. It takes some effort to | |
prevent getting tripped up by domain restrictions, they don't | |
initialize synchronously, behave strangely in response to the back | |
button, and, on several browsers, can't be moved around the DOM | |
without having them re-initialize. They did provide a very nice way to | |
namespace the library, though—CodeMirror 1 could freely pollute the | |
namespace inside the frame.</p> | |
<p>Finally, working with an editable document means working with | |
selection in arbitrary DOM structures. Internet Explorer (8 and | |
before) has an utterly different (and awkward) selection API than all | |
of the other browsers, and even among the different implementations of | |
<code>document.selection</code>, details about how exactly a selection | |
is represented vary quite a bit. Add to that the fact that Opera's | |
selection support tended to be very buggy until recently, and you can | |
imagine why CodeMirror 1 contains 700 lines of selection-handling | |
code.</p> | |
<p>And that brings us to the main issue with the CodeMirror 1 | |
code base: The proportion of browser-bug-workarounds to real | |
application code was getting dangerously high. By building on top of a | |
few dodgy features, I put the system in a vulnerable position—any | |
incompatibility and bugginess in these features, I had to paper over | |
with my own code. Not only did I have to do some serious stunt-work to | |
get it to work on older browsers (as detailed in the | |
previous <a href="http://codemirror.net/story.html">story</a>), things | |
also kept breaking in newly released versions, requiring me to come up | |
with <em>new</em> scary hacks in order to keep up. This was starting | |
to lose its appeal.</p> | |
<h2 id="approach">General Approach</h2> | |
<p>What CodeMirror 2 does is try to sidestep most of the hairy hacks | |
that came up in version 1. I owe a lot to the | |
<a href="http://ace.ajax.org">ACE</a> editor for inspiration on how to | |
approach this.</p> | |
<p>I absolutely did not want to be completely reliant on key events to | |
generate my input. Every JavaScript programmer knows that key event | |
information is horrible and incomplete. Some people (most awesomely | |
Mihai Bazon with <a href="http://ymacs.org">Ymacs</a>) have been able | |
to build more or less functioning editors by directly reading key | |
events, but it takes a lot of work (the kind of never-ending, fragile | |
work I described earlier), and will never be able to properly support | |
things like multi-keystoke international character | |
input. <a href="#keymap" class="update">[see below for caveat]</a></p> | |
<p>So what I do is focus a hidden textarea, and let the browser | |
believe that the user is typing into that. What we show to the user is | |
a DOM structure we built to represent his document. If this is updated | |
quickly enough, and shows some kind of believable cursor, it feels | |
like a real text-input control.</p> | |
<p>Another big win is that this DOM representation does not have to | |
span the whole document. Some CodeMirror 1 users insisted that they | |
needed to put a 30 thousand line XML document into CodeMirror. Putting | |
all that into the DOM takes a while, especially since, for some | |
reason, an editable DOM tree is slower than a normal one on most | |
browsers. If we have full control over what we show, we must only | |
ensure that the visible part of the document has been added, and can | |
do the rest only when needed. (Fortunately, the <code>onscroll</code> | |
event works almost the same on all browsers, and lends itself well to | |
displaying things only as they are scrolled into view.)</p> | |
<h2 id="input">Input</h2> | |
<p>ACE uses its hidden textarea only as a text input shim, and does | |
all cursor movement and things like text deletion itself by directly | |
handling key events. CodeMirror's way is to let the browser do its | |
thing as much as possible, and not, for example, define its own set of | |
key bindings. One way to do this would have been to have the whole | |
document inside the hidden textarea, and after each key event update | |
the display DOM to reflect what's in that textarea.</p> | |
<p>That'd be simple, but it is not realistic. For even medium-sized | |
document the editor would be constantly munging huge strings, and get | |
terribly slow. What CodeMirror 2 does is put the current selection, | |
along with an extra line on the top and on the bottom, into the | |
textarea.</p> | |
<p>This means that the arrow keys (and their ctrl-variations), home, | |
end, etcetera, do not have to be handled specially. We just read the | |
cursor position in the textarea, and update our cursor to match it. | |
Also, copy and paste work pretty much for free, and people get their | |
native key bindings, without any special work on my part. For example, | |
I have emacs key bindings configured for Chrome and Firefox. There is | |
no way for a script to detect this. <a class="update" | |
href="#keymap">[no longer the case]</a></p> | |
<p>Of course, since only a small part of the document sits in the | |
textarea, keys like page up and ctrl-end won't do the right thing. | |
CodeMirror is catching those events and handling them itself.</p> | |
<h2 id="selection">Selection</h2> | |
<p>Getting and setting the selection range of a textarea in modern | |
browsers is trivial—you just use the <code>selectionStart</code> | |
and <code>selectionEnd</code> properties. On IE you have to do some | |
insane stuff with temporary ranges and compensating for the fact that | |
moving the selection by a 'character' will treat \r\n as a single | |
character, but even there it is possible to build functions that | |
reliably set and get the selection range.</p> | |
<p>But consider this typical case: When I'm somewhere in my document, | |
press shift, and press the up arrow, something gets selected. Then, if | |
I, still holding shift, press the up arrow again, the top of my | |
selection is adjusted. The selection remembers where its <em>head</em> | |
and its <em>anchor</em> are, and moves the head when we shift-move. | |
This is a generally accepted property of selections, and done right by | |
every editing component built in the past twenty years.</p> | |
<p>But not something that the browser selection APIs expose.</p> | |
<p>Great. So when someone creates an 'upside-down' selection, the next | |
time CodeMirror has to update the textarea, it'll re-create the | |
selection as an 'upside-up' selection, with the anchor at the top, and | |
the next cursor motion will behave in an unexpected way—our second | |
up-arrow press in the example above will not do anything, since it is | |
interpreted in exactly the same way as the first.</p> | |
<p>No problem. We'll just, ehm, detect that the selection is | |
upside-down (you can tell by the way it was created), and then, when | |
an upside-down selection is present, and a cursor-moving key is | |
pressed in combination with shift, we quickly collapse the selection | |
in the textarea to its start, allow the key to take effect, and then | |
combine its new head with its old anchor to get the <em>real</em> | |
selection.</p> | |
<p>In short, scary hacks could not be avoided entirely in CodeMirror | |
2.</p> | |
<p>And, the observant reader might ask, how do you even know that a | |
key combo is a cursor-moving combo, if you claim you support any | |
native key bindings? Well, we don't, but we can learn. The editor | |
keeps a set known cursor-movement combos (initialized to the | |
predictable defaults), and updates this set when it observes that | |
pressing a certain key had (only) the effect of moving the cursor. | |
This, of course, doesn't work if the first time the key is used was | |
for extending an inverted selection, but it works most of the | |
time.</p> | |
<h2 id="update">Intelligent Updating</h2> | |
<p>One thing that always comes up when you have a complicated internal | |
state that's reflected in some user-visible external representation | |
(in this case, the displayed code and the textarea's content) is | |
keeping the two in sync. The naive way is to just update the display | |
every time you change your state, but this is not only error prone | |
(you'll forget), it also easily leads to duplicate work on big, | |
composite operations. Then you start passing around flags indicating | |
whether the display should be updated in an attempt to be efficient | |
again and, well, at that point you might as well give up completely.</p> | |
<p>I did go down that road, but then switched to a much simpler model: | |
simply keep track of all the things that have been changed during an | |
action, and then, only at the end, use this information to update the | |
user-visible display.</p> | |
<p>CodeMirror uses a concept of <em>operations</em>, which start by | |
calling a specific set-up function that clears the state and end by | |
calling another function that reads this state and does the required | |
updating. Most event handlers, and all the user-visible methods that | |
change state are wrapped like this. There's a method | |
called <code>operation</code> that accepts a function, and returns | |
another function that wraps the given function as an operation.</p> | |
<p>It's trivial to extend this (as CodeMirror does) to detect nesting, | |
and, when an operation is started inside an operation, simply | |
increment the nesting count, and only do the updating when this count | |
reaches zero again.</p> | |
<p>If we have a set of changed ranges and know the currently shown | |
range, we can (with some awkward code to deal with the fact that | |
changes can add and remove lines, so we're dealing with a changing | |
coordinate system) construct a map of the ranges that were left | |
intact. We can then compare this map with the part of the document | |
that's currently visible (based on scroll offset and editor height) to | |
determine whether something needs to be updated.</p> | |
<p>CodeMirror uses two update algorithms—a full refresh, where it just | |
discards the whole part of the DOM that contains the edited text and | |
rebuilds it, and a patch algorithm, where it uses the information | |
about changed and intact ranges to update only the out-of-date parts | |
of the DOM. When more than 30 percent (which is the current heuristic, | |
might change) of the lines need to be updated, the full refresh is | |
chosen (since it's faster to do than painstakingly finding and | |
updating all the changed lines), in the other case it does the | |
patching (so that, if you scroll a line or select another character, | |
the whole screen doesn't have to be | |
re-rendered). <span class="update">[the full-refresh | |
algorithm was dropped, it wasn't really faster than the patching | |
one]</span></p> | |
<p>All updating uses <code>innerHTML</code> rather than direct DOM | |
manipulation, since that still seems to be by far the fastest way to | |
build documents. There's a per-line function that combines the | |
highlighting, <a href="manual.html#markText">marking</a>, and | |
selection info for that line into a snippet of HTML. The patch updater | |
uses this to reset individual lines, the refresh updater builds an | |
HTML chunk for the whole visible document at once, and then uses a | |
single <code>innerHTML</code> update to do the refresh.</p> | |
<h2 id="parse">Parsers can be Simple</h2> | |
<p>When I wrote CodeMirror 1, I | |
thought <a href="http://codemirror.net/story.html#parser">interruptable | |
parsers</a> were a hugely scary and complicated thing, and I used a | |
bunch of heavyweight abstractions to keep this supposed complexity | |
under control: parsers | |
were <a href="http://bob.pythonmac.org/archives/2005/07/06/iteration-in-javascript/">iterators</a> | |
that consumed input from another iterator, and used funny | |
closure-resetting tricks to copy and resume themselves.</p> | |
<p>This made for a rather nice system, in that parsers formed strictly | |
separate modules, and could be composed in predictable ways. | |
Unfortunately, it was quite slow (stacking three or four iterators on | |
top of each other), and extremely intimidating to people not used to a | |
functional programming style.</p> | |
<p>With a few small changes, however, we can keep all those | |
advantages, but simplify the API and make the whole thing less | |
indirect and inefficient. CodeMirror | |
2's <a href="manual.html#modeapi">mode API</a> uses explicit state | |
objects, and makes the parser/tokenizer a function that simply takes a | |
state and a character stream abstraction, advances the stream one | |
token, and returns the way the token should be styled. This state may | |
be copied, optionally in a mode-defined way, in order to be able to | |
continue a parse at a given point. Even someone who's never touched a | |
lambda in his life can understand this approach. Additionally, far | |
fewer objects are allocated in the course of parsing now.</p> | |
<p>The biggest speedup comes from the fact that the parsing no longer | |
has to touch the DOM though. In CodeMirror 1, on an older browser, you | |
could <em>see</em> the parser work its way through the document, | |
managing some twenty lines in each 50-millisecond time slice it got. It | |
was reading its input from the DOM, and updating the DOM as it went | |
along, which any experienced JavaScript programmer will immediately | |
spot as a recipe for slowness. In CodeMirror 2, the parser usually | |
finishes the whole document in a single 100-millisecond time slice—it | |
manages some 1500 lines during that time on Chrome. All it has to do | |
is munge strings, so there is no real reason for it to be slow | |
anymore.</p> | |
<h2 id="summary">What Gives?</h2> | |
<p>Given all this, what can you expect from CodeMirror 2?</p> | |
<ul> | |
<li><strong>Small.</strong> the base library is | |
some <span class="update">45k</span> when minified | |
now, <span class="update">17k</span> when gzipped. It's smaller than | |
its own logo.</li> | |
<li><strong>Lightweight.</strong> CodeMirror 2 initializes very | |
quickly, and does almost no work when it is not focused. This means | |
you can treat it almost like a textarea, have multiple instances on a | |
page without trouble.</li> | |
<li><strong>Huge document support.</strong> Since highlighting is | |
really fast, and no DOM structure is being built for non-visible | |
content, you don't have to worry about locking up your browser when a | |
user enters a megabyte-sized document.</li> | |
<li><strong>Extended API.</strong> Some things kept coming up in the | |
mailing list, such as marking pieces of text or lines, which were | |
extremely hard to do with CodeMirror 1. The new version has proper | |
support for these built in.</li> | |
<li><strong>Tab support.</strong> Tabs inside editable documents were, | |
for some reason, a no-go. At least six different people announced they | |
were going to add tab support to CodeMirror 1, none survived (I mean, | |
none delivered a working version). CodeMirror 2 no longer removes tabs | |
from your document.</li> | |
<li><strong>Sane styling.</strong> <code>iframe</code> nodes aren't | |
really known for respecting document flow. Now that an editor instance | |
is a plain <code>div</code> element, it is much easier to size it to | |
fit the surrounding elements. You don't even have to make it scroll if | |
you do not <a href="../demo/resize.html">want to</a>.</li> | |
</ul> | |
<p>On the downside, a CodeMirror 2 instance is <em>not</em> a native | |
editable component. Though it does its best to emulate such a | |
component as much as possible, there is functionality that browsers | |
just do not allow us to hook into. Doing select-all from the context | |
menu, for example, is not currently detected by CodeMirror.</p> | |
<p id="changes" style="margin-top: 2em;"><span style="font-weight: | |
bold">[Updates from November 13th 2011]</span> Recently, I've made | |
some changes to the codebase that cause some of the text above to no | |
longer be current. I've left the text intact, but added markers at the | |
passages that are now inaccurate. The new situation is described | |
below.</p> | |
<h2 id="btree">Content Representation</h2> | |
<p>The original implementation of CodeMirror 2 represented the | |
document as a flat array of line objects. This worked well—splicing | |
arrays will require the part of the array after the splice to be | |
moved, but this is basically just a simple <code>memmove</code> of a | |
bunch of pointers, so it is cheap even for huge documents.</p> | |
<p>However, I recently added line wrapping and code folding (line | |
collapsing, basically). Once lines start taking up a non-constant | |
amount of vertical space, looking up a line by vertical position | |
(which is needed when someone clicks the document, and to determine | |
the visible part of the document during scrolling) can only be done | |
with a linear scan through the whole array, summing up line heights as | |
you go. Seeing how I've been going out of my way to make big documents | |
fast, this is not acceptable.</p> | |
<p>The new representation is based on a B-tree. The leaves of the tree | |
contain arrays of line objects, with a fixed minimum and maximum size, | |
and the non-leaf nodes simply hold arrays of child nodes. Each node | |
stores both the amount of lines that live below them and the vertical | |
space taken up by these lines. This allows the tree to be indexed both | |
by line number and by vertical position, and all access has | |
logarithmic complexity in relation to the document size.</p> | |
<p>I gave line objects and tree nodes parent pointers, to the node | |
above them. When a line has to update its height, it can simply walk | |
these pointers to the top of the tree, adding or subtracting the | |
difference in height from each node it encounters. The parent pointers | |
also make it cheaper (in complexity terms, the difference is probably | |
tiny in normal-sized documents) to find the current line number when | |
given a line object. In the old approach, the whole document array had | |
to be searched. Now, we can just walk up the tree and count the sizes | |
of the nodes coming before us at each level.</p> | |
<p>I chose B-trees, not regular binary trees, mostly because they | |
allow for very fast bulk insertions and deletions. When there is a big | |
change to a document, it typically involves adding, deleting, or | |
replacing a chunk of subsequent lines. In a regular balanced tree, all | |
these inserts or deletes would have to be done separately, which could | |
be really expensive. In a B-tree, to insert a chunk, you just walk | |
down the tree once to find where it should go, insert them all in one | |
shot, and then break up the node if needed. This breaking up might | |
involve breaking up nodes further up, but only requires a single pass | |
back up the tree. For deletion, I'm somewhat lax in keeping things | |
balanced—I just collapse nodes into a leaf when their child count goes | |
below a given number. This means that there are some weird editing | |
patterns that may result in a seriously unbalanced tree, but even such | |
an unbalanced tree will perform well, unless you spend a day making | |
strangely repeating edits to a really big document.</p> | |
<h2 id="keymap">Keymaps</h2> | |
<p><a href="#approach">Above</a>, I claimed that directly catching key | |
events for things like cursor movement is impractical because it | |
requires some browser-specific kludges. I then proceeded to explain | |
some awful <a href="#selection">hacks</a> that were needed to make it | |
possible for the selection changes to be detected through the | |
textarea. In fact, the second hack is about as bad as the first.</p> | |
<p>On top of that, in the presence of user-configurable tab sizes and | |
collapsed and wrapped lines, lining up cursor movement in the textarea | |
with what's visible on the screen becomes a nightmare. Thus, I've | |
decided to move to a model where the textarea's selection is no longer | |
depended on.</p> | |
<p>So I moved to a model where all cursor movement is handled by my | |
own code. This adds support for a goal column, proper interaction of | |
cursor movement with collapsed lines, and makes it possible for | |
vertical movement to move through wrapped lines properly, instead of | |
just treating them like non-wrapped lines.</p> | |
<p>The key event handlers now translate the key event into a string, | |
something like <code>Ctrl-Home</code> or <code>Shift-Cmd-R</code>, and | |
use that string to look up an action to perform. To make keybinding | |
customizable, this lookup goes through | |
a <a href="manual.html#option_keyMap">table</a>, using a scheme that | |
allows such tables to be chained together (for example, the default | |
Mac bindings fall through to a table named 'emacsy', which defines | |
basic Emacs-style bindings like <code>Ctrl-F</code>, and which is also | |
used by the custom Emacs bindings).</p> | |
<p>A new | |
option <a href="manual.html#option_extraKeys"><code>extraKeys</code></a> | |
allows ad-hoc keybindings to be defined in a much nicer way than what | |
was possible with the | |
old <a href="manual.html#option_onKeyEvent"><code>onKeyEvent</code></a> | |
callback. You simply provide an object mapping key identifiers to | |
functions, instead of painstakingly looking at raw key events.</p> | |
<p>Built-in commands map to strings, rather than functions, for | |
example <code>"goLineUp"</code> is the default action bound to the up | |
arrow key. This allows new keymaps to refer to them without | |
duplicating any code. New commands can be defined by assigning to | |
the <code>CodeMirror.commands</code> object, which maps such commands | |
to functions.</p> | |
<p>The hidden textarea now only holds the current selection, with no | |
extra characters around it. This has a nice advantage: polling for | |
input becomes much, much faster. If there's a big selection, this text | |
does not have to be read from the textarea every time—when we poll, | |
just noticing that something is still selected is enough to tell us | |
that no new text was typed.</p> | |
<p>The reason that cheap polling is important is that many browsers do | |
not fire useful events on IME (input method engine) input, which is | |
the thing where people inputting a language like Japanese or Chinese | |
use multiple keystrokes to create a character or sequence of | |
characters. Most modern browsers fire <code>input</code> when the | |
composing is finished, but many don't fire anything when the character | |
is updated <em>during</em> composition. So we poll, whenever the | |
editor is focused, to provide immediate updates of the display.</p> | |
</div><div class="rightsmall blk"> | |
<h2>Contents</h2> | |
<ul> | |
<li><a href="#intro">Introduction</a></li> | |
<li><a href="#approach">General Approach</a></li> | |
<li><a href="#input">Input</a></li> | |
<li><a href="#selection">Selection</a></li> | |
<li><a href="#update">Intelligent Updating</a></li> | |
<li><a href="#parse">Parsing</a></li> | |
<li><a href="#summary">What Gives?</a></li> | |
<li><a href="#btree">Content Representation</a></li> | |
<li><a href="#keymap">Key Maps</a></li> | |
</ul> | |
</div></div> | |
<div style="height: 2em"> </div> | |
</body></html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: User Manual</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<style>dl dl {margin: 0;}</style> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* User manual and | |
reference guide */ | |
</pre> | |
<div class="clear"><div class="leftbig blk"> | |
<h2 id="overview">Overview</h2> | |
<p>CodeMirror is a code-editor component that can be embedded in | |
Web pages. The code library provides <em>only</em> the editor | |
component, no accompanying buttons, auto-completion, or other IDE | |
functionality. It does provide a rich API on top of which such | |
functionality can be straightforwardly implemented. See | |
the <a href="#addons">add-ons</a> included in the distribution, | |
and | |
the <a href="http://www.octolabs.com/javascripts/codemirror-ui/">CodeMirror | |
UI</a> project, for reusable implementations of extra features.</p> | |
<p>CodeMirror works with language-specific modes. Modes are | |
JavaScript programs that help color (and optionally indent) text | |
written in a given language. The distribution comes with a few | |
modes (see the <code>mode/</code> directory), and it isn't hard | |
to <a href="#modeapi">write new ones</a> for other languages.</p> | |
<h2 id="usage">Basic Usage</h2> | |
<p>The easiest way to use CodeMirror is to simply load the script | |
and style sheet found under <code>lib/</code> in the distribution, | |
plus a mode script from one of the <code>mode/</code> directories | |
and a theme stylesheet from <code>theme/</code>. (See | |
also <a href="compress.html">the compression helper</a>.) For | |
example:</p> | |
<pre><script src="lib/codemirror.js"></script> | |
<link rel="stylesheet" href="../lib/codemirror.css"> | |
<script src="mode/javascript/javascript.js"></script></pre> | |
<p>Having done this, an editor instance can be created like | |
this:</p> | |
<pre>var myCodeMirror = CodeMirror(document.body);</pre> | |
<p>The editor will be appended to the document body, will start | |
empty, and will use the mode that we loaded. To have more control | |
over the new editor, a configuration object can be passed | |
to <code>CodeMirror</code> as a second argument:</p> | |
<pre>var myCodeMirror = CodeMirror(document.body, { | |
value: "function myScript(){return 100;}\n", | |
mode: "javascript" | |
});</pre> | |
<p>This will initialize the editor with a piece of code already in | |
it, and explicitly tell it to use the JavaScript mode (which is | |
useful when multiple modes are loaded). | |
See <a href="#config">below</a> for a full discussion of the | |
configuration options that CodeMirror accepts.</p> | |
<p>In cases where you don't want to append the editor to an | |
element, and need more control over the way it is inserted, the | |
first argument to the <code>CodeMirror</code> function can also | |
be a function that, when given a DOM element, inserts it into the | |
document somewhere. This could be used to, for example, replace a | |
textarea with a real editor:</p> | |
<pre>var myCodeMirror = CodeMirror(function(elt) { | |
myTextArea.parentNode.replaceChild(elt, myTextArea); | |
}, {value: myTextArea.value});</pre> | |
<p>However, for this use case, which is a common way to use | |
CodeMirror, the library provides a much more powerful | |
shortcut:</p> | |
<pre>var myCodeMirror = CodeMirror.fromTextArea(myTextArea);</pre> | |
<p>This will, among other things, ensure that the textarea's value | |
is updated when the form (if it is part of a form) is submitted. | |
See the <a href="#fromTextArea">API reference</a> for a full | |
description of this method.</p> | |
<h2 id="config">Configuration</h2> | |
<p>Both the <code>CodeMirror</code> function and | |
its <code>fromTextArea</code> method take as second (optional) | |
argument an object containing configuration options. Any option | |
not supplied like this will be taken | |
from <code>CodeMirror.defaults</code>, an object containing the | |
default options. You can update this object to change the defaults | |
on your page.</p> | |
<p>Options are not checked in any way, so setting bogus option | |
values is bound to lead to odd errors.</p> | |
<p>These are the supported options:</p> | |
<dl> | |
<dt id="option_value"><code>value (string)</code></dt> | |
<dd>The starting value of the editor.</dd> | |
<dt id="option_mode"><code>mode (string or object)</code></dt> | |
<dd>The mode to use. When not given, this will default to the | |
first mode that was loaded. It may be a string, which either | |
simply names the mode or is | |
a <a href="http://en.wikipedia.org/wiki/MIME">MIME</a> type | |
associated with the mode. Alternatively, it may be an object | |
containing configuration options for the mode, with | |
a <code>name</code> property that names the mode (for | |
example <code>{name: "javascript", json: true}</code>). The demo | |
pages for each mode contain information about what configuration | |
parameters the mode supports. You can ask CodeMirror which modes | |
and MIME types are loaded with | |
the <code>CodeMirror.listModes</code> | |
and <code>CodeMirror.listMIMEs</code> functions.</dd> | |
<dt id="option_theme"><code>theme (string)</code></dt> | |
<dd>The theme to style the editor with. You must make sure the | |
CSS file defining the corresponding <code>.cm-s-[name]</code> | |
styles is loaded (see | |
the <a href="../theme/"><code>theme</code></a> directory in the | |
distribution). The default is <code>"default"</code>, for which | |
colors are included in <code>codemirror.css</code>. It is | |
possible to use multiple theming classes at once—for | |
example <code>"foo bar"</code> will assign both | |
the <code>cm-s-foo</code> and the <code>cm-s-bar</code> classes | |
to the editor.</dd> | |
<dt id="option_indentUnit"><code>indentUnit (integer)</code></dt> | |
<dd>How many spaces a block (whatever that means in the edited | |
language) should be indented. The default is 2.</dd> | |
<dt id="option_tabSize"><code>tabSize (integer)</code></dt> | |
<dd>The width of a tab character. Defaults to 4.</dd> | |
<dt id="option_indentWithTabs"><code>indentWithTabs (boolean)</code></dt> | |
<dd>Whether, when indenting, the first N*<code>tabSize</code> | |
spaces should be replaced by N tabs. Default is false.</dd> | |
<dt id="option_electricChars"><code>electricChars (boolean)</code></dt> | |
<dd>Configures whether the editor should re-indent the current | |
line when a character is typed that might change its proper | |
indentation (only works if the mode supports indentation). | |
Default is true.</dd> | |
<dt id="option_keyMap"><code>keyMap (string)</code></dt> | |
<dd>Configures the keymap to use. The default | |
is <code>"default"</code>, which is the only keymap defined | |
in <code>codemirror.js</code> itself. Extra keymaps are found in | |
the <a href="../keymap/"><code>keymap</code></a> directory.</dd> | |
<dt id="option_extraKeys"><code>extraKeys (object)</code></dt> | |
<dd>Can be used to specify extra keybindings for the editor. | |
When given, should be an object with property names | |
like <code>Ctrl-A</code>, <code>Home</code>, | |
and <code>Ctrl-Alt-Left</code>. See | |
the <code>CodeMirror.keyNames</code> object for the names of all | |
the keys. The values in this object can either be functions, | |
which will be called with the CodeMirror instance when the key | |
is pressed, or strings, which should name commands defined | |
in <code>CodeMirror.commands</code> (not documented properly, | |
but looking at the source and the definition of the built-in | |
keymaps, they should be rather obvious).</dd> | |
<dt id="option_lineWrapping"><code>lineWrapping (boolean)</code></dt> | |
<dd>Whether CodeMirror should scroll or wrap for long lines. | |
Defaults to <code>false</code> (scroll).</dd> | |
<dt id="option_lineNumbers"><code>lineNumbers (boolean)</code></dt> | |
<dd>Whether to show line numbers to the left of the editor.</dd> | |
<dt id="option_firstLineNumber"><code>firstLineNumber (integer)</code></dt> | |
<dd>At which number to start counting lines. Default is 1.</dd> | |
<dt id="option_gutter"><code>gutter (boolean)</code></dt> | |
<dd>Can be used to force a 'gutter' (empty space on the left of | |
the editor) to be shown even when no line numbers are active. | |
This is useful for setting <a href="#setMarker">markers</a>.</dd> | |
<dt id="option_fixedGutter"><code>fixedGutter (boolean)</code></dt> | |
<dd>When enabled (off by default), this will make the gutter | |
stay visible when the document is scrolled horizontally.</dd> | |
<dt id="option_readOnly"><code>readOnly (boolean)</code></dt> | |
<dd>This disables editing of the editor content by the user.</dd> | |
<dt id="option_onChange"><code>onChange (function)</code></dt> | |
<dd>When given, this function will be called every time the | |
content of the editor is changed. It will be given the editor | |
instance as first argument, and an <code>{from, to, newText, | |
next}</code> object containing information about the changes | |
that occurred as second argument. <code>from</code> | |
and <code>to</code> are the positions (in the pre-change | |
coordinate system) where the change started and | |
ended. <code>newText</code> is an array of strings representing | |
the text that replaced the changed range (split by line). If | |
multiple changes happened during a single operation, the object | |
will have a <code>next</code> property pointing to another | |
change object (which may point to another, etc).</dd> | |
<dt id="option_onCursorActivity"><code>onCursorActivity (function)</code></dt> | |
<dd>Will be called when the cursor or selection moves, or any | |
change is made to the editor content.</dd> | |
<dt id="option_onGutterClick"><code>onGutterClick (function)</code></dt> | |
<dd>When given, will be called whenever the editor gutter (the | |
line-number area) is clicked. Will be given the editor instance | |
as first argument, the (zero-based) number of the line that was | |
clicked as second argument, and the raw <code>mousedown</code> | |
event object as third argument.</dd> | |
<dt id="option_onFocus"><code>onFocus, onBlur (function)</code></dt> | |
<dd>The given functions will be called whenever the editor is | |
focused or unfocused.</dd> | |
<dt id="option_onScroll"><code>onScroll (function)</code></dt> | |
<dd>When given, will be called whenever the editor is | |
scrolled.</dd> | |
<dt id="option_onHighlightComplete"><code>onHighlightComplete (function)</code></dt> | |
<dd>Whenever the editor's content has been fully highlighted, | |
this function (if given) will be called. It'll be given a single | |
argument, the editor instance.</dd> | |
<dt id="option_onUpdate"><code>onUpdate (function)</code></dt> | |
<dd>Will be called whenever CodeMirror updates its DOM display.</dd> | |
<dt id="option_matchBrackets"><code>matchBrackets (boolean)</code></dt> | |
<dd>Determines whether brackets are matched whenever the cursor | |
is moved next to a bracket.</dd> | |
<dt id="option_workTime"><code>workTime, workDelay (number)</code></dt> | |
<dd>Highlighting is done by a pseudo background-thread that will | |
work for <code>workTime</code> milliseconds, and then use | |
timeout to sleep for <code>workDelay</code> milliseconds. The | |
defaults are 200 and 300, you can change these options to make | |
the highlighting more or less aggressive.</dd> | |
<dt id="option_pollInterval"><code>pollInterval (number)</code></dt> | |
<dd>Indicates how quickly CodeMirror should poll its input | |
textarea for changes. Most input is captured by events, but some | |
things, like IME input on some browsers, doesn't generate events | |
that allow CodeMirror to properly detect it. Thus, it polls. | |
Default is 100 milliseconds.</dd> | |
<dt id="option_undoDepth"><code>undoDepth (integer)</code></dt> | |
<dd>The maximum number of undo levels that the editor stores. | |
Defaults to 40.</dd> | |
<dt id="option_tabindex"><code>tabindex (integer)</code></dt> | |
<dd>The <a href="http://www.w3.org/TR/html401/interact/forms.html#adef-tabindex">tab | |
index</a> to assign to the editor. If not given, no tab index | |
will be assigned.</dd> | |
<dt id="option_document"><code>document (DOM document)</code></dt> | |
<dd>Use this if you want to display the editor in another DOM. | |
By default it will use the global <code>document</code> | |
object.</dd> | |
<dt id="option_onKeyEvent"><code>onKeyEvent (function)</code></dt> | |
<dd>This provides a rather low-level hook into CodeMirror's key | |
handling. If provided, this function will be called on | |
every <code>keydown</code>, <code>keyup</code>, | |
and <code>keypress</code> event that CodeMirror captures. It | |
will be passed two arguments, the editor instance and the key | |
event. This key event is pretty much the raw key event, except | |
that a <code>stop()</code> method is always added to it. You | |
could feed it to, for example, <code>jQuery.Event</code> to | |
further normalize it.<br>This function can inspect the key | |
event, and handle it if it wants to. It may return true to tell | |
CodeMirror to ignore the event. Be wary that, on some browsers, | |
stopping a <code>keydown</code> does not stop | |
the <code>keypress</code> from firing, whereas on others it | |
does. If you respond to an event, you should probably inspect | |
its <code>type</code> property and only do something when it | |
is <code>keydown</code> (or <code>keypress</code> for actions | |
that need character data).</dd> | |
</dl> | |
<h2 id="styling">Customized Styling</h2> | |
<p>Up to a certain extent, CodeMirror's look can be changed by | |
modifying style sheet files. The style sheets supplied by modes | |
simply provide the colors for that mode, and can be adapted in a | |
very straightforward way. To style the editor itself, it is | |
possible to alter or override the styles defined | |
in <a href="../lib/codemirror.css"><code>codemirror.css</code></a>.</p> | |
<p>Some care must be taken there, since a lot of the rules in this | |
file are necessary to have CodeMirror function properly. Adjusting | |
colors should be safe, of course, and with some care a lot of | |
other things can be changed as well. The CSS classes defined in | |
this file serve the following roles:</p> | |
<dl> | |
<dt id="class_CodeMirror"><code>CodeMirror</code></dt> | |
<dd>The outer element of the editor. This should be used for | |
borders and positioning. Can also be used to set styles that | |
should hold for everything inside the editor (such as font | |
and font size), or to set a background.</dd> | |
<dt id="class_CodeMirror_scroll"><code>CodeMirror-scroll</code></dt> | |
<dd>This determines whether the editor scrolls (<code>overflow: | |
auto</code> + fixed height). By default, it does. Giving | |
this <code>height: auto; overflow: visible;</code> will cause | |
the editor to resize to fit its content.</dd> | |
<dt id="class_CodeMirror_focused"><code>CodeMirror-focused</code></dt> | |
<dd>Whenever the editor is focused, the top element gets this | |
class. This is used to hide the cursor and give the selection a | |
different color when the editor is not focused.</dd> | |
<dt id="class_CodeMirror_gutter"><code>CodeMirror-gutter</code></dt> | |
<dd>Use this for giving a background or a border to the editor | |
gutter. Don't set any padding here, | |
use <code>CodeMirror-gutter-text</code> for that. By default, | |
the gutter is 'fluid', meaning it will adjust its width to the | |
maximum line number or line marker width. You can also set a | |
fixed width if you want.</dd> | |
<dt id="class_CodeMirror_gutter_text"><code>CodeMirror-gutter-text</code></dt> | |
<dd>Used to style the actual line numbers. For the numbers to | |
line up, you must make sure that the font in the gutter is the | |
same as the one in the rest of the editor, so you should | |
probably only set font style and size in | |
the <code>CodeMirror</code> class.</dd> | |
<dt id="class_CodeMirror_lines"><code>CodeMirror-lines</code></dt> | |
<dd>The visible lines. If this has vertical | |
padding, <code>CodeMirror-gutter</code> should have the same | |
padding.</dd> | |
<dt id="class_CodeMirror_cursor"><code>CodeMirror-cursor</code></dt> | |
<dd>The cursor is a block element that is absolutely positioned. | |
You can make it look whichever way you want.</dd> | |
<dt id="class_CodeMirror_selected"><code>CodeMirror-selected</code></dt> | |
<dd>The selection is represented by <code>span</code> elements | |
with this class.</dd> | |
<dt id="class_CodeMirror_matchingbracket"><code>CodeMirror-matchingbracket</code>, | |
<code>CodeMirror-nonmatchingbracket</code></dt> | |
<dd>These are used to style matched (or unmatched) brackets.</dd> | |
</dl> | |
<p>The actual lines, as well as the cursor, are represented | |
by <code>pre</code> elements. By default no text styling (such as | |
bold) that might change line height is applied. If you do want | |
such effects, you'll have to give <code>CodeMirror pre</code> a | |
fixed height. Also, you must still take care that character width | |
is constant.</p> | |
<p>If your page's style sheets do funky things to | |
all <code>div</code> or <code>pre</code> elements (you probably | |
shouldn't do that), you'll have to define rules to cancel these | |
effects out again for elements under the <code>CodeMirror</code> | |
class.</p> | |
<p>Themes are also simply CSS files, which define colors for | |
various syntactic elements. See the files in | |
the <a href="../theme/"><code>theme</code></a> directory.</p> | |
<h2 id="api">Programming API</h2> | |
<p>A lot of CodeMirror features are only available through its API. | |
This has the disadvantage that you need to do work to enable them, | |
and the advantage that CodeMirror will fit seamlessly into your | |
application.</p> | |
<p>Whenever points in the document are represented, the API uses | |
objects with <code>line</code> and <code>ch</code> properties. | |
Both are zero-based. CodeMirror makes sure to 'clip' any positions | |
passed by client code so that they fit inside the document, so you | |
shouldn't worry too much about sanitizing your coordinates. If you | |
give <code>ch</code> a value of <code>null</code>, or don't | |
specify it, it will be replaced with the length of the specified | |
line.</p> | |
<dl> | |
<dt id="getValue"><code>getValue() → string</code></dt> | |
<dd>Get the current editor content.</dd> | |
<dt id="setValue"><code>setValue(string)</code></dt> | |
<dd>Set the editor content.</dd> | |
<dt id="getSelection"><code>getSelection() → string</code></dt> | |
<dd>Get the currently selected code.</dd> | |
<dt id="replaceSelection"><code>replaceSelection(string)</code></dt> | |
<dd>Replace the selection with the given string.</dd> | |
<dt id="focus"><code>focus()</code></dt> | |
<dd>Give the editor focus.</dd> | |
<dt id="setOption"><code>setOption(option, value)</code></dt> | |
<dd>Change the configuration of the editor. <code>option</code> | |
should the name of an <a href="#config">option</a>, | |
and <code>value</code> should be a valid value for that | |
option.</dd> | |
<dt id="getOption"><code>getOption(option) → value</code></dt> | |
<dd>Retrieves the current value of the given option for this | |
editor instance.</dd> | |
<dt id="cursorCoords"><code>cursorCoords(start) → object</code></dt> | |
<dd>Returns an <code>{x, y, yBot}</code> object containing the | |
coordinates of the cursor relative to the top-left corner of the | |
page. <code>yBot</code> is the coordinate of the bottom of the | |
cursor. <code>start</code> is a boolean indicating whether you | |
want the start or the end of the selection.</dd> | |
<dt id="charCoords"><code>charCoords(pos) → object</code></dt> | |
<dd>Like <code>cursorCoords</code>, but returns the position of | |
an arbitrary characters. <code>pos</code> should be | |
a <code>{line, ch}</code> object.</dd> | |
<dt id="coordsChar"><code>coordsChar(object) → pos</code></dt> | |
<dd>Given an <code>{x, y}</code> object (in page coordinates), | |
returns the <code>{line, ch}</code> position that corresponds to | |
it.</dd> | |
<dt id="undo"><code>undo()</code></dt> | |
<dd>Undo one edit (if any undo events are stored).</dd> | |
<dt id="redo"><code>redo()</code></dt> | |
<dd>Redo one undone edit.</dd> | |
<dt id="historySize"><code>historySize() → object</code></dt> | |
<dd>Returns an object with <code>{undo, redo}</code> properties, | |
both of which hold integers, indicating the amount of stored | |
undo and redo operations.</dd> | |
<dt id="clearHistory"><code>clearHistory()</code></dt> | |
<dd>Clears the editor's undo history.</dd> | |
<dt id="indentLine"><code>indentLine(line, dir)</code></dt> | |
<dd>Reset the given line's indentation to the indentation | |
prescribed by the mode. If the second argument is given, | |
indentation will be increased (if <code>dir</code> is true) or | |
decreased (if false) by an <a href="#option_indentUnit">indent | |
unit</a> instead.</dd> | |
<dt id="getTokenAt"><code>getTokenAt(pos) → object</code></dt> | |
<dd>Retrieves information about the token the current mode found | |
at the given position (a <code>{line, ch}</code> object). The | |
returned object has the following properties: | |
<dl> | |
<dt><code>start</code></dt><dd>The character (on the given line) at which the token starts.</dd> | |
<dt><code>end</code></dt><dd>The character at which the token ends.</dd> | |
<dt><code>string</code></dt><dd>The token's string.</dd> | |
<dt><code>className</code></dt><dd>The class the mode assigned | |
to the token. (Can be null when no class was assigned.)</dd> | |
<dt><code>state</code></dt><dd>The mode's state at the end of this token.</dd> | |
</dl></dd> | |
<dt id="markText"><code>markText(from, to, className) → object</code></dt> | |
<dd>Can be used to mark a range of text with a specific CSS | |
class name. <code>from</code> and <code>to</code> should | |
be <code>{line, ch}</code> objects. The method will return an | |
object with two methods, <code>clear()</code>, which removes the | |
mark, and <code>find()</code>, which returns a <code>{from, | |
to}</code> (both document positions), indicating the current | |
position of the marked range.</dd> | |
<dt id="setBookmark"><code>setBookmark(pos) → object</code></dt> | |
<dd>Inserts a bookmark, a handle that follows the text around it | |
as it is being edited, at the given position. A bookmark has two | |
methods <code>find()</code> and <code>clear()</code>. The first | |
returns the current position of the bookmark, if it is still in | |
the document, and the second explicitly removes the | |
bookmark.</dd> | |
<dt id="setMarker"><code>setMarker(line, text, className) → lineHandle</code></dt> | |
<dd>Add a gutter marker for the given line. Gutter markers are | |
shown in the line-number area (instead of the number for this | |
line). Both <code>text</code> and <code>className</code> are | |
optional. Setting <code>text</code> to a Unicode character like | |
● tends to give a nice effect. To put a picture in the gutter, | |
set <code>text</code> to a space and <code>className</code> to | |
something that sets a background image. If you | |
specify <code>text</code>, the given text (which may contain | |
HTML) will, by default, replace the line number for that line. | |
If this is not what you want, you can include the | |
string <code>%N%</code> in the text, which will be replaced by | |
the line number.</dd> | |
<dt id="clearMarker"><code>clearMarker(line)</code></dt> | |
<dd>Clears a marker created | |
with <code>setMarker</code>. <code>line</code> can be either a | |
number or a handle returned by <code>setMarker</code> (since a | |
number may now refer to a different line if something was added | |
or deleted).</dd> | |
<dt id="setLineClass"><code>setLineClass(line, className) → lineHandle</code></dt> | |
<dd>Set a CSS class name for the given line. <code>line</code> | |
can be a number or a line handle (as returned | |
by <code>setMarker</code> or this function). | |
Pass <code>null</code> to clear the class for a line.</dd> | |
<dt id="hideLine"><code>hideLine(line) → lineHandle</code></dt> | |
<dd>Hide the given line (either by number or by handle). Hidden | |
lines don't show up in the editor, and their numbers are skipped | |
when <a href="#option_lineNumbers">line numbers</a> are enabled. | |
Deleting a region around them does delete them, and coping a | |
region around will include them in the copied text.</dd> | |
<dt id="showLine"><code>showLine(line) → lineHandle</code></dt> | |
<dd>The inverse of <code>hideLine</code>—re-shows a previously | |
hidden line, by number or by handle.</dd> | |
<dt id="onDeleteLine"><code>onDeleteLine(line, func)</code></dt> | |
<dd>Register a function that should be called when the line is | |
deleted from the document.</dd> | |
<dt id="lineInfo"><code>lineInfo(line) → object</code></dt> | |
<dd>Returns the line number, text content, and marker status of | |
the given line, which can be either a number or a handle | |
returned by <code>setMarker</code>. The returned object has the | |
structure <code>{line, handle, text, markerText, markerClass}</code>.</dd> | |
<dt id="getLineHandle"><code>getLineHandle(num) → lineHandle</code></dt> | |
<dd>Fetches the line handle for the given line number.</dd> | |
<dt id="addWidget"><code>addWidget(pos, node, scrollIntoView)</code></dt> | |
<dd>Puts <code>node</code>, which should be an absolutely | |
positioned DOM node, into the editor, positioned right below the | |
given <code>{line, ch}</code> position. | |
When <code>scrollIntoView</code> is true, the editor will ensure | |
that the entire node is visible (if possible). To remove the | |
widget again, simply use DOM methods (move it somewhere else, or | |
call <code>removeChild</code> on its parent).</dd> | |
<dt id="matchBrackets"><code>matchBrackets()</code></dt> | |
<dd>Force matching-bracket-highlighting to happen.</dd> | |
<dt id="lineCount"><code>lineCount() → number</code></dt> | |
<dd>Get the number of lines in the editor.</dd> | |
<dt id="getCursor"><code>getCursor(start) → object</code></dt> | |
<dd><code>start</code> is a boolean indicating whether the start | |
or the end of the selection must be retrieved. If it is not | |
given, the current cursor pos, i.e. the side of the selection | |
that would move if you pressed an arrow key, is chosen. | |
A <code>{line, ch}</code> object will be returned.</dd> | |
<dt id="somethingSelected"><code>somethingSelected() → boolean</code></dt> | |
<dd>Return true if any text is selected.</dd> | |
<dt id="setCursor"><code>setCursor(pos)</code></dt> | |
<dd>Set the cursor position. You can either pass a | |
single <code>{line, ch}</code> object, or the line and the | |
character as two separate parameters.</dd> | |
<dt id="setSelection"><code>setSelection(start, end)</code></dt> | |
<dd>Set the selection range. <code>start</code> | |
and <code>end</code> should be <code>{line, ch}</code> objects.</dd> | |
<dt id="getLine"><code>getLine(n) → string</code></dt> | |
<dd>Get the content of line <code>n</code>.</dd> | |
<dt id="setLine"><code>setLine(n, text)</code></dt> | |
<dd>Set the content of line <code>n</code>.</dd> | |
<dt id="removeLine"><code>removeLine(n)</code></dt> | |
<dd>Remove the given line from the document.</dd> | |
<dt id="getRange"><code>getRange(from, to) → string</code></td> | |
<dd>Get the text between the given points in the editor, which | |
should be <code>{line, ch}</code> objects.</dd> | |
<dt id="replaceRange"><code>replaceRange(string, from, to)</code></dt> | |
<dd>Replace the part of the document between <code>from</code> | |
and <code>to</code> with the given string. <code>from</code> | |
and <code>to</code> must be <code>{line, ch}</code> | |
objects. <code>to</code> can be left off to simply insert the | |
string at position <code>from</code>.</dd> | |
<dt id="posFromIndex"><code>posFromIndex(index) → object</code></dt> | |
<dd>Calculates and returns a <code>{line, ch}</code> object for a | |
zero-based <code>index</code> who's value is relative to the start of the | |
editor's text. If the <code>index</code> is out of range of the text then | |
the returned object is clipped to start or end of the text | |
respectively.</dd> | |
<dt id="indexFromPos"><code>indexFromPos(object) → number</code></dt> | |
<dd>The reverse of <a href="#posFromIndex"><code>posFromIndex</code></a>.</dd> | |
</dl> | |
<p>The following are more low-level methods:</p> | |
<dl> | |
<dt id="operation"><code>operation(func) → result</code></dt> | |
<dd>CodeMirror internally buffers changes and only updates its | |
DOM structure after it has finished performing some operation. | |
If you need to perform a lot of operations on a CodeMirror | |
instance, you can call this method with a function argument. It | |
will call the function, buffering up all changes, and only doing | |
the expensive update after the function returns. This can be a | |
lot faster. The return value from this method will be the return | |
value of your function.</dd> | |
<dt id="refresh"><code>refresh()</code></dt> | |
<dd>If your code does something to change the size of the editor | |
element (window resizes are already listened for), or unhides | |
it, you should probably follow up by calling this method to | |
ensure CodeMirror is still looking as intended.</dd> | |
<dt id="getInputField"><code>getInputField() → textarea</code></dt> | |
<dd>Returns the hiden textarea used to read input.</dd> | |
<dt id="getWrapperElement"><code>getWrapperElement() → node</code></dt> | |
<dd>Returns the DOM node that represents the editor. Remove this | |
from your tree to delete an editor instance.</dd> | |
<dt id="getScrollerElement"><code>getScrollerElement() → node</code></dt> | |
<dd>Returns the DOM node that is responsible for the sizing and | |
the scrolling of the editor. You can change | |
the <code>height</code> and <code>width</code> styles of this | |
element to resize an editor. (You might have to call | |
the <a href="#refresh"><code>refresh</code></a> method | |
afterwards.)</dd> | |
<dt id="getGutterElement"><code>getGutterElement() → node</code></dt> | |
<dd>Fetches the DOM node that represents the editor gutter.</dd> | |
<dt id="getStateAfter"><code>getStateAfter(line) → state</code></dt> | |
<dd>Returns the mode's parser state, if any, at the end of the | |
given line number. If no line number is given, the state at the | |
end of the document is returned. This can be useful for storing | |
parsing errors in the state, or getting other kinds of | |
contextual information for a line.</dd> | |
</dl> | |
<p id="fromTextArea">Finally, the <code>CodeMirror</code> object | |
itself has a method <code>fromTextArea</code>. This takes a | |
textarea DOM node as first argument and an optional configuration | |
object as second. It will replace the textarea with a CodeMirror | |
instance, and wire up the form of that textarea (if any) to make | |
sure the editor contents are put into the textarea when the form | |
is submitted. A CodeMirror instance created this way has two | |
additional methods:</p> | |
<dl> | |
<dt id="save"><code>save()</code></dt> | |
<dd>Copy the content of the editor into the textarea.</dd> | |
<dt id="toTextArea"><code>toTextArea()</code></dt> | |
<dd>Remove the editor, and restore the original textarea (with | |
the editor's current content).</dd> | |
<dt id="getTextArea"><code>getTextArea() → textarea</code></dt> | |
<dd>Returns the textarea that the instance was based on.</dd> | |
</dl> | |
<p id="defineExtension">If you want to define extra methods in terms | |
of the CodeMirror API, it is possible to | |
use <code>CodeMirror.defineExtension(name, value)</code>. This | |
will cause the given value (usually a method) to be added to all | |
CodeMirror instances created from then on.</p> | |
<h2 id="addons">Add-ons</h2> | |
<p>The <code>lib/util</code> directory in the distribution | |
contains a number of reusable components that implement extra | |
editor functionality. In brief, they are:</p> | |
<dl> | |
<dt id="util_dialog"><a href="../lib/util/dialog.js"><code>dialog.js</code></a></dt> | |
<dd>Provides a very simple way to query users for text input. | |
Adds an <code>openDialog</code> method to CodeMirror instances, | |
which can be called with an HTML fragment that provides the | |
prompt (should include an <code>input</code> tag), and a | |
callback function that is called when text has been entered. | |
Depends on <code>lib/util/dialog.css</code>.</dd> | |
<dt id="util_searchcursor"><a href="../lib/util/searchcursor.js"><code>searchcursor.js</code></a></dt> | |
<dd>Adds the <code>getSearchCursor(query, start, caseFold) → | |
cursor</code> method to CodeMirror instances, which can be used | |
to implement search/replace functionality. <code>query</code> | |
can be a regular expression or a string (only strings will match | |
across lines—if they contain newlines). <code>start</code> | |
provides the starting position of the search. It can be | |
a <code>{line, ch}</code> object, or can be left off to default | |
to the start of the document. <code>caseFold</code> is only | |
relevant when matching a string. It will cause the search to be | |
case-insensitive. A search cursor has the following methods: | |
<dl> | |
<dt><code>findNext(), findPrevious() → boolean</code></dt> | |
<dd>Search forward or backward from the current position. | |
The return value indicates whether a match was found. If | |
matching a regular expression, the return value will be the | |
array returned by the <code>match</code> method, in case you | |
want to extract matched groups.</dd> | |
<dt><code>from(), to() → object</code></dt> | |
<dd>These are only valid when the last call | |
to <code>findNext</code> or <code>findPrevious</code> did | |
not return false. They will return <code>{line, ch}</code> | |
objects pointing at the start and end of the match.</dd> | |
<dt><code>replace(text)</code></dt> | |
<dd>Replaces the currently found match with the given text | |
and adjusts the cursor position to reflect the | |
replacement.</dd> | |
</dl></dd> | |
<dt id="util_search"><a href="../lib/util/search.js"><code>search.js</code></a></dt> | |
<dd>Implements the search commands. CodeMirror has keys bound to | |
these by default, but will not do anything with them unless an | |
implementation is provided. Depends | |
on <code>searchcursor.js</code>, and will make use | |
of <a href="#util_dialog"><code>openDialog</code></a> when | |
available to make prompting for search queries less ugly.</dd> | |
<dt id="util_foldcode"><a href="../lib/util/foldcode.js"><code>foldcode.js</code></a></dt> | |
<dd>Helps with code folding. See <a href="../demo/folding.html">the | |
demo</a> for an example. | |
Call <code>CodeMirror.newFoldFunction</code> with a range-finder | |
helper function to create a function that will, when applied to | |
a CodeMirror instance and a line number, attempt to fold or | |
unfold the block starting at the given line. A range-finder is a | |
language-specific functoin that also takes an instance and a | |
line number, and returns an end line for the block, or null if | |
no block is started on that line. This file | |
provides <code>CodeMirror.braceRangeFinder</code>, which finds | |
blocks in brace languages (JavaScript, C, Java, etc).</dd> | |
<dt id="util_runmode"><a href="../lib/util/runmode.js"><code>runmode.js</code></a></dt> | |
<dd>Can be used to run a CodeMirror mode over text without | |
actually opening an editor instance. | |
See <a href="../demo/runmode.html">the demo</a> for an | |
example.</dd> | |
<dt id="util_simple-hint"><a href="../lib/util/simple-hint.js"><code>simple-hint.js</code></a></dt> | |
<dd>Provides a framework for showing autocompletion hints. | |
Defines <code>CodeMirror.simpleHint</code>, which takes a | |
CodeMirror instance and a hinting function, and pops up a widget | |
that allows the user to select a completion. Hinting functions | |
are function that take an editor instance, and return | |
a <code>{list, from, to}</code> object, where <code>list</code> | |
is an array of strings (the completions), and <code>from</code> | |
and <code>to</code> give the start and end of the token that is | |
being completed. Depends | |
on <code>lib/util/simple-hint.css</code>.</dd> | |
<dt id="util_javascript-hint"><a href="../lib/util/javascript-hint.js"><code>javascript-hint.js</code></a></dt> | |
<dd>Defines <code>CodeMirror.javaScriptHint</code>, which is a | |
simple hinting function for the JavaScript mode.</dd> | |
</dl> | |
<h2 id="modeapi">Writing CodeMirror Modes</h2> | |
<p>Modes typically consist of a single JavaScript file. This file | |
defines, in the simplest case, a lexer (tokenizer) for your | |
language—a function that takes a character stream as input, | |
advances it past a token, and returns a style for that token. More | |
advanced modes can also handle indentation for the language.</p> | |
<p id="defineMode">The mode script should | |
call <code>CodeMirror.defineMode</code> to register itself with | |
CodeMirror. This function takes two arguments. The first should be | |
the name of the mode, for which you should use a lowercase string, | |
preferably one that is also the name of the files that define the | |
mode (i.e. <code>"xml"</code> is defined <code>xml.js</code>). The | |
second argument should be a function that, given a CodeMirror | |
configuration object (the thing passed to | |
the <code>CodeMirror</code> function) and an optional mode | |
configuration object (as in | |
the <a href="#option_mode"><code>mode</code></a> option), returns | |
a mode object.</p> | |
<p>Typically, you should use this second argument | |
to <code>defineMode</code> as your module scope function (modes | |
should not leak anything into the global scope!), i.e. write your | |
whole mode inside this function.</p> | |
<p>The main responsibility of a mode script is <em>parsing</em> | |
the content of the editor. Depending on the language and the | |
amount of functionality desired, this can be done in really easy | |
or extremely complicated ways. Some parsers can be stateless, | |
meaning that they look at one element (<em>token</em>) of the code | |
at a time, with no memory of what came before. Most, however, will | |
need to remember something. This is done by using a <em>state | |
object</em>, which is an object that is always passed when | |
reading a token, and which can be mutated by the tokenizer.</p> | |
<p id="startState">Modes that use a state must define | |
a <code>startState</code> method on their mode object. This is a | |
function of no arguments that produces a state object to be used | |
at the start of a document.</p> | |
<p id="token">The most important part of a mode object is | |
its <code>token(stream, state)</code> method. All modes must | |
define this method. It should read one token from the stream it is | |
given as an argument, optionally update its state, and return a | |
style string, or <code>null</code> for tokens that do not have to | |
be styled. For your styles, you can either use the 'standard' ones | |
defined in the themes (without the <code>cm-</code> prefix), or | |
define your own (as the <a href="../mode/diff/index.html">diff</a> | |
mode does) and have people include a custom CSS file for your | |
mode.<p> | |
<p id="StringStream">The stream object encapsulates a line of code | |
(tokens may never span lines) and our current position in that | |
line. It has the following API:</p> | |
<dl> | |
<dt><code>eol() → boolean</code></dt> | |
<dd>Returns true only if the stream is at the end of the | |
line.</dd> | |
<dt><code>sol() → boolean</code></dt> | |
<dd>Returns true only if the stream is at the start of the | |
line.</dd> | |
<dt><code>peek() → character</code></dt> | |
<dd>Returns the next character in the stream without advancing | |
it. Will return <code>undefined</code> at the end of the | |
line.</dd> | |
<dt><code>next() → character</code></dt> | |
<dd>Returns the next character in the stream and advances it. | |
Also returns <code>undefined</code> when no more characters are | |
available.</dd> | |
<dt><code>eat(match) → character</code></dt> | |
<dd><code>match</code> 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, <code>undefined</code> | |
is returned.</dd> | |
<dt><code>eatWhile(match) → boolean</code></dt> | |
<dd>Repeatedly calls <code>eat</code> with the given argument, | |
until it fails. Returns true if any characters were eaten.</dd> | |
<dt><code>eatSpace() → boolean</code></dt> | |
<dd>Shortcut for <code>eatWhile</code> when matching | |
white-space.</dd> | |
<dt><code>skipToEnd()</code></dt> | |
<dd>Moves the position to the end of the line.</dd> | |
<dt><code>skipTo(ch) → boolean</code></dt> | |
<dd>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.</dd> | |
<dt><code>match(pattern, consume, caseFold) → boolean</code></dt> | |
<dd>Act like a | |
multi-character <code>eat</code>—if <code>consume</code> is true | |
or not given—or a look-ahead that doesn't update the stream | |
position—if it is false. <code>pattern</code> can be either a | |
string or a regular expression starting with <code>^</code>. | |
When it is a string, <code>caseFold</code> 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 <code>match</code>, in case you need to extract | |
matched groups.</dd> | |
<dt><code>backUp(n)</code></dt> | |
<dd>Backs up the stream <code>n</code> characters. Backing it up | |
further than the start of the current token will cause things to | |
break, so be careful.</dd> | |
<dt><code>column() → integer</code></dt> | |
<dd>Returns the column (taking into account tabs) at which the | |
current token starts. Can be used to find out whether a token | |
starts a new line.</dd> | |
<dt><code>indentation() → integer</code></dt> | |
<dd>Tells you how far the current line has been indented, in | |
spaces. Corrects for tab characters.</dd> | |
<dt><code>current() → string</code></dt> | |
<dd>Get the string between the start of the current token and | |
the current stream position.</dd> | |
</dl> | |
<p id="blankLine">By default, blank lines are simply skipped when | |
tokenizing a document. For languages that have significant blank | |
lines, you can define a <code>blankLine(state)</code> method on | |
your mode that will get called whenever a blank line is passed | |
over, so that it can update the parser state.</p> | |
<p id="copyState">Because state object are mutated, and CodeMirror | |
needs to keep valid versions of a state around so that it can | |
restart a parse at any line, copies must be made of state objects. | |
The default algorithm used is that a new state object is created, | |
which gets all the properties of the old object. Any properties | |
which hold arrays get a copy of these arrays (since arrays tend to | |
be used as mutable stacks). When this is not correct, for example | |
because a mode mutates non-array properties of its state object, a | |
mode object should define a <code>copyState</code> method, | |
which is given a state and should return a safe copy of that | |
state.</p> | |
<p id="compareStates">By default, CodeMirror will stop re-parsing | |
a document as soon as it encounters a few lines that were | |
highlighted the same in the old parse as in the new one. It is | |
possible to provide an explicit way to test whether a state is | |
equivalent to another one, which CodeMirror will use (instead of | |
the unchanged-lines heuristic) to decide when to stop | |
highlighting. You do this by providing | |
a <code>compareStates</code> method on your mode object, which | |
takes two state arguments and returns a boolean indicating whether | |
they are equivalent. See the XML mode, which uses this to provide | |
reliable highlighting of bad closing tags, as an example.</p> | |
<p id="indent">If you want your mode to provide smart indentation | |
(though the <a href="#indentLine"><code>indentLine</code></a> | |
method and the <code>indentAuto</code> | |
and <code>newlineAndIndent</code> commands, which keys can be | |
<a href="#option_extraKeys">bound</a> to), you must define | |
an <code>indent(state, textAfter)</code> method on your mode | |
object.</p> | |
<p>The indentation method should inspect the given state object, | |
and optionally the <code>textAfter</code> string, which contains | |
the text on the line that is being indented, and return an | |
integer, the amount of spaces to indent. It should usually take | |
the <a href="#option_indentUnit"><code>indentUnit</code></a> | |
option into account.</p> | |
<p id="electricChars">Finally, a mode may define | |
an <code>electricChars</code> property, which should hold a string | |
containing all the characters that should trigger the behaviour | |
described for | |
the <a href="#option_electricChars"><code>electricChars</code></a> | |
option.</p> | |
<p>So, to summarize, a mode <em>must</em> provide | |
a <code>token</code> method, and it <em>may</em> | |
provide <code>startState</code>, <code>copyState</code>, | |
<code>compareStates</code>, and <code>indent</code> methods. For | |
an example of a trivial mode, see | |
the <a href="../mode/diff/diff.js">diff mode</a>, for a more involved | |
example, see the <a href="../mode/clike/clike.js">C-like | |
mode</a>.</p> | |
<p>Sometimes, it is useful for modes to <em>nest</em>—to have one | |
mode delegate work to another mode. An example of this kind of | |
mode is the <a href="../mode/htmlmixed/htmlmixed.js">mixed-mode HTML | |
mode</a>. To implement such nesting, it is usually necessary to | |
create mode objects and copy states yourself. To create a mode | |
object, there are <code>CodeMirror.getMode(options, | |
parserConfig)</code>, where the first argument is a configuration | |
object as passed to the mode constructor function, and the second | |
argument is a mode specification as in | |
the <a href="#option_mode"><code>mode</code></a> option. To copy a | |
state object, call <code>CodeMirror.copyState(mode, state)</code>, | |
where <code>mode</code> is the mode that created the given | |
state.</p> | |
<p>To make indentation work properly in a nested parser, it is | |
advisable to give the <code>startState</code> method of modes that | |
are intended to be nested an optional argument that provides the | |
base indentation for the block of code. The JavaScript and CSS | |
parser do this, for example, to allow JavaScript and CSS code | |
inside the mixed-mode HTML mode to be properly indented.</p> | |
<p>Finally, it is possible to associate your mode, or a certain | |
configuration of your mode, with | |
a <a href="http://en.wikipedia.org/wiki/MIME">MIME</a> type. For | |
example, the JavaScript mode associates itself | |
with <code>text/javascript</code>, and its JSON variant | |
with <code>application/json</code>. To do this, | |
call <code>CodeMirror.defineMIME(mime, modeSpec)</code>, | |
where <code>modeSpec</code> can be a string or object specifying a | |
mode, as in the <a href="#option_mode"><code>mode</code></a> | |
option.</p> | |
</div><div class="rightsmall blk"> | |
<h2>Contents</h2> | |
<ul> | |
<li><a href="#overview">Overview</a></li> | |
<li><a href="#usage">Basic Usage</a></li> | |
<li><a href="#config">Configuration</a></li> | |
<li><a href="#styling">Customized Styling</a></li> | |
<li><a href="#api">Programming API</a></li> | |
<li><a href="#addons">Add-ons</a></li> | |
<li><a href="#modeapi">Writing CodeMirror Modes</a></li> | |
</ul> | |
</div></div> | |
<div style="height: 2em"> </div> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<link rel="alternate" href="http://twitter.com/statuses/user_timeline/242283288.rss" type="application/rss+xml"/> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* Old release history */ | |
</pre> | |
<p class="rel">07-06-2011: <a href="http://codemirror.net/codemirror-2.1.zip">Version 2.1</a>:</p> | |
<p class="rel-note">Add | |
a <a href="manual.html#option_theme">theme</a> system | |
(<a href="../demo/theme.html">demo</a>). Note that this is not | |
backwards-compatible—you'll have to update your styles and | |
modes!</p> | |
<p class="rel">07-06-2011: <a href="http://codemirror.net/codemirror-2.02.zip">Version 2.02</a>:</p> | |
<ul class="rel-note"> | |
<li>Add a <a href="../mode/lua/index.html">Lua mode</a>.</li> | |
<li>Fix reverse-searching for a regexp.</li> | |
<li>Empty lines can no longer break highlighting.</li> | |
<li>Rework scrolling model (the outer wrapper no longer does the scrolling).</li> | |
<li>Solve horizontal jittering on long lines.</li> | |
<li>Add <a href="../demo/runmode.html">runmode.js</a>.</li> | |
<li>Immediately re-highlight text when typing.</li> | |
<li>Fix problem with 'sticking' horizontal scrollbar.</li> | |
</ul> | |
<p class="rel">26-05-2011: <a href="http://codemirror.net/codemirror-2.01.zip">Version 2.01</a>:</p> | |
<ul class="rel-note"> | |
<li>Add a <a href="../mode/smalltalk/index.html">Smalltalk mode</a>.</li> | |
<li>Add a <a href="../mode/rst/index.html">reStructuredText mode</a>.</li> | |
<li>Add a <a href="../mode/python/index.html">Python mode</a>.</li> | |
<li>Add a <a href="../mode/plsql/index.html">PL/SQL mode</a>.</li> | |
<li><code>coordsChar</code> now works</li> | |
<li>Fix a problem where <code>onCursorActivity</code> interfered with <code>onChange</code>.</li> | |
<li>Fix a number of scrolling and mouse-click-position glitches.</li> | |
<li>Pass information about the changed lines to <code>onChange</code>.</li> | |
<li>Support cmd-up/down on OS X.</li> | |
<li>Add triple-click line selection.</li> | |
<li>Don't handle shift when changing the selection through the API.</li> | |
<li>Support <code>"nocursor"</code> mode for <code>readOnly</code> option.</li> | |
<li>Add an <code>onHighlightComplete</code> option.</li> | |
<li>Fix the context menu for Firefox.</li> | |
</ul> | |
<p class="rel">28-03-2011: <a href="http://codemirror.net/codemirror-2.0.zip">Version 2.0</a>:</p> | |
<p class="rel-note">CodeMirror 2 is a complete rewrite that's | |
faster, smaller, simpler to use, and less dependent on browser | |
quirks. See <a href="internals.html">this</a> | |
and <a href="http://groups.google.com/group/codemirror/browse_thread/thread/5a8e894024a9f580">this</a> | |
for more information.</a> | |
<p class="rel">28-03-2011: <a href="http://codemirror.net/codemirror-1.0.zip">Version 1.0</a>:</p> | |
<ul class="rel-note"> | |
<li>Fix error when debug history overflows.</li> | |
<li>Refine handling of C# verbatim strings.</li> | |
<li>Fix some issues with JavaScript indentation.</li> | |
</ul> | |
<p class="rel">22-02-2011: <a href="https://github.com/marijnh/codemirror2/tree/beta2">Version 2.0 beta 2</a>:</p> | |
<p class="rel-note">Somewhate more mature API, lots of bugs shaken out.</a> | |
<p class="rel">17-02-2011: <a href="http://codemirror.net/codemirror-0.94.zip">Version 0.94</a>:</p> | |
<ul class="rel-note"> | |
<li><code>tabMode: "spaces"</code> was modified slightly (now indents when something is selected).</li> | |
<li>Fixes a bug that would cause the selection code to break on some IE versions.</li> | |
<li>Disabling spell-check on WebKit browsers now works.</li> | |
</ul> | |
<p class="rel">08-02-2011: <a href="http://codemirror.net/">Version 2.0 beta 1</a>:</p> | |
<p class="rel-note">CodeMirror 2 is a complete rewrite of | |
CodeMirror, no longer depending on an editable frame.</p> | |
<p class="rel">19-01-2011: <a href="http://codemirror.net/codemirror-0.93.zip">Version 0.93</a>:</p> | |
<ul class="rel-note"> | |
<li>Added a <a href="contrib/regex/index.html">Regular Expression</a> parser.</li> | |
<li>Fixes to the PHP parser.</li> | |
<li>Support for regular expression in search/replace.</li> | |
<li>Add <code>save</code> method to instances created with <code>fromTextArea</code>.</li> | |
<li>Add support for MS T-SQL in the SQL parser.</li> | |
<li>Support use of CSS classes for highlighting brackets.</li> | |
<li>Fix yet another hang with line-numbering in hidden editors.</li> | |
</ul> | |
<p class="rel">17-12-2010: <a href="http://codemirror.net/codemirror-0.92.zip">Version 0.92</a>:</p> | |
<ul class="rel-note"> | |
<li>Make CodeMirror work in XHTML documents.</li> | |
<li>Fix bug in handling of backslashes in Python strings.</li> | |
<li>The <code>styleNumbers</code> option is now officially | |
supported and documented.</li> | |
<li><code>onLineNumberClick</code> option added.</li> | |
<li>More consistent names <code>onLoad</code> and | |
<code>onCursorActivity</code> callbacks. Old names still work, but | |
are deprecated.</li> | |
<li>Add a <a href="contrib/freemarker/index.html">Freemarker</a> mode.</li> | |
</ul> | |
<p class="rel">11-11-2010: <a | |
href="http://codemirror.net/codemirror-0.91.zip">Version 0.91</a>:</p> | |
<ul class="rel-note"> | |
<li>Adds support for <a href="contrib/java">Java</a>.</li> | |
<li>Small additions to the <a href="contrib/php">PHP</a> and <a href="contrib/sql">SQL</a> parsers.</li> | |
<li>Work around various <a href="https://bugs.webkit.org/show_bug.cgi?id=47806">Webkit</a> <a href="https://bugs.webkit.org/show_bug.cgi?id=23474">issues</a>.</li> | |
<li>Fix <code>toTextArea</code> to update the code in the textarea.</li> | |
<li>Add a <code>noScriptCaching</code> option (hack to ease development).</li> | |
<li>Make sub-modes of <a href="mixedtest.html">HTML mixed</a> mode configurable.</li> | |
</ul> | |
<p class="rel">02-10-2010: <a | |
href="http://codemirror.net/codemirror-0.9.zip">Version 0.9</a>:</p> | |
<ul class="rel-note"> | |
<li>Add support for searching backwards.</li> | |
<li>There are now parsers for <a href="contrib/scheme/index.html">Scheme</a>, <a href="contrib/xquery/index.html">XQuery</a>, and <a href="contrib/ometa/index.html">OmetaJS</a>.</li> | |
<li>Makes <code>height: "dynamic"</code> more robust.</li> | |
<li>Fixes bug where paste did not work on OS X.</li> | |
<li>Add a <code>enterMode</code> and <code>electricChars</code> options to make indentation even more customizable.</li> | |
<li>Add <code>firstLineNumber</code> option.</li> | |
<li>Fix bad handling of <code>@media</code> rules by the CSS parser.</li> | |
<li>Take a new, more robust approach to working around the invisible-last-line bug in WebKit.</li> | |
</ul> | |
<p class="rel">22-07-2010: <a | |
href="http://codemirror.net/codemirror-0.8.zip">Version 0.8</a>:</p> | |
<ul class="rel-note"> | |
<li>Add a <code>cursorCoords</code> method to find the screen | |
coordinates of the cursor.</li> | |
<li>A number of fixes and support for more syntax in the PHP parser.</li> | |
<li>Fix indentation problem with JSON-mode JS parser in Webkit.</li> | |
<li>Add a <a href="compress.html">minification</a> UI.</li> | |
<li>Support a <code>height: dynamic</code> mode, where the editor's | |
height will adjust to the size of its content.</li> | |
<li>Better support for IME input mode.</li> | |
<li>Fix JavaScript parser getting confused when seeing a no-argument | |
function call.</li> | |
<li>Have CSS parser see the difference between selectors and other | |
identifiers.</li> | |
<li>Fix scrolling bug when pasting in a horizontally-scrolled | |
editor.</li> | |
<li>Support <code>toTextArea</code> method in instances created with | |
<code>fromTextArea</code>.</li> | |
<li>Work around new Opera cursor bug that causes the cursor to jump | |
when pressing backspace at the end of a line.</li> | |
</ul> | |
<p class="rel">27-04-2010: <a | |
href="http://codemirror.net/codemirror-0.67.zip">Version | |
0.67</a>:</p> | |
<p class="rel-note">More consistent page-up/page-down behaviour | |
across browsers. Fix some issues with hidden editors looping forever | |
when line-numbers were enabled. Make PHP parser parse | |
<code>"\\"</code> correctly. Have <code>jumpToLine</code> work on | |
line handles, and add <code>cursorLine</code> function to fetch the | |
line handle where the cursor currently is. Add new | |
<code>setStylesheet</code> function to switch style-sheets in a | |
running editor.</p> | |
<p class="rel">01-03-2010: <a | |
href="http://codemirror.net/codemirror-0.66.zip">Version | |
0.66</a>:</p> | |
<p class="rel-note">Adds <code>removeLine</code> method to API. | |
Introduces the <a href="contrib/plsql/index.html">PLSQL parser</a>. | |
Marks XML errors by adding (rather than replacing) a CSS class, so | |
that they can be disabled by modifying their style. Fixes several | |
selection bugs, and a number of small glitches.</p> | |
<p class="rel">12-11-2009: <a | |
href="http://codemirror.net/codemirror-0.65.zip">Version | |
0.65</a>:</p> | |
<p class="rel-note">Add support for having both line-wrapping and | |
line-numbers turned on, make paren-highlighting style customisable | |
(<code>markParen</code> and <code>unmarkParen</code> config | |
options), work around a selection bug that Opera | |
<em>re</em>introduced in version 10.</p> | |
<p class="rel">23-10-2009: <a | |
href="http://codemirror.net/codemirror-0.64.zip">Version | |
0.64</a>:</p> | |
<p class="rel-note">Solves some issues introduced by the | |
paste-handling changes from the previous release. Adds | |
<code>setSpellcheck</code>, <code>setTextWrapping</code>, | |
<code>setIndentUnit</code>, <code>setUndoDepth</code>, | |
<code>setTabMode</code>, and <code>setLineNumbers</code> to | |
customise a running editor. Introduces an <a | |
href="contrib/sql/index.html">SQL</a> parser. Fixes a few small | |
problems in the <a href="contrib/python/index.html">Python</a> | |
parser. And, as usual, add workarounds for various newly discovered | |
browser incompatibilities.</p> | |
<p class="rel"><em>31-08-2009</em>: <a | |
href="http://codemirror.net/codemirror-0.63.zip">Version | |
0.63</a>:</p> | |
<p class="rel-note"> Overhaul of paste-handling (less fragile), fixes for several | |
serious IE8 issues (cursor jumping, end-of-document bugs) and a number | |
of small problems.</p> | |
<p class="rel"><em>30-05-2009</em>: <a | |
href="http://codemirror.net/codemirror-0.62.zip">Version | |
0.62</a>:</p> | |
<p class="rel-note">Introduces <a href="contrib/python/index.html">Python</a> | |
and <a href="contrib/lua/index.html">Lua</a> parsers. Add | |
<code>setParser</code> (on-the-fly mode changing) and | |
<code>clearHistory</code> methods. Make parsing passes time-based | |
instead of lines-based (see the <code>passTime</code> option).</p> | |
</body></html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Reporting Bugs</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<style>li { margin-top: 1em; }</style> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* Reporting bugs | |
effectively */ | |
</pre> | |
<div class="left"> | |
<p>So you found a problem in CodeMirror. By all means, report it! Bug | |
reports from users are the main drive behind improvements to | |
CodeMirror. But first, please read over these points:</p> | |
<ol> | |
<li>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.</li> | |
<li>Include information about <strong>the browser in which the | |
problem occurred</strong>. 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.</li> | |
<li>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.</li> | |
<li>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.</li> | |
<li>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 <a href="http://jsbin.com/ihunin/edit">jsbin.com</a>, enter | |
it there, press save, and include the resulting link in your bug | |
report.</li> | |
</ol> | |
</div> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Upgrading to v2.2</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="baboon.png" class="logo" alt="logo"/>/* Upgrading to v2.2 | |
*/ | |
</pre> | |
<div class="left"> | |
<p>There are a few things in the 2.2 release that require some care | |
when upgrading.</p> | |
<h2>No more default.css</h2> | |
<p>The default theme is now included | |
in <a href="../lib/codemirror.css"><code>codemirror.css</code></a>, so | |
you do not have to included it separately anymore. (It was tiny, so | |
even if you're not using it, the extra data overhead is negligible.) | |
<h2>Different key customization</h2> | |
<p>CodeMirror has moved to a system | |
where <a href="manual.html#option_keyMap">keymaps</a> are used to | |
bind behavior to keys. This means <a href="../demo/emacs.html">custom | |
bindings</a> are now possible.</p> | |
<p>Three options that influenced key | |
behavior, <code>tabMode</code>, <code>enterMode</code>, | |
and <code>smartHome</code>, are no longer supported. Instead, you can | |
provide custom bindings to influence the way these keys act. This is | |
done through the | |
new <a href="manual.html#option_extraKeys"><code>extraKeys</code></a> | |
option, which can hold an object mapping key names to functionality. A | |
simple example would be:</p> | |
<pre> extraKeys: { | |
"Ctrl-S": function(instance) { saveText(instance.getValue()); }, | |
"Ctrl-/": "undo" | |
}</pre> | |
<p>Keys can be mapped either to functions, which will be given the | |
editor instance as argument, or to strings, which are mapped through | |
functions through the <code>CodeMirror.commands</code> table, which | |
contains all the built-in editing commands, and can be inspected and | |
extended by external code.</p> | |
<p>By default, the <code>Home</code> key is bound to | |
the <code>"goLineStartSmart"</code> command, which moves the cursor to | |
the first non-whitespace character on the line. You can set do this to | |
make it always go to the very start instead:</p> | |
<pre> extraKeys: {"Home": "goLineStart"}</pre> | |
<p>Similarly, <code>Enter</code> is bound | |
to <code>"newLineAndIndent"</code> by default. You can bind it to | |
something else to get different behavior. To disable special handling | |
completely and only get a newline character inserted, you can bind it | |
to <code>false</code>:</p> | |
<pre> extraKeys: {"Enter": false}</pre> | |
<p>The same works for <code>Tab</code>. If you don't want CodeMirror | |
to handle it, bind it to <code>false</code>. The default behaviour is | |
to indent the current line more (<code>"indentMore"</code> command), | |
and indent it less when shift is held (<code>"indentLess"</code>). | |
There are also <code>"indentAuto"</code> (smart indent) | |
and <code>"insertTab"</code> commands provided for alternate | |
behaviors. Or you can write your own handler function to do something | |
different altogether.</p> | |
<h2>Tabs</h2> | |
<p>Handling of tabs changed completely. The display width of tabs can | |
now be set with the <code>tabSize</code> option, and tabs can | |
be <a href="../demo/visibletabs.html">styled</a> by setting CSS rules | |
for the <code>cm-tab</code> class.</p> | |
<p>The default width for tabs is now 4, as opposed to the 8 that is | |
hard-wired into browsers. If you are relying on 8-space tabs, make | |
sure you explicitly set <code>tabSize: 8</code> in your options.</p> | |
</div> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror</title> | |
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans|Droid+Sans:bold"/> | |
<link rel="stylesheet" type="text/css" href="doc/docs.css"/> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> | |
<link rel="alternate" href="http://twitter.com/statuses/user_timeline/242283288.rss" type="application/rss+xml"/> | |
</head> | |
<body> | |
<h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMirror</a></h1> | |
<pre class="grey"> | |
<img src="doc/baboon.png" class="logo" alt="logo"/>/* In-browser code editing | |
made bearable */ | |
</pre> | |
<div class="clear"><div class="left blk"> | |
<p style="margin-top: 0">CodeMirror is a JavaScript library that can | |
be used to create a relatively pleasant editor interface for | |
code-like content ― computer programs, HTML markup, and | |
similar. If a mode has been written for the language you are | |
editing, the code will be coloured, and the editor will optionally | |
help you with indentation.</p> | |
<p>This is the project page for CodeMirror 2, the currently more | |
actively developed, and recommended | |
version. <a href="1/index.html">CodeMirror 1</a> is still available | |
from here.</p> | |
<div class="clear"><div class="left1 blk"> | |
<h2 style="margin-top: 0">Supported modes:</h2> | |
<ul> | |
<li><a href="mode/clike/index.html">C, Java, C#, and similar</a></li> | |
<li><a href="mode/clojure/index.html">Clojure</a></li> | |
<li><a href="mode/coffeescript/index.html">CoffeeScript</a></li> | |
<li><a href="mode/css/index.html">CSS</a></li> | |
<li><a href="mode/diff/index.html">diff</a></li> | |
<li><a href="mode/groovy/index.html">Groovy</a></li> | |
<li><a href="mode/haskell/index.html">Haskell</a></li> | |
<li><a href="mode/htmlembedded/index.html">HTML embedded scripts</a></li> | |
<li><a href="mode/htmlmixed/index.html">HTML mixed-mode</a></li> | |
<li><a href="mode/javascript/index.html">JavaScript</a></li> | |
<li><a href="mode/jinja2/index.html">Jinja2</a></li> | |
<li><a href="mode/lua/index.html">Lua</a></li> | |
<li><a href="mode/markdown/index.html">Markdown</a> (<a href="mode/gfm/index.html">Github-flavour</a>)</li> | |
<li><a href="mode/ntriples/index.html">NTriples</a></li> | |
<li><a href="mode/pascal/index.html">Pascal</a></li> | |
<li><a href="mode/perl/index.html">Perl</a></li> | |
<li><a href="mode/php/index.html">PHP</a></li> | |
<li><a href="mode/plsql/index.html">PL/SQL</a></li> | |
<li><a href="mode/python/index.html">Python</a></li> | |
<li><a href="mode/r/index.html">R</a></li> | |
<li>RPM <a href="mode/rpm/spec/index.html">spec</a> and <a href="mode/rpm/changes/index.html">changelog</a></li> | |
<li><a href="mode/rst/index.html">reStructuredText</a></li> | |
<li><a href="mode/ruby/index.html">Ruby</a></li> | |
<li><a href="mode/rust/index.html">Rust</a></li> | |
<li><a href="mode/scheme/index.html">Scheme</a></li> | |
<li><a href="mode/smalltalk/index.html">Smalltalk</a></li> | |
<li><a href="mode/sparql/index.html">SPARQL</a></li> | |
<li><a href="mode/stex/index.html">sTeX, LaTeX</a></li> | |
<li><a href="mode/tiddlywiki/index.html">Tiddlywiki</a></li> | |
<li><a href="mode/velocity/index.html">Velocity</a></li> | |
<li><a href="mode/xml/index.html">XML/HTML</a> (<a href="mode/xmlpure/index.html">alternative XML</a>)</li> | |
<li><a href="mode/yaml/index.html">YAML</a></li> | |
</ul> | |
</div><div class="left2 blk"> | |
<h2 style="margin-top: 0">Usage demos:</h2> | |
<ul> | |
<li><a href="demo/complete.html">Autocompletion</a></li> | |
<li><a href="demo/mustache.html">Mode overlays</a></li> | |
<li><a href="demo/search.html">Search/replace</a></li> | |
<li><a href="demo/folding.html">Code folding</a></li> | |
<li><a href="demo/preview.html">HTML editor with preview</a></li> | |
<li><a href="demo/resize.html">Auto-resizing editor</a></li> | |
<li><a href="demo/marker.html">Setting breakpoints</a></li> | |
<li><a href="demo/activeline.html">Highlighting the current line</a></li> | |
<li><a href="demo/theme.html">Theming</a></li> | |
<li><a href="demo/runmode.html">Stand-alone highlighting</a></li> | |
<li><a href="demo/fullscreen.html">Full-screen editing</a></li> | |
<li><a href="demo/changemode.html">Mode auto-changing</a></li> | |
<li><a href="demo/visibletabs.html">Visible tabs</a></li> | |
<li><a href="demo/formatting.html">Autoformatting of code</a></li> | |
<li><a href="demo/emacs.html">Emacs keybindings</a></li> | |
<li><a href="demo/vim.html">Vim keybindings</a></li> | |
</ul> | |
<h2>Real-world uses:</h2> | |
<ul> | |
<li><a href="http://jsbin.com">jsbin.com</a> (JS playground)</li> | |
<li><a href="http://buzzard.ups.edu/">Sage demo</a> (math system)</li> | |
<li><a href="http://www.sourcelair.com/">sourceLair</a> (online IDE)</li> | |
<li><a href="http://eloquentjavascript.net/chapter1.html">Eloquent JavaScript</a> (book)</a></li> | |
<li><a href="http://www.mergely.com/">Mergely</a> (interactive diffing)</li> | |
<li><a href="http://paperjs.org/">Paper.js</a> (graphics scripting)</li> | |
<li><a href="http://www.wescheme.org/">WeScheme</a> (learning tool)</li> | |
<li><a href="http://webglplayground.net/">WebGL playground</a></li> | |
<li><a href="http://ql.io/">ql.io</a> (http API query helper)</li> | |
<li><a href="http://elm-lang.org/Examples.elm">Elm language examples</a></li> | |
</ul> | |
</div></div> | |
<h2 id="code">Getting the code</h2> | |
<p>All of CodeMirror is released under a <a | |
href="LICENSE">MIT-style</a> license. To get it, you can download | |
the <a href="http://codemirror.net/codemirror.zip">latest | |
release</a> or the current <a | |
href="http://codemirror.net/codemirror2-latest.zip">development | |
snapshot</a> as zip files. To create a custom minified script file, | |
you can use the <a href="doc/compress.html">compression API</a>.</p> | |
<p>We use <a href="http://git-scm.com/">git</a> for version control. | |
The main repository can be fetched in this way:</p> | |
<pre class="code">git clone http://marijnhaverbeke.nl/git/codemirror2</pre> | |
<p>CodeMirror can also be found on GitHub at <a | |
href="http://github.com/marijnh/CodeMirror2">marijnh/CodeMirror2</a>. | |
If you plan to hack on the code and contribute patches, the best way | |
to do it is to create a GitHub fork, and send pull requests.</p> | |
<h2 id="documention">Documentation</h2> | |
<p>The <a href="doc/manual.html">manual</a> is your first stop for | |
learning how to use this library. It starts with a quick explanation | |
of how to use the editor, and then describes the API in detail.</p> | |
<p>For those who want to learn more about the code, there is | |
an <a href="doc/internals.html">overview of the internals</a> available. | |
The <a href="http://github.com/marijnh/CodeMirror2">source code</a> | |
itself is, for the most part, also well commented.</p> | |
<h2 id="support">Support and bug reports</h2> | |
<p>There is | |
a <a href="http://groups.google.com/group/codemirror">Google | |
group</a> (a sort of mailing list/newsgroup thing) for discussion | |
and news related to CodeMirror. When reporting a bug, | |
<a href="doc/reporting.html">read this first</a>. If you have | |
a <a href="http://github.com">github</a> account, | |
simply <a href="http://github.com/marijnh/CodeMirror2/issues">open | |
an issue there</a>. Otherwise, post something to | |
the <a href="http://groups.google.com/group/codemirror">group</a>, | |
or e-mail me directly: <a href="mailto:marijnh@gmail.com">Marijn | |
Haverbeke</a>.</p> | |
<h2 id="supported">Supported browsers</h2> | |
<p>The following browsers are able to run CodeMirror:</p> | |
<ul> | |
<li>Firefox 2 or higher</li> | |
<li>Chrome, any version</li> | |
<li>Safari 3 or higher</li> | |
<li>Internet Explorer 6 or higher</li> | |
<li>Opera 9 or higher (with some key-handling problems on OS X)</li> | |
</ul> | |
<p>I am not actively testing against every new browser release, and | |
vendors have a habit of introducing bugs all the time, so I am | |
relying on the community to tell me when something breaks. | |
See <a href="#support">here</a> for information on how to contact | |
me.</p> | |
<h2 id="commercial">Commercial support</h2> | |
<p>CodeMirror is developed and maintained by me, Marijn Haverbeke, | |
in my own time. If your company is getting value out of CodeMirror, | |
please consider purchasing a support contract.</p> | |
<ul> | |
<li>You'll be funding further work on CodeMirror.</li> | |
<li>You ensure that you get a quick response when you have a | |
problem, even when I am otherwise busy.</li> | |
</ul> | |
<p>CodeMirror support contracts exist in two | |
forms—<strong>basic</strong> at €100 per month, | |
and <strong>premium</strong> at €500 per | |
month. <a href="mailto:marijnh@gmail.com">Contact me</a> for further | |
information.</p> | |
</div> | |
<div class="right blk"> | |
<a href="http://codemirror.net/codemirror.zip" class="download">Download the latest release</a> | |
<h2>Support CodeMirror</h2> | |
<ul> | |
<li>Donate | |
(<span onclick="document.getElementById('paypal').submit();" | |
class="quasilink">Paypal</span> | |
or <span onclick="document.getElementById('bankinfo').style.display = 'block';" | |
class="quasilink">bank</span>)</li> | |
<li>Purchase <a href="#commercial">commercial support</a></li> | |
</ul> | |
<p id="bankinfo" style="display: none;"> | |
Bank: <i>Rabobank</i><br/> | |
Country: <i>Netherlands</i><br/> | |
SWIFT: <i>RABONL2U</i><br/> | |
Account: <i>147850770</i><br/> | |
Name: <i>Marijn Haverbeke</i><br/> | |
IBAN: <i>NL26 RABO 0147 8507 70</i> | |
</p> | |
<h2>Releases:</h2> | |
<p class="rel">20-12-2011: <a href="http://codemirror.net/codemirror-2.2.zip">Version 2.2</a>:</p> | |
<ul class="rel-note"> | |
<li>Slightly incompatible API changes. Read <a href="doc/upgrade_v2.2.html">this</a>.</li> | |
<li>New approach | |
to <a href="doc/manual.html#option_extraKeys">binding</a> keys, | |
support for <a href="doc/manual.html#option_keyMap">custom | |
bindings</a>.</li> | |
<li>Support for overwrite (insert).</li> | |
<li><a href="doc/manual.html#option_tabSize">Custom-width</a> | |
and <a href="demo/visibletabs.html">stylable</a> tabs.</li> | |
<li>Moved more code into <a href="doc/manual.html#addons">add-on scripts</a>.</li> | |
<li>Support for sane vertical cursor movement in wrapped lines.</li> | |
<li>More reliable handling of | |
editing <a href="doc/manual.html#markText">marked text</a>.</li> | |
<li>Add minimal <a href="demo/emacs.html">emacs</a> | |
and <a href="demo/vim.html">vim</a> bindings.</li> | |
<li>Rename <code>coordsFromIndex</code> | |
to <a href="doc/manual.html#posFromIndex"><code>posFromIndex</code></a>, | |
add <a href="doc/manual.html#indexFromPos"><code>indexFromPos</code></a> | |
method.</li> | |
</ul> | |
<p class="rel">21-11-2011: <a href="http://codemirror.net/codemirror-2.18.zip">Version 2.18</a>:</p> | |
<p class="rel-note">Fixes <code>TextMarker.clear</code>, which is broken in 2.17.</p> | |
<p class="rel">21-11-2011: <a href="http://codemirror.net/codemirror-2.17.zip">Version 2.17</a>:</p> | |
<ul class="rel-note"> | |
<li>Add support for <a href="doc/manual.html#option_lineWrapping">line | |
wrapping</a> and <a href="doc/manual.html#hideLine">code | |
folding</a>.</li> | |
<li>Add <a href="mode/gfm/index.html">Github-style Markdown</a> mode.</li> | |
<li>Add <a href="theme/monokai.css">Monokai</a> | |
and <a href="theme/rubyblue.css">Rubyblue</a> themes.</li> | |
<li>Add <a href="doc/manual.html#setBookmark"><code>setBookmark</code></a> method.</li> | |
<li>Move some of the demo code into reusable components | |
under <a href="lib/util/"><code>lib/util</code></a>.</li> | |
<li>Make screen-coord-finding code faster and more reliable.</li> | |
<li>Fix drag-and-drop in Firefox.</li> | |
<li>Improve support for IME.</li> | |
<li>Speed up content rendering.</li> | |
<li>Fix browser's built-in search in Webkit.</li> | |
<li>Make double- and triple-click work in IE.</li> | |
<li>Various fixes to modes.</li> | |
</ul> | |
<p class="rel">27-10-2011: <a href="http://codemirror.net/codemirror-2.16.zip">Version 2.16</a>:</p> | |
<ul class="rel-note"> | |
<li>Add <a href="mode/perl/index.html">Perl</a>, <a href="mode/rust/index.html">Rust</a>, <a href="mode/tiddlywiki/index.html">TiddlyWiki</a>, and <a href="mode/groovy/index.html">Groovy</a> modes.</li> | |
<li>Dragging text inside the editor now moves, rather than copies.</li> | |
<li>Add a <a href="doc/manual.html#coordsFromIndex"><code>coordsFromIndex</code></a> method.</li> | |
<li><strong>API change</strong>: <code>setValue</code> now no longer clears history. Use <a href="doc/manual.html#clearHistory"><code>clearHistory</code></a> for that.</li> | |
<li><strong>API change</strong>: <a href="doc/manual.html#markText"><code>markText</code></a> now | |
returns an object with <code>clear</code> and <code>find</code> | |
methods. Marked text is now more robust when edited.</li> | |
<li>Fix editing code with tabs in Internet Explorer.</li> | |
</ul> | |
<p class="rel">26-09-2011: <a href="http://codemirror.net/codemirror-2.15.zip">Version 2.15</a>:</p> | |
<p class="rel-note">Fix bug that snuck into 2.14: Clicking the | |
character that currently has the cursor didn't re-focus the | |
editor.</p> | |
<p class="rel">26-09-2011: <a href="http://codemirror.net/codemirror-2.14.zip">Version 2.14</a>:</p> | |
<ul class="rel-note"> | |
<li>Add <a href="mode/clojure/index.html">Clojure</a>, <a href="mode/pascal/index.html">Pascal</a>, <a href="mode/ntriples/index.html">NTriples</a>, <a href="mode/jinja2/index.html">Jinja2</a>, and <a href="mode/markdown/index.html">Markdown</a> modes.</li> | |
<li>Add <a href="theme/cobalt.css">Cobalt</a> and <a href="theme/eclipse.css">Eclipse</a> themes.</li> | |
<li>Add a <a href="doc/manual.html#option_fixedGutter"><code>fixedGutter</code></a> option.</li> | |
<li>Fix bug with <code>setValue</code> breaking cursor movement.</li> | |
<li>Make gutter updates much more efficient.</li> | |
<li>Allow dragging of text out of the editor (on modern browsers).</li> | |
</ul> | |
<p class="rel">23-08-2011: <a href="http://codemirror.net/codemirror-2.13.zip">Version 2.13</a>:</p> | |
<ul class="rel-note"> | |
<li>Add <a href="mode/ruby/index.html">Ruby</a>, <a href="mode/r/index.html">R</a>, <a href="mode/coffeescript/index.html">CoffeeScript</a>, and <a href="mode/velocity/index.html">Velocity</a> modes.</li> | |
<li>Add <a href="doc/manual.html#getGutterElement"><code>getGutterElement</code></a> to API.</li> | |
<li>Several fixes to scrolling and positioning.</li> | |
<li>Add <a href="doc/manual.html#option_smartHome"><code>smartHome</code></a> option.</li> | |
<li>Add an experimental <a href="mode/xmlpure/index.html">pure XML</a> mode.</li> | |
</ul> | |
<p class="rel">25-07-2011: <a href="http://codemirror.net/codemirror-2.12.zip">Version 2.12</a>:</p> | |
<ul class="rel-note"> | |
<li>Add a <a href="mode/sparql/index.html">SPARQL</a> mode.</li> | |
<li>Fix bug with cursor jumping around in an unfocused editor in IE.</li> | |
<li>Allow key and mouse events to bubble out of the editor. Ignore widget clicks.</li> | |
<li>Solve cursor flakiness after undo/redo.</li> | |
<li>Fix block-reindent ignoring the last few lines.</li> | |
<li>Fix parsing of multi-line attrs in XML mode.</li> | |
<li>Use <code>innerHTML</code> for HTML-escaping.</li> | |
<li>Some fixes to indentation in C-like mode.</li> | |
<li>Shrink horiz scrollbars when long lines removed.</li> | |
<li>Fix width feedback loop bug that caused the width of an inner DIV to shrink.</li> | |
</ul> | |
<p class="rel">04-07-2011: <a href="http://codemirror.net/codemirror-2.11.zip">Version 2.11</a>:</p> | |
<ul class="rel-note"> | |
<li>Add a <a href="mode/scheme/index.html">Scheme mode</a>.</li> | |
<li>Add a <code>replace</code> method to search cursors, for cursor-preserving replacements.</li> | |
<li>Make the <a href="mode/clike/index.html">C-like mode</a> mode more customizeable.</li> | |
<li>Update XML mode to spot mismatched tags.</li> | |
<li>Add <code>getStateAfter</code> API and <code>compareState</code> mode API methods for finer-grained mode magic.</li> | |
<li>Add a <code>getScrollerElement</code> API method to manipulate the scrolling DIV.</li> | |
<li>Fix drag-and-drop for Firefox.</li> | |
<li>Add a C# configuration for the <a href="mode/clike/index.html">C-like mode</a>.</li> | |
<li>Add <a href="demo/fullscreen.html">full-screen editing</a> and <a href="demo/changemode.html">mode-changing</a> demos.</li> | |
</ul> | |
<p><a href="doc/oldrelease.html">Older releases...</a></p> | |
</div></div> | |
<div style="height: 2em"> </div> | |
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" id="paypal"> | |
<input type="hidden" name="cmd" value="_s-xclick"/> | |
<input type="hidden" name="hosted_button_id" value="3FVHS5FGUY7CC"/> | |
</form> | |
</body> | |
</html> | |
// TODO number prefixes | |
(function() { | |
// Really primitive kill-ring implementation. | |
var killRing = []; | |
function addToRing(str) { | |
killRing.push(str); | |
if (killRing.length > 50) killRing.shift(); | |
} | |
function getFromRing() { return killRing[killRing.length - 1] || ""; } | |
function popFromRing() { if (killRing.length > 1) killRing.pop(); return getFromRing(); } | |
CodeMirror.keyMap.emacs = { | |
"Ctrl-X": function(cm) {cm.setOption("keyMap", "emacs-Ctrl-X");}, | |
"Ctrl-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");}, | |
"Ctrl-Alt-W": function(cm) {addToRing(cm.getSelection()); cm.replaceSelection("");}, | |
"Alt-W": function(cm) {addToRing(cm.getSelection());}, | |
"Ctrl-Y": function(cm) {cm.replaceSelection(getFromRing());}, | |
"Alt-Y": function(cm) {cm.replaceSelection(popFromRing());}, | |
"Ctrl-/": "undo", "Shift-Ctrl--": "undo", "Shift-Alt-,": "goDocStart", "Shift-Alt-.": "goDocEnd", | |
"Ctrl-S": "findNext", "Ctrl-R": "findPrev", "Ctrl-G": "clearSearch", "Shift-Alt-5": "replace", | |
"Ctrl-Z": "undo", "Cmd-Z": "undo", | |
fallthrough: ["basic", "emacsy"] | |
}; | |
CodeMirror.keyMap["emacs-Ctrl-X"] = { | |
"Ctrl-S": "save", "Ctrl-W": "save", "S": "saveAll", "F": "open", "U": "undo", "K": "close", | |
auto: "emacs", catchall: function(cm) {/*ignore*/} | |
}; | |
})(); | |
(function() { | |
var count = ""; | |
function pushCountDigit(digit) { return function(cm) {count += digit;} } | |
function popCount() { var i = parseInt(count); count = ""; return i || 1; } | |
function countTimes(func) { | |
if (typeof func == "string") func = CodeMirror.commands[func]; | |
return function(cm) { for (var i = 0, c = popCount(); i < c; ++i) func(cm); } | |
} | |
function iterObj(o, f) { | |
for (var prop in o) if (o.hasOwnProperty(prop)) f(prop, o[prop]); | |
} | |
var word = [/\w/, /[^\w\s]/], bigWord = [/\S/]; | |
function findWord(line, pos, dir, regexps) { | |
var stop = 0, next = -1; | |
if (dir > 0) { stop = line.length; next = 0; } | |
var start = stop, end = stop; | |
// Find bounds of next one. | |
outer: for (; pos != stop; pos += dir) { | |
for (var i = 0; i < regexps.length; ++i) { | |
if (regexps[i].test(line.charAt(pos + next))) { | |
start = pos; | |
for (; pos != stop; pos += dir) { | |
if (!regexps[i].test(line.charAt(pos + next))) break; | |
} | |
end = pos; | |
break outer; | |
} | |
} | |
} | |
return {from: Math.min(start, end), to: Math.max(start, end)}; | |
} | |
function moveToWord(cm, regexps, dir, where) { | |
var cur = cm.getCursor(), ch = cur.ch, line = cm.getLine(cur.line), word; | |
while (true) { | |
word = findWord(line, ch, dir, regexps); | |
ch = word[where == "end" ? "to" : "from"]; | |
if (ch == cur.ch && word.from != word.to) ch = word[dir < 0 ? "from" : "to"]; | |
else break; | |
} | |
cm.setCursor(cur.line, word[where == "end" ? "to" : "from"], true); | |
} | |
var map = CodeMirror.keyMap.vim = { | |
"0": function(cm) {count.length > 0 ? pushCountDigit("0")(cm) : CodeMirror.commands.goLineStart(cm);}, | |
"I": function(cm) {popCount(); cm.setOption("keyMap", "vim-insert");}, | |
"G": function(cm) {cm.setOption("keyMap", "vim-prefix-g");}, | |
catchall: function(cm) {/*ignore*/} | |
}; | |
// Add bindings for number keys | |
for (var i = 1; i < 10; ++i) map[i] = pushCountDigit(i); | |
// Add bindings that are influenced by number keys | |
iterObj({"H": "goColumnLeft", "L": "goColumnRight", "J": "goLineDown", "K": "goLineUp", | |
"Left": "goColumnLeft", "Right": "goColumnRight", "Down": "goLineDown", "Up": "goLineUp", | |
"Backspace": "goCharLeft", "Space": "goCharRight", | |
"B": function(cm) {moveToWord(cm, word, -1, "end");}, | |
"E": function(cm) {moveToWord(cm, word, 1, "end");}, | |
"W": function(cm) {moveToWord(cm, word, 1, "start");}, | |
"Shift-B": function(cm) {moveToWord(cm, bigWord, -1, "end");}, | |
"Shift-E": function(cm) {moveToWord(cm, bigWord, 1, "end");}, | |
"Shift-W": function(cm) {moveToWord(cm, bigWord, 1, "start");}, | |
"U": "undo", "Ctrl-R": "redo", "Shift-4": "goLineEnd"}, | |
function(key, cmd) { map[key] = countTimes(cmd); }); | |
CodeMirror.keyMap["vim-prefix-g"] = { | |
"E": countTimes(function(cm) { moveToWord(cm, word, -1, "start");}), | |
"Shift-E": countTimes(function(cm) { moveToWord(cm, bigWord, -1, "start");}), | |
auto: "vim", catchall: function(cm) {/*ignore*/} | |
}; | |
CodeMirror.keyMap["vim-insert"] = { | |
"Esc": function(cm) {cm.setOption("keyMap", "vim");}, | |
fallthrough: ["default"] | |
}; | |
})(); | |
.CodeMirror { | |
line-height: 1em; | |
font-family: monospace; | |
} | |
.CodeMirror-scroll { | |
overflow: auto; | |
height: 300px; | |
/* This is needed to prevent an IE[67] bug where the scrolled content | |
is visible outside of the scrolling box. */ | |
position: relative; | |
} | |
.CodeMirror-gutter { | |
position: absolute; left: 0; top: 0; | |
z-index: 10; | |
background-color: #f7f7f7; | |
border-right: 1px solid #eee; | |
min-width: 2em; | |
height: 100%; | |
} | |
.CodeMirror-gutter-text { | |
color: #aaa; | |
text-align: right; | |
padding: .4em .2em .4em .4em; | |
white-space: pre !important; | |
} | |
.CodeMirror-lines { | |
padding: .4em; | |
} | |
.CodeMirror pre { | |
-moz-border-radius: 0; | |
-webkit-border-radius: 0; | |
-o-border-radius: 0; | |
border-radius: 0; | |
border-width: 0; margin: 0; padding: 0; background: transparent; | |
font-family: inherit; | |
font-size: inherit; | |
padding: 0; margin: 0; | |
white-space: pre; | |
word-wrap: normal; | |
} | |
.CodeMirror-wrap pre { | |
word-wrap: break-word; | |
white-space: pre-wrap; | |
} | |
.CodeMirror-wrap .CodeMirror-scroll { | |
overflow-x: hidden; | |
} | |
.CodeMirror textarea { | |
outline: none !important; | |
} | |
.CodeMirror pre.CodeMirror-cursor { | |
z-index: 10; | |
position: absolute; | |
visibility: hidden; | |
border-left: 1px solid black; | |
} | |
.CodeMirror-focused pre.CodeMirror-cursor { | |
visibility: visible; | |
} | |
span.CodeMirror-selected { background: #d9d9d9; } | |
.CodeMirror-focused span.CodeMirror-selected { background: #d2dcf8; } | |
.CodeMirror-searching {background: #ffa;} | |
/* Default theme */ | |
.cm-s-default span.cm-keyword {color: #708;} | |
.cm-s-default span.cm-atom {color: #219;} | |
.cm-s-default span.cm-number {color: #164;} | |
.cm-s-default span.cm-def {color: #00f;} | |
.cm-s-default span.cm-variable {color: black;} | |
.cm-s-default span.cm-variable-2 {color: #05a;} | |
.cm-s-default span.cm-variable-3 {color: #085;} | |
.cm-s-default span.cm-property {color: black;} | |
.cm-s-default span.cm-operator {color: black;} | |
.cm-s-default span.cm-comment {color: #a50;} | |
.cm-s-default span.cm-string {color: #a11;} | |
.cm-s-default span.cm-string-2 {color: #f50;} | |
.cm-s-default span.cm-meta {color: #555;} | |
.cm-s-default span.cm-error {color: #f00;} | |
.cm-s-default span.cm-qualifier {color: #555;} | |
.cm-s-default span.cm-builtin {color: #30a;} | |
.cm-s-default span.cm-bracket {color: #cc7;} | |
.cm-s-default span.cm-tag {color: #170;} | |
.cm-s-default span.cm-attribute {color: #00c;} | |
.cm-s-default span.cm-header {color: #a0a;} | |
.cm-s-default span.cm-quote {color: #090;} | |
.cm-s-default span.cm-hr {color: #999;} | |
.cm-s-default span.cm-link {color: #00c;} | |
span.cm-header, span.cm-strong {font-weight: bold;} | |
span.cm-em {font-style: italic;} | |
span.cm-emstrong {font-style: italic; font-weight: bold;} | |
span.cm-link {text-decoration: underline;} | |
div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} | |
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} | |
// CodeMirror version 2.2 | |
// | |
// All functions that need access to the editor's state live inside | |
// the CodeMirror function. Below that, at the bottom of the file, | |
// some utilities are defined. | |
// CodeMirror is the only global var we claim | |
var CodeMirror = (function() { | |
// This is the function that produces an editor instance. It's | |
// closure is used to store the editor state. | |
function CodeMirror(place, givenOptions) { | |
// Determine effective options based on given values and defaults. | |
var options = {}, defaults = CodeMirror.defaults; | |
for (var opt in defaults) | |
if (defaults.hasOwnProperty(opt)) | |
options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; | |
var targetDocument = options["document"]; | |
// The element in which the editor lives. | |
var wrapper = targetDocument.createElement("div"); | |
wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""); | |
// This mess creates the base DOM structure for the editor. | |
wrapper.innerHTML = | |
'<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea | |
'<textarea style="position: absolute; padding: 0; width: 1px;" wrap="off" ' + | |
'autocorrect="off" autocapitalize="off"></textarea></div>' + | |
'<div class="CodeMirror-scroll" tabindex="-1">' + | |
'<div style="position: relative">' + // Set to the height of the text, causes scrolling | |
'<div style="position: relative">' + // Moved around its parent to cover visible view | |
'<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' + | |
// Provides positioning relative to (visible) text origin | |
'<div class="CodeMirror-lines"><div style="position: relative">' + | |
'<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden"></div>' + | |
'<pre class="CodeMirror-cursor"> </pre>' + // Absolutely positioned blinky cursor | |
'<div></div>' + // This DIV contains the actual code | |
'</div></div></div></div></div>'; | |
if (place.appendChild) place.appendChild(wrapper); else place(wrapper); | |
// I've never seen more elegant code in my life. | |
var inputDiv = wrapper.firstChild, input = inputDiv.firstChild, | |
scroller = wrapper.lastChild, code = scroller.firstChild, | |
mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild, | |
lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild, | |
cursor = measure.nextSibling, lineDiv = cursor.nextSibling; | |
themeChanged(); | |
// Needed to hide big blue blinking cursor on Mobile Safari | |
if (/AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent)) input.style.width = "0px"; | |
if (!webkit) lineSpace.draggable = true; | |
if (options.tabindex != null) input.tabIndex = options.tabindex; | |
if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; | |
// Check for problem with IE innerHTML not working when we have a | |
// P (or similar) parent node. | |
try { stringWidth("x"); } | |
catch (e) { | |
if (e.message.match(/runtime/i)) | |
e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)"); | |
throw e; | |
} | |
// Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. | |
var poll = new Delayed(), highlight = new Delayed(), blinker; | |
// mode holds a mode API object. doc is the tree of Line objects, | |
// work an array of lines that should be parsed, and history the | |
// undo history (instance of History constructor). | |
var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; | |
loadMode(); | |
// The selection. These are always maintained to point at valid | |
// positions. Inverted is used to remember that the user is | |
// selecting bottom-to-top. | |
var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; | |
// Selection-related flags. shiftSelecting obviously tracks | |
// whether the user is holding shift. | |
var shiftSelecting, lastClick, lastDoubleClick, draggingText, overwrite = false; | |
// Variables used by startOperation/endOperation to track what | |
// happened during the operation. | |
var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, | |
gutterDirty, callbacks; | |
// Current visible range (may be bigger than the view window). | |
var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; | |
// bracketHighlighted is used to remember that a backet has been | |
// marked. | |
var bracketHighlighted; | |
// Tracks the maximum line length so that the horizontal scrollbar | |
// can be kept static when scrolling. | |
var maxLine = "", maxWidth, tabText = computeTabText(); | |
// Initialize the content. | |
operation(function(){setValue(options.value || ""); updateInput = false;})(); | |
var history = new History(); | |
// Register our event handlers. | |
connect(scroller, "mousedown", operation(onMouseDown)); | |
connect(scroller, "dblclick", operation(onDoubleClick)); | |
connect(lineSpace, "dragstart", onDragStart); | |
connect(lineSpace, "selectstart", e_preventDefault); | |
// Gecko browsers fire contextmenu *after* opening the menu, at | |
// which point we can't mess with it anymore. Context menu is | |
// handled in onMouseDown for Gecko. | |
if (!gecko) connect(scroller, "contextmenu", onContextMenu); | |
connect(scroller, "scroll", function() { | |
updateDisplay([]); | |
if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px"; | |
if (options.onScroll) options.onScroll(instance); | |
}); | |
connect(window, "resize", function() {updateDisplay(true);}); | |
connect(input, "keyup", operation(onKeyUp)); | |
connect(input, "input", fastPoll); | |
connect(input, "keydown", operation(onKeyDown)); | |
connect(input, "keypress", operation(onKeyPress)); | |
connect(input, "focus", onFocus); | |
connect(input, "blur", onBlur); | |
connect(scroller, "dragenter", e_stop); | |
connect(scroller, "dragover", e_stop); | |
connect(scroller, "drop", operation(onDrop)); | |
connect(scroller, "paste", function(){focusInput(); fastPoll();}); | |
connect(input, "paste", fastPoll); | |
connect(input, "cut", operation(function(){replaceSelection("");})); | |
// IE throws unspecified error in certain cases, when | |
// trying to access activeElement before onload | |
var hasFocus; try { hasFocus = (targetDocument.activeElement == input); } catch(e) { } | |
if (hasFocus) setTimeout(onFocus, 20); | |
else onBlur(); | |
function isLine(l) {return l >= 0 && l < doc.size;} | |
// The instance object that we'll return. Mostly calls out to | |
// local functions in the CodeMirror function. Some do some extra | |
// range checking and/or clipping. operation is used to wrap the | |
// call so that changes it makes are tracked, and the display is | |
// updated afterwards. | |
var instance = wrapper.CodeMirror = { | |
getValue: getValue, | |
setValue: operation(setValue), | |
getSelection: getSelection, | |
replaceSelection: operation(replaceSelection), | |
focus: function(){focusInput(); onFocus(); fastPoll();}, | |
setOption: function(option, value) { | |
var oldVal = options[option]; | |
options[option] = value; | |
if (option == "mode" || option == "indentUnit") loadMode(); | |
else if (option == "readOnly" && value) {onBlur(); input.blur();} | |
else if (option == "theme") themeChanged(); | |
else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); | |
else if (option == "tabSize") operation(tabsChanged)(); | |
if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") | |
operation(gutterChanged)(); | |
}, | |
getOption: function(option) {return options[option];}, | |
undo: operation(undo), | |
redo: operation(redo), | |
indentLine: operation(function(n, dir) { | |
if (isLine(n)) indentLine(n, dir == null ? "smart" : dir ? "add" : "subtract"); | |
}), | |
indentSelection: operation(indentSelected), | |
historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, | |
clearHistory: function() {history = new History();}, | |
matchBrackets: operation(function(){matchBrackets(true);}), | |
getTokenAt: operation(function(pos) { | |
pos = clipPos(pos); | |
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch); | |
}), | |
getStateAfter: function(line) { | |
line = clipLine(line == null ? doc.size - 1: line); | |
return getStateBefore(line + 1); | |
}, | |
cursorCoords: function(start){ | |
if (start == null) start = sel.inverted; | |
return pageCoords(start ? sel.from : sel.to); | |
}, | |
charCoords: function(pos){return pageCoords(clipPos(pos));}, | |
coordsChar: function(coords) { | |
var off = eltOffset(lineSpace); | |
return coordsChar(coords.x - off.left, coords.y - off.top); | |
}, | |
markText: operation(markText), | |
setBookmark: setBookmark, | |
setMarker: operation(addGutterMarker), | |
clearMarker: operation(removeGutterMarker), | |
setLineClass: operation(setLineClass), | |
hideLine: operation(function(h) {return setLineHidden(h, true);}), | |
showLine: operation(function(h) {return setLineHidden(h, false);}), | |
onDeleteLine: function(line, f) { | |
if (typeof line == "number") { | |
if (!isLine(line)) return null; | |
line = getLine(line); | |
} | |
(line.handlers || (line.handlers = [])).push(f); | |
return line; | |
}, | |
lineInfo: lineInfo, | |
addWidget: function(pos, node, scroll, vert, horiz) { | |
pos = localCoords(clipPos(pos)); | |
var top = pos.yBot, left = pos.x; | |
node.style.position = "absolute"; | |
code.appendChild(node); | |
if (vert == "over") top = pos.y; | |
else if (vert == "near") { | |
var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), | |
hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft(); | |
if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) | |
top = pos.y - node.offsetHeight; | |
if (left + node.offsetWidth > hspace) | |
left = hspace - node.offsetWidth; | |
} | |
node.style.top = (top + paddingTop()) + "px"; | |
node.style.left = node.style.right = ""; | |
if (horiz == "right") { | |
left = code.clientWidth - node.offsetWidth; | |
node.style.right = "0px"; | |
} else { | |
if (horiz == "left") left = 0; | |
else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2; | |
node.style.left = (left + paddingLeft()) + "px"; | |
} | |
if (scroll) | |
scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); | |
}, | |
lineCount: function() {return doc.size;}, | |
clipPos: clipPos, | |
getCursor: function(start) { | |
if (start == null) start = sel.inverted; | |
return copyPos(start ? sel.from : sel.to); | |
}, | |
somethingSelected: function() {return !posEq(sel.from, sel.to);}, | |
setCursor: operation(function(line, ch, user) { | |
if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); | |
else setCursor(line, ch, user); | |
}), | |
setSelection: operation(function(from, to, user) { | |
(user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); | |
}), | |
getLine: function(line) {if (isLine(line)) return getLine(line).text;}, | |
getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, | |
setLine: operation(function(line, text) { | |
if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); | |
}), | |
removeLine: operation(function(line) { | |
if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); | |
}), | |
replaceRange: operation(replaceRange), | |
getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));}, | |
execCommand: function(cmd) {return commands[cmd](instance);}, | |
// Stuff used by commands, probably not much use to outside code. | |
moveH: operation(moveH), | |
deleteH: operation(deleteH), | |
moveV: operation(moveV), | |
toggleOverwrite: function() {overwrite = !overwrite;}, | |
posFromIndex: function(off) { | |
var lineNo = 0, ch; | |
doc.iter(0, doc.size, function(line) { | |
var sz = line.text.length + 1; | |
if (sz > off) { ch = off; return true; } | |
off -= sz; | |
++lineNo; | |
}); | |
return clipPos({line: lineNo, ch: ch}); | |
}, | |
indexFromPos: function (coords) { | |
if (coords.line < 0 || coords.ch < 0) return 0; | |
var index = coords.ch; | |
doc.iter(0, coords.line, function (line) { | |
index += line.text.length + 1; | |
}); | |
return index; | |
}, | |
operation: function(f){return operation(f)();}, | |
refresh: function(){updateDisplay(true);}, | |
getInputField: function(){return input;}, | |
getWrapperElement: function(){return wrapper;}, | |
getScrollerElement: function(){return scroller;}, | |
getGutterElement: function(){return gutter;} | |
}; | |
function getLine(n) { return getLineAt(doc, n); } | |
function updateLineHeight(line, height) { | |
gutterDirty = true; | |
var diff = height - line.height; | |
for (var n = line; n; n = n.parent) n.height += diff; | |
} | |
function setValue(code) { | |
var top = {line: 0, ch: 0}; | |
updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, | |
splitLines(code), top, top); | |
updateInput = true; | |
} | |
function getValue(code) { | |
var text = []; | |
doc.iter(0, doc.size, function(line) { text.push(line.text); }); | |
return text.join("\n"); | |
} | |
function onMouseDown(e) { | |
setShift(e.shiftKey); | |
// Check whether this is a click in a widget | |
for (var n = e_target(e); n != wrapper; n = n.parentNode) | |
if (n.parentNode == code && n != mover) return; | |
// See if this is a click in the gutter | |
for (var n = e_target(e); n != wrapper; n = n.parentNode) | |
if (n.parentNode == gutterText) { | |
if (options.onGutterClick) | |
options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); | |
return e_preventDefault(e); | |
} | |
var start = posFromMouse(e); | |
switch (e_button(e)) { | |
case 3: | |
if (gecko && !mac) onContextMenu(e); | |
return; | |
case 2: | |
if (start) setCursor(start.line, start.ch, true); | |
return; | |
} | |
// For button 1, if it was clicked inside the editor | |
// (posFromMouse returning non-null), we have to adjust the | |
// selection. | |
if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} | |
if (!focused) onFocus(); | |
var now = +new Date; | |
if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { | |
e_preventDefault(e); | |
setTimeout(focusInput, 20); | |
return selectLine(start.line); | |
} else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { | |
lastDoubleClick = {time: now, pos: start}; | |
e_preventDefault(e); | |
return selectWordAt(start); | |
} else { lastClick = {time: now, pos: start}; } | |
var last = start, going; | |
if (dragAndDrop && !posEq(sel.from, sel.to) && | |
!posLess(start, sel.from) && !posLess(sel.to, start)) { | |
// Let the drag handler handle this. | |
if (webkit) lineSpace.draggable = true; | |
var up = connect(targetDocument, "mouseup", operation(function(e2) { | |
if (webkit) lineSpace.draggable = false; | |
draggingText = false; | |
up(); | |
if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { | |
e_preventDefault(e2); | |
setCursor(start.line, start.ch, true); | |
focusInput(); | |
} | |
}), true); | |
draggingText = true; | |
return; | |
} | |
e_preventDefault(e); | |
setCursor(start.line, start.ch, true); | |
function extend(e) { | |
var cur = posFromMouse(e, true); | |
if (cur && !posEq(cur, last)) { | |
if (!focused) onFocus(); | |
last = cur; | |
setSelectionUser(start, cur); | |
updateInput = false; | |
var visible = visibleLines(); | |
if (cur.line >= visible.to || cur.line < visible.from) | |
going = setTimeout(operation(function(){extend(e);}), 150); | |
} | |
} | |
var move = connect(targetDocument, "mousemove", operation(function(e) { | |
clearTimeout(going); | |
e_preventDefault(e); | |
extend(e); | |
}), true); | |
var up = connect(targetDocument, "mouseup", operation(function(e) { | |
clearTimeout(going); | |
var cur = posFromMouse(e); | |
if (cur) setSelectionUser(start, cur); | |
e_preventDefault(e); | |
focusInput(); | |
updateInput = true; | |
move(); up(); | |
}), true); | |
} | |
function onDoubleClick(e) { | |
for (var n = e_target(e); n != wrapper; n = n.parentNode) | |
if (n.parentNode == gutterText) return e_preventDefault(e); | |
var start = posFromMouse(e); | |
if (!start) return; | |
lastDoubleClick = {time: +new Date, pos: start}; | |
e_preventDefault(e); | |
selectWordAt(start); | |
} | |
function onDrop(e) { | |
e.preventDefault(); | |
var pos = posFromMouse(e, true), files = e.dataTransfer.files; | |
if (!pos || options.readOnly) return; | |
if (files && files.length && window.FileReader && window.File) { | |
function loadFile(file, i) { | |
var reader = new FileReader; | |
reader.onload = function() { | |
text[i] = reader.result; | |
if (++read == n) { | |
pos = clipPos(pos); | |
operation(function() { | |
var end = replaceRange(text.join(""), pos, pos); | |
setSelectionUser(pos, end); | |
})(); | |
} | |
}; | |
reader.readAsText(file); | |
} | |
var n = files.length, text = Array(n), read = 0; | |
for (var i = 0; i < n; ++i) loadFile(files[i], i); | |
} | |
else { | |
try { | |
var text = e.dataTransfer.getData("Text"); | |
if (text) { | |
var end = replaceRange(text, pos, pos); | |
var curFrom = sel.from, curTo = sel.to; | |
setSelectionUser(pos, end); | |
if (draggingText) replaceRange("", curFrom, curTo); | |
focusInput(); | |
} | |
} | |
catch(e){} | |
} | |
} | |
function onDragStart(e) { | |
var txt = getSelection(); | |
// This will reset escapeElement | |
htmlEscape(txt); | |
e.dataTransfer.setDragImage(escapeElement, 0, 0); | |
e.dataTransfer.setData("Text", txt); | |
} | |
function handleKeyBinding(e) { | |
var name = keyNames[e.keyCode], next = keyMap[options.keyMap].auto, bound, dropShift; | |
if (name == null || e.altGraphKey) { | |
if (next) options.keyMap = next; | |
return null; | |
} | |
if (e.altKey) name = "Alt-" + name; | |
if (e.ctrlKey) name = "Ctrl-" + name; | |
if (e.metaKey) name = "Cmd-" + name; | |
if (e.shiftKey && (bound = lookupKey("Shift-" + name, options.extraKeys, options.keyMap))) { | |
dropShift = true; | |
} else { | |
bound = lookupKey(name, options.extraKeys, options.keyMap); | |
} | |
if (typeof bound == "string") { | |
if (commands.propertyIsEnumerable(bound)) bound = commands[bound]; | |
else bound = null; | |
} | |
if (next && (bound || !isModifierKey(e))) options.keyMap = next; | |
if (!bound) return false; | |
if (dropShift) { | |
var prevShift = shiftSelecting; | |
shiftSelecting = null; | |
bound(instance); | |
shiftSelecting = prevShift; | |
} else bound(instance); | |
e_preventDefault(e); | |
return true; | |
} | |
var lastStoppedKey = null; | |
function onKeyDown(e) { | |
if (!focused) onFocus(); | |
var code = e.keyCode; | |
// IE does strange things with escape. | |
if (ie && code == 27) { e.returnValue = false; } | |
setShift(code == 16 || e.shiftKey); | |
// First give onKeyEvent option a chance to handle this. | |
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; | |
var handled = handleKeyBinding(e); | |
if (window.opera) { | |
lastStoppedKey = handled ? e.keyCode : null; | |
// Opera has no cut event... we try to at least catch the key combo | |
if (!handled && (mac ? e.metaKey : e.ctrlKey) && e.keyCode == 88) | |
replaceSelection(""); | |
} | |
} | |
function onKeyPress(e) { | |
if (window.opera && e.keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} | |
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; | |
if (window.opera && !e.which && handleKeyBinding(e)) return; | |
if (options.electricChars && mode.electricChars) { | |
var ch = String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode); | |
if (mode.electricChars.indexOf(ch) > -1) | |
setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); | |
} | |
fastPoll(); | |
} | |
function onKeyUp(e) { | |
if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; | |
if (e.keyCode == 16) shiftSelecting = null; | |
} | |
function onFocus() { | |
if (options.readOnly) return; | |
if (!focused) { | |
if (options.onFocus) options.onFocus(instance); | |
focused = true; | |
if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1) | |
wrapper.className += " CodeMirror-focused"; | |
if (!leaveInputAlone) resetInput(true); | |
} | |
slowPoll(); | |
restartBlink(); | |
} | |
function onBlur() { | |
if (focused) { | |
if (options.onBlur) options.onBlur(instance); | |
focused = false; | |
wrapper.className = wrapper.className.replace(" CodeMirror-focused", ""); | |
} | |
clearInterval(blinker); | |
setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); | |
} | |
// Replace the range from from to to by the strings in newText. | |
// Afterwards, set the selection to selFrom, selTo. | |
function updateLines(from, to, newText, selFrom, selTo) { | |
if (history) { | |
var old = []; | |
doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); | |
history.addChange(from.line, newText.length, old); | |
while (history.done.length > options.undoDepth) history.done.shift(); | |
} | |
updateLinesNoUndo(from, to, newText, selFrom, selTo); | |
} | |
function unredoHelper(from, to) { | |
var change = from.pop(); | |
if (change) { | |
var replaced = [], end = change.start + change.added; | |
doc.iter(change.start, end, function(line) { replaced.push(line.text); }); | |
to.push({start: change.start, added: change.old.length, old: replaced}); | |
var pos = clipPos({line: change.start + change.old.length - 1, | |
ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}); | |
updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); | |
updateInput = true; | |
} | |
} | |
function undo() {unredoHelper(history.done, history.undone);} | |
function redo() {unredoHelper(history.undone, history.done);} | |
function updateLinesNoUndo(from, to, newText, selFrom, selTo) { | |
var recomputeMaxLength = false, maxLineLength = maxLine.length; | |
if (!options.lineWrapping) | |
doc.iter(from.line, to.line, function(line) { | |
if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} | |
}); | |
if (from.line != to.line || newText.length > 1) gutterDirty = true; | |
var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); | |
// First adjust the line structure, taking some care to leave highlighting intact. | |
if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { | |
// This is a whole-line replace. Treated specially to make | |
// sure line objects move the way they are supposed to. | |
var added = [], prevLine = null; | |
if (from.line) { | |
prevLine = getLine(from.line - 1); | |
prevLine.fixMarkEnds(lastLine); | |
} else lastLine.fixMarkStarts(); | |
for (var i = 0, e = newText.length - 1; i < e; ++i) | |
added.push(Line.inheritMarks(newText[i], prevLine)); | |
if (nlines) doc.remove(from.line, nlines, callbacks); | |
if (added.length) doc.insert(from.line, added); | |
} else if (firstLine == lastLine) { | |
if (newText.length == 1) | |
firstLine.replace(from.ch, to.ch, newText[0]); | |
else { | |
lastLine = firstLine.split(to.ch, newText[newText.length-1]); | |
firstLine.replace(from.ch, null, newText[0]); | |
firstLine.fixMarkEnds(lastLine); | |
var added = []; | |
for (var i = 1, e = newText.length - 1; i < e; ++i) | |
added.push(Line.inheritMarks(newText[i], firstLine)); | |
added.push(lastLine); | |
doc.insert(from.line + 1, added); | |
} | |
} else if (newText.length == 1) { | |
firstLine.replace(from.ch, null, newText[0]); | |
lastLine.replace(null, to.ch, ""); | |
firstLine.append(lastLine); | |
doc.remove(from.line + 1, nlines, callbacks); | |
} else { | |
var added = []; | |
firstLine.replace(from.ch, null, newText[0]); | |
lastLine.replace(null, to.ch, newText[newText.length-1]); | |
firstLine.fixMarkEnds(lastLine); | |
for (var i = 1, e = newText.length - 1; i < e; ++i) | |
added.push(Line.inheritMarks(newText[i], firstLine)); | |
if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); | |
doc.insert(from.line + 1, added); | |
} | |
if (options.lineWrapping) { | |
var perLine = scroller.clientWidth / charWidth() - 3; | |
doc.iter(from.line, from.line + newText.length, function(line) { | |
if (line.hidden) return; | |
var guess = Math.ceil(line.text.length / perLine) || 1; | |
if (guess != line.height) updateLineHeight(line, guess); | |
}); | |
} else { | |
doc.iter(from.line, i + newText.length, function(line) { | |
var l = line.text; | |
if (l.length > maxLineLength) { | |
maxLine = l; maxLineLength = l.length; maxWidth = null; | |
recomputeMaxLength = false; | |
} | |
}); | |
if (recomputeMaxLength) { | |
maxLineLength = 0; maxLine = ""; maxWidth = null; | |
doc.iter(0, doc.size, function(line) { | |
var l = line.text; | |
if (l.length > maxLineLength) { | |
maxLineLength = l.length; maxLine = l; | |
} | |
}); | |
} | |
} | |
// Add these lines to the work array, so that they will be | |
// highlighted. Adjust work lines if lines were added/removed. | |
var newWork = [], lendiff = newText.length - nlines - 1; | |
for (var i = 0, l = work.length; i < l; ++i) { | |
var task = work[i]; | |
if (task < from.line) newWork.push(task); | |
else if (task > to.line) newWork.push(task + lendiff); | |
} | |
var hlEnd = from.line + Math.min(newText.length, 500); | |
highlightLines(from.line, hlEnd); | |
newWork.push(hlEnd); | |
work = newWork; | |
startWorker(100); | |
// Remember that these lines changed, for updating the display | |
changes.push({from: from.line, to: to.line + 1, diff: lendiff}); | |
var changeObj = {from: from, to: to, text: newText}; | |
if (textChanged) { | |
for (var cur = textChanged; cur.next; cur = cur.next) {} | |
cur.next = changeObj; | |
} else textChanged = changeObj; | |
// Update the selection | |
function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} | |
setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line)); | |
// Make sure the scroll-size div has the correct height. | |
code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px"; | |
} | |
function replaceRange(code, from, to) { | |
from = clipPos(from); | |
if (!to) to = from; else to = clipPos(to); | |
code = splitLines(code); | |
function adjustPos(pos) { | |
if (posLess(pos, from)) return pos; | |
if (!posLess(to, pos)) return end; | |
var line = pos.line + code.length - (to.line - from.line) - 1; | |
var ch = pos.ch; | |
if (pos.line == to.line) | |
ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); | |
return {line: line, ch: ch}; | |
} | |
var end; | |
replaceRange1(code, from, to, function(end1) { | |
end = end1; | |
return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; | |
}); | |
return end; | |
} | |
function replaceSelection(code, collapse) { | |
replaceRange1(splitLines(code), sel.from, sel.to, function(end) { | |
if (collapse == "end") return {from: end, to: end}; | |
else if (collapse == "start") return {from: sel.from, to: sel.from}; | |
else return {from: sel.from, to: end}; | |
}); | |
} | |
function replaceRange1(code, from, to, computeSel) { | |
var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; | |
var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); | |
updateLines(from, to, code, newSel.from, newSel.to); | |
} | |
function getRange(from, to) { | |
var l1 = from.line, l2 = to.line; | |
if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); | |
var code = [getLine(l1).text.slice(from.ch)]; | |
doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); | |
code.push(getLine(l2).text.slice(0, to.ch)); | |
return code.join("\n"); | |
} | |
function getSelection() { | |
return getRange(sel.from, sel.to); | |
} | |
var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll | |
function slowPoll() { | |
if (pollingFast) return; | |
poll.set(options.pollInterval, function() { | |
startOperation(); | |
readInput(); | |
if (focused) slowPoll(); | |
endOperation(); | |
}); | |
} | |
function fastPoll() { | |
var missed = false; | |
pollingFast = true; | |
function p() { | |
startOperation(); | |
var changed = readInput(); | |
if (!changed && !missed) {missed = true; poll.set(60, p);} | |
else {pollingFast = false; slowPoll();} | |
endOperation(); | |
} | |
poll.set(20, p); | |
} | |
// Previnput is a hack to work with IME. If we reset the textarea | |
// on every change, that breaks IME. So we look for changes | |
// compared to the previous content instead. (Modern browsers have | |
// events that indicate IME taking place, but these are not widely | |
// supported or compatible enough yet to rely on.) | |
var prevInput = ""; | |
function readInput() { | |
if (leaveInputAlone || !focused || hasSelection(input)) return false; | |
var text = input.value; | |
if (text == prevInput) return false; | |
shiftSelecting = null; | |
var same = 0, l = Math.min(prevInput.length, text.length); | |
while (same < l && prevInput[same] == text[same]) ++same; | |
if (same < prevInput.length) | |
sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; | |
else if (overwrite && posEq(sel.from, sel.to)) | |
sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; | |
replaceSelection(text.slice(same), "end"); | |
prevInput = text; | |
return true; | |
} | |
function resetInput(user) { | |
if (!posEq(sel.from, sel.to)) { | |
prevInput = ""; | |
input.value = getSelection(); | |
input.select(); | |
} else if (user) prevInput = input.value = ""; | |
} | |
function focusInput() { | |
if (!options.readOnly) input.focus(); | |
} | |
function scrollEditorIntoView() { | |
if (!cursor.getBoundingClientRect) return; | |
var rect = cursor.getBoundingClientRect(); | |
// IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden | |
if (ie && rect.top == rect.bottom) return; | |
var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); | |
if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView(); | |
} | |
function scrollCursorIntoView() { | |
var cursor = localCoords(sel.inverted ? sel.from : sel.to); | |
var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; | |
return scrollIntoView(x, cursor.y, x, cursor.yBot); | |
} | |
function scrollIntoView(x1, y1, x2, y2) { | |
var pl = paddingLeft(), pt = paddingTop(), lh = textHeight(); | |
y1 += pt; y2 += pt; x1 += pl; x2 += pl; | |
var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true; | |
if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1 - 2*lh); scrolled = true;} | |
else if (y2 > screentop + screen) {scroller.scrollTop = y2 + lh - screen; scrolled = true;} | |
var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; | |
var gutterw = options.fixedGutter ? gutter.clientWidth : 0; | |
if (x1 < screenleft + gutterw) { | |
if (x1 < 50) x1 = 0; | |
scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw); | |
scrolled = true; | |
} | |
else if (x2 > screenw + screenleft - 3) { | |
scroller.scrollLeft = x2 + 10 - screenw; | |
scrolled = true; | |
if (x2 > code.clientWidth) result = false; | |
} | |
if (scrolled && options.onScroll) options.onScroll(instance); | |
return result; | |
} | |
function visibleLines() { | |
var lh = textHeight(), top = scroller.scrollTop - paddingTop(); | |
var from_height = Math.max(0, Math.floor(top / lh)); | |
var to_height = Math.ceil((top + scroller.clientHeight) / lh); | |
return {from: lineAtHeight(doc, from_height), | |
to: lineAtHeight(doc, to_height)}; | |
} | |
// Uses a set of changes plus the current scroll position to | |
// determine which DOM updates have to be made, and makes the | |
// updates. | |
function updateDisplay(changes, suppressCallback) { | |
if (!scroller.clientWidth) { | |
showingFrom = showingTo = displayOffset = 0; | |
return; | |
} | |
// Compute the new visible window | |
var visible = visibleLines(); | |
// Bail out if the visible area is already rendered and nothing changed. | |
if (changes !== true && changes.length == 0 && visible.from >= showingFrom && visible.to <= showingTo) return; | |
var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); | |
if (showingFrom < from && from - showingFrom < 20) from = showingFrom; | |
if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); | |
// Create a range of theoretically intact lines, and punch holes | |
// in that using the change info. | |
var intact = changes === true ? [] : | |
computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); | |
// Clip off the parts that won't be visible | |
var intactLines = 0; | |
for (var i = 0; i < intact.length; ++i) { | |
var range = intact[i]; | |
if (range.from < from) {range.domStart += (from - range.from); range.from = from;} | |
if (range.to > to) range.to = to; | |
if (range.from >= range.to) intact.splice(i--, 1); | |
else intactLines += range.to - range.from; | |
} | |
if (intactLines == to - from) return; | |
intact.sort(function(a, b) {return a.domStart - b.domStart;}); | |
var th = textHeight(), gutterDisplay = gutter.style.display; | |
lineDiv.style.display = gutter.style.display = "none"; | |
patchDisplay(from, to, intact); | |
lineDiv.style.display = ""; | |
// Position the mover div to align with the lines it's supposed | |
// to be showing (which will cover the visible display) | |
var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; | |
// This is just a bogus formula that detects when the editor is | |
// resized or the font size changes. | |
if (different) lastSizeC = scroller.clientHeight + th; | |
showingFrom = from; showingTo = to; | |
displayOffset = heightAtLine(doc, from); | |
mover.style.top = (displayOffset * th) + "px"; | |
code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; | |
// Since this is all rather error prone, it is honoured with the | |
// only assertion in the whole file. | |
if (lineDiv.childNodes.length != showingTo - showingFrom) | |
throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + | |
" nodes=" + lineDiv.childNodes.length); | |
if (options.lineWrapping) { | |
maxWidth = scroller.clientWidth; | |
var curNode = lineDiv.firstChild; | |
doc.iter(showingFrom, showingTo, function(line) { | |
if (!line.hidden) { | |
var height = Math.round(curNode.offsetHeight / th) || 1; | |
if (line.height != height) {updateLineHeight(line, height); gutterDirty = true;} | |
} | |
curNode = curNode.nextSibling; | |
}); | |
} else { | |
if (maxWidth == null) maxWidth = stringWidth(maxLine); | |
if (maxWidth > scroller.clientWidth) { | |
lineSpace.style.width = maxWidth + "px"; | |
// Needed to prevent odd wrapping/hiding of widgets placed in here. | |
code.style.width = ""; | |
code.style.width = scroller.scrollWidth + "px"; | |
} else { | |
lineSpace.style.width = code.style.width = ""; | |
} | |
} | |
gutter.style.display = gutterDisplay; | |
if (different || gutterDirty) updateGutter(); | |
updateCursor(); | |
if (!suppressCallback && options.onUpdate) options.onUpdate(instance); | |
return true; | |
} | |
function computeIntact(intact, changes) { | |
for (var i = 0, l = changes.length || 0; i < l; ++i) { | |
var change = changes[i], intact2 = [], diff = change.diff || 0; | |
for (var j = 0, l2 = intact.length; j < l2; ++j) { | |
var range = intact[j]; | |
if (change.to <= range.from && change.diff) | |
intact2.push({from: range.from + diff, to: range.to + diff, | |
domStart: range.domStart}); | |
else if (change.to <= range.from || change.from >= range.to) | |
intact2.push(range); | |
else { | |
if (change.from > range.from) | |
intact2.push({from: range.from, to: change.from, domStart: range.domStart}); | |
if (change.to < range.to) | |
intact2.push({from: change.to + diff, to: range.to + diff, | |
domStart: range.domStart + (change.to - range.from)}); | |
} | |
} | |
intact = intact2; | |
} | |
return intact; | |
} | |
function patchDisplay(from, to, intact) { | |
// The first pass removes the DOM nodes that aren't intact. | |
if (!intact.length) lineDiv.innerHTML = ""; | |
else { | |
function killNode(node) { | |
var tmp = node.nextSibling; | |
node.parentNode.removeChild(node); | |
return tmp; | |
} | |
var domPos = 0, curNode = lineDiv.firstChild, n; | |
for (var i = 0; i < intact.length; ++i) { | |
var cur = intact[i]; | |
while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} | |
for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} | |
} | |
while (curNode) curNode = killNode(curNode); | |
} | |
// This pass fills in the lines that actually changed. | |
var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; | |
var sfrom = sel.from.line, sto = sel.to.line, inSel = sfrom < from && sto >= from; | |
var scratch = targetDocument.createElement("div"), newElt; | |
doc.iter(from, to, function(line) { | |
var ch1 = null, ch2 = null; | |
if (inSel) { | |
ch1 = 0; | |
if (sto == j) {inSel = false; ch2 = sel.to.ch;} | |
} else if (sfrom == j) { | |
if (sto == j) {ch1 = sel.from.ch; ch2 = sel.to.ch;} | |
else {inSel = true; ch1 = sel.from.ch;} | |
} | |
if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); | |
if (!nextIntact || nextIntact.from > j) { | |
if (line.hidden) scratch.innerHTML = "<pre></pre>"; | |
else scratch.innerHTML = line.getHTML(ch1, ch2, true, tabText); | |
lineDiv.insertBefore(scratch.firstChild, curNode); | |
} else { | |
curNode = curNode.nextSibling; | |
} | |
++j; | |
}); | |
} | |
function updateGutter() { | |
if (!options.gutter && !options.lineNumbers) return; | |
var hText = mover.offsetHeight, hEditor = scroller.clientHeight; | |
gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; | |
var html = [], i = showingFrom; | |
doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { | |
if (line.hidden) { | |
html.push("<pre></pre>"); | |
} else { | |
var marker = line.gutterMarker; | |
var text = options.lineNumbers ? i + options.firstLineNumber : null; | |
if (marker && marker.text) | |
text = marker.text.replace("%N%", text != null ? text : ""); | |
else if (text == null) | |
text = "\u00a0"; | |
html.push((marker && marker.style ? '<pre class="' + marker.style + '">' : "<pre>"), text); | |
for (var j = 1; j < line.height; ++j) html.push("<br/> "); | |
html.push("</pre>"); | |
} | |
++i; | |
}); | |
gutter.style.display = "none"; | |
gutterText.innerHTML = html.join(""); | |
var minwidth = String(doc.size).length, firstNode = gutterText.firstChild, val = eltText(firstNode), pad = ""; | |
while (val.length + pad.length < minwidth) pad += "\u00a0"; | |
if (pad) firstNode.insertBefore(targetDocument.createTextNode(pad), firstNode.firstChild); | |
gutter.style.display = ""; | |
lineSpace.style.marginLeft = gutter.offsetWidth + "px"; | |
gutterDirty = false; | |
} | |
function updateCursor() { | |
var head = sel.inverted ? sel.from : sel.to, lh = textHeight(); | |
var pos = localCoords(head, true); | |
var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); | |
inputDiv.style.top = (pos.y + lineOff.top - wrapOff.top) + "px"; | |
inputDiv.style.left = (pos.x + lineOff.left - wrapOff.left) + "px"; | |
if (posEq(sel.from, sel.to)) { | |
cursor.style.top = pos.y + "px"; | |
cursor.style.left = (options.lineWrapping ? Math.min(pos.x, lineSpace.offsetWidth) : pos.x) + "px"; | |
cursor.style.display = ""; | |
} | |
else cursor.style.display = "none"; | |
} | |
function setShift(val) { | |
if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); | |
else shiftSelecting = null; | |
} | |
function setSelectionUser(from, to) { | |
var sh = shiftSelecting && clipPos(shiftSelecting); | |
if (sh) { | |
if (posLess(sh, from)) from = sh; | |
else if (posLess(to, sh)) to = sh; | |
} | |
setSelection(from, to); | |
userSelChange = true; | |
} | |
// Update the selection. Last two args are only used by | |
// updateLines, since they have to be expressed in the line | |
// numbers before the update. | |
function setSelection(from, to, oldFrom, oldTo) { | |
goalColumn = null; | |
if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} | |
if (posEq(sel.from, from) && posEq(sel.to, to)) return; | |
if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} | |
// Skip over hidden lines. | |
if (from.line != oldFrom) from = skipHidden(from, oldFrom, sel.from.ch); | |
if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); | |
if (posEq(from, to)) sel.inverted = false; | |
else if (posEq(from, sel.to)) sel.inverted = false; | |
else if (posEq(to, sel.from)) sel.inverted = true; | |
// Some ugly logic used to only mark the lines that actually did | |
// see a change in selection as changed, rather than the whole | |
// selected range. | |
if (posEq(from, to)) { | |
if (!posEq(sel.from, sel.to)) | |
changes.push({from: oldFrom, to: oldTo + 1}); | |
} | |
else if (posEq(sel.from, sel.to)) { | |
changes.push({from: from.line, to: to.line + 1}); | |
} | |
else { | |
if (!posEq(from, sel.from)) { | |
if (from.line < oldFrom) | |
changes.push({from: from.line, to: Math.min(to.line, oldFrom) + 1}); | |
else | |
changes.push({from: oldFrom, to: Math.min(oldTo, from.line) + 1}); | |
} | |
if (!posEq(to, sel.to)) { | |
if (to.line < oldTo) | |
changes.push({from: Math.max(oldFrom, from.line), to: oldTo + 1}); | |
else | |
changes.push({from: Math.max(from.line, oldTo), to: to.line + 1}); | |
} | |
} | |
sel.from = from; sel.to = to; | |
selectionChanged = true; | |
} | |
function skipHidden(pos, oldLine, oldCh) { | |
function getNonHidden(dir) { | |
var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; | |
while (lNo != end) { | |
var line = getLine(lNo); | |
if (!line.hidden) { | |
var ch = pos.ch; | |
if (ch > oldCh || ch > line.text.length) ch = line.text.length; | |
return {line: lNo, ch: ch}; | |
} | |
lNo += dir; | |
} | |
} | |
var line = getLine(pos.line); | |
if (!line.hidden) return pos; | |
if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); | |
else return getNonHidden(-1) || getNonHidden(1); | |
} | |
function setCursor(line, ch, user) { | |
var pos = clipPos({line: line, ch: ch || 0}); | |
(user ? setSelectionUser : setSelection)(pos, pos); | |
} | |
function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} | |
function clipPos(pos) { | |
if (pos.line < 0) return {line: 0, ch: 0}; | |
if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; | |
var ch = pos.ch, linelen = getLine(pos.line).text.length; | |
if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; | |
else if (ch < 0) return {line: pos.line, ch: 0}; | |
else return pos; | |
} | |
function findPosH(dir, unit) { | |
var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; | |
var lineObj = getLine(line); | |
function findNextLine() { | |
for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { | |
var lo = getLine(l); | |
if (!lo.hidden) { line = l; lineObj = lo; return true; } | |
} | |
} | |
function moveOnce(boundToLine) { | |
if (ch == (dir < 0 ? 0 : lineObj.text.length)) { | |
if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; | |
else return false; | |
} else ch += dir; | |
return true; | |
} | |
if (unit == "char") moveOnce(); | |
else if (unit == "column") moveOnce(true); | |
else if (unit == "word") { | |
var sawWord = false; | |
for (;;) { | |
if (dir < 0) if (!moveOnce()) break; | |
if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; | |
else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} | |
if (dir > 0) if (!moveOnce()) break; | |
} | |
} | |
return {line: line, ch: ch}; | |
} | |
function moveH(dir, unit) { | |
var pos = dir < 0 ? sel.from : sel.to; | |
if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); | |
setCursor(pos.line, pos.ch, true); | |
} | |
function deleteH(dir, unit) { | |
if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); | |
else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); | |
else replaceRange("", sel.from, findPosH(dir, unit)); | |
userSelChange = true; | |
} | |
var goalColumn = null; | |
function moveV(dir, unit) { | |
var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); | |
if (goalColumn != null) pos.x = goalColumn; | |
if (unit == "page") dist = scroller.clientHeight; | |
else if (unit == "line") dist = textHeight(); | |
var target = coordsChar(pos.x, pos.y + dist * dir + 2); | |
setCursor(target.line, target.ch, true); | |
goalColumn = pos.x; | |
} | |
function selectWordAt(pos) { | |
var line = getLine(pos.line).text; | |
var start = pos.ch, end = pos.ch; | |
while (start > 0 && isWordChar(line.charAt(start - 1))) --start; | |
while (end < line.length && isWordChar(line.charAt(end))) ++end; | |
setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end}); | |
} | |
function selectLine(line) { | |
setSelectionUser({line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); | |
} | |
function indentSelected(mode) { | |
if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); | |
var e = sel.to.line - (sel.to.ch ? 0 : 1); | |
for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); | |
} | |
function indentLine(n, how) { | |
if (!how) how = "add"; | |
if (how == "smart") { | |
if (!mode.indent) how = "prev"; | |
else var state = getStateBefore(n); | |
} | |
var line = getLine(n), curSpace = line.indentation(options.tabSize), | |
curSpaceString = line.text.match(/^\s*/)[0], indentation; | |
if (how == "prev") { | |
if (n) indentation = getLine(n-1).indentation(options.tabSize); | |
else indentation = 0; | |
} | |
else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); | |
else if (how == "add") indentation = curSpace + options.indentUnit; | |
else if (how == "subtract") indentation = curSpace - options.indentUnit; | |
indentation = Math.max(0, indentation); | |
var diff = indentation - curSpace; | |
if (!diff) { | |
if (sel.from.line != n && sel.to.line != n) return; | |
var indentString = curSpaceString; | |
} | |
else { | |
var indentString = "", pos = 0; | |
if (options.indentWithTabs) | |
for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} | |
while (pos < indentation) {++pos; indentString += " ";} | |
} | |
replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); | |
} | |
function loadMode() { | |
mode = CodeMirror.getMode(options, options.mode); | |
doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); | |
work = [0]; | |
startWorker(); | |
} | |
function gutterChanged() { | |
var visible = options.gutter || options.lineNumbers; | |
gutter.style.display = visible ? "" : "none"; | |
if (visible) gutterDirty = true; | |
else lineDiv.parentNode.style.marginLeft = 0; | |
} | |
function wrappingChanged(from, to) { | |
if (options.lineWrapping) { | |
wrapper.className += " CodeMirror-wrap"; | |
var perLine = scroller.clientWidth / charWidth() - 3; | |
doc.iter(0, doc.size, function(line) { | |
if (line.hidden) return; | |
var guess = Math.ceil(line.text.length / perLine) || 1; | |
if (guess != 1) updateLineHeight(line, guess); | |
}); | |
lineSpace.style.width = code.style.width = ""; | |
} else { | |
wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); | |
maxWidth = null; maxLine = ""; | |
doc.iter(0, doc.size, function(line) { | |
if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); | |
if (line.text.length > maxLine.length) maxLine = line.text; | |
}); | |
} | |
changes.push({from: 0, to: doc.size}); | |
} | |
function computeTabText() { | |
for (var str = '<span class="cm-tab">', i = 0; i < options.tabSize; ++i) str += " "; | |
return str + "</span>"; | |
} | |
function tabsChanged() { | |
tabText = computeTabText(); | |
updateDisplay(true); | |
} | |
function themeChanged() { | |
scroller.className = scroller.className.replace(/\s*cm-s-\w+/g, "") + | |
options.theme.replace(/(^|\s)\s*/g, " cm-s-"); | |
} | |
function TextMarker() { this.set = []; } | |
TextMarker.prototype.clear = operation(function() { | |
var min = Infinity, max = -Infinity; | |
for (var i = 0, e = this.set.length; i < e; ++i) { | |
var line = this.set[i], mk = line.marked; | |
if (!mk || !line.parent) continue; | |
var lineN = lineNo(line); | |
min = Math.min(min, lineN); max = Math.max(max, lineN); | |
for (var j = 0; j < mk.length; ++j) | |
if (mk[j].set == this.set) mk.splice(j--, 1); | |
} | |
if (min != Infinity) | |
changes.push({from: min, to: max + 1}); | |
}); | |
TextMarker.prototype.find = function() { | |
var from, to; | |
for (var i = 0, e = this.set.length; i < e; ++i) { | |
var line = this.set[i], mk = line.marked; | |
for (var j = 0; j < mk.length; ++j) { | |
var mark = mk[j]; | |
if (mark.set == this.set) { | |
if (mark.from != null || mark.to != null) { | |
var found = lineNo(line); | |
if (found != null) { | |
if (mark.from != null) from = {line: found, ch: mark.from}; | |
if (mark.to != null) to = {line: found, ch: mark.to}; | |
} | |
} | |
} | |
} | |
} | |
return {from: from, to: to}; | |
}; | |
function markText(from, to, className) { | |
from = clipPos(from); to = clipPos(to); | |
var tm = new TextMarker(); | |
function add(line, from, to, className) { | |
getLine(line).addMark(new MarkedText(from, to, className, tm.set)); | |
} | |
if (from.line == to.line) add(from.line, from.ch, to.ch, className); | |
else { | |
add(from.line, from.ch, null, className); | |
for (var i = from.line + 1, e = to.line; i < e; ++i) | |
add(i, null, null, className); | |
add(to.line, null, to.ch, className); | |
} | |
changes.push({from: from.line, to: to.line + 1}); | |
return tm; | |
} | |
function setBookmark(pos) { | |
pos = clipPos(pos); | |
var bm = new Bookmark(pos.ch); | |
getLine(pos.line).addMark(bm); | |
return bm; | |
} | |
function addGutterMarker(line, text, className) { | |
if (typeof line == "number") line = getLine(clipLine(line)); | |
line.gutterMarker = {text: text, style: className}; | |
gutterDirty = true; | |
return line; | |
} | |
function removeGutterMarker(line) { | |
if (typeof line == "number") line = getLine(clipLine(line)); | |
line.gutterMarker = null; | |
gutterDirty = true; | |
} | |
function changeLine(handle, op) { | |
var no = handle, line = handle; | |
if (typeof handle == "number") line = getLine(clipLine(handle)); | |
else no = lineNo(handle); | |
if (no == null) return null; | |
if (op(line, no)) changes.push({from: no, to: no + 1}); | |
else return null; | |
return line; | |
} | |
function setLineClass(handle, className) { | |
return changeLine(handle, function(line) { | |
if (line.className != className) { | |
line.className = className; | |
return true; | |
} | |
}); | |
} | |
function setLineHidden(handle, hidden) { | |
return changeLine(handle, function(line, no) { | |
if (line.hidden != hidden) { | |
line.hidden = hidden; | |
updateLineHeight(line, hidden ? 0 : 1); | |
if (hidden && (sel.from.line == no || sel.to.line == no)) | |
setSelection(skipHidden(sel.from, sel.from.line, sel.from.ch), | |
skipHidden(sel.to, sel.to.line, sel.to.ch)); | |
return (gutterDirty = true); | |
} | |
}); | |
} | |
function lineInfo(line) { | |
if (typeof line == "number") { | |
if (!isLine(line)) return null; | |
var n = line; | |
line = getLine(line); | |
if (!line) return null; | |
} | |
else { | |
var n = lineNo(line); | |
if (n == null) return null; | |
} | |
var marker = line.gutterMarker; | |
return {line: n, handle: line, text: line.text, markerText: marker && marker.text, | |
markerClass: marker && marker.style, lineClass: line.className}; | |
} | |
function stringWidth(str) { | |
measure.innerHTML = "<pre><span>x</span></pre>"; | |
measure.firstChild.firstChild.firstChild.nodeValue = str; | |
return measure.firstChild.firstChild.offsetWidth || 10; | |
} | |
// These are used to go from pixel positions to character | |
// positions, taking varying character widths into account. | |
function charFromX(line, x) { | |
if (x <= 0) return 0; | |
var lineObj = getLine(line), text = lineObj.text; | |
function getX(len) { | |
measure.innerHTML = "<pre><span>" + lineObj.getHTML(null, null, false, tabText, len) + "</span></pre>"; | |
return measure.firstChild.firstChild.offsetWidth; | |
} | |
var from = 0, fromX = 0, to = text.length, toX; | |
// Guess a suitable upper bound for our search. | |
var estimated = Math.min(to, Math.ceil(x / charWidth())); | |
for (;;) { | |
var estX = getX(estimated); | |
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); | |
else {toX = estX; to = estimated; break;} | |
} | |
if (x > toX) return to; | |
// Try to guess a suitable lower bound as well. | |
estimated = Math.floor(to * 0.8); estX = getX(estimated); | |
if (estX < x) {from = estimated; fromX = estX;} | |
// Do a binary search between these bounds. | |
for (;;) { | |
if (to - from <= 1) return (toX - x > x - fromX) ? from : to; | |
var middle = Math.ceil((from + to) / 2), middleX = getX(middle); | |
if (middleX > x) {to = middle; toX = middleX;} | |
else {from = middle; fromX = middleX;} | |
} | |
} | |
var tempId = Math.floor(Math.random() * 0xffffff).toString(16); | |
function measureLine(line, ch) { | |
var extra = ""; | |
// Include extra text at the end to make sure the measured line is wrapped in the right way. | |
if (options.lineWrapping) { | |
var end = line.text.indexOf(" ", ch + 2); | |
extra = htmlEscape(line.text.slice(ch + 1, end < 0 ? line.text.length : end + (ie ? 5 : 0))); | |
} | |
measure.innerHTML = "<pre>" + line.getHTML(null, null, false, tabText, ch) + | |
'<span id="CodeMirror-temp-' + tempId + '">' + htmlEscape(line.text.charAt(ch) || " ") + "</span>" + | |
extra + "</pre>"; | |
var elt = document.getElementById("CodeMirror-temp-" + tempId); | |
var top = elt.offsetTop, left = elt.offsetLeft; | |
// Older IEs report zero offsets for spans directly after a wrap | |
if (ie && ch && top == 0 && left == 0) { | |
var backup = document.createElement("span"); | |
backup.innerHTML = "x"; | |
elt.parentNode.insertBefore(backup, elt.nextSibling); | |
top = backup.offsetTop; | |
} | |
return {top: top, left: left}; | |
} | |
function localCoords(pos, inLineWrap) { | |
var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); | |
if (pos.ch == 0) x = 0; | |
else { | |
var sp = measureLine(getLine(pos.line), pos.ch); | |
x = sp.left; | |
if (options.lineWrapping) y += Math.max(0, sp.top); | |
} | |
return {x: x, y: y, yBot: y + lh}; | |
} | |
// Coords must be lineSpace-local | |
function coordsChar(x, y) { | |
if (y < 0) y = 0; | |
var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); | |
var lineNo = lineAtHeight(doc, heightPos); | |
if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; | |
var lineObj = getLine(lineNo), text = lineObj.text; | |
var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; | |
if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; | |
function getX(len) { | |
var sp = measureLine(lineObj, len); | |
if (tw) { | |
var off = Math.round(sp.top / th); | |
return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); | |
} | |
return sp.left; | |
} | |
var from = 0, fromX = 0, to = text.length, toX; | |
// Guess a suitable upper bound for our search. | |
var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); | |
for (;;) { | |
var estX = getX(estimated); | |
if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); | |
else {toX = estX; to = estimated; break;} | |
} | |
if (x > toX) return {line: lineNo, ch: to}; | |
// Try to guess a suitable lower bound as well. | |
estimated = Math.floor(to * 0.8); estX = getX(estimated); | |
if (estX < x) {from = estimated; fromX = estX;} | |
// Do a binary search between these bounds. | |
for (;;) { | |
if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to}; | |
var middle = Math.ceil((from + to) / 2), middleX = getX(middle); | |
if (middleX > x) {to = middle; toX = middleX;} | |
else {from = middle; fromX = middleX;} | |
} | |
} | |
function pageCoords(pos) { | |
var local = localCoords(pos, true), off = eltOffset(lineSpace); | |
return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; | |
} | |
var cachedHeight, cachedHeightFor, measureText; | |
function textHeight() { | |
if (measureText == null) { | |
measureText = "<pre>"; | |
for (var i = 0; i < 49; ++i) measureText += "x<br/>"; | |
measureText += "x</pre>"; | |
} | |
var offsetHeight = lineDiv.clientHeight; | |
if (offsetHeight == cachedHeightFor) return cachedHeight; | |
cachedHeightFor = offsetHeight; | |
measure.innerHTML = measureText; | |
cachedHeight = measure.firstChild.offsetHeight / 50 || 1; | |
measure.innerHTML = ""; | |
return cachedHeight; | |
} | |
var cachedWidth, cachedWidthFor = 0; | |
function charWidth() { | |
if (scroller.clientWidth == cachedWidthFor) return cachedWidth; | |
cachedWidthFor = scroller.clientWidth; | |
return (cachedWidth = stringWidth("x")); | |
} | |
function paddingTop() {return lineSpace.offsetTop;} | |
function paddingLeft() {return lineSpace.offsetLeft;} | |
function posFromMouse(e, liberal) { | |
var offW = eltOffset(scroller, true), x, y; | |
// Fails unpredictably on IE[67] when mouse is dragged around quickly. | |
try { x = e.clientX; y = e.clientY; } catch (e) { return null; } | |
// This is a mess of a heuristic to try and determine whether a | |
// scroll-bar was clicked or not, and to return null if one was | |
// (and !liberal). | |
if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) | |
return null; | |
var offL = eltOffset(lineSpace, true); | |
return coordsChar(x - offL.left, y - offL.top); | |
} | |
function onContextMenu(e) { | |
var pos = posFromMouse(e); | |
if (!pos || window.opera) return; // Opera is difficult. | |
if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) | |
operation(setCursor)(pos.line, pos.ch); | |
var oldCSS = input.style.cssText; | |
inputDiv.style.position = "absolute"; | |
input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + | |
"px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + | |
"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; | |
leaveInputAlone = true; | |
var val = input.value = getSelection(); | |
focusInput(); | |
input.select(); | |
function rehide() { | |
var newVal = splitLines(input.value).join("\n"); | |
if (newVal != val) operation(replaceSelection)(newVal, "end"); | |
inputDiv.style.position = "relative"; | |
input.style.cssText = oldCSS; | |
leaveInputAlone = false; | |
resetInput(true); | |
slowPoll(); | |
} | |
if (gecko) { | |
e_stop(e); | |
var mouseup = connect(window, "mouseup", function() { | |
mouseup(); | |
setTimeout(rehide, 20); | |
}, true); | |
} | |
else { | |
setTimeout(rehide, 50); | |
} | |
} | |
// Cursor-blinking | |
function restartBlink() { | |
clearInterval(blinker); | |
var on = true; | |
cursor.style.visibility = ""; | |
blinker = setInterval(function() { | |
cursor.style.visibility = (on = !on) ? "" : "hidden"; | |
}, 650); | |
} | |
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; | |
function matchBrackets(autoclear) { | |
var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; | |
var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; | |
if (!match) return; | |
var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; | |
for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) | |
if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} | |
var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; | |
function scan(line, from, to) { | |
if (!line.text) return; | |
var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; | |
for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { | |
var text = st[i]; | |
if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;} | |
for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { | |
if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { | |
var match = matching[cur]; | |
if (match.charAt(1) == ">" == forward) stack.push(cur); | |
else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; | |
else if (!stack.length) return {pos: pos, match: true}; | |
} | |
} | |
} | |
} | |
for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { | |
var line = getLine(i), first = i == head.line; | |
var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); | |
if (found) break; | |
} | |
if (!found) found = {pos: null, match: false}; | |
var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; | |
var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), | |
two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); | |
var clear = operation(function(){one.clear(); two && two.clear();}); | |
if (autoclear) setTimeout(clear, 800); | |
else bracketHighlighted = clear; | |
} | |
// Finds the line to start with when starting a parse. Tries to | |
// find a line with a stateAfter, so that it can start with a | |
// valid state. If that fails, it returns the line with the | |
// smallest indentation, which tends to need the least context to | |
// parse correctly. | |
function findStartLine(n) { | |
var minindent, minline; | |
for (var search = n, lim = n - 40; search > lim; --search) { | |
if (search == 0) return 0; | |
var line = getLine(search-1); | |
if (line.stateAfter) return search; | |
var indented = line.indentation(options.tabSize); | |
if (minline == null || minindent > indented) { | |
minline = search - 1; | |
minindent = indented; | |
} | |
} | |
return minline; | |
} | |
function getStateBefore(n) { | |
var start = findStartLine(n), state = start && getLine(start-1).stateAfter; | |
if (!state) state = startState(mode); | |
else state = copyState(mode, state); | |
doc.iter(start, n, function(line) { | |
line.highlight(mode, state, options.tabSize); | |
line.stateAfter = copyState(mode, state); | |
}); | |
if (start < n) changes.push({from: start, to: n}); | |
if (n < doc.size && !getLine(n).stateAfter) work.push(n); | |
return state; | |
} | |
function highlightLines(start, end) { | |
var state = getStateBefore(start); | |
doc.iter(start, end, function(line) { | |
line.highlight(mode, state, options.tabSize); | |
line.stateAfter = copyState(mode, state); | |
}); | |
} | |
function highlightWorker() { | |
var end = +new Date + options.workTime; | |
var foundWork = work.length; | |
while (work.length) { | |
if (!getLine(showingFrom).stateAfter) var task = showingFrom; | |
else var task = work.pop(); | |
if (task >= doc.size) continue; | |
var start = findStartLine(task), state = start && getLine(start-1).stateAfter; | |
if (state) state = copyState(mode, state); | |
else state = startState(mode); | |
var unchanged = 0, compare = mode.compareStates, realChange = false, | |
i = start, bail = false; | |
doc.iter(i, doc.size, function(line) { | |
var hadState = line.stateAfter; | |
if (+new Date > end) { | |
work.push(i); | |
startWorker(options.workDelay); | |
if (realChange) changes.push({from: task, to: i + 1}); | |
return (bail = true); | |
} | |
var changed = line.highlight(mode, state, options.tabSize); | |
if (changed) realChange = true; | |
line.stateAfter = copyState(mode, state); | |
if (compare) { | |
if (hadState && compare(hadState, state)) return true; | |
} else { | |
if (changed !== false || !hadState) unchanged = 0; | |
else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) | |
return true; | |
} | |
++i; | |
}); | |
if (bail) return; | |
if (realChange) changes.push({from: task, to: i + 1}); | |
} | |
if (foundWork && options.onHighlightComplete) | |
options.onHighlightComplete(instance); | |
} | |
function startWorker(time) { | |
if (!work.length) return; | |
highlight.set(time, operation(highlightWorker)); | |
} | |
// Operations are used to wrap changes in such a way that each | |
// change won't have to update the cursor and display (which would | |
// be awkward, slow, and error-prone), but instead updates are | |
// batched and then all combined and executed at once. | |
function startOperation() { | |
updateInput = userSelChange = textChanged = null; | |
changes = []; selectionChanged = false; callbacks = []; | |
} | |
function endOperation() { | |
var reScroll = false, updated; | |
if (selectionChanged) reScroll = !scrollCursorIntoView(); | |
if (changes.length) updated = updateDisplay(changes, true); | |
else { | |
if (selectionChanged) updateCursor(); | |
if (gutterDirty) updateGutter(); | |
} | |
if (reScroll) scrollCursorIntoView(); | |
if (selectionChanged) {scrollEditorIntoView(); restartBlink();} | |
if (focused && !leaveInputAlone && | |
(updateInput === true || (updateInput !== false && selectionChanged))) | |
resetInput(userSelChange); | |
if (selectionChanged && options.matchBrackets) | |
setTimeout(operation(function() { | |
if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} | |
if (posEq(sel.from, sel.to)) matchBrackets(false); | |
}), 20); | |
var tc = textChanged, cbs = callbacks; // these can be reset by callbacks | |
if (selectionChanged && options.onCursorActivity) | |
options.onCursorActivity(instance); | |
if (tc && options.onChange && instance) | |
options.onChange(instance, tc); | |
for (var i = 0; i < cbs.length; ++i) cbs[i](instance); | |
if (updated && options.onUpdate) options.onUpdate(instance); | |
} | |
var nestedOperation = 0; | |
function operation(f) { | |
return function() { | |
if (!nestedOperation++) startOperation(); | |
try {var result = f.apply(this, arguments);} | |
finally {if (!--nestedOperation) endOperation();} | |
return result; | |
}; | |
} | |
for (var ext in extensions) | |
if (extensions.propertyIsEnumerable(ext) && | |
!instance.propertyIsEnumerable(ext)) | |
instance[ext] = extensions[ext]; | |
return instance; | |
} // (end of function CodeMirror) | |
// The default configuration options. | |
CodeMirror.defaults = { | |
value: "", | |
mode: null, | |
theme: "default", | |
indentUnit: 2, | |
indentWithTabs: false, | |
tabSize: 4, | |
keyMap: "default", | |
extraKeys: null, | |
electricChars: true, | |
onKeyEvent: null, | |
lineWrapping: false, | |
lineNumbers: false, | |
gutter: false, | |
fixedGutter: false, | |
firstLineNumber: 1, | |
readOnly: false, | |
onChange: null, | |
onCursorActivity: null, | |
onGutterClick: null, | |
onHighlightComplete: null, | |
onUpdate: null, | |
onFocus: null, onBlur: null, onScroll: null, | |
matchBrackets: false, | |
workTime: 100, | |
workDelay: 200, | |
pollInterval: 100, | |
undoDepth: 40, | |
tabindex: null, | |
document: window.document | |
}; | |
var mac = /Mac/.test(navigator.platform); | |
var win = /Win/.test(navigator.platform); | |
// Known modes, by name and by MIME | |
var modes = {}, mimeModes = {}; | |
CodeMirror.defineMode = function(name, mode) { | |
if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; | |
modes[name] = mode; | |
}; | |
CodeMirror.defineMIME = function(mime, spec) { | |
mimeModes[mime] = spec; | |
}; | |
CodeMirror.getMode = function(options, spec) { | |
if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) | |
spec = mimeModes[spec]; | |
if (typeof spec == "string") | |
var mname = spec, config = {}; | |
else if (spec != null) | |
var mname = spec.name, config = spec; | |
var mfactory = modes[mname]; | |
if (!mfactory) { | |
if (window.console) console.warn("No mode " + mname + " found, falling back to plain text."); | |
return CodeMirror.getMode(options, "text/plain"); | |
} | |
return mfactory(options, config || {}); | |
}; | |
CodeMirror.listModes = function() { | |
var list = []; | |
for (var m in modes) | |
if (modes.propertyIsEnumerable(m)) list.push(m); | |
return list; | |
}; | |
CodeMirror.listMIMEs = function() { | |
var list = []; | |
for (var m in mimeModes) | |
if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); | |
return list; | |
}; | |
var extensions = CodeMirror.extensions = {}; | |
CodeMirror.defineExtension = function(name, func) { | |
extensions[name] = func; | |
}; | |
var commands = CodeMirror.commands = { | |
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, | |
killLine: function(cm) { | |
var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); | |
if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); | |
else cm.replaceRange("", from, sel ? to : {line: from.line}); | |
}, | |
deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, | |
undo: function(cm) {cm.undo();}, | |
redo: function(cm) {cm.redo();}, | |
goDocStart: function(cm) {cm.setCursor(0, 0, true);}, | |
goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, | |
goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, | |
goLineStartSmart: function(cm) { | |
var cur = cm.getCursor(); | |
var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); | |
cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); | |
}, | |
goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, | |
goLineUp: function(cm) {cm.moveV(-1, "line");}, | |
goLineDown: function(cm) {cm.moveV(1, "line");}, | |
goPageUp: function(cm) {cm.moveV(-1, "page");}, | |
goPageDown: function(cm) {cm.moveV(1, "page");}, | |
goCharLeft: function(cm) {cm.moveH(-1, "char");}, | |
goCharRight: function(cm) {cm.moveH(1, "char");}, | |
goColumnLeft: function(cm) {cm.moveH(-1, "column");}, | |
goColumnRight: function(cm) {cm.moveH(1, "column");}, | |
goWordLeft: function(cm) {cm.moveH(-1, "word");}, | |
goWordRight: function(cm) {cm.moveH(1, "word");}, | |
delCharLeft: function(cm) {cm.deleteH(-1, "char");}, | |
delCharRight: function(cm) {cm.deleteH(1, "char");}, | |
delWordLeft: function(cm) {cm.deleteH(-1, "word");}, | |
delWordRight: function(cm) {cm.deleteH(1, "word");}, | |
indentAuto: function(cm) {cm.indentSelection("smart");}, | |
indentMore: function(cm) {cm.indentSelection("add");}, | |
indentLess: function(cm) {cm.indentSelection("subtract");}, | |
insertTab: function(cm) {cm.replaceSelection("\t", "end");}, | |
transposeChars: function(cm) { | |
var cur = cm.getCursor(), line = cm.getLine(cur.line); | |
if (cur.ch > 0 && cur.ch < line.length - 1) | |
cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), | |
{line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1}); | |
}, | |
newlineAndIndent: function(cm) { | |
cm.replaceSelection("\n", "end"); | |
cm.indentLine(cm.getCursor().line); | |
}, | |
toggleOverwrite: function(cm) {cm.toggleOverwrite();} | |
}; | |
var keyMap = CodeMirror.keyMap = {}; | |
keyMap.basic = { | |
"Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", | |
"End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", | |
"Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "indentMore", "Shift-Tab": "indentLess", | |
"Enter": "newlineAndIndent", "Insert": "toggleOverwrite" | |
}; | |
// Note that the save and find-related commands aren't defined by | |
// default. Unknown commands are simply ignored. | |
keyMap.pcDefault = { | |
"Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", | |
"Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", | |
"Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", | |
"Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", | |
"Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", | |
fallthrough: "basic" | |
}; | |
keyMap.macDefault = { | |
"Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", | |
"Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", | |
"Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", | |
"Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", | |
"Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", | |
fallthrough: ["basic", "emacsy"] | |
}; | |
keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; | |
keyMap.emacsy = { | |
"Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", | |
"Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", | |
"Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", | |
"Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" | |
}; | |
function lookupKey(name, extraMap, map) { | |
function lookup(name, map, ft) { | |
var found = map[name]; | |
if (found != null) return found; | |
if (ft == null) ft = map.fallthrough; | |
if (ft == null) return map.catchall; | |
if (typeof ft == "string") return lookup(name, keyMap[ft]); | |
for (var i = 0, e = ft.length; i < e; ++i) { | |
found = lookup(name, keyMap[ft[i]]); | |
if (found != null) return found; | |
} | |
return null; | |
} | |
return extraMap ? lookup(name, extraMap, map) : lookup(name, keyMap[map]); | |
} | |
function isModifierKey(event) { | |
var name = keyNames[event.keyCode]; | |
return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; | |
} | |
CodeMirror.fromTextArea = function(textarea, options) { | |
if (!options) options = {}; | |
options.value = textarea.value; | |
if (!options.tabindex && textarea.tabindex) | |
options.tabindex = textarea.tabindex; | |
function save() {textarea.value = instance.getValue();} | |
if (textarea.form) { | |
// Deplorable hack to make the submit method do the right thing. | |
var rmSubmit = connect(textarea.form, "submit", save, true); | |
if (typeof textarea.form.submit == "function") { | |
var realSubmit = textarea.form.submit; | |
function wrappedSubmit() { | |
save(); | |
textarea.form.submit = realSubmit; | |
textarea.form.submit(); | |
textarea.form.submit = wrappedSubmit; | |
} | |
textarea.form.submit = wrappedSubmit; | |
} | |
} | |
textarea.style.display = "none"; | |
var instance = CodeMirror(function(node) { | |
textarea.parentNode.insertBefore(node, textarea.nextSibling); | |
}, options); | |
instance.save = save; | |
instance.getTextArea = function() { return textarea; }; | |
instance.toTextArea = function() { | |
save(); | |
textarea.parentNode.removeChild(instance.getWrapperElement()); | |
textarea.style.display = ""; | |
if (textarea.form) { | |
rmSubmit(); | |
if (typeof textarea.form.submit == "function") | |
textarea.form.submit = realSubmit; | |
} | |
}; | |
return instance; | |
}; | |
// Utility functions for working with state. Exported because modes | |
// sometimes need to do this. | |
function copyState(mode, state) { | |
if (state === true) return state; | |
if (mode.copyState) return mode.copyState(state); | |
var nstate = {}; | |
for (var n in state) { | |
var val = state[n]; | |
if (val instanceof Array) val = val.concat([]); | |
nstate[n] = val; | |
} | |
return nstate; | |
} | |
CodeMirror.copyState = copyState; | |
function startState(mode, a1, a2) { | |
return mode.startState ? mode.startState(a1, a2) : true; | |
} | |
CodeMirror.startState = startState; | |
// The character stream used by a mode's parser. | |
function StringStream(string, tabSize) { | |
this.pos = this.start = 0; | |
this.string = string; | |
this.tabSize = tabSize || 8; | |
} | |
StringStream.prototype = { | |
eol: function() {return this.pos >= this.string.length;}, | |
sol: function() {return this.pos == 0;}, | |
peek: function() {return this.string.charAt(this.pos);}, | |
next: function() { | |
if (this.pos < this.string.length) | |
return this.string.charAt(this.pos++); | |
}, | |
eat: function(match) { | |
var ch = this.string.charAt(this.pos); | |
if (typeof match == "string") var ok = ch == match; | |
else var ok = ch && (match.test ? match.test(ch) : match(ch)); | |
if (ok) {++this.pos; return ch;} | |
}, | |
eatWhile: function(match) { | |
var start = this.pos; | |
while (this.eat(match)){} | |
return this.pos > start; | |
}, | |
eatSpace: function() { | |
var start = this.pos; | |
while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; | |
return this.pos > start; | |
}, | |
skipToEnd: function() {this.pos = this.string.length;}, | |
skipTo: function(ch) { | |
var found = this.string.indexOf(ch, this.pos); | |
if (found > -1) {this.pos = found; return true;} | |
}, | |
backUp: function(n) {this.pos -= n;}, | |
column: function() {return countColumn(this.string, this.start, this.tabSize);}, | |
indentation: function() {return countColumn(this.string, null, this.tabSize);}, | |
match: function(pattern, consume, caseInsensitive) { | |
if (typeof pattern == "string") { | |
function cased(str) {return caseInsensitive ? str.toLowerCase() : 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; | |
} | |
}, | |
current: function(){return this.string.slice(this.start, this.pos);} | |
}; | |
CodeMirror.StringStream = StringStream; | |
function MarkedText(from, to, className, set) { | |
this.from = from; this.to = to; this.style = className; this.set = set; | |
} | |
MarkedText.prototype = { | |
attach: function(line) { this.set.push(line); }, | |
detach: function(line) { | |
var ix = indexOf(this.set, line); | |
if (ix > -1) this.set.splice(ix, 1); | |
}, | |
split: function(pos, lenBefore) { | |
if (this.to <= pos && this.to != null) return null; | |
var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; | |
var to = this.to == null ? null : this.to - pos + lenBefore; | |
return new MarkedText(from, to, this.style, this.set); | |
}, | |
dup: function() { return new MarkedText(null, null, this.style, this.set); }, | |
clipTo: function(fromOpen, from, toOpen, to, diff) { | |
if (this.from != null && this.from >= from) | |
this.from = Math.max(to, this.from) + diff; | |
if (this.to != null && this.to > from) | |
this.to = to < this.to ? this.to + diff : from; | |
if (fromOpen && to > this.from && (to < this.to || this.to == null)) | |
this.from = null; | |
if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) | |
this.to = null; | |
}, | |
isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, | |
sameSet: function(x) { return this.set == x.set; } | |
}; | |
function Bookmark(pos) { | |
this.from = pos; this.to = pos; this.line = null; | |
} | |
Bookmark.prototype = { | |
attach: function(line) { this.line = line; }, | |
detach: function(line) { if (this.line == line) this.line = null; }, | |
split: function(pos, lenBefore) { | |
if (pos < this.from) { | |
this.from = this.to = (this.from - pos) + lenBefore; | |
return this; | |
} | |
}, | |
isDead: function() { return this.from > this.to; }, | |
clipTo: function(fromOpen, from, toOpen, to, diff) { | |
if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { | |
this.from = 0; this.to = -1; | |
} else if (this.from > from) { | |
this.from = this.to = Math.max(to, this.from) + diff; | |
} | |
}, | |
sameSet: function(x) { return false; }, | |
find: function() { | |
if (!this.line || !this.line.parent) return null; | |
return {line: lineNo(this.line), ch: this.from}; | |
}, | |
clear: function() { | |
if (this.line) { | |
var found = indexOf(this.line.marked, this); | |
if (found != -1) this.line.marked.splice(found, 1); | |
this.line = null; | |
} | |
} | |
}; | |
// Line objects. These hold state related to a line, including | |
// highlighting info (the styles array). | |
function Line(text, styles) { | |
this.styles = styles || [text, null]; | |
this.text = text; | |
this.height = 1; | |
this.marked = this.gutterMarker = this.className = this.handlers = null; | |
this.stateAfter = this.parent = this.hidden = null; | |
} | |
Line.inheritMarks = function(text, orig) { | |
var ln = new Line(text), mk = orig && orig.marked; | |
if (mk) { | |
for (var i = 0; i < mk.length; ++i) { | |
if (mk[i].to == null && mk[i].style) { | |
var newmk = ln.marked || (ln.marked = []), mark = mk[i]; | |
var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); | |
} | |
} | |
} | |
return ln; | |
} | |
Line.prototype = { | |
// Replace a piece of a line, keeping the styles around it intact. | |
replace: function(from, to_, text) { | |
var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; | |
copyStyles(0, from, this.styles, st); | |
if (text) st.push(text, null); | |
copyStyles(to, this.text.length, this.styles, st); | |
this.styles = st; | |
this.text = this.text.slice(0, from) + text + this.text.slice(to); | |
this.stateAfter = null; | |
if (mk) { | |
var diff = text.length - (to - from); | |
for (var i = 0, mark = mk[i]; i < mk.length; ++i) { | |
mark.clipTo(from == null, from || 0, to_ == null, to, diff); | |
if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} | |
} | |
} | |
}, | |
// Split a part off a line, keeping styles and markers intact. | |
split: function(pos, textBefore) { | |
var st = [textBefore, null], mk = this.marked; | |
copyStyles(pos, this.text.length, this.styles, st); | |
var taken = new Line(textBefore + this.text.slice(pos), st); | |
if (mk) { | |
for (var i = 0; i < mk.length; ++i) { | |
var mark = mk[i]; | |
var newmark = mark.split(pos, textBefore.length); | |
if (newmark) { | |
if (!taken.marked) taken.marked = []; | |
taken.marked.push(newmark); newmark.attach(taken); | |
} | |
} | |
} | |
return taken; | |
}, | |
append: function(line) { | |
var mylen = this.text.length, mk = line.marked, mymk = this.marked; | |
this.text += line.text; | |
copyStyles(0, line.text.length, line.styles, this.styles); | |
if (mymk) { | |
for (var i = 0; i < mymk.length; ++i) | |
if (mymk[i].to == null) mymk[i].to = mylen; | |
} | |
if (mk && mk.length) { | |
if (!mymk) this.marked = mymk = []; | |
outer: for (var i = 0; i < mk.length; ++i) { | |
var mark = mk[i]; | |
if (!mark.from) { | |
for (var j = 0; j < mymk.length; ++j) { | |
var mymark = mymk[j]; | |
if (mymark.to == mylen && mymark.sameSet(mark)) { | |
mymark.to = mark.to == null ? null : mark.to + mylen; | |
if (mymark.isDead()) { | |
mymark.detach(this); | |
mk.splice(i--, 1); | |
} | |
continue outer; | |
} | |
} | |
} | |
mymk.push(mark); | |
mark.attach(this); | |
mark.from += mylen; | |
if (mark.to != null) mark.to += mylen; | |
} | |
} | |
}, | |
fixMarkEnds: function(other) { | |
var mk = this.marked, omk = other.marked; | |
if (!mk) return; | |
for (var i = 0; i < mk.length; ++i) { | |
var mark = mk[i], close = mark.to == null; | |
if (close && omk) { | |
for (var j = 0; j < omk.length; ++j) | |
if (omk[j].sameSet(mark)) {close = false; break;} | |
} | |
if (close) mark.to = this.text.length; | |
} | |
}, | |
fixMarkStarts: function() { | |
var mk = this.marked; | |
if (!mk) return; | |
for (var i = 0; i < mk.length; ++i) | |
if (mk[i].from == null) mk[i].from = 0; | |
}, | |
addMark: function(mark) { | |
mark.attach(this); | |
if (this.marked == null) this.marked = []; | |
this.marked.push(mark); | |
this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); | |
}, | |
// Run the given mode's parser over a line, update the styles | |
// array, which contains alternating fragments of text and CSS | |
// classes. | |
highlight: function(mode, state, tabSize) { | |
var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; | |
var changed = false, curWord = st[0], prevWord; | |
if (this.text == "" && mode.blankLine) mode.blankLine(state); | |
while (!stream.eol()) { | |
var style = mode.token(stream, state); | |
var substr = this.text.slice(stream.start, stream.pos); | |
stream.start = stream.pos; | |
if (pos && st[pos-1] == style) | |
st[pos-2] += substr; | |
else if (substr) { | |
if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; | |
st[pos++] = substr; st[pos++] = style; | |
prevWord = curWord; curWord = st[pos]; | |
} | |
// Give up when line is ridiculously long | |
if (stream.pos > 5000) { | |
st[pos++] = this.text.slice(stream.pos); st[pos++] = null; | |
break; | |
} | |
} | |
if (st.length != pos) {st.length = pos; changed = true;} | |
if (pos && st[pos-2] != prevWord) changed = true; | |
// Short lines with simple highlights return null, and are | |
// counted as changed by the driver because they are likely to | |
// highlight the same way in various contexts. | |
return changed || (st.length < 5 && this.text.length < 10 ? null : false); | |
}, | |
// Fetch the parser token for a given character. Useful for hacks | |
// that want to inspect the mode state (say, for completion). | |
getTokenAt: function(mode, state, ch) { | |
var txt = this.text, stream = new StringStream(txt); | |
while (stream.pos < ch && !stream.eol()) { | |
stream.start = stream.pos; | |
var style = mode.token(stream, state); | |
} | |
return {start: stream.start, | |
end: stream.pos, | |
string: stream.current(), | |
className: style || null, | |
state: state}; | |
}, | |
indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, | |
// Produces an HTML fragment for the line, taking selection, | |
// marking, and highlighting into account. | |
getHTML: function(sfrom, sto, includePre, tabText, endAt) { | |
var html = [], first = true; | |
if (includePre) | |
html.push(this.className ? '<pre class="' + this.className + '">': "<pre>"); | |
function span(text, style) { | |
if (!text) return; | |
// Work around a bug where, in some compat modes, IE ignores leading spaces | |
if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); | |
first = false; | |
if (style) html.push('<span class="', style, '">', htmlEscape(text).replace(/\t/g, tabText), "</span>"); | |
else html.push(htmlEscape(text).replace(/\t/g, tabText)); | |
} | |
var st = this.styles, allText = this.text, marked = this.marked; | |
if (sfrom == sto) sfrom = null; | |
var len = allText.length; | |
if (endAt != null) len = Math.min(endAt, len); | |
if (!allText && endAt == null) | |
span(" ", sfrom != null && sto == null ? "CodeMirror-selected" : null); | |
else if (!marked && sfrom == null) | |
for (var i = 0, ch = 0; ch < len; i+=2) { | |
var str = st[i], style = st[i+1], l = str.length; | |
if (ch + l > len) str = str.slice(0, len - ch); | |
ch += l; | |
span(str, style && "cm-" + style); | |
} | |
else { | |
var pos = 0, i = 0, text = "", style, sg = 0; | |
var markpos = -1, mark = null; | |
function nextMark() { | |
if (marked) { | |
markpos += 1; | |
mark = (markpos < marked.length) ? marked[markpos] : null; | |
} | |
} | |
nextMark(); | |
while (pos < len) { | |
var upto = len; | |
var extraStyle = ""; | |
if (sfrom != null) { | |
if (sfrom > pos) upto = sfrom; | |
else if (sto == null || sto > pos) { | |
extraStyle = " CodeMirror-selected"; | |
if (sto != null) upto = Math.min(upto, sto); | |
} | |
} | |
while (mark && mark.to != null && mark.to <= pos) nextMark(); | |
if (mark) { | |
if (mark.from > pos) upto = Math.min(upto, mark.from); | |
else { | |
extraStyle += " " + mark.style; | |
if (mark.to != null) upto = Math.min(upto, mark.to); | |
} | |
} | |
for (;;) { | |
var end = pos + text.length; | |
var appliedStyle = style; | |
if (extraStyle) appliedStyle = style ? style + extraStyle : extraStyle; | |
span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); | |
if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} | |
pos = end; | |
text = st[i++]; style = "cm-" + st[i++]; | |
} | |
} | |
if (sfrom != null && sto == null) span(" ", "CodeMirror-selected"); | |
} | |
if (includePre) html.push("</pre>"); | |
return html.join(""); | |
}, | |
cleanUp: function() { | |
this.parent = null; | |
if (this.marked) | |
for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); | |
} | |
}; | |
// Utility used by replace and split above | |
function copyStyles(from, to, source, dest) { | |
for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { | |
var part = source[i], end = pos + part.length; | |
if (state == 0) { | |
if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); | |
if (end >= from) state = 1; | |
} | |
else if (state == 1) { | |
if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); | |
else dest.push(part, source[i+1]); | |
} | |
pos = end; | |
} | |
} | |
// Data structure that holds the sequence of lines. | |
function LeafChunk(lines) { | |
this.lines = lines; | |
this.parent = null; | |
for (var i = 0, e = lines.length, height = 0; i < e; ++i) { | |
lines[i].parent = this; | |
height += lines[i].height; | |
} | |
this.height = height; | |
} | |
LeafChunk.prototype = { | |
chunkSize: function() { return this.lines.length; }, | |
remove: function(at, n, callbacks) { | |
for (var i = at, e = at + n; i < e; ++i) { | |
var line = this.lines[i]; | |
this.height -= line.height; | |
line.cleanUp(); | |
if (line.handlers) | |
for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); | |
} | |
this.lines.splice(at, n); | |
}, | |
collapse: function(lines) { | |
lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); | |
}, | |
insertHeight: function(at, lines, height) { | |
this.height += height; | |
this.lines.splice.apply(this.lines, [at, 0].concat(lines)); | |
for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this; | |
}, | |
iterN: function(at, n, op) { | |
for (var e = at + n; at < e; ++at) | |
if (op(this.lines[at])) return true; | |
} | |
}; | |
function BranchChunk(children) { | |
this.children = children; | |
var size = 0, height = 0; | |
for (var i = 0, e = children.length; i < e; ++i) { | |
var ch = children[i]; | |
size += ch.chunkSize(); height += ch.height; | |
ch.parent = this; | |
} | |
this.size = size; | |
this.height = height; | |
this.parent = null; | |
} | |
BranchChunk.prototype = { | |
chunkSize: function() { return this.size; }, | |
remove: function(at, n, callbacks) { | |
this.size -= n; | |
for (var i = 0; i < this.children.length; ++i) { | |
var child = this.children[i], sz = child.chunkSize(); | |
if (at < sz) { | |
var rm = Math.min(n, sz - at), oldHeight = child.height; | |
child.remove(at, rm, callbacks); | |
this.height -= oldHeight - child.height; | |
if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } | |
if ((n -= rm) == 0) break; | |
at = 0; | |
} else at -= sz; | |
} | |
if (this.size - n < 25) { | |
var lines = []; | |
this.collapse(lines); | |
this.children = [new LeafChunk(lines)]; | |
} | |
}, | |
collapse: function(lines) { | |
for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines); | |
}, | |
insert: function(at, lines) { | |
var height = 0; | |
for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; | |
this.insertHeight(at, lines, height); | |
}, | |
insertHeight: function(at, lines, height) { | |
this.size += lines.length; | |
this.height += height; | |
for (var i = 0, e = this.children.length; i < e; ++i) { | |
var child = this.children[i], sz = child.chunkSize(); | |
if (at <= sz) { | |
child.insertHeight(at, lines, height); | |
if (child.lines && child.lines.length > 50) { | |
while (child.lines.length > 50) { | |
var spilled = child.lines.splice(child.lines.length - 25, 25); | |
var newleaf = new LeafChunk(spilled); | |
child.height -= newleaf.height; | |
this.children.splice(i + 1, 0, newleaf); | |
newleaf.parent = this; | |
} | |
this.maybeSpill(); | |
} | |
break; | |
} | |
at -= sz; | |
} | |
}, | |
maybeSpill: function() { | |
if (this.children.length <= 10) return; | |
var me = this; | |
do { | |
var spilled = me.children.splice(me.children.length - 5, 5); | |
var sibling = new BranchChunk(spilled); | |
if (!me.parent) { // Become the parent node | |
var copy = new BranchChunk(me.children); | |
copy.parent = me; | |
me.children = [copy, sibling]; | |
me = copy; | |
} else { | |
me.size -= sibling.size; | |
me.height -= sibling.height; | |
var myIndex = indexOf(me.parent.children, me); | |
me.parent.children.splice(myIndex + 1, 0, sibling); | |
} | |
sibling.parent = me.parent; | |
} while (me.children.length > 10); | |
me.parent.maybeSpill(); | |
}, | |
iter: function(from, to, op) { this.iterN(from, to - from, op); }, | |
iterN: function(at, n, op) { | |
for (var i = 0, e = this.children.length; i < e; ++i) { | |
var child = this.children[i], sz = child.chunkSize(); | |
if (at < sz) { | |
var used = Math.min(n, sz - at); | |
if (child.iterN(at, used, op)) return true; | |
if ((n -= used) == 0) break; | |
at = 0; | |
} else at -= sz; | |
} | |
} | |
}; | |
function getLineAt(chunk, n) { | |
while (!chunk.lines) { | |
for (var i = 0;; ++i) { | |
var child = chunk.children[i], sz = child.chunkSize(); | |
if (n < sz) { chunk = child; break; } | |
n -= sz; | |
} | |
} | |
return chunk.lines[n]; | |
} | |
function lineNo(line) { | |
if (line.parent == null) return null; | |
var cur = line.parent, no = indexOf(cur.lines, line); | |
for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { | |
for (var i = 0, e = chunk.children.length; ; ++i) { | |
if (chunk.children[i] == cur) break; | |
no += chunk.children[i].chunkSize(); | |
} | |
} | |
return no; | |
} | |
function lineAtHeight(chunk, h) { | |
var n = 0; | |
outer: do { | |
for (var i = 0, e = chunk.children.length; i < e; ++i) { | |
var child = chunk.children[i], ch = child.height; | |
if (h < ch) { chunk = child; continue outer; } | |
h -= ch; | |
n += child.chunkSize(); | |
} | |
return n; | |
} while (!chunk.lines); | |
for (var i = 0, e = chunk.lines.length; i < e; ++i) { | |
var line = chunk.lines[i], lh = line.height; | |
if (h < lh) break; | |
h -= lh; | |
} | |
return n + i; | |
} | |
function heightAtLine(chunk, n) { | |
var h = 0; | |
outer: do { | |
for (var i = 0, e = chunk.children.length; i < e; ++i) { | |
var child = chunk.children[i], sz = child.chunkSize(); | |
if (n < sz) { chunk = child; continue outer; } | |
n -= sz; | |
h += child.height; | |
} | |
return h; | |
} while (!chunk.lines); | |
for (var i = 0; i < n; ++i) h += chunk.lines[i].height; | |
return h; | |
} | |
// The history object 'chunks' changes that are made close together | |
// and at almost the same time into bigger undoable units. | |
function History() { | |
this.time = 0; | |
this.done = []; this.undone = []; | |
} | |
History.prototype = { | |
addChange: function(start, added, old) { | |
this.undone.length = 0; | |
var time = +new Date, last = this.done[this.done.length - 1]; | |
if (time - this.time > 400 || !last || | |
last.start > start + added || last.start + last.added < start - last.added + last.old.length) | |
this.done.push({start: start, added: added, old: old}); | |
else { | |
var oldoff = 0; | |
if (start < last.start) { | |
for (var i = last.start - start - 1; i >= 0; --i) | |
last.old.unshift(old[i]); | |
last.added += last.start - start; | |
last.start = start; | |
} | |
else if (last.start < start) { | |
oldoff = start - last.start; | |
added += oldoff; | |
} | |
for (var i = last.added - oldoff, e = old.length; i < e; ++i) | |
last.old.push(old[i]); | |
if (last.added < added) last.added = added; | |
} | |
this.time = time; | |
} | |
}; | |
function stopMethod() {e_stop(this);} | |
// Ensure an event has a stop method. | |
function addStop(event) { | |
if (!event.stop) event.stop = stopMethod; | |
return event; | |
} | |
function e_preventDefault(e) { | |
if (e.preventDefault) e.preventDefault(); | |
else e.returnValue = false; | |
} | |
function e_stopPropagation(e) { | |
if (e.stopPropagation) e.stopPropagation(); | |
else e.cancelBubble = true; | |
} | |
function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} | |
CodeMirror.e_stop = e_stop; | |
CodeMirror.e_preventDefault = e_preventDefault; | |
CodeMirror.e_stopPropagation = e_stopPropagation; | |
function e_target(e) {return e.target || e.srcElement;} | |
function e_button(e) { | |
if (e.which) return e.which; | |
else if (e.button & 1) return 1; | |
else if (e.button & 2) return 3; | |
else if (e.button & 4) return 2; | |
} | |
// Event handler registration. If disconnect is true, it'll return a | |
// function that unregisters the handler. | |
function connect(node, type, handler, disconnect) { | |
if (typeof node.addEventListener == "function") { | |
node.addEventListener(type, handler, false); | |
if (disconnect) return function() {node.removeEventListener(type, handler, false);}; | |
} | |
else { | |
var wrapHandler = function(event) {handler(event || window.event);}; | |
node.attachEvent("on" + type, wrapHandler); | |
if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; | |
} | |
} | |
CodeMirror.connect = connect; | |
function Delayed() {this.id = null;} | |
Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; | |
// Detect drag-and-drop | |
var dragAndDrop = function() { | |
// IE8 has ondragstart and ondrop properties, but doesn't seem to | |
// actually support ondragstart the way it's supposed to work. | |
if (/MSIE [1-8]\b/.test(navigator.userAgent)) return false; | |
var div = document.createElement('div'); | |
return "draggable" in div; | |
}(); | |
var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); | |
var ie = /MSIE \d/.test(navigator.userAgent); | |
var webkit = /WebKit\//.test(navigator.userAgent); | |
var lineSep = "\n"; | |
// Feature-detect whether newlines in textareas are converted to \r\n | |
(function () { | |
var te = document.createElement("textarea"); | |
te.value = "foo\nbar"; | |
if (te.value.indexOf("\r") > -1) lineSep = "\r\n"; | |
}()); | |
// Counts the column offset in a string, taking tabs into account. | |
// Used mostly to find indentation. | |
function countColumn(string, end, tabSize) { | |
if (end == null) { | |
end = string.search(/[^\s\u00a0]/); | |
if (end == -1) end = string.length; | |
} | |
for (var i = 0, n = 0; i < end; ++i) { | |
if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); | |
else ++n; | |
} | |
return n; | |
} | |
function computedStyle(elt) { | |
if (elt.currentStyle) return elt.currentStyle; | |
return window.getComputedStyle(elt, null); | |
} | |
// Find the position of an element by following the offsetParent chain. | |
// If screen==true, it returns screen (rather than page) coordinates. | |
function eltOffset(node, screen) { | |
var bod = node.ownerDocument.body; | |
var x = 0, y = 0, skipBody = false; | |
for (var n = node; n; n = n.offsetParent) { | |
var ol = n.offsetLeft, ot = n.offsetTop; | |
// Firefox reports weird inverted offsets when the body has a border. | |
if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); } | |
else { x += ol, y += ot; } | |
if (screen && computedStyle(n).position == "fixed") | |
skipBody = true; | |
} | |
var e = screen && !skipBody ? null : bod; | |
for (var n = node.parentNode; n != e; n = n.parentNode) | |
if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;} | |
return {left: x, top: y}; | |
} | |
// Use the faster and saner getBoundingClientRect method when possible. | |
if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) { | |
// Take the parts of bounding client rect that we are interested in so we are able to edit if need be, | |
// since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) | |
try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } | |
catch(e) { box = {top: 0, left: 0}; } | |
if (!screen) { | |
// Get the toplevel scroll, working around browser differences. | |
if (window.pageYOffset == null) { | |
var t = document.documentElement || document.body.parentNode; | |
if (t.scrollTop == null) t = document.body; | |
box.top += t.scrollTop; box.left += t.scrollLeft; | |
} else { | |
box.top += window.pageYOffset; box.left += window.pageXOffset; | |
} | |
} | |
return box; | |
}; | |
// Get a node's text content. | |
function eltText(node) { | |
return node.textContent || node.innerText || node.nodeValue || ""; | |
} | |
// Operations on {line, ch} objects. | |
function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} | |
function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} | |
function copyPos(x) {return {line: x.line, ch: x.ch};} | |
var escapeElement = document.createElement("pre"); | |
function htmlEscape(str) { | |
escapeElement.textContent = str; | |
return escapeElement.innerHTML; | |
} | |
// Recent (late 2011) Opera betas insert bogus newlines at the start | |
// of the textContent, so we strip those. | |
if (htmlEscape("a") == "\na") | |
htmlEscape = function(str) { | |
escapeElement.textContent = str; | |
return escapeElement.innerHTML.slice(1); | |
}; | |
// Some IEs don't preserve tabs through innerHTML | |
else if (htmlEscape("\t") != "\t") | |
htmlEscape = function(str) { | |
escapeElement.innerHTML = ""; | |
escapeElement.appendChild(document.createTextNode(str)); | |
return escapeElement.innerHTML; | |
}; | |
CodeMirror.htmlEscape = htmlEscape; | |
// Used to position the cursor after an undo/redo by finding the | |
// last edited character. | |
function editEnd(from, to) { | |
if (!to) return from ? from.length : 0; | |
if (!from) return to.length; | |
for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) | |
if (from.charAt(i) != to.charAt(j)) break; | |
return j + 1; | |
} | |
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; | |
} | |
function isWordChar(ch) { | |
return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); | |
} | |
// See if "".split is the broken IE version, if so, provide an | |
// alternative way to split lines. | |
var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { | |
var pos = 0, nl, result = []; | |
while ((nl = string.indexOf("\n", pos)) > -1) { | |
result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl)); | |
pos = nl + 1; | |
} | |
result.push(string.slice(pos)); | |
return result; | |
} : function(string){return string.split(/\r?\n/);}; | |
CodeMirror.splitLines = splitLines; | |
var hasSelection = window.getSelection ? function(te) { | |
try { return te.selectionStart != te.selectionEnd; } | |
catch(e) { return false; } | |
} : function(te) { | |
try {var range = te.ownerDocument.selection.createRange();} | |
catch(e) {} | |
if (!range || range.parentElement() != te) return false; | |
return range.compareEndPoints("StartToEnd", range) != 0; | |
}; | |
CodeMirror.defineMode("null", function() { | |
return {token: function(stream) {stream.skipToEnd();}}; | |
}); | |
CodeMirror.defineMIME("text/plain", "null"); | |
var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", | |
19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", | |
36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", | |
46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 186: ";", 187: "=", 188: ",", | |
189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp", | |
63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right", | |
63233: "Down", 63302: "Insert", 63272: "Delete"}; | |
CodeMirror.keyNames = keyNames; | |
(function() { | |
// Number keys | |
for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i); | |
// Alphabetic keys | |
for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); | |
// Function keys | |
for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; | |
})(); | |
return CodeMirror; | |
})(); | |
.CodeMirror-dialog { | |
position: relative; | |
} | |
.CodeMirror-dialog > div { | |
position: absolute; | |
top: 0; left: 0; right: 0; | |
background: white; | |
border-bottom: 1px solid #eee; | |
z-index: 15; | |
padding: .1em .8em; | |
overflow: hidden; | |
color: #333; | |
} | |
.CodeMirror-dialog input { | |
border: none; | |
outline: none; | |
background: transparent; | |
width: 20em; | |
color: inherit; | |
font-family: monospace; | |
} | |
// Open simple dialogs on top of an editor. Relies on dialog.css. | |
(function() { | |
function dialogDiv(cm, template) { | |
var wrap = cm.getWrapperElement(); | |
var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild); | |
dialog.className = "CodeMirror-dialog"; | |
dialog.innerHTML = '<div>' + template + '</div>'; | |
return dialog; | |
} | |
CodeMirror.defineExtension("openDialog", function(template, callback) { | |
var dialog = dialogDiv(this, template); | |
var closed = false, me = this; | |
function close() { | |
if (closed) return; | |
closed = true; | |
dialog.parentNode.removeChild(dialog); | |
} | |
var inp = dialog.getElementsByTagName("input")[0]; | |
if (inp) { | |
CodeMirror.connect(inp, "keydown", function(e) { | |
if (e.keyCode == 13 || e.keyCode == 27) { | |
CodeMirror.e_stop(e); | |
close(); | |
me.focus(); | |
if (e.keyCode == 13) callback(inp.value); | |
} | |
}); | |
inp.focus(); | |
CodeMirror.connect(inp, "blur", close); | |
} | |
return close; | |
}); | |
CodeMirror.defineExtension("openConfirm", function(template, callbacks) { | |
var dialog = dialogDiv(this, template); | |
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.connect(b, "click", function(e) { | |
CodeMirror.e_preventDefault(e); | |
close(); | |
if (callback) callback(me); | |
}); | |
})(callbacks[i]); | |
CodeMirror.connect(b, "blur", function() { | |
--blurring; | |
setTimeout(function() { if (blurring <= 0) close(); }, 200); | |
}); | |
CodeMirror.connect(b, "focus", function() { ++blurring; }); | |
} | |
}); | |
})(); |
CodeMirror.braceRangeFinder = function(cm, line) { | |
var lineText = cm.getLine(line); | |
var startChar = lineText.lastIndexOf("{"); | |
if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return; | |
var tokenType = cm.getTokenAt({line: line, ch: startChar}).className; | |
var count = 1, lastLine = cm.lineCount(), end; | |
outer: for (var i = line + 1; i < lastLine; ++i) { | |
var text = cm.getLine(i), pos = 0; | |
for (;;) { | |
var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos); | |
if (nextOpen < 0) nextOpen = text.length; | |
if (nextClose < 0) nextClose = text.length; | |
pos = Math.min(nextOpen, nextClose); | |
if (pos == text.length) break; | |
if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) { | |
if (pos == nextOpen) ++count; | |
else if (!--count) { end = i; break outer; } | |
} | |
++pos; | |
} | |
} | |
if (end == null || end == line + 1) return; | |
return end; | |
}; | |
CodeMirror.newFoldFunction = function(rangeFinder, markText) { | |
var folded = []; | |
if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">▼</div>%N%'; | |
function isFolded(cm, n) { | |
for (var i = 0; i < folded.length; ++i) { | |
var start = cm.lineInfo(folded[i].start); | |
if (!start) folded.splice(i--, 1); | |
else if (start.line == n) return {pos: i, region: folded[i]}; | |
} | |
} | |
function expand(cm, region) { | |
cm.clearMarker(region.start); | |
for (var i = 0; i < region.hidden.length; ++i) | |
cm.showLine(region.hidden[i]); | |
} | |
return function(cm, line) { | |
cm.operation(function() { | |
var known = isFolded(cm, line); | |
if (known) { | |
folded.splice(known.pos, 1); | |
expand(cm, known.region); | |
} else { | |
var end = rangeFinder(cm, line); | |
if (end == null) return; | |
var hidden = []; | |
for (var i = line + 1; i < end; ++i) { | |
var handle = cm.hideLine(i); | |
if (handle) hidden.push(handle); | |
} | |
var first = cm.setMarker(line, markText); | |
var region = {start: first, hidden: hidden}; | |
cm.onDeleteLine(first, function() { expand(cm, region); }); | |
folded.push(region); | |
} | |
}); | |
}; | |
}; | |
// ============== Formatting extensions ============================ | |
// A common storage for all mode-specific formatting features | |
if (!CodeMirror.modeExtensions) CodeMirror.modeExtensions = {}; | |
// Returns the extension of the editor's current mode | |
CodeMirror.defineExtension("getModeExt", function () { | |
return CodeMirror.modeExtensions[this.getOption("mode")]; | |
}); | |
// If the current mode is 'htmlmixed', returns the extension of a mode located at | |
// the specified position (can be htmlmixed, css or javascript). Otherwise, simply | |
// returns the extension of the editor's current mode. | |
CodeMirror.defineExtension("getModeExtAtPos", function (pos) { | |
var token = this.getTokenAt(pos); | |
if (token && token.state && token.state.mode) | |
return CodeMirror.modeExtensions[token.state.mode == "html" ? "htmlmixed" : token.state.mode]; | |
else | |
return this.getModeExt(); | |
}); | |
// Comment/uncomment the specified range | |
CodeMirror.defineExtension("commentRange", function (isComment, from, to) { | |
var curMode = this.getModeExtAtPos(this.getCursor()); | |
if (isComment) { // Comment range | |
var commentedText = this.getRange(from, to); | |
this.replaceRange(curMode.commentStart + this.getRange(from, to) + curMode.commentEnd | |
, from, to); | |
if (from.line == to.line && from.ch == to.ch) { // An empty comment inserted - put cursor inside | |
this.setCursor(from.line, from.ch + curMode.commentStart.length); | |
} | |
} | |
else { // Uncomment range | |
var selText = this.getRange(from, to); | |
var startIndex = selText.indexOf(curMode.commentStart); | |
var endIndex = selText.lastIndexOf(curMode.commentEnd); | |
if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) { | |
// Take string till comment start | |
selText = selText.substr(0, startIndex) | |
// From comment start till comment end | |
+ selText.substring(startIndex + curMode.commentStart.length, endIndex) | |
// From comment end till string end | |
+ selText.substr(endIndex + curMode.commentEnd.length); | |
} | |
this.replaceRange(selText, from, to); | |
} | |
}); | |
// Applies automatic mode-aware indentation to the specified range | |
CodeMirror.defineExtension("autoIndentRange", function (from, to) { | |
var cmInstance = this; | |
this.operation(function () { | |
for (var i = from.line; i <= to.line; i++) { | |
cmInstance.indentLine(i); | |
} | |
}); | |
}); | |
// Applies automatic formatting to the specified range | |
CodeMirror.defineExtension("autoFormatRange", function (from, to) { | |
var absStart = this.indexFromPos(from); | |
var absEnd = this.indexFromPos(to); | |
// Insert additional line breaks where necessary according to the | |
// mode's syntax | |
var res = this.getModeExt().autoFormatLineBreaks(this.getValue(), absStart, absEnd); | |
var cmInstance = this; | |
// Replace and auto-indent the range | |
this.operation(function () { | |
cmInstance.replaceRange(res, from, to); | |
var startLine = cmInstance.posFromIndex(absStart).line; | |
var endLine = cmInstance.posFromIndex(absStart + res.length).line; | |
for (var i = startLine; i <= endLine; i++) { | |
cmInstance.indentLine(i); | |
} | |
}); | |
}); | |
// Define extensions for a few modes | |
CodeMirror.modeExtensions["css"] = { | |
commentStart: "/*", | |
commentEnd: "*/", | |
wordWrapChars: [";", "\\{", "\\}"], | |
autoFormatLineBreaks: function (text) { | |
return text.replace(new RegExp("(;|\\{|\\})([^\r\n])", "g"), "$1\n$2"); | |
} | |
}; | |
CodeMirror.modeExtensions["javascript"] = { | |
commentStart: "/*", | |
commentEnd: "*/", | |
wordWrapChars: [";", "\\{", "\\}"], | |
getNonBreakableBlocks: function (text) { | |
var nonBreakableRegexes = [ | |
new RegExp("for\\s*?\\(([\\s\\S]*?)\\)"), | |
new RegExp("'([\\s\\S]*?)('|$)"), | |
new RegExp("\"([\\s\\S]*?)(\"|$)"), | |
new RegExp("//.*([\r\n]|$)") | |
]; | |
var nonBreakableBlocks = new Array(); | |
for (var i = 0; i < nonBreakableRegexes.length; i++) { | |
var curPos = 0; | |
while (curPos < text.length) { | |
var m = text.substr(curPos).match(nonBreakableRegexes[i]); | |
if (m != null) { | |
nonBreakableBlocks.push({ | |
start: curPos + m.index, | |
end: curPos + m.index + m[0].length | |
}); | |
curPos += m.index + Math.max(1, m[0].length); | |
} | |
else { // No more matches | |
break; | |
} | |
} | |
} | |
nonBreakableBlocks.sort(function (a, b) { | |
return a.start - b.start; | |
}); | |
return nonBreakableBlocks; | |
}, | |
autoFormatLineBreaks: function (text) { | |
var curPos = 0; | |
var reLinesSplitter = new RegExp("(;|\\{|\\})([^\r\n])", "g"); | |
var nonBreakableBlocks = this.getNonBreakableBlocks(text); | |
if (nonBreakableBlocks != null) { | |
var res = ""; | |
for (var i = 0; i < nonBreakableBlocks.length; i++) { | |
if (nonBreakableBlocks[i].start > curPos) { // Break lines till the block | |
res += text.substring(curPos, nonBreakableBlocks[i].start).replace(reLinesSplitter, "$1\n$2"); | |
curPos = nonBreakableBlocks[i].start; | |
} | |
if (nonBreakableBlocks[i].start <= curPos | |
&& nonBreakableBlocks[i].end >= curPos) { // Skip non-breakable block | |
res += text.substring(curPos, nonBreakableBlocks[i].end); | |
curPos = nonBreakableBlocks[i].end; | |
} | |
} | |
if (curPos < text.length - 1) { | |
res += text.substr(curPos).replace(reLinesSplitter, "$1\n$2"); | |
} | |
return res; | |
} | |
else { | |
return text.replace(reLinesSplitter, "$1\n$2"); | |
} | |
} | |
}; | |
CodeMirror.modeExtensions["xml"] = { | |
commentStart: "<!--", | |
commentEnd: "-->", | |
wordWrapChars: [">"], | |
autoFormatLineBreaks: function (text) { | |
var lines = text.split("\n"); | |
var reProcessedPortion = new RegExp("(^\\s*?<|^[^<]*?)(.+)(>\\s*?$|[^>]*?$)"); | |
var reOpenBrackets = new RegExp("<", "g"); | |
var reCloseBrackets = new RegExp("(>)([^\r\n])", "g"); | |
for (var i = 0; i < lines.length; i++) { | |
var mToProcess = lines[i].match(reProcessedPortion); | |
if (mToProcess != null && mToProcess.length > 3) { // The line starts with whitespaces and ends with whitespaces | |
lines[i] = mToProcess[1] | |
+ mToProcess[2].replace(reOpenBrackets, "\n$&").replace(reCloseBrackets, "$1\n$2") | |
+ mToProcess[3]; | |
continue; | |
} | |
} | |
return lines.join("\n"); | |
} | |
}; | |
CodeMirror.modeExtensions["htmlmixed"] = { | |
commentStart: "<!--", | |
commentEnd: "-->", | |
wordWrapChars: [">", ";", "\\{", "\\}"], | |
getModeInfos: function (text, absPos) { | |
var modeInfos = new Array(); | |
modeInfos[0] = | |
{ | |
pos: 0, | |
modeExt: CodeMirror.modeExtensions["xml"], | |
modeName: "xml" | |
}; | |
var modeMatchers = new Array(); | |
modeMatchers[0] = | |
{ | |
regex: new RegExp("<style[^>]*>([\\s\\S]*?)(</style[^>]*>|$)", "i"), | |
modeExt: CodeMirror.modeExtensions["css"], | |
modeName: "css" | |
}; | |
modeMatchers[1] = | |
{ | |
regex: new RegExp("<script[^>]*>([\\s\\S]*?)(</script[^>]*>|$)", "i"), | |
modeExt: CodeMirror.modeExtensions["javascript"], | |
modeName: "javascript" | |
}; | |
var lastCharPos = (typeof (absPos) !== "undefined" ? absPos : text.length - 1); | |
// Detect modes for the entire text | |
for (var i = 0; i < modeMatchers.length; i++) { | |
var curPos = 0; | |
while (curPos <= lastCharPos) { | |
var m = text.substr(curPos).match(modeMatchers[i].regex); | |
if (m != null) { | |
if (m.length > 1 && m[1].length > 0) { | |
// Push block begin pos | |
var blockBegin = curPos + m.index + m[0].indexOf(m[1]); | |
modeInfos.push( | |
{ | |
pos: blockBegin, | |
modeExt: modeMatchers[i].modeExt, | |
modeName: modeMatchers[i].modeName | |
}); | |
// Push block end pos | |
modeInfos.push( | |
{ | |
pos: blockBegin + m[1].length, | |
modeExt: modeInfos[0].modeExt, | |
modeName: modeInfos[0].modeName | |
}); | |
curPos += m.index + m[0].length; | |
continue; | |
} | |
else { | |
curPos += m.index + Math.max(m[0].length, 1); | |
} | |
} | |
else { // No more matches | |
break; | |
} | |
} | |
} | |
// Sort mode infos | |
modeInfos.sort(function sortModeInfo(a, b) { | |
return a.pos - b.pos; | |
}); | |
return modeInfos; | |
}, | |
autoFormatLineBreaks: function (text, startPos, endPos) { | |
var modeInfos = this.getModeInfos(text); | |
var reBlockStartsWithNewline = new RegExp("^\\s*?\n"); | |
var reBlockEndsWithNewline = new RegExp("\n\\s*?$"); | |
var res = ""; | |
// Use modes info to break lines correspondingly | |
if (modeInfos.length > 1) { // Deal with multi-mode text | |
for (var i = 1; i <= modeInfos.length; i++) { | |
var selStart = modeInfos[i - 1].pos; | |
var selEnd = (i < modeInfos.length ? modeInfos[i].pos : endPos); | |
if (selStart >= endPos) { // The block starts later than the needed fragment | |
break; | |
} | |
if (selStart < startPos) { | |
if (selEnd <= startPos) { // The block starts earlier than the needed fragment | |
continue; | |
} | |
selStart = startPos; | |
} | |
if (selEnd > endPos) { | |
selEnd = endPos; | |
} | |
var textPortion = text.substring(selStart, selEnd); | |
if (modeInfos[i - 1].modeName != "xml") { // Starting a CSS or JavaScript block | |
if (!reBlockStartsWithNewline.test(textPortion) | |
&& selStart > 0) { // The block does not start with a line break | |
textPortion = "\n" + textPortion; | |
} | |
if (!reBlockEndsWithNewline.test(textPortion) | |
&& selEnd < text.length - 1) { // The block does not end with a line break | |
textPortion += "\n"; | |
} | |
} | |
res += modeInfos[i - 1].modeExt.autoFormatLineBreaks(textPortion); | |
} | |
} | |
else { // Single-mode text | |
res = modeInfos[0].modeExt.autoFormatLineBreaks(text.substring(startPos, endPos)); | |
} | |
return res; | |
} | |
}; | |
(function () { | |
function forEach(arr, f) { | |
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]); | |
} | |
function arrayContains(arr, item) { | |
if (!Array.prototype.indexOf) { | |
var i = arr.length; | |
while (i--) { | |
if (arr[i] === item) { | |
return true; | |
} | |
} | |
return false; | |
} | |
return arr.indexOf(item) != -1; | |
} | |
CodeMirror.javascriptHint = function(editor) { | |
// Find the token at the cursor | |
var cur = editor.getCursor(), token = editor.getTokenAt(cur), tprop = token; | |
// If it's not a 'word-style' token, ignore the token. | |
if (!/^[\w$_]*$/.test(token.string)) { | |
token = tprop = {start: cur.ch, end: cur.ch, string: "", state: token.state, | |
className: token.string == "." ? "property" : null}; | |
} | |
// If it is a property, find out what it is a property of. | |
while (tprop.className == "property") { | |
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start}); | |
if (tprop.string != ".") return; | |
tprop = editor.getTokenAt({line: cur.line, ch: tprop.start}); | |
if (!context) var context = []; | |
context.push(tprop); | |
} | |
return {list: getCompletions(token, context), | |
from: {line: cur.line, ch: token.start}, | |
to: {line: cur.line, ch: token.end}}; | |
} | |
var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " + | |
"toUpperCase toLowerCase split concat match replace search").split(" "); | |
var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " + | |
"lastIndexOf every some filter forEach map reduce reduceRight ").split(" "); | |
var funcProps = "prototype apply call bind".split(" "); | |
var keywords = ("break case catch continue debugger default delete do else false finally for function " + | |
"if in instanceof new null return switch throw true try typeof var void while with").split(" "); | |
function getCompletions(token, context) { | |
var found = [], start = token.string; | |
function maybeAdd(str) { | |
if (str.indexOf(start) == 0 && !arrayContains(found, str)) found.push(str); | |
} | |
function gatherCompletions(obj) { | |
if (typeof obj == "string") forEach(stringProps, maybeAdd); | |
else if (obj instanceof Array) forEach(arrayProps, maybeAdd); | |
else if (obj instanceof Function) forEach(funcProps, maybeAdd); | |
for (var name in obj) maybeAdd(name); | |
} | |
if (context) { | |
// If this is a property, see if it belongs to some object we can | |
// find in the current environment. | |
var obj = context.pop(), base; | |
if (obj.className == "variable") | |
base = window[obj.string]; | |
else if (obj.className == "string") | |
base = ""; | |
else if (obj.className == "atom") | |
base = 1; | |
while (base != null && context.length) | |
base = base[context.pop().string]; | |
if (base != null) gatherCompletions(base); | |
} | |
else { | |
// If not, just look in the window object and any local scope | |
// (reading into JS mode internals to get at the local variables) | |
for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name); | |
gatherCompletions(window); | |
forEach(keywords, maybeAdd); | |
} | |
return found; | |
} | |
})(); | |
// Utility function that allows modes to be combined. The mode given | |
// as the base argument takes care of most of the normal mode | |
// functionality, but a second (typically simple) mode is used, which | |
// can override the style of text. Both modes get to parse all of the | |
// text, but when both assign a non-null style to a piece of code, the | |
// overlay wins, unless the combine argument was true, in which case | |
// the styles are combined. | |
CodeMirror.overlayParser = function(base, overlay, combine) { | |
return { | |
startState: function() { | |
return { | |
base: CodeMirror.startState(base), | |
overlay: CodeMirror.startState(overlay), | |
basePos: 0, baseCur: null, | |
overlayPos: 0, overlayCur: null | |
}; | |
}, | |
copyState: function(state) { | |
return { | |
base: CodeMirror.copyState(base, state.base), | |
overlay: CodeMirror.copyState(overlay, state.overlay), | |
basePos: state.basePos, baseCur: null, | |
overlayPos: state.overlayPos, overlayCur: null | |
}; | |
}, | |
token: function(stream, state) { | |
if (stream.start == state.basePos) { | |
state.baseCur = base.token(stream, state.base); | |
state.basePos = stream.pos; | |
} | |
if (stream.start == state.overlayPos) { | |
stream.pos = stream.start; | |
state.overlayCur = overlay.token(stream, state.overlay); | |
state.overlayPos = stream.pos; | |
} | |
stream.pos = Math.min(state.basePos, state.overlayPos); | |
if (stream.eol()) state.basePos = state.overlayPos = 0; | |
if (state.overlayCur == null) return state.baseCur; | |
if (state.baseCur != null && combine) return state.baseCur + " " + state.overlayCur; | |
else return state.overlayCur; | |
}, | |
indent: function(state, textAfter) { | |
return base.indent(state.base, textAfter); | |
}, | |
electricChars: base.electricChars | |
}; | |
}; | |
CodeMirror.runMode = function(string, modespec, callback) { | |
var mode = CodeMirror.getMode({indentUnit: 2}, modespec); | |
var isNode = callback.nodeType == 1; | |
if (isNode) { | |
var node = callback, accum = []; | |
callback = function(string, style) { | |
if (string == "\n") | |
accum.push("<br>"); | |
else if (style) | |
accum.push("<span class=\"cm-" + CodeMirror.htmlEscape(style) + "\">" + CodeMirror.htmlEscape(string) + "</span>"); | |
else | |
accum.push(CodeMirror.htmlEscape(string)); | |
} | |
} | |
var lines = CodeMirror.splitLines(string), state = CodeMirror.startState(mode); | |
for (var i = 0, e = lines.length; i < e; ++i) { | |
if (i) callback("\n"); | |
var stream = new CodeMirror.StringStream(lines[i]); | |
while (!stream.eol()) { | |
var style = mode.token(stream, state); | |
callback(stream.current(), style, i, stream.start); | |
stream.start = stream.pos; | |
} | |
} | |
if (isNode) | |
node.innerHTML = accum.join(""); | |
}; | |
// Define search commands. Depends on dialog.js or another | |
// implementation of the openDialog method. | |
// Replace works a little oddly -- it will do the replace on the next | |
// Ctrl-G (or whatever is bound to findNext) press. You prevent a | |
// replace by making sure the match is no longer selected when hitting | |
// Ctrl-G. | |
(function() { | |
function SearchState() { | |
this.posFrom = this.posTo = this.query = null; | |
this.marked = []; | |
} | |
function getSearchState(cm) { | |
return cm._searchState || (cm._searchState = new SearchState()); | |
} | |
function dialog(cm, text, shortText, f) { | |
if (cm.openDialog) cm.openDialog(text, f); | |
else f(prompt(shortText, "")); | |
} | |
function confirmDialog(cm, text, shortText, fs) { | |
if (cm.openConfirm) cm.openConfirm(text, fs); | |
else if (confirm(shortText)) fs[0](); | |
} | |
function parseQuery(query) { | |
var isRE = query.match(/^\/(.*)\/$/); | |
return isRE ? new RegExp(isRE[1]) : query; | |
} | |
var queryDialog = | |
'Search: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; | |
function doSearch(cm, rev) { | |
var state = getSearchState(cm); | |
if (state.query) return findNext(cm, rev); | |
dialog(cm, queryDialog, "Search for:", function(query) { | |
cm.operation(function() { | |
if (!query || state.query) return; | |
state.query = parseQuery(query); | |
if (cm.lineCount() < 2000) { // This is too expensive on big documents. | |
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) | |
state.marked.push(cm.markText(cursor.from(), cursor.to(), "CodeMirror-searching")); | |
} | |
state.posFrom = state.posTo = cm.getCursor(); | |
findNext(cm, rev); | |
}); | |
}); | |
} | |
function findNext(cm, rev) {cm.operation(function() { | |
var state = getSearchState(cm); | |
var cursor = cm.getSearchCursor(state.query, rev ? state.posFrom : state.posTo); | |
if (!cursor.find(rev)) { | |
cursor = cm.getSearchCursor(state.query, rev ? {line: cm.lineCount() - 1} : {line: 0, ch: 0}); | |
if (!cursor.find(rev)) return; | |
} | |
cm.setSelection(cursor.from(), cursor.to()); | |
state.posFrom = cursor.from(); state.posTo = cursor.to(); | |
})} | |
function clearSearch(cm) {cm.operation(function() { | |
var state = getSearchState(cm); | |
if (!state.query) return; | |
state.query = null; | |
for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); | |
state.marked.length = 0; | |
})} | |
var replaceQueryDialog = | |
'Replace: <input type="text" style="width: 10em"> <span style="color: #888">(Use /re/ syntax for regexp search)</span>'; | |
var replacementQueryDialog = 'With: <input type="text" style="width: 10em">'; | |
var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>Stop</button>"; | |
function replace(cm, all) { | |
dialog(cm, replaceQueryDialog, "Replace:", function(query) { | |
if (!query) return; | |
query = parseQuery(query); | |
dialog(cm, replacementQueryDialog, "Replace with:", function(text) { | |
if (all) { | |
cm.operation(function() { | |
for (var cursor = cm.getSearchCursor(query); cursor.findNext();) { | |
if (typeof query != "string") { | |
var match = cm.getRange(cursor.from(), cursor.to()).match(query); | |
cursor.replace(text.replace(/\$(\d)/, function(w, i) {return match[i];})); | |
} else cursor.replace(text); | |
} | |
}); | |
} else { | |
clearSearch(cm); | |
var cursor = cm.getSearchCursor(query, cm.getCursor()); | |
function advance() { | |
var start = cursor.from(), match; | |
if (!(match = cursor.findNext())) { | |
cursor = cm.getSearchCursor(query); | |
if (!(match = cursor.findNext()) || | |
(cursor.from().line == start.line && cursor.from().ch == start.ch)) return; | |
} | |
cm.setSelection(cursor.from(), cursor.to()); | |
confirmDialog(cm, doReplaceConfirm, "Replace?", | |
[function() {doReplace(match);}, advance]); | |
} | |
function doReplace(match) { | |
cursor.replace(typeof query == "string" ? text : | |
text.replace(/\$(\d)/, function(w, i) {return match[i];})); | |
advance(); | |
} | |
advance(); | |
} | |
}); | |
}); | |
} | |
CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);}; | |
CodeMirror.commands.findNext = doSearch; | |
CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);}; | |
CodeMirror.commands.clearSearch = clearSearch; | |
CodeMirror.commands.replace = replace; | |
CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);}; | |
})(); | |
(function(){ | |
function SearchCursor(cm, query, pos, caseFold) { | |
this.atOccurrence = false; this.cm = cm; | |
if (caseFold == null) caseFold = typeof query == "string" && query == query.toLowerCase(); | |
pos = pos ? cm.clipPos(pos) : {line: 0, ch: 0}; | |
this.pos = {from: pos, to: pos}; | |
// The matches method is filled in based on the type of query. | |
// It takes a position and a direction, and returns an object | |
// describing the next occurrence of the query, or null if no | |
// more matches were found. | |
if (typeof query != "string") // Regexp match | |
this.matches = function(reverse, pos) { | |
if (reverse) { | |
var line = cm.getLine(pos.line).slice(0, pos.ch), match = line.match(query), start = 0; | |
while (match) { | |
var ind = line.indexOf(match[0]); | |
start += ind; | |
line = line.slice(ind + 1); | |
var newmatch = line.match(query); | |
if (newmatch) match = newmatch; | |
else break; | |
start++; | |
} | |
} | |
else { | |
var line = cm.getLine(pos.line).slice(pos.ch), match = line.match(query), | |
start = match && pos.ch + line.indexOf(match[0]); | |
} | |
if (match) | |
return {from: {line: pos.line, ch: start}, | |
to: {line: pos.line, ch: start + match[0].length}, | |
match: match}; | |
}; | |
else { // String query | |
if (caseFold) query = query.toLowerCase(); | |
var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; | |
var target = query.split("\n"); | |
// Different methods for single-line and multi-line queries | |
if (target.length == 1) | |
this.matches = function(reverse, pos) { | |
var line = fold(cm.getLine(pos.line)), len = query.length, match; | |
if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) | |
: (match = line.indexOf(query, pos.ch)) != -1) | |
return {from: {line: pos.line, ch: match}, | |
to: {line: pos.line, ch: match + len}}; | |
}; | |
else | |
this.matches = function(reverse, pos) { | |
var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); | |
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); | |
if (reverse ? offsetA >= pos.ch || offsetA != match.length | |
: offsetA <= pos.ch || offsetA != line.length - match.length) | |
return; | |
for (;;) { | |
if (reverse ? !ln : ln == cm.lineCount() - 1) return; | |
line = fold(cm.getLine(ln += reverse ? -1 : 1)); | |
match = target[reverse ? --idx : ++idx]; | |
if (idx > 0 && idx < target.length - 1) { | |
if (line != match) return; | |
else continue; | |
} | |
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); | |
if (reverse ? offsetB != line.length - match.length : offsetB != match.length) | |
return; | |
var start = {line: pos.line, ch: offsetA}, end = {line: ln, ch: offsetB}; | |
return {from: reverse ? end : start, to: reverse ? start : end}; | |
} | |
}; | |
} | |
} | |
SearchCursor.prototype = { | |
findNext: function() {return this.find(false);}, | |
findPrevious: function() {return this.find(true);}, | |
find: function(reverse) { | |
var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); | |
function savePosAndFail(line) { | |
var pos = {line: line, ch: 0}; | |
self.pos = {from: pos, to: pos}; | |
self.atOccurrence = false; | |
return false; | |
} | |
for (;;) { | |
if (this.pos = this.matches(reverse, pos)) { | |
this.atOccurrence = true; | |
return this.pos.match || true; | |
} | |
if (reverse) { | |
if (!pos.line) return savePosAndFail(0); | |
pos = {line: pos.line-1, ch: this.cm.getLine(pos.line-1).length}; | |
} | |
else { | |
var maxLine = this.cm.lineCount(); | |
if (pos.line == maxLine - 1) return savePosAndFail(maxLine); | |
pos = {line: pos.line+1, ch: 0}; | |
} | |
} | |
}, | |
from: function() {if (this.atOccurrence) return this.pos.from;}, | |
to: function() {if (this.atOccurrence) return this.pos.to;}, | |
replace: function(newText) { | |
var self = this; | |
if (this.atOccurrence) | |
self.pos.to = this.cm.replaceRange(newText, self.pos.from, self.pos.to); | |
} | |
}; | |
CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { | |
return new SearchCursor(this, query, pos, caseFold); | |
}); | |
})(); | |
.CodeMirror-completions { | |
position: absolute; | |
z-index: 10; | |
overflow: hidden; | |
-webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2); | |
-moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2); | |
box-shadow: 2px 3px 5px rgba(0,0,0,.2); | |
} | |
.CodeMirror-completions select { | |
background: #fafafa; | |
outline: none; | |
border: none; | |
padding: 0; | |
margin: 0; | |
font-family: monospace; | |
} | |
(function() { | |
CodeMirror.simpleHint = function(editor, getHints) { | |
// We want a single cursor position. | |
if (editor.somethingSelected()) return; | |
var result = getHints(editor); | |
if (!result || !result.list.length) return; | |
var completions = result.list; | |
function insert(str) { | |
editor.replaceRange(str, result.from, result.to); | |
} | |
// When there is only one completion, use it directly. | |
if (completions.length == 1) {insert(completions[0]); return true;} | |
// Build the select widget | |
var complete = document.createElement("div"); | |
complete.className = "CodeMirror-completions"; | |
var sel = complete.appendChild(document.createElement("select")); | |
// Opera doesn't move the selection when pressing up/down in a | |
// multi-select, but it does properly support the size property on | |
// single-selects, so no multi-select is necessary. | |
if (!window.opera) sel.multiple = true; | |
for (var i = 0; i < completions.length; ++i) { | |
var opt = sel.appendChild(document.createElement("option")); | |
opt.appendChild(document.createTextNode(completions[i])); | |
} | |
sel.firstChild.selected = true; | |
sel.size = Math.min(10, completions.length); | |
var pos = editor.cursorCoords(); | |
complete.style.left = pos.x + "px"; | |
complete.style.top = pos.yBot + "px"; | |
document.body.appendChild(complete); | |
// Hack to hide the scrollbar. | |
if (completions.length <= 10) | |
complete.style.width = (sel.clientWidth - 1) + "px"; | |
var done = false; | |
function close() { | |
if (done) return; | |
done = true; | |
complete.parentNode.removeChild(complete); | |
} | |
function pick() { | |
insert(completions[sel.selectedIndex]); | |
close(); | |
setTimeout(function(){editor.focus();}, 50); | |
} | |
CodeMirror.connect(sel, "blur", close); | |
CodeMirror.connect(sel, "keydown", function(event) { | |
var code = event.keyCode; | |
// Enter | |
if (code == 13) {CodeMirror.e_stop(event); pick();} | |
// Escape | |
else if (code == 27) {CodeMirror.e_stop(event); close(); editor.focus();} | |
else if (code != 38 && code != 40) { | |
close(); editor.focus(); | |
setTimeout(function(){CodeMirror.simpleHint(editor, getHints);}, 50); | |
} | |
}); | |
CodeMirror.connect(sel, "dblclick", pick); | |
sel.focus(); | |
// Opera sometimes ignores focusing a freshly created node | |
if (window.opera) setTimeout(function(){if (!done) sel.focus();}, 100); | |
return true; | |
}; | |
})(); | |
CodeMirror.defineMode("clike", function(config, parserConfig) { | |
var indentUnit = config.indentUnit, | |
keywords = parserConfig.keywords || {}, | |
blockKeywords = parserConfig.blockKeywords || {}, | |
atoms = parserConfig.atoms || {}, | |
hooks = parserConfig.hooks || {}, | |
multiLineStrings = parserConfig.multiLineStrings; | |
var isOperatorChar = /[+\-*&%=<>!?|\/]/; | |
var curPunc; | |
function tokenBase(stream, state) { | |
var ch = stream.next(); | |
if (hooks[ch]) { | |
var result = hooks[ch](stream, state); | |
if (result !== false) return result; | |
} | |
if (ch == '"' || ch == "'") { | |
state.tokenize = tokenString(ch); | |
return state.tokenize(stream, state); | |
} | |
if (/[\[\]{}\(\),;\:\.]/.test(ch)) { | |
curPunc = ch; | |
return null | |
} | |
if (/\d/.test(ch)) { | |
stream.eatWhile(/[\w\.]/); | |
return "number"; | |
} | |
if (ch == "/") { | |
if (stream.eat("*")) { | |
state.tokenize = tokenComment; | |
return tokenComment(stream, state); | |
} | |
if (stream.eat("/")) { | |
stream.skipToEnd(); | |
return "comment"; | |
} | |
} | |
if (isOperatorChar.test(ch)) { | |
stream.eatWhile(isOperatorChar); | |
return "operator"; | |
} | |
stream.eatWhile(/[\w\$_]/); | |
var cur = stream.current(); | |
if (keywords.propertyIsEnumerable(cur)) { | |
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; | |
return "keyword"; | |
} | |
if (atoms.propertyIsEnumerable(cur)) return "atom"; | |
return "word"; | |
} | |
function tokenString(quote) { | |
return function(stream, state) { | |
var escaped = false, next, end = false; | |
while ((next = stream.next()) != null) { | |
if (next == quote && !escaped) {end = true; break;} | |
escaped = !escaped && next == "\\"; | |
} | |
if (end || !(escaped || multiLineStrings)) | |
state.tokenize = tokenBase; | |
return "string"; | |
}; | |
} | |
function tokenComment(stream, state) { | |
var maybeEnd = false, ch; | |
while (ch = stream.next()) { | |
if (ch == "/" && maybeEnd) { | |
state.tokenize = tokenBase; | |
break; | |
} | |
maybeEnd = (ch == "*"); | |
} | |
return "comment"; | |
} | |
function Context(indented, column, type, align, prev) { | |
this.indented = indented; | |
this.column = column; | |
this.type = type; | |
this.align = align; | |
this.prev = prev; | |
} | |
function pushContext(state, col, type) { | |
return state.context = new Context(state.indented, col, type, null, state.context); | |
} | |
function popContext(state) { | |
var t = state.context.type; | |
if (t == ")" || t == "]" || t == "}") | |
state.indented = state.context.indented; | |
return state.context = state.context.prev; | |
} | |
// Interface | |
return { | |
startState: function(basecolumn) { | |
return { | |
tokenize: null, | |
context: new Context((basecolumn || 0) - indentUnit, 0, "top", false), | |
indented: 0, | |
startOfLine: true | |
}; | |
}, | |
token: function(stream, state) { | |
var ctx = state.context; | |
if (stream.sol()) { | |
if (ctx.align == null) ctx.align = false; | |
state.indented = stream.indentation(); | |
state.startOfLine = true; | |
} | |
if (stream.eatSpace()) return null; | |
curPunc = null; | |
var style = (state.tokenize || tokenBase)(stream, state); | |
if (style == "comment" || style == "meta") return style; | |
if (ctx.align == null) ctx.align = true; | |
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); | |
else if (curPunc == "{") pushContext(state, stream.column(), "}"); | |
else if (curPunc == "[") pushContext(state, stream.column(), "]"); | |
else if (curPunc == "(") pushContext(state, stream.column(), ")"); | |
else if (curPunc == "}") { | |
while (ctx.type == "statement") ctx = popContext(state); | |
if (ctx.type == "}") ctx = popContext(state); | |
while (ctx.type == "statement") ctx = popContext(state); | |
} | |
else if (curPunc == ctx.type) popContext(state); | |
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) | |
pushContext(state, stream.column(), "statement"); | |
state.startOfLine = false; | |
return style; | |
}, | |
indent: function(state, textAfter) { | |
if (state.tokenize != tokenBase && state.tokenize != null) return 0; | |
var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); | |
if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; | |
var closing = firstChar == ctx.type; | |
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : indentUnit); | |
else if (ctx.align) return ctx.column + (closing ? 0 : 1); | |
else return ctx.indented + (closing ? 0 : indentUnit); | |
}, | |
electricChars: "{}" | |
}; | |
}); | |
(function() { | |
function words(str) { | |
var obj = {}, words = str.split(" "); | |
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; | |
return obj; | |
} | |
var cKeywords = "auto if break int case long char register continue return default short do sizeof " + | |
"double static else struct entry switch extern typedef float union for unsigned " + | |
"goto while enum void const signed volatile"; | |
function cppHook(stream, state) { | |
if (!state.startOfLine) return false; | |
stream.skipToEnd(); | |
return "meta"; | |
} | |
// C#-style strings where "" escapes a quote. | |
function tokenAtString(stream, state) { | |
var next; | |
while ((next = stream.next()) != null) { | |
if (next == '"' && !stream.eat('"')) { | |
state.tokenize = null; | |
break; | |
} | |
} | |
return "string"; | |
} | |
CodeMirror.defineMIME("text/x-csrc", { | |
name: "clike", | |
keywords: words(cKeywords), | |
blockKeywords: words("case do else for if switch while struct"), | |
atoms: words("null"), | |
hooks: {"#": cppHook} | |
}); | |
CodeMirror.defineMIME("text/x-c++src", { | |
name: "clike", | |
keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try bool explicit new " + | |
"static_cast typeid catch operator template typename class friend private " + | |
"this using const_cast inline public throw virtual delete mutable protected " + | |
"wchar_t"), | |
blockKeywords: words("catch class do else finally for if struct switch try while"), | |
atoms: words("true false null"), | |
hooks: {"#": cppHook} | |
}); | |
CodeMirror.defineMIME("text/x-java", { | |
name: "clike", | |
keywords: words("abstract assert boolean break byte case catch char class const continue default " + | |
"do double else enum extends final finally float for goto if implements import " + | |
"instanceof int interface long native new package private protected public " + | |
"return short static strictfp super switch synchronized this throw throws transient " + | |
"try void volatile while"), | |
blockKeywords: words("catch class do else finally for if switch try while"), | |
atoms: words("true false null"), | |
hooks: { | |
"@": function(stream, state) { | |
stream.eatWhile(/[\w\$_]/); | |
return "meta"; | |
} | |
} | |
}); | |
CodeMirror.defineMIME("text/x-csharp", { | |
name: "clike", | |
keywords: words("abstract as base bool break byte case catch char checked class const continue decimal" + | |
" default delegate do double else enum event explicit extern finally fixed float for" + | |
" foreach goto if implicit in int interface internal is lock long namespace new object" + | |
" operator out override params private protected public readonly ref return sbyte sealed short" + | |
" sizeof stackalloc static string struct switch this throw try typeof uint ulong unchecked" + | |
" unsafe ushort using virtual void volatile while add alias ascending descending dynamic from get" + | |
" global group into join let orderby partial remove select set value var yield"), | |
blockKeywords: words("catch class do else finally for foreach if struct switch try while"), | |
atoms: words("true false null"), | |
hooks: { | |
"@": function(stream, state) { | |
if (stream.eat('"')) { | |
state.tokenize = tokenAtString; | |
return tokenAtString(stream, state); | |
} | |
stream.eatWhile(/[\w\$_]/); | |
return "meta"; | |
} | |
} | |
}); | |
CodeMirror.defineMIME("text/x-groovy", { | |
name: "clike", | |
keywords: words("abstract as assert boolean break byte case catch char class const continue def default " + | |
"do double else enum extends final finally float for goto if implements import " + | |
"in instanceof int interface long native new package property private protected public " + | |
"return short static strictfp super switch synchronized this throw throws transient " + | |
"try void volatile while"), | |
atoms: words("true false null"), | |
hooks: { | |
"@": function(stream, state) { | |
stream.eatWhile(/[\w\$_]/); | |
return "meta"; | |
} | |
} | |
}); | |
}()); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: C-like mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="clike.js"></script> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
<style>.CodeMirror {border: 2px inset #dee;}</style> | |
</head> | |
<body> | |
<h1>CodeMirror: C-like mode</h1> | |
<form><textarea id="code" name="code"> | |
/* C demo code */ | |
#include <zmq.h> | |
#include <pthread.h> | |
#include <semaphore.h> | |
#include <time.h> | |
#include <stdio.h> | |
#include <fcntl.h> | |
#include <malloc.h> | |
typedef struct { | |
void* arg_socket; | |
zmq_msg_t* arg_msg; | |
char* arg_string; | |
unsigned long arg_len; | |
int arg_int, arg_command; | |
int signal_fd; | |
int pad; | |
void* context; | |
sem_t sem; | |
} acl_zmq_context; | |
#define p(X) (context->arg_##X) | |
void* zmq_thread(void* context_pointer) { | |
acl_zmq_context* context = (acl_zmq_context*)context_pointer; | |
char ok = 'K', err = 'X'; | |
int res; | |
while (1) { | |
while ((res = sem_wait(&context->sem)) == EINTR); | |
if (res) {write(context->signal_fd, &err, 1); goto cleanup;} | |
switch(p(command)) { | |
case 0: goto cleanup; | |
case 1: p(socket) = zmq_socket(context->context, p(int)); break; | |
case 2: p(int) = zmq_close(p(socket)); break; | |
case 3: p(int) = zmq_bind(p(socket), p(string)); break; | |
case 4: p(int) = zmq_connect(p(socket), p(string)); break; | |
case 5: p(int) = zmq_getsockopt(p(socket), p(int), (void*)p(string), &p(len)); break; | |
case 6: p(int) = zmq_setsockopt(p(socket), p(int), (void*)p(string), p(len)); break; | |
case 7: p(int) = zmq_send(p(socket), p(msg), p(int)); break; | |
case 8: p(int) = zmq_recv(p(socket), p(msg), p(int)); break; | |
case 9: p(int) = zmq_poll(p(socket), p(int), p(len)); break; | |
} | |
p(command) = errno; | |
write(context->signal_fd, &ok, 1); | |
} | |
cleanup: | |
close(context->signal_fd); | |
free(context_pointer); | |
return 0; | |
} | |
void* zmq_thread_init(void* zmq_context, int signal_fd) { | |
acl_zmq_context* context = malloc(sizeof(acl_zmq_context)); | |
pthread_t thread; | |
context->context = zmq_context; | |
context->signal_fd = signal_fd; | |
sem_init(&context->sem, 1, 0); | |
pthread_create(&thread, 0, &zmq_thread, context); | |
pthread_detach(thread); | |
return context; | |
} | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
matchBrackets: true, | |
mode: "text/x-csrc" | |
}); | |
</script> | |
<p>Simple mode that tries to handle C-like languages as well as it | |
can. Takes two configuration parameters: <code>keywords</code>, an | |
object whose property names are the keywords in the language, | |
and <code>useCPP</code>, which determines whether C preprocessor | |
directives are recognized.</p> | |
<p><strong>MIME types defined:</strong> <code>text/x-csrc</code> | |
(C code), <code>text/x-c++src</code> (C++ | |
code), <code>text/x-java</code> (Java | |
code), <code>text/x-groovy</code> (Groovy code).</p> | |
</body> | |
</html> | |
/** | |
* Author: Hans Engel | |
* Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun) | |
*/ | |
CodeMirror.defineMode("clojure", function (config, mode) { | |
var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", TAG = "tag", | |
ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD="keyword"; | |
var INDENT_WORD_SKIP = 2, KEYWORDS_SKIP = 1; | |
function makeKeywords(str) { | |
var obj = {}, words = str.split(" "); | |
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; | |
return obj; | |
} | |
var atoms = makeKeywords("true false nil"); | |
var keywords = makeKeywords( | |
// Control structures | |
"defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord deftest slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars binding gen-class gen-and-load-class gen-and-save-class handler-case handle" + | |
// Built-ins | |
"* *1 *2 *3 *agent* *allow-unresolved-vars* *assert *clojure-version* *command-line-args* *compile-files* *compile-path* *e *err* *file* *flush-on-newline* *in* *macro-meta* *math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* *source-path* *use-context-classloader* *warn-on-reflection* + - / < <= = == > >= accessor aclone agent agent-errors aget alength alias all-ns alter alter-meta! alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 bases bean bigdec bigint binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* butlast byte byte-array bytes case cast char char-array char-escape-string char-name-string char? chars chunk chunk-append chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement concat cond condp conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec decimal? declare definline defmacro defmethod defmulti defn defn- defonce defstruct delay delay? deliver deref derive descendants destructure disj disj! dissoc dissoc! distinct distinct? doall doc dorun doseq dosync dotimes doto double double-array doubles drop drop-last drop-while empty empty? ensure enumeration-seq eval even? every? extend extend-protocol extend-type extends? extenders false? ffirst file-seq filter find find-doc find-ns find-var first float float-array float? floats flush fn fn? fnext for force format future future-call future-cancel future-cancelled? future-done? future? gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator hash hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc init-proxy instance? int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array make-hierarchy map map? mapcat max max-key memfn memoize merge merge-with meta method-sig methods min min-key mod name namespace neg? newline next nfirst nil? nnext not not-any? not-empty not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias ns-unmap nth nthnext num number? odd? or parents partial partition pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers primitives-classnames print print-ctor print-doc print-dup print-method print-namespace-doc print-simple print-special-doc print-str printf println println-str prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues quot rand rand-int range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern re-seq read read-line read-string reify reduce ref ref-history-count ref-max-history ref-min-history ref-set refer refer-clojure release-pending-sends rem remove remove-method remove-ns repeat repeatedly replace replicate require reset! reset-meta! resolve rest resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? seque sequence sequential? set set-validator! set? short short-array shorts shutdown-agents slurp some sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? special-form-anchor special-symbol? split-at split-with str stream? string? struct struct-map subs subseq subvec supers swap! symbol symbol? sync syntax-symbol-anchor take take-last take-nth take-while test the-ns time to-array to-array-2d trampoline transient tree-seq true? type unchecked-add unchecked-dec unchecked-divide unchecked-inc unchecked-multiply unchecked-negate unchecked-remainder unchecked-subtract underive unquote unquote-splicing update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector? when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context with-local-vars with-meta with-open with-out-str with-precision xml-seq"); | |
var indentKeys = makeKeywords( | |
// Built-ins | |
"ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type try catch" + | |
// Binding forms | |
"let letfn binding loop for doseq dotimes when-let if-let" + | |
// Data structures | |
"defstruct struct-map assoc" + | |
// clojure.test | |
"testing deftest" + | |
// contrib | |
"handler-case handle dotrace deftrace"); | |
var tests = { | |
digit: /\d/, | |
digit_or_colon: /[\d:]/, | |
hex: /[0-9a-fA-F]/, | |
sign: /[+-]/, | |
exponent: /[eE]/, | |
keyword_char: /[^\s\(\[\;\)\]]/, | |
basic: /[\w\$_\-]/, | |
lang_keyword: /[\w*+!\-_?:\/]/ | |
}; | |
function stateStack(indent, type, prev) { // represents a state stack object | |
this.indent = indent; | |
this.type = type; | |
this.prev = prev; | |
} | |
function pushStack(state, indent, type) { | |
state.indentStack = new stateStack(indent, type, state.indentStack); | |
} | |
function popStack(state) { | |
state.indentStack = state.indentStack.prev; | |
} | |
function isNumber(ch, stream){ | |
// hex | |
if ( ch === '0' && 'x' == stream.peek().toLowerCase() ) { | |
stream.eat('x'); | |
stream.eatWhile(tests.hex); | |
return true; | |
} | |
// leading sign | |
if ( ch == '+' || ch == '-' ) { | |
stream.eat(tests.sign); | |
ch = stream.next(); | |
} | |
if ( tests.digit.test(ch) ) { | |
stream.eat(ch); | |
stream.eatWhile(tests.digit); | |
if ( '.' == stream.peek() ) { | |
stream.eat('.'); | |
stream.eatWhile(tests.digit); | |
} | |
if ( 'e' == stream.peek().toLowerCase() ) { | |
stream.eat(tests.exponent); | |
stream.eat(tests.sign); | |
stream.eatWhile(tests.digit); | |
} | |
return true; | |
} | |
return false; | |
} | |
return { | |
startState: function () { | |
return { | |
indentStack: null, | |
indentation: 0, | |
mode: false, | |
}; | |
}, | |
token: function (stream, state) { | |
if (state.indentStack == null && stream.sol()) { | |
// update indentation, but only if indentStack is empty | |
state.indentation = stream.indentation(); | |
} | |
// skip spaces | |
if (stream.eatSpace()) { | |
return null; | |
} | |
var returnType = null; | |
switch(state.mode){ | |
case "string": // multi-line string parsing mode | |
var next, escaped = false; | |
while ((next = stream.next()) != null) { | |
if (next == "\"" && !escaped) { | |
state.mode = false; | |
break; | |
} | |
escaped = !escaped && next == "\\"; | |
} | |
returnType = STRING; // continue on in string mode | |
break; | |
default: // default parsing mode | |
var ch = stream.next(); | |
if (ch == "\"") { | |
state.mode = "string"; | |
returnType = STRING; | |
} else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) { | |
returnType = ATOM; | |
} else if (ch == ";") { // comment | |
stream.skipToEnd(); // rest of the line is a comment | |
returnType = COMMENT; | |
} else if (isNumber(ch,stream)){ | |
returnType = NUMBER; | |
} else if (ch == "(" || ch == "[") { | |
var keyWord = ''; var indentTemp = stream.column(); | |
/** | |
Either | |
(indent-word .. | |
(non-indent-word .. | |
(;something else, bracket, etc. | |
*/ | |
while ((letter = stream.eat(tests.keyword_char)) != null) { | |
keyWord += letter; | |
} | |
if (keyWord.length > 0 && indentKeys.propertyIsEnumerable(keyWord)) { // indent-word | |
pushStack(state, indentTemp + INDENT_WORD_SKIP, ch); | |
} else { // non-indent word | |
// we continue eating the spaces | |
stream.eatSpace(); | |
if (stream.eol() || stream.peek() == ";") { | |
// nothing significant after | |
// we restart indentation 1 space after | |
pushStack(state, indentTemp + 1, ch); | |
} else { | |
pushStack(state, indentTemp + stream.current().length, ch); // else we match | |
} | |
} | |
stream.backUp(stream.current().length - 1); // undo all the eating | |
returnType = BRACKET; | |
} else if (ch == ")" || ch == "]") { | |
returnType = BRACKET; | |
if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : "[")) { | |
popStack(state); | |
} | |
} else if ( ch == ":" ) { | |
stream.eatWhile(tests.lang_keyword); | |
return TAG; | |
} else { | |
stream.eatWhile(tests.basic); | |
if (keywords && keywords.propertyIsEnumerable(stream.current())) { | |
returnType = BUILTIN; | |
} else if ( atoms && atoms.propertyIsEnumerable(stream.current()) ) { | |
returnType = ATOM; | |
} else returnType = null; | |
} | |
} | |
return returnType; | |
}, | |
indent: function (state, textAfter) { | |
if (state.indentStack == null) return state.indentation; | |
return state.indentStack.indent; | |
} | |
}; | |
}); | |
CodeMirror.defineMIME("text/x-clojure", "clojure"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Clojure mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="clojure.js"></script> | |
<style>.CodeMirror {background: #f8f8f8;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Clojure mode</h1> | |
<form><textarea id="code" name="code"> | |
; Conway's Game of Life, based on the work of: | |
;; Laurent Petit https://gist.github.com/1200343 | |
;; Christophe Grand http://clj-me.cgrand.net/2011/08/19/conways-game-of-life | |
(ns ^{:doc "Conway's Game of Life."} | |
game-of-life) | |
;; Core game of life's algorithm functions | |
(defn neighbours | |
"Given a cell's coordinates, returns the coordinates of its neighbours." | |
[[x y]] | |
(for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])] | |
[(+ dx x) (+ dy y)])) | |
(defn step | |
"Given a set of living cells, computes the new set of living cells." | |
[cells] | |
(set (for [[cell n] (frequencies (mapcat neighbours cells)) | |
:when (or (= n 3) (and (= n 2) (cells cell)))] | |
cell))) | |
;; Utility methods for displaying game on a text terminal | |
(defn print-board | |
"Prints a board on *out*, representing a step in the game." | |
[board w h] | |
(doseq [x (range (inc w)) y (range (inc h))] | |
(if (= y 0) (print "\n")) | |
(print (if (board [x y]) "[X]" " . ")))) | |
(defn display-grids | |
"Prints a squence of boards on *out*, representing several steps." | |
[grids w h] | |
(doseq [board grids] | |
(print-board board w h) | |
(print "\n"))) | |
;; Launches an example board | |
(def | |
^{:doc "board represents the initial set of living cells"} | |
board #{[2 1] [2 2] [2 3]}) | |
(display-grids (take 3 (iterate step board)) 5 5) </textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-clojure</code>.</p> | |
</body> | |
</html> | |
The MIT License | |
Copyright (c) 2011 Jeff Pickhardt | |
Modified from the Python CodeMirror mode, Copyright (c) 2010 Timothy Farrell | |
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. |
/** | |
* Link to the project's GitHub page: | |
* https://github.com/pickhardt/coffeescript-codemirror-mode | |
*/ | |
CodeMirror.defineMode('coffeescript', function(conf) { | |
var ERRORCLASS = 'error'; | |
function wordRegexp(words) { | |
return new RegExp("^((" + words.join(")|(") + "))\\b"); | |
} | |
var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]"); | |
var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]'); | |
var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))"); | |
var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))"); | |
var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))"); | |
var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*"); | |
var wordOperators = wordRegexp(['and', 'or', 'not', | |
'is', 'isnt', 'in', | |
'instanceof', 'typeof']); | |
var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else', | |
'switch', 'try', 'catch', 'finally', 'class']; | |
var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete', | |
'do', 'in', 'of', 'new', 'return', 'then', | |
'this', 'throw', 'when', 'until']; | |
var keywords = wordRegexp(indentKeywords.concat(commonKeywords)); | |
indentKeywords = wordRegexp(indentKeywords); | |
var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])"); | |
var regexPrefixes = new RegExp("^(/{3}|/)"); | |
var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no']; | |
var constants = wordRegexp(commonConstants); | |
// Tokenizers | |
function tokenBase(stream, state) { | |
// Handle scope changes | |
if (stream.sol()) { | |
var scopeOffset = state.scopes[0].offset; | |
if (stream.eatSpace()) { | |
var lineOffset = stream.indentation(); | |
if (lineOffset > scopeOffset) { | |
return 'indent'; | |
} else if (lineOffset < scopeOffset) { | |
return 'dedent'; | |
} | |
return null; | |
} else { | |
if (scopeOffset > 0) { | |
dedent(stream, state); | |
} | |
} | |
} | |
if (stream.eatSpace()) { | |
return null; | |
} | |
var ch = stream.peek(); | |
// Handle comments | |
if (ch === '#') { | |
stream.skipToEnd(); | |
return 'comment'; | |
} | |
// Handle number literals | |
if (stream.match(/^-?[0-9\.]/, false)) { | |
var floatLiteral = false; | |
// Floats | |
if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) { | |
floatLiteral = true; | |
} | |
if (stream.match(/^-?\d+\.\d*/)) { | |
floatLiteral = true; | |
} | |
if (stream.match(/^-?\.\d+/)) { | |
floatLiteral = true; | |
} | |
if (floatLiteral) { | |
return 'number'; | |
} | |
// Integers | |
var intLiteral = false; | |
// Hex | |
if (stream.match(/^-?0x[0-9a-f]+/i)) { | |
intLiteral = true; | |
} | |
// Decimal | |
if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) { | |
intLiteral = true; | |
} | |
// Zero by itself with no other piece of number. | |
if (stream.match(/^-?0(?![\dx])/i)) { | |
intLiteral = true; | |
} | |
if (intLiteral) { | |
return 'number'; | |
} | |
} | |
// Handle strings | |
if (stream.match(stringPrefixes)) { | |
state.tokenize = tokenFactory(stream.current(), 'string'); | |
return state.tokenize(stream, state); | |
} | |
// Handle regex literals | |
if (stream.match(regexPrefixes)) { | |
if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division | |
state.tokenize = tokenFactory(stream.current(), 'string-2'); | |
return state.tokenize(stream, state); | |
} else { | |
stream.backUp(1); | |
} | |
} | |
// Handle operators and delimiters | |
if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) { | |
return 'punctuation'; | |
} | |
if (stream.match(doubleOperators) | |
|| stream.match(singleOperators) | |
|| stream.match(wordOperators)) { | |
return 'operator'; | |
} | |
if (stream.match(singleDelimiters)) { | |
return 'punctuation'; | |
} | |
if (stream.match(constants)) { | |
return 'atom'; | |
} | |
if (stream.match(keywords)) { | |
return 'keyword'; | |
} | |
if (stream.match(identifiers)) { | |
return 'variable'; | |
} | |
// Handle non-detected items | |
stream.next(); | |
return ERRORCLASS; | |
} | |
function tokenFactory(delimiter, outclass) { | |
var delim_re = new RegExp(delimiter); | |
var singleline = delimiter.length == 1; | |
return function tokenString(stream, state) { | |
while (!stream.eol()) { | |
stream.eatWhile(/[^'"\/\\]/); | |
if (stream.eat('\\')) { | |
stream.next(); | |
if (singleline && stream.eol()) { | |
return outclass; | |
} | |
} else if (stream.match(delim_re)) { | |
state.tokenize = tokenBase; | |
return outclass; | |
} else { | |
stream.eat(/['"\/]/); | |
} | |
} | |
if (singleline) { | |
if (conf.mode.singleLineStringErrors) { | |
outclass = ERRORCLASS | |
} else { | |
state.tokenize = tokenBase; | |
} | |
} | |
return outclass; | |
}; | |
} | |
function indent(stream, state, type) { | |
type = type || 'coffee'; | |
var indentUnit = 0; | |
if (type === 'coffee') { | |
for (var i = 0; i < state.scopes.length; i++) { | |
if (state.scopes[i].type === 'coffee') { | |
indentUnit = state.scopes[i].offset + conf.indentUnit; | |
break; | |
} | |
} | |
} else { | |
indentUnit = stream.column() + stream.current().length; | |
} | |
state.scopes.unshift({ | |
offset: indentUnit, | |
type: type | |
}); | |
} | |
function dedent(stream, state) { | |
if (state.scopes.length == 1) return; | |
if (state.scopes[0].type === 'coffee') { | |
var _indent = stream.indentation(); | |
var _indent_index = -1; | |
for (var i = 0; i < state.scopes.length; ++i) { | |
if (_indent === state.scopes[i].offset) { | |
_indent_index = i; | |
break; | |
} | |
} | |
if (_indent_index === -1) { | |
return true; | |
} | |
while (state.scopes[0].offset !== _indent) { | |
state.scopes.shift(); | |
} | |
return false | |
} else { | |
state.scopes.shift(); | |
return false; | |
} | |
} | |
function tokenLexer(stream, state) { | |
var style = state.tokenize(stream, state); | |
var current = stream.current(); | |
// Handle '.' connected identifiers | |
if (current === '.') { | |
style = state.tokenize(stream, state); | |
current = stream.current(); | |
if (style === 'variable') { | |
return 'variable'; | |
} else { | |
return ERRORCLASS; | |
} | |
} | |
// Handle properties | |
if (current === '@') { | |
style = state.tokenize(stream, state); | |
current = stream.current(); | |
if (style === 'variable') { | |
return 'variable-2'; | |
} else { | |
return ERRORCLASS; | |
} | |
} | |
// Handle scope changes. | |
if (current === 'return') { | |
state.dedent += 1; | |
} | |
if (((current === '->' || current === '=>') && | |
!state.lambda && | |
state.scopes[0].type == 'coffee' && | |
stream.peek() === '') | |
|| style === 'indent') { | |
indent(stream, state); | |
} | |
var delimiter_index = '[({'.indexOf(current); | |
if (delimiter_index !== -1) { | |
indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1)); | |
} | |
if (indentKeywords.exec(current)){ | |
indent(stream, state); | |
} | |
if (current == 'then'){ | |
dedent(stream, state); | |
} | |
if (style === 'dedent') { | |
if (dedent(stream, state)) { | |
return ERRORCLASS; | |
} | |
} | |
delimiter_index = '])}'.indexOf(current); | |
if (delimiter_index !== -1) { | |
if (dedent(stream, state)) { | |
return ERRORCLASS; | |
} | |
} | |
if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') { | |
if (state.scopes.length > 1) state.scopes.shift(); | |
state.dedent -= 1; | |
} | |
return style; | |
} | |
var external = { | |
startState: function(basecolumn) { | |
return { | |
tokenize: tokenBase, | |
scopes: [{offset:basecolumn || 0, type:'coffee'}], | |
lastToken: null, | |
lambda: false, | |
dedent: 0 | |
}; | |
}, | |
token: function(stream, state) { | |
var style = tokenLexer(stream, state); | |
state.lastToken = {style:style, content: stream.current()}; | |
if (stream.eol() && stream.lambda) { | |
state.lambda = false; | |
} | |
return style; | |
}, | |
indent: function(state, textAfter) { | |
if (state.tokenize != tokenBase) { | |
return 0; | |
} | |
return state.scopes[0].offset; | |
} | |
}; | |
return external; | |
}); | |
CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript'); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: CoffeeScript mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="coffeescript.js"></script> | |
<style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: CoffeeScript mode</h1> | |
<form><textarea id="code" name="code"> | |
# CoffeeScript mode for CodeMirror | |
# Copyright (c) 2011 Jeff Pickhardt, released under | |
# the MIT License. | |
# | |
# Modified from the Python CodeMirror mode, which also is | |
# under the MIT License Copyright (c) 2010 Timothy Farrell. | |
# | |
# The following script, Underscore.coffee, is used to | |
# demonstrate CoffeeScript mode for CodeMirror. | |
# | |
# To download CoffeeScript mode for CodeMirror, go to: | |
# https://github.com/pickhardt/coffeescript-codemirror-mode | |
# **Underscore.coffee | |
# (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.** | |
# Underscore is freely distributable under the terms of the | |
# [MIT license](http://en.wikipedia.org/wiki/MIT_License). | |
# Portions of Underscore are inspired by or borrowed from | |
# [Prototype.js](http://prototypejs.org/api), Oliver Steele's | |
# [Functional](http://osteele.com), and John Resig's | |
# [Micro-Templating](http://ejohn.org). | |
# For all details and documentation: | |
# http://documentcloud.github.com/underscore/ | |
# Baseline setup | |
# -------------- | |
# Establish the root object, `window` in the browser, or `global` on the server. | |
root = this | |
# Save the previous value of the `_` variable. | |
previousUnderscore = root._ | |
# Establish the object that gets thrown to break out of a loop iteration. | |
# `StopIteration` is SOP on Mozilla. | |
breaker = if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration | |
# Helper function to escape **RegExp** contents, because JS doesn't have one. | |
escapeRegExp = (string) -> string.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1') | |
# Save bytes in the minified (but not gzipped) version: | |
ArrayProto = Array.prototype | |
ObjProto = Object.prototype | |
# Create quick reference variables for speed access to core prototypes. | |
slice = ArrayProto.slice | |
unshift = ArrayProto.unshift | |
toString = ObjProto.toString | |
hasOwnProperty = ObjProto.hasOwnProperty | |
propertyIsEnumerable = ObjProto.propertyIsEnumerable | |
# All **ECMA5** native implementations we hope to use are declared here. | |
nativeForEach = ArrayProto.forEach | |
nativeMap = ArrayProto.map | |
nativeReduce = ArrayProto.reduce | |
nativeReduceRight = ArrayProto.reduceRight | |
nativeFilter = ArrayProto.filter | |
nativeEvery = ArrayProto.every | |
nativeSome = ArrayProto.some | |
nativeIndexOf = ArrayProto.indexOf | |
nativeLastIndexOf = ArrayProto.lastIndexOf | |
nativeIsArray = Array.isArray | |
nativeKeys = Object.keys | |
# Create a safe reference to the Underscore object for use below. | |
_ = (obj) -> new wrapper(obj) | |
# Export the Underscore object for **CommonJS**. | |
if typeof(exports) != 'undefined' then exports._ = _ | |
# Export Underscore to global scope. | |
root._ = _ | |
# Current version. | |
_.VERSION = '1.1.0' | |
# Collection Functions | |
# -------------------- | |
# The cornerstone, an **each** implementation. | |
# Handles objects implementing **forEach**, arrays, and raw objects. | |
_.each = (obj, iterator, context) -> | |
try | |
if nativeForEach and obj.forEach is nativeForEach | |
obj.forEach iterator, context | |
else if _.isNumber obj.length | |
iterator.call context, obj[i], i, obj for i in [0...obj.length] | |
else | |
iterator.call context, val, key, obj for own key, val of obj | |
catch e | |
throw e if e isnt breaker | |
obj | |
# Return the results of applying the iterator to each element. Use JavaScript | |
# 1.6's version of **map**, if possible. | |
_.map = (obj, iterator, context) -> | |
return obj.map(iterator, context) if nativeMap and obj.map is nativeMap | |
results = [] | |
_.each obj, (value, index, list) -> | |
results.push iterator.call context, value, index, list | |
results | |
# **Reduce** builds up a single result from a list of values. Also known as | |
# **inject**, or **foldl**. Uses JavaScript 1.8's version of **reduce**, if possible. | |
_.reduce = (obj, iterator, memo, context) -> | |
if nativeReduce and obj.reduce is nativeReduce | |
iterator = _.bind iterator, context if context | |
return obj.reduce iterator, memo | |
_.each obj, (value, index, list) -> | |
memo = iterator.call context, memo, value, index, list | |
memo | |
# The right-associative version of **reduce**, also known as **foldr**. Uses | |
# JavaScript 1.8's version of **reduceRight**, if available. | |
_.reduceRight = (obj, iterator, memo, context) -> | |
if nativeReduceRight and obj.reduceRight is nativeReduceRight | |
iterator = _.bind iterator, context if context | |
return obj.reduceRight iterator, memo | |
reversed = _.clone(_.toArray(obj)).reverse() | |
_.reduce reversed, iterator, memo, context | |
# Return the first value which passes a truth test. | |
_.detect = (obj, iterator, context) -> | |
result = null | |
_.each obj, (value, index, list) -> | |
if iterator.call context, value, index, list | |
result = value | |
_.breakLoop() | |
result | |
# Return all the elements that pass a truth test. Use JavaScript 1.6's | |
# **filter**, if it exists. | |
_.filter = (obj, iterator, context) -> | |
return obj.filter iterator, context if nativeFilter and obj.filter is nativeFilter | |
results = [] | |
_.each obj, (value, index, list) -> | |
results.push value if iterator.call context, value, index, list | |
results | |
# Return all the elements for which a truth test fails. | |
_.reject = (obj, iterator, context) -> | |
results = [] | |
_.each obj, (value, index, list) -> | |
results.push value if not iterator.call context, value, index, list | |
results | |
# Determine whether all of the elements match a truth test. Delegate to | |
# JavaScript 1.6's **every**, if it is present. | |
_.every = (obj, iterator, context) -> | |
iterator ||= _.identity | |
return obj.every iterator, context if nativeEvery and obj.every is nativeEvery | |
result = true | |
_.each obj, (value, index, list) -> | |
_.breakLoop() unless (result = result and iterator.call(context, value, index, list)) | |
result | |
# Determine if at least one element in the object matches a truth test. Use | |
# JavaScript 1.6's **some**, if it exists. | |
_.some = (obj, iterator, context) -> | |
iterator ||= _.identity | |
return obj.some iterator, context if nativeSome and obj.some is nativeSome | |
result = false | |
_.each obj, (value, index, list) -> | |
_.breakLoop() if (result = iterator.call(context, value, index, list)) | |
result | |
# Determine if a given value is included in the array or object, | |
# based on `===`. | |
_.include = (obj, target) -> | |
return _.indexOf(obj, target) isnt -1 if nativeIndexOf and obj.indexOf is nativeIndexOf | |
return true for own key, val of obj when val is target | |
false | |
# Invoke a method with arguments on every item in a collection. | |
_.invoke = (obj, method) -> | |
args = _.rest arguments, 2 | |
(if method then val[method] else val).apply(val, args) for val in obj | |
# Convenience version of a common use case of **map**: fetching a property. | |
_.pluck = (obj, key) -> | |
_.map(obj, (val) -> val[key]) | |
# Return the maximum item or (item-based computation). | |
_.max = (obj, iterator, context) -> | |
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj) | |
result = computed: -Infinity | |
_.each obj, (value, index, list) -> | |
computed = if iterator then iterator.call(context, value, index, list) else value | |
computed >= result.computed and (result = {value: value, computed: computed}) | |
result.value | |
# Return the minimum element (or element-based computation). | |
_.min = (obj, iterator, context) -> | |
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj) | |
result = computed: Infinity | |
_.each obj, (value, index, list) -> | |
computed = if iterator then iterator.call(context, value, index, list) else value | |
computed < result.computed and (result = {value: value, computed: computed}) | |
result.value | |
# Sort the object's values by a criterion produced by an iterator. | |
_.sortBy = (obj, iterator, context) -> | |
_.pluck(((_.map obj, (value, index, list) -> | |
{value: value, criteria: iterator.call(context, value, index, list)} | |
).sort((left, right) -> | |
a = left.criteria; b = right.criteria | |
if a < b then -1 else if a > b then 1 else 0 | |
)), 'value') | |
# Use a comparator function to figure out at what index an object should | |
# be inserted so as to maintain order. Uses binary search. | |
_.sortedIndex = (array, obj, iterator) -> | |
iterator ||= _.identity | |
low = 0 | |
high = array.length | |
while low < high | |
mid = (low + high) >> 1 | |
if iterator(array[mid]) < iterator(obj) then low = mid + 1 else high = mid | |
low | |
# Convert anything iterable into a real, live array. | |
_.toArray = (iterable) -> | |
return [] if (!iterable) | |
return iterable.toArray() if (iterable.toArray) | |
return iterable if (_.isArray(iterable)) | |
return slice.call(iterable) if (_.isArguments(iterable)) | |
_.values(iterable) | |
# Return the number of elements in an object. | |
_.size = (obj) -> _.toArray(obj).length | |
# Array Functions | |
# --------------- | |
# Get the first element of an array. Passing `n` will return the first N | |
# values in the array. Aliased as **head**. The `guard` check allows it to work | |
# with **map**. | |
_.first = (array, n, guard) -> | |
if n and not guard then slice.call(array, 0, n) else array[0] | |
# Returns everything but the first entry of the array. Aliased as **tail**. | |
# Especially useful on the arguments object. Passing an `index` will return | |
# the rest of the values in the array from that index onward. The `guard` | |
# check allows it to work with **map**. | |
_.rest = (array, index, guard) -> | |
slice.call(array, if _.isUndefined(index) or guard then 1 else index) | |
# Get the last element of an array. | |
_.last = (array) -> array[array.length - 1] | |
# Trim out all falsy values from an array. | |
_.compact = (array) -> item for item in array when item | |
# Return a completely flattened version of an array. | |
_.flatten = (array) -> | |
_.reduce array, (memo, value) -> | |
return memo.concat(_.flatten(value)) if _.isArray value | |
memo.push value | |
memo | |
, [] | |
# Return a version of the array that does not contain the specified value(s). | |
_.without = (array) -> | |
values = _.rest arguments | |
val for val in _.toArray(array) when not _.include values, val | |
# Produce a duplicate-free version of the array. If the array has already | |
# been sorted, you have the option of using a faster algorithm. | |
_.uniq = (array, isSorted) -> | |
memo = [] | |
for el, i in _.toArray array | |
memo.push el if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el)) | |
memo | |
# Produce an array that contains every item shared between all the | |
# passed-in arrays. | |
_.intersect = (array) -> | |
rest = _.rest arguments | |
_.select _.uniq(array), (item) -> | |
_.all rest, (other) -> | |
_.indexOf(other, item) >= 0 | |
# Zip together multiple lists into a single array -- elements that share | |
# an index go together. | |
_.zip = -> | |
length = _.max _.pluck arguments, 'length' | |
results = new Array length | |
for i in [0...length] | |
results[i] = _.pluck arguments, String i | |
results | |
# If the browser doesn't supply us with **indexOf** (I'm looking at you, MSIE), | |
# we need this function. Return the position of the first occurrence of an | |
# item in an array, or -1 if the item is not included in the array. | |
_.indexOf = (array, item) -> | |
return array.indexOf item if nativeIndexOf and array.indexOf is nativeIndexOf | |
i = 0; l = array.length | |
while l - i | |
if array[i] is item then return i else i++ | |
-1 | |
# Provide JavaScript 1.6's **lastIndexOf**, delegating to the native function, | |
# if possible. | |
_.lastIndexOf = (array, item) -> | |
return array.lastIndexOf(item) if nativeLastIndexOf and array.lastIndexOf is nativeLastIndexOf | |
i = array.length | |
while i | |
if array[i] is item then return i else i-- | |
-1 | |
# Generate an integer Array containing an arithmetic progression. A port of | |
# [the native Python **range** function](http://docs.python.org/library/functions.html#range). | |
_.range = (start, stop, step) -> | |
a = arguments | |
solo = a.length <= 1 | |
i = start = if solo then 0 else a[0] | |
stop = if solo then a[0] else a[1] | |
step = a[2] or 1 | |
len = Math.ceil((stop - start) / step) | |
return [] if len <= 0 | |
range = new Array len | |
idx = 0 | |
loop | |
return range if (if step > 0 then i - stop else stop - i) >= 0 | |
range[idx] = i | |
idx++ | |
i+= step | |
# Function Functions | |
# ------------------ | |
# Create a function bound to a given object (assigning `this`, and arguments, | |
# optionally). Binding with arguments is also known as **curry**. | |
_.bind = (func, obj) -> | |
args = _.rest arguments, 2 | |
-> func.apply obj or root, args.concat arguments | |
# Bind all of an object's methods to that object. Useful for ensuring that | |
# all callbacks defined on an object belong to it. | |
_.bindAll = (obj) -> | |
funcs = if arguments.length > 1 then _.rest(arguments) else _.functions(obj) | |
_.each funcs, (f) -> obj[f] = _.bind obj[f], obj | |
obj | |
# Delays a function for the given number of milliseconds, and then calls | |
# it with the arguments supplied. | |
_.delay = (func, wait) -> | |
args = _.rest arguments, 2 | |
setTimeout((-> func.apply(func, args)), wait) | |
# Memoize an expensive function by storing its results. | |
_.memoize = (func, hasher) -> | |
memo = {} | |
hasher or= _.identity | |
-> | |
key = hasher.apply this, arguments | |
return memo[key] if key of memo | |
memo[key] = func.apply this, arguments | |
# Defers a function, scheduling it to run after the current call stack has | |
# cleared. | |
_.defer = (func) -> | |
_.delay.apply _, [func, 1].concat _.rest arguments | |
# Returns the first function passed as an argument to the second, | |
# allowing you to adjust arguments, run code before and after, and | |
# conditionally execute the original function. | |
_.wrap = (func, wrapper) -> | |
-> wrapper.apply wrapper, [func].concat arguments | |
# Returns a function that is the composition of a list of functions, each | |
# consuming the return value of the function that follows. | |
_.compose = -> | |
funcs = arguments | |
-> | |
args = arguments | |
for i in [funcs.length - 1..0] by -1 | |
args = [funcs[i].apply(this, args)] | |
args[0] | |
# Object Functions | |
# ---------------- | |
# Retrieve the names of an object's properties. | |
_.keys = nativeKeys or (obj) -> | |
return _.range 0, obj.length if _.isArray(obj) | |
key for key, val of obj | |
# Retrieve the values of an object's properties. | |
_.values = (obj) -> | |
_.map obj, _.identity | |
# Return a sorted list of the function names available in Underscore. | |
_.functions = (obj) -> | |
_.filter(_.keys(obj), (key) -> _.isFunction(obj[key])).sort() | |
# Extend a given object with all of the properties in a source object. | |
_.extend = (obj) -> | |
for source in _.rest(arguments) | |
obj[key] = val for key, val of source | |
obj | |
# Create a (shallow-cloned) duplicate of an object. | |
_.clone = (obj) -> | |
return obj.slice 0 if _.isArray obj | |
_.extend {}, obj | |
# Invokes interceptor with the obj, and then returns obj. | |
# The primary purpose of this method is to "tap into" a method chain, | |
# in order to perform operations on intermediate results within | |
the chain. | |
_.tap = (obj, interceptor) -> | |
interceptor obj | |
obj | |
# Perform a deep comparison to check if two objects are equal. | |
_.isEqual = (a, b) -> | |
# Check object identity. | |
return true if a is b | |
# Different types? | |
atype = typeof(a); btype = typeof(b) | |
return false if atype isnt btype | |
# Basic equality test (watch out for coercions). | |
return true if `a == b` | |
# One is falsy and the other truthy. | |
return false if (!a and b) or (a and !b) | |
# One of them implements an `isEqual()`? | |
return a.isEqual(b) if a.isEqual | |
# Check dates' integer values. | |
return a.getTime() is b.getTime() if _.isDate(a) and _.isDate(b) | |
# Both are NaN? | |
return false if _.isNaN(a) and _.isNaN(b) | |
# Compare regular expressions. | |
if _.isRegExp(a) and _.isRegExp(b) | |
return a.source is b.source and | |
a.global is b.global and | |
a.ignoreCase is b.ignoreCase and | |
a.multiline is b.multiline | |
# If a is not an object by this point, we can't handle it. | |
return false if atype isnt 'object' | |
# Check for different array lengths before comparing contents. | |
return false if a.length and (a.length isnt b.length) | |
# Nothing else worked, deep compare the contents. | |
aKeys = _.keys(a); bKeys = _.keys(b) | |
# Different object sizes? | |
return false if aKeys.length isnt bKeys.length | |
# Recursive comparison of contents. | |
return false for key, val of a when !(key of b) or !_.isEqual(val, b[key]) | |
true | |
# Is a given array or object empty? | |
_.isEmpty = (obj) -> | |
return obj.length is 0 if _.isArray(obj) or _.isString(obj) | |
return false for own key of obj | |
true | |
# Is a given value a DOM element? | |
_.isElement = (obj) -> obj and obj.nodeType is 1 | |
# Is a given value an array? | |
_.isArray = nativeIsArray or (obj) -> !!(obj and obj.concat and obj.unshift and not obj.callee) | |
# Is a given variable an arguments object? | |
_.isArguments = (obj) -> obj and obj.callee | |
# Is the given value a function? | |
_.isFunction = (obj) -> !!(obj and obj.constructor and obj.call and obj.apply) | |
# Is the given value a string? | |
_.isString = (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr)) | |
# Is a given value a number? | |
_.isNumber = (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]' | |
# Is a given value a boolean? | |
_.isBoolean = (obj) -> obj is true or obj is false | |
# Is a given value a Date? | |
_.isDate = (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear) | |
# Is the given value a regular expression? | |
_.isRegExp = (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false)) | |
# Is the given value NaN -- this one is interesting. `NaN != NaN`, and | |
# `isNaN(undefined) == true`, so we make sure it's a number first. | |
_.isNaN = (obj) -> _.isNumber(obj) and window.isNaN(obj) | |
# Is a given value equal to null? | |
_.isNull = (obj) -> obj is null | |
# Is a given variable undefined? | |
_.isUndefined = (obj) -> typeof obj is 'undefined' | |
# Utility Functions | |
# ----------------- | |
# Run Underscore.js in noConflict mode, returning the `_` variable to its | |
# previous owner. Returns a reference to the Underscore object. | |
_.noConflict = -> | |
root._ = previousUnderscore | |
this | |
# Keep the identity function around for default iterators. | |
_.identity = (value) -> value | |
# Run a function `n` times. | |
_.times = (n, iterator, context) -> | |
iterator.call context, i for i in [0...n] | |
# Break out of the middle of an iteration. | |
_.breakLoop = -> throw breaker | |
# Add your own custom functions to the Underscore object, ensuring that | |
# they're correctly added to the OOP wrapper as well. | |
_.mixin = (obj) -> | |
for name in _.functions(obj) | |
addToWrapper name, _[name] = obj[name] | |
# Generate a unique integer id (unique within the entire client session). | |
# Useful for temporary DOM ids. | |
idCounter = 0 | |
_.uniqueId = (prefix) -> | |
(prefix or '') + idCounter++ | |
# By default, Underscore uses **ERB**-style template delimiters, change the | |
# following template settings to use alternative delimiters. | |
_.templateSettings = { | |
start: '<%' | |
end: '%>' | |
interpolate: /<%=(.+?)%>/g | |
} | |
# JavaScript templating a-la **ERB**, pilfered from John Resig's | |
# *Secrets of the JavaScript Ninja*, page 83. | |
# Single-quote fix from Rick Strahl. | |
# With alterations for arbitrary delimiters, and to preserve whitespace. | |
_.template = (str, data) -> | |
c = _.templateSettings | |
endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g") | |
fn = new Function 'obj', | |
'var p=[],print=function(){p.push.apply(p,arguments);};' + | |
'with(obj||{}){p.push(\'' + | |
str.replace(/\r/g, '\\r') | |
.replace(/\n/g, '\\n') | |
.replace(/\t/g, '\\t') | |
.replace(endMatch,"���") | |
.split("'").join("\\'") | |
.split("���").join("'") | |
.replace(c.interpolate, "',$1,'") | |
.split(c.start).join("');") | |
.split(c.end).join("p.push('") + | |
"');}return p.join('');" | |
if data then fn(data) else fn | |
# Aliases | |
# ------- | |
_.forEach = _.each | |
_.foldl = _.inject = _.reduce | |
_.foldr = _.reduceRight | |
_.select = _.filter | |
_.all = _.every | |
_.any = _.some | |
_.contains = _.include | |
_.head = _.first | |
_.tail = _.rest | |
_.methods = _.functions | |
# Setup the OOP Wrapper | |
# --------------------- | |
# If Underscore is called as a function, it returns a wrapped object that | |
# can be used OO-style. This wrapper holds altered versions of all the | |
# underscore functions. Wrapped objects may be chained. | |
wrapper = (obj) -> | |
this._wrapped = obj | |
this | |
# Helper function to continue chaining intermediate results. | |
result = (obj, chain) -> | |
if chain then _(obj).chain() else obj | |
# A method to easily add functions to the OOP wrapper. | |
addToWrapper = (name, func) -> | |
wrapper.prototype[name] = -> | |
args = _.toArray arguments | |
unshift.call args, this._wrapped | |
result func.apply(_, args), this._chain | |
# Add all ofthe Underscore functions to the wrapper object. | |
_.mixin _ | |
# Add all mutator Array functions to the wrapper. | |
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) -> | |
method = Array.prototype[name] | |
wrapper.prototype[name] = -> | |
method.apply(this._wrapped, arguments) | |
result(this._wrapped, this._chain) | |
# Add all accessor Array functions to the wrapper. | |
_.each ['concat', 'join', 'slice'], (name) -> | |
method = Array.prototype[name] | |
wrapper.prototype[name] = -> | |
result(method.apply(this._wrapped, arguments), this._chain) | |
# Start chaining a wrapped Underscore object. | |
wrapper::chain = -> | |
this._chain = true | |
this | |
# Extracts the result from a wrapped and chained object. | |
wrapper::value = -> this._wrapped | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-coffeescript</code>.</p> | |
<p>The CoffeeScript mode was written by Jeff Pickhardt (<a href="LICENSE">license</a>).</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("css", function(config) { | |
var indentUnit = config.indentUnit, type; | |
function ret(style, tp) {type = tp; return style;} | |
function tokenBase(stream, state) { | |
var ch = stream.next(); | |
if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} | |
else if (ch == "/" && stream.eat("*")) { | |
state.tokenize = tokenCComment; | |
return tokenCComment(stream, state); | |
} | |
else if (ch == "<" && stream.eat("!")) { | |
state.tokenize = tokenSGMLComment; | |
return tokenSGMLComment(stream, state); | |
} | |
else if (ch == "=") ret(null, "compare"); | |
else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); | |
else if (ch == "\"" || ch == "'") { | |
state.tokenize = tokenString(ch); | |
return state.tokenize(stream, state); | |
} | |
else if (ch == "#") { | |
stream.eatWhile(/[\w\\\-]/); | |
return ret("atom", "hash"); | |
} | |
else if (ch == "!") { | |
stream.match(/^\s*\w*/); | |
return ret("keyword", "important"); | |
} | |
else if (/\d/.test(ch)) { | |
stream.eatWhile(/[\w.%]/); | |
return ret("number", "unit"); | |
} | |
else if (/[,.+>*\/]/.test(ch)) { | |
return ret(null, "select-op"); | |
} | |
else if (/[;{}:\[\]]/.test(ch)) { | |
return ret(null, ch); | |
} | |
else { | |
stream.eatWhile(/[\w\\\-]/); | |
return ret("variable", "variable"); | |
} | |
} | |
function tokenCComment(stream, state) { | |
var maybeEnd = false, ch; | |
while ((ch = stream.next()) != null) { | |
if (maybeEnd && ch == "/") { | |
state.tokenize = tokenBase; | |
break; | |
} | |
maybeEnd = (ch == "*"); | |
} | |
return ret("comment", "comment"); | |
} | |
function tokenSGMLComment(stream, state) { | |
var dashes = 0, ch; | |
while ((ch = stream.next()) != null) { | |
if (dashes >= 2 && ch == ">") { | |
state.tokenize = tokenBase; | |
break; | |
} | |
dashes = (ch == "-") ? dashes + 1 : 0; | |
} | |
return ret("comment", "comment"); | |
} | |
function tokenString(quote) { | |
return function(stream, state) { | |
var escaped = false, ch; | |
while ((ch = stream.next()) != null) { | |
if (ch == quote && !escaped) | |
break; | |
escaped = !escaped && ch == "\\"; | |
} | |
if (!escaped) state.tokenize = tokenBase; | |
return ret("string", "string"); | |
}; | |
} | |
return { | |
startState: function(base) { | |
return {tokenize: tokenBase, | |
baseIndent: base || 0, | |
stack: []}; | |
}, | |
token: function(stream, state) { | |
if (stream.eatSpace()) return null; | |
var style = state.tokenize(stream, state); | |
var context = state.stack[state.stack.length-1]; | |
if (type == "hash" && context == "rule") style = "atom"; | |
else if (style == "variable") { | |
if (context == "rule") style = "number"; | |
else if (!context || context == "@media{") style = "tag"; | |
} | |
if (context == "rule" && /^[\{\};]$/.test(type)) | |
state.stack.pop(); | |
if (type == "{") { | |
if (context == "@media") state.stack[state.stack.length-1] = "@media{"; | |
else state.stack.push("{"); | |
} | |
else if (type == "}") state.stack.pop(); | |
else if (type == "@media") state.stack.push("@media"); | |
else if (context == "{" && type != "comment") state.stack.push("rule"); | |
return style; | |
}, | |
indent: function(state, textAfter) { | |
var n = state.stack.length; | |
if (/^\}/.test(textAfter)) | |
n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; | |
return state.baseIndent + n * indentUnit; | |
}, | |
electricChars: "}" | |
}; | |
}); | |
CodeMirror.defineMIME("text/css", "css"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: CSS mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="css.js"></script> | |
<style>.CodeMirror {background: #f8f8f8;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: CSS mode</h1> | |
<form><textarea id="code" name="code"> | |
/* Some example CSS */ | |
@import url("something.css"); | |
body { | |
margin: 0; | |
padding: 3em 6em; | |
font-family: tahoma, arial, sans-serif; | |
color: #000; | |
} | |
#navigation a { | |
font-weight: bold; | |
text-decoration: none !important; | |
} | |
h1 { | |
font-size: 2.5em; | |
} | |
h2 { | |
font-size: 1.7em; | |
} | |
h1:before, h2:before { | |
content: "::"; | |
} | |
code { | |
font-family: courier, monospace; | |
font-size: 80%; | |
color: #418A8A; | |
} | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/css</code>.</p> | |
</body> | |
</html> | |
span.cm-rangeinfo {color: #a0b;} | |
span.cm-minus {color: red;} | |
span.cm-plus {color: #2b2;} | |
CodeMirror.defineMode("diff", function() { | |
return { | |
token: function(stream) { | |
var ch = stream.next(); | |
stream.skipToEnd(); | |
if (ch == "+") return "plus"; | |
if (ch == "-") return "minus"; | |
if (ch == "@") return "rangeinfo"; | |
} | |
}; | |
}); | |
CodeMirror.defineMIME("text/x-diff", "diff"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Diff mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="diff.js"></script> | |
<link rel="stylesheet" href="diff.css"> | |
<style>.CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Diff mode</h1> | |
<form><textarea id="code" name="code"> | |
diff --git a/index.html b/index.html | |
index c1d9156..7764744 100644 | |
--- a/index.html | |
+++ b/index.html | |
@@ -95,7 +95,8 @@ StringStream.prototype = { | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
- autoMatchBrackets: true | |
+ autoMatchBrackets: true, | |
+ onGutterClick: function(x){console.log(x);} | |
}); | |
</script> | |
</body> | |
diff --git a/lib/codemirror.js b/lib/codemirror.js | |
index 04646a9..9a39cc7 100644 | |
--- a/lib/codemirror.js | |
+++ b/lib/codemirror.js | |
@@ -399,10 +399,16 @@ var CodeMirror = (function() { | |
} | |
function onMouseDown(e) { | |
- var start = posFromMouse(e), last = start; | |
+ var start = posFromMouse(e), last = start, target = e.target(); | |
if (!start) return; | |
setCursor(start.line, start.ch, false); | |
if (e.button() != 1) return; | |
+ if (target.parentNode == gutter) { | |
+ if (options.onGutterClick) | |
+ options.onGutterClick(indexOf(gutter.childNodes, target) + showingFrom); | |
+ return; | |
+ } | |
+ | |
if (!focused) onFocus(); | |
e.stop(); | |
@@ -808,7 +814,7 @@ var CodeMirror = (function() { | |
for (var i = showingFrom; i < showingTo; ++i) { | |
var marker = lines[i].gutterMarker; | |
if (marker) html.push('<div class="' + marker.style + '">' + htmlEscape(marker.text) + '</div>'); | |
- else html.push("<div>" + (options.lineNumbers ? i + 1 : "\u00a0") + "</div>"); | |
+ else html.push("<div>" + (options.lineNumbers ? i + options.firstLineNumber : "\u00a0") + "</div>"); | |
} | |
gutter.style.display = "none"; // TODO test whether this actually helps | |
gutter.innerHTML = html.join(""); | |
@@ -1371,10 +1377,8 @@ var CodeMirror = (function() { | |
if (option == "parser") setParser(value); | |
else if (option === "lineNumbers") setLineNumbers(value); | |
else if (option === "gutter") setGutter(value); | |
- else if (option === "readOnly") options.readOnly = value; | |
- else if (option === "indentUnit") {options.indentUnit = indentUnit = value; setParser(options.parser);} | |
- else if (/^(?:enterMode|tabMode|indentWithTabs|readOnly|autoMatchBrackets|undoDepth)$/.test(option)) options[option] = value; | |
- else throw new Error("Can't set option " + option); | |
+ else if (option === "indentUnit") {options.indentUnit = value; setParser(options.parser);} | |
+ else options[option] = value; | |
}, | |
cursorCoords: cursorCoords, | |
undo: operation(undo), | |
@@ -1402,7 +1406,8 @@ var CodeMirror = (function() { | |
replaceRange: operation(replaceRange), | |
operation: function(f){return operation(f)();}, | |
- refresh: function(){updateDisplay([{from: 0, to: lines.length}]);} | |
+ refresh: function(){updateDisplay([{from: 0, to: lines.length}]);}, | |
+ getInputField: function(){return input;} | |
}; | |
return instance; | |
} | |
@@ -1420,6 +1425,7 @@ var CodeMirror = (function() { | |
readOnly: false, | |
onChange: null, | |
onCursorActivity: null, | |
+ onGutterClick: null, | |
autoMatchBrackets: false, | |
workTime: 200, | |
workDelay: 300, | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-diff</code>.</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("gfm", function(config, parserConfig) { | |
var mdMode = CodeMirror.getMode(config, "markdown"); | |
var aliases = { | |
html: "htmlmixed", | |
js: "javascript", | |
json: "application/json", | |
c: "text/x-csrc", | |
"c++": "text/x-c++src", | |
java: "text/x-java", | |
csharp: "text/x-csharp", | |
"c#": "text/x-csharp", | |
}; | |
// make this lazy so that we don't need to load GFM last | |
var getMode = (function () { | |
var i, modes = {}, mimes = {}, mime; | |
var list = CodeMirror.listModes(); | |
for (i = 0; i < list.length; i++) { | |
modes[list[i]] = list[i]; | |
} | |
var mimesList = CodeMirror.listMIMEs(); | |
for (i = 0; i < mimesList.length; i++) { | |
mime = mimesList[i].mime; | |
mimes[mime] = mimesList[i].mime; | |
} | |
for (var a in aliases) { | |
if (aliases[a] in modes || aliases[a] in mimes) | |
modes[a] = aliases[a]; | |
} | |
return function (lang) { | |
return modes[lang] ? CodeMirror.getMode(config, modes[lang]) : null; | |
} | |
}()); | |
function markdown(stream, state) { | |
// intercept fenced code blocks | |
if (stream.sol() && stream.match(/^```([\w+#]*)/)) { | |
// try switching mode | |
state.localMode = getMode(RegExp.$1) | |
if (state.localMode) | |
state.localState = state.localMode.startState(); | |
state.token = local; | |
return 'code'; | |
} | |
return mdMode.token(stream, state.mdState); | |
} | |
function local(stream, state) { | |
if (stream.sol() && stream.match(/^```/)) { | |
state.localMode = state.localState = null; | |
state.token = markdown; | |
return 'code'; | |
} | |
else if (state.localMode) { | |
return state.localMode.token(stream, state.localState); | |
} else { | |
stream.skipToEnd(); | |
return 'code'; | |
} | |
} | |
// custom handleText to prevent emphasis in the middle of a word | |
// and add autolinking | |
function handleText(stream, mdState) { | |
var match; | |
if (stream.match(/^\w+:\/\/\S+/)) { | |
return 'linkhref'; | |
} | |
if (stream.match(/^[^\[*\\<>` _][^\[*\\<>` ]*[^\[*\\<>` _]/)) { | |
return mdMode.getType(mdState); | |
} | |
if (match = stream.match(/^[^\[*\\<>` ]+/)) { | |
var word = match[0]; | |
if (word[0] === '_' && word[word.length-1] === '_') { | |
stream.backUp(word.length); | |
return undefined; | |
} | |
return mdMode.getType(mdState); | |
} | |
if (stream.eatSpace()) { | |
return null; | |
} | |
} | |
return { | |
startState: function() { | |
var mdState = mdMode.startState(); | |
mdState.text = handleText; | |
return {token: markdown, mode: "markdown", mdState: mdState, | |
localMode: null, localState: null}; | |
}, | |
copyState: function(state) { | |
return {token: state.token, mode: state.mode, mdState: CodeMirror.copyState(mdMode, state.mdState), | |
localMode: state.localMode, | |
localState: state.localMode ? CodeMirror.copyState(state.localMode, state.localState) : null}; | |
}, | |
token: function(stream, state) { | |
return state.token(stream, state); | |
} | |
} | |
}); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: GFM mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="../xml/xml.js"></script> | |
<script src="../markdown/markdown.js"></script> | |
<script src="gfm.js"></script> | |
<script src="../javascript/javascript.js"></script> | |
<link rel="stylesheet" href="../markdown/markdown.css"> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: GFM mode</h1> | |
<!-- source: http://daringfireball.net/projects/markdown/basics.text --> | |
<form><textarea id="code" name="code"> | |
Github Flavored Markdown | |
======================== | |
Everything from markdown plus GFM features: | |
## Fenced code blocks | |
```javascript | |
for (var i = 0; i < items.length; i++) { | |
console.log(items[i], i); // log them | |
} | |
``` | |
See http://github.github.com/github-flavored-markdown/ | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
mode: 'gfm', | |
lineNumbers: true, | |
matchBrackets: true, | |
theme: "default" | |
}); | |
</script> | |
</body> | |
</html> | |
CodeMirror.defineMode("groovy", function(config, parserConfig) { | |
function words(str) { | |
var obj = {}, words = str.split(" "); | |
for (var i = 0; i < words.length; ++i) obj[words[i]] = true; | |
return obj; | |
} | |
var keywords = words( | |
"abstract as assert boolean break byte case catch char class const continue def default " + | |
"do double else enum extends final finally float for goto if implements import in " + | |
"instanceof int interface long native new package private protected public return " + | |
"short static strictfp super switch synchronized threadsafe throw throws transient " + | |
"try void volatile while"); | |
var blockKeywords = words("catch class do else finally for if switch try while enum interface def"); | |
var atoms = words("null true false this"); | |
var curPunc; | |
function tokenBase(stream, state) { | |
var ch = stream.next(); | |
if (ch == '"' || ch == "'") { | |
return startString(ch, stream, state); | |
} | |
if (/[\[\]{}\(\),;\:\.]/.test(ch)) { | |
curPunc = ch; | |
return null | |
} | |
if (/\d/.test(ch)) { | |
stream.eatWhile(/[\w\.]/); | |
if (stream.eat(/eE/)) { stream.eat(/\+\-/); stream.eatWhile(/\d/); } | |
return "number"; | |
} | |
if (ch == "/") { | |
if (stream.eat("*")) { | |
state.tokenize.push(tokenComment); | |
return tokenComment(stream, state); | |
} | |
if (stream.eat("/")) { | |
stream.skipToEnd(); | |
return "comment"; | |
} | |
if (expectExpression(state.lastToken)) { | |
return startString(ch, stream, state); | |
} | |
} | |
if (ch == "-" && stream.eat(">")) { | |
curPunc = "->"; | |
return null; | |
} | |
if (/[+\-*&%=<>!?|\/~]/.test(ch)) { | |
stream.eatWhile(/[+\-*&%=<>|~]/); | |
return "operator"; | |
} | |
stream.eatWhile(/[\w\$_]/); | |
if (ch == "@") { stream.eatWhile(/[\w\$_\.]/); return "meta"; } | |
if (state.lastToken == ".") return "property"; | |
if (stream.eat(":")) { curPunc = "proplabel"; return "property"; } | |
var cur = stream.current(); | |
if (atoms.propertyIsEnumerable(cur)) { return "atom"; } | |
if (keywords.propertyIsEnumerable(cur)) { | |
if (blockKeywords.propertyIsEnumerable(cur)) curPunc = "newstatement"; | |
return "keyword"; | |
} | |
return "word"; | |
} | |
tokenBase.isBase = true; | |
function startString(quote, stream, state) { | |
var tripleQuoted = false; | |
if (quote != "/" && stream.eat(quote)) { | |
if (stream.eat(quote)) tripleQuoted = true; | |
else return "string"; | |
} | |
function t(stream, state) { | |
var escaped = false, next, end = !tripleQuoted; | |
while ((next = stream.next()) != null) { | |
if (next == quote && !escaped) { | |
if (!tripleQuoted) { break; } | |
if (stream.match(quote + quote)) { end = true; break; } | |
} | |
if (quote == '"' && next == "$" && !escaped && stream.eat("{")) { | |
state.tokenize.push(tokenBaseUntilBrace()); | |
return "string"; | |
} | |
escaped = !escaped && next == "\\"; | |
} | |
if (end) state.tokenize.pop(); | |
return "string"; | |
} | |
state.tokenize.push(t); | |
return t(stream, state); | |
} | |
function tokenBaseUntilBrace() { | |
var depth = 1; | |
function t(stream, state) { | |
if (stream.peek() == "}") { | |
depth--; | |
if (depth == 0) { | |
state.tokenize.pop(); | |
return state.tokenize[state.tokenize.length-1](stream, state); | |
} | |
} else if (stream.peek() == "{") { | |
depth++; | |
} | |
return tokenBase(stream, state); | |
} | |
t.isBase = true; | |
return t; | |
} | |
function tokenComment(stream, state) { | |
var maybeEnd = false, ch; | |
while (ch = stream.next()) { | |
if (ch == "/" && maybeEnd) { | |
state.tokenize.pop(); | |
break; | |
} | |
maybeEnd = (ch == "*"); | |
} | |
return "comment"; | |
} | |
function expectExpression(last) { | |
return !last || last == "operator" || last == "->" || /[\.\[\{\(,;:]/.test(last) || | |
last == "newstatement" || last == "keyword" || last == "proplabel"; | |
} | |
function Context(indented, column, type, align, prev) { | |
this.indented = indented; | |
this.column = column; | |
this.type = type; | |
this.align = align; | |
this.prev = prev; | |
} | |
function pushContext(state, col, type) { | |
return state.context = new Context(state.indented, col, type, null, state.context); | |
} | |
function popContext(state) { | |
var t = state.context.type; | |
if (t == ")" || t == "]" || t == "}") | |
state.indented = state.context.indented; | |
return state.context = state.context.prev; | |
} | |
// Interface | |
return { | |
startState: function(basecolumn) { | |
return { | |
tokenize: [tokenBase], | |
context: new Context((basecolumn || 0) - config.indentUnit, 0, "top", false), | |
indented: 0, | |
startOfLine: true, | |
lastToken: null | |
}; | |
}, | |
token: function(stream, state) { | |
var ctx = state.context; | |
if (stream.sol()) { | |
if (ctx.align == null) ctx.align = false; | |
state.indented = stream.indentation(); | |
state.startOfLine = true; | |
// Automatic semicolon insertion | |
if (ctx.type == "statement" && !expectExpression(state.lastToken)) { | |
popContext(state); ctx = state.context; | |
} | |
} | |
if (stream.eatSpace()) return null; | |
curPunc = null; | |
var style = state.tokenize[state.tokenize.length-1](stream, state); | |
if (style == "comment") return style; | |
if (ctx.align == null) ctx.align = true; | |
if ((curPunc == ";" || curPunc == ":") && ctx.type == "statement") popContext(state); | |
// Handle indentation for {x -> \n ... } | |
else if (curPunc == "->" && ctx.type == "statement" && ctx.prev.type == "}") { | |
popContext(state); | |
state.context.align = false; | |
} | |
else if (curPunc == "{") pushContext(state, stream.column(), "}"); | |
else if (curPunc == "[") pushContext(state, stream.column(), "]"); | |
else if (curPunc == "(") pushContext(state, stream.column(), ")"); | |
else if (curPunc == "}") { | |
while (ctx.type == "statement") ctx = popContext(state); | |
if (ctx.type == "}") ctx = popContext(state); | |
while (ctx.type == "statement") ctx = popContext(state); | |
} | |
else if (curPunc == ctx.type) popContext(state); | |
else if (ctx.type == "}" || ctx.type == "top" || (ctx.type == "statement" && curPunc == "newstatement")) | |
pushContext(state, stream.column(), "statement"); | |
state.startOfLine = false; | |
state.lastToken = curPunc || style; | |
return style; | |
}, | |
indent: function(state, textAfter) { | |
if (!state.tokenize[state.tokenize.length-1].isBase) return 0; | |
var firstChar = textAfter && textAfter.charAt(0), ctx = state.context; | |
if (ctx.type == "statement" && !expectExpression(state.lastToken)) ctx = ctx.prev; | |
var closing = firstChar == ctx.type; | |
if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : config.indentUnit); | |
else if (ctx.align) return ctx.column + (closing ? 0 : 1); | |
else return ctx.indented + (closing ? 0 : config.indentUnit); | |
}, | |
electricChars: "{}" | |
}; | |
}); | |
CodeMirror.defineMIME("text/x-groovy", "groovy"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Groovy mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="groovy.js"></script> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
<style>.CodeMirror {border-top: 1px solid #500; border-bottom: 1px solid #500;}</style> | |
</head> | |
<body> | |
<h1>CodeMirror: Groovy mode</h1> | |
<form><textarea id="code" name="code"> | |
//Pattern for groovy script | |
def p = ~/.*\.groovy/ | |
new File( 'd:\\scripts' ).eachFileMatch(p) {f -> | |
// imports list | |
def imports = [] | |
f.eachLine { | |
// condition to detect an import instruction | |
ln -> if ( ln =~ '^import .*' ) { | |
imports << "${ln - 'import '}" | |
} | |
} | |
// print thmen | |
if ( ! imports.empty ) { | |
println f | |
imports.each{ println " $it" } | |
} | |
} | |
/* Coin changer demo code from http://groovy.codehaus.org */ | |
enum UsCoin { | |
quarter(25), dime(10), nickel(5), penny(1) | |
UsCoin(v) { value = v } | |
final value | |
} | |
enum OzzieCoin { | |
fifty(50), twenty(20), ten(10), five(5) | |
OzzieCoin(v) { value = v } | |
final value | |
} | |
def plural(word, count) { | |
if (count == 1) return word | |
word[-1] == 'y' ? word[0..-2] + "ies" : word + "s" | |
} | |
def change(currency, amount) { | |
currency.values().inject([]){ list, coin -> | |
int count = amount / coin.value | |
amount = amount % coin.value | |
list += "$count ${plural(coin.toString(), count)}" | |
} | |
} | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
matchBrackets: true, | |
mode: "text/x-groovy" | |
}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-groovy</code></p> | |
</body> | |
</html> | |
CodeMirror.defineMode("haskell", function(cmCfg, modeCfg) { | |
function switchState(source, setState, f) { | |
setState(f); | |
return f(source, setState); | |
} | |
// These should all be Unicode extended, as per the Haskell 2010 report | |
var smallRE = /[a-z_]/; | |
var largeRE = /[A-Z]/; | |
var digitRE = /[0-9]/; | |
var hexitRE = /[0-9A-Fa-f]/; | |
var octitRE = /[0-7]/; | |
var idRE = /[a-z_A-Z0-9']/; | |
var symbolRE = /[-!#$%&*+.\/<=>?@\\^|~:]/; | |
var specialRE = /[(),;[\]`{}]/; | |
var whiteCharRE = /[ \t\v\f]/; // newlines are handled in tokenizer | |
function normal(source, setState) { | |
if (source.eatWhile(whiteCharRE)) { | |
return null; | |
} | |
var ch = source.next(); | |
if (specialRE.test(ch)) { | |
if (ch == '{' && source.eat('-')) { | |
var t = "comment"; | |
if (source.eat('#')) { | |
t = "meta"; | |
} | |
return switchState(source, setState, ncomment(t, 1)); | |
} | |
return null; | |
} | |
if (ch == '\'') { | |
if (source.eat('\\')) { | |
source.next(); // should handle other escapes here | |
} | |
else { | |
source.next(); | |
} | |
if (source.eat('\'')) { | |
return "string"; | |
} | |
return "error"; | |
} | |
if (ch == '"') { | |
return switchState(source, setState, stringLiteral); | |
} | |
if (largeRE.test(ch)) { | |
source.eatWhile(idRE); | |
if (source.eat('.')) { | |
return "qualifier"; | |
} | |
return "variable-2"; | |
} | |
if (smallRE.test(ch)) { | |
source.eatWhile(idRE); | |
return "variable"; | |
} | |
if (digitRE.test(ch)) { | |
if (ch == '0') { | |
if (source.eat(/[xX]/)) { | |
source.eatWhile(hexitRE); // should require at least 1 | |
return "integer"; | |
} | |
if (source.eat(/[oO]/)) { | |
source.eatWhile(octitRE); // should require at least 1 | |
return "number"; | |
} | |
} | |
source.eatWhile(digitRE); | |
var t = "number"; | |
if (source.eat('.')) { | |
t = "number"; | |
source.eatWhile(digitRE); // should require at least 1 | |
} | |
if (source.eat(/[eE]/)) { | |
t = "number"; | |
source.eat(/[-+]/); | |
source.eatWhile(digitRE); // should require at least 1 | |
} | |
return t; | |
} | |
if (symbolRE.test(ch)) { | |
if (ch == '-' && source.eat(/-/)) { | |
source.eatWhile(/-/); | |
if (!source.eat(symbolRE)) { | |
source.skipToEnd(); | |
return "comment"; | |
} | |
} | |
var t = "variable"; | |
if (ch == ':') { | |
t = "variable-2"; | |
} | |
source.eatWhile(symbolRE); | |
return t; | |
} | |
return "error"; | |
} | |
function ncomment(type, nest) { | |
if (nest == 0) { | |
return normal; | |
} | |
return function(source, setState) { | |
var currNest = nest; | |
while (!source.eol()) { | |
var ch = source.next(); | |
if (ch == '{' && source.eat('-')) { | |
++currNest; | |
} | |
else if (ch == '-' && source.eat('}')) { | |
--currNest; | |
if (currNest == 0) { | |
setState(normal); | |
return type; | |
} | |
} | |
} | |
setState(ncomment(type, currNest)); | |
return type; | |
} | |
} | |
function stringLiteral(source, setState) { | |
while (!source.eol()) { | |
var ch = source.next(); | |
if (ch == '"') { | |
setState(normal); | |
return "string"; | |
} | |
if (ch == '\\') { | |
if (source.eol() || source.eat(whiteCharRE)) { | |
setState(stringGap); | |
return "string"; | |
} | |
if (source.eat('&')) { | |
} | |
else { | |
source.next(); // should handle other escapes here | |
} | |
} | |
} | |
setState(normal); | |
return "error"; | |
} | |
function stringGap(source, setState) { | |
if (source.eat('\\')) { | |
return switchState(source, setState, stringLiteral); | |
} | |
source.next(); | |
setState(normal); | |
return "error"; | |
} | |
var wellKnownWords = (function() { | |
var wkw = {}; | |
function setType(t) { | |
return function () { | |
for (var i = 0; i < arguments.length; i++) | |
wkw[arguments[i]] = t; | |
} | |
} | |
setType("keyword")( | |
"case", "class", "data", "default", "deriving", "do", "else", "foreign", | |
"if", "import", "in", "infix", "infixl", "infixr", "instance", "let", | |
"module", "newtype", "of", "then", "type", "where", "_"); | |
setType("keyword")( | |
"\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>"); | |
setType("builtin")( | |
"!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<", | |
"==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**"); | |
setType("builtin")( | |
"Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq", | |
"False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT", | |
"IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left", | |
"Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read", | |
"ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS", | |
"String", "True"); | |
setType("builtin")( | |
"abs", "acos", "acosh", "all", "and", "any", "appendFile", "asTypeOf", | |
"asin", "asinh", "atan", "atan2", "atanh", "break", "catch", "ceiling", | |
"compare", "concat", "concatMap", "const", "cos", "cosh", "curry", | |
"cycle", "decodeFloat", "div", "divMod", "drop", "dropWhile", "either", | |
"elem", "encodeFloat", "enumFrom", "enumFromThen", "enumFromThenTo", | |
"enumFromTo", "error", "even", "exp", "exponent", "fail", "filter", | |
"flip", "floatDigits", "floatRadix", "floatRange", "floor", "fmap", | |
"foldl", "foldl1", "foldr", "foldr1", "fromEnum", "fromInteger", | |
"fromIntegral", "fromRational", "fst", "gcd", "getChar", "getContents", | |
"getLine", "head", "id", "init", "interact", "ioError", "isDenormalized", | |
"isIEEE", "isInfinite", "isNaN", "isNegativeZero", "iterate", "last", | |
"lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map", | |
"mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound", | |
"minimum", "mod", "negate", "not", "notElem", "null", "odd", "or", | |
"otherwise", "pi", "pred", "print", "product", "properFraction", | |
"putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile", | |
"readIO", "readList", "readLn", "readParen", "reads", "readsPrec", | |
"realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse", | |
"round", "scaleFloat", "scanl", "scanl1", "scanr", "scanr1", "seq", | |
"sequence", "sequence_", "show", "showChar", "showList", "showParen", | |
"showString", "shows", "showsPrec", "significand", "signum", "sin", | |
"sinh", "snd", "span", "splitAt", "sqrt", "subtract", "succ", "sum", | |
"tail", "take", "takeWhile", "tan", "tanh", "toEnum", "toInteger", | |
"toRational", "truncate", "uncurry", "undefined", "unlines", "until", | |
"unwords", "unzip", "unzip3", "userError", "words", "writeFile", "zip", | |
"zip3", "zipWith", "zipWith3"); | |
return wkw; | |
})(); | |
return { | |
startState: function () { return { f: normal }; }, | |
copyState: function (s) { return { f: s.f }; }, | |
token: function(stream, state) { | |
var t = state.f(stream, function(s) { state.f = s; }); | |
var w = stream.current(); | |
return (w in wellKnownWords) ? wellKnownWords[w] : t; | |
} | |
}; | |
}); | |
CodeMirror.defineMIME("text/x-haskell", "haskell"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Haskell mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="haskell.js"></script> | |
<link rel="stylesheet" href="../../theme/elegant.css"> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Haskell mode</h1> | |
<form><textarea id="code" name="code"> | |
module UniquePerms ( | |
uniquePerms | |
) | |
where | |
-- | Find all unique permutations of a list where there might be duplicates. | |
uniquePerms :: (Eq a) => [a] -> [[a]] | |
uniquePerms = permBag . makeBag | |
-- | An unordered collection where duplicate values are allowed, | |
-- but represented with a single value and a count. | |
type Bag a = [(a, Int)] | |
makeBag :: (Eq a) => [a] -> Bag a | |
makeBag [] = [] | |
makeBag (a:as) = mix a $ makeBag as | |
where | |
mix a [] = [(a,1)] | |
mix a (bn@(b,n):bs) | a == b = (b,n+1):bs | |
| otherwise = bn : mix a bs | |
permBag :: Bag a -> [[a]] | |
permBag [] = [[]] | |
permBag bs = concatMap (\(f,cs) -> map (f:) $ permBag cs) . oneOfEach $ bs | |
where | |
oneOfEach [] = [] | |
oneOfEach (an@(a,n):bs) = | |
let bs' = if n == 1 then bs else (a,n-1):bs | |
in (a,bs') : mapSnd (an:) (oneOfEach bs) | |
apSnd f (a,b) = (a, f b) | |
mapSnd = map . apSnd | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
matchBrackets: true, | |
theme: "elegant" | |
}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-haskell</code>.</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("htmlembedded", function(config, parserConfig) { | |
//config settings | |
var scriptStartRegex = parserConfig.scriptStartRegex || /^<%/i, | |
scriptEndRegex = parserConfig.scriptEndRegex || /^%>/i; | |
//inner modes | |
var scriptingMode, htmlMixedMode; | |
//tokenizer when in html mode | |
function htmlDispatch(stream, state) { | |
if (stream.match(scriptStartRegex, false)) { | |
state.token=scriptingDispatch; | |
return scriptingMode.token(stream, state.scriptState); | |
} | |
else | |
return htmlMixedMode.token(stream, state.htmlState); | |
} | |
//tokenizer when in scripting mode | |
function scriptingDispatch(stream, state) { | |
if (stream.match(scriptEndRegex, false)) { | |
state.token=htmlDispatch; | |
return htmlMixedMode.token(stream, state.htmlState); | |
} | |
else | |
return scriptingMode.token(stream, state.scriptState); | |
} | |
return { | |
startState: function() { | |
scriptingMode = scriptingMode || CodeMirror.getMode(config, parserConfig.scriptingModeSpec); | |
htmlMixedMode = htmlMixedMode || CodeMirror.getMode(config, "htmlmixed"); | |
return { | |
token : parserConfig.startOpen ? scriptingDispatch : htmlDispatch, | |
htmlState : htmlMixedMode.startState(), | |
scriptState : scriptingMode.startState() | |
} | |
}, | |
token: function(stream, state) { | |
return state.token(stream, state); | |
}, | |
indent: function(state, textAfter) { | |
if (state.token == htmlDispatch) | |
return htmlMixedMode.indent(state.htmlState, textAfter); | |
else | |
return scriptingMode.indent(state.scriptState, textAfter); | |
}, | |
copyState: function(state) { | |
return { | |
token : state.token, | |
htmlState : CodeMirror.copyState(htmlMixedMode, state.htmlState), | |
scriptState : CodeMirror.copyState(scriptingMode, state.scriptState) | |
} | |
}, | |
electricChars: "/{}:" | |
} | |
}); | |
CodeMirror.defineMIME("application/x-ejs", { name: "htmlembedded", scriptingModeSpec:"javascript"}); | |
CodeMirror.defineMIME("application/x-aspx", { name: "htmlembedded", scriptingModeSpec:"text/x-csharp"}); | |
CodeMirror.defineMIME("application/x-jsp", { name: "htmlembedded", scriptingModeSpec:"text/x-java"}); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Html Embedded Scripts mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="../xml/xml.js"></script> | |
<script src="../javascript/javascript.js"></script> | |
<script src="../css/css.js"></script> | |
<script src="../htmlmixed/htmlmixed.js"></script> | |
<script src="htmlembedded.js"></script> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Html Embedded Scripts mode</h1> | |
<form><textarea id="code" name="code"> | |
<% | |
function hello(who) { | |
return "Hello " + who; | |
} | |
%> | |
This is an example of EJS (embedded javascript) | |
<p>The program says <%= hello("world") %>.</p> | |
<script> | |
alert("And here is some normal JS code"); // also colored | |
</script> | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
matchBrackets: true, | |
mode: "application/x-ejs", | |
indentUnit: 4, | |
indentWithTabs: true, | |
enterMode: "keep", | |
tabMode: "shift" | |
}); | |
</script> | |
<p>Mode for html embedded scripts like JSP and ASP.NET. Depends on HtmlMixed which in turn depends on | |
JavaScript, CSS and XML.<br />Other dependancies include those of the scriping language chosen.</p> | |
<p><strong>MIME types defined:</strong> <code>application/x-aspx</code> (ASP.NET), | |
<code>application/x-ejs</code> (Embedded Javascript), <code>application/x-jsp</code> (JavaServer Pages)</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { | |
var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); | |
var jsMode = CodeMirror.getMode(config, "javascript"); | |
var cssMode = CodeMirror.getMode(config, "css"); | |
function html(stream, state) { | |
var style = htmlMode.token(stream, state.htmlState); | |
if (style == "tag" && stream.current() == ">" && state.htmlState.context) { | |
if (/^script$/i.test(state.htmlState.context.tagName)) { | |
state.token = javascript; | |
state.localState = jsMode.startState(htmlMode.indent(state.htmlState, "")); | |
state.mode = "javascript"; | |
} | |
else if (/^style$/i.test(state.htmlState.context.tagName)) { | |
state.token = css; | |
state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); | |
state.mode = "css"; | |
} | |
} | |
return style; | |
} | |
function maybeBackup(stream, pat, style) { | |
var cur = stream.current(); | |
var close = cur.search(pat); | |
if (close > -1) stream.backUp(cur.length - close); | |
return style; | |
} | |
function javascript(stream, state) { | |
if (stream.match(/^<\/\s*script\s*>/i, false)) { | |
state.token = html; | |
state.curState = null; | |
state.mode = "html"; | |
return html(stream, state); | |
} | |
return maybeBackup(stream, /<\/\s*script\s*>/, | |
jsMode.token(stream, state.localState)); | |
} | |
function css(stream, state) { | |
if (stream.match(/^<\/\s*style\s*>/i, false)) { | |
state.token = html; | |
state.localState = null; | |
state.mode = "html"; | |
return html(stream, state); | |
} | |
return maybeBackup(stream, /<\/\s*style\s*>/, | |
cssMode.token(stream, state.localState)); | |
} | |
return { | |
startState: function() { | |
var state = htmlMode.startState(); | |
return {token: html, localState: null, mode: "html", htmlState: state}; | |
}, | |
copyState: function(state) { | |
if (state.localState) | |
var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState); | |
return {token: state.token, localState: local, mode: state.mode, | |
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; | |
}, | |
token: function(stream, state) { | |
return state.token(stream, state); | |
}, | |
indent: function(state, textAfter) { | |
if (state.token == html || /^\s*<\//.test(textAfter)) | |
return htmlMode.indent(state.htmlState, textAfter); | |
else if (state.token == javascript) | |
return jsMode.indent(state.localState, textAfter); | |
else | |
return cssMode.indent(state.localState, textAfter); | |
}, | |
compareStates: function(a, b) { | |
return htmlMode.compareStates(a.htmlState, b.htmlState); | |
}, | |
electricChars: "/{}:" | |
} | |
}); | |
CodeMirror.defineMIME("text/html", "htmlmixed"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: HTML mixed mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="../xml/xml.js"></script> | |
<script src="../javascript/javascript.js"></script> | |
<script src="../css/css.js"></script> | |
<script src="htmlmixed.js"></script> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
</head> | |
<body> | |
<h1>CodeMirror: HTML mixed mode</h1> | |
<form><textarea id="code" name="code"> | |
<html style="color: green"> | |
<!-- this is a comment --> | |
<head> | |
<title>Mixed HTML Example</title> | |
<style type="text/css"> | |
h1 {font-family: comic sans; color: #f0f;} | |
div {background: yellow !important;} | |
body { | |
max-width: 50em; | |
margin: 1em 2em 1em 5em; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>Mixed HTML Example</h1> | |
<script> | |
function jsFunc(arg1, arg2) { | |
if (arg1 && arg2) document.body.innerHTML = "achoo"; | |
} | |
</script> | |
</body> | |
</html> | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {mode: "text/html", tabMode: "indent"}); | |
</script> | |
<p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p> | |
<p><strong>MIME types defined:</strong> <code>text/html</code> | |
(redefined, only takes effect if you load this parser after the | |
XML parser).</p> | |
</body> | |
</html> | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: JavaScript mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="javascript.js"></script> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
</head> | |
<body> | |
<h1>CodeMirror: JavaScript mode</h1> | |
<div><textarea id="code" name="code"> | |
// Demo code (the actual new parser character stream implementation) | |
function StringStream(string) { | |
this.pos = 0; | |
this.string = string; | |
} | |
StringStream.prototype = { | |
done: function() {return this.pos >= this.string.length;}, | |
peek: function() {return this.string.charAt(this.pos);}, | |
next: function() { | |
if (this.pos < this.string.length) | |
return this.string.charAt(this.pos++); | |
}, | |
eat: function(match) { | |
var ch = this.string.charAt(this.pos); | |
if (typeof match == "string") var ok = ch == match; | |
else var ok = ch && match.test ? match.test(ch) : match(ch); | |
if (ok) {this.pos++; return ch;} | |
}, | |
eatWhile: function(match) { | |
var start = this.pos; | |
while (this.eat(match)); | |
if (this.pos > start) return this.string.slice(start, this.pos); | |
}, | |
backUp: function(n) {this.pos -= n;}, | |
column: function() {return this.pos;}, | |
eatSpace: function() { | |
var start = this.pos; | |
while (/\s/.test(this.string.charAt(this.pos))) this.pos++; | |
return this.pos - start; | |
}, | |
match: function(pattern, consume, caseInsensitive) { | |
if (typeof pattern == "string") { | |
function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} | |
if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { | |
if (consume !== false) this.pos += str.length; | |
return true; | |
} | |
} | |
else { | |
var match = this.string.slice(this.pos).match(pattern); | |
if (match && consume !== false) this.pos += match[0].length; | |
return match; | |
} | |
} | |
}; | |
</textarea></div> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
lineNumbers: true, | |
matchBrackets: true | |
}); | |
</script> | |
<p>JavaScript mode supports a single configuration | |
option, <code>json</code>, which will set the mode to expect JSON | |
data rather than a JavaScript program.</p> | |
<p><strong>MIME types defined:</strong> <code>text/javascript</code>, <code>application/json</code>.</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("javascript", function(config, parserConfig) { | |
var indentUnit = config.indentUnit; | |
var jsonMode = parserConfig.json; | |
// 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"}; | |
return { | |
"if": A, "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 | |
}; | |
}(); | |
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)) { | |
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.reAllowed) { | |
nextUntilUnescaped(stream, "/"); | |
stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla | |
return ret("regexp", "string"); | |
} | |
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.kwAllowed) ? 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}; | |
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) { | |
var state = cx.state; | |
if (state.context) { | |
cx.marked = "def"; | |
for (var v = state.localVars; v; v = v.next) | |
if (v.name == varname) return; | |
state.localVars = {name: varname, next: state.localVars}; | |
} | |
} | |
// Combinators | |
var defaultVars = {name: "this", next: {name: "arguments"}}; | |
function pushcontext() { | |
if (!cx.state.context) cx.state.localVars = defaultVars; | |
cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; | |
} | |
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; | |
state.lexical = new JSLexical(state.indented, 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 expecting(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 == "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) { | |
if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator); | |
if (type == "function") return cont(functiondef); | |
if (type == "keyword c") return cont(maybeexpression); | |
if (type == "(") return cont(pushlex(")"), expression, expect(")"), poplex, maybeoperator); | |
if (type == "operator") return cont(expression); | |
if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator); | |
if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator); | |
return cont(); | |
} | |
function maybeexpression(type) { | |
if (type.match(/[;\}\)\],]/)) return pass(); | |
return pass(expression); | |
} | |
function maybeoperator(type, value) { | |
if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator); | |
if (type == "operator") return cont(expression); | |
if (type == ";") return; | |
if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator); | |
if (type == ".") return cont(property, maybeoperator); | |
if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator); | |
} | |
function maybelabel(type) { | |
if (type == ":") return cont(poplex, statement); | |
return pass(maybeoperator, expect(";"), poplex); | |
} | |
function property(type) { | |
if (type == "variable") {cx.marked = "property"; return cont();} | |
} | |
function objprop(type) { | |
if (type == "variable") cx.marked = "property"; | |
if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression); | |
} | |
function commasep(what, end) { | |
function proceed(type) { | |
if (type == ",") return cont(what, proceed); | |
if (type == end) return cont(); | |
return cont(expect(end)); | |
} | |
return function commaSeparated(type) { | |
if (type == end) return cont(); | |
else return pass(what, proceed); | |
}; | |
} | |
function block(type) { | |
if (type == "}") return cont(); | |
return pass(statement, block); | |
} | |
function vardef1(type, value) { | |
if (type == "variable"){register(value); return cont(vardef2);} | |
return cont(); | |
} | |
function vardef2(type, value) { | |
if (value == "=") return cont(expression, vardef2); | |
if (type == ",") return cont(vardef1); | |
} | |
function forspec1(type) { | |
if (type == "var") return cont(vardef1, forspec2); | |
if (type == ";") return pass(forspec2); | |
if (type == "variable") return cont(formaybein); | |
return pass(forspec2); | |
} | |
function formaybein(type, value) { | |
if (value == "in") return cont(expression); | |
return cont(maybeoperator, forspec2); | |
} | |
function forspec2(type, value) { | |
if (type == ";") return cont(forspec3); | |
if (value == "in") return cont(expression); | |
return cont(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 cont();} | |
} | |
// Interface | |
return { | |
startState: function(basecolumn) { | |
return { | |
tokenize: jsTokenBase, | |
reAllowed: true, | |
kwAllowed: true, | |
cc: [], | |
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), | |
localVars: null, | |
context: null, | |
indented: 0 | |
}; | |
}, | |
token: function(stream, state) { | |
if (stream.sol()) { | |
if (!state.lexical.hasOwnProperty("align")) | |
state.lexical.align = false; | |
state.indented = stream.indentation(); | |
} | |
if (stream.eatSpace()) return null; | |
var style = state.tokenize(stream, state); | |
if (type == "comment") return style; | |
state.reAllowed = type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/); | |
state.kwAllowed = type != '.'; | |
return parseJS(state, style, type, content, stream); | |
}, | |
indent: function(state, textAfter) { | |
if (state.tokenize != jsTokenBase) return 0; | |
var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, | |
type = lexical.type, closing = firstChar == type; | |
if (type == "vardef") return lexical.indented + 4; | |
else if (type == "form" && firstChar == "{") return lexical.indented; | |
else if (type == "stat" || type == "form") return lexical.indented + indentUnit; | |
else if (lexical.info == "switch" && !closing) | |
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: ":{}" | |
}; | |
}); | |
CodeMirror.defineMIME("text/javascript", "javascript"); | |
CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Jinja2 mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="jinja2.js"></script> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Jinja2 mode</h1> | |
<form><textarea id="code" name="code"> | |
<html style="color: green"> | |
<!-- this is a comment --> | |
<head> | |
<title>Jinja2 Example</title> | |
</head> | |
<body> | |
<ul> | |
{# this is a comment #} | |
{%- for item in li -%} | |
<li> | |
{{ item.label }} | |
</li> | |
{% endfor -%} | |
</ul> | |
</body> | |
</html> | |
</textarea></form> | |
<script> | |
var editor = | |
CodeMirror.fromTextArea(document.getElementById("code"), {mode: | |
{name: "jinja2", htmlMode: true}}); | |
</script> | |
</body> | |
</html> | |
CodeMirror.defineMode("jinja2", function(config, parserConf) { | |
var keywords = ["block", "endblock", "for", "endfor", "in", "true", "false", | |
"loop", "none", "self", "super", "if", "as", "not", "and", | |
"else", "import", "with", "without", "context"]; | |
keywords = new RegExp("^((" + keywords.join(")|(") + "))\\b"); | |
function tokenBase (stream, state) { | |
var ch = stream.next(); | |
if (ch == "{") { | |
if (ch = stream.eat(/\{|%|#/)) { | |
stream.eat("-"); | |
state.tokenize = inTag(ch); | |
return "tag"; | |
} | |
} | |
} | |
function inTag (close) { | |
if (close == "{") { | |
close = "}"; | |
} | |
return function (stream, state) { | |
var ch = stream.next(); | |
if ((ch == close || (ch == "-" && stream.eat(close))) | |
&& stream.eat("}")) { | |
state.tokenize = tokenBase; | |
return "tag"; | |
} | |
if (stream.match(keywords)) { | |
return "keyword"; | |
} | |
return close == "#" ? "comment" : "string"; | |
}; | |
} | |
return { | |
startState: function () { | |
return {tokenize: tokenBase}; | |
}, | |
token: function (stream, state) { | |
return state.tokenize(stream, state); | |
} | |
}; | |
}); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Lua mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="lua.js"></script> | |
<link rel="stylesheet" href="../../theme/neat.css"> | |
<style>.CodeMirror {border: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Lua mode</h1> | |
<form><textarea id="code" name="code"> | |
--[[ | |
example useless code to show lua syntax highlighting | |
this is multiline comment | |
]] | |
function blahblahblah(x) | |
local table = { | |
"asd" = 123, | |
"x" = 0.34, | |
} | |
if x ~= 3 then | |
print( x ) | |
elseif x == "string" | |
my_custom_function( 0x34 ) | |
else | |
unknown_function( "some string" ) | |
end | |
--single line comment | |
end | |
function blablabla3() | |
for k,v in ipairs( table ) do | |
--abcde.. | |
y=[=[ | |
x=[[ | |
x is a multi line string | |
]] | |
but its definition is iside a highest level string! | |
]=] | |
print(" \"\" ") | |
s = math.sin( x ) | |
end | |
end | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
tabMode: "indent", | |
matchBrackets: true, | |
theme: "neat" | |
}); | |
</script> | |
<p>Loosely based on Franciszek | |
Wawrzak's <a href="http://codemirror.net/1/contrib/lua">CodeMirror | |
1 mode</a>. One configuration parameter is | |
supported, <code>specials</code>, to which you can provide an | |
array of strings to have those identifiers highlighted with | |
the <code>lua-special</code> style.</p> | |
<p><strong>MIME types defined:</strong> <code>text/x-lua</code>.</p> | |
</body> | |
</html> | |
// LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's | |
// CodeMirror 1 mode. | |
// highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting | |
CodeMirror.defineMode("lua", function(config, parserConfig) { | |
var indentUnit = config.indentUnit; | |
function prefixRE(words) { | |
return new RegExp("^(?:" + words.join("|") + ")", "i"); | |
} | |
function wordRE(words) { | |
return new RegExp("^(?:" + words.join("|") + ")$", "i"); | |
} | |
var specials = wordRE(parserConfig.specials || []); | |
// long list of standard functions from lua manual | |
var builtins = wordRE([ | |
"_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load", | |
"loadfile","loadstring","module","next","pairs","pcall","print","rawequal","rawget","rawset","require", | |
"select","setfenv","setmetatable","tonumber","tostring","type","unpack","xpcall", | |
"coroutine.create","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield", | |
"debug.debug","debug.getfenv","debug.gethook","debug.getinfo","debug.getlocal","debug.getmetatable", | |
"debug.getregistry","debug.getupvalue","debug.setfenv","debug.sethook","debug.setlocal","debug.setmetatable", | |
"debug.setupvalue","debug.traceback", | |
"close","flush","lines","read","seek","setvbuf","write", | |
"io.close","io.flush","io.input","io.lines","io.open","io.output","io.popen","io.read","io.stderr","io.stdin", | |
"io.stdout","io.tmpfile","io.type","io.write", | |
"math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.cos","math.cosh","math.deg", | |
"math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10","math.max", | |
"math.min","math.modf","math.pi","math.pow","math.rad","math.random","math.randomseed","math.sin","math.sinh", | |
"math.sqrt","math.tan","math.tanh", | |
"os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale", | |
"os.time","os.tmpname", | |
"package.cpath","package.loaded","package.loaders","package.loadlib","package.path","package.preload", | |
"package.seeall", | |
"string.byte","string.char","string.dump","string.find","string.format","string.gmatch","string.gsub", | |
"string.len","string.lower","string.match","string.rep","string.reverse","string.sub","string.upper", | |
"table.concat","table.insert","table.maxn","table.remove","table.sort" | |
]); | |
var keywords = wordRE(["and","break","elseif","false","nil","not","or","return", | |
"true","function", "end", "if", "then", "else", "do", | |
"while", "repeat", "until", "for", "in", "local" ]); | |
var indentTokens = wordRE(["function", "if","repeat","do", "\\(", "{"]); | |
var dedentTokens = wordRE(["end", "until", "\\)", "}"]); | |
var dedentPartial = prefixRE(["end", "until", "\\)", "}", "else", "elseif"]); | |
function readBracket(stream) { | |
var level = 0; | |
while (stream.eat("=")) ++level; | |
stream.eat("["); | |
return level; | |
} | |
function normal(stream, state) { | |
var ch = stream.next(); | |
if (ch == "-" && stream.eat("-")) { | |
if (stream.eat("[")) | |
return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state); | |
stream.skipToEnd(); | |
return "comment"; | |
} | |
if (ch == "\"" || ch == "'") | |
return (state.cur = string(ch))(stream, state); | |
if (ch == "[" && /[\[=]/.test(stream.peek())) | |
return (state.cur = bracketed(readBracket(stream), "string"))(stream, state); | |
if (/\d/.test(ch)) { | |
stream.eatWhile(/[\w.%]/); | |
return "number"; | |
} | |
if (/[\w_]/.test(ch)) { | |
stream.eatWhile(/[\w\\\-_.]/); | |
return "variable"; | |
} | |
return null; | |
} | |
function bracketed(level, style) { | |
return function(stream, state) { | |
var curlev = null, ch; | |
while ((ch = stream.next()) != null) { | |
if (curlev == null) {if (ch == "]") curlev = 0;} | |
else if (ch == "=") ++curlev; | |
else if (ch == "]" && curlev == level) { state.cur = normal; break; } | |
else curlev = null; | |
} | |
return style; | |
}; | |
} | |
function string(quote) { | |
return function(stream, state) { | |
var escaped = false, ch; | |
while ((ch = stream.next()) != null) { | |
if (ch == quote && !escaped) break; | |
escaped = !escaped && ch == "\\"; | |
} | |
if (!escaped) state.cur = normal; | |
return "string"; | |
}; | |
} | |
return { | |
startState: function(basecol) { | |
return {basecol: basecol || 0, indentDepth: 0, cur: normal}; | |
}, | |
token: function(stream, state) { | |
if (stream.eatSpace()) return null; | |
var style = state.cur(stream, state); | |
var word = stream.current(); | |
if (style == "variable") { | |
if (keywords.test(word)) style = "keyword"; | |
else if (builtins.test(word)) style = "builtin"; | |
else if (specials.test(word)) style = "variable-2"; | |
} | |
if ((style != "comment") && (style != "string")){ | |
if (indentTokens.test(word)) ++state.indentDepth; | |
else if (dedentTokens.test(word)) --state.indentDepth; | |
} | |
return style; | |
}, | |
indent: function(state, textAfter) { | |
var closing = dedentPartial.test(textAfter); | |
return state.basecol + indentUnit * (state.indentDepth - (closing ? 1 : 0)); | |
} | |
}; | |
}); | |
CodeMirror.defineMIME("text/x-lua", "lua"); | |
<!doctype html> | |
<html> | |
<head> | |
<title>CodeMirror: Markdown mode</title> | |
<link rel="stylesheet" href="../../lib/codemirror.css"> | |
<script src="../../lib/codemirror.js"></script> | |
<script src="../xml/xml.js"></script> | |
<script src="markdown.js"></script> | |
<link rel="stylesheet" href="markdown.css"> | |
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style> | |
<link rel="stylesheet" href="../../doc/docs.css"> | |
</head> | |
<body> | |
<h1>CodeMirror: Markdown mode</h1> | |
<!-- source: http://daringfireball.net/projects/markdown/basics.text --> | |
<form><textarea id="code" name="code"> | |
Markdown: Basics | |
================ | |
<ul id="ProjectSubmenu"> | |
<li><a href="/projects/markdown/" title="Markdown Project Page">Main</a></li> | |
<li><a class="selected" title="Markdown Basics">Basics</a></li> | |
<li><a href="/projects/markdown/syntax" title="Markdown Syntax Documentation">Syntax</a></li> | |
<li><a href="/projects/markdown/license" title="Pricing and License Information">License</a></li> | |
<li><a href="/projects/markdown/dingus" title="Online Markdown Web Form">Dingus</a></li> | |
</ul> | |
Getting the Gist of Markdown's Formatting Syntax | |
------------------------------------------------ | |
This page offers a brief overview of what it's like to use Markdown. | |
The [syntax page] [s] provides complete, detailed documentation for | |
every feature, but Markdown should be very easy to pick up simply by | |
looking at a few examples of it in action. The examples on this page | |
are written in a before/after style, showing example syntax and the | |
HTML output produced by Markdown. | |
It's also helpful to simply try Markdown out; the [Dingus] [d] is a | |
web application that allows you type your own Markdown-formatted text | |
and translate it to XHTML. | |
**Note:** This document is itself written using Markdown; you | |
can [see the source for it by adding '.text' to the URL] [src]. | |
[s]: /projects/markdown/syntax "Markdown Syntax" | |
[d]: /projects/markdown/dingus "Markdown Dingus" | |
[src]: /projects/markdown/basics.text | |
## Paragraphs, Headers, Blockquotes ## | |
A paragraph is simply one or more consecutive lines of text, separated | |
by one or more blank lines. (A blank line is any line that looks like | |
a blank line -- a line containing nothing but spaces or tabs is | |
considered blank.) Normal paragraphs should not be indented with | |
spaces or tabs. | |
Markdown offers two styles of headers: *Setext* and *atx*. | |
Setext-style headers for `<h1>` and `<h2>` are created by | |
"underlining" with equal signs (`=`) and hyphens (`-`), respectively. | |
To create an atx-style header, you put 1-6 hash marks (`#`) at the | |
beginning of the line -- the number of hashes equals the resulting | |
HTML header level. | |
Blockquotes are indicated using email-style '`>`' angle brackets. | |
Markdown: | |
A First Level Header | |
==================== | |
A Second Level Header | |
--------------------- | |
Now is the time for all good men to come to | |
the aid of their country. This is just a | |
regular paragraph. | |
The quick brown fox jumped over the lazy | |
dog's back. | |
### Header 3 | |
> This is a blockquote. | |
> | |
> This is the second paragraph in the blockquote. | |
> | |
> ## This is an H2 in a blockquote | |
Output: | |
<h1>A First Level Header</h1> | |
<h2>A Second Level Header</h2> | |
<p>Now is the time for all good men to come to | |
the aid of their country. This is just a | |
regular paragraph.</p> | |
<p>The quick brown fox jumped over the lazy | |
dog's back.</p> | |
<h3>Header 3</h3> | |
<blockquote> | |
<p>This is a blockquote.</p> | |
<p>This is the second paragraph in the blockquote.</p> | |
<h2>This is an H2 in a blockquote</h2> | |
</blockquote> | |
### Phrase Emphasis ### | |
Markdown uses asterisks and underscores to indicate spans of emphasis. | |
Markdown: | |
Some of these words *are emphasized*. | |
Some of these words _are emphasized also_. | |
Use two asterisks for **strong emphasis**. | |
Or, if you prefer, __use two underscores instead__. | |
Output: | |
<p>Some of these words <em>are emphasized</em>. | |
Some of these words <em>are emphasized also</em>.</p> | |
<p>Use two asterisks for <strong>strong emphasis</strong>. | |
Or, if you prefer, <strong>use two underscores instead</strong>.</p> | |
## Lists ## | |
Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`, | |
`+`, and `-`) as list markers. These three markers are | |
interchangable; this: | |
* Candy. | |
* Gum. | |
* Booze. | |
this: | |
+ Candy. | |
+ Gum. | |
+ Booze. | |
and this: | |
- Candy. | |
- Gum. | |
- Booze. | |
all produce the same output: | |
<ul> | |
<li>Candy.</li> | |
<li>Gum.</li> | |
<li>Booze.</li> | |
</ul> | |
Ordered (numbered) lists use regular numbers, followed by periods, as | |
list markers: | |
1. Red | |
2. Green | |
3. Blue | |
Output: | |
<ol> | |
<li>Red</li> | |
<li>Green</li> | |
<li>Blue</li> | |
</ol> | |
If you put blank lines between items, you'll get `<p>` tags for the | |
list item text. You can create multi-paragraph list items by indenting | |
the paragraphs by 4 spaces or 1 tab: | |
* A list item. | |
With multiple paragraphs. | |
* Another item in the list. | |
Output: | |
<ul> | |
<li><p>A list item.</p> | |
<p>With multiple paragraphs.</p></li> | |
<li><p>Another item in the list.</p></li> | |
</ul> | |
### Links ### | |
Markdown supports two styles for creating links: *inline* and | |
*reference*. With both styles, you use square brackets to delimit the | |
text you want to turn into a link. | |
Inline-style links use parentheses immediately after the link text. | |
For example: | |
This is an [example link](http://example.com/). | |
Output: | |
<p>This is an <a href="http://example.com/"> | |
example link</a>.</p> | |
Optionally, you may include a title attribute in the parentheses: | |
This is an [example link](http://example.com/ "With a Title"). | |
Output: | |
<p>This is an <a href="http://example.com/" title="With a Title"> | |
example link</a>.</p> | |
Reference-style links allow you to refer to your links by names, which | |
you define elsewhere in your document: | |
I get 10 times more traffic from [Google][1] than from | |
[Yahoo][2] or [MSN][3]. | |
[1]: http://google.com/ "Google" | |
[2]: http://search.yahoo.com/ "Yahoo Search" | |
[3]: http://search.msn.com/ "MSN Search" | |
Output: | |
<p>I get 10 times more traffic from <a href="http://google.com/" | |
title="Google">Google</a> than from <a href="http://search.yahoo.com/" | |
title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/" | |
title="MSN Search">MSN</a>.</p> | |
The title attribute is optional. Link names may contain letters, | |
numbers and spaces, but are *not* case sensitive: | |
I start my morning with a cup of coffee and | |
[The New York Times][NY Times]. | |
[ny times]: http://www.nytimes.com/ | |
Output: | |
<p>I start my morning with a cup of coffee and | |
<a href="http://www.nytimes.com/">The New York Times</a>.</p> | |
### Images ### | |
Image syntax is very much like link syntax. | |
Inline (titles are optional): | |
![alt text](/path/to/img.jpg "Title") | |
Reference-style: | |
![alt text][id] | |
[id]: /path/to/img.jpg "Title" | |
Both of the above examples produce the same output: | |
<img src="/path/to/img.jpg" alt="alt text" title="Title" /> | |
### Code ### | |
In a regular paragraph, you can create code span by wrapping text in | |
backtick quotes. Any ampersands (`&`) and angle brackets (`<` or | |
`>`) will automatically be translated into HTML entities. This makes | |
it easy to use Markdown to write about HTML example code: | |
I strongly recommend against using any `<blink>` tags. | |
I wish SmartyPants used named entities like `&mdash;` | |
instead of decimal-encoded entites like `&#8212;`. | |
Output: | |
<p>I strongly recommend against using any | |
<code>&lt;blink&gt;</code> tags.</p> | |
<p>I wish SmartyPants used named entities like | |
<code>&amp;mdash;</code> instead of decimal-encoded | |
entites like <code>&amp;#8212;</code>.</p> | |
To specify an entire block of pre-formatted code, indent every line of | |
the block by 4 spaces or 1 tab. Just like with code spans, `&`, `<`, | |
and `>` characters will be escaped automatically. | |
Markdown: | |
If you want your page to validate under XHTML 1.0 Strict, | |
you've got to put paragraph tags in your blockquotes: | |
<blockquote> | |
<p>For example.</p> | |
</blockquote> | |
Output: | |
<p>If you want your page to validate under XHTML 1.0 Strict, | |
you've got to put paragraph tags in your blockquotes:</p> | |
<pre><code>&lt;blockquote&gt; | |
&lt;p&gt;For example.&lt;/p&gt; | |
&lt;/blockquote&gt; | |
</code></pre> | |
</textarea></form> | |
<script> | |
var editor = CodeMirror.fromTextArea(document.getElementById("code"), { | |
mode: 'markdown', | |
lineNumbers: true, | |
matchBrackets: true, | |
theme: "default" | |
}); | |
</script> | |
<p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p> | |
</body> | |
</html> | |
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { | |
var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); | |
var header = 'header' | |
, code = 'comment' | |
, quote = 'quote' | |
, list = 'string' | |
, hr = 'hr' | |
, linktext = 'link' | |
, linkhref = 'string' | |
, em = 'em' | |
, strong = 'strong' | |
, emstrong = 'emstrong'; | |
var hrRE = /^[*-=_]/ | |
, ulRE = /^[*-+]\s+/ | |
, olRE = /^[0-9]\.\s+/ | |
, headerRE = /^(?:\={3,}|-{3,})$/ | |
, codeRE = /^(k:\t|\s{4,})/ | |
, textRE = /^[^\[*_\\<>`]+/; | |
function switchInline(stream, state, f) { | |
state.f = state.inline = f; | |
return f(stream, state); | |
} | |
function switchBlock(stream, state, f) { | |
state.f = state.block = f; | |
return f(stream, state); | |
} | |
// Blocks | |
function blockNormal(stream, state) { | |
if (stream.match(codeRE)) { | |
stream.skipToEnd(); | |
return code; | |
} | |
if (stream.eatSpace()) { | |
return null; | |
} | |
if (stream.peek() === '#' || stream.match(headerRE)) { | |
stream.skipToEnd(); | |
return header; | |
} | |
if (stream.eat('>')) { | |
state.indentation++; | |
return quote; | |
} | |
if (stream.peek() === '[') { | |
return switchInline(stream, state, footnoteLink); | |
} | |
if (hrRE.test(stream.peek())) { | |
var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$'); | |
if (stream.match(re, true)) { | |
return hr; | |
} | |
} | |
var match; | |
if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { | |
state.indentation += match[0].length; | |
return list; | |
} | |
return switchInline(stream, state, state.inline); | |
} | |
function htmlBlock(stream, state) { | |
var style = htmlMode.token(stream, state.htmlState); | |
if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { | |
state.f = inlineNormal; | |
state.block = blockNormal; | |
} | |
return style; | |
} | |
// Inline | |
function getType(state) { | |
return state.strong ? (state.em ? emstrong : strong) | |
: (state.em ? em : null); | |
} | |
function handleText(stream, state) { | |
if (stream.match(textRE, true)) { | |
return getType(state); | |
} | |
return undefined; | |
} | |
function inlineNormal(stream, state) { | |
var style = state.text(stream, state) | |
if (typeof style !== 'undefined') | |
return style; | |
var ch = stream.next(); | |
if (ch === '\\') { | |
stream.next(); | |
return getType(state); | |
} | |
if (ch === '`') { | |
return switchInline(stream, state, inlineElement(code, '`')); | |
} | |
if (ch === '[') { | |
return switchInline(stream, state, linkText); | |
} | |
if (ch === '<' && stream.match(/^\w/, false)) { | |
stream.backUp(1); | |
return switchBlock(stream, state, htmlBlock); | |
} | |
var t = getType(state); | |
if (ch === '*' || ch === '_') { | |
if (stream.eat(ch)) { | |
return (state.strong = !state.strong) ? getType(state) : t; | |
} | |
return (state.em = !state.em) ? getType(state) : t; | |
} | |
return getType(state); | |
} | |
function linkText(stream, state) { | |
while (!stream.eol()) { | |
var ch = stream.next(); |