├── screenshot-1.png ├── screenshot-2.png ├── includes ├── css │ └── motif.css ├── js │ ├── wds-customizer-css.js │ └── motif.js ├── custom-control.php └── libs │ └── codemirror │ ├── LICENSE │ ├── lib │ ├── codemirror.css │ └── codemirror.js │ ├── mode │ └── css.js │ └── motif-codemirror.js ├── composer.json ├── README.md └── wds-customizer-css.php /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/WDS-Customizer-CSS/HEAD/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebDevStudios/WDS-Customizer-CSS/HEAD/screenshot-2.png -------------------------------------------------------------------------------- /includes/css/motif.css: -------------------------------------------------------------------------------- 1 | #wds-customizer-css .customize-section-content { 2 | padding: 0; 3 | } 4 | 5 | #wds-customizer-css .customize-control { 6 | margin-bottom: 0; 7 | } 8 | -------------------------------------------------------------------------------- /includes/js/wds-customizer-css.js: -------------------------------------------------------------------------------- 1 | ( function( $ ) { 2 | wp.customize( 'wds_custom_css', function( value ) { 3 | value.bind( function( $new_styles ) { 4 | $('#wds-customizer-css').html( $new_styles ); 5 | } ); 6 | } ); 7 | 8 | } )( jQuery ); 9 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webdevstudios/wds-customizer-css", 3 | "type": "wordpress-plugin", 4 | "license": "GPL-2.0+", 5 | "description": "Easily add custom styles to your WordPress site", 6 | "homepage": "https://github.com/WebDevStudios/WDS-Customizer-CSS", 7 | "authors": [ 8 | { 9 | "name": "WebDevStudios", 10 | "email": "contact@webdevstudios.com", 11 | "homepage": "http://webdevstudios.com/" 12 | } 13 | ], 14 | "keywords": [ 15 | "wordpress" 16 | ], 17 | "support": { 18 | "issues": "https://github.com/WebDevStudios/WDS-Customizer-CSS/issues" 19 | }, 20 | "require": { 21 | "php": ">=5.3.0", 22 | "composer/installers": "~1.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /includes/custom-control.php: -------------------------------------------------------------------------------- 1 | 11 | 12 | 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | Please note that some subdirectories of the CodeMirror distribution 22 | include their own LICENSE files, and are released under different 23 | licences. 24 | -------------------------------------------------------------------------------- /includes/js/motif.js: -------------------------------------------------------------------------------- 1 | (function( $ ) { 2 | var isRtl = 'rtl' === document.body.parentNode.dir, 3 | MotifControl, sidebar; 4 | 5 | // SIDEBAR 6 | sidebar = function( width ) { 7 | var margin = 'margin' + ( isRtl ? 'Right' : 'Left' ); 8 | 9 | $('.wp-full-overlay').css( margin, width ); 10 | $('.wp-full-overlay-sidebar').css( margin, -1 * width ).width( width ); 11 | }; 12 | 13 | MotifControl = wp.customize.Control.extend({ 14 | ready: function() { 15 | var control = this, 16 | setting = this.setting, 17 | textarea = this.container.find( 'textarea' )[0], 18 | editor; 19 | 20 | // When the setting changes... 21 | setting.bind( function( to ) { 22 | // ...and the editor doesn't already have the CSS 23 | // update the editor's value. 24 | if ( to !== editor.getValue() ) 25 | editor.setValue( to ); 26 | }); 27 | 28 | // Set our codemirror options. 29 | this.codemirror = $.extend( true, {}, this.codemirror, { 30 | // Get the current value to send to codemirror 31 | value: setting.get(), 32 | // When the editor finds a change... 33 | onChange: function() { 34 | // ...update the setting to that value. 35 | setting.set( editor.getValue() ); 36 | } 37 | } ); 38 | 39 | // Create the editor from the textarea. 40 | this.editor = editor = CodeMirror.fromTextArea( textarea, this.codemirror ); 41 | }, 42 | 43 | // focus: function() { 44 | // this.editor.focus(); 45 | // this.editor.setCursor( this.editor.lineCount() ); 46 | // }, 47 | 48 | codemirror: { 49 | mode: 'text/css', 50 | lineNumbers: true, 51 | lineWrapping: true, 52 | matchBrackets: true, 53 | indentUnit: 4, 54 | indentWithTabs: true, 55 | enterMode: 'keep' 56 | } 57 | }); 58 | 59 | // Controls with the type 'motif' should made into 60 | // MotifControls 61 | wp.customize.controlConstructor.wds_custom_css = MotifControl; 62 | 63 | // SIDEBAR 64 | wp.customize.bind( 'ready', function() { 65 | var section = $('#accordion-section-wds_custom_css_section'), 66 | originalSidebarWidth = $('.wp-full-overlay-sidebar').width(); 67 | 68 | $('.customize-section-title').click( function() { 69 | sidebar( section.hasClass('open') ? 400 : originalSidebarWidth ); 70 | }); 71 | }); 72 | }( jQuery )); 73 | -------------------------------------------------------------------------------- /includes/libs/codemirror/lib/codemirror.css: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | line-height: 1em; 3 | font-family: monospace; 4 | } 5 | 6 | .CodeMirror-scroll { 7 | overflow: auto; 8 | height: 300px; 9 | /* This is needed to prevent an IE[67] bug where the scrolled content 10 | is visible outside of the scrolling box. */ 11 | position: relative; 12 | outline: none; 13 | } 14 | 15 | .CodeMirror-gutter { 16 | position: absolute; left: 0; top: 0; 17 | z-index: 10; 18 | background-color: #f7f7f7; 19 | border-right: 1px solid #eee; 20 | min-width: 2em; 21 | height: 100%; 22 | } 23 | .CodeMirror-gutter-text { 24 | color: #aaa; 25 | text-align: right; 26 | padding: .4em .2em .4em .4em; 27 | white-space: pre !important; 28 | } 29 | .CodeMirror-lines { 30 | padding: .4em; 31 | white-space: pre; 32 | } 33 | 34 | .CodeMirror pre { 35 | -moz-border-radius: 0; 36 | -webkit-border-radius: 0; 37 | -o-border-radius: 0; 38 | border-radius: 0; 39 | border-width: 0; margin: 0; padding: 0; background: transparent; 40 | font-family: inherit; 41 | font-size: inherit; 42 | padding: 0; margin: 0; 43 | white-space: pre; 44 | word-wrap: normal; 45 | line-height: inherit; 46 | color: inherit; 47 | } 48 | 49 | .CodeMirror-wrap pre { 50 | word-wrap: break-word; 51 | white-space: pre-wrap; 52 | word-break: normal; 53 | } 54 | .CodeMirror-wrap .CodeMirror-scroll { 55 | overflow-x: hidden; 56 | } 57 | 58 | .CodeMirror textarea { 59 | outline: none !important; 60 | } 61 | 62 | .CodeMirror pre.CodeMirror-cursor { 63 | z-index: 10; 64 | position: absolute; 65 | visibility: hidden; 66 | border-left: 1px solid black; 67 | border-right: none; 68 | width: 0; 69 | } 70 | .cm-keymap-fat-cursor pre.CodeMirror-cursor { 71 | width: auto; 72 | border: 0; 73 | background: transparent; 74 | background: rgba(0, 200, 0, .4); 75 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); 76 | } 77 | /* Kludge to turn off filter in ie9+, which also accepts rgba */ 78 | .cm-keymap-fat-cursor pre.CodeMirror-cursor:not(#nonsense_id) { 79 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 80 | } 81 | .CodeMirror pre.CodeMirror-cursor.CodeMirror-overwrite {} 82 | .CodeMirror-focused pre.CodeMirror-cursor { 83 | visibility: visible; 84 | } 85 | 86 | div.CodeMirror-selected { background: #d9d9d9; } 87 | .CodeMirror-focused div.CodeMirror-selected { background: #d7d4f0; } 88 | 89 | .CodeMirror-searching { 90 | background: #ffa; 91 | background: rgba(255, 255, 0, .4); 92 | } 93 | 94 | /* Default theme */ 95 | 96 | .cm-s-default span.cm-keyword {color: #708;} 97 | .cm-s-default span.cm-atom {color: #219;} 98 | .cm-s-default span.cm-number {color: #164;} 99 | .cm-s-default span.cm-def {color: #00f;} 100 | .cm-s-default span.cm-variable {color: black;} 101 | .cm-s-default span.cm-variable-2 {color: #05a;} 102 | .cm-s-default span.cm-variable-3 {color: #085;} 103 | .cm-s-default span.cm-property {color: black;} 104 | .cm-s-default span.cm-operator {color: black;} 105 | .cm-s-default span.cm-comment {color: #a50;} 106 | .cm-s-default span.cm-string {color: #a11;} 107 | .cm-s-default span.cm-string-2 {color: #f50;} 108 | .cm-s-default span.cm-meta {color: #555;} 109 | .cm-s-default span.cm-error {color: #f00;} 110 | .cm-s-default span.cm-qualifier {color: #555;} 111 | .cm-s-default span.cm-builtin {color: #30a;} 112 | .cm-s-default span.cm-bracket {color: #cc7;} 113 | .cm-s-default span.cm-tag {color: #170;} 114 | .cm-s-default span.cm-attribute {color: #00c;} 115 | .cm-s-default span.cm-header {color: blue;} 116 | .cm-s-default span.cm-quote {color: #090;} 117 | .cm-s-default span.cm-hr {color: #999;} 118 | .cm-s-default span.cm-link {color: #00c;} 119 | 120 | span.cm-header, span.cm-strong {font-weight: bold;} 121 | span.cm-em {font-style: italic;} 122 | span.cm-emstrong {font-style: italic; font-weight: bold;} 123 | span.cm-link {text-decoration: underline;} 124 | 125 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 126 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 127 | -------------------------------------------------------------------------------- /includes/libs/codemirror/mode/css.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("css", function(config) { 2 | var indentUnit = config.indentUnit, type; 3 | function ret(style, tp) {type = tp; return style;} 4 | 5 | function tokenBase(stream, state) { 6 | var ch = stream.next(); 7 | if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());} 8 | else if (ch == "/" && stream.eat("*")) { 9 | state.tokenize = tokenCComment; 10 | return tokenCComment(stream, state); 11 | } 12 | else if (ch == "<" && stream.eat("!")) { 13 | state.tokenize = tokenSGMLComment; 14 | return tokenSGMLComment(stream, state); 15 | } 16 | else if (ch == "=") ret(null, "compare"); 17 | else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); 18 | else if (ch == "\"" || ch == "'") { 19 | state.tokenize = tokenString(ch); 20 | return state.tokenize(stream, state); 21 | } 22 | else if (ch == "#") { 23 | stream.eatWhile(/[\w\\\-]/); 24 | return ret("atom", "hash"); 25 | } 26 | else if (ch == "!") { 27 | stream.match(/^\s*\w*/); 28 | return ret("keyword", "important"); 29 | } 30 | else if (/\d/.test(ch)) { 31 | stream.eatWhile(/[\w.%]/); 32 | return ret("number", "unit"); 33 | } 34 | else if (/[,.+>*\/]/.test(ch)) { 35 | return ret(null, "select-op"); 36 | } 37 | else if (/[;{}:\[\]]/.test(ch)) { 38 | return ret(null, ch); 39 | } 40 | else { 41 | stream.eatWhile(/[\w\\\-]/); 42 | return ret("variable", "variable"); 43 | } 44 | } 45 | 46 | function tokenCComment(stream, state) { 47 | var maybeEnd = false, ch; 48 | while ((ch = stream.next()) != null) { 49 | if (maybeEnd && ch == "/") { 50 | state.tokenize = tokenBase; 51 | break; 52 | } 53 | maybeEnd = (ch == "*"); 54 | } 55 | return ret("comment", "comment"); 56 | } 57 | 58 | function tokenSGMLComment(stream, state) { 59 | var dashes = 0, ch; 60 | while ((ch = stream.next()) != null) { 61 | if (dashes >= 2 && ch == ">") { 62 | state.tokenize = tokenBase; 63 | break; 64 | } 65 | dashes = (ch == "-") ? dashes + 1 : 0; 66 | } 67 | return ret("comment", "comment"); 68 | } 69 | 70 | function tokenString(quote) { 71 | return function(stream, state) { 72 | var escaped = false, ch; 73 | while ((ch = stream.next()) != null) { 74 | if (ch == quote && !escaped) 75 | break; 76 | escaped = !escaped && ch == "\\"; 77 | } 78 | if (!escaped) state.tokenize = tokenBase; 79 | return ret("string", "string"); 80 | }; 81 | } 82 | 83 | return { 84 | startState: function(base) { 85 | return {tokenize: tokenBase, 86 | baseIndent: base || 0, 87 | stack: []}; 88 | }, 89 | 90 | token: function(stream, state) { 91 | if (stream.eatSpace()) return null; 92 | var style = state.tokenize(stream, state); 93 | 94 | var context = state.stack[state.stack.length-1]; 95 | if (type == "hash" && context != "rule") style = "string-2"; 96 | else if (style == "variable") { 97 | if (context == "rule") style = "number"; 98 | else if (!context || context == "@media{") style = "tag"; 99 | } 100 | 101 | if (context == "rule" && /^[\{\};]$/.test(type)) 102 | state.stack.pop(); 103 | if (type == "{") { 104 | if (context == "@media") state.stack[state.stack.length-1] = "@media{"; 105 | else state.stack.push("{"); 106 | } 107 | else if (type == "}") state.stack.pop(); 108 | else if (type == "@media") state.stack.push("@media"); 109 | else if (context == "{" && type != "comment") state.stack.push("rule"); 110 | return style; 111 | }, 112 | 113 | indent: function(state, textAfter) { 114 | var n = state.stack.length; 115 | if (/^\}/.test(textAfter)) 116 | n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1; 117 | return state.baseIndent + n * indentUnit; 118 | }, 119 | 120 | electricChars: "}" 121 | }; 122 | }); 123 | 124 | CodeMirror.defineMIME("text/css", "css"); 125 | -------------------------------------------------------------------------------- /wds-customizer-css.php: -------------------------------------------------------------------------------- 1 | basename = plugin_basename( __FILE__ ); 31 | $this->directory_path = plugin_dir_path( __FILE__ ); 32 | $this->directory_url = plugins_url( dirname( $this->basename ) ); 33 | 34 | // Load Textdomain 35 | load_plugin_textdomain( 'wds-customizer-css', false, dirname( $this->basename ) . '/languages' ); 36 | } 37 | 38 | public function include_scripts() { 39 | wp_enqueue_script( 'wds-customizer-css-js', plugins_url( 'includes/js/wds-customizer-css.js', __FILE__ ), array( 'jquery', 'customize-preview' ), '1.0.0', true ); 40 | } 41 | 42 | /** 43 | * Register CPTs & taxonomies. 44 | */ 45 | public function do_hooks() { 46 | add_action( 'customize_register', array( $this, 'init_customizer' ) ); 47 | add_action( 'wp_head', array( $this, 'add_styles' ) ); 48 | add_action( 'customize_preview_init', array( $this, 'include_scripts' ) ); 49 | add_action( 'admin_menu', array( $this, 'add_menu_page' ) ); 50 | } 51 | 52 | public function init_customizer( $wp_customize ) { 53 | require_once $this->directory_path . '/includes/custom-control.php'; 54 | 55 | $wp_customize->add_setting( 'wds_custom_css', array( 56 | 'transport' => 'postMessage', 57 | ) 58 | ); 59 | $wp_customize->add_section( 'wds_custom_css_section' , array( 60 | 'title' => __( 'Custom CSS', 'wds-customizer-css' ), 61 | 'priority' => apply_filters( 'wds_custom_css_section_priority', 30 ), 62 | ) ); 63 | 64 | $wp_customize->add_control( new WDS_Custom_CSS_Textarea_Control( $wp_customize, 'wds_custom_css', array( 65 | 'label' => __( 'Custom CSS', 'wds-customizer-css' ), 66 | 'section' => 'wds_custom_css_section', 67 | 'settings' => 'wds_custom_css', 68 | ) ) ); 69 | 70 | } 71 | 72 | public function add_styles() { 73 | ?> 74 | 77 | " . __( 'WDS Custom CSS', 'wds-customizer-css' ) . ""; 96 | echo "" . __( 'Want to see a real-time preview? Edit in the Customizer.', 'wds-customizer-css' ) . ""; 97 | echo "
"; 98 | 99 | wp_nonce_field( 'wds_custom_css_nonce' ); 100 | 101 | echo "\n"; 102 | echo "\n"; 103 | echo "\n"; 104 | echo "
" . __( 'Custom CSS', 'wds-customizer-css' ) . "
"; 105 | echo "

"; 106 | } 107 | 108 | } 109 | 110 | $_GLOBALS['WDS_Customizer_CSS'] = new WDS_Customizer_CSS; 111 | $_GLOBALS['WDS_Customizer_CSS']->do_hooks(); 112 | } 113 | -------------------------------------------------------------------------------- /includes/libs/codemirror/motif-codemirror.js: -------------------------------------------------------------------------------- 1 | var CodeMirror=function(){function a(d,e){function Zb(a){return a>=0&&a=c.to||b.linee-400&&_(Ab.pos,d))return C(a),setTimeout(Ic,20),dd(d.line);if(zb&&zb.time>e-400&&_(zb.pos,d))return Ab={time:e,pos:d},C(a),cd(d);zb={time:e,pos:d};var g=d,h;if(f.dragDrop&&T&&!f.readOnly&&!_(xb.from,xb.to)&&!ab(d,xb.from)&&!ab(xb.to,d)){P&&(kb.draggable=!0);function i(b){P&&(kb.draggable=!1),Cb=!1,j(),k(),Math.abs(a.clientX-b.clientX)+Math.abs(a.clientY-b.clientY)<10&&(C(b),Wc(d.line,d.ch,!0),Ic())}var j=I(document,"mouseup",Zd(i),!0),k=I(U,"drop",Zd(i),!0);Cb=!0,kb.dragDrop&&kb.dragDrop();return}C(a),Wc(d.line,d.ch,!0);var n=I(document,"mousemove",Zd(function(a){clearTimeout(h),C(a),!M&&!G(a)?m(a):l(a)}),!0),j=I(document,"mouseup",Zd(m),!0)}function ec(a){for(var b=F(a);b!=A;b=b.parentNode)if(b.parentNode==db)return C(a);var c=Md(a);if(!c)return;Ab={time:+(new Date),pos:c},C(a),cd(c)}function fc(a){if(f.onDragEvent&&f.onDragEvent($b,B(a)))return;a.preventDefault();var b=Md(a,!0),c=a.dataTransfer.files;if(!b||f.readOnly)return;if(c&&c.length&&window.FileReader&&window.File){function d(a,c){var d=new FileReader;d.onload=function(){g[c]=d.result,++h==e&&(b=Yc(b),Zd(function(){var a=xc(g.join(""),b,b);Tc(b,a)})())},d.readAsText(a)}var e=c.length,g=Array(e),h=0;for(var i=0;i-1&&setTimeout(Zd(function(){fd(xb.to.line,"smart")}),75);if(jc(a,d))return;Ec()}function oc(a){if(f.onKeyEvent&&f.onKeyEvent($b,B(a)))return;H(a,"keyCode")==16&&(yb=null)}function pc(){if(f.readOnly=="nocursor")return;wb||(f.onFocus&&f.onFocus($b),wb=!0,A.className.search(/\bCodeMirror-focused\b/)==-1&&(A.className+=" CodeMirror-focused"),Kb||Hc(!0)),Dc(),Od()}function qc(){wb&&(f.onBlur&&f.onBlur($b),wb=!1,Sb&&Zd(function(){Sb&&(Sb(),Sb=null)})(),A.className=A.className.replace(" CodeMirror-focused","")),clearInterval(sb),setTimeout(function(){wb||(yb=null)},150)}function rc(a,b,c,d,e){if(Eb)return;if(Wb){var g=[];ub.iter(a.line,b.line+1,function(a){g.push(a.text)}),Wb.addChange(a.line,c.length,g);while(Wb.done.length>f.undoDepth)Wb.done.shift()}vc(a,b,c,d,e)}function sc(a,b){if(!a.length)return;var c=a.pop(),d=[];for(var e=c.length-1;e>=0;e-=1){var f=c[e],g=[],h=f.start+f.added;ub.iter(f.start,h,function(a){g.push(a.text)}),d.push({start:f.start,added:f.old.length,old:g});var i=Yc({line:f.start+f.old.length-1,ch:eb(g[g.length-1],f.old[f.old.length-1])});vc({line:f.start,ch:0},{line:h-1,ch:_b(h-1).text.length},f.old,i,i)}Fb=!0,b.push(d)}function tc(){sc(Wb.done,Wb.undone)}function uc(){sc(Wb.undone,Wb.done)}function vc(a,b,c,d,e){function y(a){return a<=Math.min(b.line,b.line+s)?a:a+s}if(Eb)return;var g=!1,h=Tb.length;f.lineWrapping||ub.iter(a.line,b.line+1,function(a){if(!a.hidden&&a.text.length==h)return g=!0,!0});if(a.line!=b.line||c.length>1)Lb=!0;var i=b.line-a.line,j=_b(a.line),k=_b(b.line);if(a.ch==0&&b.ch==0&&c[c.length-1]==""){var l=[],m=null;a.line?(m=_b(a.line-1),m.fixMarkEnds(k)):k.fixMarkStarts();for(var n=0,o=c.length-1;n1&&ub.remove(a.line+1,i-1,Mb),ub.insert(a.line+1,l)}if(f.lineWrapping){var p=Math.max(5,U.clientWidth/Jd()-3);ub.iter(a.line,a.line+c.length,function(a){if(a.hidden)return;var b=Math.ceil(a.text.length/p)||1;b!=a.height&&ac(a,b)})}else ub.iter(a.line,a.line+c.length,function(a){var b=a.text;!a.hidden&&b.length>h&&(Tb=b,h=b.length,Ub=null,g=!1)}),g&&(Nb=!0);var q=[],s=c.length-i-1;for(var n=0,t=vb.length;nb.line&&q.push(u+s)}var v=a.line+Math.min(c.length,500);Td(a.line,v),q.push(v),vb=q,Vd(100),Hb.push({from:a.line,to:b.line+1,diff:s});var w={from:a,to:b,text:c};if(Ib){for(var x=Ib;x.next;x=x.next);x.next=w}else Ib=w;Uc(d,e,y(xb.from.line),y(xb.to.line)),U.clientHeight&&(W.style.height=ub.height*Gd()+2*Kd()+"px")}function wc(){var a=0;Tb="",Ub=null,ub.iter(0,ub.size,function(b){var c=b.text;!b.hidden&&c.length>a&&(a=c.length,Tb=c)}),Nb=!1}function xc(a,b,c){function d(d){if(ab(d,b))return d;if(!ab(c,d))return e;var f=d.line+a.length-(c.line-b.line)-1,g=d.ch;return d.line==c.line&&(g+=a[a.length-1].length-(c.ch-(c.line==b.line?b.ch:0))),{line:f,ch:g}}b=Yc(b),c?c=Yc(c):c=b,a=hb(a);var e;return zc(a,b,c,function(a){return e=a,{from:d(xb.from),to:d(xb.to)}}),e}function yc(a,b){zc(hb(a),xb.from,xb.to,function(a){return b=="end"?{from:a,to:a}:b=="start"?{from:xb.from,to:xb.from}:{from:xb.from,to:a}})}function zc(a,b,c,d){var e=a.length==1?a[0].length+b.ch:a[a.length-1].length,f=d({line:b.line+a.length-1,ch:e});rc(b,c,a,f.from,f.to)}function Ac(a,b){var c=a.line,d=b.line;if(c==d)return _b(c).text.slice(a.ch,b.ch);var e=[_b(c).text.slice(a.ch)];return ub.iter(c+1,d,function(a){e.push(a.text)}),e.push(_b(d).text.slice(0,b.ch)),e.join("\n")}function Bc(){return Ac(xb.from,xb.to)}function Dc(){if(Cc)return;qb.set(f.pollInterval,function(){Wd(),Gc(),wb&&Dc(),Xd()})}function Ec(){function b(){Wd();var c=Gc();!c&&!a?(a=!0,qb.set(60,b)):(Cc=!1,Dc()),Xd()}var a=!1;Cc=!0,qb.set(20,b)}function Gc(){if(Kb||!wb||ib(R)||f.readOnly)return!1;var a=R.value;if(a==Fc)return!1;yb=null;var b=0,c=Math.min(Fc.length,a.length);while(b1e3?R.value=Fc="":Fc=a,!0}function Hc(a){_(xb.from,xb.to)?a&&(Fc=R.value=""):(Fc="",R.value=Bc(),$(R))}function Ic(){f.readOnly!="nocursor"&&R.focus()}function Jc(){if(!mb.getBoundingClientRect)return;var a=mb.getBoundingClientRect();if(M&&a.top==a.bottom)return;var b=window.innerHeight||Math.max(document.body.offsetHeight,document.documentElement.offsetHeight);(a.top<0||a.bottom>b)&&mb.scrollIntoView()}function Kc(){var a=Ad(xb.inverted?xb.from:xb.to),b=f.lineWrapping?Math.min(a.x,kb.offsetWidth):a.x;return Lc(b,a.y,b,a.yBot)}function Lc(a,b,c,d){var e=Ld(),g=Kd();b+=g,d+=g,a+=e,c+=e;var h=U.clientHeight,i=U.scrollTop,j=!1,k=!0;bi+h&&(U.scrollTop=d-h,j=!0);var l=U.clientWidth,m=U.scrollLeft,n=f.fixedGutter?cb.clientWidth:0,o=al+m-3&&(U.scrollLeft=c+10-l,j=!0,c>W.clientWidth&&(k=!1)),j&&f.onScroll&&f.onScroll($b),k}function Mc(){var a=Gd(),b=U.scrollTop-Kd(),c=Math.max(0,Math.floor(b/a)),d=Math.ceil((b+U.clientHeight)/a);return{from:x(ub,c),to:x(ub,d)}}function Nc(a,b){function n(){Ub=U.clientWidth;var a=ob.firstChild,b=!1;return ub.iter(Pb,Qb,function(c){if(!c.hidden){var d=Math.round(a.offsetHeight/k)||1;c.height!=d&&(ac(c,d),Lb=b=!0)}a=a.nextSibling}),b&&(W.style.height=ub.height*k+2*Kd()+"px"),b}if(!U.clientWidth){Pb=Qb=Ob=0;return}var c=Mc();if(a!==!0&&a.length==0&&c.from>Pb&&c.toe&&Qb-e<20&&(e=Math.min(ub.size,Qb));var g=a===!0?[]:Oc([{from:Pb,to:Qb,domStart:0}],a),h=0;for(var i=0;ie&&(j.to=e),j.from>=j.to?g.splice(i--,1):h+=j.to-j.from}if(h==e-d&&d==Pb&&e==Qb)return;g.sort(function(a,b){return a.domStart-b.domStart});var k=Gd(),l=cb.style.display;ob.style.display="none",Pc(d,e,g),ob.style.display=cb.style.display="";var m=d!=Pb||e!=Qb||Rb!=U.clientHeight+k;m&&(Rb=U.clientHeight+k),Pb=d,Qb=e,Ob=y(ub,d),X.style.top=Ob*k+"px",U.clientHeight&&(W.style.height=ub.height*k+2*Kd()+"px");if(ob.childNodes.length!=Qb-Pb)throw new Error("BAD PATCH! "+JSON.stringify(g)+" size="+(Qb-Pb)+" nodes="+ob.childNodes.length);return f.lineWrapping?n():(Ub==null&&(Ub=wd(Tb)),Ub>U.clientWidth?(kb.style.width=Ub+"px",W.style.width="",W.style.width=U.scrollWidth+"px"):kb.style.width=W.style.width=""),cb.style.display=l,(m||Lb)&&Qc()&&f.lineWrapping&&n()&&Qc(),Rc(),!b&&f.onUpdate&&f.onUpdate($b),!0}function Oc(a,b){for(var c=0,d=b.length||0;c=j.to?f.push(j):(e.from>j.from&&f.push({from:j.from,to:e.from,domStart:j.domStart}),e.toe)f=d(f),e++;for(var j=0,k=i.to-i.from;jj){if(a.hidden)var b=m.innerHTML="
";else{var b=""+a.getHTML(jd)+"";a.bgClassName&&(b='
 
'+b+"
")}m.innerHTML=b,ob.insertBefore(m.firstChild,f)}else f=f.nextSibling;++j})}function Qc(){if(!f.gutter&&!f.lineNumbers)return;var a=X.offsetHeight,b=U.clientHeight;cb.style.height=(a-b<2?b:a)+"px";var c=[],d=Pb,e;ub.iter(Pb,Math.max(Qb,Pb+1),function(a){if(a.hidden)c.push("
");else{var b=a.gutterMarker,g=f.lineNumbers?d+f.firstLineNumber:null;b&&b.text?g=b.text.replace("%N%",g!=null?g:""):g==null&&(g="\u00a0"),c.push(b&&b.style?'
':"
",g);for(var h=1;h ");c.push("
"),b||(e=d)}++d}),cb.style.display="none",db.innerHTML=c.join("");if(e!=null){var g=db.childNodes[e-Pb],h=String(ub.size).length,i=Z(g),j="";while(i.length+j.length2;return kb.style.marginLeft=cb.offsetWidth+"px",Lb=!1,k}function Rc(){var a=_(xb.from,xb.to),b=Ad(xb.from,!0),c=a?b:Ad(xb.to,!0),d=xb.inverted?b:c,e=Gd(),g=Y(A),h=Y(ob);D.style.top=Math.max(0,Math.min(U.offsetHeight,d.y+h.top-g.top))+"px",D.style.left=Math.max(0,Math.min(U.offsetWidth,d.x+h.left-g.left))+"px";if(a)mb.style.top=d.y+"px",mb.style.left=(f.lineWrapping?Math.min(d.x,kb.offsetWidth):d.x)+"px",mb.style.display="",nb.style.display="none";else{var i=b.y==c.y,j="",k=kb.clientWidth||kb.offsetWidth,l=kb.clientHeight||kb.offsetHeight;function m(a,b,c,d){var e=O?"width: "+(c?k-c-a:k)+"px":"right: "+c+"px";j+='
'}if(xb.from.ch&&b.y>=0){var n=i?k-c.x:0;m(b.x,b.y,n,e)}var o=Math.max(0,b.y+(xb.from.ch?e:0)),p=Math.min(c.y,l)-o;p>.2*e&&m(0,o,0,p),(!i||!xb.from.ch)&&c.yc||h>g.text.length)h=g.text.length;return{line:d,ch:h}}d+=b}}var e=_b(a.line),f=a.ch==e.text.length&&a.ch!=c;return e.hidden?a.line>=b?d(1)||d(-1):d(-1)||d(1):a}function Wc(a,b,c){var d=Yc({line:a,ch:b||0});(c?Tc:Uc)(d,d)}function Xc(a){return Math.max(0,Math.min(a,ub.size-1))}function Yc(a){if(a.line<0)return{line:0,ch:0};if(a.line>=ub.size)return{line:ub.size-1,ch:_b(ub.size-1).text.length};var b=a.ch,c=_b(a.line).text.length;return b==null||b>c?{line:a.line,ch:c}:b<0?{line:a.line,ch:0}:a}function Zc(a,b){function g(){for(var b=d+a,c=a<0?-1:ub.size;b!=c;b+=a){var e=_b(b);if(!e.hidden)return d=b,f=e,!0}}function h(b){if(e==(a<0?0:f.text.length)){if(!!b||!g())return!1;e=a<0?f.text.length:0}else e+=a;return!0}var c=xb.inverted?xb.from:xb.to,d=c.line,e=c.ch,f=_b(d);if(b=="char")h();else if(b=="column")h(!0);else if(b=="word"){var i=!1;for(;;){if(a<0&&!h())break;if(gb(f.text.charAt(e)))i=!0;else if(i){a<0&&(a=1,h());break}if(a>0&&!h())break}}return{line:d,ch:e}}function $c(a,b){var c=a<0?xb.from:xb.to;if(yb||_(xb.from,xb.to))c=Zc(a,b);Wc(c.line,c.ch,!0)}function _c(a,b){_(xb.from,xb.to)?a<0?xc("",Zc(a,b),xb.to):xc("",xb.from,Zc(a,b)):xc("",xb.from,xb.to),Gb=!0}function bd(a,b){var c=0,d=Ad(xb.inverted?xb.from:xb.to,!0);ad!=null&&(d.x=ad),b=="page"?c=Math.min(U.clientHeight,window.innerHeight||document.documentElement.clientHeight):b=="line"&&(c=Gd());var e=Bd(d.x,d.y+c*a+2);b=="page"&&(U.scrollTop+=Ad(e,!0).y-d.y),Wc(e.line,e.ch,!0),ad=d.x}function cd(a){var b=_b(a.line).text,c=a.ch,d=a.ch;while(c>0&&gb(b.charAt(c-1)))--c;while(dTb.length&&(Tb=a.text)});Hb.push({from:0,to:ub.size})}function jd(a){var b=f.tabSize-a%f.tabSize,c=Vb[b];if(c)return c;for(var d='',e=0;e",width:b}}function kd(){U.className=U.className.replace(/\s*cm-s-\S+/g,"")+f.theme.replace(/(^|\s)\s*/g," cm-s-")}function ld(){var a=i[f.keyMap].style;A.className=A.className.replace(/\s*cm-keymap-\S+/g,"")+(a?" cm-keymap-"+a:"")}function md(){this.set=[]}function nd(a,b,c){function e(a,b,c,e){_b(a).addMark(new p(b,c,e,d))}a=Yc(a),b=Yc(b);var d=new md;if(!ab(a,b))return d;if(a.line==b.line)e(a.line,a.ch,b.ch,c);else{e(a.line,a.ch,null,c);for(var f=a.line+1,g=b.line;f=a.ch)&&b.push(f.marker||f)}return b}function qd(a,b,c){return typeof a=="number"&&(a=_b(Xc(a))),a.gutterMarker={text:b,style:c},Lb=!0,a}function rd(a){typeof a=="number"&&(a=_b(Xc(a))),a.gutterMarker=null,Lb=!0}function sd(a,b){var c=a,d=a;return typeof a=="number"?d=_b(Xc(a)):c=w(a),c==null?null:b(d,c)?(Hb.push({from:c,to:c+1}),d):null}function td(a,b,c){return sd(a,function(a){if(a.className!=b||a.bgClassName!=c)return a.className=b,a.bgClassName=c,!0})}function ud(a,b){return sd(a,function(a,c){if(a.hidden!=b){a.hidden=b;if(!f.lineWrapping){var d=a.text;b&&d.length==Tb.length?Nb=!0:!b&&d.length>Tb.length&&(Tb=d,Ub=null,Nb=!1)}ac(a,b?0:1);var e=xb.from.line,g=xb.to.line;if(b&&(e==c||g==c)){var h=e==c?Vc({line:e,ch:0},e,0):xb.from,i=g==c?Vc({line:g,ch:0},g,0):xb.to;if(!i)return;Uc(h,i)}return Lb=!0}})}function vd(a){if(typeof a=="number"){if(!Zb(a))return null;var b=a;a=_b(a);if(!a)return null}else{var b=w(a);if(b==null)return null}var c=a.gutterMarker;return{line:b,handle:a,text:a.text,markerText:c&&c.text,markerClass:c&&c.style,lineClass:a.className,bgClass:a.bgClassName}}function wd(a){return lb.innerHTML="
x
",lb.firstChild.firstChild.firstChild.nodeValue=a,lb.firstChild.firstChild.offsetWidth||10}function xd(a,b){function e(a){return zd(c,a).left}if(b<=0)return 0;var c=_b(a),d=c.text,f=0,g=0,h=d.length,i,j=Math.min(h,Math.ceil(b/Jd()));for(;;){var k=e(j);if(!(k<=b&&ji)return h;j=Math.floor(h*.8),k=e(j),kb-g?f:h;var l=Math.ceil((f+h)/2),m=e(l);m>b?(h=l,i=m):(f=l,g=m)}}function zd(a,b){if(b==0)return{top:0,left:0};var c=f.lineWrapping&&b"+a.getHTML(jd,b,yd,c)+"
";var d=document.getElementById(yd),e=d.offsetTop,g=d.offsetLeft;if(M&&e==0&&g==0){var h=document.createElement("span");h.innerHTML="x",d.parentNode.insertBefore(h,d.nextSibling),e=h.offsetTop}return{top:e,left:g}}function Ad(a,b){var c,d=Gd(),e=d*(y(ub,a.line)-(b?Ob:0));if(a.ch==0)c=0;else{var g=zd(_b(a.line),a.ch);c=g.left,f.lineWrapping&&(e+=Math.max(0,g.top))}return{x:c,y:e,yBot:e+d}}function Bd(a,b){function l(a){var b=zd(h,a);if(j){var d=Math.round(b.top/c);return Math.max(0,b.left+(d-k)*U.clientWidth)}return b.left}b<0&&(b=0);var c=Gd(),d=Jd(),e=Ob+Math.floor(b/c),g=x(ub,e);if(g>=ub.size)return{line:ub.size-1,ch:_b(ub.size-1).text.length};var h=_b(g),i=h.text,j=f.lineWrapping,k=j?e-y(ub,g):0;if(a<=0&&k==0)return{line:g,ch:0};var m=0,n=0,o=i.length,p,q=Math.min(o,Math.ceil((a+k*U.clientWidth*.9)/d));for(;;){var r=l(q);if(!(r<=a&&qp)return{line:g,ch:o};q=Math.floor(o*.8),r=l(q),ra-n?m:o};var s=Math.ceil((m+o)/2),t=l(s);t>a?(o=s,p=t):(m=s,n=t)}}function Cd(a){var b=Ad(a,!0),c=Y(kb);return{x:c.left+b.x,y:c.top+b.y,yBot:c.top+b.yBot}}function Gd(){if(Fd==null){Fd="
";for(var a=0;a<49;++a)Fd+="x
";Fd+="x
"}var b=ob.clientHeight;return b==Ed?Dd:(Ed=b,lb.innerHTML=Fd,Dd=lb.firstChild.offsetHeight/50||1,lb.innerHTML="",Dd)}function Jd(){return U.clientWidth==Id?Hd:(Id=U.clientWidth,Hd=wd("x"))}function Kd(){return kb.offsetTop}function Ld(){return kb.offsetLeft}function Md(a,b){var c=Y(U,!0),d,e;try{d=a.clientX,e=a.clientY}catch(a){return null}if(!b&&(d-c.left>U.clientWidth||e-c.top>U.clientHeight))return null;var f=Y(kb,!0);return Bd(d-f.left,e-f.top)}function Nd(a){function f(){var a=hb(R.value).join("\n");a!=e&&Zd(yc)(a,"end"),D.style.position="relative",R.style.cssText=d,N&&(U.scrollTop=c),Kb=!1,Hc(!0),Dc()}var b=Md(a),c=U.scrollTop;if(!b||window.opera)return;(_(xb.from,xb.to)||ab(b,xb.from)||!ab(b,xb.to))&&Zd(Wc)(b.line,b.ch);var d=R.style.cssText;D.style.position="absolute",R.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(a.clientY-5)+"px; left: "+(a.clientX-5)+"px; z-index: 1000; background: white; "+"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",Kb=!0;var e=R.value=Bc();Ic(),$(R);if(L){E(a);var g=I(window,"mouseup",function(){g(),setTimeout(f,20)},!0)}else setTimeout(f,50)}function Od(){clearInterval(sb);var a=!0;mb.style.visibility="",sb=setInterval(function(){mb.style.visibility=(a=!a)?"":"hidden"},650)}function Qd(a){function p(a,b,c){if(!a.text)return;var d=a.styles,e=g?0:a.text.length-1,f;for(var i=g?0:d.length-2,j=g?d.length:-2;i!=j;i+=2*h){var k=d[i];if(d[i+1]!=null&&d[i+1]!=m){e+=h*k.length;continue}for(var l=g?0:k.length-1,p=g?k.length:-1;l!=p;l+=h,e+=h)if(e>=b&&e"==g)n.push(f);else{if(n.pop()!=q.charAt(0))return{pos:e,match:!1};if(!n.length)return{pos:e,match:!0}}}}}var b=xb.inverted?xb.from:xb.to,c=_b(b.line),d=b.ch-1,e=d>=0&&Pd[c.text.charAt(d)]||Pd[c.text.charAt(++d)];if(!e)return;var f=e.charAt(0),g=e.charAt(1)==">",h=g?1:-1,i=c.styles;for(var j=d+1,k=0,l=i.length;ke;--d){if(d==0)return 0;var g=_b(d-1);if(g.stateAfter)return d;var h=g.indentation(f.tabSize);if(c==null||b>h)c=d-1,b=h}return c}function Sd(a){var b=Rd(a),c=b&&_b(b-1).stateAfter;return c?c=m(tb,c):c=n(tb),ub.iter(b,a,function(a){a.highlight(tb,c,f.tabSize),a.stateAfter=m(tb,c)}),b=ub.size)continue;var d=Rd(c),e=d&&_b(d-1).stateAfter;e?e=m(tb,e):e=n(tb);var g=0,h=tb.compareStates,i=!1,j=d,k=!1;ub.iter(j,ub.size,function(b){var d=b.stateAfter;if(+(new Date)>a)return vb.push(j),Vd(f.workDelay),i&&Hb.push({from:c,to:j+1}),k=!0;var l=b.highlight(tb,e,f.tabSize);l&&(i=!0),b.stateAfter=m(tb,e);var n=null;if(h){var o=d&&h(d,e);o!=K&&(n=!!o)}n==null&&(l!==!1||!d?g=0:++g>3&&(!tb.indent||tb.indent(d,"")==tb.indent(e,""))&&(n=!0));if(n)return!0;++j});if(k)return;i&&Hb.push({from:c,to:j+1})}b&&f.onHighlightComplete&&f.onHighlightComplete($b)}function Vd(a){if(!vb.length)return;rb.set(a,Zd(Ud))}function Wd(){Fb=Gb=Ib=null,Hb=[],Jb=!1,Mb=[]}function Xd(){var a=!1,b;Nb&&wc(),Jb&&(a=!Kc()),Hb.length?b=Nc(Hb,!0):(Jb&&Rc(),Lb&&Qc()),a&&Kc(),Jb&&(Jc(),Od()),wb&&!Kb&&(Fb===!0||Fb!==!1&&Jb)&&Hc(Gb),Jb&&f.matchBrackets&&setTimeout(Zd(function(){Sb&&(Sb(),Sb=null),_(xb.from,xb.to)&&Qd(!1)}),20);var c=Ib,d=Mb;Jb&&f.onCursorActivity&&f.onCursorActivity($b),c&&f.onChange&&$b&&f.onChange($b,c);for(var e=0;eh&&a.y>b.offsetHeight&&(f=a.y-b.offsetHeight),g+b.offsetWidth>i&&(g=i-b.offsetWidth)}b.style.top=f+Kd()+"px",b.style.left=b.style.right="",e=="right"?(g=W.clientWidth-b.offsetWidth,b.style.right="0px"):(e=="left"?g=0:e=="middle"&&(g=(W.clientWidth-b.offsetWidth)/2),b.style.left=g+Ld()+"px"),c&&Lc(g,f,g+b.offsetWidth,f+b.offsetHeight)},lineCount:function(){return ub.size},clipPos:Yc,getCursor:function(a){return a==null&&(a=xb.inverted),bb(a?xb.from:xb.to)},somethingSelected:function(){return!_(xb.from,xb.to)},setCursor:Zd(function(a,b,c){b==null&&typeof a.line=="number"?Wc(a.line,a.ch,c):Wc(a,b,c)}),setSelection:Zd(function(a,b,c){(c?Tc:Uc)(Yc(a),Yc(b||a))}),getLine:function(a){if(Zb(a))return _b(a).text},getLineHandle:function(a){if(Zb(a))return _b(a)},setLine:Zd(function(a,b){Zb(a)&&xc(b,{line:a,ch:0},{line:a,ch:_b(a).text.length})}),removeLine:Zd(function(a){Zb(a)&&xc("",{line:a,ch:0},Yc({line:a+1,ch:0}))}),replaceRange:Zd(xc),getRange:function(a,b){return Ac(Yc(a),Yc(b))},triggerOnKeyDown:Zd(mc),execCommand:function(a){return h[a]($b)},moveH:Zd($c),deleteH:Zd(_c),moveV:Zd(bd),toggleOverwrite:function(){Db?(Db=!1,mb.className=mb.className.replace(" CodeMirror-overwrite","")):(Db=!0,mb.className+=" CodeMirror-overwrite")},posFromIndex:function(a){var b=0,c;return ub.iter(0,ub.size,function(d){var e=d.text.length+1;if(e>a)return c=a,!0;a-=e,++b}),Yc({line:b,ch:c})},indexFromPos:function(a){if(a.line<0||a.ch<0)return 0;var b=a.ch;return ub.iter(0,a.line,function(a){b+=a.text.length+1}),b},scrollTo:function(a,b){a!=null&&(U.scrollLeft=a),b!=null&&(U.scrollTop=b),Nc([])},operation:function(a){return Zd(a)()},compoundChange:function(a){return $d(a)},refresh:function(){Nc(!0),U.scrollHeight>Bb&&(U.scrollTop=Bb)},getInputField:function(){return R},getWrapperElement:function(){return A},getScrollerElement:function(){return U},getGutterElement:function(){return cb}},kc=null,lc,Cc=!1,Fc="",ad=null;md.prototype.clear=Zd(function(){var a=Infinity,b=-Infinity;for(var c=0,d=this.set.length;c",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"},Yd=0;for(var _d in g)g.propertyIsEnumerable(_d)&&!$b.propertyIsEnumerable(_d)&&($b[_d]=g[_d]);return $b}function j(a){return typeof a=="string"?i[a]:a}function k(a,b,c,d,e){function f(b){b=j(b);var c=b[a];if(c!=null&&d(c))return!0;if(b.nofallthrough)return e&&e(),!0;var g=b.fallthrough;if(g==null)return!1;if(Object.prototype.toString.call(g)!="[object Array]")return f(g);for(var h=0,i=g.length;ha&&d.push(h.slice(a-f,Math.min(h.length,b-f)),c[e+1]),i>=a&&(g=1)):g==1&&(i>b?d.push(h.slice(0,b-f),c[e+1]):d.push(h,c[e+1])),f=i}}function t(a){this.lines=a,this.parent=null;for(var b=0,c=a.length,d=0;b=0&&d>=0;--c,--d)if(a.charAt(c)!=b.charAt(d))break;return d+1}function fb(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0,d=a.length;c2){c.dependencies=[];for(var d=2;d0&&b.ch=this.string.length},sol:function(){return this.pos==0},peek:function(){return this.string.charAt(this.pos)},next:function(){if(this.posb},eatSpace:function(){var a=this.pos;while(/[\s\u00a0]/.test(this.string.charAt(this.pos)))++this.pos;return this.pos>a},skipToEnd:function(){this.pos=this.string.length},skipTo:function(a){var b=this.string.indexOf(a,this.pos);if(b>-1)return this.pos=b,!0},backUp:function(a){this.pos-=a},column:function(){return W(this.string,this.start,this.tabSize)},indentation:function(){return W(this.string,null,this.tabSize)},match:function(a,b,c){if(typeof a!="string"){var e=this.string.slice(this.pos).match(a);return e&&b!==!1&&(this.pos+=e[0].length),e}function d(a){return c?a.toLowerCase():a}if(d(this.string).indexOf(d(a),this.pos)==this.pos)return b!==!1&&(this.pos+=a.length),!0},current:function(){return this.string.slice(this.start,this.pos)}},a.StringStream=o,p.prototype={attach:function(a){this.marker.set.push(a)},detach:function(a){var b=fb(this.marker.set,a);b>-1&&this.marker.set.splice(b,1)},split:function(a,b){if(this.to<=a&&this.to!=null)return null;var c=this.fromthis.from&&(d=b&&(this.from=Math.max(d,this.from)+e),c&&(bthis.from||this.from==null)?this.to=null:this.to!=null&&this.to>b&&(this.to=d=this.to},sameSet:function(a){return this.marker==a.marker}},q.prototype={attach:function(a){this.line=a},detach:function(a){this.line==a&&(this.line=null)},split:function(a,b){if(athis.to},clipTo:function(a,b,c,d,e){(a||bthis.to)?(this.from=0,this.to=-1):this.from>b&&(this.from=this.to=Math.max(d,this.from)+e)},sameSet:function(a){return!1},find:function(){return!this.line||!this.line.parent?null:{line:w(this.line),ch:this.from}},clear:function(){if(this.line){var a=fb(this.line.marked,this);a!=-1&&this.line.marked.splice(a,1),this.line=null}}},r.inheritMarks=function(a,b){var c=new r(a),d=b&&b.marked;if(d)for(var e=0;e5e3){e[f++]=this.text.slice(d.pos),e[f++]=null;break}}return e.length!=f&&(e.length=f,g=!0),f&&e[f-2]!=i&&(g=!0),g||(e.length<5&&this.text.length<10?null:!1)},getTokenAt:function(a,b,c){var d=this.text,e=new o(d);while(e.pos',d,""):e.push(d)}function p(a){return a?"cm-"+a.replace(/ +/g," cm-"):null}var e=[],f=!0,g=0,i=h;if(b!=null){var j=0,k='';i=function(a,c){var f=a.length;if(b>=j&&bj&&(h(a.slice(0,b-j),c),d&&e.push("")),e.push(k);var g=b-j;h(window.opera?a.slice(g,g+1):a.slice(g),c),e.push(""),window.opera&&h(a.slice(g+1),c),b--,j+=f}else j+=f,h(a,c),j==b&&j==o?e.push(k+" "):j>b+10&&/\s/.test(a)&&(i=function(){})}}var l=this.styles,m=this.text,n=this.marked,o=m.length;if(!m&&b==null)i(" ");else if(!n||!n.length)for(var q=0,r=0;ro&&(s=s.slice(0,o-r)),r+=u,i(s,p(t))}else{var v=0,q=0,w="",t,x=0,y=n[0].from||0,z=[],A=0;function B(){var a;while(AD?w.slice(0,D-v):w,F);if(E>=D){w=w.slice(D-v),v=D;break}v=E}w=l[q++],t=p(l[q++])}}}return e.join("")},cleanUp:function(){this.parent=null;if(this.marked)for(var a=0,b=this.marked.length;a50){while(f.lines.length>50){var h=f.lines.splice(f.lines.length-25,25),i=new t(h);f.height-=i.height,this.children.splice(d+1,0,i),i.parent=this}this.maybeSpill()}break}a-=g}},maybeSpill:function(){if(this.children.length<=10)return;var a=this;do{var b=a.children.splice(a.children.length-5,5),c=new u(b);if(!a.parent){var d=new u(a.children);d.parent=a,a.children=[d,c],a=d}else{a.size-=c.size,a.height-=c.height;var e=fb(a.parent.children,a);a.parent.children.splice(e+1,0,c)}c.parent=a.parent}while(a.children.length>10);a.parent.maybeSpill()},iter:function(a,b,c){this.iterN(a,b-a,c)},iterN:function(a,b,c){for(var d=0,e=this.children.length;d400||!f||this.closed||f.start>a+c.length||f.start+f.added0;--j)f.old.unshift(c[j-1]);for(var j=i;j>0;--j)f.old.push(c[c.length-j]);h&&(f.start=a),f.added+=b-(c.length-h-i)}this.time=d},startCompound:function(){this.compound++||(this.closed=!0)},endCompound:function(){--this.compound||(this.closed=!0)}},a.e_stop=E,a.e_preventDefault=C,a.e_stopPropagation=D,a.connect=I,J.prototype={set:function(a,b){clearTimeout(this.id),this.id=setTimeout(b,a)}};var K=a.Pass={toString:function(){return"CodeMirror.Pass"}},L=/gecko\/\d{7}/i.test(navigator.userAgent),M=/MSIE \d/.test(navigator.userAgent),N=/MSIE [1-8]\b/.test(navigator.userAgent),O=M&&document.documentMode==5,P=/WebKit\//.test(navigator.userAgent),Q=/Chrome\//.test(navigator.userAgent),R=/Apple Computer/.test(navigator.vendor),S=/KHTML\//.test(navigator.userAgent),T=function(){if(N)return!1;var a=document.createElement("div");return"draggable"in a||"dragDrop"in a}(),U=function(){var a=document.createElement("textarea");return a.value="foo\nbar",a.value.indexOf("\r")>-1?"\r\n":"\n"}(),V=/^$/;L?V=/$'/:R?V=/\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/:Q&&(V=/\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/),document.documentElement.getBoundingClientRect!=null&&(Y=function(a,b){try{var c=a.getBoundingClientRect();c={top:c.top,left:c.left}}catch(d){c={top:0,left:0}}if(!b)if(window.pageYOffset==null){var e=document.documentElement||document.body.parentNode;e.scrollTop==null&&(e=document.body),c.top+=e.scrollTop,c.left+=e.scrollLeft}else c.top+=window.pageYOffset,c.left+=window.pageXOffset;return c});var cb=document.createElement("pre");db("a")=="\na"?db=function(a){return cb.textContent=a,cb.innerHTML.slice(1)}:db(" ")!=" "&&(db=function(a){return cb.innerHTML="",cb.appendChild(document.createTextNode(a)),cb.innerHTML}),a.htmlEscape=db;var hb="\n\nb".split(/\n/).length!=3?function(a){var b=0,c,d=[];while((c=a.indexOf("\n",b))>-1)d.push(a.slice(b,a.charAt(c-1)=="\r"?c-1:c)),b=c+1;return d.push(a.slice(b)),d}:function(a){return a.split(/\r?\n/)};a.splitLines=hb;var ib=window.getSelection?function(a){try{return a.selectionStart!=a.selectionEnd}catch(b){return!1}}:function(a){try{var b=a.ownerDocument.selection.createRange()}catch(c){}return!b||b.parentElement()!=a?!1:b.compareEndPoints("StartToEnd",b)!=0};a.defineMode("null",function(){return{token:function(a){a.skipToEnd()}}}),a.defineMIME("text/plain","null");var jb={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",127:"Delete",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"};return a.keyNames=jb,function(){for(var a=0;a<10;a++)jb[a+48]=String(a);for(var a=65;a<=90;a++)jb[a]=String.fromCharCode(a);for(var a=1;a<=12;a++)jb[a+111]=jb[a+63235]="F"+a}(),a}();CodeMirror.defineMode("css",function(a){function d(a,b){return c=b,a}function e(a,b){var c=a.next();if(c=="@")return a.eatWhile(/[\w\\\-]/),d("meta",a.current());if(c=="/"&&a.eat("*"))return b.tokenize=f,f(a,b);if(c=="<"&&a.eat("!"))return b.tokenize=g,g(a,b);if(c!="=")return c!="~"&&c!="|"||!a.eat("=")?c=='"'||c=="'"?(b.tokenize=h(c),b.tokenize(a,b)):c=="#"?(a.eatWhile(/[\w\\\-]/),d("atom","hash")):c=="!"?(a.match(/^\s*\w*/),d("keyword","important")):/\d/.test(c)?(a.eatWhile(/[\w.%]/),d("number","unit")):/[,.+>*\/]/.test(c)?d(null,"select-op"):/[;{}:\[\]]/.test(c)?d(null,c):(a.eatWhile(/[\w\\\-]/),d("variable","variable")):d(null,"compare");d(null,"compare")}function f(a,b){var c=!1,f;while((f=a.next())!=null){if(c&&f=="/"){b.tokenize=e;break}c=f=="*"}return d("comment","comment")}function g(a,b){var c=0,f;while((f=a.next())!=null){if(c>=2&&f==">"){b.tokenize=e;break}c=f=="-"?c+1:0}return d("comment","comment")}function h(a){return function(b,c){var f=!1,g;while((g=b.next())!=null){if(g==a&&!f)break;f=!f&&g=="\\"}return f||(c.tokenize=e),d("string","string")}}var b=a.indentUnit,c;return{startState:function(a){return{tokenize:e,baseIndent:a||0,stack:[]}},token:function(a,b){if(a.eatSpace())return null;var d=b.tokenize(a,b),e=b.stack[b.stack.length-1];if(c=="hash"&&e!="rule")d="string-2";else if(d=="variable")if(e=="rule")d="number";else if(!e||e=="@media{")d="tag";return e=="rule"&&/^[\{\};]$/.test(c)&&b.stack.pop(),c=="{"?e=="@media"?b.stack[b.stack.length-1]="@media{":b.stack.push("{"):c=="}"?b.stack.pop():c=="@media"?b.stack.push("@media"):e=="{"&&c!="comment"&&b.stack.push("rule"),d},indent:function(a,c){var d=a.stack.length;return/^\}/.test(c)&&(d-=a.stack[a.stack.length-1]=="rule"?2:1),a.baseIndent+d*b},electricChars:"}"}}),CodeMirror.defineMIME("text/css","css") 2 | -------------------------------------------------------------------------------- /includes/libs/codemirror/lib/codemirror.js: -------------------------------------------------------------------------------- 1 | // CodeMirror version 2.25 2 | // 3 | // All functions that need access to the editor's state live inside 4 | // the CodeMirror function. Below that, at the bottom of the file, 5 | // some utilities are defined. 6 | 7 | // CodeMirror is the only global var we claim 8 | var CodeMirror = (function() { 9 | // This is the function that produces an editor instance. Its 10 | // closure is used to store the editor state. 11 | function CodeMirror(place, givenOptions) { 12 | // Determine effective options based on given values and defaults. 13 | var options = {}, defaults = CodeMirror.defaults; 14 | for (var opt in defaults) 15 | if (defaults.hasOwnProperty(opt)) 16 | options[opt] = (givenOptions && givenOptions.hasOwnProperty(opt) ? givenOptions : defaults)[opt]; 17 | 18 | // The element in which the editor lives. 19 | var wrapper = document.createElement("div"); 20 | wrapper.className = "CodeMirror" + (options.lineWrapping ? " CodeMirror-wrap" : ""); 21 | // This mess creates the base DOM structure for the editor. 22 | wrapper.innerHTML = 23 | '
' + // Wraps and hides input textarea 24 | '
' + 26 | '
' + 27 | '
' + // Set to the height of the text, causes scrolling 28 | '
' + // Moved around its parent to cover visible view 29 | '
' + 30 | // Provides positioning relative to (visible) text origin 31 | '
' + 32 | '
' + 33 | '
 
' + // Absolutely positioned blinky cursor 34 | '
' + // DIVs containing the selection and the actual code 35 | '
'; 36 | if (place.appendChild) place.appendChild(wrapper); else place(wrapper); 37 | // I've never seen more elegant code in my life. 38 | var inputDiv = wrapper.firstChild, input = inputDiv.firstChild, 39 | scroller = wrapper.lastChild, code = scroller.firstChild, 40 | mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild, 41 | lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild, 42 | cursor = measure.nextSibling, selectionDiv = cursor.nextSibling, 43 | lineDiv = selectionDiv.nextSibling; 44 | themeChanged(); keyMapChanged(); 45 | // Needed to hide big blue blinking cursor on Mobile Safari 46 | if (ios) input.style.width = "0px"; 47 | if (!webkit) lineSpace.draggable = true; 48 | lineSpace.style.outline = "none"; 49 | if (options.tabindex != null) input.tabIndex = options.tabindex; 50 | if (options.autofocus) focusInput(); 51 | if (!options.gutter && !options.lineNumbers) gutter.style.display = "none"; 52 | // Needed to handle Tab key in KHTML 53 | if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute"; 54 | 55 | // Check for problem with IE innerHTML not working when we have a 56 | // P (or similar) parent node. 57 | try { stringWidth("x"); } 58 | catch (e) { 59 | if (e.message.match(/runtime/i)) 60 | e = new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)"); 61 | throw e; 62 | } 63 | 64 | // Delayed object wrap timeouts, making sure only one is active. blinker holds an interval. 65 | var poll = new Delayed(), highlight = new Delayed(), blinker; 66 | 67 | // mode holds a mode API object. doc is the tree of Line objects, 68 | // work an array of lines that should be parsed, and history the 69 | // undo history (instance of History constructor). 70 | var mode, doc = new BranchChunk([new LeafChunk([new Line("")])]), work, focused; 71 | loadMode(); 72 | // The selection. These are always maintained to point at valid 73 | // positions. Inverted is used to remember that the user is 74 | // selecting bottom-to-top. 75 | var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false}; 76 | // Selection-related flags. shiftSelecting obviously tracks 77 | // whether the user is holding shift. 78 | var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText, 79 | overwrite = false, suppressEdits = false; 80 | // Variables used by startOperation/endOperation to track what 81 | // happened during the operation. 82 | var updateInput, userSelChange, changes, textChanged, selectionChanged, leaveInputAlone, 83 | gutterDirty, callbacks, maxLengthChanged; 84 | // Current visible range (may be bigger than the view window). 85 | var displayOffset = 0, showingFrom = 0, showingTo = 0, lastSizeC = 0; 86 | // bracketHighlighted is used to remember that a bracket has been 87 | // marked. 88 | var bracketHighlighted; 89 | // Tracks the maximum line length so that the horizontal scrollbar 90 | // can be kept static when scrolling. 91 | var maxLine = "", maxWidth; 92 | var tabCache = {}; 93 | 94 | // Initialize the content. 95 | operation(function(){setValue(options.value || ""); updateInput = false;})(); 96 | var history = new History(); 97 | 98 | // Register our event handlers. 99 | connect(scroller, "mousedown", operation(onMouseDown)); 100 | connect(scroller, "dblclick", operation(onDoubleClick)); 101 | connect(lineSpace, "selectstart", e_preventDefault); 102 | // Gecko browsers fire contextmenu *after* opening the menu, at 103 | // which point we can't mess with it anymore. Context menu is 104 | // handled in onMouseDown for Gecko. 105 | if (!gecko) connect(scroller, "contextmenu", onContextMenu); 106 | connect(scroller, "scroll", function() { 107 | lastScrollPos = scroller.scrollTop; 108 | updateDisplay([]); 109 | if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px"; 110 | if (options.onScroll) options.onScroll(instance); 111 | }); 112 | connect(window, "resize", function() {updateDisplay(true);}); 113 | connect(input, "keyup", operation(onKeyUp)); 114 | connect(input, "input", fastPoll); 115 | connect(input, "keydown", operation(onKeyDown)); 116 | connect(input, "keypress", operation(onKeyPress)); 117 | connect(input, "focus", onFocus); 118 | connect(input, "blur", onBlur); 119 | 120 | if (options.dragDrop) { 121 | connect(lineSpace, "dragstart", onDragStart); 122 | function drag_(e) { 123 | if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; 124 | e_stop(e); 125 | } 126 | connect(scroller, "dragenter", drag_); 127 | connect(scroller, "dragover", drag_); 128 | connect(scroller, "drop", operation(onDrop)); 129 | } 130 | connect(scroller, "paste", function(){focusInput(); fastPoll();}); 131 | connect(input, "paste", fastPoll); 132 | connect(input, "cut", operation(function(){ 133 | if (!options.readOnly) replaceSelection(""); 134 | })); 135 | 136 | // Needed to handle Tab key in KHTML 137 | if (khtml) connect(code, "mouseup", function() { 138 | if (document.activeElement == input) input.blur(); 139 | focusInput(); 140 | }); 141 | 142 | // IE throws unspecified error in certain cases, when 143 | // trying to access activeElement before onload 144 | var hasFocus; try { hasFocus = (document.activeElement == input); } catch(e) { } 145 | if (hasFocus || options.autofocus) setTimeout(onFocus, 20); 146 | else onBlur(); 147 | 148 | function isLine(l) {return l >= 0 && l < doc.size;} 149 | // The instance object that we'll return. Mostly calls out to 150 | // local functions in the CodeMirror function. Some do some extra 151 | // range checking and/or clipping. operation is used to wrap the 152 | // call so that changes it makes are tracked, and the display is 153 | // updated afterwards. 154 | var instance = wrapper.CodeMirror = { 155 | getValue: getValue, 156 | setValue: operation(setValue), 157 | getSelection: getSelection, 158 | replaceSelection: operation(replaceSelection), 159 | focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();}, 160 | setOption: function(option, value) { 161 | var oldVal = options[option]; 162 | options[option] = value; 163 | if (option == "mode" || option == "indentUnit") loadMode(); 164 | else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();} 165 | else if (option == "readOnly" && !value) {resetInput(true);} 166 | else if (option == "theme") themeChanged(); 167 | else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)(); 168 | else if (option == "tabSize") updateDisplay(true); 169 | else if (option == "keyMap") keyMapChanged(); 170 | if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") { 171 | gutterChanged(); 172 | updateDisplay(true); 173 | } 174 | }, 175 | getOption: function(option) {return options[option];}, 176 | undo: operation(undo), 177 | redo: operation(redo), 178 | indentLine: operation(function(n, dir) { 179 | if (typeof dir != "string") { 180 | if (dir == null) dir = options.smartIndent ? "smart" : "prev"; 181 | else dir = dir ? "add" : "subtract"; 182 | } 183 | if (isLine(n)) indentLine(n, dir); 184 | }), 185 | indentSelection: operation(indentSelected), 186 | historySize: function() {return {undo: history.done.length, redo: history.undone.length};}, 187 | clearHistory: function() {history = new History();}, 188 | matchBrackets: operation(function(){matchBrackets(true);}), 189 | getTokenAt: operation(function(pos) { 190 | pos = clipPos(pos); 191 | return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), pos.ch); 192 | }), 193 | getStateAfter: function(line) { 194 | line = clipLine(line == null ? doc.size - 1: line); 195 | return getStateBefore(line + 1); 196 | }, 197 | cursorCoords: function(start, mode) { 198 | if (start == null) start = sel.inverted; 199 | return this.charCoords(start ? sel.from : sel.to, mode); 200 | }, 201 | charCoords: function(pos, mode) { 202 | pos = clipPos(pos); 203 | if (mode == "local") return localCoords(pos, false); 204 | if (mode == "div") return localCoords(pos, true); 205 | return pageCoords(pos); 206 | }, 207 | coordsChar: function(coords) { 208 | var off = eltOffset(lineSpace); 209 | return coordsChar(coords.x - off.left, coords.y - off.top); 210 | }, 211 | markText: operation(markText), 212 | setBookmark: setBookmark, 213 | findMarksAt: findMarksAt, 214 | setMarker: operation(addGutterMarker), 215 | clearMarker: operation(removeGutterMarker), 216 | setLineClass: operation(setLineClass), 217 | hideLine: operation(function(h) {return setLineHidden(h, true);}), 218 | showLine: operation(function(h) {return setLineHidden(h, false);}), 219 | onDeleteLine: function(line, f) { 220 | if (typeof line == "number") { 221 | if (!isLine(line)) return null; 222 | line = getLine(line); 223 | } 224 | (line.handlers || (line.handlers = [])).push(f); 225 | return line; 226 | }, 227 | lineInfo: lineInfo, 228 | addWidget: function(pos, node, scroll, vert, horiz) { 229 | pos = localCoords(clipPos(pos)); 230 | var top = pos.yBot, left = pos.x; 231 | node.style.position = "absolute"; 232 | code.appendChild(node); 233 | if (vert == "over") top = pos.y; 234 | else if (vert == "near") { 235 | var vspace = Math.max(scroller.offsetHeight, doc.height * textHeight()), 236 | hspace = Math.max(code.clientWidth, lineSpace.clientWidth) - paddingLeft(); 237 | if (pos.yBot + node.offsetHeight > vspace && pos.y > node.offsetHeight) 238 | top = pos.y - node.offsetHeight; 239 | if (left + node.offsetWidth > hspace) 240 | left = hspace - node.offsetWidth; 241 | } 242 | node.style.top = (top + paddingTop()) + "px"; 243 | node.style.left = node.style.right = ""; 244 | if (horiz == "right") { 245 | left = code.clientWidth - node.offsetWidth; 246 | node.style.right = "0px"; 247 | } else { 248 | if (horiz == "left") left = 0; 249 | else if (horiz == "middle") left = (code.clientWidth - node.offsetWidth) / 2; 250 | node.style.left = (left + paddingLeft()) + "px"; 251 | } 252 | if (scroll) 253 | scrollIntoView(left, top, left + node.offsetWidth, top + node.offsetHeight); 254 | }, 255 | 256 | lineCount: function() {return doc.size;}, 257 | clipPos: clipPos, 258 | getCursor: function(start) { 259 | if (start == null) start = sel.inverted; 260 | return copyPos(start ? sel.from : sel.to); 261 | }, 262 | somethingSelected: function() {return !posEq(sel.from, sel.to);}, 263 | setCursor: operation(function(line, ch, user) { 264 | if (ch == null && typeof line.line == "number") setCursor(line.line, line.ch, user); 265 | else setCursor(line, ch, user); 266 | }), 267 | setSelection: operation(function(from, to, user) { 268 | (user ? setSelectionUser : setSelection)(clipPos(from), clipPos(to || from)); 269 | }), 270 | getLine: function(line) {if (isLine(line)) return getLine(line).text;}, 271 | getLineHandle: function(line) {if (isLine(line)) return getLine(line);}, 272 | setLine: operation(function(line, text) { 273 | if (isLine(line)) replaceRange(text, {line: line, ch: 0}, {line: line, ch: getLine(line).text.length}); 274 | }), 275 | removeLine: operation(function(line) { 276 | if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0})); 277 | }), 278 | replaceRange: operation(replaceRange), 279 | getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));}, 280 | 281 | triggerOnKeyDown: operation(onKeyDown), 282 | execCommand: function(cmd) {return commands[cmd](instance);}, 283 | // Stuff used by commands, probably not much use to outside code. 284 | moveH: operation(moveH), 285 | deleteH: operation(deleteH), 286 | moveV: operation(moveV), 287 | toggleOverwrite: function() { 288 | if(overwrite){ 289 | overwrite = false; 290 | cursor.className = cursor.className.replace(" CodeMirror-overwrite", ""); 291 | } else { 292 | overwrite = true; 293 | cursor.className += " CodeMirror-overwrite"; 294 | } 295 | }, 296 | 297 | posFromIndex: function(off) { 298 | var lineNo = 0, ch; 299 | doc.iter(0, doc.size, function(line) { 300 | var sz = line.text.length + 1; 301 | if (sz > off) { ch = off; return true; } 302 | off -= sz; 303 | ++lineNo; 304 | }); 305 | return clipPos({line: lineNo, ch: ch}); 306 | }, 307 | indexFromPos: function (coords) { 308 | if (coords.line < 0 || coords.ch < 0) return 0; 309 | var index = coords.ch; 310 | doc.iter(0, coords.line, function (line) { 311 | index += line.text.length + 1; 312 | }); 313 | return index; 314 | }, 315 | scrollTo: function(x, y) { 316 | if (x != null) scroller.scrollLeft = x; 317 | if (y != null) scroller.scrollTop = y; 318 | updateDisplay([]); 319 | }, 320 | 321 | operation: function(f){return operation(f)();}, 322 | compoundChange: function(f){return compoundChange(f);}, 323 | refresh: function(){ 324 | updateDisplay(true); 325 | if (scroller.scrollHeight > lastScrollPos) 326 | scroller.scrollTop = lastScrollPos; 327 | }, 328 | getInputField: function(){return input;}, 329 | getWrapperElement: function(){return wrapper;}, 330 | getScrollerElement: function(){return scroller;}, 331 | getGutterElement: function(){return gutter;} 332 | }; 333 | 334 | function getLine(n) { return getLineAt(doc, n); } 335 | function updateLineHeight(line, height) { 336 | gutterDirty = true; 337 | var diff = height - line.height; 338 | for (var n = line; n; n = n.parent) n.height += diff; 339 | } 340 | 341 | function setValue(code) { 342 | var top = {line: 0, ch: 0}; 343 | updateLines(top, {line: doc.size - 1, ch: getLine(doc.size-1).text.length}, 344 | splitLines(code), top, top); 345 | updateInput = true; 346 | } 347 | function getValue() { 348 | var text = []; 349 | doc.iter(0, doc.size, function(line) { text.push(line.text); }); 350 | return text.join("\n"); 351 | } 352 | 353 | function onMouseDown(e) { 354 | setShift(e_prop(e, "shiftKey")); 355 | // Check whether this is a click in a widget 356 | for (var n = e_target(e); n != wrapper; n = n.parentNode) 357 | if (n.parentNode == code && n != mover) return; 358 | 359 | // See if this is a click in the gutter 360 | for (var n = e_target(e); n != wrapper; n = n.parentNode) 361 | if (n.parentNode == gutterText) { 362 | if (options.onGutterClick) 363 | options.onGutterClick(instance, indexOf(gutterText.childNodes, n) + showingFrom, e); 364 | return e_preventDefault(e); 365 | } 366 | 367 | var start = posFromMouse(e); 368 | 369 | switch (e_button(e)) { 370 | case 3: 371 | if (gecko && !mac) onContextMenu(e); 372 | return; 373 | case 2: 374 | if (start) setCursor(start.line, start.ch, true); 375 | setTimeout(focusInput, 20); 376 | return; 377 | } 378 | // For button 1, if it was clicked inside the editor 379 | // (posFromMouse returning non-null), we have to adjust the 380 | // selection. 381 | if (!start) {if (e_target(e) == scroller) e_preventDefault(e); return;} 382 | 383 | if (!focused) onFocus(); 384 | 385 | var now = +new Date; 386 | if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) { 387 | e_preventDefault(e); 388 | setTimeout(focusInput, 20); 389 | return selectLine(start.line); 390 | } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) { 391 | lastDoubleClick = {time: now, pos: start}; 392 | e_preventDefault(e); 393 | return selectWordAt(start); 394 | } else { lastClick = {time: now, pos: start}; } 395 | 396 | var last = start, going; 397 | if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) && 398 | !posLess(start, sel.from) && !posLess(sel.to, start)) { 399 | // Let the drag handler handle this. 400 | if (webkit) lineSpace.draggable = true; 401 | function dragEnd(e2) { 402 | if (webkit) lineSpace.draggable = false; 403 | draggingText = false; 404 | up(); drop(); 405 | if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) { 406 | e_preventDefault(e2); 407 | setCursor(start.line, start.ch, true); 408 | focusInput(); 409 | } 410 | } 411 | var up = connect(document, "mouseup", operation(dragEnd), true); 412 | var drop = connect(scroller, "drop", operation(dragEnd), true); 413 | draggingText = true; 414 | // IE's approach to draggable 415 | if (lineSpace.dragDrop) lineSpace.dragDrop(); 416 | return; 417 | } 418 | e_preventDefault(e); 419 | setCursor(start.line, start.ch, true); 420 | 421 | function extend(e) { 422 | var cur = posFromMouse(e, true); 423 | if (cur && !posEq(cur, last)) { 424 | if (!focused) onFocus(); 425 | last = cur; 426 | setSelectionUser(start, cur); 427 | updateInput = false; 428 | var visible = visibleLines(); 429 | if (cur.line >= visible.to || cur.line < visible.from) 430 | going = setTimeout(operation(function(){extend(e);}), 150); 431 | } 432 | } 433 | 434 | function done(e) { 435 | clearTimeout(going); 436 | var cur = posFromMouse(e); 437 | if (cur) setSelectionUser(start, cur); 438 | e_preventDefault(e); 439 | focusInput(); 440 | updateInput = true; 441 | move(); up(); 442 | } 443 | var move = connect(document, "mousemove", operation(function(e) { 444 | clearTimeout(going); 445 | e_preventDefault(e); 446 | if (!ie && !e_button(e)) done(e); 447 | else extend(e); 448 | }), true); 449 | var up = connect(document, "mouseup", operation(done), true); 450 | } 451 | function onDoubleClick(e) { 452 | for (var n = e_target(e); n != wrapper; n = n.parentNode) 453 | if (n.parentNode == gutterText) return e_preventDefault(e); 454 | var start = posFromMouse(e); 455 | if (!start) return; 456 | lastDoubleClick = {time: +new Date, pos: start}; 457 | e_preventDefault(e); 458 | selectWordAt(start); 459 | } 460 | function onDrop(e) { 461 | if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return; 462 | e.preventDefault(); 463 | var pos = posFromMouse(e, true), files = e.dataTransfer.files; 464 | if (!pos || options.readOnly) return; 465 | if (files && files.length && window.FileReader && window.File) { 466 | function loadFile(file, i) { 467 | var reader = new FileReader; 468 | reader.onload = function() { 469 | text[i] = reader.result; 470 | if (++read == n) { 471 | pos = clipPos(pos); 472 | operation(function() { 473 | var end = replaceRange(text.join(""), pos, pos); 474 | setSelectionUser(pos, end); 475 | })(); 476 | } 477 | }; 478 | reader.readAsText(file); 479 | } 480 | var n = files.length, text = Array(n), read = 0; 481 | for (var i = 0; i < n; ++i) loadFile(files[i], i); 482 | } 483 | else { 484 | try { 485 | var text = e.dataTransfer.getData("Text"); 486 | if (text) { 487 | compoundChange(function() { 488 | var curFrom = sel.from, curTo = sel.to; 489 | setSelectionUser(pos, pos); 490 | if (draggingText) replaceRange("", curFrom, curTo); 491 | replaceSelection(text); 492 | focusInput(); 493 | }); 494 | } 495 | } 496 | catch(e){} 497 | } 498 | } 499 | function onDragStart(e) { 500 | var txt = getSelection(); 501 | e.dataTransfer.setData("Text", txt); 502 | 503 | // Use dummy image instead of default browsers image. 504 | if (gecko || chrome) { 505 | var img = document.createElement('img'); 506 | img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image 507 | e.dataTransfer.setDragImage(img, 0, 0); 508 | } 509 | } 510 | 511 | function doHandleBinding(bound, dropShift) { 512 | if (typeof bound == "string") { 513 | bound = commands[bound]; 514 | if (!bound) return false; 515 | } 516 | var prevShift = shiftSelecting; 517 | try { 518 | if (options.readOnly) suppressEdits = true; 519 | if (dropShift) shiftSelecting = null; 520 | bound(instance); 521 | } catch(e) { 522 | if (e != Pass) throw e; 523 | return false; 524 | } finally { 525 | shiftSelecting = prevShift; 526 | suppressEdits = false; 527 | } 528 | return true; 529 | } 530 | function handleKeyBinding(e) { 531 | // Handle auto keymap transitions 532 | var startMap = getKeyMap(options.keyMap), next = startMap.auto; 533 | clearTimeout(maybeTransition); 534 | if (next && !isModifierKey(e)) maybeTransition = setTimeout(function() { 535 | if (getKeyMap(options.keyMap) == startMap) { 536 | options.keyMap = (next.call ? next.call(null, instance) : next); 537 | } 538 | }, 50); 539 | 540 | var name = keyNames[e_prop(e, "keyCode")], handled = false; 541 | if (name == null || e.altGraphKey) return false; 542 | if (e_prop(e, "altKey")) name = "Alt-" + name; 543 | if (e_prop(e, "ctrlKey")) name = "Ctrl-" + name; 544 | if (e_prop(e, "metaKey")) name = "Cmd-" + name; 545 | 546 | var stopped = false; 547 | function stop() { stopped = true; } 548 | 549 | if (e_prop(e, "shiftKey")) { 550 | handled = lookupKey("Shift-" + name, options.extraKeys, options.keyMap, 551 | function(b) {return doHandleBinding(b, true);}, stop) 552 | || lookupKey(name, options.extraKeys, options.keyMap, function(b) { 553 | if (typeof b == "string" && /^go[A-Z]/.test(b)) return doHandleBinding(b); 554 | }, stop); 555 | } else { 556 | handled = lookupKey(name, options.extraKeys, options.keyMap, doHandleBinding, stop); 557 | } 558 | if (stopped) handled = false; 559 | if (handled) { 560 | e_preventDefault(e); 561 | restartBlink(); 562 | if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; } 563 | } 564 | return handled; 565 | } 566 | function handleCharBinding(e, ch) { 567 | var handled = lookupKey("'" + ch + "'", options.extraKeys, 568 | options.keyMap, function(b) { return doHandleBinding(b, true); }); 569 | if (handled) { 570 | e_preventDefault(e); 571 | restartBlink(); 572 | } 573 | return handled; 574 | } 575 | 576 | var lastStoppedKey = null, maybeTransition; 577 | function onKeyDown(e) { 578 | if (!focused) onFocus(); 579 | if (ie && e.keyCode == 27) { e.returnValue = false; } 580 | if (pollingFast) { if (readInput()) pollingFast = false; } 581 | if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; 582 | var code = e_prop(e, "keyCode"); 583 | // IE does strange things with escape. 584 | setShift(code == 16 || e_prop(e, "shiftKey")); 585 | // First give onKeyEvent option a chance to handle this. 586 | var handled = handleKeyBinding(e); 587 | if (window.opera) { 588 | lastStoppedKey = handled ? code : null; 589 | // Opera has no cut event... we try to at least catch the key combo 590 | if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey")) 591 | replaceSelection(""); 592 | } 593 | } 594 | function onKeyPress(e) { 595 | if (pollingFast) readInput(); 596 | if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; 597 | var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode"); 598 | if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;} 599 | if (((window.opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return; 600 | var ch = String.fromCharCode(charCode == null ? keyCode : charCode); 601 | if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) { 602 | if (mode.electricChars.indexOf(ch) > -1) 603 | setTimeout(operation(function() {indentLine(sel.to.line, "smart");}), 75); 604 | } 605 | if (handleCharBinding(e, ch)) return; 606 | fastPoll(); 607 | } 608 | function onKeyUp(e) { 609 | if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return; 610 | if (e_prop(e, "keyCode") == 16) shiftSelecting = null; 611 | } 612 | 613 | function onFocus() { 614 | if (options.readOnly == "nocursor") return; 615 | if (!focused) { 616 | if (options.onFocus) options.onFocus(instance); 617 | focused = true; 618 | if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1) 619 | wrapper.className += " CodeMirror-focused"; 620 | if (!leaveInputAlone) resetInput(true); 621 | } 622 | slowPoll(); 623 | restartBlink(); 624 | } 625 | function onBlur() { 626 | if (focused) { 627 | if (options.onBlur) options.onBlur(instance); 628 | focused = false; 629 | if (bracketHighlighted) 630 | operation(function(){ 631 | if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; } 632 | })(); 633 | wrapper.className = wrapper.className.replace(" CodeMirror-focused", ""); 634 | } 635 | clearInterval(blinker); 636 | setTimeout(function() {if (!focused) shiftSelecting = null;}, 150); 637 | } 638 | 639 | // Replace the range from from to to by the strings in newText. 640 | // Afterwards, set the selection to selFrom, selTo. 641 | function updateLines(from, to, newText, selFrom, selTo) { 642 | if (suppressEdits) return; 643 | if (history) { 644 | var old = []; 645 | doc.iter(from.line, to.line + 1, function(line) { old.push(line.text); }); 646 | history.addChange(from.line, newText.length, old); 647 | while (history.done.length > options.undoDepth) history.done.shift(); 648 | } 649 | updateLinesNoUndo(from, to, newText, selFrom, selTo); 650 | } 651 | function unredoHelper(from, to) { 652 | if (!from.length) return; 653 | var set = from.pop(), out = []; 654 | for (var i = set.length - 1; i >= 0; i -= 1) { 655 | var change = set[i]; 656 | var replaced = [], end = change.start + change.added; 657 | doc.iter(change.start, end, function(line) { replaced.push(line.text); }); 658 | out.push({start: change.start, added: change.old.length, old: replaced}); 659 | var pos = clipPos({line: change.start + change.old.length - 1, 660 | ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])}); 661 | updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos); 662 | } 663 | updateInput = true; 664 | to.push(out); 665 | } 666 | function undo() {unredoHelper(history.done, history.undone);} 667 | function redo() {unredoHelper(history.undone, history.done);} 668 | 669 | function updateLinesNoUndo(from, to, newText, selFrom, selTo) { 670 | if (suppressEdits) return; 671 | var recomputeMaxLength = false, maxLineLength = maxLine.length; 672 | if (!options.lineWrapping) 673 | doc.iter(from.line, to.line + 1, function(line) { 674 | if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;} 675 | }); 676 | if (from.line != to.line || newText.length > 1) gutterDirty = true; 677 | 678 | var nlines = to.line - from.line, firstLine = getLine(from.line), lastLine = getLine(to.line); 679 | // First adjust the line structure, taking some care to leave highlighting intact. 680 | if (from.ch == 0 && to.ch == 0 && newText[newText.length - 1] == "") { 681 | // This is a whole-line replace. Treated specially to make 682 | // sure line objects move the way they are supposed to. 683 | var added = [], prevLine = null; 684 | if (from.line) { 685 | prevLine = getLine(from.line - 1); 686 | prevLine.fixMarkEnds(lastLine); 687 | } else lastLine.fixMarkStarts(); 688 | for (var i = 0, e = newText.length - 1; i < e; ++i) 689 | added.push(Line.inheritMarks(newText[i], prevLine)); 690 | if (nlines) doc.remove(from.line, nlines, callbacks); 691 | if (added.length) doc.insert(from.line, added); 692 | } else if (firstLine == lastLine) { 693 | if (newText.length == 1) 694 | firstLine.replace(from.ch, to.ch, newText[0]); 695 | else { 696 | lastLine = firstLine.split(to.ch, newText[newText.length-1]); 697 | firstLine.replace(from.ch, null, newText[0]); 698 | firstLine.fixMarkEnds(lastLine); 699 | var added = []; 700 | for (var i = 1, e = newText.length - 1; i < e; ++i) 701 | added.push(Line.inheritMarks(newText[i], firstLine)); 702 | added.push(lastLine); 703 | doc.insert(from.line + 1, added); 704 | } 705 | } else if (newText.length == 1) { 706 | firstLine.replace(from.ch, null, newText[0]); 707 | lastLine.replace(null, to.ch, ""); 708 | firstLine.append(lastLine); 709 | doc.remove(from.line + 1, nlines, callbacks); 710 | } else { 711 | var added = []; 712 | firstLine.replace(from.ch, null, newText[0]); 713 | lastLine.replace(null, to.ch, newText[newText.length-1]); 714 | firstLine.fixMarkEnds(lastLine); 715 | for (var i = 1, e = newText.length - 1; i < e; ++i) 716 | added.push(Line.inheritMarks(newText[i], firstLine)); 717 | if (nlines > 1) doc.remove(from.line + 1, nlines - 1, callbacks); 718 | doc.insert(from.line + 1, added); 719 | } 720 | if (options.lineWrapping) { 721 | var perLine = Math.max(5, scroller.clientWidth / charWidth() - 3); 722 | doc.iter(from.line, from.line + newText.length, function(line) { 723 | if (line.hidden) return; 724 | var guess = Math.ceil(line.text.length / perLine) || 1; 725 | if (guess != line.height) updateLineHeight(line, guess); 726 | }); 727 | } else { 728 | doc.iter(from.line, from.line + newText.length, function(line) { 729 | var l = line.text; 730 | if (!line.hidden && l.length > maxLineLength) { 731 | maxLine = l; maxLineLength = l.length; maxWidth = null; 732 | recomputeMaxLength = false; 733 | } 734 | }); 735 | if (recomputeMaxLength) maxLengthChanged = true; 736 | } 737 | 738 | // Add these lines to the work array, so that they will be 739 | // highlighted. Adjust work lines if lines were added/removed. 740 | var newWork = [], lendiff = newText.length - nlines - 1; 741 | for (var i = 0, l = work.length; i < l; ++i) { 742 | var task = work[i]; 743 | if (task < from.line) newWork.push(task); 744 | else if (task > to.line) newWork.push(task + lendiff); 745 | } 746 | var hlEnd = from.line + Math.min(newText.length, 500); 747 | highlightLines(from.line, hlEnd); 748 | newWork.push(hlEnd); 749 | work = newWork; 750 | startWorker(100); 751 | // Remember that these lines changed, for updating the display 752 | changes.push({from: from.line, to: to.line + 1, diff: lendiff}); 753 | var changeObj = {from: from, to: to, text: newText}; 754 | if (textChanged) { 755 | for (var cur = textChanged; cur.next; cur = cur.next) {} 756 | cur.next = changeObj; 757 | } else textChanged = changeObj; 758 | 759 | // Update the selection 760 | function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;} 761 | setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line)); 762 | 763 | // Make sure the scroll-size div has the correct height. 764 | if (scroller.clientHeight) 765 | code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px"; 766 | } 767 | 768 | function computeMaxLength() { 769 | var maxLineLength = 0; 770 | maxLine = ""; maxWidth = null; 771 | doc.iter(0, doc.size, function(line) { 772 | var l = line.text; 773 | if (!line.hidden && l.length > maxLineLength) { 774 | maxLineLength = l.length; maxLine = l; 775 | } 776 | }); 777 | maxLengthChanged = false; 778 | } 779 | 780 | function replaceRange(code, from, to) { 781 | from = clipPos(from); 782 | if (!to) to = from; else to = clipPos(to); 783 | code = splitLines(code); 784 | function adjustPos(pos) { 785 | if (posLess(pos, from)) return pos; 786 | if (!posLess(to, pos)) return end; 787 | var line = pos.line + code.length - (to.line - from.line) - 1; 788 | var ch = pos.ch; 789 | if (pos.line == to.line) 790 | ch += code[code.length-1].length - (to.ch - (to.line == from.line ? from.ch : 0)); 791 | return {line: line, ch: ch}; 792 | } 793 | var end; 794 | replaceRange1(code, from, to, function(end1) { 795 | end = end1; 796 | return {from: adjustPos(sel.from), to: adjustPos(sel.to)}; 797 | }); 798 | return end; 799 | } 800 | function replaceSelection(code, collapse) { 801 | replaceRange1(splitLines(code), sel.from, sel.to, function(end) { 802 | if (collapse == "end") return {from: end, to: end}; 803 | else if (collapse == "start") return {from: sel.from, to: sel.from}; 804 | else return {from: sel.from, to: end}; 805 | }); 806 | } 807 | function replaceRange1(code, from, to, computeSel) { 808 | var endch = code.length == 1 ? code[0].length + from.ch : code[code.length-1].length; 809 | var newSel = computeSel({line: from.line + code.length - 1, ch: endch}); 810 | updateLines(from, to, code, newSel.from, newSel.to); 811 | } 812 | 813 | function getRange(from, to) { 814 | var l1 = from.line, l2 = to.line; 815 | if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch); 816 | var code = [getLine(l1).text.slice(from.ch)]; 817 | doc.iter(l1 + 1, l2, function(line) { code.push(line.text); }); 818 | code.push(getLine(l2).text.slice(0, to.ch)); 819 | return code.join("\n"); 820 | } 821 | function getSelection() { 822 | return getRange(sel.from, sel.to); 823 | } 824 | 825 | var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll 826 | function slowPoll() { 827 | if (pollingFast) return; 828 | poll.set(options.pollInterval, function() { 829 | startOperation(); 830 | readInput(); 831 | if (focused) slowPoll(); 832 | endOperation(); 833 | }); 834 | } 835 | function fastPoll() { 836 | var missed = false; 837 | pollingFast = true; 838 | function p() { 839 | startOperation(); 840 | var changed = readInput(); 841 | if (!changed && !missed) {missed = true; poll.set(60, p);} 842 | else {pollingFast = false; slowPoll();} 843 | endOperation(); 844 | } 845 | poll.set(20, p); 846 | } 847 | 848 | // Previnput is a hack to work with IME. If we reset the textarea 849 | // on every change, that breaks IME. So we look for changes 850 | // compared to the previous content instead. (Modern browsers have 851 | // events that indicate IME taking place, but these are not widely 852 | // supported or compatible enough yet to rely on.) 853 | var prevInput = ""; 854 | function readInput() { 855 | if (leaveInputAlone || !focused || hasSelection(input) || options.readOnly) return false; 856 | var text = input.value; 857 | if (text == prevInput) return false; 858 | shiftSelecting = null; 859 | var same = 0, l = Math.min(prevInput.length, text.length); 860 | while (same < l && prevInput[same] == text[same]) ++same; 861 | if (same < prevInput.length) 862 | sel.from = {line: sel.from.line, ch: sel.from.ch - (prevInput.length - same)}; 863 | else if (overwrite && posEq(sel.from, sel.to)) 864 | sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))}; 865 | replaceSelection(text.slice(same), "end"); 866 | if (text.length > 1000) { input.value = prevInput = ""; } 867 | else prevInput = text; 868 | return true; 869 | } 870 | function resetInput(user) { 871 | if (!posEq(sel.from, sel.to)) { 872 | prevInput = ""; 873 | input.value = getSelection(); 874 | selectInput(input); 875 | } else if (user) prevInput = input.value = ""; 876 | } 877 | 878 | function focusInput() { 879 | if (options.readOnly != "nocursor") input.focus(); 880 | } 881 | 882 | function scrollEditorIntoView() { 883 | if (!cursor.getBoundingClientRect) return; 884 | var rect = cursor.getBoundingClientRect(); 885 | // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden 886 | if (ie && rect.top == rect.bottom) return; 887 | var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight); 888 | if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView(); 889 | } 890 | function scrollCursorIntoView() { 891 | var cursor = localCoords(sel.inverted ? sel.from : sel.to); 892 | var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x; 893 | return scrollIntoView(x, cursor.y, x, cursor.yBot); 894 | } 895 | function scrollIntoView(x1, y1, x2, y2) { 896 | var pl = paddingLeft(), pt = paddingTop(); 897 | y1 += pt; y2 += pt; x1 += pl; x2 += pl; 898 | var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true; 899 | if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1); scrolled = true;} 900 | else if (y2 > screentop + screen) {scroller.scrollTop = y2 - screen; scrolled = true;} 901 | 902 | var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft; 903 | var gutterw = options.fixedGutter ? gutter.clientWidth : 0; 904 | var atLeft = x1 < gutterw + pl + 10; 905 | if (x1 < screenleft + gutterw || atLeft) { 906 | if (atLeft) x1 = 0; 907 | scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw); 908 | scrolled = true; 909 | } 910 | else if (x2 > screenw + screenleft - 3) { 911 | scroller.scrollLeft = x2 + 10 - screenw; 912 | scrolled = true; 913 | if (x2 > code.clientWidth) result = false; 914 | } 915 | if (scrolled && options.onScroll) options.onScroll(instance); 916 | return result; 917 | } 918 | 919 | function visibleLines() { 920 | var lh = textHeight(), top = scroller.scrollTop - paddingTop(); 921 | var fromHeight = Math.max(0, Math.floor(top / lh)); 922 | var toHeight = Math.ceil((top + scroller.clientHeight) / lh); 923 | return {from: lineAtHeight(doc, fromHeight), 924 | to: lineAtHeight(doc, toHeight)}; 925 | } 926 | // Uses a set of changes plus the current scroll position to 927 | // determine which DOM updates have to be made, and makes the 928 | // updates. 929 | function updateDisplay(changes, suppressCallback) { 930 | if (!scroller.clientWidth) { 931 | showingFrom = showingTo = displayOffset = 0; 932 | return; 933 | } 934 | // Compute the new visible window 935 | var visible = visibleLines(); 936 | // Bail out if the visible area is already rendered and nothing changed. 937 | if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return; 938 | var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100); 939 | if (showingFrom < from && from - showingFrom < 20) from = showingFrom; 940 | if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo); 941 | 942 | // Create a range of theoretically intact lines, and punch holes 943 | // in that using the change info. 944 | var intact = changes === true ? [] : 945 | computeIntact([{from: showingFrom, to: showingTo, domStart: 0}], changes); 946 | // Clip off the parts that won't be visible 947 | var intactLines = 0; 948 | for (var i = 0; i < intact.length; ++i) { 949 | var range = intact[i]; 950 | if (range.from < from) {range.domStart += (from - range.from); range.from = from;} 951 | if (range.to > to) range.to = to; 952 | if (range.from >= range.to) intact.splice(i--, 1); 953 | else intactLines += range.to - range.from; 954 | } 955 | if (intactLines == to - from && from == showingFrom && to == showingTo) return; 956 | intact.sort(function(a, b) {return a.domStart - b.domStart;}); 957 | 958 | var th = textHeight(), gutterDisplay = gutter.style.display; 959 | lineDiv.style.display = "none"; 960 | patchDisplay(from, to, intact); 961 | lineDiv.style.display = gutter.style.display = ""; 962 | 963 | // Position the mover div to align with the lines it's supposed 964 | // to be showing (which will cover the visible display) 965 | var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th; 966 | // This is just a bogus formula that detects when the editor is 967 | // resized or the font size changes. 968 | if (different) lastSizeC = scroller.clientHeight + th; 969 | showingFrom = from; showingTo = to; 970 | displayOffset = heightAtLine(doc, from); 971 | mover.style.top = (displayOffset * th) + "px"; 972 | if (scroller.clientHeight) 973 | code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; 974 | 975 | // Since this is all rather error prone, it is honoured with the 976 | // only assertion in the whole file. 977 | if (lineDiv.childNodes.length != showingTo - showingFrom) 978 | throw new Error("BAD PATCH! " + JSON.stringify(intact) + " size=" + (showingTo - showingFrom) + 979 | " nodes=" + lineDiv.childNodes.length); 980 | 981 | function checkHeights() { 982 | maxWidth = scroller.clientWidth; 983 | var curNode = lineDiv.firstChild, heightChanged = false; 984 | doc.iter(showingFrom, showingTo, function(line) { 985 | if (!line.hidden) { 986 | var height = Math.round(curNode.offsetHeight / th) || 1; 987 | if (line.height != height) { 988 | updateLineHeight(line, height); 989 | gutterDirty = heightChanged = true; 990 | } 991 | } 992 | curNode = curNode.nextSibling; 993 | }); 994 | if (heightChanged) 995 | code.style.height = (doc.height * th + 2 * paddingTop()) + "px"; 996 | return heightChanged; 997 | } 998 | 999 | if (options.lineWrapping) { 1000 | checkHeights(); 1001 | } else { 1002 | if (maxWidth == null) maxWidth = stringWidth(maxLine); 1003 | if (maxWidth > scroller.clientWidth) { 1004 | lineSpace.style.width = maxWidth + "px"; 1005 | // Needed to prevent odd wrapping/hiding of widgets placed in here. 1006 | code.style.width = ""; 1007 | code.style.width = scroller.scrollWidth + "px"; 1008 | } else { 1009 | lineSpace.style.width = code.style.width = ""; 1010 | } 1011 | } 1012 | 1013 | gutter.style.display = gutterDisplay; 1014 | if (different || gutterDirty) { 1015 | // If the gutter grew in size, re-check heights. If those changed, re-draw gutter. 1016 | updateGutter() && options.lineWrapping && checkHeights() && updateGutter(); 1017 | } 1018 | updateSelection(); 1019 | if (!suppressCallback && options.onUpdate) options.onUpdate(instance); 1020 | return true; 1021 | } 1022 | 1023 | function computeIntact(intact, changes) { 1024 | for (var i = 0, l = changes.length || 0; i < l; ++i) { 1025 | var change = changes[i], intact2 = [], diff = change.diff || 0; 1026 | for (var j = 0, l2 = intact.length; j < l2; ++j) { 1027 | var range = intact[j]; 1028 | if (change.to <= range.from && change.diff) 1029 | intact2.push({from: range.from + diff, to: range.to + diff, 1030 | domStart: range.domStart}); 1031 | else if (change.to <= range.from || change.from >= range.to) 1032 | intact2.push(range); 1033 | else { 1034 | if (change.from > range.from) 1035 | intact2.push({from: range.from, to: change.from, domStart: range.domStart}); 1036 | if (change.to < range.to) 1037 | intact2.push({from: change.to + diff, to: range.to + diff, 1038 | domStart: range.domStart + (change.to - range.from)}); 1039 | } 1040 | } 1041 | intact = intact2; 1042 | } 1043 | return intact; 1044 | } 1045 | 1046 | function patchDisplay(from, to, intact) { 1047 | // The first pass removes the DOM nodes that aren't intact. 1048 | if (!intact.length) lineDiv.innerHTML = ""; 1049 | else { 1050 | function killNode(node) { 1051 | var tmp = node.nextSibling; 1052 | node.parentNode.removeChild(node); 1053 | return tmp; 1054 | } 1055 | var domPos = 0, curNode = lineDiv.firstChild, n; 1056 | for (var i = 0; i < intact.length; ++i) { 1057 | var cur = intact[i]; 1058 | while (cur.domStart > domPos) {curNode = killNode(curNode); domPos++;} 1059 | for (var j = 0, e = cur.to - cur.from; j < e; ++j) {curNode = curNode.nextSibling; domPos++;} 1060 | } 1061 | while (curNode) curNode = killNode(curNode); 1062 | } 1063 | // This pass fills in the lines that actually changed. 1064 | var nextIntact = intact.shift(), curNode = lineDiv.firstChild, j = from; 1065 | var scratch = document.createElement("div"); 1066 | doc.iter(from, to, function(line) { 1067 | if (nextIntact && nextIntact.to == j) nextIntact = intact.shift(); 1068 | if (!nextIntact || nextIntact.from > j) { 1069 | if (line.hidden) var html = scratch.innerHTML = "
";
1070 |           else {
1071 |             var html = ''
1072 |               + line.getHTML(makeTab) + '';
1073 |             // Kludge to make sure the styled element lies behind the selection (by z-index)
1074 |             if (line.bgClassName)
1075 |               html = '
 
' + html + "
"; 1077 | } 1078 | scratch.innerHTML = html; 1079 | lineDiv.insertBefore(scratch.firstChild, curNode); 1080 | } else { 1081 | curNode = curNode.nextSibling; 1082 | } 1083 | ++j; 1084 | }); 1085 | } 1086 | 1087 | function updateGutter() { 1088 | if (!options.gutter && !options.lineNumbers) return; 1089 | var hText = mover.offsetHeight, hEditor = scroller.clientHeight; 1090 | gutter.style.height = (hText - hEditor < 2 ? hEditor : hText) + "px"; 1091 | var html = [], i = showingFrom, normalNode; 1092 | doc.iter(showingFrom, Math.max(showingTo, showingFrom + 1), function(line) { 1093 | if (line.hidden) { 1094 | html.push("
");
1095 |         } else {
1096 |           var marker = line.gutterMarker;
1097 |           var text = options.lineNumbers ? i + options.firstLineNumber : null;
1098 |           if (marker && marker.text)
1099 |             text = marker.text.replace("%N%", text != null ? text : "");
1100 |           else if (text == null)
1101 |             text = "\u00a0";
1102 |           html.push((marker && marker.style ? '
' : "
"), text);
1103 |           for (var j = 1; j < line.height; ++j) html.push("
 "); 1104 | html.push("
"); 1105 | if (!marker) normalNode = i; 1106 | } 1107 | ++i; 1108 | }); 1109 | gutter.style.display = "none"; 1110 | gutterText.innerHTML = html.join(""); 1111 | // Make sure scrolling doesn't cause number gutter size to pop 1112 | if (normalNode != null) { 1113 | var node = gutterText.childNodes[normalNode - showingFrom]; 1114 | var minwidth = String(doc.size).length, val = eltText(node), pad = ""; 1115 | while (val.length + pad.length < minwidth) pad += "\u00a0"; 1116 | if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild); 1117 | } 1118 | gutter.style.display = ""; 1119 | var resized = Math.abs((parseInt(lineSpace.style.marginLeft) || 0) - gutter.offsetWidth) > 2; 1120 | lineSpace.style.marginLeft = gutter.offsetWidth + "px"; 1121 | gutterDirty = false; 1122 | return resized; 1123 | } 1124 | function updateSelection() { 1125 | var collapsed = posEq(sel.from, sel.to); 1126 | var fromPos = localCoords(sel.from, true); 1127 | var toPos = collapsed ? fromPos : localCoords(sel.to, true); 1128 | var headPos = sel.inverted ? fromPos : toPos, th = textHeight(); 1129 | var wrapOff = eltOffset(wrapper), lineOff = eltOffset(lineDiv); 1130 | inputDiv.style.top = Math.max(0, Math.min(scroller.offsetHeight, headPos.y + lineOff.top - wrapOff.top)) + "px"; 1131 | inputDiv.style.left = Math.max(0, Math.min(scroller.offsetWidth, headPos.x + lineOff.left - wrapOff.left)) + "px"; 1132 | if (collapsed) { 1133 | cursor.style.top = headPos.y + "px"; 1134 | cursor.style.left = (options.lineWrapping ? Math.min(headPos.x, lineSpace.offsetWidth) : headPos.x) + "px"; 1135 | cursor.style.display = ""; 1136 | selectionDiv.style.display = "none"; 1137 | } else { 1138 | var sameLine = fromPos.y == toPos.y, html = ""; 1139 | var clientWidth = lineSpace.clientWidth || lineSpace.offsetWidth; 1140 | var clientHeight = lineSpace.clientHeight || lineSpace.offsetHeight; 1141 | function add(left, top, right, height) { 1142 | var rstyle = quirksMode ? "width: " + (!right ? clientWidth : clientWidth - right - left) + "px" 1143 | : "right: " + right + "px"; 1144 | html += '
'; 1146 | } 1147 | if (sel.from.ch && fromPos.y >= 0) { 1148 | var right = sameLine ? clientWidth - toPos.x : 0; 1149 | add(fromPos.x, fromPos.y, right, th); 1150 | } 1151 | var middleStart = Math.max(0, fromPos.y + (sel.from.ch ? th : 0)); 1152 | var middleHeight = Math.min(toPos.y, clientHeight) - middleStart; 1153 | if (middleHeight > 0.2 * th) 1154 | add(0, middleStart, 0, middleHeight); 1155 | if ((!sameLine || !sel.from.ch) && toPos.y < clientHeight - .5 * th) 1156 | add(0, toPos.y, clientWidth - toPos.x, th); 1157 | selectionDiv.innerHTML = html; 1158 | cursor.style.display = "none"; 1159 | selectionDiv.style.display = ""; 1160 | } 1161 | } 1162 | 1163 | function setShift(val) { 1164 | if (val) shiftSelecting = shiftSelecting || (sel.inverted ? sel.to : sel.from); 1165 | else shiftSelecting = null; 1166 | } 1167 | function setSelectionUser(from, to) { 1168 | var sh = shiftSelecting && clipPos(shiftSelecting); 1169 | if (sh) { 1170 | if (posLess(sh, from)) from = sh; 1171 | else if (posLess(to, sh)) to = sh; 1172 | } 1173 | setSelection(from, to); 1174 | userSelChange = true; 1175 | } 1176 | // Update the selection. Last two args are only used by 1177 | // updateLines, since they have to be expressed in the line 1178 | // numbers before the update. 1179 | function setSelection(from, to, oldFrom, oldTo) { 1180 | goalColumn = null; 1181 | if (oldFrom == null) {oldFrom = sel.from.line; oldTo = sel.to.line;} 1182 | if (posEq(sel.from, from) && posEq(sel.to, to)) return; 1183 | if (posLess(to, from)) {var tmp = to; to = from; from = tmp;} 1184 | 1185 | // Skip over hidden lines. 1186 | if (from.line != oldFrom) { 1187 | var from1 = skipHidden(from, oldFrom, sel.from.ch); 1188 | // If there is no non-hidden line left, force visibility on current line 1189 | if (!from1) setLineHidden(from.line, false); 1190 | else from = from1; 1191 | } 1192 | if (to.line != oldTo) to = skipHidden(to, oldTo, sel.to.ch); 1193 | 1194 | if (posEq(from, to)) sel.inverted = false; 1195 | else if (posEq(from, sel.to)) sel.inverted = false; 1196 | else if (posEq(to, sel.from)) sel.inverted = true; 1197 | 1198 | if (options.autoClearEmptyLines && posEq(sel.from, sel.to)) { 1199 | var head = sel.inverted ? from : to; 1200 | if (head.line != sel.from.line && sel.from.line < doc.size) { 1201 | var oldLine = getLine(sel.from.line); 1202 | if (/^\s+$/.test(oldLine.text)) 1203 | setTimeout(operation(function() { 1204 | if (oldLine.parent && /^\s+$/.test(oldLine.text)) { 1205 | var no = lineNo(oldLine); 1206 | replaceRange("", {line: no, ch: 0}, {line: no, ch: oldLine.text.length}); 1207 | } 1208 | }, 10)); 1209 | } 1210 | } 1211 | 1212 | sel.from = from; sel.to = to; 1213 | selectionChanged = true; 1214 | } 1215 | function skipHidden(pos, oldLine, oldCh) { 1216 | function getNonHidden(dir) { 1217 | var lNo = pos.line + dir, end = dir == 1 ? doc.size : -1; 1218 | while (lNo != end) { 1219 | var line = getLine(lNo); 1220 | if (!line.hidden) { 1221 | var ch = pos.ch; 1222 | if (toEnd || ch > oldCh || ch > line.text.length) ch = line.text.length; 1223 | return {line: lNo, ch: ch}; 1224 | } 1225 | lNo += dir; 1226 | } 1227 | } 1228 | var line = getLine(pos.line); 1229 | var toEnd = pos.ch == line.text.length && pos.ch != oldCh; 1230 | if (!line.hidden) return pos; 1231 | if (pos.line >= oldLine) return getNonHidden(1) || getNonHidden(-1); 1232 | else return getNonHidden(-1) || getNonHidden(1); 1233 | } 1234 | function setCursor(line, ch, user) { 1235 | var pos = clipPos({line: line, ch: ch || 0}); 1236 | (user ? setSelectionUser : setSelection)(pos, pos); 1237 | } 1238 | 1239 | function clipLine(n) {return Math.max(0, Math.min(n, doc.size-1));} 1240 | function clipPos(pos) { 1241 | if (pos.line < 0) return {line: 0, ch: 0}; 1242 | if (pos.line >= doc.size) return {line: doc.size-1, ch: getLine(doc.size-1).text.length}; 1243 | var ch = pos.ch, linelen = getLine(pos.line).text.length; 1244 | if (ch == null || ch > linelen) return {line: pos.line, ch: linelen}; 1245 | else if (ch < 0) return {line: pos.line, ch: 0}; 1246 | else return pos; 1247 | } 1248 | 1249 | function findPosH(dir, unit) { 1250 | var end = sel.inverted ? sel.from : sel.to, line = end.line, ch = end.ch; 1251 | var lineObj = getLine(line); 1252 | function findNextLine() { 1253 | for (var l = line + dir, e = dir < 0 ? -1 : doc.size; l != e; l += dir) { 1254 | var lo = getLine(l); 1255 | if (!lo.hidden) { line = l; lineObj = lo; return true; } 1256 | } 1257 | } 1258 | function moveOnce(boundToLine) { 1259 | if (ch == (dir < 0 ? 0 : lineObj.text.length)) { 1260 | if (!boundToLine && findNextLine()) ch = dir < 0 ? lineObj.text.length : 0; 1261 | else return false; 1262 | } else ch += dir; 1263 | return true; 1264 | } 1265 | if (unit == "char") moveOnce(); 1266 | else if (unit == "column") moveOnce(true); 1267 | else if (unit == "word") { 1268 | var sawWord = false; 1269 | for (;;) { 1270 | if (dir < 0) if (!moveOnce()) break; 1271 | if (isWordChar(lineObj.text.charAt(ch))) sawWord = true; 1272 | else if (sawWord) {if (dir < 0) {dir = 1; moveOnce();} break;} 1273 | if (dir > 0) if (!moveOnce()) break; 1274 | } 1275 | } 1276 | return {line: line, ch: ch}; 1277 | } 1278 | function moveH(dir, unit) { 1279 | var pos = dir < 0 ? sel.from : sel.to; 1280 | if (shiftSelecting || posEq(sel.from, sel.to)) pos = findPosH(dir, unit); 1281 | setCursor(pos.line, pos.ch, true); 1282 | } 1283 | function deleteH(dir, unit) { 1284 | if (!posEq(sel.from, sel.to)) replaceRange("", sel.from, sel.to); 1285 | else if (dir < 0) replaceRange("", findPosH(dir, unit), sel.to); 1286 | else replaceRange("", sel.from, findPosH(dir, unit)); 1287 | userSelChange = true; 1288 | } 1289 | var goalColumn = null; 1290 | function moveV(dir, unit) { 1291 | var dist = 0, pos = localCoords(sel.inverted ? sel.from : sel.to, true); 1292 | if (goalColumn != null) pos.x = goalColumn; 1293 | if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight); 1294 | else if (unit == "line") dist = textHeight(); 1295 | var target = coordsChar(pos.x, pos.y + dist * dir + 2); 1296 | if (unit == "page") scroller.scrollTop += localCoords(target, true).y - pos.y; 1297 | setCursor(target.line, target.ch, true); 1298 | goalColumn = pos.x; 1299 | } 1300 | 1301 | function selectWordAt(pos) { 1302 | var line = getLine(pos.line).text; 1303 | var start = pos.ch, end = pos.ch; 1304 | while (start > 0 && isWordChar(line.charAt(start - 1))) --start; 1305 | while (end < line.length && isWordChar(line.charAt(end))) ++end; 1306 | setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end}); 1307 | } 1308 | function selectLine(line) { 1309 | setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0})); 1310 | } 1311 | function indentSelected(mode) { 1312 | if (posEq(sel.from, sel.to)) return indentLine(sel.from.line, mode); 1313 | var e = sel.to.line - (sel.to.ch ? 0 : 1); 1314 | for (var i = sel.from.line; i <= e; ++i) indentLine(i, mode); 1315 | } 1316 | 1317 | function indentLine(n, how) { 1318 | if (!how) how = "add"; 1319 | if (how == "smart") { 1320 | if (!mode.indent) how = "prev"; 1321 | else var state = getStateBefore(n); 1322 | } 1323 | 1324 | var line = getLine(n), curSpace = line.indentation(options.tabSize), 1325 | curSpaceString = line.text.match(/^\s*/)[0], indentation; 1326 | if (how == "prev") { 1327 | if (n) indentation = getLine(n-1).indentation(options.tabSize); 1328 | else indentation = 0; 1329 | } 1330 | else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text); 1331 | else if (how == "add") indentation = curSpace + options.indentUnit; 1332 | else if (how == "subtract") indentation = curSpace - options.indentUnit; 1333 | indentation = Math.max(0, indentation); 1334 | var diff = indentation - curSpace; 1335 | 1336 | if (!diff) { 1337 | if (sel.from.line != n && sel.to.line != n) return; 1338 | var indentString = curSpaceString; 1339 | } 1340 | else { 1341 | var indentString = "", pos = 0; 1342 | if (options.indentWithTabs) 1343 | for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";} 1344 | while (pos < indentation) {++pos; indentString += " ";} 1345 | } 1346 | 1347 | replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length}); 1348 | } 1349 | 1350 | function loadMode() { 1351 | mode = CodeMirror.getMode(options, options.mode); 1352 | doc.iter(0, doc.size, function(line) { line.stateAfter = null; }); 1353 | work = [0]; 1354 | startWorker(); 1355 | } 1356 | function gutterChanged() { 1357 | var visible = options.gutter || options.lineNumbers; 1358 | gutter.style.display = visible ? "" : "none"; 1359 | if (visible) gutterDirty = true; 1360 | else lineDiv.parentNode.style.marginLeft = 0; 1361 | } 1362 | function wrappingChanged(from, to) { 1363 | if (options.lineWrapping) { 1364 | wrapper.className += " CodeMirror-wrap"; 1365 | var perLine = scroller.clientWidth / charWidth() - 3; 1366 | doc.iter(0, doc.size, function(line) { 1367 | if (line.hidden) return; 1368 | var guess = Math.ceil(line.text.length / perLine) || 1; 1369 | if (guess != 1) updateLineHeight(line, guess); 1370 | }); 1371 | lineSpace.style.width = code.style.width = ""; 1372 | } else { 1373 | wrapper.className = wrapper.className.replace(" CodeMirror-wrap", ""); 1374 | maxWidth = null; maxLine = ""; 1375 | doc.iter(0, doc.size, function(line) { 1376 | if (line.height != 1 && !line.hidden) updateLineHeight(line, 1); 1377 | if (line.text.length > maxLine.length) maxLine = line.text; 1378 | }); 1379 | } 1380 | changes.push({from: 0, to: doc.size}); 1381 | } 1382 | function makeTab(col) { 1383 | var w = options.tabSize - col % options.tabSize, cached = tabCache[w]; 1384 | if (cached) return cached; 1385 | for (var str = '', i = 0; i < w; ++i) str += " "; 1386 | return (tabCache[w] = {html: str + "", width: w}); 1387 | } 1388 | function themeChanged() { 1389 | scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") + 1390 | options.theme.replace(/(^|\s)\s*/g, " cm-s-"); 1391 | } 1392 | function keyMapChanged() { 1393 | var style = keyMap[options.keyMap].style; 1394 | wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") + 1395 | (style ? " cm-keymap-" + style : ""); 1396 | } 1397 | 1398 | function TextMarker() { this.set = []; } 1399 | TextMarker.prototype.clear = operation(function() { 1400 | var min = Infinity, max = -Infinity; 1401 | for (var i = 0, e = this.set.length; i < e; ++i) { 1402 | var line = this.set[i], mk = line.marked; 1403 | if (!mk || !line.parent) continue; 1404 | var lineN = lineNo(line); 1405 | min = Math.min(min, lineN); max = Math.max(max, lineN); 1406 | for (var j = 0; j < mk.length; ++j) 1407 | if (mk[j].marker == this) mk.splice(j--, 1); 1408 | } 1409 | if (min != Infinity) 1410 | changes.push({from: min, to: max + 1}); 1411 | }); 1412 | TextMarker.prototype.find = function() { 1413 | var from, to; 1414 | for (var i = 0, e = this.set.length; i < e; ++i) { 1415 | var line = this.set[i], mk = line.marked; 1416 | for (var j = 0; j < mk.length; ++j) { 1417 | var mark = mk[j]; 1418 | if (mark.marker == this) { 1419 | if (mark.from != null || mark.to != null) { 1420 | var found = lineNo(line); 1421 | if (found != null) { 1422 | if (mark.from != null) from = {line: found, ch: mark.from}; 1423 | if (mark.to != null) to = {line: found, ch: mark.to}; 1424 | } 1425 | } 1426 | } 1427 | } 1428 | } 1429 | return {from: from, to: to}; 1430 | }; 1431 | 1432 | function markText(from, to, className) { 1433 | from = clipPos(from); to = clipPos(to); 1434 | var tm = new TextMarker(); 1435 | if (!posLess(from, to)) return tm; 1436 | function add(line, from, to, className) { 1437 | getLine(line).addMark(new MarkedText(from, to, className, tm)); 1438 | } 1439 | if (from.line == to.line) add(from.line, from.ch, to.ch, className); 1440 | else { 1441 | add(from.line, from.ch, null, className); 1442 | for (var i = from.line + 1, e = to.line; i < e; ++i) 1443 | add(i, null, null, className); 1444 | add(to.line, null, to.ch, className); 1445 | } 1446 | changes.push({from: from.line, to: to.line + 1}); 1447 | return tm; 1448 | } 1449 | 1450 | function setBookmark(pos) { 1451 | pos = clipPos(pos); 1452 | var bm = new Bookmark(pos.ch); 1453 | getLine(pos.line).addMark(bm); 1454 | return bm; 1455 | } 1456 | 1457 | function findMarksAt(pos) { 1458 | pos = clipPos(pos); 1459 | var markers = [], marked = getLine(pos.line).marked; 1460 | if (!marked) return markers; 1461 | for (var i = 0, e = marked.length; i < e; ++i) { 1462 | var m = marked[i]; 1463 | if ((m.from == null || m.from <= pos.ch) && 1464 | (m.to == null || m.to >= pos.ch)) 1465 | markers.push(m.marker || m); 1466 | } 1467 | return markers; 1468 | } 1469 | 1470 | function addGutterMarker(line, text, className) { 1471 | if (typeof line == "number") line = getLine(clipLine(line)); 1472 | line.gutterMarker = {text: text, style: className}; 1473 | gutterDirty = true; 1474 | return line; 1475 | } 1476 | function removeGutterMarker(line) { 1477 | if (typeof line == "number") line = getLine(clipLine(line)); 1478 | line.gutterMarker = null; 1479 | gutterDirty = true; 1480 | } 1481 | 1482 | function changeLine(handle, op) { 1483 | var no = handle, line = handle; 1484 | if (typeof handle == "number") line = getLine(clipLine(handle)); 1485 | else no = lineNo(handle); 1486 | if (no == null) return null; 1487 | if (op(line, no)) changes.push({from: no, to: no + 1}); 1488 | else return null; 1489 | return line; 1490 | } 1491 | function setLineClass(handle, className, bgClassName) { 1492 | return changeLine(handle, function(line) { 1493 | if (line.className != className || line.bgClassName != bgClassName) { 1494 | line.className = className; 1495 | line.bgClassName = bgClassName; 1496 | return true; 1497 | } 1498 | }); 1499 | } 1500 | function setLineHidden(handle, hidden) { 1501 | return changeLine(handle, function(line, no) { 1502 | if (line.hidden != hidden) { 1503 | line.hidden = hidden; 1504 | if (!options.lineWrapping) { 1505 | var l = line.text; 1506 | if (hidden && l.length == maxLine.length) { 1507 | maxLengthChanged = true; 1508 | } 1509 | else if (!hidden && l.length > maxLine.length) { 1510 | maxLine = l; maxWidth = null; 1511 | maxLengthChanged = false; 1512 | } 1513 | } 1514 | updateLineHeight(line, hidden ? 0 : 1); 1515 | var fline = sel.from.line, tline = sel.to.line; 1516 | if (hidden && (fline == no || tline == no)) { 1517 | var from = fline == no ? skipHidden({line: fline, ch: 0}, fline, 0) : sel.from; 1518 | var to = tline == no ? skipHidden({line: tline, ch: 0}, tline, 0) : sel.to; 1519 | // Can't hide the last visible line, we'd have no place to put the cursor 1520 | if (!to) return; 1521 | setSelection(from, to); 1522 | } 1523 | return (gutterDirty = true); 1524 | } 1525 | }); 1526 | } 1527 | 1528 | function lineInfo(line) { 1529 | if (typeof line == "number") { 1530 | if (!isLine(line)) return null; 1531 | var n = line; 1532 | line = getLine(line); 1533 | if (!line) return null; 1534 | } 1535 | else { 1536 | var n = lineNo(line); 1537 | if (n == null) return null; 1538 | } 1539 | var marker = line.gutterMarker; 1540 | return {line: n, handle: line, text: line.text, markerText: marker && marker.text, 1541 | markerClass: marker && marker.style, lineClass: line.className, bgClass: line.bgClassName}; 1542 | } 1543 | 1544 | function stringWidth(str) { 1545 | measure.innerHTML = "
x
"; 1546 | measure.firstChild.firstChild.firstChild.nodeValue = str; 1547 | return measure.firstChild.firstChild.offsetWidth || 10; 1548 | } 1549 | // These are used to go from pixel positions to character 1550 | // positions, taking varying character widths into account. 1551 | function charFromX(line, x) { 1552 | if (x <= 0) return 0; 1553 | var lineObj = getLine(line), text = lineObj.text; 1554 | function getX(len) { 1555 | return measureLine(lineObj, len).left; 1556 | } 1557 | var from = 0, fromX = 0, to = text.length, toX; 1558 | // Guess a suitable upper bound for our search. 1559 | var estimated = Math.min(to, Math.ceil(x / charWidth())); 1560 | for (;;) { 1561 | var estX = getX(estimated); 1562 | if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); 1563 | else {toX = estX; to = estimated; break;} 1564 | } 1565 | if (x > toX) return to; 1566 | // Try to guess a suitable lower bound as well. 1567 | estimated = Math.floor(to * 0.8); estX = getX(estimated); 1568 | if (estX < x) {from = estimated; fromX = estX;} 1569 | // Do a binary search between these bounds. 1570 | for (;;) { 1571 | if (to - from <= 1) return (toX - x > x - fromX) ? from : to; 1572 | var middle = Math.ceil((from + to) / 2), middleX = getX(middle); 1573 | if (middleX > x) {to = middle; toX = middleX;} 1574 | else {from = middle; fromX = middleX;} 1575 | } 1576 | } 1577 | 1578 | var tempId = "CodeMirror-temp-" + Math.floor(Math.random() * 0xffffff).toString(16); 1579 | function measureLine(line, ch) { 1580 | if (ch == 0) return {top: 0, left: 0}; 1581 | var wbr = options.lineWrapping && ch < line.text.length && 1582 | spanAffectsWrapping.test(line.text.slice(ch - 1, ch + 1)); 1583 | measure.innerHTML = "
" + line.getHTML(makeTab, ch, tempId, wbr) + "
"; 1584 | var elt = document.getElementById(tempId); 1585 | var top = elt.offsetTop, left = elt.offsetLeft; 1586 | // Older IEs report zero offsets for spans directly after a wrap 1587 | if (ie && top == 0 && left == 0) { 1588 | var backup = document.createElement("span"); 1589 | backup.innerHTML = "x"; 1590 | elt.parentNode.insertBefore(backup, elt.nextSibling); 1591 | top = backup.offsetTop; 1592 | } 1593 | return {top: top, left: left}; 1594 | } 1595 | function localCoords(pos, inLineWrap) { 1596 | var x, lh = textHeight(), y = lh * (heightAtLine(doc, pos.line) - (inLineWrap ? displayOffset : 0)); 1597 | if (pos.ch == 0) x = 0; 1598 | else { 1599 | var sp = measureLine(getLine(pos.line), pos.ch); 1600 | x = sp.left; 1601 | if (options.lineWrapping) y += Math.max(0, sp.top); 1602 | } 1603 | return {x: x, y: y, yBot: y + lh}; 1604 | } 1605 | // Coords must be lineSpace-local 1606 | function coordsChar(x, y) { 1607 | if (y < 0) y = 0; 1608 | var th = textHeight(), cw = charWidth(), heightPos = displayOffset + Math.floor(y / th); 1609 | var lineNo = lineAtHeight(doc, heightPos); 1610 | if (lineNo >= doc.size) return {line: doc.size - 1, ch: getLine(doc.size - 1).text.length}; 1611 | var lineObj = getLine(lineNo), text = lineObj.text; 1612 | var tw = options.lineWrapping, innerOff = tw ? heightPos - heightAtLine(doc, lineNo) : 0; 1613 | if (x <= 0 && innerOff == 0) return {line: lineNo, ch: 0}; 1614 | function getX(len) { 1615 | var sp = measureLine(lineObj, len); 1616 | if (tw) { 1617 | var off = Math.round(sp.top / th); 1618 | return Math.max(0, sp.left + (off - innerOff) * scroller.clientWidth); 1619 | } 1620 | return sp.left; 1621 | } 1622 | var from = 0, fromX = 0, to = text.length, toX; 1623 | // Guess a suitable upper bound for our search. 1624 | var estimated = Math.min(to, Math.ceil((x + innerOff * scroller.clientWidth * .9) / cw)); 1625 | for (;;) { 1626 | var estX = getX(estimated); 1627 | if (estX <= x && estimated < to) estimated = Math.min(to, Math.ceil(estimated * 1.2)); 1628 | else {toX = estX; to = estimated; break;} 1629 | } 1630 | if (x > toX) return {line: lineNo, ch: to}; 1631 | // Try to guess a suitable lower bound as well. 1632 | estimated = Math.floor(to * 0.8); estX = getX(estimated); 1633 | if (estX < x) {from = estimated; fromX = estX;} 1634 | // Do a binary search between these bounds. 1635 | for (;;) { 1636 | if (to - from <= 1) return {line: lineNo, ch: (toX - x > x - fromX) ? from : to}; 1637 | var middle = Math.ceil((from + to) / 2), middleX = getX(middle); 1638 | if (middleX > x) {to = middle; toX = middleX;} 1639 | else {from = middle; fromX = middleX;} 1640 | } 1641 | } 1642 | function pageCoords(pos) { 1643 | var local = localCoords(pos, true), off = eltOffset(lineSpace); 1644 | return {x: off.left + local.x, y: off.top + local.y, yBot: off.top + local.yBot}; 1645 | } 1646 | 1647 | var cachedHeight, cachedHeightFor, measureText; 1648 | function textHeight() { 1649 | if (measureText == null) { 1650 | measureText = "
";
1651 |         for (var i = 0; i < 49; ++i) measureText += "x
"; 1652 | measureText += "x
"; 1653 | } 1654 | var offsetHeight = lineDiv.clientHeight; 1655 | if (offsetHeight == cachedHeightFor) return cachedHeight; 1656 | cachedHeightFor = offsetHeight; 1657 | measure.innerHTML = measureText; 1658 | cachedHeight = measure.firstChild.offsetHeight / 50 || 1; 1659 | measure.innerHTML = ""; 1660 | return cachedHeight; 1661 | } 1662 | var cachedWidth, cachedWidthFor = 0; 1663 | function charWidth() { 1664 | if (scroller.clientWidth == cachedWidthFor) return cachedWidth; 1665 | cachedWidthFor = scroller.clientWidth; 1666 | return (cachedWidth = stringWidth("x")); 1667 | } 1668 | function paddingTop() {return lineSpace.offsetTop;} 1669 | function paddingLeft() {return lineSpace.offsetLeft;} 1670 | 1671 | function posFromMouse(e, liberal) { 1672 | var offW = eltOffset(scroller, true), x, y; 1673 | // Fails unpredictably on IE[67] when mouse is dragged around quickly. 1674 | try { x = e.clientX; y = e.clientY; } catch (e) { return null; } 1675 | // This is a mess of a heuristic to try and determine whether a 1676 | // scroll-bar was clicked or not, and to return null if one was 1677 | // (and !liberal). 1678 | if (!liberal && (x - offW.left > scroller.clientWidth || y - offW.top > scroller.clientHeight)) 1679 | return null; 1680 | var offL = eltOffset(lineSpace, true); 1681 | return coordsChar(x - offL.left, y - offL.top); 1682 | } 1683 | function onContextMenu(e) { 1684 | var pos = posFromMouse(e), scrollPos = scroller.scrollTop; 1685 | if (!pos || window.opera) return; // Opera is difficult. 1686 | if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to)) 1687 | operation(setCursor)(pos.line, pos.ch); 1688 | 1689 | var oldCSS = input.style.cssText; 1690 | inputDiv.style.position = "absolute"; 1691 | input.style.cssText = "position: fixed; width: 30px; height: 30px; top: " + (e.clientY - 5) + 1692 | "px; left: " + (e.clientX - 5) + "px; z-index: 1000; background: white; " + 1693 | "border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; 1694 | leaveInputAlone = true; 1695 | var val = input.value = getSelection(); 1696 | focusInput(); 1697 | selectInput(input); 1698 | function rehide() { 1699 | var newVal = splitLines(input.value).join("\n"); 1700 | if (newVal != val) operation(replaceSelection)(newVal, "end"); 1701 | inputDiv.style.position = "relative"; 1702 | input.style.cssText = oldCSS; 1703 | if (ie_lt9) scroller.scrollTop = scrollPos; 1704 | leaveInputAlone = false; 1705 | resetInput(true); 1706 | slowPoll(); 1707 | } 1708 | 1709 | if (gecko) { 1710 | e_stop(e); 1711 | var mouseup = connect(window, "mouseup", function() { 1712 | mouseup(); 1713 | setTimeout(rehide, 20); 1714 | }, true); 1715 | } else { 1716 | setTimeout(rehide, 50); 1717 | } 1718 | } 1719 | 1720 | // Cursor-blinking 1721 | function restartBlink() { 1722 | clearInterval(blinker); 1723 | var on = true; 1724 | cursor.style.visibility = ""; 1725 | blinker = setInterval(function() { 1726 | cursor.style.visibility = (on = !on) ? "" : "hidden"; 1727 | }, 650); 1728 | } 1729 | 1730 | var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"}; 1731 | function matchBrackets(autoclear) { 1732 | var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1; 1733 | var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)]; 1734 | if (!match) return; 1735 | var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles; 1736 | for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2) 1737 | if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;} 1738 | 1739 | var stack = [line.text.charAt(pos)], re = /[(){}[\]]/; 1740 | function scan(line, from, to) { 1741 | if (!line.text) return; 1742 | var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur; 1743 | for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) { 1744 | var text = st[i]; 1745 | if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;} 1746 | for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) { 1747 | if (pos >= from && pos < to && re.test(cur = text.charAt(j))) { 1748 | var match = matching[cur]; 1749 | if (match.charAt(1) == ">" == forward) stack.push(cur); 1750 | else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false}; 1751 | else if (!stack.length) return {pos: pos, match: true}; 1752 | } 1753 | } 1754 | } 1755 | } 1756 | for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) { 1757 | var line = getLine(i), first = i == head.line; 1758 | var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length); 1759 | if (found) break; 1760 | } 1761 | if (!found) found = {pos: null, match: false}; 1762 | var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket"; 1763 | var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style), 1764 | two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style); 1765 | var clear = operation(function(){one.clear(); two && two.clear();}); 1766 | if (autoclear) setTimeout(clear, 800); 1767 | else bracketHighlighted = clear; 1768 | } 1769 | 1770 | // Finds the line to start with when starting a parse. Tries to 1771 | // find a line with a stateAfter, so that it can start with a 1772 | // valid state. If that fails, it returns the line with the 1773 | // smallest indentation, which tends to need the least context to 1774 | // parse correctly. 1775 | function findStartLine(n) { 1776 | var minindent, minline; 1777 | for (var search = n, lim = n - 40; search > lim; --search) { 1778 | if (search == 0) return 0; 1779 | var line = getLine(search-1); 1780 | if (line.stateAfter) return search; 1781 | var indented = line.indentation(options.tabSize); 1782 | if (minline == null || minindent > indented) { 1783 | minline = search - 1; 1784 | minindent = indented; 1785 | } 1786 | } 1787 | return minline; 1788 | } 1789 | function getStateBefore(n) { 1790 | var start = findStartLine(n), state = start && getLine(start-1).stateAfter; 1791 | if (!state) state = startState(mode); 1792 | else state = copyState(mode, state); 1793 | doc.iter(start, n, function(line) { 1794 | line.highlight(mode, state, options.tabSize); 1795 | line.stateAfter = copyState(mode, state); 1796 | }); 1797 | if (start < n) changes.push({from: start, to: n}); 1798 | if (n < doc.size && !getLine(n).stateAfter) work.push(n); 1799 | return state; 1800 | } 1801 | function highlightLines(start, end) { 1802 | var state = getStateBefore(start); 1803 | doc.iter(start, end, function(line) { 1804 | line.highlight(mode, state, options.tabSize); 1805 | line.stateAfter = copyState(mode, state); 1806 | }); 1807 | } 1808 | function highlightWorker() { 1809 | var end = +new Date + options.workTime; 1810 | var foundWork = work.length; 1811 | while (work.length) { 1812 | if (!getLine(showingFrom).stateAfter) var task = showingFrom; 1813 | else var task = work.pop(); 1814 | if (task >= doc.size) continue; 1815 | var start = findStartLine(task), state = start && getLine(start-1).stateAfter; 1816 | if (state) state = copyState(mode, state); 1817 | else state = startState(mode); 1818 | 1819 | var unchanged = 0, compare = mode.compareStates, realChange = false, 1820 | i = start, bail = false; 1821 | doc.iter(i, doc.size, function(line) { 1822 | var hadState = line.stateAfter; 1823 | if (+new Date > end) { 1824 | work.push(i); 1825 | startWorker(options.workDelay); 1826 | if (realChange) changes.push({from: task, to: i + 1}); 1827 | return (bail = true); 1828 | } 1829 | var changed = line.highlight(mode, state, options.tabSize); 1830 | if (changed) realChange = true; 1831 | line.stateAfter = copyState(mode, state); 1832 | var done = null; 1833 | if (compare) { 1834 | var same = hadState && compare(hadState, state); 1835 | if (same != Pass) done = !!same; 1836 | } 1837 | if (done == null) { 1838 | if (changed !== false || !hadState) unchanged = 0; 1839 | else if (++unchanged > 3 && (!mode.indent || mode.indent(hadState, "") == mode.indent(state, ""))) 1840 | done = true; 1841 | } 1842 | if (done) return true; 1843 | ++i; 1844 | }); 1845 | if (bail) return; 1846 | if (realChange) changes.push({from: task, to: i + 1}); 1847 | } 1848 | if (foundWork && options.onHighlightComplete) 1849 | options.onHighlightComplete(instance); 1850 | } 1851 | function startWorker(time) { 1852 | if (!work.length) return; 1853 | highlight.set(time, operation(highlightWorker)); 1854 | } 1855 | 1856 | // Operations are used to wrap changes in such a way that each 1857 | // change won't have to update the cursor and display (which would 1858 | // be awkward, slow, and error-prone), but instead updates are 1859 | // batched and then all combined and executed at once. 1860 | function startOperation() { 1861 | updateInput = userSelChange = textChanged = null; 1862 | changes = []; selectionChanged = false; callbacks = []; 1863 | } 1864 | function endOperation() { 1865 | var reScroll = false, updated; 1866 | if (maxLengthChanged) computeMaxLength(); 1867 | if (selectionChanged) reScroll = !scrollCursorIntoView(); 1868 | if (changes.length) updated = updateDisplay(changes, true); 1869 | else { 1870 | if (selectionChanged) updateSelection(); 1871 | if (gutterDirty) updateGutter(); 1872 | } 1873 | if (reScroll) scrollCursorIntoView(); 1874 | if (selectionChanged) {scrollEditorIntoView(); restartBlink();} 1875 | 1876 | if (focused && !leaveInputAlone && 1877 | (updateInput === true || (updateInput !== false && selectionChanged))) 1878 | resetInput(userSelChange); 1879 | 1880 | if (selectionChanged && options.matchBrackets) 1881 | setTimeout(operation(function() { 1882 | if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;} 1883 | if (posEq(sel.from, sel.to)) matchBrackets(false); 1884 | }), 20); 1885 | var tc = textChanged, cbs = callbacks; // these can be reset by callbacks 1886 | if (selectionChanged && options.onCursorActivity) 1887 | options.onCursorActivity(instance); 1888 | if (tc && options.onChange && instance) 1889 | options.onChange(instance, tc); 1890 | for (var i = 0; i < cbs.length; ++i) cbs[i](instance); 1891 | if (updated && options.onUpdate) options.onUpdate(instance); 1892 | } 1893 | var nestedOperation = 0; 1894 | function operation(f) { 1895 | return function() { 1896 | if (!nestedOperation++) startOperation(); 1897 | try {var result = f.apply(this, arguments);} 1898 | finally {if (!--nestedOperation) endOperation();} 1899 | return result; 1900 | }; 1901 | } 1902 | 1903 | function compoundChange(f) { 1904 | history.startCompound(); 1905 | try { return f(); } finally { history.endCompound(); } 1906 | } 1907 | 1908 | for (var ext in extensions) 1909 | if (extensions.propertyIsEnumerable(ext) && 1910 | !instance.propertyIsEnumerable(ext)) 1911 | instance[ext] = extensions[ext]; 1912 | return instance; 1913 | } // (end of function CodeMirror) 1914 | 1915 | // The default configuration options. 1916 | CodeMirror.defaults = { 1917 | value: "", 1918 | mode: null, 1919 | theme: "default", 1920 | indentUnit: 2, 1921 | indentWithTabs: false, 1922 | smartIndent: true, 1923 | tabSize: 4, 1924 | keyMap: "default", 1925 | extraKeys: null, 1926 | electricChars: true, 1927 | autoClearEmptyLines: false, 1928 | onKeyEvent: null, 1929 | onDragEvent: null, 1930 | lineWrapping: false, 1931 | lineNumbers: false, 1932 | gutter: false, 1933 | fixedGutter: false, 1934 | firstLineNumber: 1, 1935 | readOnly: false, 1936 | dragDrop: true, 1937 | onChange: null, 1938 | onCursorActivity: null, 1939 | onGutterClick: null, 1940 | onHighlightComplete: null, 1941 | onUpdate: null, 1942 | onFocus: null, onBlur: null, onScroll: null, 1943 | matchBrackets: false, 1944 | workTime: 100, 1945 | workDelay: 200, 1946 | pollInterval: 100, 1947 | undoDepth: 40, 1948 | tabindex: null, 1949 | autofocus: null 1950 | }; 1951 | 1952 | var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent); 1953 | var mac = ios || /Mac/.test(navigator.platform); 1954 | var win = /Win/.test(navigator.platform); 1955 | 1956 | // Known modes, by name and by MIME 1957 | var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {}; 1958 | CodeMirror.defineMode = function(name, mode) { 1959 | if (!CodeMirror.defaults.mode && name != "null") CodeMirror.defaults.mode = name; 1960 | if (arguments.length > 2) { 1961 | mode.dependencies = []; 1962 | for (var i = 2; i < arguments.length; ++i) mode.dependencies.push(arguments[i]); 1963 | } 1964 | modes[name] = mode; 1965 | }; 1966 | CodeMirror.defineMIME = function(mime, spec) { 1967 | mimeModes[mime] = spec; 1968 | }; 1969 | CodeMirror.resolveMode = function(spec) { 1970 | if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) 1971 | spec = mimeModes[spec]; 1972 | else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) 1973 | return CodeMirror.resolveMode("application/xml"); 1974 | if (typeof spec == "string") return {name: spec}; 1975 | else return spec || {name: "null"}; 1976 | }; 1977 | CodeMirror.getMode = function(options, spec) { 1978 | var spec = CodeMirror.resolveMode(spec); 1979 | var mfactory = modes[spec.name]; 1980 | if (!mfactory) return CodeMirror.getMode(options, "text/plain"); 1981 | return mfactory(options, spec); 1982 | }; 1983 | CodeMirror.listModes = function() { 1984 | var list = []; 1985 | for (var m in modes) 1986 | if (modes.propertyIsEnumerable(m)) list.push(m); 1987 | return list; 1988 | }; 1989 | CodeMirror.listMIMEs = function() { 1990 | var list = []; 1991 | for (var m in mimeModes) 1992 | if (mimeModes.propertyIsEnumerable(m)) list.push({mime: m, mode: mimeModes[m]}); 1993 | return list; 1994 | }; 1995 | 1996 | var extensions = CodeMirror.extensions = {}; 1997 | CodeMirror.defineExtension = function(name, func) { 1998 | extensions[name] = func; 1999 | }; 2000 | 2001 | var commands = CodeMirror.commands = { 2002 | selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});}, 2003 | killLine: function(cm) { 2004 | var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to); 2005 | if (!sel && cm.getLine(from.line).length == from.ch) cm.replaceRange("", from, {line: from.line + 1, ch: 0}); 2006 | else cm.replaceRange("", from, sel ? to : {line: from.line}); 2007 | }, 2008 | deleteLine: function(cm) {var l = cm.getCursor().line; cm.replaceRange("", {line: l, ch: 0}, {line: l});}, 2009 | undo: function(cm) {cm.undo();}, 2010 | redo: function(cm) {cm.redo();}, 2011 | goDocStart: function(cm) {cm.setCursor(0, 0, true);}, 2012 | goDocEnd: function(cm) {cm.setSelection({line: cm.lineCount() - 1}, null, true);}, 2013 | goLineStart: function(cm) {cm.setCursor(cm.getCursor().line, 0, true);}, 2014 | goLineStartSmart: function(cm) { 2015 | var cur = cm.getCursor(); 2016 | var text = cm.getLine(cur.line), firstNonWS = Math.max(0, text.search(/\S/)); 2017 | cm.setCursor(cur.line, cur.ch <= firstNonWS && cur.ch ? 0 : firstNonWS, true); 2018 | }, 2019 | goLineEnd: function(cm) {cm.setSelection({line: cm.getCursor().line}, null, true);}, 2020 | goLineUp: function(cm) {cm.moveV(-1, "line");}, 2021 | goLineDown: function(cm) {cm.moveV(1, "line");}, 2022 | goPageUp: function(cm) {cm.moveV(-1, "page");}, 2023 | goPageDown: function(cm) {cm.moveV(1, "page");}, 2024 | goCharLeft: function(cm) {cm.moveH(-1, "char");}, 2025 | goCharRight: function(cm) {cm.moveH(1, "char");}, 2026 | goColumnLeft: function(cm) {cm.moveH(-1, "column");}, 2027 | goColumnRight: function(cm) {cm.moveH(1, "column");}, 2028 | goWordLeft: function(cm) {cm.moveH(-1, "word");}, 2029 | goWordRight: function(cm) {cm.moveH(1, "word");}, 2030 | delCharLeft: function(cm) {cm.deleteH(-1, "char");}, 2031 | delCharRight: function(cm) {cm.deleteH(1, "char");}, 2032 | delWordLeft: function(cm) {cm.deleteH(-1, "word");}, 2033 | delWordRight: function(cm) {cm.deleteH(1, "word");}, 2034 | indentAuto: function(cm) {cm.indentSelection("smart");}, 2035 | indentMore: function(cm) {cm.indentSelection("add");}, 2036 | indentLess: function(cm) {cm.indentSelection("subtract");}, 2037 | insertTab: function(cm) {cm.replaceSelection("\t", "end");}, 2038 | defaultTab: function(cm) { 2039 | if (cm.somethingSelected()) cm.indentSelection("add"); 2040 | else cm.replaceSelection("\t", "end"); 2041 | }, 2042 | transposeChars: function(cm) { 2043 | var cur = cm.getCursor(), line = cm.getLine(cur.line); 2044 | if (cur.ch > 0 && cur.ch < line.length - 1) 2045 | cm.replaceRange(line.charAt(cur.ch) + line.charAt(cur.ch - 1), 2046 | {line: cur.line, ch: cur.ch - 1}, {line: cur.line, ch: cur.ch + 1}); 2047 | }, 2048 | newlineAndIndent: function(cm) { 2049 | cm.replaceSelection("\n", "end"); 2050 | cm.indentLine(cm.getCursor().line); 2051 | }, 2052 | toggleOverwrite: function(cm) {cm.toggleOverwrite();} 2053 | }; 2054 | 2055 | var keyMap = CodeMirror.keyMap = {}; 2056 | keyMap.basic = { 2057 | "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", 2058 | "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", 2059 | "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto", 2060 | "Enter": "newlineAndIndent", "Insert": "toggleOverwrite" 2061 | }; 2062 | // Note that the save and find-related commands aren't defined by 2063 | // default. Unknown commands are simply ignored. 2064 | keyMap.pcDefault = { 2065 | "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", 2066 | "Ctrl-Home": "goDocStart", "Alt-Up": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Down": "goDocEnd", 2067 | "Ctrl-Left": "goWordLeft", "Ctrl-Right": "goWordRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", 2068 | "Ctrl-Backspace": "delWordLeft", "Ctrl-Delete": "delWordRight", "Ctrl-S": "save", "Ctrl-F": "find", 2069 | "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", 2070 | "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", 2071 | fallthrough: "basic" 2072 | }; 2073 | keyMap.macDefault = { 2074 | "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", 2075 | "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goWordLeft", 2076 | "Alt-Right": "goWordRight", "Cmd-Left": "goLineStart", "Cmd-Right": "goLineEnd", "Alt-Backspace": "delWordLeft", 2077 | "Ctrl-Alt-Backspace": "delWordRight", "Alt-Delete": "delWordRight", "Cmd-S": "save", "Cmd-F": "find", 2078 | "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", 2079 | "Cmd-[": "indentLess", "Cmd-]": "indentMore", 2080 | fallthrough: ["basic", "emacsy"] 2081 | }; 2082 | keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; 2083 | keyMap.emacsy = { 2084 | "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", 2085 | "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", 2086 | "Ctrl-V": "goPageUp", "Shift-Ctrl-V": "goPageDown", "Ctrl-D": "delCharRight", "Ctrl-H": "delCharLeft", 2087 | "Alt-D": "delWordRight", "Alt-Backspace": "delWordLeft", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars" 2088 | }; 2089 | 2090 | function getKeyMap(val) { 2091 | if (typeof val == "string") return keyMap[val]; 2092 | else return val; 2093 | } 2094 | function lookupKey(name, extraMap, map, handle, stop) { 2095 | function lookup(map) { 2096 | map = getKeyMap(map); 2097 | var found = map[name]; 2098 | if (found != null && handle(found)) return true; 2099 | if (map.nofallthrough) { 2100 | if (stop) stop(); 2101 | return true; 2102 | } 2103 | var fallthrough = map.fallthrough; 2104 | if (fallthrough == null) return false; 2105 | if (Object.prototype.toString.call(fallthrough) != "[object Array]") 2106 | return lookup(fallthrough); 2107 | for (var i = 0, e = fallthrough.length; i < e; ++i) { 2108 | if (lookup(fallthrough[i])) return true; 2109 | } 2110 | return false; 2111 | } 2112 | if (extraMap && lookup(extraMap)) return true; 2113 | return lookup(map); 2114 | } 2115 | function isModifierKey(event) { 2116 | var name = keyNames[e_prop(event, "keyCode")]; 2117 | return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod"; 2118 | } 2119 | 2120 | CodeMirror.fromTextArea = function(textarea, options) { 2121 | if (!options) options = {}; 2122 | options.value = textarea.value; 2123 | if (!options.tabindex && textarea.tabindex) 2124 | options.tabindex = textarea.tabindex; 2125 | if (options.autofocus == null && textarea.getAttribute("autofocus") != null) 2126 | options.autofocus = true; 2127 | 2128 | function save() {textarea.value = instance.getValue();} 2129 | if (textarea.form) { 2130 | // Deplorable hack to make the submit method do the right thing. 2131 | var rmSubmit = connect(textarea.form, "submit", save, true); 2132 | if (typeof textarea.form.submit == "function") { 2133 | var realSubmit = textarea.form.submit; 2134 | function wrappedSubmit() { 2135 | save(); 2136 | textarea.form.submit = realSubmit; 2137 | textarea.form.submit(); 2138 | textarea.form.submit = wrappedSubmit; 2139 | } 2140 | textarea.form.submit = wrappedSubmit; 2141 | } 2142 | } 2143 | 2144 | textarea.style.display = "none"; 2145 | var instance = CodeMirror(function(node) { 2146 | textarea.parentNode.insertBefore(node, textarea.nextSibling); 2147 | }, options); 2148 | instance.save = save; 2149 | instance.getTextArea = function() { return textarea; }; 2150 | instance.toTextArea = function() { 2151 | save(); 2152 | textarea.parentNode.removeChild(instance.getWrapperElement()); 2153 | textarea.style.display = ""; 2154 | if (textarea.form) { 2155 | rmSubmit(); 2156 | if (typeof textarea.form.submit == "function") 2157 | textarea.form.submit = realSubmit; 2158 | } 2159 | }; 2160 | return instance; 2161 | }; 2162 | 2163 | // Utility functions for working with state. Exported because modes 2164 | // sometimes need to do this. 2165 | function copyState(mode, state) { 2166 | if (state === true) return state; 2167 | if (mode.copyState) return mode.copyState(state); 2168 | var nstate = {}; 2169 | for (var n in state) { 2170 | var val = state[n]; 2171 | if (val instanceof Array) val = val.concat([]); 2172 | nstate[n] = val; 2173 | } 2174 | return nstate; 2175 | } 2176 | CodeMirror.copyState = copyState; 2177 | function startState(mode, a1, a2) { 2178 | return mode.startState ? mode.startState(a1, a2) : true; 2179 | } 2180 | CodeMirror.startState = startState; 2181 | 2182 | // The character stream used by a mode's parser. 2183 | function StringStream(string, tabSize) { 2184 | this.pos = this.start = 0; 2185 | this.string = string; 2186 | this.tabSize = tabSize || 8; 2187 | } 2188 | StringStream.prototype = { 2189 | eol: function() {return this.pos >= this.string.length;}, 2190 | sol: function() {return this.pos == 0;}, 2191 | peek: function() {return this.string.charAt(this.pos);}, 2192 | next: function() { 2193 | if (this.pos < this.string.length) 2194 | return this.string.charAt(this.pos++); 2195 | }, 2196 | eat: function(match) { 2197 | var ch = this.string.charAt(this.pos); 2198 | if (typeof match == "string") var ok = ch == match; 2199 | else var ok = ch && (match.test ? match.test(ch) : match(ch)); 2200 | if (ok) {++this.pos; return ch;} 2201 | }, 2202 | eatWhile: function(match) { 2203 | var start = this.pos; 2204 | while (this.eat(match)){} 2205 | return this.pos > start; 2206 | }, 2207 | eatSpace: function() { 2208 | var start = this.pos; 2209 | while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos; 2210 | return this.pos > start; 2211 | }, 2212 | skipToEnd: function() {this.pos = this.string.length;}, 2213 | skipTo: function(ch) { 2214 | var found = this.string.indexOf(ch, this.pos); 2215 | if (found > -1) {this.pos = found; return true;} 2216 | }, 2217 | backUp: function(n) {this.pos -= n;}, 2218 | column: function() {return countColumn(this.string, this.start, this.tabSize);}, 2219 | indentation: function() {return countColumn(this.string, null, this.tabSize);}, 2220 | match: function(pattern, consume, caseInsensitive) { 2221 | if (typeof pattern == "string") { 2222 | function cased(str) {return caseInsensitive ? str.toLowerCase() : str;} 2223 | if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) { 2224 | if (consume !== false) this.pos += pattern.length; 2225 | return true; 2226 | } 2227 | } 2228 | else { 2229 | var match = this.string.slice(this.pos).match(pattern); 2230 | if (match && consume !== false) this.pos += match[0].length; 2231 | return match; 2232 | } 2233 | }, 2234 | current: function(){return this.string.slice(this.start, this.pos);} 2235 | }; 2236 | CodeMirror.StringStream = StringStream; 2237 | 2238 | function MarkedText(from, to, className, marker) { 2239 | this.from = from; this.to = to; this.style = className; this.marker = marker; 2240 | } 2241 | MarkedText.prototype = { 2242 | attach: function(line) { this.marker.set.push(line); }, 2243 | detach: function(line) { 2244 | var ix = indexOf(this.marker.set, line); 2245 | if (ix > -1) this.marker.set.splice(ix, 1); 2246 | }, 2247 | split: function(pos, lenBefore) { 2248 | if (this.to <= pos && this.to != null) return null; 2249 | var from = this.from < pos || this.from == null ? null : this.from - pos + lenBefore; 2250 | var to = this.to == null ? null : this.to - pos + lenBefore; 2251 | return new MarkedText(from, to, this.style, this.marker); 2252 | }, 2253 | dup: function() { return new MarkedText(null, null, this.style, this.marker); }, 2254 | clipTo: function(fromOpen, from, toOpen, to, diff) { 2255 | if (fromOpen && to > this.from && (to < this.to || this.to == null)) 2256 | this.from = null; 2257 | else if (this.from != null && this.from >= from) 2258 | this.from = Math.max(to, this.from) + diff; 2259 | if (toOpen && (from < this.to || this.to == null) && (from > this.from || this.from == null)) 2260 | this.to = null; 2261 | else if (this.to != null && this.to > from) 2262 | this.to = to < this.to ? this.to + diff : from; 2263 | }, 2264 | isDead: function() { return this.from != null && this.to != null && this.from >= this.to; }, 2265 | sameSet: function(x) { return this.marker == x.marker; } 2266 | }; 2267 | 2268 | function Bookmark(pos) { 2269 | this.from = pos; this.to = pos; this.line = null; 2270 | } 2271 | Bookmark.prototype = { 2272 | attach: function(line) { this.line = line; }, 2273 | detach: function(line) { if (this.line == line) this.line = null; }, 2274 | split: function(pos, lenBefore) { 2275 | if (pos < this.from) { 2276 | this.from = this.to = (this.from - pos) + lenBefore; 2277 | return this; 2278 | } 2279 | }, 2280 | isDead: function() { return this.from > this.to; }, 2281 | clipTo: function(fromOpen, from, toOpen, to, diff) { 2282 | if ((fromOpen || from < this.from) && (toOpen || to > this.to)) { 2283 | this.from = 0; this.to = -1; 2284 | } else if (this.from > from) { 2285 | this.from = this.to = Math.max(to, this.from) + diff; 2286 | } 2287 | }, 2288 | sameSet: function(x) { return false; }, 2289 | find: function() { 2290 | if (!this.line || !this.line.parent) return null; 2291 | return {line: lineNo(this.line), ch: this.from}; 2292 | }, 2293 | clear: function() { 2294 | if (this.line) { 2295 | var found = indexOf(this.line.marked, this); 2296 | if (found != -1) this.line.marked.splice(found, 1); 2297 | this.line = null; 2298 | } 2299 | } 2300 | }; 2301 | 2302 | // Line objects. These hold state related to a line, including 2303 | // highlighting info (the styles array). 2304 | function Line(text, styles) { 2305 | this.styles = styles || [text, null]; 2306 | this.text = text; 2307 | this.height = 1; 2308 | this.marked = this.gutterMarker = this.className = this.bgClassName = this.handlers = null; 2309 | this.stateAfter = this.parent = this.hidden = null; 2310 | } 2311 | Line.inheritMarks = function(text, orig) { 2312 | var ln = new Line(text), mk = orig && orig.marked; 2313 | if (mk) { 2314 | for (var i = 0; i < mk.length; ++i) { 2315 | if (mk[i].to == null && mk[i].style) { 2316 | var newmk = ln.marked || (ln.marked = []), mark = mk[i]; 2317 | var nmark = mark.dup(); newmk.push(nmark); nmark.attach(ln); 2318 | } 2319 | } 2320 | } 2321 | return ln; 2322 | } 2323 | Line.prototype = { 2324 | // Replace a piece of a line, keeping the styles around it intact. 2325 | replace: function(from, to_, text) { 2326 | var st = [], mk = this.marked, to = to_ == null ? this.text.length : to_; 2327 | copyStyles(0, from, this.styles, st); 2328 | if (text) st.push(text, null); 2329 | copyStyles(to, this.text.length, this.styles, st); 2330 | this.styles = st; 2331 | this.text = this.text.slice(0, from) + text + this.text.slice(to); 2332 | this.stateAfter = null; 2333 | if (mk) { 2334 | var diff = text.length - (to - from); 2335 | for (var i = 0; i < mk.length; ++i) { 2336 | var mark = mk[i]; 2337 | mark.clipTo(from == null, from || 0, to_ == null, to, diff); 2338 | if (mark.isDead()) {mark.detach(this); mk.splice(i--, 1);} 2339 | } 2340 | } 2341 | }, 2342 | // Split a part off a line, keeping styles and markers intact. 2343 | split: function(pos, textBefore) { 2344 | var st = [textBefore, null], mk = this.marked; 2345 | copyStyles(pos, this.text.length, this.styles, st); 2346 | var taken = new Line(textBefore + this.text.slice(pos), st); 2347 | if (mk) { 2348 | for (var i = 0; i < mk.length; ++i) { 2349 | var mark = mk[i]; 2350 | var newmark = mark.split(pos, textBefore.length); 2351 | if (newmark) { 2352 | if (!taken.marked) taken.marked = []; 2353 | taken.marked.push(newmark); newmark.attach(taken); 2354 | if (newmark == mark) mk.splice(i--, 1); 2355 | } 2356 | } 2357 | } 2358 | return taken; 2359 | }, 2360 | append: function(line) { 2361 | var mylen = this.text.length, mk = line.marked, mymk = this.marked; 2362 | this.text += line.text; 2363 | copyStyles(0, line.text.length, line.styles, this.styles); 2364 | if (mymk) { 2365 | for (var i = 0; i < mymk.length; ++i) 2366 | if (mymk[i].to == null) mymk[i].to = mylen; 2367 | } 2368 | if (mk && mk.length) { 2369 | if (!mymk) this.marked = mymk = []; 2370 | outer: for (var i = 0; i < mk.length; ++i) { 2371 | var mark = mk[i]; 2372 | if (!mark.from) { 2373 | for (var j = 0; j < mymk.length; ++j) { 2374 | var mymark = mymk[j]; 2375 | if (mymark.to == mylen && mymark.sameSet(mark)) { 2376 | mymark.to = mark.to == null ? null : mark.to + mylen; 2377 | if (mymark.isDead()) { 2378 | mymark.detach(this); 2379 | mk.splice(i--, 1); 2380 | } 2381 | continue outer; 2382 | } 2383 | } 2384 | } 2385 | mymk.push(mark); 2386 | mark.attach(this); 2387 | mark.from += mylen; 2388 | if (mark.to != null) mark.to += mylen; 2389 | } 2390 | } 2391 | }, 2392 | fixMarkEnds: function(other) { 2393 | var mk = this.marked, omk = other.marked; 2394 | if (!mk) return; 2395 | for (var i = 0; i < mk.length; ++i) { 2396 | var mark = mk[i], close = mark.to == null; 2397 | if (close && omk) { 2398 | for (var j = 0; j < omk.length; ++j) 2399 | if (omk[j].sameSet(mark)) {close = false; break;} 2400 | } 2401 | if (close) mark.to = this.text.length; 2402 | } 2403 | }, 2404 | fixMarkStarts: function() { 2405 | var mk = this.marked; 2406 | if (!mk) return; 2407 | for (var i = 0; i < mk.length; ++i) 2408 | if (mk[i].from == null) mk[i].from = 0; 2409 | }, 2410 | addMark: function(mark) { 2411 | mark.attach(this); 2412 | if (this.marked == null) this.marked = []; 2413 | this.marked.push(mark); 2414 | this.marked.sort(function(a, b){return (a.from || 0) - (b.from || 0);}); 2415 | }, 2416 | // Run the given mode's parser over a line, update the styles 2417 | // array, which contains alternating fragments of text and CSS 2418 | // classes. 2419 | highlight: function(mode, state, tabSize) { 2420 | var stream = new StringStream(this.text, tabSize), st = this.styles, pos = 0; 2421 | var changed = false, curWord = st[0], prevWord; 2422 | if (this.text == "" && mode.blankLine) mode.blankLine(state); 2423 | while (!stream.eol()) { 2424 | var style = mode.token(stream, state); 2425 | var substr = this.text.slice(stream.start, stream.pos); 2426 | stream.start = stream.pos; 2427 | if (pos && st[pos-1] == style) 2428 | st[pos-2] += substr; 2429 | else if (substr) { 2430 | if (!changed && (st[pos+1] != style || (pos && st[pos-2] != prevWord))) changed = true; 2431 | st[pos++] = substr; st[pos++] = style; 2432 | prevWord = curWord; curWord = st[pos]; 2433 | } 2434 | // Give up when line is ridiculously long 2435 | if (stream.pos > 5000) { 2436 | st[pos++] = this.text.slice(stream.pos); st[pos++] = null; 2437 | break; 2438 | } 2439 | } 2440 | if (st.length != pos) {st.length = pos; changed = true;} 2441 | if (pos && st[pos-2] != prevWord) changed = true; 2442 | // Short lines with simple highlights return null, and are 2443 | // counted as changed by the driver because they are likely to 2444 | // highlight the same way in various contexts. 2445 | return changed || (st.length < 5 && this.text.length < 10 ? null : false); 2446 | }, 2447 | // Fetch the parser token for a given character. Useful for hacks 2448 | // that want to inspect the mode state (say, for completion). 2449 | getTokenAt: function(mode, state, ch) { 2450 | var txt = this.text, stream = new StringStream(txt); 2451 | while (stream.pos < ch && !stream.eol()) { 2452 | stream.start = stream.pos; 2453 | var style = mode.token(stream, state); 2454 | } 2455 | return {start: stream.start, 2456 | end: stream.pos, 2457 | string: stream.current(), 2458 | className: style || null, 2459 | state: state}; 2460 | }, 2461 | indentation: function(tabSize) {return countColumn(this.text, null, tabSize);}, 2462 | // Produces an HTML fragment for the line, taking selection, 2463 | // marking, and highlighting into account. 2464 | getHTML: function(makeTab, wrapAt, wrapId, wrapWBR) { 2465 | var html = [], first = true, col = 0; 2466 | function span_(text, style) { 2467 | if (!text) return; 2468 | // Work around a bug where, in some compat modes, IE ignores leading spaces 2469 | if (first && ie && text.charAt(0) == " ") text = "\u00a0" + text.slice(1); 2470 | first = false; 2471 | if (text.indexOf("\t") == -1) { 2472 | col += text.length; 2473 | var escaped = htmlEscape(text); 2474 | } else { 2475 | var escaped = ""; 2476 | for (var pos = 0;;) { 2477 | var idx = text.indexOf("\t", pos); 2478 | if (idx == -1) { 2479 | escaped += htmlEscape(text.slice(pos)); 2480 | col += text.length - pos; 2481 | break; 2482 | } else { 2483 | col += idx - pos; 2484 | var tab = makeTab(col); 2485 | escaped += htmlEscape(text.slice(pos, idx)) + tab.html; 2486 | col += tab.width; 2487 | pos = idx + 1; 2488 | } 2489 | } 2490 | } 2491 | if (style) html.push('', escaped, ""); 2492 | else html.push(escaped); 2493 | } 2494 | var span = span_; 2495 | if (wrapAt != null) { 2496 | var outPos = 0, open = ""; 2497 | span = function(text, style) { 2498 | var l = text.length; 2499 | if (wrapAt >= outPos && wrapAt < outPos + l) { 2500 | if (wrapAt > outPos) { 2501 | span_(text.slice(0, wrapAt - outPos), style); 2502 | // See comment at the definition of spanAffectsWrapping 2503 | if (wrapWBR) html.push(""); 2504 | } 2505 | html.push(open); 2506 | var cut = wrapAt - outPos; 2507 | span_(window.opera ? text.slice(cut, cut + 1) : text.slice(cut), style); 2508 | html.push(""); 2509 | if (window.opera) span_(text.slice(cut + 1), style); 2510 | wrapAt--; 2511 | outPos += l; 2512 | } else { 2513 | outPos += l; 2514 | span_(text, style); 2515 | // Output empty wrapper when at end of line 2516 | if (outPos == wrapAt && outPos == len) html.push(open + " "); 2517 | // Stop outputting HTML when gone sufficiently far beyond measure 2518 | else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){}; 2519 | } 2520 | } 2521 | } 2522 | 2523 | var st = this.styles, allText = this.text, marked = this.marked; 2524 | var len = allText.length; 2525 | function styleToClass(style) { 2526 | if (!style) return null; 2527 | return "cm-" + style.replace(/ +/g, " cm-"); 2528 | } 2529 | 2530 | if (!allText && wrapAt == null) { 2531 | span(" "); 2532 | } else if (!marked || !marked.length) { 2533 | for (var i = 0, ch = 0; ch < len; i+=2) { 2534 | var str = st[i], style = st[i+1], l = str.length; 2535 | if (ch + l > len) str = str.slice(0, len - ch); 2536 | ch += l; 2537 | span(str, styleToClass(style)); 2538 | } 2539 | } else { 2540 | var pos = 0, i = 0, text = "", style, sg = 0; 2541 | var nextChange = marked[0].from || 0, marks = [], markpos = 0; 2542 | function advanceMarks() { 2543 | var m; 2544 | while (markpos < marked.length && 2545 | ((m = marked[markpos]).from == pos || m.from == null)) { 2546 | if (m.style != null) marks.push(m); 2547 | ++markpos; 2548 | } 2549 | nextChange = markpos < marked.length ? marked[markpos].from : Infinity; 2550 | for (var i = 0; i < marks.length; ++i) { 2551 | var to = marks[i].to || Infinity; 2552 | if (to == pos) marks.splice(i--, 1); 2553 | else nextChange = Math.min(to, nextChange); 2554 | } 2555 | } 2556 | var m = 0; 2557 | while (pos < len) { 2558 | if (nextChange == pos) advanceMarks(); 2559 | var upto = Math.min(len, nextChange); 2560 | while (true) { 2561 | if (text) { 2562 | var end = pos + text.length; 2563 | var appliedStyle = style; 2564 | for (var j = 0; j < marks.length; ++j) 2565 | appliedStyle = (appliedStyle ? appliedStyle + " " : "") + marks[j].style; 2566 | span(end > upto ? text.slice(0, upto - pos) : text, appliedStyle); 2567 | if (end >= upto) {text = text.slice(upto - pos); pos = upto; break;} 2568 | pos = end; 2569 | } 2570 | text = st[i++]; style = styleToClass(st[i++]); 2571 | } 2572 | } 2573 | } 2574 | return html.join(""); 2575 | }, 2576 | cleanUp: function() { 2577 | this.parent = null; 2578 | if (this.marked) 2579 | for (var i = 0, e = this.marked.length; i < e; ++i) this.marked[i].detach(this); 2580 | } 2581 | }; 2582 | // Utility used by replace and split above 2583 | function copyStyles(from, to, source, dest) { 2584 | for (var i = 0, pos = 0, state = 0; pos < to; i+=2) { 2585 | var part = source[i], end = pos + part.length; 2586 | if (state == 0) { 2587 | if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]); 2588 | if (end >= from) state = 1; 2589 | } 2590 | else if (state == 1) { 2591 | if (end > to) dest.push(part.slice(0, to - pos), source[i+1]); 2592 | else dest.push(part, source[i+1]); 2593 | } 2594 | pos = end; 2595 | } 2596 | } 2597 | 2598 | // Data structure that holds the sequence of lines. 2599 | function LeafChunk(lines) { 2600 | this.lines = lines; 2601 | this.parent = null; 2602 | for (var i = 0, e = lines.length, height = 0; i < e; ++i) { 2603 | lines[i].parent = this; 2604 | height += lines[i].height; 2605 | } 2606 | this.height = height; 2607 | } 2608 | LeafChunk.prototype = { 2609 | chunkSize: function() { return this.lines.length; }, 2610 | remove: function(at, n, callbacks) { 2611 | for (var i = at, e = at + n; i < e; ++i) { 2612 | var line = this.lines[i]; 2613 | this.height -= line.height; 2614 | line.cleanUp(); 2615 | if (line.handlers) 2616 | for (var j = 0; j < line.handlers.length; ++j) callbacks.push(line.handlers[j]); 2617 | } 2618 | this.lines.splice(at, n); 2619 | }, 2620 | collapse: function(lines) { 2621 | lines.splice.apply(lines, [lines.length, 0].concat(this.lines)); 2622 | }, 2623 | insertHeight: function(at, lines, height) { 2624 | this.height += height; 2625 | this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); 2626 | for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this; 2627 | }, 2628 | iterN: function(at, n, op) { 2629 | for (var e = at + n; at < e; ++at) 2630 | if (op(this.lines[at])) return true; 2631 | } 2632 | }; 2633 | function BranchChunk(children) { 2634 | this.children = children; 2635 | var size = 0, height = 0; 2636 | for (var i = 0, e = children.length; i < e; ++i) { 2637 | var ch = children[i]; 2638 | size += ch.chunkSize(); height += ch.height; 2639 | ch.parent = this; 2640 | } 2641 | this.size = size; 2642 | this.height = height; 2643 | this.parent = null; 2644 | } 2645 | BranchChunk.prototype = { 2646 | chunkSize: function() { return this.size; }, 2647 | remove: function(at, n, callbacks) { 2648 | this.size -= n; 2649 | for (var i = 0; i < this.children.length; ++i) { 2650 | var child = this.children[i], sz = child.chunkSize(); 2651 | if (at < sz) { 2652 | var rm = Math.min(n, sz - at), oldHeight = child.height; 2653 | child.remove(at, rm, callbacks); 2654 | this.height -= oldHeight - child.height; 2655 | if (sz == rm) { this.children.splice(i--, 1); child.parent = null; } 2656 | if ((n -= rm) == 0) break; 2657 | at = 0; 2658 | } else at -= sz; 2659 | } 2660 | if (this.size - n < 25) { 2661 | var lines = []; 2662 | this.collapse(lines); 2663 | this.children = [new LeafChunk(lines)]; 2664 | this.children[0].parent = this; 2665 | } 2666 | }, 2667 | collapse: function(lines) { 2668 | for (var i = 0, e = this.children.length; i < e; ++i) this.children[i].collapse(lines); 2669 | }, 2670 | insert: function(at, lines) { 2671 | var height = 0; 2672 | for (var i = 0, e = lines.length; i < e; ++i) height += lines[i].height; 2673 | this.insertHeight(at, lines, height); 2674 | }, 2675 | insertHeight: function(at, lines, height) { 2676 | this.size += lines.length; 2677 | this.height += height; 2678 | for (var i = 0, e = this.children.length; i < e; ++i) { 2679 | var child = this.children[i], sz = child.chunkSize(); 2680 | if (at <= sz) { 2681 | child.insertHeight(at, lines, height); 2682 | if (child.lines && child.lines.length > 50) { 2683 | while (child.lines.length > 50) { 2684 | var spilled = child.lines.splice(child.lines.length - 25, 25); 2685 | var newleaf = new LeafChunk(spilled); 2686 | child.height -= newleaf.height; 2687 | this.children.splice(i + 1, 0, newleaf); 2688 | newleaf.parent = this; 2689 | } 2690 | this.maybeSpill(); 2691 | } 2692 | break; 2693 | } 2694 | at -= sz; 2695 | } 2696 | }, 2697 | maybeSpill: function() { 2698 | if (this.children.length <= 10) return; 2699 | var me = this; 2700 | do { 2701 | var spilled = me.children.splice(me.children.length - 5, 5); 2702 | var sibling = new BranchChunk(spilled); 2703 | if (!me.parent) { // Become the parent node 2704 | var copy = new BranchChunk(me.children); 2705 | copy.parent = me; 2706 | me.children = [copy, sibling]; 2707 | me = copy; 2708 | } else { 2709 | me.size -= sibling.size; 2710 | me.height -= sibling.height; 2711 | var myIndex = indexOf(me.parent.children, me); 2712 | me.parent.children.splice(myIndex + 1, 0, sibling); 2713 | } 2714 | sibling.parent = me.parent; 2715 | } while (me.children.length > 10); 2716 | me.parent.maybeSpill(); 2717 | }, 2718 | iter: function(from, to, op) { this.iterN(from, to - from, op); }, 2719 | iterN: function(at, n, op) { 2720 | for (var i = 0, e = this.children.length; i < e; ++i) { 2721 | var child = this.children[i], sz = child.chunkSize(); 2722 | if (at < sz) { 2723 | var used = Math.min(n, sz - at); 2724 | if (child.iterN(at, used, op)) return true; 2725 | if ((n -= used) == 0) break; 2726 | at = 0; 2727 | } else at -= sz; 2728 | } 2729 | } 2730 | }; 2731 | 2732 | function getLineAt(chunk, n) { 2733 | while (!chunk.lines) { 2734 | for (var i = 0;; ++i) { 2735 | var child = chunk.children[i], sz = child.chunkSize(); 2736 | if (n < sz) { chunk = child; break; } 2737 | n -= sz; 2738 | } 2739 | } 2740 | return chunk.lines[n]; 2741 | } 2742 | function lineNo(line) { 2743 | if (line.parent == null) return null; 2744 | var cur = line.parent, no = indexOf(cur.lines, line); 2745 | for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { 2746 | for (var i = 0, e = chunk.children.length; ; ++i) { 2747 | if (chunk.children[i] == cur) break; 2748 | no += chunk.children[i].chunkSize(); 2749 | } 2750 | } 2751 | return no; 2752 | } 2753 | function lineAtHeight(chunk, h) { 2754 | var n = 0; 2755 | outer: do { 2756 | for (var i = 0, e = chunk.children.length; i < e; ++i) { 2757 | var child = chunk.children[i], ch = child.height; 2758 | if (h < ch) { chunk = child; continue outer; } 2759 | h -= ch; 2760 | n += child.chunkSize(); 2761 | } 2762 | return n; 2763 | } while (!chunk.lines); 2764 | for (var i = 0, e = chunk.lines.length; i < e; ++i) { 2765 | var line = chunk.lines[i], lh = line.height; 2766 | if (h < lh) break; 2767 | h -= lh; 2768 | } 2769 | return n + i; 2770 | } 2771 | function heightAtLine(chunk, n) { 2772 | var h = 0; 2773 | outer: do { 2774 | for (var i = 0, e = chunk.children.length; i < e; ++i) { 2775 | var child = chunk.children[i], sz = child.chunkSize(); 2776 | if (n < sz) { chunk = child; continue outer; } 2777 | n -= sz; 2778 | h += child.height; 2779 | } 2780 | return h; 2781 | } while (!chunk.lines); 2782 | for (var i = 0; i < n; ++i) h += chunk.lines[i].height; 2783 | return h; 2784 | } 2785 | 2786 | // The history object 'chunks' changes that are made close together 2787 | // and at almost the same time into bigger undoable units. 2788 | function History() { 2789 | this.time = 0; 2790 | this.done = []; this.undone = []; 2791 | this.compound = 0; 2792 | this.closed = false; 2793 | } 2794 | History.prototype = { 2795 | addChange: function(start, added, old) { 2796 | this.undone.length = 0; 2797 | var time = +new Date, cur = this.done[this.done.length - 1], last = cur && cur[cur.length - 1]; 2798 | var dtime = time - this.time; 2799 | 2800 | if (this.compound && cur && !this.closed) { 2801 | cur.push({start: start, added: added, old: old}); 2802 | } else if (dtime > 400 || !last || this.closed || 2803 | last.start > start + old.length || last.start + last.added < start) { 2804 | this.done.push([{start: start, added: added, old: old}]); 2805 | this.closed = false; 2806 | } else { 2807 | var startBefore = Math.max(0, last.start - start), 2808 | endAfter = Math.max(0, (start + old.length) - (last.start + last.added)); 2809 | for (var i = startBefore; i > 0; --i) last.old.unshift(old[i - 1]); 2810 | for (var i = endAfter; i > 0; --i) last.old.push(old[old.length - i]); 2811 | if (startBefore) last.start = start; 2812 | last.added += added - (old.length - startBefore - endAfter); 2813 | } 2814 | this.time = time; 2815 | }, 2816 | startCompound: function() { 2817 | if (!this.compound++) this.closed = true; 2818 | }, 2819 | endCompound: function() { 2820 | if (!--this.compound) this.closed = true; 2821 | } 2822 | }; 2823 | 2824 | function stopMethod() {e_stop(this);} 2825 | // Ensure an event has a stop method. 2826 | function addStop(event) { 2827 | if (!event.stop) event.stop = stopMethod; 2828 | return event; 2829 | } 2830 | 2831 | function e_preventDefault(e) { 2832 | if (e.preventDefault) e.preventDefault(); 2833 | else e.returnValue = false; 2834 | } 2835 | function e_stopPropagation(e) { 2836 | if (e.stopPropagation) e.stopPropagation(); 2837 | else e.cancelBubble = true; 2838 | } 2839 | function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} 2840 | CodeMirror.e_stop = e_stop; 2841 | CodeMirror.e_preventDefault = e_preventDefault; 2842 | CodeMirror.e_stopPropagation = e_stopPropagation; 2843 | 2844 | function e_target(e) {return e.target || e.srcElement;} 2845 | function e_button(e) { 2846 | if (e.which) return e.which; 2847 | else if (e.button & 1) return 1; 2848 | else if (e.button & 2) return 3; 2849 | else if (e.button & 4) return 2; 2850 | } 2851 | 2852 | // Allow 3rd-party code to override event properties by adding an override 2853 | // object to an event object. 2854 | function e_prop(e, prop) { 2855 | var overridden = e.override && e.override.hasOwnProperty(prop); 2856 | return overridden ? e.override[prop] : e[prop]; 2857 | } 2858 | 2859 | // Event handler registration. If disconnect is true, it'll return a 2860 | // function that unregisters the handler. 2861 | function connect(node, type, handler, disconnect) { 2862 | if (typeof node.addEventListener == "function") { 2863 | node.addEventListener(type, handler, false); 2864 | if (disconnect) return function() {node.removeEventListener(type, handler, false);}; 2865 | } 2866 | else { 2867 | var wrapHandler = function(event) {handler(event || window.event);}; 2868 | node.attachEvent("on" + type, wrapHandler); 2869 | if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);}; 2870 | } 2871 | } 2872 | CodeMirror.connect = connect; 2873 | 2874 | function Delayed() {this.id = null;} 2875 | Delayed.prototype = {set: function(ms, f) {clearTimeout(this.id); this.id = setTimeout(f, ms);}}; 2876 | 2877 | var Pass = CodeMirror.Pass = {toString: function(){return "CodeMirror.Pass";}}; 2878 | 2879 | var gecko = /gecko\/\d{7}/i.test(navigator.userAgent); 2880 | var ie = /MSIE \d/.test(navigator.userAgent); 2881 | var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent); 2882 | var quirksMode = ie && document.documentMode == 5; 2883 | var webkit = /WebKit\//.test(navigator.userAgent); 2884 | var chrome = /Chrome\//.test(navigator.userAgent); 2885 | var safari = /Apple Computer/.test(navigator.vendor); 2886 | var khtml = /KHTML\//.test(navigator.userAgent); 2887 | 2888 | // Detect drag-and-drop 2889 | var dragAndDrop = function() { 2890 | // There is *some* kind of drag-and-drop support in IE6-8, but I 2891 | // couldn't get it to work yet. 2892 | if (ie_lt9) return false; 2893 | var div = document.createElement('div'); 2894 | return "draggable" in div || "dragDrop" in div; 2895 | }(); 2896 | 2897 | // Feature-detect whether newlines in textareas are converted to \r\n 2898 | var lineSep = function () { 2899 | var te = document.createElement("textarea"); 2900 | te.value = "foo\nbar"; 2901 | if (te.value.indexOf("\r") > -1) return "\r\n"; 2902 | return "\n"; 2903 | }(); 2904 | 2905 | // For a reason I have yet to figure out, some browsers disallow 2906 | // word wrapping between certain characters *only* if a new inline 2907 | // element is started between them. This makes it hard to reliably 2908 | // measure the position of things, since that requires inserting an 2909 | // extra span. This terribly fragile set of regexps matches the 2910 | // character combinations that suffer from this phenomenon on the 2911 | // various browsers. 2912 | var spanAffectsWrapping = /^$/; // Won't match any two-character string 2913 | if (gecko) spanAffectsWrapping = /$'/; 2914 | else if (safari) spanAffectsWrapping = /\-[^ \-?]|\?[^ !'\"\),.\-\/:;\?\]\}]/; 2915 | else if (chrome) spanAffectsWrapping = /\-[^ \-\.?]|\?[^ \-\.?\]\}:;!'\"\),\/]|[\.!\"#&%\)*+,:;=>\]|\}~][\(\{\[<]|\$'/; 2916 | 2917 | // Counts the column offset in a string, taking tabs into account. 2918 | // Used mostly to find indentation. 2919 | function countColumn(string, end, tabSize) { 2920 | if (end == null) { 2921 | end = string.search(/[^\s\u00a0]/); 2922 | if (end == -1) end = string.length; 2923 | } 2924 | for (var i = 0, n = 0; i < end; ++i) { 2925 | if (string.charAt(i) == "\t") n += tabSize - (n % tabSize); 2926 | else ++n; 2927 | } 2928 | return n; 2929 | } 2930 | 2931 | function computedStyle(elt) { 2932 | if (elt.currentStyle) return elt.currentStyle; 2933 | return window.getComputedStyle(elt, null); 2934 | } 2935 | 2936 | // Find the position of an element by following the offsetParent chain. 2937 | // If screen==true, it returns screen (rather than page) coordinates. 2938 | function eltOffset(node, screen) { 2939 | var bod = node.ownerDocument.body; 2940 | var x = 0, y = 0, skipBody = false; 2941 | for (var n = node; n; n = n.offsetParent) { 2942 | var ol = n.offsetLeft, ot = n.offsetTop; 2943 | // Firefox reports weird inverted offsets when the body has a border. 2944 | if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); } 2945 | else { x += ol, y += ot; } 2946 | if (screen && computedStyle(n).position == "fixed") 2947 | skipBody = true; 2948 | } 2949 | var e = screen && !skipBody ? null : bod; 2950 | for (var n = node.parentNode; n != e; n = n.parentNode) 2951 | if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;} 2952 | return {left: x, top: y}; 2953 | } 2954 | // Use the faster and saner getBoundingClientRect method when possible. 2955 | if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) { 2956 | // Take the parts of bounding client rect that we are interested in so we are able to edit if need be, 2957 | // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page) 2958 | try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; } 2959 | catch(e) { box = {top: 0, left: 0}; } 2960 | if (!screen) { 2961 | // Get the toplevel scroll, working around browser differences. 2962 | if (window.pageYOffset == null) { 2963 | var t = document.documentElement || document.body.parentNode; 2964 | if (t.scrollTop == null) t = document.body; 2965 | box.top += t.scrollTop; box.left += t.scrollLeft; 2966 | } else { 2967 | box.top += window.pageYOffset; box.left += window.pageXOffset; 2968 | } 2969 | } 2970 | return box; 2971 | }; 2972 | 2973 | // Get a node's text content. 2974 | function eltText(node) { 2975 | return node.textContent || node.innerText || node.nodeValue || ""; 2976 | } 2977 | function selectInput(node) { 2978 | if (ios) { // Mobile Safari apparently has a bug where select() is broken. 2979 | node.selectionStart = 0; 2980 | node.selectionEnd = node.value.length; 2981 | } else node.select(); 2982 | } 2983 | 2984 | // Operations on {line, ch} objects. 2985 | function posEq(a, b) {return a.line == b.line && a.ch == b.ch;} 2986 | function posLess(a, b) {return a.line < b.line || (a.line == b.line && a.ch < b.ch);} 2987 | function copyPos(x) {return {line: x.line, ch: x.ch};} 2988 | 2989 | var escapeElement = document.createElement("pre"); 2990 | function htmlEscape(str) { 2991 | escapeElement.textContent = str; 2992 | return escapeElement.innerHTML; 2993 | } 2994 | // Recent (late 2011) Opera betas insert bogus newlines at the start 2995 | // of the textContent, so we strip those. 2996 | if (htmlEscape("a") == "\na") 2997 | htmlEscape = function(str) { 2998 | escapeElement.textContent = str; 2999 | return escapeElement.innerHTML.slice(1); 3000 | }; 3001 | // Some IEs don't preserve tabs through innerHTML 3002 | else if (htmlEscape("\t") != "\t") 3003 | htmlEscape = function(str) { 3004 | escapeElement.innerHTML = ""; 3005 | escapeElement.appendChild(document.createTextNode(str)); 3006 | return escapeElement.innerHTML; 3007 | }; 3008 | CodeMirror.htmlEscape = htmlEscape; 3009 | 3010 | // Used to position the cursor after an undo/redo by finding the 3011 | // last edited character. 3012 | function editEnd(from, to) { 3013 | if (!to) return 0; 3014 | if (!from) return to.length; 3015 | for (var i = from.length, j = to.length; i >= 0 && j >= 0; --i, --j) 3016 | if (from.charAt(i) != to.charAt(j)) break; 3017 | return j + 1; 3018 | } 3019 | 3020 | function indexOf(collection, elt) { 3021 | if (collection.indexOf) return collection.indexOf(elt); 3022 | for (var i = 0, e = collection.length; i < e; ++i) 3023 | if (collection[i] == elt) return i; 3024 | return -1; 3025 | } 3026 | function isWordChar(ch) { 3027 | return /\w/.test(ch) || ch.toUpperCase() != ch.toLowerCase(); 3028 | } 3029 | 3030 | // See if "".split is the broken IE version, if so, provide an 3031 | // alternative way to split lines. 3032 | var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) { 3033 | var pos = 0, nl, result = []; 3034 | while ((nl = string.indexOf("\n", pos)) > -1) { 3035 | result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl)); 3036 | pos = nl + 1; 3037 | } 3038 | result.push(string.slice(pos)); 3039 | return result; 3040 | } : function(string){return string.split(/\r?\n/);}; 3041 | CodeMirror.splitLines = splitLines; 3042 | 3043 | var hasSelection = window.getSelection ? function(te) { 3044 | try { return te.selectionStart != te.selectionEnd; } 3045 | catch(e) { return false; } 3046 | } : function(te) { 3047 | try {var range = te.ownerDocument.selection.createRange();} 3048 | catch(e) {} 3049 | if (!range || range.parentElement() != te) return false; 3050 | return range.compareEndPoints("StartToEnd", range) != 0; 3051 | }; 3052 | 3053 | CodeMirror.defineMode("null", function() { 3054 | return {token: function(stream) {stream.skipToEnd();}}; 3055 | }); 3056 | CodeMirror.defineMIME("text/plain", "null"); 3057 | 3058 | var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 3059 | 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 3060 | 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 3061 | 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 127: "Delete", 186: ";", 187: "=", 188: ",", 3062 | 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp", 3063 | 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right", 3064 | 63233: "Down", 63302: "Insert", 63272: "Delete"}; 3065 | CodeMirror.keyNames = keyNames; 3066 | (function() { 3067 | // Number keys 3068 | for (var i = 0; i < 10; i++) keyNames[i + 48] = String(i); 3069 | // Alphabetic keys 3070 | for (var i = 65; i <= 90; i++) keyNames[i] = String.fromCharCode(i); 3071 | // Function keys 3072 | for (var i = 1; i <= 12; i++) keyNames[i + 111] = keyNames[i + 63235] = "F" + i; 3073 | })(); 3074 | 3075 | return CodeMirror; 3076 | })(); 3077 | --------------------------------------------------------------------------------