Commit b08164df by Will Daly

Refactor Studio editing JavaScript to make it easier to test

Add JavaScript tests for Studio editing and XBlock server interaction
Refactor student-facing JS views to use JS server interface.
manage.py test runs JavaScript tests
Fix data type issue for rendering peer/self/grade on page load
parent 7d4952d9
......@@ -48,3 +48,4 @@ htmlcov
# node
node_modules
npm-debug.log
coverage
......@@ -4,7 +4,6 @@ python:
install:
- "pip install -r requirements/test.txt --use-mirrors"
- "pip install -e ."
- "npm install"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
......@@ -12,7 +11,6 @@ before_script:
script:
- "python manage.py test"
- "python manage.py harvest"
- "npm test"
- "python setup.py install"
after_success:
coveralls
......@@ -27,18 +27,12 @@ up in the XBlock workbench.
Running Tests
=============
To run the Python test suite:
To run the unit test suite:
.. code:: bash
python manage.py test
To run the JavaScript test suite (after installing `node <http://nodejs.org/download/>`_)
.. code:: bash
npm install && npm test
License
=======
......
......@@ -252,6 +252,7 @@ class OpenAssessmentBlock(XBlock, SubmissionMixin, PeerAssessmentMixin, SelfAsse
context = Context(context_dict)
frag = Fragment(template.render(context))
frag.add_css(load("static/css/openassessment.css"))
frag.add_javascript(load("static/js/src/oa_server.js"))
frag.add_javascript(load("static/js/src/oa_base.js"))
frag.initialize_js('OpenAssessmentBlock')
return frag
......
var CodeMirror=function(){function a(d,e){function Vb(a){return a>=0&&a<sb.size}function Xb(a){return v(sb,a)}function Yb(a,b){Jb=!0;for(var c=b-a.height,d=a;d;d=d.parent)d.height+=c}function Zb(a){var b={line:0,ch:0};nc(b,{line:sb.size-1,ch:Xb(sb.size-1).text.length},eb(a),b,b),Db=!0}function $b(){var b=[];return sb.iter(0,sb.size,function(a){b.push(a.text)}),b.join("\n")}function _b(a){function j(a){var b=Gd(a,!0);if(b&&!Y(b,g)){ub||lc(),g=b,Oc(d,b),Db=!1;var c=Hc();(b.line>=c.to||b.line<c.from)&&(h=setTimeout(Td(function(){j(a)}),150))}}function k(a){clearTimeout(h);var b=Gd(a);b&&Oc(d,b),C(a),Dc(),Db=!0,l(),i()}Nc(H(a,"shiftKey"));for(var b=F(a);b!=s;b=b.parentNode)if(b.parentNode==T&&b!=U)return;for(var b=F(a);b!=s;b=b.parentNode)if(b.parentNode==hb)return f.onGutterClick&&f.onGutterClick(Wb,cb(hb.childNodes,b)+Mb,a),C(a);var d=Gd(a);switch(G(a)){case 3:return L&&!c&&Hd(a),void 0;case 2:return d&&Rc(d.line,d.ch,!0),void 0}if(!d)return F(a)==S&&C(a),void 0;ub||lc();var e=+new Date;if(yb&&yb.time>e-400&&Y(yb.pos,d))return C(a),setTimeout(Dc,20),$c(d.line);if(xb&&xb.time>e-400&&Y(xb.pos,d))return yb={time:e,pos:d},C(a),Zc(d);xb={time:e,pos:d};var h,g=d;if(R&&!f.readOnly&&!Y(vb.from,vb.to)&&!Z(d,vb.from)&&!Z(vb.to,d)){O&&(ib.draggable=!0);var i=I(document,"mouseup",Td(function(b){O&&(ib.draggable=!1),Ab=!1,i(),Math.abs(a.clientX-b.clientX)+Math.abs(a.clientY-b.clientY)<10&&(C(b),Rc(d.line,d.ch,!0),Dc())}),!0);return Ab=!0,ib.dragDrop&&ib.dragDrop(),void 0}C(a),Rc(d.line,d.ch,!0);var l=I(document,"mousemove",Td(function(a){clearTimeout(h),C(a),M||G(a)?j(a):k(a)}),!0),i=I(document,"mouseup",Td(k),!0)}function ac(a){for(var b=F(a);b!=s;b=b.parentNode)if(b.parentNode==hb)return C(a);var c=Gd(a);c&&(yb={time:+new Date,pos:c},C(a),Zc(c))}function bc(a){function d(a,c){var d=new FileReader;d.onload=function(){g[c]=d.result,++h==e&&(b=Tc(b),Td(function(){var a=sc(g.join(""),b,b);Oc(b,a)})())},d.readAsText(a)}a.preventDefault();var b=Gd(a,!0),c=a.dataTransfer.files;if(b&&!f.readOnly)if(c&&c.length&&window.FileReader&&window.File)for(var e=c.length,g=Array(e),h=0,i=0;e>i;++i)d(c[i],i);else try{var g=a.dataTransfer.getData("Text");if(g){var j=vb.from,k=vb.to;Oc(b,b),Ab&&sc("",j,k),tc(g),Dc()}}catch(a){}}function cc(a){var b=wc();if(a.dataTransfer.setData("Text",b),L||P){var c=document.createElement("img");c.scr="data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs=",a.dataTransfer.setDragImage(c,0,0)}}function dc(a,b){if("string"==typeof a&&(a=h[a],!a))return!1;var c=wb;try{f.readOnly&&(Cb=!0),b&&(wb=null),a(Wb)}catch(d){if(d!=K)throw d;return!1}finally{wb=c,Cb=!1}return!0}function ec(a){var b=j(f.keyMap),c=b.auto;clearTimeout(hc),c&&!l(a)&&(hc=setTimeout(function(){j(f.keyMap)==b&&(f.keyMap=c.call?c.call(null,Wb):c)},50));var d=gb[H(a,"keyCode")],e=!1;return null==d||a.altGraphKey?!1:(H(a,"altKey")&&(d="Alt-"+d),H(a,"ctrlKey")&&(d="Ctrl-"+d),H(a,"metaKey")&&(d="Cmd-"+d),e=H(a,"shiftKey")?k("Shift-"+d,f.extraKeys,f.keyMap,function(a){return dc(a,!0)})||k(d,f.extraKeys,f.keyMap,function(a){return"string"==typeof a&&/^go[A-Z]/.test(a)?dc(a):void 0}):k(d,f.extraKeys,f.keyMap,dc),e&&(C(a),M&&(a.oldKeyCode=a.keyCode,a.keyCode=0)),e)}function fc(a,b){var c=k("'"+b+"'",f.extraKeys,f.keyMap,dc);return c&&C(a),c}function ic(a){if(ub||lc(),M&&27==a.keyCode&&(a.returnValue=!1),xc&&Bc()&&(xc=!1),!f.onKeyEvent||!f.onKeyEvent(Wb,B(a))){var b=H(a,"keyCode");Nc(16==b||H(a,"shiftKey"));var d=ec(a);window.opera&&(gc=d?b:null,!d&&88==b&&H(a,c?"metaKey":"ctrlKey")&&tc(""))}}function jc(a){if(xc&&Bc(),!f.onKeyEvent||!f.onKeyEvent(Wb,B(a))){var b=H(a,"keyCode"),c=H(a,"charCode");if(window.opera&&b==gc)return gc=null,C(a),void 0;if(!(window.opera&&!a.which||Q)||!ec(a)){var d=String.fromCharCode(null==c?b:c);f.electricChars&&rb.electricChars&&f.smartIndent&&!f.readOnly&&rb.electricChars.indexOf(d)>-1&&setTimeout(Td(function(){ad(vb.to.line,"smart")}),75),fc(a,d)||zc()}}}function kc(a){f.onKeyEvent&&f.onKeyEvent(Wb,B(a))||16==H(a,"keyCode")&&(wb=null)}function lc(){"nocursor"!=f.readOnly&&(ub||(f.onFocus&&f.onFocus(Wb),ub=!0,-1==s.className.search(/\bCodeMirror-focused\b/)&&(s.className+=" CodeMirror-focused"),Ib||Cc(!0)),yc(),Id())}function mc(){ub&&(f.onBlur&&f.onBlur(Wb),ub=!1,Pb&&Td(function(){Pb&&(Pb(),Pb=null)})(),s.className=s.className.replace(" CodeMirror-focused","")),clearInterval(qb),setTimeout(function(){ub||(wb=null)},150)}function nc(a,b,c,d,e){if(!Cb){if(Tb){var g=[];for(sb.iter(a.line,b.line+1,function(a){g.push(a.text)}),Tb.addChange(a.line,c.length,g);Tb.done.length>f.undoDepth;)Tb.done.shift()}rc(a,b,c,d,e)}}function oc(a,b){if(a.length){for(var c=a.pop(),d=[],e=c.length-1;e>=0;e-=1){var f=c[e],g=[],h=f.start+f.added;sb.iter(f.start,h,function(a){g.push(a.text)}),d.push({start:f.start,added:f.old.length,old:g});var i=Tc({line:f.start+f.old.length-1,ch:bb(g[g.length-1],f.old[f.old.length-1])});rc({line:f.start,ch:0},{line:h-1,ch:Xb(h-1).text.length},f.old,i,i)}Db=!0,b.push(d)}}function pc(){oc(Tb.done,Tb.undone)}function qc(){oc(Tb.undone,Tb.done)}function rc(a,b,c,d,e){function y(a){return a<=Math.min(b.line,b.line+s)?a:a+s}if(!Cb){var g=!1,h=Qb.length;f.lineWrapping||sb.iter(a.line,b.line,function(a){return a.text.length==h?(g=!0,!0):void 0}),(a.line!=b.line||c.length>1)&&(Jb=!0);var i=b.line-a.line,j=Xb(a.line),k=Xb(b.line);if(0==a.ch&&0==b.ch&&""==c[c.length-1]){var l=[],m=null;a.line?(m=Xb(a.line-1),m.fixMarkEnds(k)):k.fixMarkStarts();for(var n=0,o=c.length-1;o>n;++n)l.push(r.inheritMarks(c[n],m));i&&sb.remove(a.line,i,Kb),l.length&&sb.insert(a.line,l)}else if(j==k)if(1==c.length)j.replace(a.ch,b.ch,c[0]);else{k=j.split(b.ch,c[c.length-1]),j.replace(a.ch,null,c[0]),j.fixMarkEnds(k);for(var l=[],n=1,o=c.length-1;o>n;++n)l.push(r.inheritMarks(c[n],j));l.push(k),sb.insert(a.line+1,l)}else if(1==c.length)j.replace(a.ch,null,c[0]),k.replace(null,b.ch,""),j.append(k),sb.remove(a.line+1,i,Kb);else{var l=[];j.replace(a.ch,null,c[0]),k.replace(null,b.ch,c[c.length-1]),j.fixMarkEnds(k);for(var n=1,o=c.length-1;o>n;++n)l.push(r.inheritMarks(c[n],j));i>1&&sb.remove(a.line+1,i-1,Kb),sb.insert(a.line+1,l)}if(f.lineWrapping){var p=S.clientWidth/Dd()-3;sb.iter(a.line,a.line+c.length,function(a){if(!a.hidden){var b=Math.ceil(a.text.length/p)||1;b!=a.height&&Yb(a,b)}})}else sb.iter(a.line,n+c.length,function(a){var b=a.text;b.length>h&&(Qb=b,h=b.length,Rb=null,g=!1)}),g&&(h=0,Qb="",Rb=null,sb.iter(0,sb.size,function(a){var b=a.text;b.length>h&&(h=b.length,Qb=b)}));for(var q=[],s=c.length-i-1,n=0,t=tb.length;t>n;++n){var u=tb[n];u<a.line?q.push(u):u>b.line&&q.push(u+s)}var v=a.line+Math.min(c.length,500);Nd(a.line,v),q.push(v),tb=q,Pd(100),Fb.push({from:a.line,to:b.line+1,diff:s});var w={from:a,to:b,text:c};if(Gb){for(var x=Gb;x.next;x=x.next);x.next=w}else Gb=w;Pc(d,e,y(vb.from.line),y(vb.to.line)),S.clientHeight&&(T.style.height=sb.height*Ad()+2*Ed()+"px")}}function sc(a,b,c){function d(d){if(Z(d,b))return d;if(!Z(c,d))return e;var f=d.line+a.length-(c.line-b.line)-1,g=d.ch;return d.line==c.line&&(g+=a[a.length-1].length-(c.ch-(c.line==b.line?b.ch:0))),{line:f,ch:g}}b=Tc(b),c=c?Tc(c):b,a=eb(a);var e;return uc(a,b,c,function(a){return e=a,{from:d(vb.from),to:d(vb.to)}}),e}function tc(a,b){uc(eb(a),vb.from,vb.to,function(a){return"end"==b?{from:a,to:a}:"start"==b?{from:vb.from,to:vb.from}:{from:vb.from,to:a}})}function uc(a,b,c,d){var e=1==a.length?a[0].length+b.ch:a[a.length-1].length,f=d({line:b.line+a.length-1,ch:e});nc(b,c,a,f.from,f.to)}function vc(a,b){var c=a.line,d=b.line;if(c==d)return Xb(c).text.slice(a.ch,b.ch);var e=[Xb(c).text.slice(a.ch)];return sb.iter(c+1,d,function(a){e.push(a.text)}),e.push(Xb(d).text.slice(0,b.ch)),e.join("\n")}function wc(){return vc(vb.from,vb.to)}function yc(){xc||ob.set(f.pollInterval,function(){Qd(),Bc(),ub&&yc(),Rd()})}function zc(){function b(){Qd();var c=Bc();c||a?(xc=!1,yc()):(a=!0,ob.set(60,b)),Rd()}var a=!1;xc=!0,ob.set(20,b)}function Bc(){if(Ib||!ub||fb(D)||f.readOnly)return!1;var a=D.value;if(a==Ac)return!1;wb=null;for(var b=0,c=Math.min(Ac.length,a.length);c>b&&Ac[b]==a[b];)++b;return b<Ac.length?vb.from={line:vb.from.line,ch:vb.from.ch-(Ac.length-b)}:Bb&&Y(vb.from,vb.to)&&(vb.to={line:vb.to.line,ch:Math.min(Xb(vb.to.line).text.length,vb.to.ch+(a.length-b))}),tc(a.slice(b),"end"),Ac=a,!0}function Cc(a){Y(vb.from,vb.to)?a&&(Ac=D.value=""):(Ac="",D.value=wc(),X(D))}function Dc(){"nocursor"!=f.readOnly&&D.focus()}function Ec(){if(kb.getBoundingClientRect){var a=kb.getBoundingClientRect();if(!M||a.top!=a.bottom){var b=window.innerHeight||Math.max(document.body.offsetHeight,document.documentElement.offsetHeight);(a.top<0||a.bottom>b)&&kb.scrollIntoView()}}}function Fc(){var a=ud(vb.inverted?vb.from:vb.to),b=f.lineWrapping?Math.min(a.x,ib.offsetWidth):a.x;return Gc(b,a.y,b,a.yBot)}function Gc(a,b,c,d){var e=Fd(),g=Ed();b+=g,d+=g,a+=e,c+=e;var h=S.clientHeight,i=S.scrollTop,j=!1,k=!0;i>b?(S.scrollTop=Math.max(0,b),j=!0):d>i+h&&(S.scrollTop=d-h,j=!0);var l=S.clientWidth,m=S.scrollLeft,n=f.fixedGutter?_.clientWidth:0;return m+n>a?(50>a&&(a=0),S.scrollLeft=Math.max(0,a-10-n),j=!0):c>l+m-3&&(S.scrollLeft=c+10-l,j=!0,c>T.clientWidth&&(k=!1)),j&&f.onScroll&&f.onScroll(Wb),k}function Hc(){var a=Ad(),b=S.scrollTop-Ed(),c=Math.max(0,Math.floor(b/a)),d=Math.ceil((b+S.clientHeight)/a);return{from:x(sb,c),to:x(sb,d)}}function Ic(a,b){function n(){Rb=S.clientWidth;var a=mb.firstChild,b=!1;return sb.iter(Mb,Nb,function(c){if(!c.hidden){var d=Math.round(a.offsetHeight/k)||1;c.height!=d&&(Yb(c,d),Jb=b=!0)}a=a.nextSibling}),b&&(T.style.height=sb.height*k+2*Ed()+"px"),b}if(!S.clientWidth)return Mb=Nb=Lb=0,void 0;var c=Hc();if(!(a!==!0&&0==a.length&&c.from>Mb&&c.to<Nb)){var d=Math.max(c.from-100,0),e=Math.min(sb.size,c.to+100);d>Mb&&20>d-Mb&&(d=Mb),Nb>e&&20>Nb-e&&(e=Math.min(sb.size,Nb));for(var g=a===!0?[]:Jc([{from:Mb,to:Nb,domStart:0}],a),h=0,i=0;i<g.length;++i){var j=g[i];j.from<d&&(j.domStart+=d-j.from,j.from=d),j.to>e&&(j.to=e),j.from>=j.to?g.splice(i--,1):h+=j.to-j.from}if(h!=e-d){g.sort(function(a,b){return a.domStart-b.domStart});var k=Ad(),l=_.style.display;mb.style.display="none",Kc(d,e,g),mb.style.display=_.style.display="";var m=d!=Mb||e!=Nb||Ob!=S.clientHeight+k;if(m&&(Ob=S.clientHeight+k),Mb=d,Nb=e,Lb=y(sb,d),U.style.top=Lb*k+"px",S.clientHeight&&(T.style.height=sb.height*k+2*Ed()+"px"),mb.childNodes.length!=Nb-Mb)throw new Error("BAD PATCH! "+JSON.stringify(g)+" size="+(Nb-Mb)+" nodes="+mb.childNodes.length);return f.lineWrapping?n():(null==Rb&&(Rb=qd(Qb)),Rb>S.clientWidth?(ib.style.width=Rb+"px",T.style.width="",T.style.width=S.scrollWidth+"px"):ib.style.width=T.style.width=""),_.style.display=l,(m||Jb)&&Lc()&&f.lineWrapping&&n()&&Lc(),Mc(),!b&&f.onUpdate&&f.onUpdate(Wb),!0}}}function Jc(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,domStart:j.domStart}):e.to<=j.from||e.from>=j.to?f.push(j):(e.from>j.from&&f.push({from:j.from,to:e.from,domStart:j.domStart}),e.to<j.to&&f.push({from:e.to+g,to:j.to+g,domStart:j.domStart+(e.to-j.from)}))}a=f}return a}function Kc(a,b,c){function d(a){var b=a.nextSibling;return a.parentNode.removeChild(a),b}if(c.length){for(var e=0,f=mb.firstChild,h=0;h<c.length;++h){for(var i=c[h];i.domStart>e;)f=d(f),e++;for(var j=0,k=i.to-i.from;k>j;++j)f=f.nextSibling,e++}for(;f;)f=d(f)}else mb.innerHTML="";var l=c.shift(),f=mb.firstChild,j=a,m=document.createElement("div");sb.iter(a,b,function(a){if(l&&l.to==j&&(l=c.shift()),!l||l.from>j){if(a.hidden)var b=m.innerHTML="<pre></pre>";else{var b="<pre"+(a.className?' class="'+a.className+'"':"")+">"+a.getHTML(ed)+"</pre>";a.bgClassName&&(b='<div style="position: relative"><pre class="'+a.bgClassName+'" style="position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: -2">&#160;</pre>'+b+"</div>")}m.innerHTML=b,mb.insertBefore(m.firstChild,f)}else f=f.nextSibling;++j})}function Lc(){if(f.gutter||f.lineNumbers){var a=U.offsetHeight,b=S.clientHeight;_.style.height=(2>a-b?b:a)+"px";var e,c=[],d=Mb;if(sb.iter(Mb,Math.max(Nb,Mb+1),function(a){if(a.hidden)c.push("<pre></pre>");else{var b=a.gutterMarker,g=f.lineNumbers?d+f.firstLineNumber:null;b&&b.text?g=b.text.replace("%N%",null!=g?g:""):null==g&&(g="\xa0"),c.push(b&&b.style?'<pre class="'+b.style+'">':"<pre>",g);for(var h=1;h<a.height;++h)c.push("<br/>&#160;");c.push("</pre>"),b||(e=d)}++d}),_.style.display="none",hb.innerHTML=c.join(""),null!=e){for(var g=hb.childNodes[e-Mb],h=String(sb.size).length,i=W(g),j="";i.length+j.length<h;)j+="\xa0";j&&g.insertBefore(document.createTextNode(j),g.firstChild)}_.style.display="";var k=Math.abs((parseInt(ib.style.marginLeft)||0)-_.offsetWidth)>2;return ib.style.marginLeft=_.offsetWidth+"px",Jb=!1,k}}function Mc(){function k(a,b,c,d){j+='<div class="CodeMirror-selected" style="position: absolute; left: '+a+"px; top: "+b+"px; right: "+c+"px; height: "+d+'px"></div>'}var a=Y(vb.from,vb.to),b=ud(vb.from,!0),c=a?b:ud(vb.to,!0),d=vb.inverted?b:c,e=Ad(),g=V(s),h=V(mb);if(A.style.top=Math.max(0,Math.min(S.offsetHeight,d.y+h.top-g.top))+"px",A.style.left=Math.max(0,Math.min(S.offsetWidth,d.x+h.left-g.left))+"px",a)kb.style.top=d.y+"px",kb.style.left=(f.lineWrapping?Math.min(d.x,ib.offsetWidth):d.x)+"px",kb.style.display="",lb.style.display="none";else{var i=b.y==c.y,j="",l=ib.clientWidth||ib.offsetWidth,m=ib.clientHeight||ib.offsetHeight;if(vb.from.ch&&b.y>=0){var n=i?l-c.x:0;k(b.x,b.y,n,e)}var o=Math.max(0,b.y+(vb.from.ch?e:0)),p=Math.min(c.y,m)-o;p>.2*e&&k(0,o,0,p),i&&vb.from.ch||!(c.y<m-.5*e)||k(0,c.y,l-c.x,e),lb.innerHTML=j,kb.style.display="none",lb.style.display=""}}function Nc(a){wb=a?wb||(vb.inverted?vb.to:vb.from):null}function Oc(a,b){var c=wb&&Tc(wb);c&&(Z(c,a)?a=c:Z(b,c)&&(b=c)),Pc(a,b),Eb=!0}function Pc(a,b,c,d){if(Xc=null,null==c&&(c=vb.from.line,d=vb.to.line),!Y(vb.from,a)||!Y(vb.to,b)){if(Z(b,a)){var e=b;b=a,a=e}if(a.line!=c){var g=Qc(a,c,vb.from.ch);g?a=g:od(a.line,!1)}if(b.line!=d&&(b=Qc(b,d,vb.to.ch)),Y(a,b)?vb.inverted=!1:Y(a,vb.to)?vb.inverted=!1:Y(b,vb.from)&&(vb.inverted=!0),f.autoClearEmptyLines&&Y(vb.from,vb.to)){var h=vb.inverted?a:b;if(h.line!=vb.from.line&&vb.from.line<sb.size){var i=Xb(vb.from.line);/^\s+$/.test(i.text)&&setTimeout(Td(function(){if(i.parent&&/^\s+$/.test(i.text)){var a=w(i);sc("",{line:a,ch:0},{line:a,ch:i.text.length})}},10))}}vb.from=a,vb.to=b,Hb=!0}}function Qc(a,b,c){function d(b){for(var d=a.line+b,e=1==b?sb.size:-1;d!=e;){var f=Xb(d);if(!f.hidden){var g=a.ch;return(g>c||g>f.text.length)&&(g=f.text.length),{line:d,ch:g}}d+=b}}var e=Xb(a.line);return e.hidden?a.line>=b?d(1)||d(-1):d(-1)||d(1):a}function Rc(a,b,c){var d=Tc({line:a,ch:b||0});(c?Oc:Pc)(d,d)}function Sc(a){return Math.max(0,Math.min(a,sb.size-1))}function Tc(a){if(a.line<0)return{line:0,ch:0};if(a.line>=sb.size)return{line:sb.size-1,ch:Xb(sb.size-1).text.length};var b=a.ch,c=Xb(a.line).text.length;return null==b||b>c?{line:a.line,ch:c}:0>b?{line:a.line,ch:0}:a}function Uc(a,b){function g(){for(var b=d+a,c=0>a?-1:sb.size;b!=c;b+=a){var e=Xb(b);if(!e.hidden)return d=b,f=e,!0}}function h(b){if(e==(0>a?0:f.text.length)){if(b||!g())return!1;e=0>a?f.text.length:0}else e+=a;return!0}var c=vb.inverted?vb.from:vb.to,d=c.line,e=c.ch,f=Xb(d);if("char"==b)h();else if("column"==b)h(!0);else if("word"==b)for(var i=!1;!(0>a)||h();){if(db(f.text.charAt(e)))i=!0;else if(i){0>a&&(a=1,h());break}if(a>0&&!h())break}return{line:d,ch:e}}function Vc(a,b){var c=0>a?vb.from:vb.to;(wb||Y(vb.from,vb.to))&&(c=Uc(a,b)),Rc(c.line,c.ch,!0)}function Wc(a,b){Y(vb.from,vb.to)?0>a?sc("",Uc(a,b),vb.to):sc("",vb.from,Uc(a,b)):sc("",vb.from,vb.to),Eb=!0}function Yc(a,b){var c=0,d=ud(vb.inverted?vb.from:vb.to,!0);null!=Xc&&(d.x=Xc),"page"==b?c=Math.min(S.clientHeight,window.innerHeight||document.documentElement.clientHeight):"line"==b&&(c=Ad());var e=vd(d.x,d.y+c*a+2);"page"==b&&(S.scrollTop+=ud(e,!0).y-d.y),Rc(e.line,e.ch,!0),Xc=d.x}function Zc(a){for(var b=Xb(a.line).text,c=a.ch,d=a.ch;c>0&&db(b.charAt(c-1));)--c;for(;d<b.length&&db(b.charAt(d));)++d;Oc({line:a.line,ch:c},{line:a.line,ch:d})}function $c(a){Oc({line:a,ch:0},Tc({line:a+1,ch:0}))}function _c(a){if(Y(vb.from,vb.to))return ad(vb.from.line,a);for(var b=vb.to.line-(vb.to.ch?0:1),c=vb.from.line;b>=c;++c)ad(c,a)}function ad(a,b){if(b||(b="add"),"smart"==b)if(rb.indent)var c=Md(a);else b="prev";var h,d=Xb(a),e=d.indentation(f.tabSize),g=d.text.match(/^\s*/)[0];"prev"==b?h=a?Xb(a-1).indentation(f.tabSize):0:"smart"==b?h=rb.indent(c,d.text.slice(g.length),d.text):"add"==b?h=e+f.indentUnit:"subtract"==b&&(h=e-f.indentUnit),h=Math.max(0,h);var i=h-e;if(i){var j="",k=0;if(f.indentWithTabs)for(var l=Math.floor(h/f.tabSize);l;--l)k+=f.tabSize,j+=" ";for(;h>k;)++k,j+=" "}else{if(vb.from.line!=a&&vb.to.line!=a)return;var j=g}sc(j,{line:a,ch:0},{line:a,ch:g.length})}function bd(){rb=a.getMode(f,f.mode),sb.iter(0,sb.size,function(a){a.stateAfter=null}),tb=[0],Pd()}function cd(){var a=f.gutter||f.lineNumbers;_.style.display=a?"":"none",a?Jb=!0:mb.parentNode.style.marginLeft=0}function dd(){if(f.lineWrapping){s.className+=" CodeMirror-wrap";var c=S.clientWidth/Dd()-3;sb.iter(0,sb.size,function(a){if(!a.hidden){var b=Math.ceil(a.text.length/c)||1;1!=b&&Yb(a,b)}}),ib.style.width=T.style.width=""}else s.className=s.className.replace(" CodeMirror-wrap",""),Rb=null,Qb="",sb.iter(0,sb.size,function(a){1==a.height||a.hidden||Yb(a,1),a.text.length>Qb.length&&(Qb=a.text)});Fb.push({from:0,to:sb.size})}function ed(a){var b=f.tabSize-a%f.tabSize,c=Sb[b];if(c)return c;for(var d='<span class="cm-tab">',e=0;b>e;++e)d+=" ";return Sb[b]={html:d+"</span>",width:b}}function fd(){S.className=S.className.replace(/\s*cm-s-\w+/g,"")+f.theme.replace(/(^|\s)\s*/g," cm-s-")}function gd(){this.set=[]}function hd(a,b,c){function e(a,b,c,e){Xb(a).addMark(new p(b,c,e,d))}a=Tc(a),b=Tc(b);var d=new gd;if(!Z(a,b))return d;if(a.line==b.line)e(a.line,a.ch,b.ch,c);else{e(a.line,a.ch,null,c);for(var f=a.line+1,g=b.line;g>f;++f)e(f,null,null,c);e(b.line,null,b.ch,c)}return Fb.push({from:a.line,to:b.line+1}),d}function id(a){a=Tc(a);var b=new q(a.ch);return Xb(a.line).addMark(b),b}function jd(a){a=Tc(a);var b=[],c=Xb(a.line).marked;if(!c)return b;for(var d=0,e=c.length;e>d;++d){var f=c[d];(null==f.from||f.from<=a.ch)&&(null==f.to||f.to>=a.ch)&&b.push(f.marker||f)}return b}function kd(a,b,c){return"number"==typeof a&&(a=Xb(Sc(a))),a.gutterMarker={text:b,style:c},Jb=!0,a}function ld(a){"number"==typeof a&&(a=Xb(Sc(a))),a.gutterMarker=null,Jb=!0}function md(a,b){var c=a,d=a;return"number"==typeof a?d=Xb(Sc(a)):c=w(a),null==c?null:b(d,c)?(Fb.push({from:c,to:c+1}),d):null}function nd(a,b,c){return md(a,function(a){return a.className!=b||a.bgClassName!=c?(a.className=b,a.bgClassName=c,!0):void 0})}function od(a,b){return md(a,function(a,c){if(a.hidden!=b){a.hidden=b,Yb(a,b?0:1);var d=vb.from.line,e=vb.to.line;if(b&&(d==c||e==c)){var f=d==c?Qc({line:d,ch:0},d,0):vb.from,g=e==c?Qc({line:e,ch:0},e,0):vb.to;if(!g)return;Pc(f,g)}return Jb=!0}})}function pd(a){if("number"==typeof a){if(!Vb(a))return null;var b=a;if(a=Xb(a),!a)return null}else{var b=w(a);if(null==b)return null}var c=a.gutterMarker;return{line:b,handle:a,text:a.text,markerText:c&&c.text,markerClass:c&&c.style,lineClass:a.className,bgClass:a.bgClassName}}function qd(a){return jb.innerHTML="<pre><span>x</span></pre>",jb.firstChild.firstChild.firstChild.nodeValue=a,jb.firstChild.firstChild.offsetWidth||10}function td(a,b){if(0==b)return{top:0,left:0};var c="";if(f.lineWrapping){var d=a.text.indexOf(" ",b+6);c=ab(a.text.slice(b+1,0>d?a.text.length:d+(M?5:0)))}jb.innerHTML="<pre>"+a.getHTML(ed,b)+'<span id="CodeMirror-temp-'+sd+'">'+ab(a.text.charAt(b)||" ")+"</span>"+c+"</pre>";var e=document.getElementById("CodeMirror-temp-"+sd),g=e.offsetTop,h=e.offsetLeft;if(M&&0==g&&0==h){var i=document.createElement("span");i.innerHTML="x",e.parentNode.insertBefore(i,e.nextSibling),g=i.offsetTop}return{top:g,left:h}}function ud(a,b){var c,d=Ad(),e=d*(y(sb,a.line)-(b?Lb:0));if(0==a.ch)c=0;else{var g=td(Xb(a.line),a.ch);c=g.left,f.lineWrapping&&(e+=Math.max(0,g.top))}return{x:c,y:e,yBot:e+d}}function vd(a,b){function l(a){var b=td(h,a);if(j){var d=Math.round(b.top/c);return Math.max(0,b.left+(d-k)*S.clientWidth)}return b.left}0>b&&(b=0);var c=Ad(),d=Dd(),e=Lb+Math.floor(b/c),g=x(sb,e);if(g>=sb.size)return{line:sb.size-1,ch:Xb(sb.size-1).text.length};var h=Xb(g),i=h.text,j=f.lineWrapping,k=j?e-y(sb,g):0;if(0>=a&&0==k)return{line:g,ch:0};for(var p,m=0,n=0,o=i.length,q=Math.min(o,Math.ceil((a+.9*k*S.clientWidth)/d));;){var r=l(q);if(!(a>=r&&o>q)){p=r,o=q;break}q=Math.min(o,Math.ceil(1.2*q))}if(a>p)return{line:g,ch:o};for(q=Math.floor(.8*o),r=l(q),a>r&&(m=q,n=r);;){if(1>=o-m)return{line:g,ch:p-a>a-n?m:o};var s=Math.ceil((m+o)/2),t=l(s);t>a?(o=s,p=t):(m=s,n=t)}}function wd(a){var b=ud(a,!0),c=V(ib);return{x:c.left+b.x,y:c.top+b.y,yBot:c.top+b.yBot}}function Ad(){if(null==zd){zd="<pre>";for(var a=0;49>a;++a)zd+="x<br/>";zd+="x</pre>"}var b=mb.clientHeight;return b==yd?xd:(yd=b,jb.innerHTML=zd,xd=jb.firstChild.offsetHeight/50||1,jb.innerHTML="",xd)}function Dd(){return S.clientWidth==Cd?Bd:(Cd=S.clientWidth,Bd=qd("x"))}function Ed(){return ib.offsetTop}function Fd(){return ib.offsetLeft}function Gd(a,b){var d,e,c=V(S,!0);try{d=a.clientX,e=a.clientY}catch(a){return null}if(!b&&(d-c.left>S.clientWidth||e-c.top>S.clientHeight))return null;var f=V(ib,!0);return vd(d-f.left,e-f.top)}function Hd(a){function f(){var a=eb(D.value).join("\n");a!=e&&Td(tc)(a,"end"),A.style.position="relative",D.style.cssText=d,N&&(S.scrollTop=c),Ib=!1,Cc(!0),yc()}var b=Gd(a),c=S.scrollTop;if(b&&!window.opera){(Y(vb.from,vb.to)||Z(b,vb.from)||!Z(b,vb.to))&&Td(Rc)(b.line,b.ch);var d=D.style.cssText;A.style.position="absolute",D.style.cssText="position: fixed; width: 30px; height: 30px; top: "+(a.clientY-5)+"px; left: "+(a.clientX-5)+"px; z-index: 1000; background: white; "+"border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);",Ib=!0;var e=D.value=wc();if(Dc(),X(D),L){E(a);var g=I(window,"mouseup",function(){g(),setTimeout(f,20)},!0)}else setTimeout(f,50)}}function Id(){clearInterval(qb);var a=!0;kb.style.visibility="",qb=setInterval(function(){kb.style.visibility=(a=!a)?"":"hidden"},650)}function Kd(a){function p(a,b,c){if(a.text)for(var f,d=a.styles,e=g?0:a.text.length-1,i=g?0:d.length-2,j=g?d.length:-2;i!=j;i+=2*h){var k=d[i];if(null==d[i+1]||d[i+1]==m){for(var l=g?0:k.length-1,p=g?k.length:-1;l!=p;l+=h,e+=h)if(e>=b&&c>e&&o.test(f=k.charAt(l))){var q=Jd[f];if(">"==q.charAt(1)==g)n.push(f);else{if(n.pop()!=q.charAt(0))return{pos:e,match:!1};if(!n.length)return{pos:e,match:!0}}}}else e+=h*k.length}}var b=vb.inverted?vb.from:vb.to,c=Xb(b.line),d=b.ch-1,e=d>=0&&Jd[c.text.charAt(d)]||Jd[c.text.charAt(++d)];if(e){for(var g=(e.charAt(0),">"==e.charAt(1)),h=g?1:-1,i=c.styles,j=d+1,k=0,l=i.length;l>k;k+=2)if((j-=i[k].length)<=0){var m=i[k+1];break}for(var n=[c.text.charAt(d)],o=/[(){}[\]]/,k=b.line,l=g?Math.min(k+100,sb.size):Math.max(-1,k-100);k!=l;k+=h){var c=Xb(k),q=k==b.line,r=p(c,q&&g?d+1:0,q&&!g?d:c.text.length);if(r)break}r||(r={pos:null,match:!1});var m=r.match?"CodeMirror-matchingbracket":"CodeMirror-nonmatchingbracket",s=hd({line:b.line,ch:d},{line:b.line,ch:d+1},m),t=null!=r.pos&&hd({line:k,ch:r.pos},{line:k,ch:r.pos+1},m),u=Td(function(){s.clear(),t&&t.clear()});a?setTimeout(u,800):Pb=u}}function Ld(a){for(var b,c,d=a,e=a-40;d>e;--d){if(0==d)return 0;var g=Xb(d-1);if(g.stateAfter)return d;var h=g.indentation(f.tabSize);(null==c||b>h)&&(c=d-1,b=h)}return c}function Md(a){var b=Ld(a),c=b&&Xb(b-1).stateAfter;return c=c?m(rb,c):n(rb),sb.iter(b,a,function(a){a.highlight(rb,c,f.tabSize),a.stateAfter=m(rb,c)}),a>b&&Fb.push({from:b,to:a}),a<sb.size&&!Xb(a).stateAfter&&tb.push(a),c}function Nd(a,b){var c=Md(a);sb.iter(a,b,function(a){a.highlight(rb,c,f.tabSize),a.stateAfter=m(rb,c)})}function Od(){for(var a=+new Date+f.workTime,b=tb.length;tb.length;){if(Xb(Mb).stateAfter)var c=tb.pop();else var c=Mb;if(!(c>=sb.size)){var d=Ld(c),e=d&&Xb(d-1).stateAfter;e=e?m(rb,e):n(rb);var g=0,h=rb.compareStates,i=!1,j=d,k=!1;if(sb.iter(j,sb.size,function(b){var d=b.stateAfter;if(+new Date>a)return tb.push(j),Pd(f.workDelay),i&&Fb.push({from:c,to:j+1}),k=!0;var l=b.highlight(rb,e,f.tabSize);if(l&&(i=!0),b.stateAfter=m(rb,e),h){if(d&&h(d,e))return!0}else if(l===!1&&d){if(++g>3&&(!rb.indent||rb.indent(d,"")==rb.indent(e,"")))return!0}else g=0;++j}),k)return;i&&Fb.push({from:c,to:j+1})}}b&&f.onHighlightComplete&&f.onHighlightComplete(Wb)}function Pd(a){tb.length&&pb.set(a,Td(Od))}function Qd(){Db=Eb=Gb=null,Fb=[],Hb=!1,Kb=[]}function Rd(){var b,a=!1;Hb&&(a=!Fc()),Fb.length?b=Ic(Fb,!0):(Hb&&Mc(),Jb&&Lc()),a&&Fc(),Hb&&(Ec(),Id()),ub&&!Ib&&(Db===!0||Db!==!1&&Hb)&&Cc(Eb),Hb&&f.matchBrackets&&setTimeout(Td(function(){Pb&&(Pb(),Pb=null),Y(vb.from,vb.to)&&Kd(!1)}),20);var c=Gb,d=Kb;Hb&&f.onCursorActivity&&f.onCursorActivity(Wb),c&&f.onChange&&Wb&&f.onChange(Wb,c);for(var e=0;e<d.length;++e)d[e](Wb);b&&f.onUpdate&&f.onUpdate(Wb)}function Td(a){return function(){Sd++||Qd();try{var b=a.apply(this,arguments)}finally{--Sd||Rd()}return b}}var f={},i=a.defaults;for(var o in i)i.hasOwnProperty(o)&&(f[o]=(e&&e.hasOwnProperty(o)?e:i)[o]);var s=document.createElement("div");s.className="CodeMirror"+(f.lineWrapping?" CodeMirror-wrap":""),s.innerHTML='<div style="overflow: hidden; position: relative; width: 3px; height: 0px;"><textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" autocorrect="off" autocapitalize="off"></textarea></div><div class="CodeMirror-scroll" tabindex="-1"><div style="position: relative"><div style="position: relative"><div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div><div class="CodeMirror-lines"><div style="position: relative; z-index: 0"><div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;"></div><pre class="CodeMirror-cursor">&#160;</pre><div style="position: relative; z-index: -1"></div><div></div></div></div></div></div></div>',d.appendChild?d.appendChild(s):d(s);var A=s.firstChild,D=A.firstChild,S=s.lastChild,T=S.firstChild,U=T.firstChild,_=U.firstChild,hb=_.firstChild,ib=_.nextSibling.firstChild,jb=ib.firstChild,kb=jb.nextSibling,lb=kb.nextSibling,mb=lb.nextSibling;fd(),b&&(D.style.width="0px"),O||(ib.draggable=!0),ib.style.outline="none",null!=f.tabindex&&(D.tabIndex=f.tabindex),f.autofocus&&Dc(),f.gutter||f.lineNumbers||(_.style.display="none"),Q&&(A.style.height="1px",A.style.position="absolute");try{qd("x")}catch(nb){throw nb.message.match(/runtime/i)&&(nb=new Error("A CodeMirror inside a P-style element does not work in Internet Explorer. (innerHTML bug)")),nb}var qb,rb,tb,ub,ob=new J,pb=new J,sb=new u([new t([new r("")])]);bd();var wb,xb,yb,Ab,Db,Eb,Fb,Gb,Hb,Ib,Jb,Kb,Pb,Rb,vb={from:{line:0,ch:0},to:{line:0,ch:0},inverted:!1},zb=0,Bb=!1,Cb=!1,Lb=0,Mb=0,Nb=0,Ob=0,Qb="",Sb={};Td(function(){Zb(f.value||""),Db=!1})();var Tb=new z;I(S,"mousedown",Td(_b)),I(S,"dblclick",Td(ac)),I(ib,"dragstart",cc),I(ib,"selectstart",C),L||I(S,"contextmenu",Hd),I(S,"scroll",function(){zb=S.scrollTop,Ic([]),f.fixedGutter&&(_.style.left=S.scrollLeft+"px"),f.onScroll&&f.onScroll(Wb)}),I(window,"resize",function(){Ic(!0)}),I(D,"keyup",Td(kc)),I(D,"input",zc),I(D,"keydown",Td(ic)),I(D,"keypress",Td(jc)),I(D,"focus",lc),I(D,"blur",mc),I(S,"dragenter",E),I(S,"dragover",E),I(S,"drop",Td(bc)),I(S,"paste",function(){Dc(),zc()}),I(D,"paste",zc),I(D,"cut",Td(function(){f.readOnly||tc("")})),Q&&I(T,"mouseup",function(){document.activeElement==D&&D.blur(),Dc()});var Ub;try{Ub=document.activeElement==D}catch(nb){}Ub||f.autofocus?setTimeout(lc,20):mc();var hc,Wb=s.CodeMirror={getValue:$b,setValue:Td(Zb),getSelection:wc,replaceSelection:Td(tc),focus:function(){window.focus(),Dc(),lc(),zc()},setOption:function(a,b){var c=f[a];f[a]=b,"mode"==a||"indentUnit"==a?bd():"readOnly"==a&&"nocursor"==b?(mc(),D.blur()):"readOnly"!=a||b?"theme"==a?fd():"lineWrapping"==a&&c!=b?Td(dd)():"tabSize"==a&&Ic(!0):Cc(!0),("lineNumbers"==a||"gutter"==a||"firstLineNumber"==a||"theme"==a)&&(cd(),Ic(!0))},getOption:function(a){return f[a]},undo:Td(pc),redo:Td(qc),indentLine:Td(function(a,b){"string"!=typeof b&&(b=null==b?f.smartIndent?"smart":"prev":b?"add":"subtract"),Vb(a)&&ad(a,b)}),indentSelection:Td(_c),historySize:function(){return{undo:Tb.done.length,redo:Tb.undone.length}},clearHistory:function(){Tb=new z},matchBrackets:Td(function(){Kd(!0)}),getTokenAt:Td(function(a){return a=Tc(a),Xb(a.line).getTokenAt(rb,Md(a.line),a.ch)}),getStateAfter:function(a){return a=Sc(null==a?sb.size-1:a),Md(a+1)},cursorCoords:function(a,b){return null==a&&(a=vb.inverted),this.charCoords(a?vb.from:vb.to,b)},charCoords:function(a,b){return a=Tc(a),"local"==b?ud(a,!1):"div"==b?ud(a,!0):wd(a)},coordsChar:function(a){var b=V(ib);return vd(a.x-b.left,a.y-b.top)},markText:Td(hd),setBookmark:id,findMarksAt:jd,setMarker:Td(kd),clearMarker:Td(ld),setLineClass:Td(nd),hideLine:Td(function(a){return od(a,!0)}),showLine:Td(function(a){return od(a,!1)}),onDeleteLine:function(a,b){if("number"==typeof a){if(!Vb(a))return null;a=Xb(a)}return(a.handlers||(a.handlers=[])).push(b),a},lineInfo:pd,addWidget:function(a,b,c,d,e){a=ud(Tc(a));var f=a.yBot,g=a.x;if(b.style.position="absolute",T.appendChild(b),"over"==d)f=a.y;else if("near"==d){var h=Math.max(S.offsetHeight,sb.height*Ad()),i=Math.max(T.clientWidth,ib.clientWidth)-Fd();a.yBot+b.offsetHeight>h&&a.y>b.offsetHeight&&(f=a.y-b.offsetHeight),g+b.offsetWidth>i&&(g=i-b.offsetWidth)}b.style.top=f+Ed()+"px",b.style.left=b.style.right="","right"==e?(g=T.clientWidth-b.offsetWidth,b.style.right="0px"):("left"==e?g=0:"middle"==e&&(g=(T.clientWidth-b.offsetWidth)/2),b.style.left=g+Fd()+"px"),c&&Gc(g,f,g+b.offsetWidth,f+b.offsetHeight)},lineCount:function(){return sb.size},clipPos:Tc,getCursor:function(a){return null==a&&(a=vb.inverted),$(a?vb.from:vb.to)},somethingSelected:function(){return!Y(vb.from,vb.to)},setCursor:Td(function(a,b,c){null==b&&"number"==typeof a.line?Rc(a.line,a.ch,c):Rc(a,b,c)}),setSelection:Td(function(a,b,c){(c?Oc:Pc)(Tc(a),Tc(b||a))}),getLine:function(a){return Vb(a)?Xb(a).text:void 0},getLineHandle:function(a){return Vb(a)?Xb(a):void 0},setLine:Td(function(a,b){Vb(a)&&sc(b,{line:a,ch:0},{line:a,ch:Xb(a).text.length})}),removeLine:Td(function(a){Vb(a)&&sc("",{line:a,ch:0},Tc({line:a+1,ch:0}))}),replaceRange:Td(sc),getRange:function(a,b){return vc(Tc(a),Tc(b))},triggerOnKeyDown:Td(ic),execCommand:function(a){return h[a](Wb)},moveH:Td(Vc),deleteH:Td(Wc),moveV:Td(Yc),toggleOverwrite:function(){Bb?(Bb=!1,kb.className=kb.className.replace(" CodeMirror-overwrite","")):(Bb=!0,kb.className+=" CodeMirror-overwrite")},posFromIndex:function(a){var c,b=0;return sb.iter(0,sb.size,function(d){var e=d.text.length+1;return e>a?(c=a,!0):(a-=e,++b,void 0)}),Tc({line:b,ch:c})},indexFromPos:function(a){if(a.line<0||a.ch<0)return 0;var b=a.ch;return sb.iter(0,a.line,function(a){b+=a.text.length+1}),b},scrollTo:function(a,b){null!=a&&(S.scrollLeft=a),null!=b&&(S.scrollTop=b),Ic([])},operation:function(a){return Td(a)()},refresh:function(){Ic(!0),S.scrollHeight>zb&&(S.scrollTop=zb)},getInputField:function(){return D},getWrapperElement:function(){return s},getScrollerElement:function(){return S},getGutterElement:function(){return _}},gc=null,xc=!1,Ac="",Xc=null;gd.prototype.clear=Td(function(){for(var a=1/0,b=-1/0,c=0,d=this.set.length;d>c;++c){var e=this.set[c],f=e.marked;if(f&&e.parent){var g=w(e);a=Math.min(a,g),b=Math.max(b,g);for(var h=0;h<f.length;++h)f[h].marker==this&&f.splice(h--,1)}}1/0!=a&&Fb.push({from:a,to:b+1})}),gd.prototype.find=function(){for(var a,b,c=0,d=this.set.length;d>c;++c)for(var e=this.set[c],f=e.marked,g=0;g<f.length;++g){var h=f[g];if(h.marker==this&&(null!=h.from||null!=h.to)){var i=w(e);null!=i&&(null!=h.from&&(a={line:i,ch:h.from}),null!=h.to&&(b={line:i,ch:h.to}))}}return{from:a,to:b}};var xd,yd,zd,Bd,sd=Math.floor(16777215*Math.random()).toString(16),Cd=0,Jd={"(":")>",")":"(<","[":"]>","]":"[<","{":"}>","}":"{<"},Sd=0;
for(var Ud in g)g.propertyIsEnumerable(Ud)&&!Wb.propertyIsEnumerable(Ud)&&(Wb[Ud]=g[Ud]);return Wb}function j(a){return"string"==typeof a?i[a]:a}function k(a,b,c,d){function e(b){b=j(b);var c=b[a];if(null!=c&&d(c))return!0;if(b.catchall)return d(b.catchall);var f=b.fallthrough;if(null==f)return!1;if("[object Array]"!=Object.prototype.toString.call(f))return e(f);for(var g=0,h=f.length;h>g;++g)if(e(f[g]))return!0;return!1}return b&&e(b)?!0:e(c)}function l(a){var b=gb[H(a,"keyCode")];return"Ctrl"==b||"Alt"==b||"Shift"==b||"Mod"==b}function m(a,b){if(b===!0)return b;if(a.copyState)return a.copyState(b);var c={};for(var d in b){var e=b[d];e instanceof Array&&(e=e.concat([])),c[d]=e}return c}function n(a,b,c){return a.startState?a.startState(b,c):!0}function o(a,b){this.pos=this.start=0,this.string=a,this.tabSize=b||8}function p(a,b,c,d){this.from=a,this.to=b,this.style=c,this.marker=d}function q(a){this.from=a,this.to=a,this.line=null}function r(a,b){this.styles=b||[a,null],this.text=a,this.height=1,this.marked=this.gutterMarker=this.className=this.bgClassName=this.handlers=null,this.stateAfter=this.parent=this.hidden=null}function s(a,b,c,d){for(var e=0,f=0,g=0;b>f;e+=2){var h=c[e],i=f+h.length;0==g?(i>a&&d.push(h.slice(a-f,Math.min(h.length,b-f)),c[e+1]),i>=a&&(g=1)):1==g&&(i>b?d.push(h.slice(0,b-f),c[e+1]):d.push(h,c[e+1])),f=i}}function t(a){this.lines=a,this.parent=null;for(var b=0,c=a.length,d=0;c>b;++b)a[b].parent=this,d+=a[b].height;this.height=d}function u(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 v(a,b){for(;!a.lines;)for(var c=0;;++c){var d=a.children[c],e=d.chunkSize();if(e>b){a=d;break}b-=e}return a.lines[b]}function w(a){if(null==a.parent)return null;for(var b=a.parent,c=cb(b.lines,a),d=b.parent;d;b=d,d=d.parent){var e=0;for(d.children.length;d.children[e]!=b;++e)c+=d.children[e].chunkSize()}return c}function x(a,b){var c=0;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 y(a,b){var c=0;a:do{for(var d=0,e=a.children.length;e>d;++d){var f=a.children[d],g=f.chunkSize();if(g>b){a=f;continue a}b-=g,c+=f.height}return c}while(!a.lines);for(var d=0;b>d;++d)c+=a.lines[d].height;return c}function z(){this.time=0,this.done=[],this.undone=[]}function A(){E(this)}function B(a){return a.stop||(a.stop=A),a}function C(a){a.preventDefault?a.preventDefault():a.returnValue=!1}function D(a){a.stopPropagation?a.stopPropagation():a.cancelBubble=!0}function E(a){C(a),D(a)}function F(a){return a.target||a.srcElement}function G(a){return a.which?a.which:1&a.button?1:2&a.button?3:4&a.button?2:void 0}function H(a,b){var c=a.override&&a.override.hasOwnProperty(b);return c?a.override[b]:a[b]}function I(a,b,c,d){if("function"==typeof a.addEventListener){if(a.addEventListener(b,c,!1),d)return function(){a.removeEventListener(b,c,!1)}}else{var e=function(a){c(a||window.event)};if(a.attachEvent("on"+b,e),d)return function(){a.detachEvent("on"+b,e)}}}function J(){this.id=null}function T(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 U(a){return a.currentStyle?a.currentStyle:window.getComputedStyle(a,null)}function V(a,b){for(var c=a.ownerDocument.body,d=0,e=0,f=!1,g=a;g;g=g.offsetParent){var h=g.offsetLeft,i=g.offsetTop;g==c?(d+=Math.abs(h),e+=Math.abs(i)):(d+=h,e+=i),b&&"fixed"==U(g).position&&(f=!0)}for(var j=b&&!f?null:c,g=a.parentNode;g!=j;g=g.parentNode)null!=g.scrollLeft&&(d-=g.scrollLeft,e-=g.scrollTop);return{left:d,top:e}}function W(a){return a.textContent||a.innerText||a.nodeValue||""}function X(a){b?(a.selectionStart=0,a.selectionEnd=a.value.length):a.select()}function Y(a,b){return a.line==b.line&&a.ch==b.ch}function Z(a,b){return a.line<b.line||a.line==b.line&&a.ch<b.ch}function $(a){return{line:a.line,ch:a.ch}}function ab(a){return _.textContent=a,_.innerHTML}function bb(a,b){if(!b)return 0;if(!a)return b.length;for(var c=a.length,d=b.length;c>=0&&d>=0&&a.charAt(c)==b.charAt(d);--c,--d);return d+1}function cb(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 db(a){return/\w/.test(a)||a.toUpperCase()!=a.toLowerCase()}a.defaults={value:"",mode:null,theme:"default",indentUnit:2,indentWithTabs:!1,smartIndent:!0,tabSize:4,keyMap:"default",extraKeys:null,electricChars:!0,autoClearEmptyLines:!1,onKeyEvent:null,lineWrapping:!1,lineNumbers:!1,gutter:!1,fixedGutter:!1,firstLineNumber:1,readOnly:!1,onChange:null,onCursorActivity:null,onGutterClick:null,onHighlightComplete:null,onUpdate:null,onFocus:null,onBlur:null,onScroll:null,matchBrackets:!1,workTime:100,workDelay:200,pollInterval:100,undoDepth:40,tabindex:null,autofocus:null};var b=/AppleWebKit/.test(navigator.userAgent)&&/Mobile\/\w+/.test(navigator.userAgent),c=b||/Mac/.test(navigator.platform);/Win/.test(navigator.platform);var e=a.modes={},f=a.mimeModes={};a.defineMode=function(b,c){a.defaults.mode||"null"==b||(a.defaults.mode=b),e[b]=c},a.defineMIME=function(a,b){f[a]=b},a.resolveMode=function(b){if("string"==typeof b&&f.hasOwnProperty(b))b=f[b];else if("string"==typeof b&&/^[\w\-]+\/[\w\-]+\+xml$/.test(b))return a.resolveMode("application/xml");return"string"==typeof b?{name:b}:b||{name:"null"}},a.getMode=function(b,c){var c=a.resolveMode(c),d=e[c.name];return d?d(b,c):(window.console&&console.warn("No mode "+c.name+" found, falling back to plain text."),a.getMode(b,"text/plain"))},a.listModes=function(){var a=[];for(var b in e)e.propertyIsEnumerable(b)&&a.push(b);return a},a.listMIMEs=function(){var a=[];for(var b in f)f.propertyIsEnumerable(b)&&a.push({mime:b,mode:f[b]});return a};var g=a.extensions={};a.defineExtension=function(a,b){g[a]=b};var h=a.commands={selectAll:function(a){a.setSelection({line:0,ch:0},{line:a.lineCount()-1})},killLine:function(a){var b=a.getCursor(!0),c=a.getCursor(!1),d=!Y(b,c);d||a.getLine(b.line).length!=b.ch?a.replaceRange("",b,d?c:{line:b.line}):a.replaceRange("",b,{line:b.line+1,ch:0})},deleteLine:function(a){var b=a.getCursor().line;a.replaceRange("",{line:b,ch:0},{line:b})},undo:function(a){a.undo()},redo:function(a){a.redo()},goDocStart:function(a){a.setCursor(0,0,!0)},goDocEnd:function(a){a.setSelection({line:a.lineCount()-1},null,!0)},goLineStart:function(a){a.setCursor(a.getCursor().line,0,!0)},goLineStartSmart:function(a){var b=a.getCursor(),c=a.getLine(b.line),d=Math.max(0,c.search(/\S/));a.setCursor(b.line,b.ch<=d&&b.ch?0:d,!0)},goLineEnd:function(a){a.setSelection({line:a.getCursor().line},null,!0)},goLineUp:function(a){a.moveV(-1,"line")},goLineDown:function(a){a.moveV(1,"line")},goPageUp:function(a){a.moveV(-1,"page")},goPageDown:function(a){a.moveV(1,"page")},goCharLeft:function(a){a.moveH(-1,"char")},goCharRight:function(a){a.moveH(1,"char")},goColumnLeft:function(a){a.moveH(-1,"column")},goColumnRight:function(a){a.moveH(1,"column")},goWordLeft:function(a){a.moveH(-1,"word")},goWordRight:function(a){a.moveH(1,"word")},delCharLeft:function(a){a.deleteH(-1,"char")},delCharRight:function(a){a.deleteH(1,"char")},delWordLeft:function(a){a.deleteH(-1,"word")},delWordRight:function(a){a.deleteH(1,"word")},indentAuto:function(a){a.indentSelection("smart")},indentMore:function(a){a.indentSelection("add")},indentLess:function(a){a.indentSelection("subtract")},insertTab:function(a){a.replaceSelection(" ","end")},transposeChars:function(a){var b=a.getCursor(),c=a.getLine(b.line);b.ch>0&&b.ch<c.length-1&&a.replaceRange(c.charAt(b.ch)+c.charAt(b.ch-1),{line:b.line,ch:b.ch-1},{line:b.line,ch:b.ch+1})},newlineAndIndent:function(a){a.replaceSelection("\n","end"),a.indentLine(a.getCursor().line)},toggleOverwrite:function(a){a.toggleOverwrite()}},i=a.keyMap={};i.basic={Left:"goCharLeft",Right:"goCharRight",Up:"goLineUp",Down:"goLineDown",End:"goLineEnd",Home:"goLineStartSmart",PageUp:"goPageUp",PageDown:"goPageDown",Delete:"delCharRight",Backspace:"delCharLeft",Tab:"insertTab","Shift-Tab":"indentAuto",Enter:"newlineAndIndent",Insert:"toggleOverwrite"},i.pcDefault={"Ctrl-A":"selectAll","Ctrl-D":"deleteLine","Ctrl-Z":"undo","Shift-Ctrl-Z":"redo","Ctrl-Y":"redo","Ctrl-Home":"goDocStart","Alt-Up":"goDocStart","Ctrl-End":"goDocEnd","Ctrl-Down":"goDocEnd","Ctrl-Left":"goWordLeft","Ctrl-Right":"goWordRight","Alt-Left":"goLineStart","Alt-Right":"goLineEnd","Ctrl-Backspace":"delWordLeft","Ctrl-Delete":"delWordRight","Ctrl-S":"save","Ctrl-F":"find","Ctrl-G":"findNext","Shift-Ctrl-G":"findPrev","Shift-Ctrl-F":"replace","Shift-Ctrl-R":"replaceAll","Ctrl-[":"indentLess","Ctrl-]":"indentMore",fallthrough:"basic"},i.macDefault={"Cmd-A":"selectAll","Cmd-D":"deleteLine","Cmd-Z":"undo","Shift-Cmd-Z":"redo","Cmd-Y":"redo","Cmd-Up":"goDocStart","Cmd-End":"goDocEnd","Cmd-Down":"goDocEnd","Alt-Left":"goWordLeft","Alt-Right":"goWordRight","Cmd-Left":"goLineStart","Cmd-Right":"goLineEnd","Alt-Backspace":"delWordLeft","Ctrl-Alt-Backspace":"delWordRight","Alt-Delete":"delWordRight","Cmd-S":"save","Cmd-F":"find","Cmd-G":"findNext","Shift-Cmd-G":"findPrev","Cmd-Alt-F":"replace","Shift-Cmd-Alt-F":"replaceAll","Cmd-[":"indentLess","Cmd-]":"indentMore",fallthrough:["basic","emacsy"]},i["default"]=c?i.macDefault:i.pcDefault,i.emacsy={"Ctrl-F":"goCharRight","Ctrl-B":"goCharLeft","Ctrl-P":"goLineUp","Ctrl-N":"goLineDown","Alt-F":"goWordRight","Alt-B":"goWordLeft","Ctrl-A":"goLineStart","Ctrl-E":"goLineEnd","Ctrl-V":"goPageUp","Shift-Ctrl-V":"goPageDown","Ctrl-D":"delCharRight","Ctrl-H":"delCharLeft","Alt-D":"delWordRight","Alt-Backspace":"delWordLeft","Ctrl-K":"killLine","Ctrl-T":"transposeChars"},a.fromTextArea=function(b,c){function d(){b.value=h.getValue()}function g(){d(),b.form.submit=f,b.form.submit(),b.form.submit=g}if(c||(c={}),c.value=b.value,!c.tabindex&&b.tabindex&&(c.tabindex=b.tabindex),null==c.autofocus&&null!=b.getAttribute("autofocus")&&(c.autofocus=!0),b.form){var e=I(b.form,"submit",d,!0);if("function"==typeof b.form.submit){var f=b.form.submit;b.form.submit=g}}b.style.display="none";var h=a(function(a){b.parentNode.insertBefore(a,b.nextSibling)},c);return h.save=d,h.getTextArea=function(){return b},h.toTextArea=function(){d(),b.parentNode.removeChild(h.getWrapperElement()),b.style.display="",b.form&&(e(),"function"==typeof b.form.submit&&(b.form.submit=f))},h},a.copyState=m,a.startState=n,o.prototype={eol:function(){return this.pos>=this.string.length},sol:function(){return 0==this.pos},peek:function(){return this.string.charAt(this.pos)},next:function(){return this.pos<this.string.length?this.string.charAt(this.pos++):void 0},eat:function(a){var b=this.string.charAt(this.pos);if("string"==typeof a)var c=b==a;else var c=b&&(a.test?a.test(b):a(b));return c?(++this.pos,b):void 0},eatWhile:function(a){for(var b=this.pos;this.eat(a););return this.pos>b},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 T(this.string,this.start,this.tabSize)},indentation:function(){return T(this.string,null,this.tabSize)},match:function(a,b,c){function d(a){return c?a.toLowerCase():a}if("string"!=typeof a){var e=this.string.slice(this.pos).match(a);return e&&b!==!1&&(this.pos+=e[0].length),e}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)}},a.StringStream=o,p.prototype={attach:function(a){this.marker.set.push(a)},detach:function(a){var b=cb(this.marker.set,a);b>-1&&this.marker.set.splice(b,1)},split:function(a,b){if(this.to<=a&&null!=this.to)return null;var c=this.from<a||null==this.from?null:this.from-a+b,d=null==this.to?null:this.to-a+b;return new p(c,d,this.style,this.marker)},dup:function(){return new p(null,null,this.style,this.marker)},clipTo:function(a,b,c,d,e){a&&d>this.from&&(d<this.to||null==this.to)?this.from=null:null!=this.from&&this.from>=b&&(this.from=Math.max(d,this.from)+e),c&&(b<this.to||null==this.to)&&(b>this.from||null==this.from)?this.to=null:null!=this.to&&this.to>b&&(this.to=d<this.to?this.to+e:b)},isDead:function(){return null!=this.from&&null!=this.to&&this.from>=this.to},sameSet:function(a){return this.marker==a.marker}},q.prototype={attach:function(a){this.line=a},detach:function(a){this.line==a&&(this.line=null)},split:function(a,b){return a<this.from?(this.from=this.to=this.from-a+b,this):void 0},isDead:function(){return this.from>this.to},clipTo:function(a,b,c,d,e){(a||b<this.from)&&(c||d>this.to)?(this.from=0,this.to=-1):this.from>b&&(this.from=this.to=Math.max(d,this.from)+e)},sameSet:function(){return!1},find:function(){return this.line&&this.line.parent?{line:w(this.line),ch:this.from}:null},clear:function(){if(this.line){var a=cb(this.line.marked,this);-1!=a&&this.line.marked.splice(a,1),this.line=null}}},r.inheritMarks=function(a,b){var c=new r(a),d=b&&b.marked;if(d)for(var e=0;e<d.length;++e)if(null==d[e].to&&d[e].style){var f=c.marked||(c.marked=[]),g=d[e],h=g.dup();f.push(h),h.attach(c)}return c},r.prototype={replace:function(a,b,c){var d=[],e=this.marked,f=null==b?this.text.length:b;if(s(0,a,this.styles,d),c&&d.push(c,null),s(f,this.text.length,this.styles,d),this.styles=d,this.text=this.text.slice(0,a)+c+this.text.slice(f),this.stateAfter=null,e)for(var g=c.length-(f-a),h=0;h<e.length;++h){var i=e[h];i.clipTo(null==a,a||0,null==b,f,g),i.isDead()&&(i.detach(this),e.splice(h--,1))}},split:function(a,b){var c=[b,null],d=this.marked;s(a,this.text.length,this.styles,c);var e=new r(b+this.text.slice(a),c);if(d)for(var f=0;f<d.length;++f){var g=d[f],h=g.split(a,b.length);h&&(e.marked||(e.marked=[]),e.marked.push(h),h.attach(e),h==g&&d.splice(f--,1))}return e},append:function(a){var b=this.text.length,c=a.marked,d=this.marked;if(this.text+=a.text,s(0,a.text.length,a.styles,this.styles),d)for(var e=0;e<d.length;++e)null==d[e].to&&(d[e].to=b);if(c&&c.length){d||(this.marked=d=[]);a:for(var e=0;e<c.length;++e){var f=c[e];if(!f.from)for(var g=0;g<d.length;++g){var h=d[g];if(h.to==b&&h.sameSet(f)){h.to=null==f.to?null:f.to+b,h.isDead()&&(h.detach(this),c.splice(e--,1));continue a}}d.push(f),f.attach(this),f.from+=b,null!=f.to&&(f.to+=b)}}},fixMarkEnds:function(a){var b=this.marked,c=a.marked;if(b)for(var d=0;d<b.length;++d){var e=b[d],f=null==e.to;if(f&&c)for(var g=0;g<c.length;++g)if(c[g].sameSet(e)){f=!1;break}f&&(e.to=this.text.length)}},fixMarkStarts:function(){var a=this.marked;if(a)for(var b=0;b<a.length;++b)null==a[b].from&&(a[b].from=0)},addMark:function(a){a.attach(this),null==this.marked&&(this.marked=[]),this.marked.push(a),this.marked.sort(function(a,b){return(a.from||0)-(b.from||0)})},highlight:function(a,b,c){var i,d=new o(this.text,c),e=this.styles,f=0,g=!1,h=e[0];for(""==this.text&&a.blankLine&&a.blankLine(b);!d.eol();){var j=a.token(d,b),k=this.text.slice(d.start,d.pos);if(d.start=d.pos,f&&e[f-1]==j?e[f-2]+=k:k&&(!g&&(e[f+1]!=j||f&&e[f-2]!=i)&&(g=!0),e[f++]=k,e[f++]=j,i=h,h=e[f]),d.pos>5e3){e[f++]=this.text.slice(d.pos),e[f++]=null;break}}return e.length!=f&&(e.length=f,g=!0),f&&e[f-2]!=i&&(g=!0),g||(e.length<5&&this.text.length<10?null:!1)},getTokenAt:function(a,b,c){for(var d=this.text,e=new o(d);e.pos<c&&!e.eol();){e.start=e.pos;var f=a.token(e,b)}return{start:e.start,end:e.pos,string:e.current(),className:f||null,state:b}},indentation:function(a){return T(this.text,null,a)},getHTML:function(a,b){function f(b,f){if(b){if(d&&M&&" "==b.charAt(0)&&(b="\xa0"+b.slice(1)),d=!1,-1==b.indexOf(" ")){e+=b.length;var g=ab(b)}else for(var g="",h=0;;){var i=b.indexOf(" ",h);if(-1==i){g+=ab(b.slice(h)),e+=b.length-h;break}e+=i-h;var j=a(e);g+=ab(b.slice(h,i))+j.html,e+=j.width,h=i+1}f?c.push('<span class="',f,'">',g,"</span>"):c.push(g)}}function k(a){return a?"cm-"+a.replace(/ +/g," cm-"):null}function w(){for(var a;v<i.length&&((a=i[v]).from==q||null==a.from);)null!=a.style&&u.push(a),++v;t=v<i.length?i[v].from:1/0;for(var b=0;b<u.length;++b){var c=u[b].to||1/0;c==q?u.splice(b--,1):t=Math.min(c,t)}}var c=[],d=!0,e=0,g=this.styles,h=this.text,i=this.marked,j=h.length;if(null!=b&&(j=Math.min(b,j)),h||null!=b)if(i&&i.length)for(var o,q=0,l=0,r="",t=i[0].from||0,u=[],v=0;j>q;){t==q&&w();for(var y=Math.min(j,t);;){if(r){for(var z=q+r.length,A=o,B=0;B<u.length;++B)A=(A?A+" ":"")+u[B].style;if(f(z>y?r.slice(0,y-q):r,A),z>=y){r=r.slice(y-q),q=y;break}q=z}r=g[l++],o=k(g[l++])}}else for(var l=0,m=0;j>m;l+=2){var n=g[l],o=g[l+1],p=n.length;m+p>j&&(n=n.slice(0,j-m)),m+=p,f(n,k(o))}else f(" ");return c.join("")},cleanUp:function(){if(this.parent=null,this.marked)for(var a=0,b=this.marked.length;b>a;++a)this.marked[a].detach(this)}},t.prototype={chunkSize:function(){return this.lines.length},remove:function(a,b,c){for(var d=a,e=a+b;e>d;++d){var f=this.lines[d];if(this.height-=f.height,f.cleanUp(),f.handlers)for(var g=0;g<f.handlers.length;++g)c.push(f.handlers[g])}this.lines.splice(a,b)},collapse:function(a){a.splice.apply(a,[a.length,0].concat(this.lines))},insertHeight:function(a,b,c){this.height+=c,this.lines.splice.apply(this.lines,[a,0].concat(b));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}},u.prototype={chunkSize:function(){return this.size},remove:function(a,b,c){this.size-=b;for(var d=0;d<this.children.length;++d){var e=this.children[d],f=e.chunkSize();if(f>a){var g=Math.min(b,f-a),h=e.height;if(e.remove(a,g,c),this.height-=h-e.height,f==g&&(this.children.splice(d--,1),e.parent=null),0==(b-=g))break;a=0}else a-=f}if(this.size-b<25){var i=[];this.collapse(i),this.children=[new t(i)],this.children[0].parent=this}},collapse:function(a){for(var b=0,c=this.children.length;c>b;++b)this.children[b].collapse(a)},insert:function(a,b){for(var c=0,d=0,e=b.length;e>d;++d)c+=b[d].height;this.insertHeight(a,b,c)},insertHeight: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.insertHeight(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 t(h);f.height-=i.height,this.children.splice(d+1,0,i),i.parent=this}this.maybeSpill()}break}a-=g}},maybeSpill:function(){if(!(this.children.length<=10)){var a=this;do{var b=a.children.splice(a.children.length-5,5),c=new u(b);if(a.parent){a.size-=c.size,a.height-=c.height;var e=cb(a.parent.children,a);a.parent.children.splice(e+1,0,c)}else{var d=new u(a.children);d.parent=a,a.children=[d,c],a=d}c.parent=a.parent}while(a.children.length>10);a.parent.maybeSpill()}},iter:function(a,b,c){this.iterN(a,b-a,c)},iterN:function(a,b,c){for(var d=0,e=this.children.length;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}}},z.prototype={addChange:function(a,b,c){this.undone.length=0;var d=+new Date,e=this.done[this.done.length-1],f=e&&e[e.length-1],g=d-this.time;if(g>400||!f)this.done.push([{start:a,added:b,old:c}]);else if(f.start>a+c.length||f.start+f.added<a-f.added+f.old.length)e.push({start:a,added:b,old:c});else{var h=0;if(a<f.start){for(var i=f.start-a-1;i>=0;--i)f.old.unshift(c[i]);h=Math.min(0,b-c.length),f.added+=f.start-a+h,f.start=a}else f.start<a&&(h=a-f.start,b+=h);for(var i=f.added-h,j=c.length;j>i;++i)f.old.push(c[i]);f.added<b&&(f.added=b)}this.time=d}},a.e_stop=E,a.e_preventDefault=C,a.e_stopPropagation=D,a.connect=I,J.prototype={set:function(a,b){clearTimeout(this.id),this.id=setTimeout(b,a)}};var K=a.Pass={toString:function(){return"CodeMirror.Pass"}},L=/gecko\/\d{7}/i.test(navigator.userAgent),M=/MSIE \d/.test(navigator.userAgent),N=/MSIE [1-8]\b/.test(navigator.userAgent),O=/WebKit\//.test(navigator.userAgent),P=/Chrome\//.test(navigator.userAgent),Q=/KHTML\//.test(navigator.userAgent),R=function(){if(N)return!1;var a=document.createElement("div");return"draggable"in a||"dragDrop"in a}(),S="\n";!function(){var a=document.createElement("textarea");a.value="foo\nbar",a.value.indexOf("\r")>-1&&(S="\r\n")}(),null!=document.documentElement.getBoundingClientRect&&(V=function(a,b){try{var c=a.getBoundingClientRect();c={top:c.top,left:c.left}}catch(d){c={top:0,left:0}}if(!b)if(null==window.pageYOffset){var e=document.documentElement||document.body.parentNode;null==e.scrollTop&&(e=document.body),c.top+=e.scrollTop,c.left+=e.scrollLeft}else c.top+=window.pageYOffset,c.left+=window.pageXOffset;return c});var _=document.createElement("pre");"\na"==ab("a")?ab=function(a){return _.textContent=a,_.innerHTML.slice(1)}:" "!=ab(" ")&&(ab=function(a){return _.innerHTML="",_.appendChild(document.createTextNode(a)),_.innerHTML}),a.htmlEscape=ab;var eb=3!="\n\nb".split(/\n/).length?function(a){for(var c,b=0,d=[];(c=a.indexOf("\n",b))>-1;)d.push(a.slice(b,"\r"==a.charAt(c-1)?c-1:c)),b=c+1;return d.push(a.slice(b)),d}:function(a){return a.split(/\r?\n/)};a.splitLines=eb;var fb=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};a.defineMode("null",function(){return{token:function(a){a.skipToEnd()}}}),a.defineMIME("text/plain","null");var gb={3:"Enter",8:"Backspace",9:"Tab",13:"Enter",16:"Shift",17:"Ctrl",18:"Alt",19:"Pause",20:"CapsLock",27:"Esc",32:"Space",33:"PageUp",34:"PageDown",35:"End",36:"Home",37:"Left",38:"Up",39:"Right",40:"Down",44:"PrintScrn",45:"Insert",46:"Delete",59:";",91:"Mod",92:"Mod",93:"Mod",127:"Delete",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'",63276:"PageUp",63277:"PageDown",63275:"End",63273:"Home",63234:"Left",63232:"Up",63235:"Right",63233:"Down",63302:"Insert",63272:"Delete"};return a.keyNames=gb,function(){for(var a=0;10>a;a++)gb[a+48]=String(a);for(var a=65;90>=a;a++)gb[a]=String.fromCharCode(a);for(var a=1;12>=a;a++)gb[a+111]=gb[a+63235]="F"+a}(),a}();CodeMirror.defineMode("xml",function(a,b){function h(a,b){function c(c){return b.tokenize=c,c(a,b)}var d=a.next();if("<"==d){if(a.eat("!"))return a.eat("[")?a.match("CDATA[")?c(k("atom","]]>")):null:a.match("--")?c(k("comment","-->")):a.match("DOCTYPE",!0,!0)?(a.eatWhile(/[\w\._\-]/),c(l(1))):null;if(a.eat("?"))return a.eatWhile(/[\w\._\-]/),b.tokenize=k("meta","?>"),"meta";g=a.eat("/")?"closeTag":"openTag",a.eatSpace(),f="";for(var e;e=a.eat(/[^\s\u00a0=<>\"\'\/?]/);)f+=e;return b.tokenize=i,"tag"}if("&"==d){var h;return h=a.eat("#")?a.eat("x")?a.eatWhile(/[a-fA-F\d]/)&&a.eat(";"):a.eatWhile(/[\d]/)&&a.eat(";"):a.eatWhile(/[\w\.\-:]/)&&a.eat(";"),h?"atom":"error"}return a.eatWhile(/[^&<]/),null}function i(a,b){var c=a.next();return">"==c||"/"==c&&a.eat(">")?(b.tokenize=h,g=">"==c?"endTag":"selfcloseTag","tag"):"="==c?(g="equals",null):/[\'\"]/.test(c)?(b.tokenize=j(c),b.tokenize(a,b)):(a.eatWhile(/[^\s\u00a0=<>\"\'\/?]/),"word")}function j(a){return function(b,c){for(;!b.eol();)if(b.next()==a){c.tokenize=i;break}return"string"}}function k(a,b){return function(c,d){for(;!c.eol();){if(c.match(b)){d.tokenize=h;break}c.next()}return a}}function l(a){return function(b,c){for(var d;null!=(d=b.next());){if("<"==d)return c.tokenize=l(a+1),c.tokenize(b,c);if(">"==d){if(1==a){c.tokenize=h;break}return c.tokenize=l(a-1),c.tokenize(b,c)}}return"meta"}}function o(){for(var a=arguments.length-1;a>=0;a--)m.cc.push(arguments[a])}function p(){return o.apply(null,arguments),!0}function q(a,b){var c=d.doNotIndent.hasOwnProperty(a)||m.context&&m.context.noIndent;m.context={prev:m.context,tagName:a,indent:m.indented,startOfLine:b,noIndent:c}}function r(){m.context&&(m.context=m.context.prev)}function s(a){if("openTag"==a)return m.tagName=f,p(v,t(m.startOfLine));if("closeTag"==a){var b=!1;return b=m.context?m.context.tagName!=f:!0,b&&(n="error"),p(u(b))}return p()}function t(a){return function(b){return"selfcloseTag"==b||"endTag"==b&&d.autoSelfClosers.hasOwnProperty(m.tagName.toLowerCase())?p():"endTag"==b?(q(m.tagName,a),p()):p()}}function u(a){return function(b){return a&&(n="error"),"endTag"==b?(r(),p()):(n="error",p(arguments.callee))}}function v(a){return"word"==a?(n="attribute",p(w,v)):"endTag"==a||"selfcloseTag"==a?o():(n="error",p(v))}function w(a){return"equals"==a?p(x,v):(d.allowMissing||(n="error"),"endTag"==a||"selfcloseTag"==a?o():p())}function x(a){return"string"==a?p(y):"word"==a&&d.allowUnquoted?(n="string",p()):(n="error","endTag"==a||"selfCloseTag"==a?o():p())}function y(a){return"string"==a?p(y):o()}var f,g,m,n,c=a.indentUnit,d=b.htmlMode?{autoSelfClosers:{br:!0,img:!0,hr:!0,link:!0,input:!0,meta:!0,col:!0,frame:!0,base:!0,area:!0},doNotIndent:{pre:!0},allowUnquoted:!0,allowMissing:!1}:{autoSelfClosers:{},doNotIndent:{},allowUnquoted:!1,allowMissing:!1},e=b.alignCDATA;return{startState:function(){return{tokenize:h,cc:[],indented:0,startOfLine:!0,tagName:null,context:null}},token:function(a,b){if(a.sol()&&(b.startOfLine=!0,b.indented=a.indentation()),a.eatSpace())return null;n=g=f=null;var c=b.tokenize(a,b);if(b.type=g,(c||g)&&"comment"!=c)for(m=b;;){var d=b.cc.pop()||s;if(d(g||c))break}return b.startOfLine=!1,n||c},indent:function(a,b,d){var f=a.context;if(a.tokenize!=i&&a.tokenize!=h||f&&f.noIndent)return d?d.match(/^(\s*)/)[0].length:0;if(e&&/<!\[CDATA\[/.test(b))return 0;for(f&&/^<\//.test(b)&&(f=f.prev);f&&!f.startOfLine;)f=f.prev;return f?f.indent+c:0},compareStates:function(a,b){if(a.indented!=b.indented||a.tokenize!=b.tokenize)return!1;for(var c=a.context,d=b.context;;c=c.prev,d=d.prev){if(!c||!d)return c==d;if(c.tagName!=d.tagName)return!1}},electricChars:"/"}}),CodeMirror.defineMIME("application/xml","xml"),CodeMirror.mimeModes.hasOwnProperty("text/html")||CodeMirror.defineMIME("text/html",{name:"xml",htmlMode:!0});
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,9 +2,49 @@
Tests for OA XBlock editing.
*/
describe("OpenAssessment editor", function() {
describe("OpenAssessment.StudioUI", function() {
var runtime = null;
var runtime = {
notify: function(type, data) {}
};
// Stub server that returns dummy data or reports errors.
var StubServer = function() {
this.loadError = false;
this.updateError = false;
this.xml = '<openassessment></openassessment>';
this.errorPromise = $.Deferred(function(defer) {
defer.rejectWith(this, ['Test error']);
}).promise();
this.loadXml = function() {
var xml = this.xml;
if (!this.loadError) {
return $.Deferred(function(defer) {
defer.resolveWith(this, [xml]);
}).promise();
}
else {
return this.errorPromise;
}
}
this.updateXml = function(xml) {
if (!this.updateError) {
this.xml = xml;
return $.Deferred(function(defer) {
defer.resolve();
}).promise();
}
else {
return this.errorPromise;
}
}
};
var server = null;
var ui = null;
beforeEach(function() {
......@@ -12,47 +52,56 @@ describe("OpenAssessment editor", function() {
jasmine.getFixtures().fixturesPath = 'base/fixtures'
loadFixtures('oa_edit.html');
// Mock the runtime
runtime = {
notify: function(type, data) {},
// Create the stub server
server = new StubServer();
// Dummy handler URL returns whatever it's passed in for the handler name
handlerUrl: function(element, handler) {
return handler;
}
};
// Mock the runtime
spyOn(runtime, 'notify');
// Create the object under test
var el = $('#openassessment-edit').get(0);
ui = new OpenAssessment.StudioUI(runtime, el, server);
});
it("loads the XML definition", function() {
// Stub AJAX calls to always return successful
spyOn($, 'ajax').andCallFake(function(params) {
params.success({
'success': true,
'xml': '<openassessment></openassessment>',
'msg': ''
});
});
// Initialize the editor
var editor = OpenAssessmentEditor(runtime, $('#openassessment-edit'));
// Initialize the UI
ui.load()
// Expect that the XML definition was loaded
var editorContents = $('.openassessment-editor').text();
expect(editorContents).toEqual('<openassessment></openassessment>');
var contents = ui.codeBox.getValue();
expect(contents).toEqual('<openassessment></openassessment>');
});
it("saves the XML definition", function() {
expect(false).toBe(true);
// Update the XML
ui.codeBox.setValue('<openassessment>test!</openassessment>');
// Save the updated XML
ui.save();
// Expect the saving notification to start/end
expect(runtime.notify).toHaveBeenCalledWith('save', {state: 'start'});
expect(runtime.notify).toHaveBeenCalledWith('save', {state: 'end'});
// Expect the server's XML to have been updated
expect(server.xml).toEqual('<openassessment>test!</openassessment>');
});
it("cancels editing", function() {
ui.cancel();
expect(runtime.notify).toHaveBeenCalledWith('cancel', {});
});
it("reverts the XML definition on cancellation", function() {
expect(false).toBe(true);
it("displays an error when server reports a load XML error", function() {
server.loadError = true;
ui.load();
expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'});
});
it("displays validation errors but preserves the author's changes", function() {
expect(false).toBe(true);
it("displays an error when server reports an update XML error", function() {
server.updateError = true;
ui.save('<openassessment>test!</openassessment>');
expect(runtime.notify).toHaveBeenCalledWith('error', {msg: 'Test error'});
});
});
/*
Tests for OA XBlock server interactions.
*/
describe("OpenAssessment.Server", function() {
// Stub runtime implementation that returns the handler as the URL
var runtime = {
handlerUrl: function(element, handler) { return "/" + handler }
}
var server = null;
/**
Stub AJAX requests.
Args:
success (bool): If true, return a promise that resolves;
otherwise, return a promise that fails.
responseData(object): Data to pass to the caller if the AJAX
call completes successfully.
**/
var stubAjax = function(success, responseData) {
spyOn($, 'ajax').andReturn(
$.Deferred(function(defer) {
if (success) { defer.resolveWith(this, [responseData]); }
else { defer.reject() }
}).promise()
);
}
beforeEach(function() {
// Create the server
// Since the runtime is a stub implementation that ignores the element passed to it,
// we can set the element parameter to null.
server = new OpenAssessment.Server(runtime, null);
});
it("Renders the XBlock as HTML", function() {
stubAjax(true, "<div>Open Assessment</div>");
var loadedHtml = "";
server.render('submission').done(function(html) {
loadedHtml = html;
});
expect(loadedHtml).toEqual("<div>Open Assessment</div>");
expect($.ajax).toHaveBeenCalledWith({
url: '/render_submission', type: "POST", dataType: "html"
});
});
it("Sends a submission the XBlock", function() {
// Status, student ID, attempt number
stubAjax(true, [true, 1, 2]);
var receivedStudentId = null;
var receivedAttemptNum = null;
server.submit("This is only a test").done(
function(studentId, attemptNum) {
receivedStudentId = studentId;
receivedAttemptNum = attemptNum;
}
);
expect(receivedStudentId).toEqual(1);
expect(receivedAttemptNum).toEqual(2);
expect($.ajax).toHaveBeenCalledWith({
url: '/submit',
type: "POST",
data: {submission: "This is only a test"}
});
});
it("loads the XBlock's XML definition", function() {
stubAjax(true, { success: true, xml: "<openassessment />" });
var loadedXml = "";
server.loadXml().done(function(xml) {
loadedXml = xml;
});
expect(loadedXml).toEqual('<openassessment />');
expect($.ajax).toHaveBeenCalledWith({
url: '/xml', type: "POST", data: '""'
});
});
it("updates the XBlock's XML definition", function() {
stubAjax(true, { success: true });
server.updateXml('<openassessment />');
expect($.ajax).toHaveBeenCalledWith({
url: '/update_xml', type: "POST",
data: JSON.stringify({xml: '<openassessment />'})
});
});
it("informs the caller of an Ajax error when rendering as HTML", function() {
stubAjax(false, null);
var receivedMsg = "";
server.render('submission').fail(function(msg) {
receivedMsg = msg;
});
expect(receivedMsg).toEqual("Could not contact server.");
});
it("informs the caller of an Ajax error when sending a submission", function() {
stubAjax(false, null);
var receivedErrorCode = "";
var receivedErrorMsg = "";
server.submit('This is only a test.').fail(
function(errorCode, errorMsg) {
receivedErrorCode = errorCode;
receivedErrorMsg = errorMsg;
}
);
expect(receivedErrorCode).toEqual("AJAX");
expect(receivedErrorMsg).toEqual("Could not contact server.");
});
it("informs the caller of an server error when sending a submission", function() {
stubAjax(true, [false, "ENODATA", "Error occurred!"]);
var receivedErrorCode = "";
var receivedErrorMsg = "";
server.submit('This is only a test.').fail(
function(errorCode, errorMsg) {
receivedErrorCode = errorCode;
receivedErrorMsg = errorMsg;
}
);
expect(receivedErrorCode).toEqual("ENODATA");
expect(receivedErrorMsg).toEqual("Error occurred!");
});
it("informs the caller of an Ajax error when loading XML", function() {
stubAjax(false, null);
var receivedMsg = null;
server.loadXml().fail(function(msg) {
receivedMsg = msg;
});
expect(receivedMsg).toEqual("Could not contact server.");
});
it("informs the caller of an Ajax error when updating XML", function() {
stubAjax(false, null);
var receivedMsg = null;
server.updateXml('test').fail(function(msg) {
receivedMsg = msg;
});
expect(receivedMsg).toEqual("Could not contact server.");
});
it("informs the caller of a server error when loading XML", function() {
stubAjax(true, { success: false, msg: "Test error" });
var receivedMsg = null;
server.updateXml('test').fail(function(msg) {
receivedMsg = msg;
});
expect(receivedMsg).toEqual("Test error");
});
it("informs the caller of a server error when updating XML", function() {
stubAjax(true, { success: false, msg: "Test error" });
var receivedMsg = null;
server.loadXml().fail(function(msg) {
receivedMsg = msg;
});
expect(receivedMsg).toEqual("Test error");
});
});
......@@ -123,6 +123,7 @@ function OpenAssessmentBlock(runtime, element) {
$.ajax({
type: "POST",
url: renderPeerUrl,
dataType: "html",
success: function(data) {
$(peerListItem, element).replaceWith(data);
collapse($(peerListItem, element));
......@@ -132,6 +133,7 @@ function OpenAssessmentBlock(runtime, element) {
$.ajax({
type: "POST",
url: renderSelfUrl,
dataType: "html",
success: function(data) {
$(selfListItem, element).replaceWith(data);
collapse($(selfListItem, element));
......@@ -140,6 +142,7 @@ function OpenAssessmentBlock(runtime, element) {
$.ajax({
type: "POST",
dataType: "html",
url: renderGradeUrl,
success: function(data) {
$(gradeListItem, element).replaceWith(data);
......
/* JavaScript for Studio editing view of Open Assessment XBlock */
function OpenAssessmentEditor(runtime, element) {
function displayError(errorMsg) {
runtime.notify('error', {msg: errorMsg});
}
// Update editor with the XBlock's current content
function updateEditorFromXBlock(editor) {
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'xml'),
data: "\"\"",
success: function(data) {
if (data.success) {
editor.setValue(data.xml);
}
else {
displayError(data.msg);
}
/* Namespace for open assessment */
if (typeof OpenAssessment == "undefined" || !OpenAssessment) {
OpenAssessment = {};
}
/**
Interface for editing UI in Studio.
The constructor initializes the DOM for editing.
Args:
runtime (Runtime): an XBlock runtime instance.
element (DOM element): The DOM element representing this XBlock.
server (OpenAssessment.Server): The interface to the XBlock server.
Returns:
OpenAssessment.StudioUI
**/
OpenAssessment.StudioUI = function(runtime, element, server) {
this.runtime = runtime;
this.server = server;
// Initialize the code box
this.codeBox = CodeMirror.fromTextArea(
$(element).find('.openassessment-editor').first().get(0),
{mode: "xml", lineNumbers: true, lineWrapping: true}
);
// Install click handlers
var ui = this;
$(element).find('.openassessment-save-button').click(
function(eventData) {
ui.save();
});
$(element).find('.openassessment-cancel-button').click(
function(eventData) {
ui.cancel();
});
};
OpenAssessment.StudioUI.prototype = {
/**
Load the XBlock XML definition from the server and display it in the UI.
**/
load: function() {
var ui = this;
this.server.loadXml().done(
function(xml) {
ui.codeBox.setValue(xml);
}).fail(function(msg) {
ui.showError(msg);
}
);
},
/**
Save the updated XML definition to the server.
**/
save: function() {
// Notify the client-side runtime that we are starting
// to save so it can show the "Saving..." notification
this.runtime.notify('save', {state: 'start'});
// Send the updated XML to the server
var xml = this.codeBox.getValue();
var ui = this;
this.server.updateXml(xml).done(function() {
// Notify the client-side runtime that we finished saving
// so it can hide the "Saving..." notification.
ui.runtime.notify('save', {state: 'end'});
}).fail(function(msg) {
ui.showError(msg);
});
}
},
function initializeEditor() {
var textAreas = $(element).find('.openassessment-editor');
if (textAreas.length < 1) {
console.warn("Could not find element for OpenAssessmentBlock XML editor");
return null;
}
else {
return CodeMirror.fromTextArea(
textAreas[0], {mode: "xml", lineNumbers: true, lineWrapping: true}
);
}
}
/**
Cancel editing.
**/
cancel: function() {
// Notify the client-side runtime so it will close the editing modal.
this.runtime.notify('cancel', {});
},
function initializeSaveButton(editor) {
saveButtons = $(element).find('.openassessment-save-button');
if (saveButtons.length < 1) {
console.warn("Could not find element for OpenAssessmentBlock save button");
}
else {
saveButtons.click(function (eventObject) {
// Notify the client-side runtime that we are starting
// to save so it can show the "Saving..." notification
runtime.notify('save', {state: 'start'});
// POST the updated description to the XBlock
// The server-side code is responsible for validating and persisting
// the updated content.
$.ajax({
type: "POST",
url: runtime.handlerUrl(element, 'update_xml'),
data: JSON.stringify({ xml: editor.getValue() }),
success: function(data) {
// Notify the client-side runtime that we finished saving
// so it can hide the "Saving..." notification.
if (data.success) {
runtime.notify('save', {state: 'end'});
}
// Display an error alert if any errors occurred
else {
displayError(data.msg);
}
}
});
});
}
}
/**
Display an error message to the user.
function initializeCancelButton(editor) {
cancelButtons = $(element).find('.openassessment-cancel-button');
if (cancelButtons.length < 1) {
console.warn("Could not find element for OpenAssessmentBlock cancel button");
}
else {
cancelButtons.click(function (eventObject) {
// Revert to the XBlock's current content
updateEditorFromXBlock(editor);
// Notify the client-side runtime so it will close the editing modal.
runtime.notify('cancel', {});
});
}
Args:
errorMsg (string): The error message to display.
**/
showError: function(errorMsg) {
this.runtime.notify('error', {msg: errorMsg});
}
};
/* XBlock entry point for Studio view */
function OpenAssessmentEditor(runtime, element) {
$(function ($) {
editor = initializeEditor();
if (editor) {
updateEditorFromXBlock(editor);
initializeSaveButton(editor);
initializeCancelButton(editor);
}
/**
Initialize the editing interface on page load.
**/
$(function($) {
var server = new OpenAssessment.Server(runtime, element);
var ui = new OpenAssessment.StudioUI(runtime, element, server);
ui.load();
});
}
/* JavaScript interface for interacting with server-side OpenAssessment XBlock */
/* Namespace for open assessment */
if (typeof OpenAssessment == "undefined" || !OpenAssessment) {
OpenAssessment = {};
}
/**
Interface for server-side XBlock handlers.
Args:
runtime (Runtime): An XBlock runtime instance.
element (DOM element): The DOM element representing this XBlock.
Returns:
OpenAssessment.Server
**/
OpenAssessment.Server = function(runtime, element) {
this.runtime = runtime;
this.element = element;
};
OpenAssessment.Server.prototype = {
/**
Construct the URL for the handler, specific to one instance of the XBlock on the page.
Args:
handler (string): The name of the XBlock handler.
Returns:
URL (string)
**/
url: function(handler) {
return this.runtime.handlerUrl(this.element, handler);
},
/**
Render the XBlock.
Args:
component (string): The component to render.
Returns:
A JQuery promise, which resolves with the HTML of the rendered XBlock
and fails with an error message.
Example:
server.render('submission').done(
function(html) { console.log(html); }
).fail(
function(err) { console.log(err); }
)
**/
render: function(component) {
var url = this.url('render_' + component);
return $.Deferred(function(defer) {
$.ajax({
url: url,
type: "POST",
dataType: "html"
}).done(function(data) {
defer.resolveWith(this, [data]);
}).fail(function(data) {
defer.rejectWith(this, ['Could not contact server.']);
})
}).promise();
},
/**
Send a submission to the XBlock.
Args:
submission (string): The text of the student's submission.
Returns:
A JQuery promise, which resolves with the student's ID and attempt number
if the call was successful and fails with an status code and error message otherwise.
**/
submit: function(submission) {
var url = this.url('submit');
return $.Deferred(function(defer) {
$.ajax({
type: "POST",
url: url,
data: {submission: submission}
}).done(function(data) {
var success = data[0];
if (success) {
var studentId = data[1];
var attemptNum = data[2];
defer.resolveWith(this, [studentId, attemptNum]);
}
else {
var errorNum = data[1];
var errorMsg = data[2];
defer.rejectWith(this, [errorNum, errorMsg]);
}
}).fail(function(data) {
defer.rejectWith(this, ["AJAX", "Could not contact server."]);
})
}).promise();
},
/**
Load the XBlock's XML definition from the server.
Returns:
A JQuery promise, which resolves with the XML definition
and fails with an error message.
Example:
server.loadXml().done(
function(xml) { console.log(xml); }
).fail(
function(err) { console.log(err); }
);
**/
loadXml: function() {
var url = this.url('xml');
return $.Deferred(function(defer) {
$.ajax({
type: "POST", url: url, data: "\"\""
}).done(function(data) {
if (data.success) { defer.resolveWith(this, [data.xml]); }
else { defer.rejectWith(this, [data.msg]); }
}).fail(function(data) {
defer.rejectWith(this, ['Could not contact server.']);
});
}).promise();
},
/**
Update the XBlock's XML definition on the server.
Returns:
A JQuery promise, which resolves with no arguments
and fails with an error message.
Example usage:
server.updateXml(xml).done(
function() {}
).fail(
function(err) { console.log(err); }
);
**/
updateXml: function(xml) {
var url = this.url('update_xml');
var payload = JSON.stringify({xml: xml});
return $.Deferred(function(defer) {
$.ajax({
type: "POST", url: url, data: payload
}).done(function(data) {
if (data.success) { defer.resolve() }
else { defer.rejectWith(this, [data.msg]); }
}).fail(function(data) {
defer.rejectWith(this, ['Could not contact server.']);
});
}).promise();
}
};
......@@ -38,6 +38,7 @@ class StudioMixin(object):
"""
rendered_template = get_template('openassessmentblock/oa_edit.html').render(Context({}))
frag = Fragment(rendered_template)
frag.add_javascript(pkg_resources.resource_string(__name__, "static/js/src/oa_server.js"))
frag.add_javascript(pkg_resources.resource_string(__name__, "static/js/src/oa_edit.js"))
frag.initialize_js('OpenAssessmentEditor')
return frag
......
......@@ -36,15 +36,18 @@ module.exports = function(config) {
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/*.js': 'coverage'
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
reporters: ['progress', 'coverage'],
coverageReporter: {
type : 'text'
},
// web server port
port: 9876,
......@@ -73,4 +76,5 @@ module.exports = function(config) {
singleRun: true
});
};
......@@ -15,3 +15,7 @@ if __name__ == "__main__":
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
# Execute JavaScript tests
if 'test' in sys.argv:
os.system('npm install && npm test')
......@@ -2,6 +2,7 @@
"devDependencies": {
"karma": "~0.11",
"karma-jasmine": "0.1.3",
"karma-coverage": "0.1.5",
"karma-firefox-launcher": "~0.1.3"
},
"scripts": {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment