├── CNAME ├── favicon.ico ├── index.html └── lib ├── codemirror.css ├── codemirror.js ├── codemirror.min.js ├── mergely.css ├── mergely.js ├── mergely.min.js └── searchcursor.js /CNAME: -------------------------------------------------------------------------------- 1 | diff.hust.cc -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aTool-org/diff-online/ff208644c24588c21da7dcf2fc018a464917d08e/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | Online File Diff and Mergely, Powered By aTool. 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /lib/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 300px; 7 | } 8 | .CodeMirror-scroll { 9 | /* Set scrolling behaviour here */ 10 | overflow: auto; 11 | } 12 | 13 | /* PADDING */ 14 | 15 | .CodeMirror-lines { 16 | padding: 4px 0; /* Vertical padding around content */ 17 | } 18 | .CodeMirror pre { 19 | padding: 0 4px; /* Horizontal padding of content */ 20 | } 21 | 22 | .CodeMirror-scrollbar-filler { 23 | background-color: white; /* The little square between H and V scrollbars */ 24 | } 25 | 26 | /* GUTTER */ 27 | 28 | .CodeMirror-gutters { 29 | border-right: 1px solid #ddd; 30 | background-color: #f7f7f7; 31 | } 32 | .CodeMirror-linenumbers {} 33 | .CodeMirror-linenumber { 34 | padding: 0 3px 0 5px; 35 | min-width: 20px; 36 | text-align: right; 37 | color: #999; 38 | } 39 | 40 | /* CURSOR */ 41 | 42 | .CodeMirror div.CodeMirror-cursor { 43 | border-left: 1px solid black; 44 | } 45 | /* Shown when moving in bi-directional text */ 46 | .CodeMirror div.CodeMirror-secondarycursor { 47 | border-left: 1px solid silver; 48 | } 49 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 50 | width: auto; 51 | border: 0; 52 | background: transparent; 53 | background: rgba(0, 200, 0, .4); 54 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#6600c800, endColorstr=#4c00c800); 55 | } 56 | /* Kludge to turn off filter in ie9+, which also accepts rgba */ 57 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor:not(#nonsense_id) { 58 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 59 | } 60 | /* Can style cursor different in overwrite (non-insert) mode */ 61 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 62 | 63 | /* DEFAULT THEME */ 64 | 65 | .cm-s-default .cm-keyword {color: #708;} 66 | .cm-s-default .cm-atom {color: #219;} 67 | .cm-s-default .cm-number {color: #164;} 68 | .cm-s-default .cm-def {color: #00f;} 69 | .cm-s-default .cm-variable {color: black;} 70 | .cm-s-default .cm-variable-2 {color: #05a;} 71 | .cm-s-default .cm-variable-3 {color: #085;} 72 | .cm-s-default .cm-property {color: black;} 73 | .cm-s-default .cm-operator {color: black;} 74 | .cm-s-default .cm-comment {color: #a50;} 75 | .cm-s-default .cm-string {color: #a11;} 76 | .cm-s-default .cm-string-2 {color: #f50;} 77 | .cm-s-default .cm-meta {color: #555;} 78 | .cm-s-default .cm-error {color: #f00;} 79 | .cm-s-default .cm-qualifier {color: #555;} 80 | .cm-s-default .cm-builtin {color: #30a;} 81 | .cm-s-default .cm-bracket {color: #997;} 82 | .cm-s-default .cm-tag {color: #170;} 83 | .cm-s-default .cm-attribute {color: #00c;} 84 | .cm-s-default .cm-header {color: blue;} 85 | .cm-s-default .cm-quote {color: #090;} 86 | .cm-s-default .cm-hr {color: #999;} 87 | .cm-s-default .cm-link {color: #00c;} 88 | 89 | .cm-negative {color: #d44;} 90 | .cm-positive {color: #292;} 91 | .cm-header, .cm-strong {font-weight: bold;} 92 | .cm-em {font-style: italic;} 93 | .cm-emstrong {font-style: italic; font-weight: bold;} 94 | .cm-link {text-decoration: underline;} 95 | 96 | .cm-invalidchar {color: #f00;} 97 | 98 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 99 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 100 | 101 | /* STOP */ 102 | 103 | /* The rest of this file contains styles related to the mechanics of 104 | the editor. You probably shouldn't touch them. */ 105 | 106 | .CodeMirror { 107 | line-height: 1; 108 | position: relative; 109 | overflow: hidden; 110 | } 111 | 112 | .CodeMirror-scroll { 113 | /* 30px is the magic margin used to hide the element's real scrollbars */ 114 | /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */ 115 | margin-bottom: -30px; margin-right: -30px; 116 | padding-bottom: 30px; padding-right: 30px; 117 | height: 100%; 118 | outline: none; /* Prevent dragging from highlighting the element */ 119 | position: relative; 120 | } 121 | .CodeMirror-sizer { 122 | position: relative; 123 | } 124 | 125 | /* The fake, visible scrollbars. Used to force redraw during scrolling 126 | before actuall scrolling happens, thus preventing shaking and 127 | flickering artifacts. */ 128 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler { 129 | position: absolute; 130 | z-index: 6; 131 | display: none; 132 | } 133 | .CodeMirror-vscrollbar { 134 | right: 0; top: 0; 135 | overflow-x: hidden; 136 | overflow-y: scroll; 137 | } 138 | .CodeMirror-hscrollbar { 139 | bottom: 0; left: 0; 140 | overflow-y: hidden; 141 | overflow-x: scroll; 142 | } 143 | .CodeMirror-scrollbar-filler { 144 | right: 0; bottom: 0; 145 | z-index: 6; 146 | } 147 | 148 | .CodeMirror-gutters { 149 | position: absolute; left: 0; top: 0; 150 | height: 100%; 151 | padding-bottom: 30px; 152 | z-index: 3; 153 | } 154 | .CodeMirror-gutter { 155 | height: 100%; 156 | display: inline-block; 157 | /* Hack to make IE7 behave */ 158 | *zoom:1; 159 | *display:inline; 160 | } 161 | .CodeMirror-gutter-elt { 162 | position: absolute; 163 | cursor: default; 164 | z-index: 4; 165 | } 166 | 167 | .CodeMirror-lines { 168 | cursor: text; 169 | } 170 | .CodeMirror pre { 171 | /* Reset some styles that the rest of the page might have set */ 172 | -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0; 173 | border-width: 0; 174 | background: transparent; 175 | font-family: inherit; 176 | font-size: inherit; 177 | margin: 0; 178 | white-space: pre; 179 | word-wrap: normal; 180 | line-height: inherit; 181 | color: inherit; 182 | z-index: 2; 183 | position: relative; 184 | overflow: visible; 185 | } 186 | .CodeMirror-wrap pre { 187 | word-wrap: break-word; 188 | white-space: pre-wrap; 189 | word-break: normal; 190 | } 191 | .CodeMirror-linebackground { 192 | position: absolute; 193 | left: 0; right: 0; top: 0; bottom: 0; 194 | z-index: 0; 195 | } 196 | 197 | .CodeMirror-linewidget { 198 | position: relative; 199 | z-index: 2; 200 | overflow: auto; 201 | } 202 | 203 | .CodeMirror-widget { 204 | display: inline-block; 205 | } 206 | 207 | .CodeMirror-wrap .CodeMirror-scroll { 208 | overflow-x: hidden; 209 | } 210 | 211 | .CodeMirror-measure { 212 | position: absolute; 213 | width: 100%; height: 0px; 214 | overflow: hidden; 215 | visibility: hidden; 216 | } 217 | .CodeMirror-measure pre { position: static; } 218 | 219 | .CodeMirror div.CodeMirror-cursor { 220 | position: absolute; 221 | visibility: hidden; 222 | border-right: none; 223 | width: 0; 224 | } 225 | .CodeMirror-focused div.CodeMirror-cursor { 226 | visibility: visible; 227 | } 228 | 229 | .CodeMirror-selected { background: #d9d9d9; } 230 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 231 | 232 | .cm-searching { 233 | background: #ffa; 234 | background: rgba(255, 255, 0, .4); 235 | } 236 | 237 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 238 | .CodeMirror span { *vertical-align: text-bottom; } 239 | 240 | @media print { 241 | /* Hide the cursor when printing */ 242 | .CodeMirror div.CodeMirror-cursor { 243 | visibility: hidden; 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /lib/codemirror.min.js: -------------------------------------------------------------------------------- 1 | window.CodeMirror=function(){"use strict";function w(a,c){if(!(this instanceof w))return new w(a,c);this.options=c=c||{};for(var d in Mc)!c.hasOwnProperty(d)&&Mc.hasOwnProperty(d)&&(c[d]=Mc[d]);I(c);var e="string"==typeof c.value?0:c.value.first,f=this.display=x(a,e);f.wrapper.CodeMirror=this,F(this),c.autofocus&&!o&&Db(this),this.state={keyMaps:[],overlays:[],modeGen:0,overwrite:!1,focused:!1,suppressEdits:!1,pasteIncoming:!1,draggingText:!1,highlight:new De},D(this),c.lineWrapping&&(this.display.wrapper.className+=" CodeMirror-wrap");var g=c.value;"string"==typeof g&&(g=new Rd(c.value,c.mode)),vb(this,Vd)(this,g),b&&setTimeout(Ne(Cb,this,!0),20),Fb(this);var h;try{h=document.activeElement==f.input}catch(i){}h||c.autofocus&&!o?setTimeout(Ne(_b,this),20):ac(this),vb(this,function(){for(var a in Lc)Lc.propertyIsEnumerable(a)&&Lc[a](this,c[a],Oc);for(var b=0;bb.maxLineLength&&(b.maxLineLength=d,b.maxLine=a)})}function I(a){for(var b=!1,c=0;ca.scroller.clientWidth,f=d>a.scroller.clientHeight;f?(a.scrollbarV.style.display="block",a.scrollbarV.style.bottom=e?$e(a.measure)+"px":"0",a.scrollbarV.firstChild.style.height=d-a.scroller.clientHeight+a.scrollbarV.clientHeight+"px"):a.scrollbarV.style.display="",e?(a.scrollbarH.style.display="block",a.scrollbarH.style.right=f?$e(a.measure)+"px":"0",a.scrollbarH.firstChild.style.width=a.scroller.scrollWidth-a.scroller.clientWidth+a.scrollbarH.clientWidth+"px"):a.scrollbarH.style.display="",e&&f?(a.scrollbarFiller.style.display="block",a.scrollbarFiller.style.height=a.scrollbarFiller.style.width=$e(a.measure)+"px"):a.scrollbarFiller.style.display="",k&&0===$e(a.measure)&&(a.scrollbarV.style.minWidth=a.scrollbarH.style.minHeight=l?"18px":"12px")}function K(a,b,c){var d=a.scroller.scrollTop,e=a.wrapper.clientHeight;"number"==typeof c?d=c:c&&(d=c.top,e=c.bottom-c.top),d=Math.floor(d-db(a));var f=Math.ceil(d+e);return{from:_d(b,d),to:_d(b,f)}}function L(a){var b=a.display;if(b.alignWidgets||b.gutters.firstChild&&a.options.fixedGutter){for(var c=O(b)-b.scroller.scrollLeft+a.doc.scrollLeft,d=b.gutters.offsetWidth,e=c+"px",f=b.lineDiv.firstChild;f;f=f.nextSibling)if(f.alignable)for(var g=0,h=f.alignable;ge.showingFrom&&g.tom&&e.showingTo-m<20&&(m=Math.min(k,e.showingTo)),v)for(l=$d(pd(f,Wd(f,l)));k>m&&qd(f,Wd(f,m));)++m;var n=[{from:Math.max(e.showingFrom,f.first),to:Math.min(e.showingTo,k)}];if(n=n[0].from>=n[0].to?[]:S(n,b),v)for(var j=0;jo.from)){n.splice(j--,1);break}o.to=q}for(var r=0,j=0;jm&&(o.to=m),o.from>=o.to?n.splice(j--,1):r+=o.to-o.from}if(r==m-l&&l==e.showingFrom&&m==e.showingTo)return R(a),void 0;n.sort(function(a,b){return a.from-b.from});var s=document.activeElement;.7*(m-l)>r&&(e.lineDiv.style.display="none"),U(a,l,m,n,i),e.lineDiv.style.display="",document.activeElement!=s&&s.offsetHeight&&s.focus();var t=l!=e.showingFrom||m!=e.showingTo||e.lastSizeC!=e.wrapper.clientHeight;t&&(e.lastSizeC=e.wrapper.clientHeight),e.showingFrom=l,e.showingTo=m,_(a,100);for(var x,u=e.lineDiv.offsetTop,w=e.lineDiv.firstChild;w;w=w.nextSibling)if(w.lineObj){if(c){var y=w.offsetTop+w.offsetHeight;x=y-u,u=y}else{var z=We(w);x=z.bottom-z.top}var A=w.lineObj.height-x;if(2>x&&(x=qb(e)),A>.001||-.001>A){Zd(w.lineObj,x);var B=w.lineObj.widgets;if(B)for(var j=0;jm&&Q(a,[],d),!0}}function R(a){var b=a.display.viewOffset=ae(a,Wd(a.doc,a.display.showingFrom));a.display.mover.style.top=b+"px"}function S(a,b){for(var c=0,d=b.length||0;d>c;++c){for(var e=b[c],f=[],g=e.diff||0,h=0,i=a.length;i>h;++h){var j=a[h];e.to<=j.from&&e.diff?f.push({from:j.from+g,to:j.to+g}):e.to<=j.from||e.from>=j.to?f.push(j):(e.from>j.from&&f.push({from:j.from,to:e.from}),e.ton){for(;k.lineObj!=b;)k=l(k);i&&n>=f&&k.lineNumber&&Ve(k.lineNumber,N(a.options,n)),k=k.nextSibling}else{if(b.widgets)for(var r,p=0,q=k;q&&20>p;++p,q=q.nextSibling)if(q.lineObj==b&&/div/i.test(q.nodeName)){r=q;break}var s=V(a,b,n,g,r);if(s!=r)j.insertBefore(s,k);else{for(;k!=r;)k=l(k);k=k.nextSibling}s.lineObj=b}++n});k;)k=l(k)}function V(a,b,d,e,f){var j,g=Hd(a,b),h=b.gutterMarkers,i=a.display;if(!(a.options.lineNumbers||h||b.bgClass||b.wrapClass||b.widgets))return g;if(f){f.alignable=null;for(var n,k=!0,l=0,m=f.firstChild;m;m=n)if(n=m.nextSibling,/\bCodeMirror-linewidget\b/.test(m.className)){for(var o=0,p=!0;ob&&(b=0),e.appendChild(Se("div",null,"CodeMirror-selected","position: absolute; left: "+a+"px; top: "+b+"px; width: "+(null==c?f-a:c)+"px; height: "+(d-b)+"px"))}function i(b,d,e,i){function m(c){return kb(a,oc(b,c),"div",j)}var j=Wd(c,b),k=j.text.length,l=i?1/0:-1/0;return ff(be(j),d||0,null==e?k:e,function(a,b,c){var j=m("rtl"==c?b-1:a),n=m("rtl"==c?a:b-1),o=j.left,p=n.right;n.top-j.top>3&&(h(o,j.top,null,j.bottom),o=g,j.bottomo&&(o=g),h(o,n.top,p-o,n.bottom)}),l}var b=a.display,c=a.doc,d=a.doc.sel,e=document.createDocumentFragment(),f=b.lineSpace.offsetWidth,g=eb(a.display);if(d.from.line==d.to.line)i(d.from.line,d.from.ch,d.to.ch);else{for(var l,n,j=Wd(c,d.from.line),k=j,m=[d.from.line,d.from.ch];l=od(k);){var o=l.find();if(m.push(o.from.ch,o.to.line,o.to.ch),o.to.line==d.to.line){m.push(d.to.ch),n=!0;break}k=Wd(c,o.to.line)}if(n)for(var p=0;pq&&h(g,q,null,r)}}Ue(b.selectionDiv,e),b.selectionDiv.style.display=""}function $(a){var b=a.display;clearInterval(b.blinker);var c=!0;b.cursor.style.visibility=b.otherCursor.style.visibility="",b.blinker=setInterval(function(){b.cursor.offsetHeight&&(b.cursor.style.visibility=b.otherCursor.style.visibility=(c=!c)?"":"hidden")},a.options.cursorBlinkRate)}function _(a,b){a.doc.mode.startState&&a.doc.frontier=a.display.showingTo)){var f,c=+new Date+a.options.workTime,d=Tc(b.mode,cb(a,b.frontier)),e=[];b.iter(b.frontier,Math.min(b.first+b.size,a.display.showingTo+500),function(g){if(b.frontier>=a.display.showingFrom){var h=g.styles;g.styles=Cd(a,g,d);for(var i=!h||h.length!=g.styles.length,j=0;!i&&jc?(_(a,a.options.workDelay),!0):void 0}),e.length&&vb(a,function(){for(var a=0;ag;--f){if(f<=e.first)return e.first;var h=Wd(e,f-1);if(h.stateAfter)return f;var i=Ee(h.text,null,a.options.tabSize);(null==d||c>i)&&(d=f-1,c=i)}return d}function cb(a,b){var c=a.doc,d=a.display;if(!c.mode.startState)return!0;var e=bb(a,b),f=e>c.first&&Wd(c,e-1).stateAfter;return f=f?Tc(c.mode,f):Uc(c.mode),c.iter(e,b,function(g){Ed(a,g,f);var h=e==b-1||0==e%5||e>=d.showingFrom&&ee&&0==f&&(e=1)}return{left:c>f?g.right:g.left,right:f>c?g.left:g.right,top:g.top,bottom:g.bottom}}function gb(a,b){for(var c=a.display,d=a.display.measureLineCache,e=0;e100){for(var i=document.createDocumentFragment(),j=10,k=h.childNodes.length,l=0,m=Math.ceil(k/j);m>l;++l){for(var n=Se("div",null,null,"display: inline-block"),o=0;j>o&&k;++o)n.appendChild(h.firstChild),--k;i.appendChild(n)}h.appendChild(i)}Ue(f.measure,h);var p=We(f.lineDiv),q=[],r=Me(e.text.length),s=h.offsetHeight;d&&f.measure.first!=h&&Ue(f.measure,h);for(var t,l=0;lw||v>y)&&(v>=x&&y>=w||x>=v&&w>=y||Math.min(w,y)-Math.max(v,x)>=w-v>>1)){q[o]=Math.min(v,x),q[o+1]=Math.max(w,y);break}}o==q.length&&q.push(v,w);var z=u.right;t.measureRight&&(z=We(t.measureRight).left),r[l]={left:u.left-p.left,right:z-p.left,top:o}}for(var t,l=0;lh)return f(h,n);var q=n?m.to:m.from,r=n?m.from:m.to;if(q==h)p=l&&m.level<(o=g[l-1]).level?f(o.level%2?o.from:o.to-1,!0):f(n&&m.from!=m.to?h-1:h),n==k?i=p:j=p;else if(r==h){var o=lc)return mb(d.first,0,!0);var e=_d(d,c),f=d.first+d.size-1;if(e>f)return mb(d.first+d.size-1,Wd(d,f).text.length,!0);for(0>b&&(b=0);;){var g=Wd(d,e),h=ob(a,g,e,b,c),i=od(g),j=i&&i.find();if(!(i&&h.ch>=j.from.ch))return h;e=j.to.line}}function ob(a,b,c,d,e){function j(d){var e=lb(a,oc(c,d),"line",b,i);return g=!0,f>e.bottom?Math.max(0,e.left-h):fq)return mb(c,n,r);for(;;){if(k?n==m||n==nf(b,m,1):1>=n-m){for(var s=q-d>d-o,t=s?m:n;Re.test(b.text.charAt(t));)++t;var u=mb(c,t,s?p:r);return u.after=s,u}var v=Math.ceil(l/2),w=m+v;if(k){w=m;for(var x=0;v>x;++x)w=nf(b,w,1)}var y=j(w);y>d?(n=w,q=y,(r=g)&&(q+=1e3),l-=v):(m=w,o=y,p=g,l=v)}}function qb(a){if(null!=a.cachedTextHeight)return a.cachedTextHeight;if(null==pb){pb=Se("pre");for(var b=0;49>b;++b)pb.appendChild(document.createTextNode("x")),pb.appendChild(Se("br"));pb.appendChild(document.createTextNode("x"))}Ue(a.measure,pb);var c=pb.offsetHeight/50;return c>3&&(a.cachedTextHeight=c),Te(a.measure),c||1}function rb(a){if(null!=a.cachedCharWidth)return a.cachedCharWidth;var b=Se("span","x"),c=Se("pre",[b]);Ue(a.measure,c);var d=b.offsetWidth;return d>2&&(a.cachedCharWidth=d),d||10}function tb(a){a.curOp={changes:[],updateInput:null,userSelChange:null,textChanged:null,selectionChanged:!1,updateMaxLine:!1,updateScrollPos:!1,id:++sb},xe++||(we=[])}function ub(a){var b=a.curOp,c=a.doc,d=a.display;if(a.curOp=null,b.updateMaxLine&&H(a),d.maxLineChanged&&!a.options.lineWrapping){var e=gb(a,d.maxLine).width;d.sizer.style.minWidth=Math.max(0,e+3+Be)+"px",d.maxLineChanged=!1;var f=Math.max(0,d.sizer.offsetLeft+d.sizer.offsetWidth-d.scroller.clientWidth);fi&&d[i]==g[i];)++i;var k=f.from,l=f.to;i1e3?c.value=a.display.prevInput="":a.display.prevInput=g,h&&ub(a),a.state.pasteIncoming=!1,!0}function Cb(a,b){var c,d,e=a.doc;pc(e.sel.from,e.sel.to)?b&&(a.display.prevInput=a.display.input.value=""):(a.display.prevInput="",c=df&&(e.sel.to.line-e.sel.from.line>100||(d=a.getSelection()).length>1e3),a.display.input.value=c?"-":d||a.getSelection(),a.state.focused&&Ie(a.display.input)),a.display.inaccurateSelection=c}function Db(a){"nocursor"==a.options.readOnly||o&&document.activeElement==a.display.input||a.display.input.focus()}function Eb(a){return a.options.readOnly||a.doc.cantEdit}function Fb(a){function c(){a.state.focused&&setTimeout(Ne(Db,a),0)}function d(){b.cachedCharWidth=b.cachedTextHeight=null,ib(a),xb(a,Ne(yb,a))}function e(){for(var a=b.wrapper.parentNode;a&&a!=document.body;a=a.parentNode);a?setTimeout(e,5e3):ue(window,"resize",d)}function f(b){a.options.onDragEvent&&a.options.onDragEvent(a,ne(b))||qe(b)}function g(){b.inaccurateSelection&&(b.prevInput="",b.inaccurateSelection=!1,b.input.value=a.getSelection(),Ie(b.input))}var b=a.display;te(b.scroller,"mousedown",vb(a,Kb)),te(b.scroller,"dblclick",vb(a,oe)),te(b.lineSpace,"selectstart",function(a){Gb(b,a)||oe(a)}),t||te(b.scroller,"contextmenu",function(b){cc(a,b)}),te(b.scroller,"scroll",function(){Ob(a,b.scroller.scrollTop),Pb(a,b.scroller.scrollLeft,!0),ve(a,"scroll",a)}),te(b.scrollbarV,"scroll",function(){Ob(a,b.scrollbarV.scrollTop)}),te(b.scrollbarH,"scroll",function(){Pb(a,b.scrollbarH.scrollLeft)}),te(b.scroller,"mousewheel",function(b){Sb(a,b)}),te(b.scroller,"DOMMouseScroll",function(b){Sb(a,b)}),te(b.scrollbarH,"mousedown",c),te(b.scrollbarV,"mousedown",c),te(b.wrapper,"scroll",function(){b.wrapper.scrollTop=b.wrapper.scrollLeft=0}),te(window,"resize",d),setTimeout(e,5e3),te(b.input,"keyup",vb(a,function(b){a.options.onKeyEvent&&a.options.onKeyEvent(a,ne(b))||16==b.keyCode&&(a.doc.sel.shift=!1)})),te(b.input,"input",Ne(Ab,a)),te(b.input,"keydown",vb(a,Zb)),te(b.input,"keypress",vb(a,$b)),te(b.input,"focus",Ne(_b,a)),te(b.input,"blur",Ne(ac,a)),a.options.dragDrop&&(te(b.scroller,"dragstart",function(b){Nb(a,b)}),te(b.scroller,"dragenter",f),te(b.scroller,"dragover",f),te(b.scroller,"drop",vb(a,Lb))),te(b.scroller,"paste",function(c){Gb(b,c)||(Db(a),Ab(a))}),te(b.input,"paste",function(){a.state.pasteIncoming=!0,Ab(a)}),te(b.input,"cut",g),te(b.input,"copy",g),j&&te(b.sizer,"mouseup",function(){document.activeElement==b.input&&b.input.blur(),Db(a)})}function Gb(a,b){for(var c=re(b);c!=a.wrapper;c=c.parentNode){if(!c)return!0;if(/\bCodeMirror-(?:line)?widget\b/.test(c.className)||c.parentNode==a.sizer&&c!=a.mover)return!0}}function Hb(a,b,c){var d=a.display;if(!c){var e=re(b);if(e==d.scrollbarH||e==d.scrollbarH.firstChild||e==d.scrollbarV||e==d.scrollbarV.firstChild||e==d.scrollbarFiller)return null}var f,g,h=We(d.lineSpace);try{f=b.clientX,g=b.clientY}catch(b){return null}return nb(a,f-h.left,g-h.top)}function Kb(a){function p(a){if("single"==j)return wc(c.doc,tc(f,h),a),void 0;if(n=tc(f,n),o=tc(f,o),"double"==j){var b=Jc(Wd(f,a.line).text,a);qc(a,n)?wc(c.doc,b.from,o):wc(c.doc,n,b.to)}else"triple"==j&&(qc(a,n)?wc(c.doc,o,tc(f,oc(a.line,0))):wc(c.doc,n,tc(f,oc(a.line+1,0))))}function s(a){var b=++r,e=Hb(c,a,!0);if(e)if(pc(e,l)){var h=a.clientYq.bottom?20:0;h&&setTimeout(vb(c,function(){r==b&&(d.scroller.scrollTop+=h,s(a))}),50)}else{c.state.focused||_b(c),l=e,p(e);var g=K(d,f);(e.line>=g.to||e.linei-400&&pc(Jb.pos,h))j="triple",oe(a),setTimeout(Ne(Db,c),20),Kc(c,h.line);else if(Ib&&Ib.time>i-400&&pc(Ib.pos,h)){j="double",Jb={time:i,pos:h},oe(a);var k=Jc(Wd(f,h.line).text,h);wc(c.doc,k.from,k.to)}else Ib={time:i,pos:h};var l=h;if(c.options.dragDrop&&Xe&&!Eb(c)&&!pc(g.from,g.to)&&!qc(h,g.from)&&!qc(g.to,h)&&"single"==j){var m=vb(c,function(b){e&&(d.scroller.draggable=!1),c.state.draggingText=!1,ue(document,"mouseup",m),ue(d.scroller,"drop",m),Math.abs(a.clientX-b.clientX)+Math.abs(a.clientY-b.clientY)<10&&(oe(b),wc(c.doc,h),Db(c))});return e&&(d.scroller.draggable=!0),c.state.draggingText=m,d.scroller.dragDrop&&d.scroller.dragDrop(),te(document,"mouseup",m),te(d.scroller,"drop",m),void 0}oe(a),"single"==j&&wc(c.doc,tc(f,h));var n=g.from,o=g.to,q=We(d.wrapper),r=0,v=vb(c,function(a){b||se(a)?s(a):u(a)}),w=vb(c,u);te(document,"mousemove",v),te(document,"mouseup",w)}}function Lb(a){var b=this;if(!(Gb(b.display,a)||b.options.onDragEvent&&b.options.onDragEvent(b,ne(a)))){oe(a);var c=Hb(b,a,!0),d=a.dataTransfer.files;if(c&&!Eb(b))if(d&&d.length&&window.FileReader&&window.File)for(var e=d.length,f=Array(e),g=0,h=function(a,d){var h=new FileReader;h.onload=function(){f[d]=h.result,++g==e&&(c=tc(b.doc,c),nc(b.doc,f.join(""),c,"around","paste"))},h.readAsText(a)},i=0;e>i;++i)h(d[i],i);else{if(b.state.draggingText&&!qc(c,b.doc.sel.from)&&!qc(b.doc.sel.to,c))return b.state.draggingText(a),setTimeout(Ne(Db,b),20),void 0;try{var f=a.dataTransfer.getData("Text");if(f){var j=b.doc.sel.from,k=b.doc.sel.to;yc(b.doc,c,c),b.state.draggingText&&nc(b.doc,"",j,k,"paste"),b.replaceSelection(f,null,"paste"),Db(b),_b(b)}}catch(a){}}}}function Mb(a,b){var c=a.display;try{var d=b.clientX,e=b.clientY}catch(b){return!1}if(d>=Math.floor(We(c.gutters).right))return!1;if(oe(b),!Ae(a,"gutterClick"))return!0;var f=We(c.lineDiv);if(e>f.bottom)return!0;e-=f.top-c.viewOffset;for(var g=0;g=d){var i=_d(a.doc,e),j=a.options.gutters[g];ye(a,"gutterClick",a,i,j,b);break}}return!0}function Nb(a,b){if(!Gb(a.display,b)){var c=a.getSelection();if(b.dataTransfer.setData("Text",c),b.dataTransfer.setDragImage&&!i){var d=Se("img",null,null,"position: fixed; left: 0; top: 0;");h&&(d.width=d.height=1,a.display.wrapper.appendChild(d),d._top=d.offsetTop),b.dataTransfer.setDragImage(d,0,0),h&&d.parentNode.removeChild(d)}}}function Ob(b,c){Math.abs(b.doc.scrollTop-c)<2||(b.doc.scrollTop=c,a||P(b,[],c),b.display.scroller.scrollTop!=c&&(b.display.scroller.scrollTop=c),b.display.scrollbarV.scrollTop!=c&&(b.display.scrollbarV.scrollTop=c),a&&P(b,[]))}function Pb(a,b,c){(c?b==a.doc.scrollLeft:Math.abs(a.doc.scrollLeft-b)<2)||(b=Math.min(b,a.display.scroller.scrollWidth-a.display.scroller.clientWidth),a.doc.scrollLeft=b,L(a),a.display.scroller.scrollLeft!=b&&(a.display.scroller.scrollLeft=b),a.display.scrollbarH.scrollLeft!=b&&(a.display.scrollbarH.scrollLeft=b))}function Sb(b,c){var d=c.wheelDeltaX,f=c.wheelDeltaY;if(null==d&&c.detail&&c.axis==c.HORIZONTAL_AXIS&&(d=c.detail),null==f&&c.detail&&c.axis==c.VERTICAL_AXIS?f=c.detail:null==f&&(f=c.wheelDelta),f&&p&&e)for(var g=c.target;g!=j;g=g.parentNode)if(g.lineObj){b.display.currentWheelTarget=g; 2 | break}var i=b.display,j=i.scroller;if(d&&!a&&!h&&null!=Rb)return f&&Ob(b,Math.max(0,Math.min(j.scrollTop+f*Rb,j.scrollHeight-j.clientHeight))),Pb(b,Math.max(0,Math.min(j.scrollLeft+d*Rb,j.scrollWidth-j.clientWidth))),oe(c),i.wheelStartX=null,void 0;if(f&&null!=Rb){var k=f*Rb,l=b.doc.scrollTop,m=l+i.wrapper.clientHeight;0>k?l=Math.max(0,l+k-50):m=Math.min(b.doc.height,m+k+50),P(b,[],{top:l,bottom:m})}20>Qb&&(null==i.wheelStartX?(i.wheelStartX=j.scrollLeft,i.wheelStartY=j.scrollTop,i.wheelDX=d,i.wheelDY=f,setTimeout(function(){if(null!=i.wheelStartX){var a=j.scrollLeft-i.wheelStartX,b=j.scrollTop-i.wheelStartY,c=b&&i.wheelDY&&b/i.wheelDY||a&&i.wheelDX&&a/i.wheelDX;i.wheelStartX=i.wheelStartY=null,c&&(Rb=(Rb*Qb+c)/(Qb+1),++Qb)}},200)):(i.wheelDX+=d,i.wheelDY+=f))}function Tb(a,b,c){if("string"==typeof b&&(b=Vc[b],!b))return!1;a.display.pollingFast&&Bb(a)&&(a.display.pollingFast=!1);var d=a.doc,e=d.sel.shift,f=!1;try{Eb(a)&&(a.state.suppressEdits=!0),c&&(d.sel.shift=!1),f=b(a)!=Ce}finally{d.sel.shift=e,a.state.suppressEdits=!1}return f}function Ub(a){var b=a.state.keyMaps.slice(0);return b.push(a.options.keyMap),a.options.extraKeys&&b.unshift(a.options.extraKeys),b}function Wb(a,b){var c=Xc(a.options.keyMap),e=c.auto;clearTimeout(Vb),e&&!Zc(b)&&(Vb=setTimeout(function(){Xc(a.options.keyMap)==c&&(a.options.keyMap=e.call?e.call(null,a):e)},50));var f=$c(b,!0),g=!1;if(!f)return!1;var h=Ub(a);return g=b.shiftKey?Yc("Shift-"+f,h,function(b){return Tb(a,b,!0)})||Yc(f,h,function(b){return"string"==typeof b&&/^go[A-Z]/.test(b)?Tb(a,b):void 0}):Yc(f,h,function(b){return Tb(a,b)}),"stop"==g&&(g=!1),g&&(oe(b),$(a),d&&(b.oldKeyCode=b.keyCode,b.keyCode=0)),g}function Xb(a,b,c){var d=Yc("'"+c+"'",Ub(a),function(b){return Tb(a,b,!0)});return d&&(oe(b),$(a)),d}function Zb(a){var c=this;if(c.state.focused||_b(c),b&&27==a.keyCode&&(a.returnValue=!1),!c.options.onKeyEvent||!c.options.onKeyEvent(c,ne(a))){var d=a.keyCode;c.doc.sel.shift=16==d||a.shiftKey;var e=Wb(c,a);h&&(Yb=e?d:null,!e&&88==d&&!df&&(p?a.metaKey:a.ctrlKey)&&c.replaceSelection(""))}}function $b(a){var b=this;if(!b.options.onKeyEvent||!b.options.onKeyEvent(b,ne(a))){var c=a.keyCode,d=a.charCode;if(h&&c==Yb)return Yb=null,oe(a),void 0;if(!(h&&(!a.which||a.which<10)||j)||!Wb(b,a)){var e=String.fromCharCode(null==d?c:d);this.options.electricChars&&this.doc.mode.electricChars&&this.options.smartIndent&&!Eb(this)&&this.doc.mode.electricChars.indexOf(e)>-1&&setTimeout(vb(b,function(){Fc(b,b.doc.sel.to.line,"smart")}),75),Xb(b,a,e)||Ab(b)}}}function _b(a){"nocursor"!=a.options.readOnly&&(a.state.focused||(ve(a,"focus",a),a.state.focused=!0,-1==a.display.wrapper.className.search(/\bCodeMirror-focused\b/)&&(a.display.wrapper.className+=" CodeMirror-focused"),Cb(a,!0)),zb(a),$(a))}function ac(a){a.state.focused&&(ve(a,"blur",a),a.state.focused=!1,a.display.wrapper.className=a.display.wrapper.className.replace(" CodeMirror-focused","")),clearInterval(a.display.blinker),setTimeout(function(){a.state.focused||(a.doc.sel.shift=!1)},150)}function cc(a,c){function k(){if(e.inputDiv.style.position="relative",e.input.style.cssText=j,d&&(e.scrollbarV.scrollTop=e.scroller.scrollTop=i),zb(a),null!=e.input.selectionStart&&(!b||d)){clearTimeout(bc);var c=e.input.value=" "+(pc(f.from,f.to)?"":e.input.value),g=0;e.prevInput=" ",e.input.selectionStart=1,e.input.selectionEnd=c.length;var h=function(){" "==e.prevInput&&0==e.input.selectionStart?vb(a,Vc.selectAll)(a):g++<10?bc=setTimeout(h,500):Cb(a)};bc=setTimeout(h,200)}}var e=a.display,f=a.doc.sel;if(!Gb(e,c)){var g=Hb(a,c),i=e.scroller.scrollTop;if(g&&!h){(pc(f.from,f.to)||qc(g,f.from)||!qc(g,f.to))&&vb(a,yc)(a.doc,g,g);var j=e.input.style.cssText;if(e.inputDiv.style.position="absolute",e.input.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(c.clientY-5)+"px; left: "+(c.clientX-5)+"px; z-index: 1000; background: white; outline: none;"+"border-width: 0; outline: none; overflow: hidden; opacity: .05; -ms-opacity: .05; filter: alpha(opacity=5);",Db(a),Cb(a,!0),pc(f.from,f.to)&&(e.input.value=e.prevInput=" "),t){qe(c);var l=function(){ue(window,"mouseup",l),setTimeout(k,20)};te(window,"mouseup",l)}else setTimeout(k,50)}}}function dc(a){return oc(a.from.line+a.text.length-1,He(a.text).length+(1==a.text.length?a.from.ch:0))}function ec(a,b,c){if(!qc(b.from,c))return tc(a,c);var d=b.text.length-1-(b.to.line-b.from.line);if(c.line>b.to.line+d){var e=c.line-d,f=a.first+a.size-1;return e>f?oc(f,Wd(a,f).text.length):uc(c,Wd(a,e).text.length)}if(c.line==b.to.line+d)return uc(c,He(b.text).length+(1==b.text.length?b.from.ch:0)+Wd(a,b.to.line).text.length-b.to.ch);var g=c.line-b.from.line;return uc(c,b.text[g].length+(g?0:b.from.ch))}function fc(a,b,c){if(c&&"object"==typeof c)return{anchor:ec(a,b,c.anchor),head:ec(a,b,c.head)};if("start"==c)return{anchor:b.from,head:b.from};var d=dc(b);if("around"==c)return{anchor:b.from,head:d};if("end"==c)return{anchor:d,head:d};var e=function(a){if(qc(a,b.from))return a;if(!qc(b.to,a))return d;var c=a.line+b.text.length-(b.to.line-b.from.line)-1,e=a.ch;return a.line==b.to.line&&(e+=d.ch-b.to.ch),oc(c,e)};return{anchor:e(a.sel.anchor),head:e(a.sel.head)}}function gc(a,b){var c={canceled:!1,from:b.from,to:b.to,text:b.text,origin:b.origin,update:function(b,c,d,e){b&&(this.from=tc(a,b)),c&&(this.to=tc(a,c)),d&&(this.text=d),void 0!==e&&(this.origin=e)},cancel:function(){this.canceled=!0}};return ve(a,"beforeChange",a,c),a.cm&&ve(a.cm,"beforeChange",a.cm,c),c.canceled?null:{from:c.from,to:c.to,text:c.text,origin:c.origin}}function hc(a,b,c,d){if(a.cm){if(!a.cm.curOp)return vb(a.cm,hc)(a,b,c,d);if(a.cm.state.suppressEdits)return}if(!(Ae(a,"beforeChange")||a.cm&&Ae(a.cm,"beforeChange"))||(b=gc(a,b))){var e=u&&!d&&ld(a,b.from,b.to);if(e){for(var f=e.length-1;f>=1;--f)ic(a,{from:e[f].from,to:e[f].to,text:[""]});e.length&&ic(a,{from:e[0].from,to:e[0].to,text:b.text},c)}else ic(a,b,c)}}function ic(a,b,c){var d=fc(a,b,c);fe(a,b,d,a.cm?a.cm.curOp.id:0/0),lc(a,b,d,jd(a,b));var e=[];Ud(a,function(a,c){c||-1!=Je(e,a.history)||(le(a.history,b),e.push(a.history)),lc(a,b,null,jd(a,b))})}function jc(a,b){var c=a.history,d=("undo"==b?c.done:c.undone).pop();if(d){c.dirtyCounter+="undo"==b?-1:1;var e={changes:[],anchorBefore:d.anchorAfter,headBefore:d.headAfter,anchorAfter:d.anchorBefore,headAfter:d.headBefore};("undo"==b?c.undone:c.done).push(e);for(var f=d.changes.length-1;f>=0;--f){var g=d.changes[f];g.origin=b,e.changes.push(ee(a,g));var h=f?fc(a,g,null):{anchor:d.anchorBefore,head:d.headBefore};lc(a,g,h,kd(a,g));var i=[];Ud(a,function(a,b){b||-1!=Je(i,a.history)||(le(a.history,g),i.push(a.history)),lc(a,g,null,kd(a,g))})}}}function kc(a,b){function c(a){return oc(a.line+b,a.ch)}a.first+=b,a.cm&&yb(a.cm,a.first,a.first,b),a.sel.head=c(a.sel.head),a.sel.anchor=c(a.sel.anchor),a.sel.from=c(a.sel.from),a.sel.to=c(a.sel.to)}function lc(a,b,c,d){if(a.cm&&!a.cm.curOp)return vb(a.cm,lc)(a,b,c,d);if(b.to.linea.lastLine())){if(b.from.linef&&(b={from:b.from,to:oc(f,Wd(a,f).text.length),text:[b.text[0]],origin:b.origin}),c||(c=fc(a,b,null)),a.cm?mc(a.cm,b,d,c):Nd(a,b,d,c)}}function mc(a,b,c,d){var e=a.doc,f=a.display,g=b.from,h=b.to,i=!1,j=g.line;a.options.lineWrapping||(j=$d(pd(e,Wd(e,g.line))),e.iter(j,h.line+1,function(a){return a==f.maxLine?(i=!0,!0):void 0})),Nd(e,b,c,d,A(a)),a.options.lineWrapping||(e.iter(j,g.line+b.text.length,function(a){var b=G(e,a);b>f.maxLineLength&&(f.maxLine=a,f.maxLineLength=b,f.maxLineChanged=!0,i=!1)}),i&&(a.curOp.updateMaxLine=!0)),e.frontier=Math.min(e.frontier,g.line),_(a,400);var k=b.text.length-(h.line-g.line)-1;if(yb(a,g.line,h.line+1,k),Ae(a,"change")){var l={from:g,to:h,text:b.text,origin:b.origin};if(a.curOp.textChanged){for(var m=a.curOp.textChanged;m.next;m=m.next);m.next=l}else a.curOp.textChanged=l}}function nc(a,b,c,d,e){if(d||(d=c),qc(d,c)){var f=d;d=c,c=f}"string"==typeof b&&(b=bf(b)),hc(a,{from:c,to:d,text:b,origin:e},null)}function oc(a,b){return this instanceof oc?(this.line=a,this.ch=b,void 0):new oc(a,b)}function pc(a,b){return a.line==b.line&&a.ch==b.ch}function qc(a,b){return a.linec?oc(c,Wd(a,c).text.length):uc(b,Wd(a,b.line).text.length)}function uc(a,b){var c=a.ch;return null==c||c>b?oc(a.line,b):0>c?oc(a.line,0):a}function vc(a,b){return b>=a.first&&b=f.ch:k.to>f.ch))){if(d&&l.clearOnEnter){(i||(i=[])).push(l);continue}if(!l.atomic)continue;var m=l.find()[0>g?"from":"to"];if(pc(m,f)&&(m.ch+=g,m.ch<0?m=m.line>a.first?tc(a,oc(m.line-1)):null:m.ch>h.text.length&&(m=m.line(window.innerHeight||document.documentElement.clientHeight)&&(e=!1),null!=e&&!m){var f="none"==c.cursor.style.display;f&&(c.cursor.style.display="",c.cursor.style.left=b.left+"px",c.cursor.style.top=b.top-c.viewOffset+"px"),c.cursor.scrollIntoView(e),f&&(c.cursor.style.display="none")}}}function Cc(a,b){for(;;){var c=!1,d=lb(a,b),e=Ec(a,d.left,d.top,d.left,d.bottom),f=a.doc.scrollTop,g=a.doc.scrollLeft;if(null!=e.scrollTop&&(Ob(a,e.scrollTop),Math.abs(a.doc.scrollTop-f)>1&&(c=!0)),null!=e.scrollLeft&&(Pb(a,e.scrollLeft),Math.abs(a.doc.scrollLeft-g)>1&&(c=!0)),!c)return d}}function Dc(a,b,c,d,e){var f=Ec(a,b,c,d,e);null!=f.scrollTop&&Ob(a,f.scrollTop),null!=f.scrollLeft&&Pb(a,f.scrollLeft)}function Ec(a,b,c,d,e){var f=a.display,g=db(f);c+=g,e+=g;var h=f.scroller.clientHeight-Be,i=f.scroller.scrollTop,j={},k=a.doc.height+2*g,l=g+10>c,m=e+g>k-10;i>c?j.scrollTop=l?0:Math.max(0,c):e>i+h&&(j.scrollTop=(m?k:e)-h);var n=f.scroller.clientWidth-Be,o=f.scroller.scrollLeft;b+=f.gutters.offsetWidth,d+=f.gutters.offsetWidth;var p=f.gutters.offsetWidth,q=p+10>b;return o+p>b||q?(q&&(b=0),j.scrollLeft=Math.max(0,b-10-p)):d>n+o-3&&(j.scrollLeft=d+10-n),j}function Fc(a,b,c,d){var e=a.doc;if(c||(c="add"),"smart"==c)if(a.doc.mode.indent)var f=cb(a,b);else c="prev";var k,g=a.options.tabSize,h=Wd(e,b),i=Ee(h.text,null,g),j=h.text.match(/^\s*/)[0];if("smart"==c&&(k=a.doc.mode.indent(f,h.text.slice(j.length),h.text),k==Ce)){if(!d)return;c="prev"}"prev"==c?k=b>e.first?Ee(Wd(e,b-1).text,null,g):0:"add"==c?k=i+a.options.indentUnit:"subtract"==c&&(k=i-a.options.indentUnit),k=Math.max(0,k);var l="",m=0;if(a.options.indentWithTabs)for(var n=Math.floor(k/g);n;--n)m+=g,l+=" ";k>m&&(l+=Ge(k-m)),l!=j&&nc(a.doc,l,oc(b,0),oc(b,j.length),"+input"),h.stateAfter=null}function Gc(a,b,c){var d=b,e=b,f=a.doc;return"number"==typeof b?e=Wd(f,sc(f,b)):d=$d(b),null==d?null:c(e,d)?(yb(a,d,d+1),e):null}function Hc(a,b,c,d,e){function j(){var b=f+c;return b=a.first+a.size?i=!1:(f=b,h=Wd(a,b))}function k(a){var b=(e?nf:of)(h,g,c,!0);if(null==b){if(a||!j())return i=!1;g=e?(0>c?kf:jf)(h):0>c?h.text.length:0}else g=b;return!0}var f=b.line,g=b.ch,h=Wd(a,f),i=!0;if("char"==d)k();else if("column"==d)k(!0);else if("word"==d)for(var l=!1;!(0>c)||k();){if(Pe(h.text.charAt(g)))l=!0;else if(l){0>c&&(c=1,k());break}if(c>0&&!k())break}var m=Ac(a,oc(f,g),c,!0);return i||(m.hitSide=!0),m}function Ic(a,b,c,d){var g,e=a.doc,f=b.left;if("page"==d){var h=Math.min(a.display.wrapper.clientHeight,window.innerHeight||document.documentElement.clientHeight);g=b.top+c*h}else"line"==d&&(g=c>0?b.bottom+3:b.top-3);for(;;){var i=nb(a,f,g);if(!i.outside)break;if(0>c?0>=g:g>=e.height){i.hitSide=!0;break}g+=5*c}return i}function Jc(a,b){var c=b.ch,d=b.ch;if(a){b.after===!1||d==a.length?--c:++d;for(var e=a.charAt(c),f=Pe(e)?Pe:/\s/.test(e)?function(a){return/\s/.test(a)}:function(a){return!/\s/.test(a)&&!Pe(a)};c>0&&f(a.charAt(c-1));)--c;for(;dg;++g){var i=d(f[g]);if(i)return i}return!1}for(var e=0;e=b:f.to>b);(e||(e=[])).push({from:f.from,to:i?null:f.to,marker:g})}}return e}function id(a,b,c){if(a)for(var e,d=0;d=b:f.to>b);if(h||"bookmark"==g.type&&f.from==b&&(!c||f.marker.insertLeft)){var i=null==f.from||(g.inclusiveLeft?f.from<=b:f.from0&&h)for(var l=0;ll;++l)o.push(q);o.push(i)}return o}function kd(a,b){var c=he(a,b),d=jd(a,b);if(!c)return d;if(!d)return c;for(var e=0;eb)&&(!d||d.width5e3&&(f=!1,i.pos=Math.min(b.length,i.start+5e4),j=null);var k=i.current();i.start=i.pos,f&&h==j?g+=k:(g&&e(g,h),g=k,h=j)}g&&e(g,h)}function Cd(a,b,c){var d=[a.state.modeGen];Bd(a,b.text,a.doc.mode,c,function(a,b){d.push(a,b)});for(var e=0;e=i?e-=i:(d.splice(g,1,h.slice(0,e),d[g+1],h.slice(e)),e=0),g+=2}if(b)if(f.opaque)d.splice(c,g-c,a,b),g=c+2;else for(;g>c;c+=2){var h=d[c+1];d[c+1]=h?h+" "+b:b}})}return d}function Dd(a,b){return b.styles&&b.styles[0]==a.state.modeGen||(b.styles=Cd(a,b,b.stateAfter=cb(a,$d(b)))),b.styles}function Ed(a,b,c){var d=a.doc.mode,e=new _c(b.text,a.options.tabSize);for(""==b.text&&d.blankLine&&d.blankLine(c);!e.eol()&&e.pos<=5e3;)d.token(e,c),e.start=e.pos}function Gd(a){return a?Fd[a]||(Fd[a]="cm-"+a.replace(/ +/g," cm-")):null}function Hd(a,c,d){for(var e,g,h,f=c,i=!0;e=nd(f);)i=!1,f=Wd(a.doc,e.find().from.line),g||(g=f);var j={pre:Se("pre"),col:0,pos:0,display:!d,measure:null,addedOne:!1,cm:a};f.textClass&&(j.pre.className=f.textClass);do{j.measure=f==c&&d,j.pos=0,j.addToken=j.measure?Kd:Jd,d&&h&&f!=c&&!j.addedOne&&(d[0]=j.pre.appendChild(af(a.display.measure)),j.addedOne=!0);var k=Md(f,j,Dd(a,f));h=f==g,k&&(f=Wd(a.doc,k.to.line),i=!1)}while(k);d&&!j.addedOne&&(d[0]=j.pre.appendChild(i?Se("span","\xa0"):af(a.display.measure))),j.pre.firstChild||qd(a.doc,c)||j.pre.appendChild(document.createTextNode("\xa0"));var l;if(d&&b&&(l=be(f))){var m=l.length-1;l[m].from==l[m].to&&--m;var n=l[m],o=l[m-1];if(n.from+1==n.to&&o&&n.level="\ud800"&&"\udbff">g&&fh)?(null!=r.to&&k>r.to&&(k=r.to,m=""),s.className&&(l+=" "+s.className),s.startStyle&&r.from==h&&(n+=" "+s.startStyle),s.endStyle&&r.to==k&&(m+=" "+s.endStyle),s.collapsed&&(!o||o.marker.widthh&&k>r.from&&(k=r.from),"bookmark"==s.type&&r.from==h&&s.replacedWith&&(p=s.replacedWith)}if(o&&(o.from||0)==h&&(Ld(b,(null==o.to?g:o.to)-h,null!=o.from&&o.marker.replacedWith),null==o.to))return o.marker.find();p&&!o&&Ld(b,0,p)}if(h>=g)break;for(var t=Math.min(g,k);;){if(i){var u=h+i.length;if(!o){var v=u>t?i.slice(0,t-h):i;b.addToken(b,v,j?j+l:l,n,h+v.length==k?m:"")}if(u>=t){i=i.slice(t-h),h=t;break}h=u,n=""}i=c[e++],j=Gd(c[e++])}}else for(var e=1;eo;++o)q.push(yd(i[o],f(o),e));zd(k,k.text,m,e),n&&a.remove(g.line,n),q.length&&a.insert(g.line,q)}else if(j==k)if(1==i.length)zd(j,j.text.slice(0,g.ch)+l+j.text.slice(h.ch),m,e);else{for(var q=[],o=1,p=i.length-1;p>o;++o)q.push(yd(i[o],f(o),e));q.push(yd(l+j.text.slice(h.ch),m,e)),zd(j,j.text.slice(0,g.ch)+i[0],f(0),e),a.insert(g.line+1,q)}else if(1==i.length)zd(j,j.text.slice(0,g.ch)+i[0]+k.text.slice(h.ch),f(0),e),a.remove(g.line+1,n);else{zd(j,j.text.slice(0,g.ch)+i[0],f(0),e),zd(k,l+k.text.slice(h.ch),m,e);for(var o=1,p=i.length-1,q=[];p>o;++o)q.push(yd(i[o],f(o),e));n>1&&a.remove(g.line+1,n-1),a.insert(g.line+1,q)}ye(a,"change",a,b),yc(a,d.anchor,d.head,null,!0)}function Od(a){this.lines=a,this.parent=null;for(var b=0,c=a.length,d=0;c>b;++b)a[b].parent=this,d+=a[b].height;this.height=d}function Pd(a){this.children=a;for(var b=0,c=0,d=0,e=a.length;e>d;++d){var f=a[d];b+=f.chunkSize(),c+=f.height,f.parent=this}this.size=b,this.height=c,this.parent=null}function Ud(a,b,c){function d(a,e,f){if(a.linked)for(var g=0;gb){a=d;break}b-=e}return a.lines[b]}function Xd(a,b,c){var d=[],e=b.line;return a.iter(b.line,c.line+1,function(a){var f=a.text;e==c.line&&(f=f.slice(0,c.ch)),e==b.line&&(f=f.slice(b.ch)),d.push(f),++e}),d}function Yd(a,b,c){var d=[];return a.iter(b,c,function(a){d.push(a.text)}),d}function Zd(a,b){for(var c=b-a.height,d=a;d;d=d.parent)d.height+=c}function $d(a){if(null==a.parent)return null;for(var b=a.parent,c=Je(b.lines,a),d=b.parent;d;b=d,d=d.parent)for(var e=0;d.children[e]!=b;++e)c+=d.children[e].chunkSize();return c+b.first}function _d(a,b){var c=a.first;a:do{for(var d=0,e=a.children.length;e>d;++d){var f=a.children[d],g=f.height;if(g>b){a=f;continue a}b-=g,c+=f.chunkSize()}return c}while(!a.lines);for(var d=0,e=a.lines.length;e>d;++d){var h=a.lines[d],i=h.height;if(i>b)break;b-=i}return c+d}function ae(a,b){b=pd(a.doc,b);for(var c=0,d=b.parent,e=0;ef-600||"*"==b.origin.charAt(0)))){var h=He(g.changes);pc(b.from,b.to)&&pc(b.from,h.to)?h.to=dc(b):g.changes.push(ee(a,b)),g.anchorAfter=c.anchor,g.headAfter=c.head}else{for(g={changes:[ee(a,b)],anchorBefore:a.sel.anchor,headBefore:a.sel.head,anchorAfter:c.anchor,headAfter:c.head},e.done.push(g);e.done.length>e.undoDepth;)e.done.shift();e.dirtyCounter<0?e.dirtyCounter=0/0:e.dirtyCounter++}e.lastTime=f,e.lastOp=d,e.lastOrigin=b.origin}function ge(a){if(!a)return null;for(var c,b=0;b-1&&(He(g)[k]=i[k],delete i[k])}}return d}function je(a,b,c,d){c0}function De(){this.id=null}function Ee(a,b,c){null==b&&(b=a.search(/[^\s\u00a0]/),-1==b&&(b=a.length));for(var d=0,e=0;b>d;++d)" "==a.charAt(d)?e+=c-e%c:++e;return e}function Ge(a){for(;Fe.length<=a;)Fe.push(He(Fe)+" ");return Fe[a]}function He(a){return a[a.length-1]}function Ie(a){n?(a.selectionStart=0,a.selectionEnd=a.value.length):a.select()}function Je(a,b){if(a.indexOf)return a.indexOf(b);for(var c=0,d=a.length;d>c;++c)if(a[c]==b)return c;return-1}function Ke(a,b){function c(){}c.prototype=a;var d=new c;return b&&Le(b,d),d}function Le(a,b){b||(b={});for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b}function Me(a){for(var b=[],c=0;a>c;++c)b.push(void 0);return b}function Ne(a){var b=Array.prototype.slice.call(arguments,1);return function(){return a.apply(null,b)}}function Pe(a){return/\w/.test(a)||a>"\x80"&&(a.toUpperCase()!=a.toLowerCase()||Oe.test(a))}function Qe(a){for(var b in a)if(a.hasOwnProperty(b)&&a[b])return!1; 3 | return!0}function Se(a,b,c,d){var e=document.createElement(a);if(c&&(e.className=c),d&&(e.style.cssText=d),"string"==typeof b)Ve(e,b);else if(b)for(var f=0;f2&&!c)}return _e?Se("span","\u200b"):Se("span","\xa0",null,"display: inline-block; width: 1px; margin-right: -1px")}function ff(a,b,c,d){if(!a)return d(b,c,"ltr");for(var e=0;eb||b==c&&f.to==b)&&d(Math.max(f.from,b),Math.min(f.to,c),1==f.level?"rtl":"ltr")}}function gf(a){return a.level%2?a.to:a.from}function hf(a){return a.level%2?a.from:a.to}function jf(a){var b=be(a);return b?gf(b[0]):0}function kf(a){var b=be(a);return b?hf(He(b)):a.text.length}function lf(a,b){var c=Wd(a.doc,b),d=pd(a.doc,c);d!=c&&(b=$d(d));var e=be(d),f=e?e[0].level%2?kf(d):jf(d):0;return oc(b,f)}function mf(a,b){for(var c,d;c=od(d=Wd(a.doc,b));)b=c.find().to.line;var e=be(d),f=e?e[0].level%2?jf(d):kf(d):d.text.length;return oc(b,f)}function nf(a,b,c,d){var e=be(a);if(!e)return of(a,b,c,d);for(var f=d?function(b,c){do b+=c;while(b>0&&Re.test(a.text.charAt(b)));return b}:function(a,b){return a+b},g=e[0].level,h=0;hb||j&&(i.from==b||i.to==b))break}for(var k=f(b,i.level%2?-c:c);null!=k;)if(i.level%2==g){if(!(ki.to))break;i=e[h+=c],k=i&&(c>0==i.level%2?f(i.to,-1):f(i.from,1))}else if(k==gf(i))i=e[--h],k=i&&hf(i);else{if(k!=hf(i))break;i=e[++h],k=i&&gf(i)}return 0>k||k>a.text.length?null:k}function of(a,b,c,d){var e=b+c;if(d)for(;e>0&&Re.test(a.text.charAt(e));)e+=c;return 0>e||e>a.text.length?null:e}var a=/gecko\/\d/i.test(navigator.userAgent),b=/MSIE \d/.test(navigator.userAgent),c=b&&(null==document.documentMode||document.documentMode<8),d=b&&(null==document.documentMode||document.documentMode<9),e=/WebKit\//.test(navigator.userAgent),f=e&&/Qt\/\d+\.\d+/.test(navigator.userAgent),g=/Chrome\//.test(navigator.userAgent),h=/Opera\//.test(navigator.userAgent),i=/Apple Computer/.test(navigator.vendor),j=/KHTML\//.test(navigator.userAgent),k=/Mac OS X 1\d\D([7-9]|\d\d)\D/.test(navigator.userAgent),l=/Mac OS X 1\d\D([8-9]|\d\d)\D/.test(navigator.userAgent),m=/PhantomJS/.test(navigator.userAgent),n=/AppleWebKit/.test(navigator.userAgent)&&/Mobile\/\w+/.test(navigator.userAgent),o=n||/Android|webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(navigator.userAgent),p=n||/Mac/.test(navigator.platform),q=/windows/i.test(navigator.platform),r=h&&navigator.userAgent.match(/Version\/(\d*\.\d*)/);r&&(r=Number(r[1]));var pb,Ib,Jb,s=p&&(f||h&&(null==r||12.11>r)),t=a||b&&!d,u=!1,v=!1,sb=0,Qb=0,Rb=null;b?Rb=-.53:a?Rb=15:g?Rb=-.7:i&&(Rb=-1/3);var Vb,bc,Yb=null;w.Pos=oc,w.prototype={focus:function(){window.focus(),Db(this),_b(this),Ab(this)},setOption:function(a,b){var c=this.options,d=c[a];(c[a]!=b||"mode"==a)&&(c[a]=b,Lc.hasOwnProperty(a)&&vb(this,Lc[a])(this,b,d))},getOption:function(a){return this.options[a]},getDoc:function(){return this.doc},addKeyMap:function(a){this.state.keyMaps.push(a)},removeKeyMap:function(a){for(var b=this.state.keyMaps,c=0;c=d;++d)Fc(this,d,a)}),getTokenAt:function(a){var b=this.doc;a=tc(b,a);for(var c=cb(this,a.line),d=this.doc.mode,e=Wd(b,a.line),f=new _c(e.text,this.options.tabSize);f.posi)&&a.top>b.offsetHeight?g=a.top-b.offsetHeight:a.bottom+b.offsetHeight<=i&&(g=a.bottom),h+b.offsetWidth>j&&(h=j-b.offsetWidth)}b.style.top=g+db(f)+"px",b.style.left=b.style.right="","right"==e?(h=f.sizer.clientWidth-b.offsetWidth,b.style.right="0px"):("left"==e?h=0:"middle"==e&&(h=(f.sizer.clientWidth-b.offsetWidth)/2),b.style.left=h+"px"),c&&Dc(this,h,g,h+b.offsetWidth,g+b.offsetHeight)},triggerOnKeyDown:vb(null,Zb),execCommand:function(a){return Vc[a](this)},findPosH:function(a,b,c,d){var e=1;0>b&&(e=-1,b=-b);for(var f=0,g=tc(this.doc,a);b>f&&(g=Hc(this.doc,g,e,c,d),!g.hitSide);++f);return g},moveH:vb(null,function(a,b){var d,c=this.doc.sel;d=c.shift||c.extend||pc(c.from,c.to)?Hc(this.doc,c.head,a,b,this.options.rtlMoveVisually):0>a?c.from:c.to,wc(this.doc,d,d,a)}),deleteH:vb(null,function(a,b){var c=this.doc.sel;pc(c.from,c.to)?nc(this.doc,"",c.from,Hc(this.doc,c.head,a,b,!1),"+delete"):nc(this.doc,"",c.from,c.to,"+delete"),this.curOp.userSelChange=!0}),findPosV:function(a,b,c,d){var e=1,f=d;0>b&&(e=-1,b=-b);for(var g=0,h=tc(this.doc,a);b>g;++g){var i=lb(this,h,"div");if(null==f?f=i.left:i.left=f,h=Ic(this,i,e,c),h.hitSide)break}return h},moveV:vb(null,function(a,b){var c=this.doc.sel,d=lb(this,c.head,"div");null!=c.goalColumn&&(d.left=c.goalColumn);var e=Ic(this,d,a,b);"page"==b&&(this.display.scrollbarV.scrollTop+=kb(this,e,"div").top-d.top),wc(this.doc,e,e,a),c.goalColumn=d.left}),toggleOverwrite:function(){(this.state.overwrite=!this.state.overwrite)?this.display.cursor.className+=" CodeMirror-overwrite":this.display.cursor.className=this.display.cursor.className.replace(" CodeMirror-overwrite","")},scrollTo:vb(null,function(a,b){this.curOp.updateScrollPos={scrollLeft:a,scrollTop:b}}),getScrollInfo:function(){var a=this.display.scroller,b=Be;return{left:a.scrollLeft,top:a.scrollTop,height:a.scrollHeight-b,width:a.scrollWidth-b,clientHeight:a.clientHeight-b,clientWidth:a.clientWidth-b}},scrollIntoView:function(a){"number"==typeof a&&(a=oc(a,0)),a&&null==a.line?Dc(this,a.left,a.top,a.right,a.bottom):(a=a?tc(this.doc,a):this.doc.sel.head,Cc(this,a))},setSize:function(a,b){function c(a){return"number"==typeof a||/^\d+$/.test(String(a))?a+"px":a}null!=a&&(this.display.wrapper.style.width=c(a)),null!=b&&(this.display.wrapper.style.height=c(b)),this.refresh()},on:function(a,b){te(this,a,b)},off:function(a,b){ue(this,a,b)},operation:function(a){return xb(this,a)},refresh:vb(null,function(){ib(this),this.curOp.updateScrollPos={scrollTop:this.doc.scrollTop,scrollLeft:this.doc.scrollLeft},yb(this)}),swapDoc:vb(null,function(a){var b=this.doc;return b.cm=null,Vd(this,a),ib(this),this.curOp.updateScrollPos={scrollTop:a.scrollTop,scrollLeft:a.scrollLeft},b}),getInputField:function(){return this.display.input},getWrapperElement:function(){return this.display.wrapper},getScrollerElement:function(){return this.display.scroller},getGutterElement:function(){return this.display.gutters}};var Lc=w.optionHandlers={},Mc=w.defaults={},Oc=w.Init={toString:function(){return"CodeMirror.Init"}};Nc("value","",function(a,b){a.setValue(b)},!0),Nc("mode",null,function(a,b){a.doc.modeOption=b,y(a)},!0),Nc("indentUnit",2,y,!0),Nc("indentWithTabs",!1),Nc("smartIndent",!0),Nc("tabSize",4,function(a){y(a),ib(a),yb(a)},!0),Nc("electricChars",!0),Nc("rtlMoveVisually",!q),Nc("theme","default",function(a){D(a),E(a)},!0),Nc("keyMap","default",C),Nc("extraKeys",null),Nc("onKeyEvent",null),Nc("onDragEvent",null),Nc("lineWrapping",!1,z,!0),Nc("gutters",[],function(a){I(a.options),E(a)},!0),Nc("fixedGutter",!0,function(a,b){a.display.gutters.style.left=b?O(a.display)+"px":"0",a.refresh()},!0),Nc("lineNumbers",!1,function(a){I(a.options),E(a)},!0),Nc("firstLineNumber",1,E,!0),Nc("lineNumberFormatter",function(a){return a},E,!0),Nc("showCursorWhenSelecting",!1,X,!0),Nc("readOnly",!1,function(a,b){"nocursor"==b?(ac(a),a.display.input.blur()):b||Cb(a,!0)}),Nc("dragDrop",!0),Nc("cursorBlinkRate",530),Nc("cursorHeight",1),Nc("workTime",100),Nc("workDelay",100),Nc("flattenSpans",!0),Nc("pollInterval",100),Nc("undoDepth",40,function(a,b){a.doc.history.undoDepth=b}),Nc("viewportMargin",10,function(a){a.refresh()},!0),Nc("tabindex",null,function(a,b){a.display.input.tabIndex=b||""}),Nc("autofocus",null);var Pc=w.modes={},Qc=w.mimeModes={};w.defineMode=function(a,b){if(w.defaults.mode||"null"==a||(w.defaults.mode=a),arguments.length>2){b.dependencies=[];for(var c=2;c0&&b.ch=this.string.length},sol:function(){return 0==this.pos},peek:function(){return this.string.charAt(this.pos)||void 0},next:function(){return this.posb},eatSpace:function(){for(var a=this.pos;/[\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);return b>-1?(this.pos=b,!0):void 0},backUp:function(a){this.pos-=a},column:function(){return Ee(this.string,this.start,this.tabSize)},indentation:function(){return Ee(this.string,null,this.tabSize)},match:function(a,b,c){if("string"!=typeof a){var e=this.string.slice(this.pos).match(a);return e&&e.index>0?null:(e&&b!==!1&&(this.pos+=e[0].length),e)}var d=function(a){return c?a.toLowerCase():a};return d(this.string).indexOf(d(a),this.pos)==this.pos?(b!==!1&&(this.pos+=a.length),!0):void 0},current:function(){return this.string.slice(this.start,this.pos)}},w.StringStream=_c,w.TextMarker=ad,ad.prototype.clear=function(){if(!this.explicitlyCleared){var a=this.doc.cm,b=a&&!a.curOp;b&&tb(a);for(var c=null,d=null,e=0;ea.display.maxLineLength&&(a.display.maxLine=h,a.display.maxLineLength=i,a.display.maxLineChanged=!0)}null!=c&&a&&yb(a,c,d+1),this.lines.length=0,this.explicitlyCleared=!0,this.collapsed&&this.doc.cantEdit&&(this.doc.cantEdit=!1,a&&zc(a)),b&&ub(a),ye(this,"clear")}},ad.prototype.find=function(){for(var a,b,c=0;cc;++c){var e=this.lines[c];this.height-=e.height,Ad(e),ye(e,"delete")}this.lines.splice(a,b)},collapse:function(a){a.splice.apply(a,[a.length,0].concat(this.lines))},insertInner:function(a,b,c){this.height+=c,this.lines=this.lines.slice(0,a).concat(b).concat(this.lines.slice(a));for(var d=0,e=b.length;e>d;++d)b[d].parent=this},iterN:function(a,b,c){for(var d=a+b;d>a;++a)if(c(this.lines[a]))return!0}},Pd.prototype={chunkSize:function(){return this.size},removeInner:function(a,b){this.size-=b;for(var c=0;ca){var f=Math.min(b,e-a),g=d.height;if(d.removeInner(a,f),this.height-=g-d.height,e==f&&(this.children.splice(c--,1),d.parent=null),0==(b-=f))break;a=0}else a-=e}if(this.size-b<25){var h=[];this.collapse(h),this.children=[new Od(h)],this.children[0].parent=this}},collapse:function(a){for(var b=0,c=this.children.length;c>b;++b)this.children[b].collapse(a)},insertInner:function(a,b,c){this.size+=b.length,this.height+=c;for(var d=0,e=this.children.length;e>d;++d){var f=this.children[d],g=f.chunkSize();if(g>=a){if(f.insertInner(a,b,c),f.lines&&f.lines.length>50){for(;f.lines.length>50;){var h=f.lines.splice(f.lines.length-25,25),i=new Od(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)){var a=this;do{var b=a.children.splice(a.children.length-5,5),c=new Pd(b);if(a.parent){a.size-=c.size,a.height-=c.height;var e=Je(a.parent.children,a);a.parent.children.splice(e+1,0,c)}else{var d=new Pd(a.children);d.parent=a,a.children=[d,c],a=d}c.parent=a.parent}while(a.children.length>10);a.parent.maybeSpill()}},iterN:function(a,b,c){for(var d=0,e=this.children.length;e>d;++d){var f=this.children[d],g=f.chunkSize();if(g>a){var h=Math.min(b,g-a);if(f.iterN(a,h,c))return!0;if(0==(b-=h))break;a=0}else a-=g}}};var Qd=0,Rd=w.Doc=function(a,b,c){if(!(this instanceof Rd))return new Rd(a,b,c);null==c&&(c=0),Pd.call(this,[new Od([yd("",null)])]),this.first=c,this.scrollTop=this.scrollLeft=0,this.cantEdit=!1,this.history=ce(),this.frontier=c;var d=oc(c,0);this.sel={from:d,to:d,head:d,anchor:d,shift:!1,extend:!1,goalColumn:null},this.id=++Qd,this.modeOption=b,"string"==typeof a&&(a=bf(a)),Nd(this,{from:d,to:d,text:a},null,{head:d,anchor:d})};Rd.prototype=Ke(Pd.prototype,{iter:function(a,b,c){c?this.iterN(a-this.first,b-a,c):this.iterN(this.first,this.first+this.size,a)},insert:function(a,b){for(var c=0,d=0,e=b.length;e>d;++d)c+=b[d].height;this.insertInner(a-this.first,b,c)},remove:function(a,b){this.removeInner(a-this.first,b)},getValue:function(a){var b=Yd(this,this.first,this.first+this.size);return a===!1?b:b.join(a||"\n")},setValue:function(a){var b=oc(this.first,0),c=this.first+this.size-1;hc(this,{from:b,to:oc(c,Wd(this,c).text.length),text:bf(a),origin:"setValue"},{head:b,anchor:b},!0)},replaceRange:function(a,b,c,d){b=tc(this,b),c=c?tc(this,c):b,nc(this,a,b,c,d)},getRange:function(a,b,c){var d=Xd(this,tc(this,a),tc(this,b));return c===!1?d:d.join(c||"\n")},getLine:function(a){var b=this.getLineHandle(a);return b&&b.text},setLine:function(a,b){vc(this,a)&&nc(this,b,oc(a,0),tc(this,oc(a)))},removeLine:function(a){vc(this,a)&&nc(this,"",oc(a,0),tc(this,oc(a+1,0)))},getLineHandle:function(a){return vc(this,a)?Wd(this,a):void 0},getLineNumber:function(a){return $d(a)},lineCount:function(){return this.size},firstLine:function(){return this.first},lastLine:function(){return this.first+this.size-1},clipPos:function(a){return tc(this,a)},getCursor:function(a){var c,b=this.sel;return c=null==a||"head"==a?b.head:"anchor"==a?b.anchor:"end"==a||a===!1?b.to:b.from,rc(c)},somethingSelected:function(){return!pc(this.sel.head,this.sel.anchor)},setCursor:wb(function(a,b,c){var d=tc(this,"number"==typeof a?oc(a,b||0):a);c?wc(this,d):yc(this,d,d)}),setSelection:wb(function(a,b){yc(this,tc(this,a),tc(this,b||a))}),extendSelection:wb(function(a,b){wc(this,tc(this,a),b&&tc(this,b))}),getSelection:function(a){return this.getRange(this.sel.from,this.sel.to,a)},replaceSelection:function(a,b,c){hc(this,{from:this.sel.from,to:this.sel.to,text:bf(a),origin:c},b||"around")},undo:wb(function(){jc(this,"undo")}),redo:wb(function(){jc(this,"redo")}),setExtending:function(a){this.sel.extend=a},historySize:function(){var a=this.history;return{undo:a.done.length,redo:a.undone.length}},clearHistory:function(){this.history=ce()},markClean:function(){this.history.dirtyCounter=0,this.history.lastOp=this.history.lastOrigin=null},isClean:function(){return 0==this.history.dirtyCounter},getHistory:function(){return{done:ie(this.history.done),undone:ie(this.history.undone)}},setHistory:function(a){var b=this.history=ce();b.done=a.done.slice(0),b.undone=a.undone.slice(0)},markText:function(a,b,c){return bd(this,tc(this,a),tc(this,b),c,"range")},setBookmark:function(a,b){var c={replacedWith:b&&(null==b.nodeType?b.widget:b),insertLeft:b&&b.insertLeft};return a=tc(this,a),bd(this,a,a,c,"bookmark")},findMarksAt:function(a){a=tc(this,a);var b=[],c=Wd(this,a.line).markedSpans;if(c)for(var d=0;d=a.ch)&&b.push(e.marker.parent||e.marker)}return b},getAllMarks:function(){var a=[];return this.iter(function(b){var c=b.markedSpans;if(c)for(var d=0;da?(b=a,!0):(a-=e,++c,void 0)}),tc(this,oc(c,b))},indexFromPos:function(a){a=tc(this,a);var b=a.ch;return a.lineb&&(b=a.from),null!=a.to&&a.to\]|\}~][\(\{\[<]|\$'/);var Ze,_e,bf=3!="\n\nb".split(/\n/).length?function(a){for(var b=0,c=[],d=a.length;d>=b;){var e=a.indexOf("\n",b);-1==e&&(e=a.length);var f=a.slice(b,"\r"==a.charAt(e-1)?e-1:e),g=f.indexOf("\r");-1!=g?(c.push(f.slice(0,g)),b+=g+1):(c.push(f),b=e+1)}return c}:function(a){return a.split(/\r\n?|\n/)};w.splitLines=bf;var cf=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?0!=b.compareEndPoints("StartToEnd",b):!1},df=function(){var a=Se("div");return"oncopy"in a?!0:(a.setAttribute("oncopy","return;"),"function"==typeof a.oncopy)}(),ef={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",109:"-",107:"=",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"};w.keyNames=ef,function(){for(var a=0;10>a;a++)ef[a+48]=String(a);for(var a=65;90>=a;a++)ef[a]=String.fromCharCode(a);for(var a=1;12>=a;a++)ef[a+111]=ef[a+63235]="F"+a}();var pf=function(){function c(c){return 255>=c?a.charAt(c):c>=1424&&1524>=c?"R":c>=1536&&1791>=c?b.charAt(c-1536):c>=1792&&2220>=c?"r":"L"}var a="bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLL",b="rrrrrrrrrrrr,rNNmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmrrrrrrrnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmNmmmmrrrrrrrrrrrrrrrrrr",d=/[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/,e=/[stwN]/,f=/[LRr]/,g=/[Lb1n]/,h=/[1n]/,i="L";return function(a){if(!d.test(a))return!1;for(var l,b=a.length,j=[],k=0;b>k;++k)j.push(l=c(a.charCodeAt(k)));for(var k=0,m=i;b>k;++k){var l=j[k];"m"==l?j[k]=m:m=l}for(var k=0,n=i;b>k;++k){var l=j[k]; 4 | "1"==l&&"r"==n?j[k]="n":f.test(l)&&(n=l,"r"==l&&(j[k]="R"))}for(var k=1,m=j[0];b-1>k;++k){var l=j[k];"+"==l&&"1"==m&&"1"==j[k+1]?j[k]="1":","!=l||m!=j[k+1]||"1"!=m&&"n"!=m||(j[k]=m),m=l}for(var k=0;b>k;++k){var l=j[k];if(","==l)j[k]="N";else if("%"==l){for(var o=k+1;b>o&&"%"==j[o];++o);for(var p=k&&"!"==j[k-1]||b-1>o&&"1"==j[o]?"1":"N",q=k;o>q;++q)j[q]=p;k=o-1}}for(var k=0,n=i;b>k;++k){var l=j[k];"L"==n&&"1"==l?j[k]="L":f.test(l)&&(n=l)}for(var k=0;b>k;++k)if(e.test(j[k])){for(var o=k+1;b>o&&e.test(j[o]);++o);for(var r="L"==(k?j[k-1]:i),s="L"==(b-1>o?j[o]:i),p=r||s?"L":"R",q=k;o>q;++q)j[q]=p;k=o-1}for(var u,t=[],k=0;b>k;)if(g.test(j[k])){var v=k;for(++k;b>k&&g.test(j[k]);++k);t.push({from:v,to:k,level:0})}else{var w=k,x=t.length;for(++k;b>k&&"L"!=j[k];++k);for(var q=w;k>q;)if(h.test(j[q])){q>w&&t.splice(x,0,{from:w,to:q,level:1});var y=q;for(++q;k>q&&h.test(j[q]);++q);t.splice(x,0,{from:y,to:q,level:2}),w=q}else++q;k>w&&t.splice(x,0,{from:w,to:k,level:1})}return 1==t[0].level&&(u=a.match(/^\s+/))&&(t[0].from=u[0].length,t.unshift({from:0,to:u[0].length,level:0})),1==He(t).level&&(u=a.match(/\s+$/))&&(He(t).to-=u[0].length,t.push({from:b-u[0].length,to:b,level:0})),t[0].level!=He(t).level&&t.push({from:b,to:b,level:t[0].level}),t}}();return w.version="3.1",w}(); -------------------------------------------------------------------------------- /lib/mergely.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 by Jamie Peabody, http://www.mergely.com 3 | * All rights reserved. 4 | * Version: 3.3.7 2014-08-17 5 | */ 6 | 7 | /* required */ 8 | .mergely-column textarea { width: 80px; height: 200px; } 9 | .mergely-column { float: left; } 10 | .mergely-margin { float: left; } 11 | .mergely-canvas { float: left; width: 28px; } 12 | 13 | /* resizeable */ 14 | .mergely-resizer { width: 100%; height: 100%; } 15 | 16 | /* style configuration */ 17 | .mergely-column { border: 1px solid #ccc; } 18 | .mergely-active { border: 1px solid #a3d1ff; } 19 | 20 | .mergely.a.rhs.start { border-top: 1px solid #a3d1ff; } 21 | .mergely.a.lhs.start.end, 22 | .mergely.a.rhs.end { border-bottom: 1px solid #a3d1ff; } 23 | .mergely.a.rhs { background-color: #ddeeff; } 24 | .mergely.a.lhs.start.end.first { border-bottom: 0; border-top: 1px solid #a3d1ff; } 25 | 26 | .mergely.d.lhs { background-color: #edc0c0; } 27 | .mergely.d.lhs.end, 28 | .mergely.d.rhs.start.end { border-bottom: 1px solid #ff7f7f; } 29 | .mergely.d.rhs.start.end.first { border-bottom: 0; border-top: 1px solid #ff7f7f; } 30 | .mergely.d.lhs.start { border-top: 1px solid #ff7f7f; } 31 | 32 | .mergely.c.lhs, 33 | .mergely.c.rhs { background-color: #fafafa; } 34 | .mergely.c.lhs.start, 35 | .mergely.c.rhs.start { border-top: 1px solid #a3a3a3; } 36 | .mergely.c.lhs.end, 37 | .mergely.c.rhs.end { border-bottom: 1px solid #a3a3a3; } 38 | 39 | .mergely.ch.a.rhs { background-color: #ddeeff; } 40 | .mergely.ch.d.lhs { background-color: #edc0c0; text-decoration: line-through; color: #888; } 41 | -------------------------------------------------------------------------------- /lib/mergely.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 by Jamie Peabody, http://www.mergely.com 3 | * All rights reserved. 4 | * Version: 3.3.7 2014-08-17 5 | */ 6 | Mgly = {}; 7 | 8 | Mgly.Timer = function(){ 9 | var self = this; 10 | self.start = function() { self.t0 = new Date().getTime(); } 11 | self.stop = function() { 12 | var t1 = new Date().getTime(); 13 | var d = t1 - self.t0; 14 | self.t0 = t1; 15 | return d; 16 | } 17 | self.start(); 18 | } 19 | 20 | Mgly.ChangeExpression = new RegExp(/(^(?![><-])*\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/); 21 | 22 | Mgly.DiffParser = function(diff) { 23 | var changes = []; 24 | var change_id = 0; 25 | // parse diff 26 | var diff_lines = diff.split(/\n/); 27 | for (var i = 0; i < diff_lines.length; ++i) { 28 | if (diff_lines[i].length == 0) continue; 29 | var change = {}; 30 | var test = Mgly.ChangeExpression.exec(diff_lines[i]); 31 | if (test == null) continue; 32 | // lines are zero-based 33 | var fr = test[1].split(','); 34 | change['lhs-line-from'] = fr[0] - 1; 35 | if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1; 36 | else change['lhs-line-to'] = fr[1] - 1; 37 | var to = test[3].split(','); 38 | change['rhs-line-from'] = to[0] - 1; 39 | if (to.length == 1) change['rhs-line-to'] = to[0] - 1; 40 | else change['rhs-line-to'] = to[1] - 1; 41 | change['op'] = test[2]; 42 | changes[change_id++] = change; 43 | } 44 | return changes; 45 | } 46 | 47 | Mgly.sizeOf = function(obj) { 48 | var size = 0, key; 49 | for (key in obj) { 50 | if (obj.hasOwnProperty(key)) size++; 51 | } 52 | return size; 53 | } 54 | 55 | Mgly.LCS = function(x, y) { 56 | this.x = x.replace(/[ ]{1}/g, '\n'); 57 | this.y = y.replace(/[ ]{1}/g, '\n'); 58 | } 59 | jQuery.extend(Mgly.LCS.prototype, { 60 | clear: function() { this.ready = 0; }, 61 | diff: function(added, removed) { 62 | var d = new Mgly.diff(this.x, this.y, {ignorews: false}); 63 | var changes = Mgly.DiffParser(d.normal_form()); 64 | var li = 0, lj = 0; 65 | for (var i = 0; i < changes.length; ++i) { 66 | var change = changes[i]; 67 | if (change.op != 'a') { 68 | // find the starting index of the line 69 | li = d.getLines('lhs').slice(0, change['lhs-line-from']).join(' ').length; 70 | // get the index of the the span of the change 71 | lj = change['lhs-line-to'] + 1; 72 | // get the changed text 73 | var lchange = d.getLines('lhs').slice(change['lhs-line-from'], lj).join(' '); 74 | if (change.op == 'd') lchange += ' ';// include the leading space 75 | else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word 76 | // output the changed index and text 77 | removed(li, li + lchange.length); 78 | } 79 | if (change.op != 'd') { 80 | // find the starting index of the line 81 | li = d.getLines('rhs').slice(0, change['rhs-line-from']).join(' ').length; 82 | // get the index of the the span of the change 83 | lj = change['rhs-line-to'] + 1; 84 | // get the changed text 85 | var rchange = d.getLines('rhs').slice(change['rhs-line-from'], lj).join(' '); 86 | if (change.op == 'a') rchange += ' ';// include the leading space 87 | else if (li > 0 && change.op == 'c') li += 1; // ignore leading space if not first word 88 | // output the changed index and text 89 | added(li, li + rchange.length); 90 | } 91 | } 92 | } 93 | }); 94 | 95 | Mgly.CodeifyText = function(settings) { 96 | this._max_code = 0; 97 | this._diff_codes = {}; 98 | this.ctxs = {}; 99 | this.options = {ignorews: false}; 100 | jQuery.extend(this, settings); 101 | this.lhs = settings.lhs.split('\n'); 102 | this.rhs = settings.rhs.split('\n'); 103 | } 104 | 105 | jQuery.extend(Mgly.CodeifyText.prototype, { 106 | getCodes: function(side) { 107 | if (!this.ctxs.hasOwnProperty(side)) { 108 | var ctx = this._diff_ctx(this[side]); 109 | this.ctxs[side] = ctx; 110 | ctx.codes.length = Object.keys(ctx.codes).length; 111 | } 112 | return this.ctxs[side].codes; 113 | }, 114 | getLines: function(side) { 115 | return this.ctxs[side].lines; 116 | }, 117 | _diff_ctx: function(lines) { 118 | var ctx = {i: 0, codes: {}, lines: lines}; 119 | this._codeify(lines, ctx); 120 | return ctx; 121 | }, 122 | _codeify: function(lines, ctx) { 123 | var code = this._max_code; 124 | for (var i = 0; i < lines.length; ++i) { 125 | var line = lines[i]; 126 | if (this.options.ignorews) { 127 | line = line.replace(/\s+/g, ''); 128 | } 129 | var aCode = this._diff_codes[line]; 130 | if (aCode != undefined) { 131 | ctx.codes[i] = aCode; 132 | } 133 | else { 134 | this._max_code++; 135 | this._diff_codes[line] = this._max_code; 136 | ctx.codes[i] = this._max_code; 137 | } 138 | } 139 | } 140 | }); 141 | 142 | Mgly.diff = function(lhs, rhs, options) { 143 | var opts = jQuery.extend({ignorews: false}, options); 144 | this.codeify = new Mgly.CodeifyText({ 145 | lhs: lhs, 146 | rhs: rhs, 147 | options: opts 148 | }); 149 | var lhs_ctx = { 150 | codes: this.codeify.getCodes('lhs'), 151 | modified: {} 152 | }; 153 | var rhs_ctx = { 154 | codes: this.codeify.getCodes('rhs'), 155 | modified: {} 156 | }; 157 | var max = (lhs_ctx.codes.length + rhs_ctx.codes.length + 1); 158 | var vector_d = Array( 2 * max + 2 ); 159 | var vector_u = Array( 2 * max + 2 ); 160 | this._lcs(lhs_ctx, 0, lhs_ctx.codes.length, rhs_ctx, 0, rhs_ctx.codes.length, vector_u, vector_d); 161 | this._optimize(lhs_ctx); 162 | this._optimize(rhs_ctx); 163 | this.items = this._create_diffs(lhs_ctx, rhs_ctx); 164 | }; 165 | 166 | jQuery.extend(Mgly.diff.prototype, { 167 | changes: function() { return this.items; }, 168 | getLines: function(side) { 169 | return this.codeify.getLines(side); 170 | }, 171 | normal_form: function() { 172 | var nf = ''; 173 | for (var index = 0; index < this.items.length; ++index) { 174 | var item = this.items[index]; 175 | var lhs_str = ''; 176 | var rhs_str = ''; 177 | var change = 'c'; 178 | if (item.lhs_deleted_count == 0 && item.rhs_inserted_count > 0) change = 'a'; 179 | else if (item.lhs_deleted_count > 0 && item.rhs_inserted_count == 0) change = 'd'; 180 | 181 | if (item.lhs_deleted_count == 1) lhs_str = item.lhs_start + 1; 182 | else if (item.lhs_deleted_count == 0) lhs_str = item.lhs_start; 183 | else lhs_str = (item.lhs_start + 1) + ',' + (item.lhs_start + item.lhs_deleted_count); 184 | 185 | if (item.rhs_inserted_count == 1) rhs_str = item.rhs_start + 1; 186 | else if (item.rhs_inserted_count == 0) rhs_str = item.rhs_start; 187 | else rhs_str = (item.rhs_start + 1) + ',' + (item.rhs_start + item.rhs_inserted_count); 188 | nf += lhs_str + change + rhs_str + '\n'; 189 | 190 | var lhs_lines = this.getLines('lhs'); 191 | var rhs_lines = this.getLines('rhs'); 192 | if (rhs_lines && lhs_lines) { 193 | // if rhs/lhs lines have been retained, output contextual diff 194 | for (var i = item.lhs_start; i < item.lhs_start + item.lhs_deleted_count; ++i) { 195 | nf += '< ' + lhs_lines[i] + '\n'; 196 | } 197 | if (item.rhs_inserted_count && item.lhs_deleted_count) nf += '---\n'; 198 | for (var i = item.rhs_start; i < item.rhs_start + item.rhs_inserted_count; ++i) { 199 | nf += '> ' + rhs_lines[i] + '\n'; 200 | } 201 | } 202 | } 203 | return nf; 204 | }, 205 | _lcs: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) { 206 | while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_lower] == rhs_ctx.codes[rhs_lower]) ) { 207 | ++lhs_lower; 208 | ++rhs_lower; 209 | } 210 | while ( (lhs_lower < lhs_upper) && (rhs_lower < rhs_upper) && (lhs_ctx.codes[lhs_upper - 1] == rhs_ctx.codes[rhs_upper - 1]) ) { 211 | --lhs_upper; 212 | --rhs_upper; 213 | } 214 | if (lhs_lower == lhs_upper) { 215 | while (rhs_lower < rhs_upper) { 216 | rhs_ctx.modified[ rhs_lower++ ] = true; 217 | } 218 | } 219 | else if (rhs_lower == rhs_upper) { 220 | while (lhs_lower < lhs_upper) { 221 | lhs_ctx.modified[ lhs_lower++ ] = true; 222 | } 223 | } 224 | else { 225 | var sms = this._sms(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d); 226 | this._lcs(lhs_ctx, lhs_lower, sms.x, rhs_ctx, rhs_lower, sms.y, vector_u, vector_d); 227 | this._lcs(lhs_ctx, sms.x, lhs_upper, rhs_ctx, sms.y, rhs_upper, vector_u, vector_d); 228 | } 229 | }, 230 | _sms: function(lhs_ctx, lhs_lower, lhs_upper, rhs_ctx, rhs_lower, rhs_upper, vector_u, vector_d) { 231 | var max = lhs_ctx.codes.length + rhs_ctx.codes.length + 1; 232 | var kdown = lhs_lower - rhs_lower; 233 | var kup = lhs_upper - rhs_upper; 234 | var delta = (lhs_upper - lhs_lower) - (rhs_upper - rhs_lower); 235 | var odd = (delta & 1) != 0; 236 | var offset_down = max - kdown; 237 | var offset_up = max - kup; 238 | var maxd = ((lhs_upper - lhs_lower + rhs_upper - rhs_lower) / 2) + 1; 239 | vector_d[ offset_down + kdown + 1 ] = lhs_lower; 240 | vector_u[ offset_up + kup - 1 ] = lhs_upper; 241 | var ret = {x:0,y:0}; 242 | for (var d = 0; d <= maxd; ++d) { 243 | for (var k = kdown - d; k <= kdown + d; k += 2) { 244 | var x, y; 245 | if (k == kdown - d) { 246 | x = vector_d[ offset_down + k + 1 ];//down 247 | } 248 | else { 249 | x = vector_d[ offset_down + k - 1 ] + 1;//right 250 | if ((k < (kdown + d)) && (vector_d[ offset_down + k + 1 ] >= x)) { 251 | x = vector_d[ offset_down + k + 1 ];//down 252 | } 253 | } 254 | y = x - k; 255 | // find the end of the furthest reaching forward D-path in diagonal k. 256 | while ((x < lhs_upper) && (y < rhs_upper) && (lhs_ctx.codes[x] == rhs_ctx.codes[y])) { 257 | x++; y++; 258 | } 259 | vector_d[ offset_down + k ] = x; 260 | // overlap ? 261 | if (odd && (kup - d < k) && (k < kup + d)) { 262 | if (vector_u[offset_up + k] <= vector_d[offset_down + k]) { 263 | ret.x = vector_d[offset_down + k]; 264 | ret.y = vector_d[offset_down + k] - k; 265 | return (ret); 266 | } 267 | } 268 | } 269 | // Extend the reverse path. 270 | for (var k = kup - d; k <= kup + d; k += 2) { 271 | // find the only or better starting point 272 | var x, y; 273 | if (k == kup + d) { 274 | x = vector_u[offset_up + k - 1]; // up 275 | } else { 276 | x = vector_u[offset_up + k + 1] - 1; // left 277 | if ((k > kup - d) && (vector_u[offset_up + k - 1] < x)) 278 | x = vector_u[offset_up + k - 1]; // up 279 | } 280 | y = x - k; 281 | while ((x > lhs_lower) && (y > rhs_lower) && (lhs_ctx.codes[x - 1] == rhs_ctx.codes[y - 1])) { 282 | // diagonal 283 | x--; 284 | y--; 285 | } 286 | vector_u[offset_up + k] = x; 287 | // overlap ? 288 | if (!odd && (kdown - d <= k) && (k <= kdown + d)) { 289 | if (vector_u[offset_up + k] <= vector_d[offset_down + k]) { 290 | ret.x = vector_d[offset_down + k]; 291 | ret.y = vector_d[offset_down + k] - k; 292 | return (ret); 293 | } 294 | } 295 | } 296 | } 297 | throw "the algorithm should never come here."; 298 | }, 299 | _optimize: function(ctx) { 300 | var start = 0, end = 0; 301 | while (start < ctx.length) { 302 | while ((start < ctx.length) && (ctx.modified[start] == undefined || ctx.modified[start] == false)) { 303 | start++; 304 | } 305 | end = start; 306 | while ((end < ctx.length) && (ctx.modified[end] == true)) { 307 | end++; 308 | } 309 | if ((end < ctx.length) && (ctx.ctx[start] == ctx.codes[end])) { 310 | ctx.modified[start] = false; 311 | ctx.modified[end] = true; 312 | } 313 | else { 314 | start = end; 315 | } 316 | } 317 | }, 318 | _create_diffs: function(lhs_ctx, rhs_ctx) { 319 | var items = []; 320 | var lhs_start = 0, rhs_start = 0; 321 | var lhs_line = 0, rhs_line = 0; 322 | 323 | while (lhs_line < lhs_ctx.codes.length || rhs_line < rhs_ctx.codes.length) { 324 | if ((lhs_line < lhs_ctx.codes.length) && (!lhs_ctx.modified[lhs_line]) 325 | && (rhs_line < rhs_ctx.codes.length) && (!rhs_ctx.modified[rhs_line])) { 326 | // equal lines 327 | lhs_line++; 328 | rhs_line++; 329 | } 330 | else { 331 | // maybe deleted and/or inserted lines 332 | lhs_start = lhs_line; 333 | rhs_start = rhs_line; 334 | 335 | while (lhs_line < lhs_ctx.codes.length && (rhs_line >= rhs_ctx.codes.length || lhs_ctx.modified[lhs_line])) 336 | lhs_line++; 337 | 338 | while (rhs_line < rhs_ctx.codes.length && (lhs_line >= lhs_ctx.codes.length || rhs_ctx.modified[rhs_line])) 339 | rhs_line++; 340 | 341 | if ((lhs_start < lhs_line) || (rhs_start < rhs_line)) { 342 | // store a new difference-item 343 | items.push({ 344 | lhs_start: lhs_start, 345 | rhs_start: rhs_start, 346 | lhs_deleted_count: lhs_line - lhs_start, 347 | rhs_inserted_count: rhs_line - rhs_start 348 | }); 349 | } 350 | } 351 | } 352 | return items; 353 | } 354 | }); 355 | 356 | Mgly.mergely = function(el, options) { 357 | if (el) { 358 | this.init(el, options); 359 | } 360 | }; 361 | 362 | jQuery.extend(Mgly.mergely.prototype, { 363 | name: 'mergely', 364 | //http://jupiterjs.com/news/writing-the-perfect-jquery-plugin 365 | init: function(el, options) { 366 | this.diffView = new Mgly.CodeMirrorDiffView(el, options); 367 | this.bind(el); 368 | }, 369 | bind: function(el) { 370 | this.diffView.bind(el); 371 | } 372 | }); 373 | 374 | Mgly.CodeMirrorDiffView = function(el, options) { 375 | CodeMirror.defineExtension('centerOnCursor', function() { 376 | var coords = this.cursorCoords(null, 'local'); 377 | this.scrollTo(null, 378 | (coords.y + coords.yBot) / 2 - (this.getScrollerElement().clientHeight / 2)); 379 | }); 380 | this.init(el, options); 381 | }; 382 | 383 | jQuery.extend(Mgly.CodeMirrorDiffView.prototype, { 384 | init: function(el, options) { 385 | this.settings = { 386 | autoupdate: true, 387 | autoresize: true, 388 | rhs_margin: 'right', 389 | lcs: true, 390 | sidebar: true, 391 | viewport: false, 392 | ignorews: false, 393 | fadein: 'fast', 394 | editor_width: '650px', 395 | editor_height: '400px', 396 | resize_timeout: 500, 397 | change_timeout: 150, 398 | fgcolor: {a:'#4ba3fa',c:'#a3a3a3',d:'#ff7f7f', // color for differences (soft color) 399 | ca:'#4b73ff',cc:'#434343',cd:'#ff4f4f'}, // color for currently active difference (bright color) 400 | bgcolor: '#eee', 401 | vpcolor: 'rgba(0, 0, 200, 0.5)', 402 | lhs: function(setValue) { }, 403 | rhs: function(setValue) { }, 404 | loaded: function() { }, 405 | _auto_width: function(w) { return w; }, 406 | resize: function(init) { 407 | var scrollbar = init ? 16 : 0; 408 | var w = jQuery(el).parent().width() + scrollbar; 409 | if (this.width == 'auto') { 410 | w = this._auto_width(w); 411 | } 412 | else { 413 | w = this.width; 414 | this.editor_width = w; 415 | } 416 | if (this.height == 'auto') { 417 | //h = this._auto_height(h); 418 | h = jQuery(el).parent().height(); 419 | } 420 | else { 421 | h = this.height; 422 | this.editor_height = h; 423 | } 424 | var content_width = w / 2.0 - 2 * 8 - 8; 425 | var content_height = h; 426 | var self = jQuery(el); 427 | self.find('.mergely-column').css({ width: content_width + 'px' }); 428 | self.find('.mergely-column, .mergely-canvas, .mergely-margin, .mergely-column textarea, .CodeMirror-scroll, .cm-s-default').css({ height: content_height + 'px' }); 429 | self.find('.mergely-canvas').css({ height: content_height + 'px' }); 430 | self.find('.mergely-column textarea').css({ width: content_width + 'px' }); 431 | self.css({ width: w, height: h, clear: 'both' }); 432 | if (self.css('display') == 'none') { 433 | if (this.fadein != false) self.fadeIn(this.fadein); 434 | else self.show(); 435 | if (this.loaded) this.loaded(); 436 | } 437 | if (this.resized) this.resized(); 438 | }, 439 | _debug: '', //scroll,draw,calc,diff,markup,change 440 | resized: function() { } 441 | }; 442 | var cmsettings = { 443 | mode: 'text/plain', 444 | readOnly: false, 445 | lineWrapping: false, 446 | lineNumbers: true, 447 | gutters: ['merge', 'CodeMirror-linenumbers'] 448 | } 449 | this.lhs_cmsettings = {}; 450 | this.rhs_cmsettings = {}; 451 | 452 | // save this element for faster queries 453 | this.element = jQuery(el); 454 | 455 | // save options if there are any 456 | if (options && options.cmsettings) jQuery.extend(this.lhs_cmsettings, cmsettings, options.cmsettings, options.lhs_cmsettings); 457 | if (options && options.cmsettings) jQuery.extend(this.rhs_cmsettings, cmsettings, options.cmsettings, options.rhs_cmsettings); 458 | if (options) jQuery.extend(this.settings, options); 459 | 460 | // bind if the element is destroyed 461 | this.element.bind('destroyed', jQuery.proxy(this.teardown, this)); 462 | 463 | // save this instance in jQuery data, binding this view to the node 464 | jQuery.data(el, 'mergely', this); 465 | }, 466 | unbind: function() { 467 | if (this.changed_timeout != null) clearTimeout(this.changed_timeout); 468 | this.editor[this.id + '-lhs'].toTextArea(); 469 | this.editor[this.id + '-rhs'].toTextArea(); 470 | }, 471 | destroy: function() { 472 | this.element.unbind('destroyed', this.teardown); 473 | this.teardown(); 474 | }, 475 | teardown: function() { 476 | this.unbind(); 477 | }, 478 | lhs: function(text) { 479 | this.editor[this.id + '-lhs'].setValue(text); 480 | }, 481 | rhs: function(text) { 482 | this.editor[this.id + '-rhs'].setValue(text); 483 | }, 484 | update: function() { 485 | this._changing(this.id + '-lhs', this.id + '-rhs'); 486 | }, 487 | unmarkup: function() { 488 | this._clear(); 489 | }, 490 | scrollToDiff: function(direction) { 491 | if (!this.changes.length) return; 492 | if (direction == 'next') { 493 | this._current_diff = Math.min(++this._current_diff, this.changes.length - 1); 494 | } 495 | else { 496 | this._current_diff = Math.max(--this._current_diff, 0); 497 | } 498 | this._scroll_to_change(this.changes[this._current_diff]); 499 | this._changed(this.id + '-lhs', this.id + '-rhs'); 500 | }, 501 | mergeCurrentChange: function(side) { 502 | if (!this.changes.length) return; 503 | if (side == 'lhs' && !this.lhs_cmsettings.readOnly) { 504 | this._merge_change(this.changes[this._current_diff], 'rhs', 'lhs'); 505 | } 506 | else if (side == 'rhs' && !this.rhs_cmsettings.readOnly) { 507 | this._merge_change(this.changes[this._current_diff], 'lhs', 'rhs'); 508 | } 509 | }, 510 | scrollTo: function(side, num) { 511 | var le = this.editor[this.id + '-lhs']; 512 | var re = this.editor[this.id + '-rhs']; 513 | if (side == 'lhs') { 514 | le.setCursor(num); 515 | le.centerOnCursor(); 516 | } 517 | else { 518 | re.setCursor(num); 519 | re.centerOnCursor(); 520 | } 521 | }, 522 | options: function(opts) { 523 | if (opts) { 524 | jQuery.extend(this.settings, opts); 525 | if (this.settings.autoresize) this.resize(); 526 | if (this.settings.autoupdate) this.update(); 527 | if (this.settings.hasOwnProperty('rhs_margin')) { 528 | // dynamically swap the margin 529 | if (this.settings.rhs_margin == 'left') { 530 | this.element.find('.mergely-margin:last-child').insertAfter( 531 | this.element.find('.mergely-canvas')); 532 | } 533 | else { 534 | var target = this.element.find('.mergely-margin').last(); 535 | target.appendTo(target.parent()); 536 | } 537 | } 538 | if (this.settings.hasOwnProperty('sidebar')) { 539 | // dynamically enable sidebars 540 | if (this.settings.sidebar) { 541 | jQuery(this.element).find('.mergely-margin').css({display: 'block'}); 542 | } 543 | else { 544 | jQuery(this.element).find('.mergely-margin').css({display: 'none'}); 545 | } 546 | } 547 | } 548 | else { 549 | return this.settings; 550 | } 551 | }, 552 | swap: function() { 553 | if (this.lhs_cmsettings.readOnly || this.rhs_cmsettings.readOnly) return; 554 | var le = this.editor[this.id + '-lhs']; 555 | var re = this.editor[this.id + '-rhs']; 556 | var tmp = re.getValue(); 557 | re.setValue(le.getValue()); 558 | le.setValue(tmp); 559 | }, 560 | merge: function(side) { 561 | var le = this.editor[this.id + '-lhs']; 562 | var re = this.editor[this.id + '-rhs']; 563 | if (side == 'lhs' && !this.lhs_cmsettings.readOnly) le.setValue(re.getValue()); 564 | else if (!this.rhs_cmsettings.readOnly) re.setValue(le.getValue()); 565 | }, 566 | get: function(side) { 567 | var ed = this.editor[this.id + '-' + side]; 568 | var t = ed.getValue(); 569 | if (t == undefined) return ''; 570 | return t; 571 | }, 572 | clear: function(side) { 573 | if (side == 'lhs' && this.lhs_cmsettings.readOnly) return; 574 | if (side == 'rhs' && this.rhs_cmsettings.readOnly) return; 575 | var ed = this.editor[this.id + '-' + side]; 576 | ed.setValue(''); 577 | }, 578 | cm: function(side) { 579 | return this.editor[this.id + '-' + side]; 580 | }, 581 | search: function(side, query, direction) { 582 | var le = this.editor[this.id + '-lhs']; 583 | var re = this.editor[this.id + '-rhs']; 584 | var editor; 585 | if (side == 'lhs') editor = le; 586 | else editor = re; 587 | direction = (direction == 'prev') ? 'findPrevious' : 'findNext'; 588 | if ((editor.getSelection().length == 0) || (this.prev_query[side] != query)) { 589 | this.cursor[this.id] = editor.getSearchCursor(query, { line: 0, ch: 0 }, false); 590 | this.prev_query[side] = query; 591 | } 592 | var cursor = this.cursor[this.id]; 593 | 594 | if (cursor[direction]()) { 595 | editor.setSelection(cursor.from(), cursor.to()); 596 | } 597 | else { 598 | cursor = editor.getSearchCursor(query, { line: 0, ch: 0 }, false); 599 | } 600 | }, 601 | resize: function() { 602 | this.settings.resize(); 603 | this._changing(this.id + '-lhs', this.id + '-rhs'); 604 | this._set_top_offset(this.id + '-lhs'); 605 | }, 606 | diff: function() { 607 | var lhs = this.editor[this.id + '-lhs'].getValue(); 608 | var rhs = this.editor[this.id + '-rhs'].getValue(); 609 | var d = new Mgly.diff(lhs, rhs, this.settings); 610 | return d.normal_form(); 611 | }, 612 | bind: function(el) { 613 | jQuery(this.element).hide();//hide 614 | this.id = jQuery(el).attr('id'); 615 | var height = this.settings.editor_height; 616 | var width = this.settings.editor_width; 617 | this.changed_timeout = null; 618 | this.chfns = {}; 619 | this.chfns[this.id + '-lhs'] = []; 620 | this.chfns[this.id + '-rhs'] = []; 621 | this.prev_query = []; 622 | this.cursor = []; 623 | this._skipscroll = {}; 624 | this.change_exp = new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/); 625 | var merge_lhs_button; 626 | var merge_rhs_button; 627 | if (jQuery.button != undefined) { 628 | //jquery ui 629 | merge_lhs_button = ''; 630 | merge_rhs_button = ''; 631 | } 632 | else { 633 | // homebrew 634 | var style = 'opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;margin-top: -2px;'; 635 | merge_lhs_button = '
<
'; 636 | merge_rhs_button = '
>
'; 637 | } 638 | this.merge_rhs_button = jQuery(merge_rhs_button); 639 | this.merge_lhs_button = jQuery(merge_lhs_button); 640 | 641 | // create the textarea and canvas elements 642 | jQuery(this.element).append(jQuery('
')); 643 | jQuery(this.element).append(jQuery('
')); 644 | jQuery(this.element).append(jQuery('
')); 645 | var rmargin = jQuery('
'); 646 | if (!this.settings.sidebar) { 647 | jQuery(this.element).find('.mergely-margin').css({display: 'none'}); 648 | } 649 | if (this.settings.rhs_margin == 'left') { 650 | jQuery(this.element).append(rmargin); 651 | } 652 | jQuery(this.element).append(jQuery('
')); 653 | if (this.settings.rhs_margin != 'left') { 654 | jQuery(this.element).append(rmargin); 655 | } 656 | //codemirror 657 | var cmstyle = '#' + this.id + ' .CodeMirror-gutter-text { padding: 5px 0 0 0; }' + 658 | '#' + this.id + ' .CodeMirror-lines pre, ' + '#' + this.id + ' .CodeMirror-gutter-text pre { line-height: 18px; }' + 659 | '.CodeMirror-linewidget { overflow: hidden; };'; 660 | if (this.settings.autoresize) { 661 | cmstyle += this.id + ' .CodeMirror-scroll { height: 100%; overflow: auto; }'; 662 | } 663 | // adjust the margin line height 664 | cmstyle += '\n.CodeMirror { line-height: 18px; }'; 665 | jQuery('').appendTo('head'); 666 | 667 | //bind 668 | var rhstx = jQuery('#' + this.id + '-rhs').get(0); 669 | if (!rhstx) { 670 | console.error('rhs textarea not defined - Mergely not initialized properly'); 671 | return; 672 | } 673 | var lhstx = jQuery('#' + this.id + '-lhs').get(0); 674 | if (!rhstx) { 675 | console.error('lhs textarea not defined - Mergely not initialized properly'); 676 | return; 677 | } 678 | var self = this; 679 | this.editor = []; 680 | this.editor[this.id + '-lhs'] = CodeMirror.fromTextArea(lhstx, this.lhs_cmsettings); 681 | this.editor[this.id + '-rhs'] = CodeMirror.fromTextArea(rhstx, this.rhs_cmsettings); 682 | this.editor[this.id + '-lhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); }); 683 | this.editor[this.id + '-lhs'].on('scroll', function(){ self._scrolling(self.id + '-lhs'); }); 684 | this.editor[this.id + '-rhs'].on('change', function(){ if (self.settings.autoupdate) self._changing(self.id + '-lhs', self.id + '-rhs'); }); 685 | this.editor[this.id + '-rhs'].on('scroll', function(){ self._scrolling(self.id + '-rhs'); }); 686 | // resize 687 | if (this.settings.autoresize) { 688 | var sz_timeout1 = null; 689 | var sz = function(init) { 690 | //self.em_height = null; //recalculate 691 | if (self.settings.resize) self.settings.resize(init); 692 | self.editor[self.id + '-lhs'].refresh(); 693 | self.editor[self.id + '-rhs'].refresh(); 694 | if (self.settings.autoupdate) { 695 | self._changing(self.id + '-lhs', self.id + '-rhs'); 696 | } 697 | } 698 | jQuery(window).resize( 699 | function () { 700 | if (sz_timeout1) clearTimeout(sz_timeout1); 701 | sz_timeout1 = setTimeout(sz, self.settings.resize_timeout); 702 | } 703 | ); 704 | sz(true); 705 | } 706 | //bind 707 | 708 | if (this.settings.lhs) { 709 | var setv = this.editor[this.id + '-lhs'].getDoc().setValue; 710 | this.settings.lhs(setv.bind(this.editor[this.id + '-lhs'].getDoc())); 711 | } 712 | if (this.settings.rhs) { 713 | var setv = this.editor[this.id + '-rhs'].getDoc().setValue; 714 | this.settings.rhs(setv.bind(this.editor[this.id + '-rhs'].getDoc())); 715 | } 716 | }, 717 | 718 | _scroll_to_change : function(change) { 719 | if (!change) return; 720 | var self = this; 721 | var led = self.editor[self.id+'-lhs']; 722 | var red = self.editor[self.id+'-rhs']; 723 | 724 | var yref = led.getScrollerElement().offsetHeight * 1/2; // center between >0 and 1/2 725 | 726 | // set cursors 727 | led.setCursor(Math.max(change["lhs-line-from"],0), 0); // use led.getCursor().ch ? 728 | red.setCursor(Math.max(change["rhs-line-from"],0), 0); 729 | 730 | // using directly CodeMirror breaks canvas alignment 731 | // var ly = led.charCoords({line: Math.max(change["lhs-line-from"],0), ch: 0}, "local").top; 732 | 733 | // calculate scroll offset for current change. Warning: returns relative y position so we scroll to 0 first. 734 | led.scrollTo(null, 0); 735 | red.scrollTo(null, 0); 736 | self._calculate_offsets(self.id+'-lhs', self.id+'-rhs', [change]); 737 | led.scrollTo(null, Math.max(change["lhs-y-start"]-yref, 0)); 738 | red.scrollTo(null, Math.max(change["rhs-y-start"]-yref, 0)); 739 | // right pane should simply follows 740 | }, 741 | 742 | _scrolling: function(editor_name) { 743 | if (this._skipscroll[editor_name] === true) { 744 | // scrolling one side causes the other to event - ignore it 745 | this._skipscroll[editor_name] = false; 746 | return; 747 | } 748 | var scroller = jQuery(this.editor[editor_name].getScrollerElement()); 749 | if (this.midway == undefined) { 750 | this.midway = (scroller.height() / 2.0 + scroller.offset().top).toFixed(2); 751 | } 752 | // balance-line 753 | var midline = this.editor[editor_name].coordsChar({left:0, top:this.midway}); 754 | var top_to = scroller.scrollTop(); 755 | var left_to = scroller.scrollLeft(); 756 | 757 | this.trace('scroll', 'side', editor_name); 758 | this.trace('scroll', 'midway', this.midway); 759 | this.trace('scroll', 'midline', midline); 760 | this.trace('scroll', 'top_to', top_to); 761 | this.trace('scroll', 'left_to', left_to); 762 | 763 | var editor_name1 = this.id + '-lhs'; 764 | var editor_name2 = this.id + '-rhs'; 765 | 766 | for (var name in this.editor) { 767 | if (!this.editor.hasOwnProperty(name)) continue; 768 | if (editor_name == name) continue; //same editor 769 | var this_side = editor_name.replace(this.id + '-', ''); 770 | var other_side = name.replace(this.id + '-', ''); 771 | var top_adjust = 0; 772 | 773 | // find the last change that is less than or within the midway point 774 | // do not move the rhs until the lhs end point is >= the rhs end point. 775 | var last_change = null; 776 | var force_scroll = false; 777 | for (var i = 0; i < this.changes.length; ++i) { 778 | var change = this.changes[i]; 779 | if ((midline.line >= change[this_side+'-line-from'])) { 780 | last_change = change; 781 | if (midline.line >= last_change[this_side+'-line-to']) { 782 | if (!change.hasOwnProperty(this_side+'-y-start') || 783 | !change.hasOwnProperty(this_side+'-y-end') || 784 | !change.hasOwnProperty(other_side+'-y-start') || 785 | !change.hasOwnProperty(other_side+'-y-end')){ 786 | // change outside of viewport 787 | force_scroll = true; 788 | } 789 | else { 790 | top_adjust += 791 | (change[this_side+'-y-end'] - change[this_side+'-y-start']) - 792 | (change[other_side+'-y-end'] - change[other_side+'-y-start']); 793 | } 794 | } 795 | } 796 | } 797 | 798 | var vp = this.editor[name].getViewport(); 799 | var scroll = true; 800 | if (last_change) { 801 | this.trace('scroll', 'last change before midline', last_change); 802 | if (midline.line >= vp.from && midline <= vp.to) { 803 | scroll = false; 804 | } 805 | } 806 | this.trace('scroll', 'scroll', scroll); 807 | if (scroll || force_scroll) { 808 | // scroll the other side 809 | this.trace('scroll', 'scrolling other side', top_to - top_adjust); 810 | var scroller = jQuery(this.editor[name].getScrollerElement()); 811 | this._skipscroll[name] = true;//disable next event 812 | scroller.scrollTop(top_to - top_adjust).scrollLeft(left_to); 813 | } 814 | else this.trace('scroll', 'not scrolling other side'); 815 | 816 | if (this.settings.autoupdate) { 817 | var timer = new Mgly.Timer(); 818 | this._calculate_offsets(editor_name1, editor_name2, this.changes); 819 | this.trace('change', 'offsets time', timer.stop()); 820 | this._markup_changes(editor_name1, editor_name2, this.changes); 821 | this.trace('change', 'markup time', timer.stop()); 822 | this._draw_diff(editor_name1, editor_name2, this.changes); 823 | this.trace('change', 'draw time', timer.stop()); 824 | } 825 | this.trace('scroll', 'scrolled'); 826 | } 827 | }, 828 | _changing: function(editor_name1, editor_name2) { 829 | this.trace('change', 'changing-timeout', this.changed_timeout); 830 | var self = this; 831 | if (this.changed_timeout != null) clearTimeout(this.changed_timeout); 832 | this.changed_timeout = setTimeout(function(){ 833 | var timer = new Mgly.Timer(); 834 | self._changed(editor_name1, editor_name2); 835 | self.trace('change', 'total time', timer.stop()); 836 | }, this.settings.change_timeout); 837 | }, 838 | _changed: function(editor_name1, editor_name2) { 839 | this._clear(); 840 | this._diff(editor_name1, editor_name2); 841 | }, 842 | _clear: function() { 843 | var self = this; 844 | for (var name in this.editor) { 845 | if (!this.editor.hasOwnProperty(name)) continue; 846 | var editor = this.editor[name]; 847 | var fns = self.chfns[name]; 848 | // clear editor changes 849 | editor.operation(function() { 850 | var timer = new Mgly.Timer(); 851 | for (var i = 0, l = editor.lineCount(); i < l; ++i) { 852 | editor.removeLineClass(i, 'background'); 853 | } 854 | for (var i = 0; i < fns.length; ++i) { 855 | //var edid = editor.getDoc().id; 856 | var change = fns[i]; 857 | //if (change.doc.id != edid) continue; 858 | if (change.lines.length) { 859 | self.trace('change', 'clear text', change.lines[0].text); 860 | } 861 | change.clear(); 862 | } 863 | editor.clearGutter('merge'); 864 | self.trace('change', 'clear time', timer.stop()); 865 | }); 866 | } 867 | self.chfns[name] = []; 868 | 869 | var ex = this._draw_info(this.id + '-lhs', this.id + '-rhs'); 870 | var ctx_lhs = ex.clhs.get(0).getContext('2d'); 871 | var ctx_rhs = ex.crhs.get(0).getContext('2d'); 872 | var ctx = ex.dcanvas.getContext('2d'); 873 | 874 | ctx_lhs.beginPath(); 875 | ctx_lhs.fillStyle = this.settings.bgcolor; 876 | ctx_lhs.strokeStyle = '#888'; 877 | ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height); 878 | ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height); 879 | 880 | ctx_rhs.beginPath(); 881 | ctx_rhs.fillStyle = this.settings.bgcolor; 882 | ctx_rhs.strokeStyle = '#888'; 883 | ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height); 884 | ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height); 885 | 886 | ctx.beginPath(); 887 | ctx.fillStyle = '#fff'; 888 | ctx.fillRect(0, 0, this.draw_mid_width, ex.visible_page_height); 889 | }, 890 | _diff: function(editor_name1, editor_name2) { 891 | var lhs = this.editor[editor_name1].getValue(); 892 | var rhs = this.editor[editor_name2].getValue(); 893 | var timer = new Mgly.Timer(); 894 | var d = new Mgly.diff(lhs, rhs, this.settings); 895 | this.trace('change', 'diff time', timer.stop()); 896 | this.changes = Mgly.DiffParser(d.normal_form()); 897 | this.trace('change', 'parse time', timer.stop()); 898 | if (this._current_diff === undefined) { 899 | // go to first difference on start-up 900 | this._current_diff = 0; 901 | this._scroll_to_change(this.changes[0]); 902 | } 903 | this.trace('change', 'scroll_to_change time', timer.stop()); 904 | this._calculate_offsets(editor_name1, editor_name2, this.changes); 905 | this.trace('change', 'offsets time', timer.stop()); 906 | this._markup_changes(editor_name1, editor_name2, this.changes); 907 | this.trace('change', 'markup time', timer.stop()); 908 | this._draw_diff(editor_name1, editor_name2, this.changes); 909 | this.trace('change', 'draw time', timer.stop()); 910 | }, 911 | _parse_diff: function (editor_name1, editor_name2, diff) { 912 | this.trace('diff', 'diff results:\n', diff); 913 | var changes = []; 914 | var change_id = 0; 915 | // parse diff 916 | var diff_lines = diff.split(/\n/); 917 | for (var i = 0; i < diff_lines.length; ++i) { 918 | if (diff_lines[i].length == 0) continue; 919 | var change = {}; 920 | var test = this.change_exp.exec(diff_lines[i]); 921 | if (test == null) continue; 922 | // lines are zero-based 923 | var fr = test[1].split(','); 924 | change['lhs-line-from'] = fr[0] - 1; 925 | if (fr.length == 1) change['lhs-line-to'] = fr[0] - 1; 926 | else change['lhs-line-to'] = fr[1] - 1; 927 | var to = test[3].split(','); 928 | change['rhs-line-from'] = to[0] - 1; 929 | if (to.length == 1) change['rhs-line-to'] = to[0] - 1; 930 | else change['rhs-line-to'] = to[1] - 1; 931 | // TODO: optimize for changes that are adds/removes 932 | if (change['lhs-line-from'] < 0) change['lhs-line-from'] = 0; 933 | if (change['lhs-line-to'] < 0) change['lhs-line-to'] = 0; 934 | if (change['rhs-line-from'] < 0) change['rhs-line-from'] = 0; 935 | if (change['rhs-line-to'] < 0) change['rhs-line-to'] = 0; 936 | change['op'] = test[2]; 937 | changes[change_id++] = change; 938 | this.trace('diff', 'change', change); 939 | } 940 | return changes; 941 | }, 942 | _get_viewport: function(editor_name1, editor_name2) { 943 | var lhsvp = this.editor[editor_name1].getViewport(); 944 | var rhsvp = this.editor[editor_name2].getViewport(); 945 | return {from: Math.min(lhsvp.from, rhsvp.from), to: Math.max(lhsvp.to, rhsvp.to)}; 946 | }, 947 | _is_change_in_view: function(vp, change) { 948 | if (!this.settings.viewport) return true; 949 | if ((change['lhs-line-from'] < vp.from && change['lhs-line-to'] < vp.to) || 950 | (change['lhs-line-from'] > vp.from && change['lhs-line-to'] > vp.to) || 951 | (change['rhs-line-from'] < vp.from && change['rhs-line-to'] < vp.to) || 952 | (change['rhs-line-from'] > vp.from && change['rhs-line-to'] > vp.to)) { 953 | // if the change is outside the viewport, skip 954 | return false; 955 | } 956 | return true; 957 | }, 958 | _set_top_offset: function (editor_name1) { 959 | // save the current scroll position of the editor 960 | var saveY = this.editor[editor_name1].getScrollInfo().top; 961 | // temporarily scroll to top 962 | this.editor[editor_name1].scrollTo(null, 0); 963 | 964 | // this is the distance from the top of the screen to the top of the 965 | // content of the first codemirror editor 966 | var topnode = jQuery('#' + this.id + ' .CodeMirror-measure').first(); 967 | var top_offset = topnode.offset().top - 4; 968 | if(!top_offset) return false; 969 | 970 | // restore editor's scroll position 971 | this.editor[editor_name1].scrollTo(null, saveY); 972 | 973 | this.draw_top_offset = 0.5 - top_offset; 974 | return true; 975 | }, 976 | _calculate_offsets: function (editor_name1, editor_name2, changes) { 977 | if (this.em_height == null) { 978 | if(!this._set_top_offset(editor_name1)) return; //try again 979 | this.em_height = this.editor[editor_name1].defaultTextHeight(); 980 | if (!this.em_height) { 981 | console.warn('Failed to calculate offsets, using 18 by default'); 982 | this.em_height = 18; 983 | } 984 | this.draw_lhs_min = 0.5; 985 | var c = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas'); 986 | if (!c.length) { 987 | console.error('failed to find canvas', '#' + editor_name1 + '-' + editor_name2 + '-canvas'); 988 | } 989 | if (!c.width()) { 990 | console.error('canvas width is 0'); 991 | return; 992 | } 993 | this.draw_mid_width = jQuery('#' + editor_name1 + '-' + editor_name2 + '-canvas').width(); 994 | this.draw_rhs_max = this.draw_mid_width - 0.5; //24.5; 995 | this.draw_lhs_width = 5; 996 | this.draw_rhs_width = 5; 997 | this.trace('calc', 'change offsets calculated', {top_offset: this.draw_top_offset, lhs_min: this.draw_lhs_min, rhs_max: this.draw_rhs_max, lhs_width: this.draw_lhs_width, rhs_width: this.draw_rhs_width}); 998 | } 999 | var lhschc = this.editor[editor_name1].charCoords({line: 0}); 1000 | var rhschc = this.editor[editor_name2].charCoords({line: 0}); 1001 | var vp = this._get_viewport(editor_name1, editor_name2); 1002 | 1003 | for (var i = 0; i < changes.length; ++i) { 1004 | var change = changes[i]; 1005 | 1006 | if (!this.settings.sidebar && !this._is_change_in_view(vp, change)) { 1007 | // if the change is outside the viewport, skip 1008 | delete change['lhs-y-start']; 1009 | delete change['lhs-y-end']; 1010 | delete change['rhs-y-start']; 1011 | delete change['rhs-y-end']; 1012 | continue; 1013 | } 1014 | var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0; 1015 | var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; 1016 | var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; 1017 | var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; 1018 | 1019 | var ls, le, rs, re; 1020 | if (this.editor[editor_name1].getOption('lineWrapping') || this.editor[editor_name1].getOption('lineWrapping')) { 1021 | // If using line-wrapping, we must get the height of the line 1022 | var tls = this.editor[editor_name1].cursorCoords({line: llf, ch: 0}, 'page'); 1023 | var lhssh = this.editor[editor_name1].getLineHandle(llf); 1024 | ls = { top: tls.top, bottom: tls.top + lhssh.height }; 1025 | 1026 | var tle = this.editor[editor_name1].cursorCoords({line: llt, ch: 0}, 'page'); 1027 | var lhseh = this.editor[editor_name1].getLineHandle(llt); 1028 | le = { top: tle.top, bottom: tle.top + lhseh.height }; 1029 | 1030 | var tls = this.editor[editor_name2].cursorCoords({line: rlf, ch: 0}, 'page'); 1031 | var rhssh = this.editor[editor_name2].getLineHandle(rlf); 1032 | rs = { top: tls.top, bottom: tls.top + rhssh.height }; 1033 | 1034 | var tle = this.editor[editor_name2].cursorCoords({line: rlt, ch: 0}, 'page'); 1035 | var rhseh = this.editor[editor_name2].getLineHandle(rlt); 1036 | re = { top: tle.top, bottom: tle.top + rhseh.height }; 1037 | } 1038 | else { 1039 | // If not using line-wrapping, we can calculate the line position 1040 | ls = { 1041 | top: lhschc.top + llf * this.em_height, 1042 | bottom: lhschc.bottom + llf * this.em_height + 2 1043 | }; 1044 | le = { 1045 | top: lhschc.top + llt * this.em_height, 1046 | bottom: lhschc.bottom + llt * this.em_height + 2 1047 | }; 1048 | rs = { 1049 | top: rhschc.top + rlf * this.em_height, 1050 | bottom: rhschc.bottom + rlf * this.em_height + 2 1051 | }; 1052 | re = { 1053 | top: rhschc.top + rlt * this.em_height, 1054 | bottom: rhschc.bottom + rlt * this.em_height + 2 1055 | }; 1056 | } 1057 | 1058 | if (change['op'] == 'a') { 1059 | // adds (right), normally start from the end of the lhs, 1060 | // except for the case when the start of the rhs is 0 1061 | if (rlf > 0) { 1062 | ls.top = ls.bottom; 1063 | ls.bottom += this.em_height; 1064 | le = ls; 1065 | } 1066 | } 1067 | else if (change['op'] == 'd') { 1068 | // deletes (left) normally finish from the end of the rhs, 1069 | // except for the case when the start of the lhs is 0 1070 | if (llf > 0) { 1071 | rs.top = rs.bottom; 1072 | rs.bottom += this.em_height; 1073 | re = rs; 1074 | } 1075 | } 1076 | change['lhs-y-start'] = this.draw_top_offset + ls.top; 1077 | if (change['op'] == 'c' || change['op'] == 'd') { 1078 | change['lhs-y-end'] = this.draw_top_offset + le.bottom; 1079 | } 1080 | else { 1081 | change['lhs-y-end'] = this.draw_top_offset + le.top; 1082 | } 1083 | change['rhs-y-start'] = this.draw_top_offset + rs.top; 1084 | if (change['op'] == 'c' || change['op'] == 'a') { 1085 | change['rhs-y-end'] = this.draw_top_offset + re.bottom; 1086 | } 1087 | else { 1088 | change['rhs-y-end'] = this.draw_top_offset + re.top; 1089 | } 1090 | this.trace('calc', 'change calculated', i, change); 1091 | } 1092 | return changes; 1093 | }, 1094 | _markup_changes: function (editor_name1, editor_name2, changes) { 1095 | jQuery('.merge-button').remove(); // clear 1096 | 1097 | var self = this; 1098 | var led = this.editor[editor_name1]; 1099 | var red = this.editor[editor_name2]; 1100 | 1101 | var timer = new Mgly.Timer(); 1102 | led.operation(function() { 1103 | for (var i = 0; i < changes.length; ++i) { 1104 | var change = changes[i]; 1105 | var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0; 1106 | var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; 1107 | var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; 1108 | var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; 1109 | 1110 | var clazz = ['mergely', 'lhs', change['op'], 'cid-' + i]; 1111 | led.addLineClass(llf, 'background', 'start'); 1112 | led.addLineClass(llt, 'background', 'end'); 1113 | 1114 | if (llf == 0 && llt == 0 && rlf == 0) { 1115 | led.addLineClass(llf, 'background', clazz.join(' ')); 1116 | led.addLineClass(llf, 'background', 'first'); 1117 | } 1118 | else { 1119 | // apply change for each line in-between the changed lines 1120 | for (var j = llf; j <= llt; ++j) { 1121 | led.addLineClass(j, 'background', clazz.join(' ')); 1122 | led.addLineClass(j, 'background', clazz.join(' ')); 1123 | } 1124 | } 1125 | 1126 | if (!red.getOption('readOnly')) { 1127 | // add widgets to lhs, if rhs is not read only 1128 | var rhs_button = self.merge_rhs_button.clone(); 1129 | if (rhs_button.button) { 1130 | //jquery-ui support 1131 | rhs_button.button({icons: {primary: 'ui-icon-triangle-1-e'}, text: false}); 1132 | } 1133 | rhs_button.addClass('merge-button'); 1134 | rhs_button.attr('id', 'merge-rhs-' + i); 1135 | led.setGutterMarker(llf, 'merge', rhs_button.get(0)); 1136 | } 1137 | } 1138 | }); 1139 | 1140 | var vp = this._get_viewport(editor_name1, editor_name2); 1141 | 1142 | this.trace('change', 'markup lhs-editor time', timer.stop()); 1143 | red.operation(function() { 1144 | for (var i = 0; i < changes.length; ++i) { 1145 | var change = changes[i]; 1146 | var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0; 1147 | var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; 1148 | var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; 1149 | var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; 1150 | 1151 | if (!self._is_change_in_view(vp, change)) { 1152 | // if the change is outside the viewport, skip 1153 | continue; 1154 | } 1155 | 1156 | var clazz = ['mergely', 'rhs', change['op'], 'cid-' + i]; 1157 | red.addLineClass(rlf, 'background', 'start'); 1158 | red.addLineClass(rlt, 'background', 'end'); 1159 | 1160 | if (rlf == 0 && rlt == 0 && llf == 0) { 1161 | red.addLineClass(rlf, 'background', clazz.join(' ')); 1162 | red.addLineClass(rlf, 'background', 'first'); 1163 | } 1164 | else { 1165 | // apply change for each line in-between the changed lines 1166 | for (var j = rlf; j <= rlt; ++j) { 1167 | red.addLineClass(j, 'background', clazz.join(' ')); 1168 | red.addLineClass(j, 'background', clazz.join(' ')); 1169 | } 1170 | } 1171 | 1172 | if (!led.getOption('readOnly')) { 1173 | // add widgets to rhs, if lhs is not read only 1174 | var lhs_button = self.merge_lhs_button.clone(); 1175 | if (lhs_button.button) { 1176 | //jquery-ui support 1177 | lhs_button.button({icons: {primary: 'ui-icon-triangle-1-w'}, text: false}); 1178 | } 1179 | lhs_button.addClass('merge-button'); 1180 | lhs_button.attr('id', 'merge-lhs-' + i); 1181 | red.setGutterMarker(rlf, 'merge', lhs_button.get(0)); 1182 | } 1183 | } 1184 | }); 1185 | this.trace('change', 'markup rhs-editor time', timer.stop()); 1186 | 1187 | // mark text deleted, LCS changes 1188 | var marktext = []; 1189 | for (var i = 0; this.settings.lcs && i < changes.length; ++i) { 1190 | var change = changes[i]; 1191 | var llf = change['lhs-line-from'] >= 0 ? change['lhs-line-from'] : 0; 1192 | var llt = change['lhs-line-to'] >= 0 ? change['lhs-line-to'] : 0; 1193 | var rlf = change['rhs-line-from'] >= 0 ? change['rhs-line-from'] : 0; 1194 | var rlt = change['rhs-line-to'] >= 0 ? change['rhs-line-to'] : 0; 1195 | 1196 | if (!this._is_change_in_view(vp, change)) { 1197 | // if the change is outside the viewport, skip 1198 | continue; 1199 | } 1200 | if (change['op'] == 'd') { 1201 | // apply delete to cross-out (left-hand side only) 1202 | var from = llf; 1203 | var to = llt; 1204 | var to_ln = led.lineInfo(to); 1205 | if (to_ln) { 1206 | marktext.push([led, {line:from, ch:0}, {line:to, ch:to_ln.text.length}, {className: 'mergely ch d lhs'}]); 1207 | } 1208 | } 1209 | else if (change['op'] == 'c') { 1210 | // apply LCS changes to each line 1211 | for (var j = llf, k = rlf, p = 0; 1212 | ((j >= 0) && (j <= llt)) || ((k >= 0) && (k <= rlt)); 1213 | ++j, ++k) { 1214 | if (k + p > rlt) { 1215 | // lhs continues past rhs, mark lhs as deleted 1216 | var lhs_line = led.getLine( j ); 1217 | marktext.push([led, {line:j, ch:0}, {line:j, ch:lhs_line.length}, {className: 'mergely ch d lhs'}]); 1218 | continue; 1219 | } 1220 | if (j + p > llt) { 1221 | // rhs continues past lhs, mark rhs as added 1222 | var rhs_line = red.getLine( k ); 1223 | marktext.push([red, {line:k, ch:0}, {line:k, ch:rhs_line.length}, {className: 'mergely ch a rhs'}]); 1224 | continue; 1225 | } 1226 | var lhs_line = led.getLine( j ); 1227 | var rhs_line = red.getLine( k ); 1228 | var lhs_start = { line: -1, ch: -1 }; 1229 | var lhs_stop = { line: -1, ch: -1 }; 1230 | var rhs_start = { line: -1, ch: -1 }; 1231 | var rhs_stop = { line: -1, ch: -1 }; 1232 | 1233 | var lcs = new Mgly.LCS(lhs_line, rhs_line); 1234 | lcs.diff( 1235 | function (from, to) {//added 1236 | marktext.push([red, {line:k, ch:from}, {line:k, ch:to}, {className: 'mergely ch a rhs'}]); 1237 | }, 1238 | removed = function (from, to) {//removed 1239 | marktext.push([led, {line:j, ch:from}, {line:j, ch:to}, {className: 'mergely ch d lhs'}]); 1240 | } 1241 | ); 1242 | } 1243 | } 1244 | } 1245 | this.trace('change', 'LCS marktext time', timer.stop()); 1246 | 1247 | // mark changes outside closure 1248 | led.operation(function() { 1249 | // apply lhs markup 1250 | for (var i = 0; i < marktext.length; ++i) { 1251 | var m = marktext[i]; 1252 | if (m[0].doc.id != led.getDoc().id) continue; 1253 | self.chfns[self.id + '-lhs'].push(m[0].markText(m[1], m[2], m[3])); 1254 | } 1255 | }); 1256 | red.operation(function() { 1257 | // apply lhs markup 1258 | for (var i = 0; i < marktext.length; ++i) { 1259 | var m = marktext[i]; 1260 | if (m[0].doc.id != red.getDoc().id) continue; 1261 | self.chfns[self.id + '-rhs'].push(m[0].markText(m[1], m[2], m[3])); 1262 | } 1263 | }); 1264 | this.trace('change', 'LCS markup time', timer.stop()); 1265 | 1266 | // merge buttons 1267 | var ed = {lhs:led, rhs:red}; 1268 | jQuery('.merge-button').on('click', function(ev){ 1269 | // side of mouseenter 1270 | var side = 'rhs'; 1271 | var oside = 'lhs'; 1272 | var parent = jQuery(this).parents('#' + self.id + '-editor-lhs'); 1273 | if (parent.length) { 1274 | side = 'lhs'; 1275 | oside = 'rhs'; 1276 | } 1277 | var pos = ed[side].coordsChar({left:ev.pageX, top:ev.pageY}); 1278 | 1279 | // get the change id 1280 | var cid = null; 1281 | var info = ed[side].lineInfo(pos.line); 1282 | jQuery.each(info.bgClass.split(' '), function(i, clazz) { 1283 | if (clazz.indexOf('cid-') == 0) { 1284 | cid = parseInt(clazz.split('-')[1], 10); 1285 | return false; 1286 | } 1287 | }); 1288 | var change = self.changes[cid]; 1289 | self._merge_change(change, side, oside); 1290 | return false; 1291 | }); 1292 | this.trace('change', 'markup buttons time', timer.stop()); 1293 | }, 1294 | _merge_change : function(change, side, oside) { 1295 | if (!change) return; 1296 | var led = this.editor[this.id+'-lhs']; 1297 | var red = this.editor[this.id+'-rhs']; 1298 | var ed = {lhs:led, rhs:red}; 1299 | 1300 | 1301 | var text = ed[side].getRange( 1302 | CodeMirror.Pos(change[side + '-line-from'], 0), 1303 | CodeMirror.Pos(change[side + '-line-to'] + 1, 0)); 1304 | 1305 | if (change['op'] == 'c') { 1306 | ed[oside].replaceRange(text, 1307 | CodeMirror.Pos(change[oside + '-line-from'], 0), 1308 | CodeMirror.Pos(change[oside + '-line-to'] + 1, 0)); 1309 | } 1310 | else if (side == 'rhs') { 1311 | if (change['op'] == 'a') { 1312 | ed[oside].replaceRange(text, 1313 | CodeMirror.Pos(change[oside + '-line-from'] + 1, 0), 1314 | CodeMirror.Pos(change[oside + '-line-to'] + 1, 0)); 1315 | } 1316 | else {// 'd' 1317 | var from = parseInt(change[oside + '-line-from']); 1318 | var to = parseInt(change[oside + '-line-to']); 1319 | for (var i = to; i >= from; --i) { 1320 | ed[oside].removeLine(i); 1321 | } 1322 | } 1323 | } 1324 | else if (side == 'lhs') { 1325 | if (change['op'] == 'a') { 1326 | var from = parseInt(change[oside + '-line-from']); 1327 | var to = parseInt(change[oside + '-line-to']); 1328 | for (var i = to; i >= from; --i) { 1329 | ed[oside].removeLine(i); 1330 | } 1331 | } 1332 | else {// 'd' 1333 | ed[oside].replaceRange( text, 1334 | CodeMirror.Pos(change[oside + '-line-from'] + 1, 0)); 1335 | } 1336 | } 1337 | //reset 1338 | ed['lhs'].setValue(ed['lhs'].getValue()); 1339 | ed['rhs'].setValue(ed['rhs'].getValue()); 1340 | 1341 | this._scroll_to_change(change) 1342 | }, 1343 | _draw_info: function(editor_name1, editor_name2) { 1344 | var visible_page_height = jQuery(this.editor[editor_name1].getScrollerElement()).height(); 1345 | var gutter_height = jQuery(this.editor[editor_name1].getScrollerElement()).children(':first-child').height(); 1346 | var dcanvas = document.getElementById(editor_name1 + '-' + editor_name2 + '-canvas'); 1347 | if (dcanvas == undefined) throw 'Failed to find: ' + editor_name1 + '-' + editor_name2 + '-canvas'; 1348 | var clhs = jQuery('#' + this.id + '-lhs-margin'); 1349 | var crhs = jQuery('#' + this.id + '-rhs-margin'); 1350 | return { 1351 | visible_page_height: visible_page_height, 1352 | gutter_height: gutter_height, 1353 | visible_page_ratio: (visible_page_height / gutter_height), 1354 | margin_ratio: (visible_page_height / gutter_height), 1355 | lhs_scroller: jQuery(this.editor[editor_name1].getScrollerElement()), 1356 | rhs_scroller: jQuery(this.editor[editor_name2].getScrollerElement()), 1357 | lhs_lines: this.editor[editor_name1].lineCount(), 1358 | rhs_lines: this.editor[editor_name2].lineCount(), 1359 | dcanvas: dcanvas, 1360 | clhs: clhs, 1361 | crhs: crhs, 1362 | lhs_xyoffset: jQuery(clhs).offset(), 1363 | rhs_xyoffset: jQuery(crhs).offset() 1364 | }; 1365 | }, 1366 | _draw_diff: function(editor_name1, editor_name2, changes) { 1367 | var ex = this._draw_info(editor_name1, editor_name2); 1368 | var mcanvas_lhs = ex.clhs.get(0); 1369 | var mcanvas_rhs = ex.crhs.get(0); 1370 | var ctx = ex.dcanvas.getContext('2d'); 1371 | var ctx_lhs = mcanvas_lhs.getContext('2d'); 1372 | var ctx_rhs = mcanvas_rhs.getContext('2d'); 1373 | 1374 | this.trace('draw', 'visible_page_height', ex.visible_page_height); 1375 | this.trace('draw', 'gutter_height', ex.gutter_height); 1376 | this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); 1377 | this.trace('draw', 'lhs-scroller-top', ex.lhs_scroller.scrollTop()); 1378 | this.trace('draw', 'rhs-scroller-top', ex.rhs_scroller.scrollTop()); 1379 | 1380 | jQuery.each(jQuery.find('#' + this.id + ' canvas'), function () { 1381 | jQuery(this).get(0).height = ex.visible_page_height; 1382 | }); 1383 | 1384 | ex.clhs.unbind('click'); 1385 | ex.crhs.unbind('click'); 1386 | 1387 | ctx_lhs.beginPath(); 1388 | ctx_lhs.fillStyle = this.settings.bgcolor; 1389 | ctx_lhs.strokeStyle = '#888'; 1390 | ctx_lhs.fillRect(0, 0, 6.5, ex.visible_page_height); 1391 | ctx_lhs.strokeRect(0, 0, 6.5, ex.visible_page_height); 1392 | 1393 | ctx_rhs.beginPath(); 1394 | ctx_rhs.fillStyle = this.settings.bgcolor; 1395 | ctx_rhs.strokeStyle = '#888'; 1396 | ctx_rhs.fillRect(0, 0, 6.5, ex.visible_page_height); 1397 | ctx_rhs.strokeRect(0, 0, 6.5, ex.visible_page_height); 1398 | 1399 | var vp = this._get_viewport(editor_name1, editor_name2); 1400 | for (var i = 0; i < changes.length; ++i) { 1401 | var change = changes[i]; 1402 | 1403 | this.trace('draw', change); 1404 | // margin indicators 1405 | var lhs_y_start = ((change['lhs-y-start'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio); 1406 | var lhs_y_end = ((change['lhs-y-end'] + ex.lhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1; 1407 | var rhs_y_start = ((change['rhs-y-start'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio); 1408 | var rhs_y_end = ((change['rhs-y-end'] + ex.rhs_scroller.scrollTop()) * ex.visible_page_ratio) + 1; 1409 | this.trace('draw', 'marker calculated', lhs_y_start, lhs_y_end, rhs_y_start, rhs_y_end); 1410 | 1411 | ctx_lhs.beginPath(); 1412 | ctx_lhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']]; 1413 | ctx_lhs.strokeStyle = '#000'; 1414 | ctx_lhs.lineWidth = 0.5; 1415 | ctx_lhs.fillRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); 1416 | ctx_lhs.strokeRect(1.5, lhs_y_start, 4.5, Math.max(lhs_y_end - lhs_y_start, 5)); 1417 | 1418 | ctx_rhs.beginPath(); 1419 | ctx_rhs.fillStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']]; 1420 | ctx_rhs.strokeStyle = '#000'; 1421 | ctx_rhs.lineWidth = 0.5; 1422 | ctx_rhs.fillRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); 1423 | ctx_rhs.strokeRect(1.5, rhs_y_start, 4.5, Math.max(rhs_y_end - rhs_y_start, 5)); 1424 | 1425 | if (!this._is_change_in_view(vp, change)) { 1426 | continue; 1427 | } 1428 | 1429 | lhs_y_start = change['lhs-y-start']; 1430 | lhs_y_end = change['lhs-y-end']; 1431 | rhs_y_start = change['rhs-y-start']; 1432 | rhs_y_end = change['rhs-y-end']; 1433 | 1434 | var radius = 3; 1435 | 1436 | // draw left box 1437 | ctx.beginPath(); 1438 | ctx.strokeStyle = this.settings.fgcolor[(this._current_diff==i?'c':'')+change['op']]; 1439 | ctx.lineWidth = (this._current_diff==i) ? 1.5 : 1; 1440 | 1441 | var rectWidth = this.draw_lhs_width; 1442 | var rectHeight = lhs_y_end - lhs_y_start - 1; 1443 | var rectX = this.draw_lhs_min; 1444 | var rectY = lhs_y_start; 1445 | // top and top top-right corner 1446 | 1447 | // draw left box 1448 | ctx.moveTo(rectX, rectY); 1449 | if (navigator.appName == 'Microsoft Internet Explorer') { 1450 | // IE arcs look awful 1451 | ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_start); 1452 | ctx.lineTo(this.draw_lhs_min + this.draw_lhs_width, lhs_y_end + 1); 1453 | ctx.lineTo(this.draw_lhs_min, lhs_y_end + 1); 1454 | } 1455 | else { 1456 | if (rectHeight <= 0) { 1457 | ctx.lineTo(rectX + rectWidth, rectY); 1458 | } 1459 | else { 1460 | ctx.arcTo(rectX + rectWidth, rectY, rectX + rectWidth, rectY + radius, radius); 1461 | ctx.arcTo(rectX + rectWidth, rectY + rectHeight, rectX + rectWidth - radius, rectY + rectHeight, radius); 1462 | } 1463 | // bottom line 1464 | ctx.lineTo(rectX, rectY + rectHeight); 1465 | } 1466 | ctx.stroke(); 1467 | 1468 | rectWidth = this.draw_rhs_width; 1469 | rectHeight = rhs_y_end - rhs_y_start - 1; 1470 | rectX = this.draw_rhs_max; 1471 | rectY = rhs_y_start; 1472 | 1473 | // draw right box 1474 | ctx.moveTo(rectX, rectY); 1475 | if (navigator.appName == 'Microsoft Internet Explorer') { 1476 | ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_start); 1477 | ctx.lineTo(this.draw_rhs_max - this.draw_rhs_width, rhs_y_end + 1); 1478 | ctx.lineTo(this.draw_rhs_max, rhs_y_end + 1); 1479 | } 1480 | else { 1481 | if (rectHeight <= 0) { 1482 | ctx.lineTo(rectX - rectWidth, rectY); 1483 | } 1484 | else { 1485 | ctx.arcTo(rectX - rectWidth, rectY, rectX - rectWidth, rectY + radius, radius); 1486 | ctx.arcTo(rectX - rectWidth, rectY + rectHeight, rectX - radius, rectY + rectHeight, radius); 1487 | } 1488 | ctx.lineTo(rectX, rectY + rectHeight); 1489 | } 1490 | ctx.stroke(); 1491 | 1492 | // connect boxes 1493 | var cx = this.draw_lhs_min + this.draw_lhs_width; 1494 | var cy = lhs_y_start + (lhs_y_end + 1 - lhs_y_start) / 2.0; 1495 | var dx = this.draw_rhs_max - this.draw_rhs_width; 1496 | var dy = rhs_y_start + (rhs_y_end + 1 - rhs_y_start) / 2.0; 1497 | ctx.moveTo(cx, cy); 1498 | if (cy == dy) { 1499 | ctx.lineTo(dx, dy); 1500 | } 1501 | else { 1502 | // fancy! 1503 | ctx.bezierCurveTo( 1504 | cx + 12, cy - 3, // control-1 X,Y 1505 | dx - 12, dy - 3, // control-2 X,Y 1506 | dx, dy); 1507 | } 1508 | ctx.stroke(); 1509 | } 1510 | 1511 | // visible window feedback 1512 | ctx_lhs.fillStyle = this.settings.vpcolor; 1513 | ctx_rhs.fillStyle = this.settings.vpcolor; 1514 | 1515 | var lto = ex.clhs.height() * ex.visible_page_ratio; 1516 | var lfrom = (ex.lhs_scroller.scrollTop() / ex.gutter_height) * ex.clhs.height(); 1517 | var rto = ex.crhs.height() * ex.visible_page_ratio; 1518 | var rfrom = (ex.rhs_scroller.scrollTop() / ex.gutter_height) * ex.crhs.height(); 1519 | this.trace('draw', 'cls.height', ex.clhs.height()); 1520 | this.trace('draw', 'lhs_scroller.scrollTop()', ex.lhs_scroller.scrollTop()); 1521 | this.trace('draw', 'gutter_height', ex.gutter_height); 1522 | this.trace('draw', 'visible_page_ratio', ex.visible_page_ratio); 1523 | this.trace('draw', 'lhs from', lfrom, 'lhs to', lto); 1524 | this.trace('draw', 'rhs from', rfrom, 'rhs to', rto); 1525 | 1526 | ctx_lhs.fillRect(1.5, lfrom, 4.5, lto); 1527 | ctx_rhs.fillRect(1.5, rfrom, 4.5, rto); 1528 | 1529 | ex.clhs.click(function (ev) { 1530 | var y = ev.pageY - ex.lhs_xyoffset.top - (lto / 2); 1531 | var sto = Math.max(0, (y / mcanvas_lhs.height) * ex.lhs_scroller.get(0).scrollHeight); 1532 | ex.lhs_scroller.scrollTop(sto); 1533 | }); 1534 | ex.crhs.click(function (ev) { 1535 | var y = ev.pageY - ex.rhs_xyoffset.top - (rto / 2); 1536 | var sto = Math.max(0, (y / mcanvas_rhs.height) * ex.rhs_scroller.get(0).scrollHeight); 1537 | ex.rhs_scroller.scrollTop(sto); 1538 | }); 1539 | }, 1540 | trace: function(name) { 1541 | if(this.settings._debug.indexOf(name) >= 0) { 1542 | arguments[0] = name+':'; 1543 | console.log([].slice.apply(arguments)); 1544 | } 1545 | } 1546 | }); 1547 | 1548 | jQuery.pluginMaker = function(plugin) { 1549 | // add the plugin function as a jQuery plugin 1550 | jQuery.fn[plugin.prototype.name] = function(options) { 1551 | // get the arguments 1552 | var args = jQuery.makeArray(arguments), 1553 | after = args.slice(1); 1554 | var rc = undefined; 1555 | this.each(function() { 1556 | // see if we have an instance 1557 | var instance = jQuery.data(this, plugin.prototype.name); 1558 | if (instance) { 1559 | // call a method on the instance 1560 | if (typeof options == "string") { 1561 | rc = instance[options].apply(instance, after); 1562 | } else if (instance.update) { 1563 | // call update on the instance 1564 | return instance.update.apply(instance, args); 1565 | } 1566 | } else { 1567 | // create the plugin 1568 | new plugin(this, options); 1569 | } 1570 | }); 1571 | if (rc != undefined) return rc; 1572 | }; 1573 | }; 1574 | 1575 | // make the mergely widget 1576 | jQuery.pluginMaker(Mgly.mergely); 1577 | -------------------------------------------------------------------------------- /lib/mergely.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2014 by Jamie Peabody, http://www.mergely.com 3 | * All rights reserved. 4 | * Version: 3.3.7 2014-08-17 5 | */ 6 | Mgly={},Mgly.Timer=function(){var a=this;a.start=function(){a.t0=(new Date).getTime()},a.stop=function(){var b=(new Date).getTime(),c=b-a.t0;return a.t0=b,c},a.start()},Mgly.ChangeExpression=new RegExp(/(^(?![><-])*\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/),Mgly.DiffParser=function(a){for(var b=[],c=0,d=a.split(/\n/),e=0;e0&&"c"==h.op&&(e+=1),b(e,e+i.length)}if("d"!=h.op){e=c.getLines("rhs").slice(0,h["rhs-line-from"]).join(" ").length,f=h["rhs-line-to"]+1;var j=c.getLines("rhs").slice(h["rhs-line-from"],f).join(" ");"a"==h.op?j+=" ":e>0&&"c"==h.op&&(e+=1),a(e,e+j.length)}}}}),Mgly.CodeifyText=function(a){this._max_code=0,this._diff_codes={},this.ctxs={},this.options={ignorews:!1},jQuery.extend(this,a),this.lhs=a.lhs.split("\n"),this.rhs=a.rhs.split("\n")},jQuery.extend(Mgly.CodeifyText.prototype,{getCodes:function(a){if(!this.ctxs.hasOwnProperty(a)){var b=this._diff_ctx(this[a]);this.ctxs[a]=b,b.codes.length=Object.keys(b.codes).length}return this.ctxs[a].codes},getLines:function(a){return this.ctxs[a].lines},_diff_ctx:function(a){var b={i:0,codes:{},lines:a};return this._codeify(a,b),b},_codeify:function(a,b){this._max_code;for(var d=0;d0?f="a":c.lhs_deleted_count>0&&0==c.rhs_inserted_count&&(f="d"),d=1==c.lhs_deleted_count?c.lhs_start+1:0==c.lhs_deleted_count?c.lhs_start:c.lhs_start+1+","+(c.lhs_start+c.lhs_deleted_count),e=1==c.rhs_inserted_count?c.rhs_start+1:0==c.rhs_inserted_count?c.rhs_start:c.rhs_start+1+","+(c.rhs_start+c.rhs_inserted_count),a+=d+f+e+"\n";var g=this.getLines("lhs"),h=this.getLines("rhs");if(h&&g){for(var i=c.lhs_start;i "+h[i]+"\n"}}return a},_lcs:function(a,b,c,d,e,f,g,h){for(;c>b&&f>e&&a.codes[b]==d.codes[e];)++b,++e;for(;c>b&&f>e&&a.codes[c-1]==d.codes[f-1];)--c,--f;if(b==c)for(;f>e;)d.modified[e++]=!0;else if(e==f)for(;c>b;)a.modified[b++]=!0;else{var i=this._sms(a,b,c,d,e,f,g,h);this._lcs(a,b,i.x,d,e,i.y,g,h),this._lcs(a,i.x,c,d,i.y,f,g,h)}},_sms:function(a,b,c,d,e,f,g,h){var i=a.codes.length+d.codes.length+1,j=b-e,k=c-f,l=c-b-(f-e),m=0!=(1&l),n=i-j,o=i-k,p=(c-b+f-e)/2+1;h[n+j+1]=b,g[o+k-1]=c;for(var q={x:0,y:0},r=0;p>=r;++r){for(var s=j-r;j+r>=s;s+=2){var t,u;for(s==j-r?t=h[n+s+1]:(t=h[n+s-1]+1,j+r>s&&h[n+s+1]>=t&&(t=h[n+s+1])),u=t-s;c>t&&f>u&&a.codes[t]==d.codes[u];)t++,u++;if(h[n+s]=t,m&&s>k-r&&k+r>s&&g[o+s]<=h[n+s])return q.x=h[n+s],q.y=h[n+s]-s,q}for(var s=k-r;k+r>=s;s+=2){var t,u;for(s==k+r?t=g[o+s-1]:(t=g[o+s+1]-1,s>k-r&&g[o+s-1]b&&u>e&&a.codes[t-1]==d.codes[u-1];)t--,u--;if(g[o+s]=t,!m&&s>=j-r&&j+r>=s&&g[o+s]<=h[n+s])return q.x=h[n+s],q.y=h[n+s]-s,q}}throw"the algorithm should never come here."},_optimize:function(a){for(var b=0,c=0;b=b.codes.length||a.modified[f]);)f++;for(;g=a.codes.length||b.modified[g]);)g++;(f>d||g>e)&&c.push({lhs_start:d,rhs_start:e,lhs_deleted_count:f-d,rhs_inserted_count:g-e})}return c}}),Mgly.mergely=function(a,b){a&&this.init(a,b)},jQuery.extend(Mgly.mergely.prototype,{name:"mergely",init:function(a,b){this.diffView=new Mgly.CodeMirrorDiffView(a,b),this.bind(a)},bind:function(a){this.diffView.bind(a)}}),Mgly.CodeMirrorDiffView=function(a,b){CodeMirror.defineExtension("centerOnCursor",function(){var a=this.cursorCoords(null,"local");this.scrollTo(null,(a.y+a.yBot)/2-this.getScrollerElement().clientHeight/2)}),this.init(a,b)},jQuery.extend(Mgly.CodeMirrorDiffView.prototype,{init:function(a,b){this.settings={autoupdate:!0,autoresize:!0,rhs_margin:"right",lcs:!0,sidebar:!0,viewport:!1,ignorews:!1,fadein:"fast",editor_width:"650px",editor_height:"400px",resize_timeout:500,change_timeout:150,fgcolor:{a:"#4ba3fa",c:"#a3a3a3",d:"#ff7f7f",ca:"#4b73ff",cc:"#434343",cd:"#ff4f4f"},bgcolor:"#eee",vpcolor:"rgba(0, 0, 200, 0.5)",lhs:function(){},rhs:function(){},loaded:function(){},_auto_width:function(a){return a},resize:function(b){var c=b?16:0,d=jQuery(a).parent().width()+c;"auto"==this.width?d=this._auto_width(d):(d=this.width,this.editor_width=d),"auto"==this.height?h=jQuery(a).parent().height():(h=this.height,this.editor_height=h);var e=d/2-16-8,f=h,g=jQuery(a);g.find(".mergely-column").css({width:e+"px"}),g.find(".mergely-column, .mergely-canvas, .mergely-margin, .mergely-column textarea, .CodeMirror-scroll, .cm-s-default").css({height:f+"px"}),g.find(".mergely-canvas").css({height:f+"px"}),g.find(".mergely-column textarea").css({width:e+"px"}),g.css({width:d,height:h,clear:"both"}),"none"==g.css("display")&&(0!=this.fadein?g.fadeIn(this.fadein):g.show(),this.loaded&&this.loaded()),this.resized&&this.resized()},_debug:"",resized:function(){}};var c={mode:"text/plain",readOnly:!1,lineWrapping:!1,lineNumbers:!0,gutters:["merge","CodeMirror-linenumbers"]};this.lhs_cmsettings={},this.rhs_cmsettings={},this.element=jQuery(a),b&&b.cmsettings&&jQuery.extend(this.lhs_cmsettings,c,b.cmsettings,b.lhs_cmsettings),b&&b.cmsettings&&jQuery.extend(this.rhs_cmsettings,c,b.cmsettings,b.rhs_cmsettings),b&&jQuery.extend(this.settings,b),this.element.bind("destroyed",jQuery.proxy(this.teardown,this)),jQuery.data(a,"mergely",this)},unbind:function(){null!=this.changed_timeout&&clearTimeout(this.changed_timeout),this.editor[this.id+"-lhs"].toTextArea(),this.editor[this.id+"-rhs"].toTextArea()},destroy:function(){this.element.unbind("destroyed",this.teardown),this.teardown()},teardown:function(){this.unbind()},lhs:function(a){this.editor[this.id+"-lhs"].setValue(a)},rhs:function(a){this.editor[this.id+"-rhs"].setValue(a)},update:function(){this._changing(this.id+"-lhs",this.id+"-rhs")},unmarkup:function(){this._clear()},scrollToDiff:function(a){this.changes.length&&(this._current_diff="next"==a?Math.min(++this._current_diff,this.changes.length-1):Math.max(--this._current_diff,0),this._scroll_to_change(this.changes[this._current_diff]),this._changed(this.id+"-lhs",this.id+"-rhs"))},mergeCurrentChange:function(a){this.changes.length&&("lhs"!=a||this.lhs_cmsettings.readOnly?"rhs"!=a||this.rhs_cmsettings.readOnly||this._merge_change(this.changes[this._current_diff],"lhs","rhs"):this._merge_change(this.changes[this._current_diff],"rhs","lhs"))},scrollTo:function(a,b){var c=this.editor[this.id+"-lhs"],d=this.editor[this.id+"-rhs"];"lhs"==a?(c.setCursor(b),c.centerOnCursor()):(d.setCursor(b),d.centerOnCursor())},options:function(a){if(!a)return this.settings;if(jQuery.extend(this.settings,a),this.settings.autoresize&&this.resize(),this.settings.autoupdate&&this.update(),this.settings.hasOwnProperty("rhs_margin"))if("left"==this.settings.rhs_margin)this.element.find(".mergely-margin:last-child").insertAfter(this.element.find(".mergely-canvas"));else{var b=this.element.find(".mergely-margin").last();b.appendTo(b.parent())}this.settings.hasOwnProperty("sidebar")&&(this.settings.sidebar?jQuery(this.element).find(".mergely-margin").css({display:"block"}):jQuery(this.element).find(".mergely-margin").css({display:"none"}))},swap:function(){if(!this.lhs_cmsettings.readOnly&&!this.rhs_cmsettings.readOnly){var a=this.editor[this.id+"-lhs"],b=this.editor[this.id+"-rhs"],c=b.getValue();b.setValue(a.getValue()),a.setValue(c)}},merge:function(a){var b=this.editor[this.id+"-lhs"],c=this.editor[this.id+"-rhs"];"lhs"!=a||this.lhs_cmsettings.readOnly?this.rhs_cmsettings.readOnly||c.setValue(b.getValue()):b.setValue(c.getValue())},get:function(a){var b=this.editor[this.id+"-"+a],c=b.getValue();return void 0==c?"":c},clear:function(a){if(!("lhs"==a&&this.lhs_cmsettings.readOnly||"rhs"==a&&this.rhs_cmsettings.readOnly)){var b=this.editor[this.id+"-"+a];b.setValue("")}},cm:function(a){return this.editor[this.id+"-"+a]},search:function(a,b,c){var f,d=this.editor[this.id+"-lhs"],e=this.editor[this.id+"-rhs"];f="lhs"==a?d:e,c="prev"==c?"findPrevious":"findNext",(0==f.getSelection().length||this.prev_query[a]!=b)&&(this.cursor[this.id]=f.getSearchCursor(b,{line:0,ch:0},!1),this.prev_query[a]=b);var g=this.cursor[this.id];g[c]()?f.setSelection(g.from(),g.to()):g=f.getSearchCursor(b,{line:0,ch:0},!1)},resize:function(){this.settings.resize(),this._changing(this.id+"-lhs",this.id+"-rhs"),this._set_top_offset(this.id+"-lhs")},diff:function(){var a=this.editor[this.id+"-lhs"].getValue(),b=this.editor[this.id+"-rhs"].getValue(),c=new Mgly.diff(a,b,this.settings);return c.normal_form()},bind:function(a){jQuery(this.element).hide(),this.id=jQuery(a).attr("id");var b=this.settings.editor_height,c=this.settings.editor_width;this.changed_timeout=null,this.chfns={},this.chfns[this.id+"-lhs"]=[],this.chfns[this.id+"-rhs"]=[],this.prev_query=[],this.cursor=[],this._skipscroll={},this.change_exp=new RegExp(/(\d+(?:,\d+)?)([acd])(\d+(?:,\d+)?)/);var d,e;if(void 0!=jQuery.button)d='',e='';else{var f="opacity:0.4;width:10px;height:15px;background-color:#888;cursor:pointer;text-align:center;color:#eee;border:1px solid: #222;margin-right:5px;margin-top: -2px;";d='
<
',e='
>
'}this.merge_rhs_button=jQuery(e),this.merge_lhs_button=jQuery(d),jQuery(this.element).append(jQuery('
')),jQuery(this.element).append(jQuery('
')),jQuery(this.element).append(jQuery('
'));var g=jQuery('
');this.settings.sidebar||jQuery(this.element).find(".mergely-margin").css({display:"none"}),"left"==this.settings.rhs_margin&&jQuery(this.element).append(g),jQuery(this.element).append(jQuery('
')),"left"!=this.settings.rhs_margin&&jQuery(this.element).append(g);var h="#"+this.id+" .CodeMirror-gutter-text { padding: 5px 0 0 0; }"+"#"+this.id+" .CodeMirror-lines pre, "+"#"+this.id+" .CodeMirror-gutter-text pre { line-height: 18px; }"+".CodeMirror-linewidget { overflow: hidden; };";this.settings.autoresize&&(h+=this.id+" .CodeMirror-scroll { height: 100%; overflow: auto; }"),h+="\n.CodeMirror { line-height: 18px; }",jQuery('").appendTo("head");var i=jQuery("#"+this.id+"-rhs").get(0);if(!i)return console.error("rhs textarea not defined - Mergely not initialized properly"),void 0;var j=jQuery("#"+this.id+"-lhs").get(0);if(!i)return console.error("lhs textarea not defined - Mergely not initialized properly"),void 0;var k=this;if(this.editor=[],this.editor[this.id+"-lhs"]=CodeMirror.fromTextArea(j,this.lhs_cmsettings),this.editor[this.id+"-rhs"]=CodeMirror.fromTextArea(i,this.rhs_cmsettings),this.editor[this.id+"-lhs"].on("change",function(){k.settings.autoupdate&&k._changing(k.id+"-lhs",k.id+"-rhs")}),this.editor[this.id+"-lhs"].on("scroll",function(){k._scrolling(k.id+"-lhs")}),this.editor[this.id+"-rhs"].on("change",function(){k.settings.autoupdate&&k._changing(k.id+"-lhs",k.id+"-rhs")}),this.editor[this.id+"-rhs"].on("scroll",function(){k._scrolling(k.id+"-rhs")}),this.settings.autoresize){var l=null,m=function(a){k.settings.resize&&k.settings.resize(a),k.editor[k.id+"-lhs"].refresh(),k.editor[k.id+"-rhs"].refresh(),k.settings.autoupdate&&k._changing(k.id+"-lhs",k.id+"-rhs")};jQuery(window).resize(function(){l&&clearTimeout(l),l=setTimeout(m,k.settings.resize_timeout)}),m(!0)}if(this.settings.lhs){var n=this.editor[this.id+"-lhs"].getDoc().setValue;this.settings.lhs(n.bind(this.editor[this.id+"-lhs"].getDoc()))}if(this.settings.rhs){var n=this.editor[this.id+"-rhs"].getDoc().setValue;this.settings.rhs(n.bind(this.editor[this.id+"-rhs"].getDoc()))}},_scroll_to_change:function(a){if(a){var b=this,c=b.editor[b.id+"-lhs"],d=b.editor[b.id+"-rhs"],e=1*c.getScrollerElement().offsetHeight/2;c.setCursor(Math.max(a["lhs-line-from"],0),0),d.setCursor(Math.max(a["rhs-line-from"],0),0),c.scrollTo(null,0),d.scrollTo(null,0),b._calculate_offsets(b.id+"-lhs",b.id+"-rhs",[a]),c.scrollTo(null,Math.max(a["lhs-y-start"]-e,0)),d.scrollTo(null,Math.max(a["rhs-y-start"]-e,0))}},_scrolling:function(a){if(this._skipscroll[a]===!0)return this._skipscroll[a]=!1,void 0;var b=jQuery(this.editor[a].getScrollerElement());void 0==this.midway&&(this.midway=(b.height()/2+b.offset().top).toFixed(2));var c=this.editor[a].coordsChar({left:0,top:this.midway}),d=b.scrollTop(),e=b.scrollLeft();this.trace("scroll","side",a),this.trace("scroll","midway",this.midway),this.trace("scroll","midline",c),this.trace("scroll","top_to",d),this.trace("scroll","left_to",e);var f=this.id+"-lhs",g=this.id+"-rhs";for(var h in this.editor)if(this.editor.hasOwnProperty(h)&&a!=h){for(var i=a.replace(this.id+"-",""),j=h.replace(this.id+"-",""),k=0,l=null,m=!1,n=0;n=o[i+"-line-from"]&&(l=o,c.line>=l[i+"-line-to"]&&(o.hasOwnProperty(i+"-y-start")&&o.hasOwnProperty(i+"-y-end")&&o.hasOwnProperty(j+"-y-start")&&o.hasOwnProperty(j+"-y-end")?k+=o[i+"-y-end"]-o[i+"-y-start"]-(o[j+"-y-end"]-o[j+"-y-start"]):m=!0))}var p=this.editor[h].getViewport(),q=!0;if(l&&(this.trace("scroll","last change before midline",l),c.line>=p.from&&c<=p.to&&(q=!1)),this.trace("scroll","scroll",q),q||m){this.trace("scroll","scrolling other side",d-k);var b=jQuery(this.editor[h].getScrollerElement());this._skipscroll[h]=!0,b.scrollTop(d-k).scrollLeft(e)}else this.trace("scroll","not scrolling other side");if(this.settings.autoupdate){var r=new Mgly.Timer;this._calculate_offsets(f,g,this.changes),this.trace("change","offsets time",r.stop()),this._markup_changes(f,g,this.changes),this.trace("change","markup time",r.stop()),this._draw_diff(f,g,this.changes),this.trace("change","draw time",r.stop())}this.trace("scroll","scrolled")}},_changing:function(a,b){this.trace("change","changing-timeout",this.changed_timeout);var c=this;null!=this.changed_timeout&&clearTimeout(this.changed_timeout),this.changed_timeout=setTimeout(function(){var d=new Mgly.Timer;c._changed(a,b),c.trace("change","total time",d.stop())},this.settings.change_timeout)},_changed:function(a,b){this._clear(),this._diff(a,b)},_clear:function(){var a=this;for(var b in this.editor)if(this.editor.hasOwnProperty(b)){var c=this.editor[b],d=a.chfns[b];c.operation(function(){for(var b=new Mgly.Timer,e=0,f=c.lineCount();f>e;++e)c.removeLineClass(e,"background");for(var e=0;ea.from&&b["lhs-line-to"]>a.to||b["rhs-line-from"]a.from&&b["rhs-line-to"]>a.to?!1:!0:!0},_set_top_offset:function(a){var b=this.editor[a].getScrollInfo().top;this.editor[a].scrollTo(null,0);var c=jQuery("#"+this.id+" .CodeMirror-measure").first(),d=c.offset().top-4;return d?(this.editor[a].scrollTo(null,b),this.draw_top_offset=.5-d,!0):!1},_calculate_offsets:function(a,b,c){if(null==this.em_height){if(!this._set_top_offset(a))return;this.em_height=this.editor[a].defaultTextHeight(),this.em_height||(console.warn("Failed to calculate offsets, using 18 by default"),this.em_height=18),this.draw_lhs_min=.5;var d=jQuery("#"+a+"-"+b+"-canvas");if(d.length||console.error("failed to find canvas","#"+a+"-"+b+"-canvas"),!d.width())return console.error("canvas width is 0"),void 0;this.draw_mid_width=jQuery("#"+a+"-"+b+"-canvas").width(),this.draw_rhs_max=this.draw_mid_width-.5,this.draw_lhs_width=5,this.draw_rhs_width=5,this.trace("calc","change offsets calculated",{top_offset:this.draw_top_offset,lhs_min:this.draw_lhs_min,rhs_max:this.draw_rhs_max,lhs_width:this.draw_lhs_width,rhs_width:this.draw_rhs_width})}for(var e=this.editor[a].charCoords({line:0}),f=this.editor[b].charCoords({line:0}),g=this._get_viewport(a,b),h=0;h=0?i["lhs-line-from"]:0,k=i["lhs-line-to"]>=0?i["lhs-line-to"]:0,l=i["rhs-line-from"]>=0?i["rhs-line-from"]:0,m=i["rhs-line-to"]>=0?i["rhs-line-to"]:0;if(this.editor[a].getOption("lineWrapping")||this.editor[a].getOption("lineWrapping")){var r=this.editor[a].cursorCoords({line:j,ch:0},"page"),s=this.editor[a].getLineHandle(j);n={top:r.top,bottom:r.top+s.height};var t=this.editor[a].cursorCoords({line:k,ch:0},"page"),u=this.editor[a].getLineHandle(k);o={top:t.top,bottom:t.top+u.height};var r=this.editor[b].cursorCoords({line:l,ch:0},"page"),v=this.editor[b].getLineHandle(l);p={top:r.top,bottom:r.top+v.height};var t=this.editor[b].cursorCoords({line:m,ch:0},"page"),w=this.editor[b].getLineHandle(m);q={top:t.top,bottom:t.top+w.height}}else n={top:e.top+j*this.em_height,bottom:e.bottom+j*this.em_height+2},o={top:e.top+k*this.em_height,bottom:e.bottom+k*this.em_height+2},p={top:f.top+l*this.em_height,bottom:f.bottom+l*this.em_height+2},q={top:f.top+m*this.em_height,bottom:f.bottom+m*this.em_height+2};"a"==i.op?l>0&&(n.top=n.bottom,n.bottom+=this.em_height,o=n):"d"==i.op&&j>0&&(p.top=p.bottom,p.bottom+=this.em_height,q=p),i["lhs-y-start"]=this.draw_top_offset+n.top,i["lhs-y-end"]="c"==i.op||"d"==i.op?this.draw_top_offset+o.bottom:this.draw_top_offset+o.top,i["rhs-y-start"]=this.draw_top_offset+p.top,i["rhs-y-end"]="c"==i.op||"a"==i.op?this.draw_top_offset+q.bottom:this.draw_top_offset+q.top,this.trace("calc","change calculated",h,i)}else delete i["lhs-y-start"],delete i["lhs-y-end"],delete i["rhs-y-start"],delete i["rhs-y-end"]}return c},_markup_changes:function(a,b,c){jQuery(".merge-button").remove();var d=this,e=this.editor[a],f=this.editor[b],g=new Mgly.Timer;e.operation(function(){for(var a=0;a=0?b["lhs-line-from"]:0,h=b["lhs-line-to"]>=0?b["lhs-line-to"]:0,i=b["rhs-line-from"]>=0?b["rhs-line-from"]:0;b["rhs-line-to"]>=0?b["rhs-line-to"]:0;var k=["mergely","lhs",b.op,"cid-"+a];if(e.addLineClass(g,"background","start"),e.addLineClass(h,"background","end"),0==g&&0==h&&0==i)e.addLineClass(g,"background",k.join(" ")),e.addLineClass(g,"background","first");else for(var l=g;h>=l;++l)e.addLineClass(l,"background",k.join(" ")),e.addLineClass(l,"background",k.join(" "));if(!f.getOption("readOnly")){var m=d.merge_rhs_button.clone();m.button&&m.button({icons:{primary:"ui-icon-triangle-1-e"},text:!1}),m.addClass("merge-button"),m.attr("id","merge-rhs-"+a),e.setGutterMarker(g,"merge",m.get(0))}}});var h=this._get_viewport(a,b);this.trace("change","markup lhs-editor time",g.stop()),f.operation(function(){for(var a=0;a=0?b["lhs-line-from"]:0;b["lhs-line-to"]>=0?b["lhs-line-to"]:0;var j=b["rhs-line-from"]>=0?b["rhs-line-from"]:0,k=b["rhs-line-to"]>=0?b["rhs-line-to"]:0;if(d._is_change_in_view(h,b)){var l=["mergely","rhs",b.op,"cid-"+a];if(f.addLineClass(j,"background","start"),f.addLineClass(k,"background","end"),0==j&&0==k&&0==g)f.addLineClass(j,"background",l.join(" ")),f.addLineClass(j,"background","first");else for(var m=j;k>=m;++m)f.addLineClass(m,"background",l.join(" ")),f.addLineClass(m,"background",l.join(" "));if(!e.getOption("readOnly")){var n=d.merge_lhs_button.clone();n.button&&n.button({icons:{primary:"ui-icon-triangle-1-w"},text:!1}),n.addClass("merge-button"),n.attr("id","merge-lhs-"+a),f.setGutterMarker(j,"merge",n.get(0))}}}}),this.trace("change","markup rhs-editor time",g.stop());for(var i=[],j=0;this.settings.lcs&&j=0?k["lhs-line-from"]:0,m=k["lhs-line-to"]>=0?k["lhs-line-to"]:0,n=k["rhs-line-from"]>=0?k["rhs-line-from"]:0,o=k["rhs-line-to"]>=0?k["rhs-line-to"]:0;if(this._is_change_in_view(h,k))if("d"==k.op){var p=l,q=m,r=e.lineInfo(q);r&&i.push([e,{line:p,ch:0},{line:q,ch:r.text.length},{className:"mergely ch d lhs"}])}else if("c"==k.op)for(var s=l,t=n,u=0;s>=0&&m>=s||t>=0&&o>=t;++s,++t)if(t+u>o){var v=e.getLine(s);i.push([e,{line:s,ch:0},{line:s,ch:v.length},{className:"mergely ch d lhs"}])}else if(s+u>m){var w=f.getLine(t);i.push([f,{line:t,ch:0},{line:t,ch:w.length},{className:"mergely ch a rhs"}])}else{var v=e.getLine(s),w=f.getLine(t),B=new Mgly.LCS(v,w);B.diff(function(a,b){i.push([f,{line:t,ch:a},{line:t,ch:b},{className:"mergely ch a rhs"}])},removed=function(a,b){i.push([e,{line:s,ch:a},{line:s,ch:b},{className:"mergely ch d lhs"}])})}}this.trace("change","LCS marktext time",g.stop()),e.operation(function(){for(var a=0;a=h;--j)f[c].removeLine(j);else if("lhs"==b)if("a"==a.op)for(var h=parseInt(a[c+"-line-from"]),i=parseInt(a[c+"-line-to"]),j=i;j>=h;--j)f[c].removeLine(j);else f[c].replaceRange(g,CodeMirror.Pos(a[c+"-line-from"]+1,0));f.lhs.setValue(f.lhs.getValue()),f.rhs.setValue(f.rhs.getValue()),this._scroll_to_change(a)}},_draw_info:function(a,b){var c=jQuery(this.editor[a].getScrollerElement()).height(),d=jQuery(this.editor[a].getScrollerElement()).children(":first-child").height(),e=document.getElementById(a+"-"+b+"-canvas");if(void 0==e)throw"Failed to find: "+a+"-"+b+"-canvas";var f=jQuery("#"+this.id+"-lhs-margin"),g=jQuery("#"+this.id+"-rhs-margin");return{visible_page_height:c,gutter_height:d,visible_page_ratio:c/d,margin_ratio:c/d,lhs_scroller:jQuery(this.editor[a].getScrollerElement()),rhs_scroller:jQuery(this.editor[b].getScrollerElement()),lhs_lines:this.editor[a].lineCount(),rhs_lines:this.editor[b].lineCount(),dcanvas:e,clhs:f,crhs:g,lhs_xyoffset:jQuery(f).offset(),rhs_xyoffset:jQuery(g).offset()}},_draw_diff:function(a,b,c){var d=this._draw_info(a,b),e=d.clhs.get(0),f=d.crhs.get(0),g=d.dcanvas.getContext("2d"),h=e.getContext("2d"),i=f.getContext("2d");this.trace("draw","visible_page_height",d.visible_page_height),this.trace("draw","gutter_height",d.gutter_height),this.trace("draw","visible_page_ratio",d.visible_page_ratio),this.trace("draw","lhs-scroller-top",d.lhs_scroller.scrollTop()),this.trace("draw","rhs-scroller-top",d.rhs_scroller.scrollTop()),jQuery.each(jQuery.find("#"+this.id+" canvas"),function(){jQuery(this).get(0).height=d.visible_page_height}),d.clhs.unbind("click"),d.crhs.unbind("click"),h.beginPath(),h.fillStyle=this.settings.bgcolor,h.strokeStyle="#888",h.fillRect(0,0,6.5,d.visible_page_height),h.strokeRect(0,0,6.5,d.visible_page_height),i.beginPath(),i.fillStyle=this.settings.bgcolor,i.strokeStyle="#888",i.fillRect(0,0,6.5,d.visible_page_height),i.strokeRect(0,0,6.5,d.visible_page_height);for(var j=this._get_viewport(a,b),k=0;k=s?g.lineTo(t+r,u):(g.arcTo(t+r,u,t+r,u+q,q),g.arcTo(t+r,u+s,t+r-q,u+s,q)),g.lineTo(t,u+s)),g.stroke(),r=this.draw_rhs_width,s=p-o-1,t=this.draw_rhs_max,u=o,g.moveTo(t,u),"Microsoft Internet Explorer"==navigator.appName?(g.lineTo(this.draw_rhs_max-this.draw_rhs_width,o),g.lineTo(this.draw_rhs_max-this.draw_rhs_width,p+1),g.lineTo(this.draw_rhs_max,p+1)):(0>=s?g.lineTo(t-r,u):(g.arcTo(t-r,u,t-r,u+q,q),g.arcTo(t-r,u+s,t-q,u+s,q)),g.lineTo(t,u+s)),g.stroke();var v=this.draw_lhs_min+this.draw_lhs_width,w=m+(n+1-m)/2,x=this.draw_rhs_max-this.draw_rhs_width,y=o+(p+1-o)/2;g.moveTo(v,w),w==y?g.lineTo(x,y):g.bezierCurveTo(v+12,w-3,x-12,y-3,x,y),g.stroke()}}h.fillStyle=this.settings.vpcolor,i.fillStyle=this.settings.vpcolor;var z=d.clhs.height()*d.visible_page_ratio,A=d.lhs_scroller.scrollTop()/d.gutter_height*d.clhs.height(),B=d.crhs.height()*d.visible_page_ratio,C=d.rhs_scroller.scrollTop()/d.gutter_height*d.crhs.height();this.trace("draw","cls.height",d.clhs.height()),this.trace("draw","lhs_scroller.scrollTop()",d.lhs_scroller.scrollTop()),this.trace("draw","gutter_height",d.gutter_height),this.trace("draw","visible_page_ratio",d.visible_page_ratio),this.trace("draw","lhs from",A,"lhs to",z),this.trace("draw","rhs from",C,"rhs to",B),h.fillRect(1.5,A,4.5,z),i.fillRect(1.5,C,4.5,B),d.clhs.click(function(a){var b=a.pageY-d.lhs_xyoffset.top-z/2,c=Math.max(0,b/e.height*d.lhs_scroller.get(0).scrollHeight);d.lhs_scroller.scrollTop(c)}),d.crhs.click(function(a){var b=a.pageY-d.rhs_xyoffset.top-B/2,c=Math.max(0,b/f.height*d.rhs_scroller.get(0).scrollHeight);d.rhs_scroller.scrollTop(c)})},trace:function(a){this.settings._debug.indexOf(a)>=0&&(arguments[0]=a+":",console.log([].slice.apply(arguments)))}}),jQuery.pluginMaker=function(a){jQuery.fn[a.prototype.name]=function(b){var c=jQuery.makeArray(arguments),d=c.slice(1),e=void 0;return this.each(function(){var f=jQuery.data(this,a.prototype.name);if(f){if("string"==typeof b)e=f[b].apply(f,d);else if(f.update)return f.update.apply(f,c)}else new a(this,b)}),void 0!=e?e:void 0}},jQuery.pluginMaker(Mgly.mergely); -------------------------------------------------------------------------------- /lib/searchcursor.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | var Pos = CodeMirror.Pos; 3 | 4 | function SearchCursor(cm, query, pos, caseFold) { 5 | this.atOccurrence = false; this.cm = cm; 6 | if (caseFold == null && typeof query == "string") caseFold = false; 7 | 8 | pos = pos ? cm.clipPos(pos) : Pos(0, 0); 9 | this.pos = {from: pos, to: pos}; 10 | 11 | // The matches method is filled in based on the type of query. 12 | // It takes a position and a direction, and returns an object 13 | // describing the next occurrence of the query, or null if no 14 | // more matches were found. 15 | if (typeof query != "string") { // Regexp match 16 | if (!query.global) query = new RegExp(query.source, query.ignoreCase ? "ig" : "g"); 17 | this.matches = function(reverse, pos) { 18 | if (reverse) { 19 | query.lastIndex = 0; 20 | var line = cm.getLine(pos.line).slice(0, pos.ch), match = query.exec(line), start = 0; 21 | while (match) { 22 | start += match.index + 1; 23 | line = line.slice(start); 24 | query.lastIndex = 0; 25 | var newmatch = query.exec(line); 26 | if (newmatch) match = newmatch; 27 | else break; 28 | } 29 | start--; 30 | } else { 31 | query.lastIndex = pos.ch; 32 | var line = cm.getLine(pos.line), match = query.exec(line), 33 | start = match && match.index; 34 | } 35 | if (match && match[0]) 36 | return {from: Pos(pos.line, start), 37 | to: Pos(pos.line, start + match[0].length), 38 | match: match}; 39 | }; 40 | } else { // String query 41 | if (caseFold) query = query.toLowerCase(); 42 | var fold = caseFold ? function(str){return str.toLowerCase();} : function(str){return str;}; 43 | var target = query.split("\n"); 44 | // Different methods for single-line and multi-line queries 45 | if (target.length == 1) { 46 | if (!query.length) { 47 | // Empty string would match anything and never progress, so 48 | // we define it to match nothing instead. 49 | this.matches = function() {}; 50 | } else { 51 | this.matches = function(reverse, pos) { 52 | var line = fold(cm.getLine(pos.line)), len = query.length, match; 53 | if (reverse ? (pos.ch >= len && (match = line.lastIndexOf(query, pos.ch - len)) != -1) 54 | : (match = line.indexOf(query, pos.ch)) != -1) 55 | return {from: Pos(pos.line, match), 56 | to: Pos(pos.line, match + len)}; 57 | }; 58 | } 59 | } else { 60 | this.matches = function(reverse, pos) { 61 | var ln = pos.line, idx = (reverse ? target.length - 1 : 0), match = target[idx], line = fold(cm.getLine(ln)); 62 | var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match)); 63 | if (reverse ? offsetA >= pos.ch || offsetA != match.length 64 | : offsetA <= pos.ch || offsetA != line.length - match.length) 65 | return; 66 | for (;;) { 67 | if (reverse ? !ln : ln == cm.lineCount() - 1) return; 68 | line = fold(cm.getLine(ln += reverse ? -1 : 1)); 69 | match = target[reverse ? --idx : ++idx]; 70 | if (idx > 0 && idx < target.length - 1) { 71 | if (line != match) return; 72 | else continue; 73 | } 74 | var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length); 75 | if (reverse ? offsetB != line.length - match.length : offsetB != match.length) 76 | return; 77 | var start = Pos(pos.line, offsetA), end = Pos(ln, offsetB); 78 | return {from: reverse ? end : start, to: reverse ? start : end}; 79 | } 80 | }; 81 | } 82 | } 83 | } 84 | 85 | SearchCursor.prototype = { 86 | findNext: function() {return this.find(false);}, 87 | findPrevious: function() {return this.find(true);}, 88 | 89 | find: function(reverse) { 90 | var self = this, pos = this.cm.clipPos(reverse ? this.pos.from : this.pos.to); 91 | function savePosAndFail(line) { 92 | var pos = Pos(line, 0); 93 | self.pos = {from: pos, to: pos}; 94 | self.atOccurrence = false; 95 | return false; 96 | } 97 | 98 | for (;;) { 99 | if (this.pos = this.matches(reverse, pos)) { 100 | if (!this.pos.from || !this.pos.to) { console.log(this.matches, this.pos); } 101 | this.atOccurrence = true; 102 | return this.pos.match || true; 103 | } 104 | if (reverse) { 105 | if (!pos.line) return savePosAndFail(0); 106 | pos = Pos(pos.line-1, this.cm.getLine(pos.line-1).length); 107 | } 108 | else { 109 | var maxLine = this.cm.lineCount(); 110 | if (pos.line == maxLine - 1) return savePosAndFail(maxLine); 111 | pos = Pos(pos.line + 1, 0); 112 | } 113 | } 114 | }, 115 | 116 | from: function() {if (this.atOccurrence) return this.pos.from;}, 117 | to: function() {if (this.atOccurrence) return this.pos.to;}, 118 | 119 | replace: function(newText) { 120 | if (!this.atOccurrence) return; 121 | var lines = CodeMirror.splitLines(newText); 122 | this.cm.replaceRange(lines, this.pos.from, this.pos.to); 123 | this.pos.to = Pos(this.pos.from.line + lines.length - 1, 124 | lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0)); 125 | } 126 | }; 127 | 128 | CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) { 129 | return new SearchCursor(this, query, pos, caseFold); 130 | }); 131 | })(); --------------------------------------------------------------------------------