0;l++){u=[],f=a[l];for(h=0;h۱۰۰هزار","10k unit":"۱۰هزار واحد",Follow:"دنبال کردن","Follow %{screen_name}":"دنبال کردن %{screen_name}",K:"هزار",M:"میلیون",Tweet:"توییت","Tweet %{hashtag}":"توییت کردن %{hashtag}","Tweet to %{name}":"به %{name} توییت کنید"},fi:{"%{followers_count} followers":"%{followers_count} seuraajaa","100K+":"100 000+","10k unit":"10 000 yksikköä",Follow:"Seuraa","Follow %{screen_name}":"Seuraa käyttäjää %{screen_name}",K:"tuhatta",M:"milj.",Tweet:"Twiittaa","Tweet %{hashtag}":"Twiittaa %{hashtag}","Tweet to %{name}":"Twiittaa käyttäjälle %{name}"},fil:{"%{followers_count} followers":"%{followers_count} mga tagasunod","10k unit":"10k yunit",Follow:"Sundan","Follow %{screen_name}":"Sundan si %{screen_name}",Tweet:"I-tweet","Tweet %{hashtag}":"I-tweet ang %{hashtag}","Tweet to %{name}":"Mag-Tweet kay %{name}"},fr:{"%{followers_count} followers":"%{followers_count} abonnés","10k unit":"unité de 10k",Follow:"Suivre","Follow %{screen_name}":"Suivre %{screen_name}",Tweet:"Tweeter","Tweet %{hashtag}":"Tweeter %{hashtag}","Tweet to %{name}":"Tweeter à %{name}"},he:{"%{followers_count} followers":"%{followers_count} עוקבים","100K+":"מאות אלפים","10k unit":"עשרות אלפים",Follow:"מעקב","Follow %{screen_name}":"לעקוב אחר %{screen_name}",K:"אלף",M:"מיליון",Tweet:"ציוץ","Tweet %{hashtag}":"צייצו %{hashtag}","Tweet to %{name}":"ציוץ אל %{name}"},hi:{"%{followers_count} followers":"%{followers_count} फ़ॉलोअर्स","100K+":"1 लाख+","10k unit":"10 हजार इकाईयां",Follow:"फ़ॉलो","Follow %{screen_name}":"%{screen_name} को फ़ॉलो करें",K:"हजार",M:"मिलियन",Tweet:"ट्वीट","Tweet %{hashtag}":"ट्वीट %{hashtag}","Tweet to %{name}":"%{name} को ट्वीट करें"},hu:{"%{followers_count} followers":"%{followers_count} követő","100K+":"100E+","10k unit":"10E+",Follow:"Követés","Follow %{screen_name}":"%{screen_name} követése",K:"E","Tweet %{hashtag}":"%{hashtag} tweetelése","Tweet to %{name}":"Tweet küldése neki: %{name}"},id:{"%{followers_count} followers":"%{followers_count} pengikut","100K+":"100 ribu+","10k unit":"10 ribu unit",Follow:"Ikuti","Follow %{screen_name}":"Ikuti %{screen_name}",K:" ribu",M:" juta","Tweet to %{name}":"Tweet ke %{name}"},it:{"%{followers_count} followers":"%{followers_count} follower","10k unit":"10k unità",Follow:"Segui","Follow %{screen_name}":"Segui %{screen_name}","Tweet %{hashtag}":"Twitta %{hashtag}","Tweet to %{name}":"Twitta a %{name}"},ja:{"%{followers_count} followers":"%{followers_count}人のフォロワー","100K+":"100K以上","10k unit":"万",Follow:"フォローする","Follow %{screen_name}":"%{screen_name}さんをフォロー",Tweet:"ツイート","Tweet %{hashtag}":"%{hashtag} をツイートする","Tweet to %{name}":"%{name}さんへツイートする"},ko:{"%{followers_count} followers":"%{followers_count}명의 팔로워","100K+":"100만 이상","10k unit":"만 단위",Follow:"팔로우","Follow %{screen_name}":"%{screen_name} 님 팔로우하기",K:"천",M:"백만",Tweet:"트윗","Tweet %{hashtag}":"%{hashtag} 관련 트윗하기","Tweet to %{name}":"%{name}님에게 트윗하기"},msa:{"%{followers_count} followers":"%{followers_count} pengikut","100K+":"100 ribu+","10k unit":"10 ribu unit",Follow:"Ikut","Follow %{screen_name}":"Ikut %{screen_name}",K:"ribu",M:"juta","Tweet to %{name}":"Tweet kepada %{name}"},nl:{"%{followers_count} followers":"%{followers_count} volgers","100K+":"100k+","10k unit":"10k-eenheid",Follow:"Volgen","Follow %{screen_name}":"%{screen_name} volgen",K:"k",M:" mln.",Tweet:"Tweeten","Tweet %{hashtag}":"%{hashtag} tweeten","Tweet to %{name}":"Tweeten naar %{name}"},no:{"%{followers_count} followers":"%{followers_count} følgere","100K+":"100 K+","10k unit":"10-K-enhet",Follow:"Følg","Follow %{screen_name}":"Følg %{screen_name}","Tweet to %{name}":"Send en tweet til %{name}"},pl:{"%{followers_count} followers":"%{followers_count} obserwujących","100K+":"100 tys.+","10k unit":"10 tys.",Follow:"Obserwuj","Follow %{screen_name}":"Obserwuj %{screen_name}",K:"tys.",M:"mln",Tweet:"Tweetnij","Tweet %{hashtag}":"Tweetnij %{hashtag}","Tweet to %{name}":"Tweetnij do %{name}"},pt:{"%{followers_count} followers":"%{followers_count} seguidores","100K+":"+100 mil","10k unit":"10 mil unidades",Follow:"Seguir","Follow %{screen_name}":"Seguir %{screen_name}",K:"Mil",Tweet:"Tweetar","Tweet %{hashtag}":"Tweetar %{hashtag}","Tweet to %{name}":"Tweetar para %{name}"},ru:{"%{followers_count} followers":"Читатели: %{followers_count} ","100K+":"100 тыс.+","10k unit":"блок 10k",Follow:"Читать","Follow %{screen_name}":"Читать %{screen_name}",K:"тыс.",M:"млн.",Tweet:"Твитнуть","Tweet %{hashtag}":"Твитнуть %{hashtag}","Tweet to %{name}":"Твитнуть %{name}"},sv:{"%{followers_count} followers":"%{followers_count} följare","10k unit":"10k",Follow:"Följ","Follow %{screen_name}":"Följ %{screen_name}",Tweet:"Tweeta","Tweet %{hashtag}":"Tweeta %{hashtag}","Tweet to %{name}":"Tweeta till %{name}"},th:{"%{followers_count} followers":"%{followers_count} ผู้ติดตาม","100K+":"100พัน+","10k unit":"หน่วย 10พัน",Follow:"ติดตาม","Follow %{screen_name}":"ติดตาม %{screen_name}",K:"พัน",M:"ล้าน",Tweet:"ทวีต","Tweet %{hashtag}":"ทวีต %{hashtag}","Tweet to %{name}":"ทวีตถึง %{name}"},tr:{"%{followers_count} followers":"%{followers_count} takipçi","100K+":"+100 bin","10k unit":"10 bin birim",Follow:"Takip et","Follow %{screen_name}":"Takip et: %{screen_name}",K:"bin",M:"milyon",Tweet:"Tweetle","Tweet %{hashtag}":"Tweetle: %{hashtag}","Tweet to %{name}":"Tweetle: %{name}"},ur:{"%{followers_count} followers":"%{followers_count} فالورز","100K+":"ایک لاکھ سے زیادہ","10k unit":"دس ہزار یونٹ",Follow:"فالو کریں","Follow %{screen_name}":"%{screen_name} کو فالو کریں",K:"ہزار",M:"ملین",Tweet:"ٹویٹ کریں","Tweet %{hashtag}":"%{hashtag} ٹویٹ کریں","Tweet to %{name}":"%{name} کو ٹویٹ کریں"},"zh-cn":{"%{followers_count} followers":"%{followers_count} 关注者","100K+":"10万+","10k unit":"1万单元",Follow:"关注","Follow %{screen_name}":"关注 %{screen_name}",K:"千",M:"百万",Tweet:"发推","Tweet %{hashtag}":"以 %{hashtag} 发推","Tweet to %{name}":"发推给 %{name}"},"zh-tw":{"%{followers_count} followers":"%{followers_count} 位跟隨者","100K+":"超過十萬","10k unit":"1萬 單位",Follow:"跟隨","Follow %{screen_name}":"跟隨 %{screen_name}",K:"千",M:"百萬",Tweet:"推文","Tweet %{hashtag}":"推文%{hashtag}","Tweet to %{name}":"推文給%{name}"}};a.aug(g.prototype,{setLanguage:function(e){var t;e||(e=this.params().lang||this.dataAttr("lang")||b(this.srcEl)),e=e&&e.toLowerCase();if(!e)return this.lang="en";if(m[e])return this.lang=e;t=e.replace(/[\-_].*/,"");if(m[t])return this.lang=t;this.lang="en"},_:function(e,t){var n=this.lang;t=t||{};if(!n||!m.hasOwnProperty(n))n=this.lang="en";return e=m[n]&&m[n][e]||e,this.ringo(e,t,/%\{([\w_]+)\}/g)},ringo:function(e,t,n){return n=n||/\{\{([\w_]+)\}\}/g,e.replace(n,function(e,n){return t[n]!==undefined?t[n]:e})},add:function(e){h.list.push(this),h.byId[this.id]=e},create:function(e,t,n){var i=this,o;return n[v]=!0,o=r(a.aug({id:this.id,src:e,"class":this.classAttr.join(" ")},n),t,this.targetEl&&this.targetEl.ownerDocument),this.srcEl?this.layout(function(){return i.srcEl.parentNode.replaceChild(o,i.srcEl),o}):this.targetEl?this.layout(function(){return i.targetEl.appendChild(o),o}):s.reject("Did not append widget")},params:function(){var e,t;return this.srcOb?t=this.srcOb:(e=this.srcEl&&this.srcEl.href&&this.srcEl.href.split("?")[1],t=e?o.decode(e):{}),this.params=function(){return t},t},dataAttr:function(e){return this.srcEl&&this.srcEl.getAttribute("data-"+e)},attr:function(e){return this.srcEl&&this.srcEl.getAttribute(e)},layout:function(e){return d.enqueue(e)},styles:{base:[["font","normal normal normal 11px/18px 'Helvetica Neue', Arial, sans-serif"],["margin","0"],["padding","0"],["whiteSpace","nowrap"]],button:[["fontWeight","bold"],["textShadow","0 1px 0 rgba(255,255,255,.5)"]],large:[["fontSize","13px"],["lineHeight","26px"]],vbubble:[["fontSize","16px"]]},width:function(){throw new Error(name+" not implemented")},height:function(){return this.size=="m"?20:28},minWidth:function(){},maxWidth:function(){},minHeight:function(){},maxHeight:function(){},dimensions:function(){function e(e){switch(typeof e){case"string":return e;case"undefined":return;default:return e+"px"}}var t={width:this.width(),height:this.height()};return this.minWidth()&&(t["min-width"]=this.minWidth()),this.maxWidth()&&(t["max-width"]=this.maxWidth()),this.minHeight()&&(t["min-height"]=this.minHeight()),this.maxHeight()&&(t["max-height"]=this.maxHeight()),a.forIn(t,function(n,r){t[n]=e(r)}),t},generateId:function(){return this.srcEl&&this.srcEl.id||"twitter-widget-"+l++}}),g.afterLoad=function(e){p.push(e)},g.doLayout=function(){d.exec()},g.doLayoutAsync=function(){d.delayedExec()},g.init=function(e){c=e},g.find=function(e){return e&&h.byId[e]?h.byId[e].element:null},g.embed=function(e){function o(e){var t=e.element&&e.element()||e;if(e!==document.body)return t}var n=c.widgets,r=[],i=[];u.isArray(e)||(e=[e||document]),a.forEach(e,function(e){a.forIn(n,function(n,i){var s,o;n.match(/\./)?(s=n.split("."),o=t.all(s[1],e,s[0])):o=e.getElementsByTagName(n),a.forEach(o,function(e){if(e.getAttribute(v))return;e.setAttribute(v,"true"),r.push(new i(e))})})}),g.doLayout(),a.forEach(r,function(e){var t;h.byId[e.id]=e,h.list.push(e),t=e.render(c),e.rendered&&(t=e.rendered()),i.push(t),t.then(function(e){var t=o(e);t&&twttr.events.trigger("rendered",{target:t})})}),s.every.apply(null,i).then(function(e){var t=a.map(Array.prototype.slice.call(e),o);t=a.filter(t,function(t){return t}),t.length&&twttr.events.trigger("loaded",{widgets:t})}),g.doLayoutAsync(),y()},window.setInterval(function(){g.doLayout()},500),e(g)})});
-provide("tfw/widget/intent",function(e){using("tfw/widget/base","util/util","util/querystring","util/uri","util/promise",function(t,n,r,i,s){function p(e){var t=Math.round(c/2-a/2),n=0;l>f&&(n=Math.round(l/2-f/2)),window.open(e,undefined,[u,"width="+a,"height="+f,"left="+t,"top="+n].join(","))}function d(e,t){using("tfw/hub/client",function(n){n.openIntent(e,t)})}function v(e){var t="original_referer="+location.href;return[e,t].join(e.indexOf("?")==-1?"?":"&")}function m(e){var t,r,i,s;e=e||window.event,t=e.target||e.srcElement;if(e.altKey||e.metaKey||e.shiftKey)return;while(t){if(~n.indexOf(["A","AREA"],t.nodeName))break;t=t.parentNode}t&&t.href&&(r=t.href.match(o),r&&(s=v(t.href),s=s.replace(/^http[:]/,"https:"),s=s.replace(/^\/\//,"https://"),g(s,t),e.returnValue=!1,e.preventDefault&&e.preventDefault()))}function g(e,t){if(twttr.events.hub&&t){var n=new y(h.generateId(),t);h.add(n),d(e,t),twttr.events.trigger("click",{target:t,region:"intent",type:"click",data:{}})}else p(e)}function y(e,t){this.id=e,this.element=this.srcEl=t}function b(e){this.srcEl=[],this.element=e}var o=/twitter\.com(\:\d{2,4})?\/intent\/(\w+)/,u="scrollbars=yes,resizable=yes,toolbar=no,location=yes",a=550,f=520,l=screen.height,c=screen.width,h;b.prototype=new t,n.aug(b.prototype,{render:function(e){return h=this,window.__twitterIntentHandler||(document.addEventListener?document.addEventListener("click",m,!1):document.attachEvent&&document.attachEvent("onclick",m),window.__twitterIntentHandler=!0),s.fulfill(document.body)}}),b.open=g,e(b)})});
-provide("dom/classname",function(e){function t(e){return new RegExp("\\b"+e+"\\b","g")}function n(e,n){if(e.classList){e.classList.add(n);return}t(n).test(e.className)||(e.className+=" "+n)}function r(e,n){if(e.classList){e.classList.remove(n);return}e.className=e.className.replace(t(n)," ")}function i(e,t,i){return e.classList&&e.classList.toggle?e.classList.toggle(t,i):(i?n(e,t):r(e,t),i)}function s(e,i,s){if(e.classList&&o(e,i)){r(e,i),n(e,s);return}e.className=e.className.replace(t(i),s)}function o(e,n){return e.classList?e.classList.contains(n):t(n).test(e.className)}e({add:n,remove:r,replace:s,toggle:i,present:o})});
-provide("util/throttle",function(e){function t(e,t,n){function o(){var n=+(new Date);window.clearTimeout(s);if(n-i>t){i=n,e.call(r);return}s=window.setTimeout(o,t)}var r=n||this,i=0,s;return o}e(t)});
-provide("util/css",function(e){using("util/util",function(t){e({sanitize:function(e,n,r){var i=/^[\w ,%\/"'\-_#]+$/,s=e&&t.map(e.split(";"),function(e){return t.map(e.split(":").slice(0,2),function(e){return t.trim(e)})}),o=0,u,a=[],f=r?"!important":"";n=n||/^(font|text\-|letter\-|color|line\-)[\w\-]*$/;for(;s&&(u=s[o]);o++)u[0].match(n)&&u[1].match(i)&&a.push(u.join(":")+f);return a.join(";")}})})});
-provide("tfw/util/params",function(e){using("util/querystring","util/twitter",function(t,n){e(function(e,r){return function(i){var s,o="data-tw-params",u,a=i.innerHTML;if(!i)return;if(!n.isTwitterURL(i.href))return;if(i.getAttribute(o))return;i.setAttribute(o,!0);if(typeof r=="function"){s=r.call(this,i);for(u in s)s.hasOwnProperty(u)&&(e[u]=s[u])}i.href=t.url(i.href,e)}})})});
-provide("util/params",function(e){using("util/querystring",function(t){var n=function(e){var n=e.search.substr(1);return t.decode(n)},r=function(e){var n=e.href,r=n.indexOf("#"),i=r<0?"":n.substring(r+1);return t.decode(i)},i=function(e){var t={},i=n(e),s=r(e);for(var o in i)i.hasOwnProperty(o)&&(t[o]=i[o]);for(var o in s)s.hasOwnProperty(o)&&(t[o]=s[o]);return t};e({combined:i,fromQuery:n,fromFragment:r})})});
-provide("tfw/util/env",function(e){using("util/params",function(t){function r(){var e=36e5,r=t.combined(document.location)._;return n!==undefined?n:(n=!1,r&&/^\d+$/.test(r)&&(n=+(new Date)-parseInt(r)=r;return t[e]=i,i}var t={force_new_cookie:100,rufous_pixel:100,decider_fixture:12.34};e({isAvailable:n})});
-provide("dom/cookie",function(e){using("util/util",function(t){e(function(e,n,r){var i=t.aug({},r);if(arguments.length>1&&String(n)!=="[object Object]"){if(n===null||n===undefined)i.expires=-1;if(typeof i.expires=="number"){var s=i.expires,o=new Date((new Date).getTime()+s*60*1e3);i.expires=o}return n=String(n),document.cookie=[encodeURIComponent(e),"=",i.raw?n:encodeURIComponent(n),i.expires?"; expires="+i.expires.toUTCString():"",i.path?"; path="+i.path:"",i.domain?"; domain="+i.domain:"",i.secure?"; secure":""].join("")}i=n||{};var u,a=i.raw?function(e){return e}:decodeURIComponent;return(u=(new RegExp("(?:^|; )"+encodeURIComponent(e)+"=([^;]*)")).exec(document.cookie))?a(u[1]):null})})});
-provide("util/donottrack",function(e){using("dom/cookie",function(t){e(function(e,n){var r=/\.(gov|mil)(:\d+)?$/i,i=/https?:\/\/([^\/]+).*/i;return e=e||document.referrer,e=i.test(e)&&i.exec(e)[1],n=n||document.location.host,t("dnt")?!0:r.test(n)?!0:e&&r.test(e)?!0:document.navigator?document.navigator["doNotTrack"]==1:navigator?navigator["doNotTrack"]==1||navigator["msDoNotTrack"]==1:!1})})});
-provide("tfw/util/guest_cookie",function(e){using("dom/cookie","util/donottrack","util/decider",function(t,n,r){function s(){var e=t(i)||!1;if(!e)return;e.match(/^v3\:/)||o()}function o(){t(i)&&t(i,null,{domain:".twitter.com",path:"/"})}function u(){n()&&o()}var i="pid";e({set:u,destroy:o,forceNewCookie:s,guest_id_cookie:i})})});
-provide("sandbox/baseframe",function(e){using("util/domready","util/env","util/iframe","util/promise","util/util",function(t,n,r,i,s){function u(e,t,n,o){var u;this.readyPromise=new i(s.bind(function(e){this.resolver=e},this)),this.attrs=e||{},this.styles=t||{},this.appender=n||function(e){document.body.appendChild(e)},this.layout=o||function(e){return new i(function(t){return t.fulfill(e())})},this.frame=u=r(this.attrs,this.styles),u.onreadystatechange=u.onload=this.getCallback(this.onLoad),this.layout(s.bind(function(){this.appender(u)},this))}var o=0;window.twttr=window.twttr||{},window.twttr.sandbox=window.twttr.sandbox||{},u.prototype.getCallback=function(e){var t=this,n=!1;return function(){n||(n=!0,e.call(t))}},u.prototype.registerCallback=function(e){var t="cb"+o++;return window.twttr.sandbox[t]=e,t},u.prototype.onLoad=function(){try{this.document=this.frame.contentWindow.document}catch(e){this.setDocDomain();return}this.writeStandardsDoc(),this.resolver.fulfill(this)},u.prototype.ready=function(){return this.readyPromise},u.prototype.setDocDomain=function(){var e=r(this.attrs,this.styles),t=this.registerCallback(this.getCallback(this.onLoad));e.src=["javascript:",'document.write("");',"try { window.parent.document; }","catch (e) {",'document.domain="'+document.domain+'";',"}",'window.parent.twttr.sandbox["'+t+'"]();'].join(""),this.layout(s.bind(function(){this.frame.parentNode.removeChild(this.frame),this.frame=null,this.appender?this.appender(e):document.body.appendChild(e),this.frame=e},this))},u.prototype.writeStandardsDoc=function(){if(!n.anyIE()||n.cspEnabled())return;var e=["","","","","try { window.parent.document; }",'catch (e) {document.domain="'+document.domain+'";}'," ","","",""].join("");this.document.write(e),this.document.close()},e(u)})});
-provide("sandbox/minimal",function(e){using("sandbox/baseframe","util/env","util/promise","util/util",function(t,n,r,i){function s(e,t){if(!e)return;this._frame=e,this._win=e.contentWindow,this._doc=this._win.document,this._body=this._doc.body,this._head=this._body.parentNode.children[0],this.layout=t}i.aug(s.prototype,{createElement:function(e){return this._doc.createElement(e)},createDocumentFragment:function(){return this._doc.createDocumentFragment()},appendChild:function(e){return this.layout(i.bind(function(){return this._body.appendChild(e)},this))},setBaseTarget:function(e){var t=this._doc.createElement("base");return t.target=e,this.layout(i.bind(function(){return this._head.appendChild(t)},this))},setTitle:function(e){if(!e)return;this._frame.title=e},element:function(){return this._frame},document:function(){return this._doc}}),s.createSandbox=function(e,n,r,i){var o=new t(e,n,r,i);return o.ready().then(function(e){return new s(e.frame,e.layout)})},e(s)})});
-provide("tfw/util/tracking",function(e){using("dom/cookie","dom/delegate","sandbox/minimal","util/donottrack","util/promise","tfw/util/guest_cookie","tfw/util/env","util/iframe","util/util","$xd/json2.js",function(t,n,r,i,s,o,u,a,f){function S(){return b?b:b=r.createSandbox({id:"rufous-sandbox"},{display:"none"}).then(f.bind(function(e){y=e,d=H(),v=B();while(m[0])A.apply(this,m.shift());return g?O():[d,v]},this))}function x(e,t,n,r){var i=!f.isObject(e),s=t?!f.isObject(t):!1,o,u;if(i||s)return;o=_(e),u=D(t,!!n,!!r),L(o,u,!0)}function T(e,n,r,s){var a=c[n],l,h,p=o.guest_id_cookie;if(!a)return;e=e||{},s=!!s,r=!!r,h=e.original_redirect_referrer||document.referrer,s=s||i(h),l=f.aug({},e),r||(k(l,"referrer",h),k(l,"widget",+u.isDynamicWidget()),k(l,"hask",+!!t("k")),k(l,"li",+!!t("twid")),k(l,p,t(p)||"")),s&&(k(l,"dnt",1),F(l)),j(a+"?"+P(l))}function N(e,t,n,r,i){var s=C(e.target||e.srcElement);s.action=i||"click",x(s,t,n,r)}function C(e,t){var n;return t=t||{},!e||e.nodeType!==1?t:((n=e.getAttribute("data-scribe"))&&f.forEach(n.split(" "),function(e){var n=f.trim(e).split(":"),r=n[0],i=n[1];r&&i&&!t[r]&&(t[r]=i)}),C(e.parentNode,t))}function k(e,t,n){var r=l+t;if(!e)return;return e[r]=n,e}function L(e,t,n){var r,i,s,o,u=w+"?";if(!f.isObject(e)||!f.isObject(t))return;s=f.aug({},t,{event_namespace:e}),n?(u+=P({l:I(s)}),j(u)):(r=d.firstChild,r.value=+r.value||+s.dnt,o=I(s),i=y.createElement("input"),i.type="hidden",i.name="l",i.value=o,d.appendChild(i))}function A(e,t,n,r){var i=!f.isObject(e),s=t?!f.isObject(t):!1,o,u;if(i||s)return;if(!y||!d){m.push([e,t,n,r]);return}o=_(e),u=D(t,!!n,!!r),L(o,u)}function O(){if(!d)return g=!0,b||s.reject();if(d.children.length<=2)return s.reject();var e=s.every(y.appendChild(d),y.appendChild(v)).then(function(e){var t=e[0],r=e[1];return n.on(r,"load",function(){window.setTimeout(M(t,r),0)}),t.submit(),e});return d=H(),v=B(),e}function M(e,t){return function(){var n=e.parentNode;if(!n)return;n.removeChild(e),n.removeChild(t)}}function _(e){return f.aug({client:"tfw"},e||{})}function D(e,t,n){var r={_category_:"tfw_client_event"},s,o;return t=!!t,n=!!n,s=f.aug(r,e||{}),o=s.widget_origin||document.referrer,s.format_version=1,s.dnt=n=n||i(o),s.triggered_on=s.triggered_on||+(new Date),t||(s.widget_origin=o),n&&F(s),s}function P(e){var t=[],n,r,i;for(n in e)e.hasOwnProperty(n)&&(r=encodeURIComponent(n),i=encodeURIComponent(e[n]),i=i.replace(/'/g,"%27"),t.push(r+"="+i));return t.join("&")}function H(){var e=y.createElement("form"),t=y.createElement("input"),n=y.createElement("input");return p++,e.action=w,e.method="POST",e.target="rufous-frame-"+p,e.id="rufous-form-"+p,t.type="hidden",t.name="dnt",t.value=0,n.type="hidden",n.name="tfw_redirect",n.value=E,e.appendChild(t),e.appendChild(n),e}function B(){var e="rufous-frame-"+p;return a({id:e,name:e,width:0,height:0,border:0},{display:"none"},y.document())}function j(e){var t=new Image;t.src=e}function F(e){f.forIn(e,function(t){~f.indexOf(h,t)&&delete e[t]})}function I(e){var t=Array.prototype.toJSON,n;return delete Array.prototype.toJSON,n=JSON.stringify(e),t&&(Array.prototype.toJSON=t),n}var l="twttr_",c={tweetbutton:"//p.twitter.com/t.gif",followbutton:"//p.twitter.com/f.gif",tweetembed:"//p.twitter.com/e.gif"},h=["hask","li","logged_in","pid","user_id",o.guest_id_cookie,l+"hask",l+"li",l+o.guest_id_cookie],p=0,d,v,m=[],g,y,b,w="https://twitter.com/i/jot",E="https://platform.twitter.com/jot.html";o.forceNewCookie(),e({enqueue:A,flush:O,initPostLogging:S,scribeInteraction:N,extractTermsFromDOM:C,addPixel:x,addLegacyPixel:T,addVar:k})})});
-provide("tfw/util/data",function(e){using("util/logger","util/util","util/querystring",function(t,n,r){function c(e){return function(n){n.error?e.error&&e.error(n):n.headers&&n.headers.status!=200?(e.error&&e.error(n),t.warn(n.headers.message)):e.success&&e.success(n),e.complete&&e.complete(n),h(e)}}function h(e){var t=e.script;t&&(t.onload=t.onreadystatechange=null,t.parentNode&&t.parentNode.removeChild(t),e.script=undefined,t=undefined),e.callbackName&&twttr.tfw.callbacks[e.callbackName]&&delete twttr.tfw.callbacks[e.callbackName]}function p(e){var t={};return e.success&&n.isType("function",e.success)&&(t.success=e.success),e.error&&n.isType("function",e.error)&&(t.error=e.error),e.complete&&n.isType("function",e.complete)&&(t.complete=e.complete),t}window.twttr=window.twttr||{},twttr.tfw=twttr.tfw||{},twttr.tfw.callbacks=twttr.tfw.callbacks||{};var i="twttr.tfw.callbacks",s=twttr.tfw.callbacks,o="cb",u=0,a=!1,f={},l={tweets:"https://syndication.twitter.com/tweets.json",timeline:"https://cdn.syndication.twimg.com/widgets/timelines/",timelinePoll:"https://syndication.twitter.com/widgets/timelines/paged/",timelinePreview:"https://syndication.twitter.com/widgets/timelines/preview/"};twttr.widgets&&twttr.widgets.endpoints&&n.aug(l,twttr.widgets.endpoints),f.jsonp=function(e,t,n){var f=n||o+u,l=i+"."+f,h=document.createElement("script"),p={callback:l,suppress_response_codes:!0};s[f]=c(t);if(a||!/^https?\:$/.test(window.location.protocol))e=e.replace(/^\/\//,"https://");h.src=r.url(e,p),h.async="async",document.body.appendChild(h),t.script=h,t.callbackName=f,n||u++},f.config=function(e){if(e.forceSSL===!0||e.forceSSL===!1)a=e.forceSSL},f.tweets=function(e){var t=arguments[0],n=p(t),i={ids:e.ids.join(","),lang:e.lang},s=r.url(l.tweets,i);this.jsonp(s,n)},f.timeline=function(e){var t=arguments[0],i=p(t),s,o=9e5,u=Math.floor(+(new Date)/o),a={lang:e.lang,t:u,domain:window.location.host,dnt:e.dnt,override_type:e.overrideType,override_id:e.overrideId,override_name:e.overrideName,override_owner_id:e.overrideOwnerId,override_owner_name:e.overrideOwnerName,with_replies:e.withReplies};n.compact(a),s=r.url(l.timeline+e.id,a),this.jsonp(s,i,"tl_"+e.id+"_"+e.instanceId)},f.timelinePoll=function(e){var t=arguments[0],i=p(t),s={lang:e.lang,since_id:e.sinceId,max_id:e.maxId,min_position:e.minPosition,max_position:e.maxPosition,domain:window.location.host,dnt:e.dnt,override_type:e.overrideType,override_id:e.overrideId,override_name:e.overrideName,override_owner_id:e.overrideOwnerId,override_owner_name:e.overrideOwnerName,with_replies:e.withReplies},o;n.compact(s),o=r.url(l.timelinePoll+e.id,s),this.jsonp(o,i,"tlPoll_"+e.id+"_"+e.instanceId+"_"+(e.sinceId||e.maxId||e.maxPosition||e.minPosition))},f.timelinePreview=function(e){var t=arguments[0],n=p(t),i=e.params,s=r.url(l.timelinePreview,i);this.jsonp(s,n)},e(f)})});
-provide("anim/transition",function(e){function t(e,t){var n;return t=t||window,n=t.requestAnimationFrame||t.webkitRequestAnimationFrame||t.mozRequestAnimationFrame||t.msRequestAnimationFrame||t.oRequestAnimationFrame||function(n){t.setTimeout(function(){e(+(new Date))},1e3/60)},n(e)}function n(e,t){return Math.sin(Math.PI/2*t)*e}function r(e,n,r,i,s){function a(){var u=+(new Date),f=u-o,l=Math.min(f/r,1),c=i?i(n,l):n*l;e(c);if(l==1)return;t(a,s)}var o=+(new Date),u;t(a)}e({animate:r,requestAnimationFrame:t,easeOut:n})});
-provide("util/datetime",function(e){using("util/util",function(t){function h(e){return e<10?"0"+e:e}function p(e){function i(e,n){return t&&t[e]&&(e=t[e]),e.replace(/%\{([\w_]+)\}/g,function(e,t){return n[t]!==undefined?n[t]:e})}var t=e&&e.phrases,n=e&&e.months||s,r=e&&e.formats||o;this.timeAgo=function(e){var t=p.parseDate(e),s=+(new Date),o=s-t,h;return t?isNaN(o)||o1?i("seconds"):i("second")})})):o1?i("minutes"):i("minute")})})):o1?i("hours"):i("hour")})})):o%{abbr}';p.parseDate=function(e){var o=e||"",u=o.toString(),a,f;return a=function(){var e;if(i.test(u))return parseInt(u,10);if(e=u.match(r))return Date.UTC(e[7],t.indexOf(s,e[1]),e[2],e[3],e[4],e[5]);if(e=u.match(n))return Date.UTC(e[1],e[2]-1,e[3],e[4],e[5],e[6])}(),a?(f=new Date(a),!isNaN(f.getTime())&&f):!1},e(p)})});
-provide("sandbox/frame",function(e){using("sandbox/baseframe","sandbox/minimal","util/env","util/promise","util/util",function(t,n,r,i,s){function h(){var e,t;a={};if(f)return;e=document.body.offsetHeight,t=document.body.offsetWidth;if(e==c&&t==l)return;s.forEach(u,function(e){e.dispatchFrameResize(l,c)}),c=e,l=t}function p(e){var t;return e.id?e.id:(t=e.getAttribute("data-twttr-id"))?t:(t="twttr-sandbox-"+o++,e.setAttribute("data-twttr-id",t),t)}function d(e,t){n.apply(this,[e,t]),this._resizeHandlers=[],u.push(this),this._win.addEventListener?this._win.addEventListener("resize",s.bind(function(){this.dispatchFrameResize()},this),!0):this._win.attachEvent("onresize",s.bind(function(){this.dispatchFrameResize(this._win.event)},this))}var o=0,u=[],a={},f,l=0,c=0;window.addEventListener?window.addEventListener("resize",h,!0):document.body.attachEvent("onresize",function(){h(window.event)}),d.prototype=new n,s.aug(d.prototype,{dispatchFrameResize:function(){var e=this._frame.parentNode,t=p(e),n=a[t];f=!0;if(!this._resizeHandlers.length)return;n||(n=a[t]={w:this._frame.offsetWidth,h:this._frame.offsetHeight});if(this._frameWidth==n.w&&this._frameHeight==n.h)return;this._frameWidth=n.w,this._frameHeight=n.h,s.forEach(this._resizeHandlers,function(e){e(n.w,n.h)}),window.setTimeout(function(){a={}},50)},appendStyleSheet:function(e){var t=this._doc.createElement("link");return t.type="text/css",t.rel="stylesheet",t.href=e,this.layout(s.bind(function(){return this._head.appendChild(t)},this))},appendCss:function(e){var t;return r.cspEnabled()?i.reject("CSP enabled; cannot embed inline styles."):(t=this._doc.createElement("style"),t.type="text/css",t.styleSheet?t.styleSheet.cssText=e:t.appendChild(this._doc.createTextNode(e)),this.layout(s.bind(function(){return this._head.appendChild(t)},this)))},style:function(e){return this.layout(s.bind(function(){s.forIn(e,s.bind(function(e,t){this._frame.style[e]=t},this))},this))},onresize:function(e){this._resizeHandlers.push(e)},width:function(e){return e!==undefined&&(this._frame.width=e),this._frame.offsetWidth},height:function(e){return e!==undefined&&(this._frame.height=e),this._frame.offsetHeight}}),d.createSandbox=function(e,n,r,i){var s=new t(e,n,r,i);return s.ready().then(function(e){return new d(e.frame,e.layout)})},e(d)})});
-provide("tfw/util/assets",function(e){using("util/env",function(t){function r(e,r){var i=n[e],s;return t.retina()?s="2x":t.ie6()||t.ie7()?s="gif":s="default",r&&(s+=".rtl"),i[s]}var n={"embed/timeline.css":{"default":"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.default.css","2x":"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.2x.css",gif:"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.gif.css","default.rtl":"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.default.rtl.css","2x.rtl":"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.2x.rtl.css","gif.rtl":"embed/timeline.037a0cac0aa5abbe2c1b5c5cd368d398.gif.rtl.css"}};e(r)})});
-provide("tfw/widget/syndicatedbase",function(e){using("tfw/widget/base","tfw/widget/intent","tfw/util/assets","tfw/util/globals","tfw/util/tracking","dom/classname","dom/get","dom/delegate","sandbox/frame","util/env","util/promise","util/twitter","util/util",function(t,n,r,i,s,o,u,a,f,l,c,h,p){function S(){y=x.VALID_COLOR.test(i.val("widgets:link-color"))&&RegExp.$1,w=x.VALID_COLOR.test(i.val("widgets:border-color"))&&RegExp.$1,b=i.val("widgets:theme")}function x(e){if(!e)return;var n;this.readyPromise=new c(p.bind(function(e){this.readyResolver=e},this)),this.renderedPromise=new c(p.bind(function(e){this.renderResolver=e},this)),t.apply(this,[e]),n=this.params(),this.targetEl=this.srcEl&&this.srcEl.parentNode||n.targetEl||document.body,this.predefinedWidth=x.VALID_UNIT.test(n.width||this.attr("width"))&&RegExp.$1,this.layout(p.bind(function(){return this.containerWidth=this.targetEl&&this.targetEl.offsetWidth},this)).then(p.bind(function(e){var t=this.predefinedWidth||e||this.dimensions.DEFAULT_WIDTH;this.height=x.VALID_UNIT.test(n.height||this.attr("height"))&&RegExp.$1,this.width=Math.max(this.dimensions.MIN_WIDTH,Math.min(t,this.dimensions.DEFAULT_WIDTH))},this)),x.VALID_COLOR.test(n.linkColor||this.dataAttr("link-color"))?this.linkColor=RegExp.$1:this.linkColor=y,x.VALID_COLOR.test(n.borderColor||this.dataAttr("border-color"))?this.borderColor=RegExp.$1:this.borderColor=w,this.theme=n.theme||this.attr("data-theme")||b,this.theme=/(dark|light)/.test(this.theme)?this.theme:"",this.classAttr.push(l.touch()?"is-touch":"not-touch"),l.ie9()&&this.classAttr.push("ie9"),f.createSandbox({"class":this.renderedClassNames,id:this.id},{width:"1px",height:"0px",border:"none",position:"absolute",visibility:"hidden"},p.bind(function(e){this.srcEl?this.targetEl.insertBefore(e,this.srcEl):this.targetEl.appendChild(e)},this),this.layout).then(p.bind(function(e){this.setupSandbox(e)},this))}var d=[".customisable",".customisable:link",".customisable:visited",".customisable:hover",".customisable:focus",".customisable:active",".customisable-highlight:hover",".customisable-highlight:focus","a:hover .customisable-highlight","a:focus .customisable-highlight"],v=["a:hover .ic-mask","a:focus .ic-mask"],m=[".customisable-border"],g=[".timeline-header h1.summary",".timeline-header h1.summary a:link",".timeline-header h1.summary a:visited"],y,b,w,E={TWEET:0,RETWEET:10};x.prototype=new t,p.aug(x.prototype,{setupSandbox:function(e){this.sandbox=e,c.some(e.appendCss("body{display:none}"),e.setBaseTarget("_blank"),e.appendStyleSheet(twttr.widgets.config.assetUrl()+"/"+r("embed/timeline.css"))).then(p.bind(function(){this.readyResolver.fulfill(e)},this))},ready:function(){return this.readyPromise},rendered:function(){return this.renderedPromise},contentWidth:function(e){var t=this.dimensions,n=this.fullBleedPhoto?0:this.chromeless&&this.narrow?t.NARROW_MEDIA_PADDING_CL:this.chromeless?t.WIDE_MEDIA_PADDING_CL:this.narrow?t.NARROW_MEDIA_PADDING:t.WIDE_MEDIA_PADDING;return(e||this.width)-n},addSiteStyles:function(){var e=p.bind(function(e){return(this.theme=="dark"?".thm-dark ":"")+e},this),t=[];this.headingStyle&&t.push(p.map(g,e).join(",")+"{"+this.headingStyle+"}"),this.linkColor&&(t.push(p.map(d,e).join(",")+"{color:"+this.linkColor+"}"),t.push(p.map(v,e).join(",")+"{background-color:"+this.linkColor+"}")),this.borderColor&&t.push(p.map(m,e).concat(this.theme=="dark"?[".thm-dark.customisable-border"]:[]).join(",")+"{border-color:"+this.borderColor+"}");if(!t.length)return;return this.sandbox.appendCss(t.join(""))},setNarrow:function(){var e=this.narrow;return this.narrow=this.width0&&(e.width=i.width),i.height>0&&(e.height=i.height)}),n=i.height>n?i.height:n},this)),n}}),x.VALID_UNIT=/^([0-9]+)( ?px)?$/,x.VALID_COLOR=/^(#(?:[0-9a-f]{3}|[0-9a-f]{6}))$/i,x.retinize=function(e){if(!l.retina())return;p.forEach(e.getElementsByTagName("IMG"),function(e){var t=e.getAttribute("data-src-2x");t&&(e.src=t)})},x.scaleDimensions=function(e,t,n,r){return t>e&&t>r&&(e*=r/t,t=r),e>n&&(t*=n/e,e=n,t>r&&(e*=r/t,t=r)),{width:Math.ceil(e),height:Math.ceil(t)}},S(),e(x)})});
-provide("tfw/widget/timeline",function(e){using("tfw/widget/base","tfw/widget/syndicatedbase","util/datetime","util/promise","anim/transition","tfw/util/article","tfw/util/data","tfw/util/tracking","tfw/util/params","util/css","util/env","util/throttle","util/twitter","util/querystring","util/typevalidator","util/util","dom/delegate","dom/classname","dom/get",function(t,n,r,i,s,o,u,a,f,l,c,h,p,d,v,m,g,y,b){function I(e){if(!e)return;var t,r,i,s,o,u,a,f;n.apply(this,[e]),t=this.params(),r=(t.chrome||this.dataAttr("chrome")||"").split(" "),this.preview=t.previewParams,this.widgetId=t.widgetId||this.dataAttr("widget-id"),this.instanceId=++F,this.cursors={maxPosition:0,minPosition:0},(s=t.screenName||this.dataAttr("screen-name"))||(o=t.userId||this.dataAttr("user-id"))?this.override={overrideType:"user",overrideId:o,overrideName:s,withReplies:v.asBoolean(t.showReplies||this.dataAttr("show-replies"))?"true":"false"}:(s=t.favoritesScreenName||this.dataAttr("favorites-screen-name"))||(o=t.favoritesUserId||this.dataAttr("favorites-user-id"))?this.override={overrideType:"favorites",overrideId:o,overrideName:s}:((s=t.listOwnerScreenName||this.dataAttr("list-owner-screen-name"))||(o=t.listOwnerId||this.dataAttr("list-owner-id")))&&((u=t.listId||this.dataAttr("list-id"))||(a=t.listSlug||this.dataAttr("list-slug")))?this.override={overrideType:"list",overrideOwnerId:o,overrideOwnerName:s,overrideId:u,overrideName:a}:(f=t.customTimelineId||this.dataAttr("custom-timeline-id"))?this.override={overrideType:"custom",overrideId:f}:this.override={},this.tweetLimit=v.asInt(t.tweetLimit||this.dataAttr("tweet-limit")),this.staticTimeline=this.tweetLimit>0,r.length&&(i=~m.indexOf(r,"none"),this.chromeless=i||~m.indexOf(r,"transparent"),this.headerless=i||~m.indexOf(r,"noheader"),this.footerless=i||~m.indexOf(r,"nofooter"),this.borderless=i||~m.indexOf(r,"noborders"),this.noscrollbar=~m.indexOf(r,"noscrollbar")),this.headingStyle=l.sanitize(t.headingStyle||this.dataAttr("heading-style"),undefined,!0),this.classAttr.push("twitter-timeline-rendered"),this.ariaPolite=t.ariaPolite||this.dataAttr("aria-polite")}var w="1.0",E={CLIENT_SIDE_USER:0,CLIENT_SIDE_APP:2},S="timeline",x="new-tweets-bar",T="timeline-header",N="timeline-footer",C="stream",k="h-feed",L="tweet",A="expanded",O="detail-expander",M="expand",_="permalink",D="twitter-follow-button",P="no-more-pane",H="pending-scroll-in",B="pending-new-tweet",j="pending-new-tweet-display",F=0;I.prototype=new n,m.aug(I.prototype,{renderedClassNames:"twitter-timeline twitter-timeline-rendered",dimensions:{DEFAULT_HEIGHT:"600",DEFAULT_WIDTH:"520",NARROW_WIDTH:"320",MIN_WIDTH:"180",MIN_HEIGHT:"200",WIDE_MEDIA_PADDING:81,NARROW_MEDIA_PADDING:16,WIDE_MEDIA_PADDING_CL:60,NARROW_MEDIA_PADDING_CL:12},create:function(e){var r=this.sandbox.createElement("div"),i,s,o,u=[],f;r.innerHTML=e.body,i=r.children[0]||!1;if(!i)return;return this.reconfigure(e.config),this.discardStaticOverflow(i),this.sandbox.setTitle(i.getAttribute("data-iframe-title")||"Timeline"),n.retinize(i),this.constrainMedia(i),this.searchQuery=i.getAttribute("data-search-query"),this.profileId=i.getAttribute("data-profile-id"),this.timelineType=i.getAttribute("data-timeline-type"),f=this.getTweetDetails(r),m.forIn(f,function(e){u.push(e)}),o=this.baseScribeData(),o.item_ids=u,o.item_details=f,this.timelineType&&a.enqueue({page:this.timelineType+"_timeline",component:"timeline",element:"initial",action:u.length?"results":"no_results"},o,!0,this.dnt),a.enqueue({page:"timeline",component:"timeline",element:"initial",action:u.length?"results":"no_results"},o,!0,this.dnt),a.flush(),this.ariaPolite=="assertive"&&(s=b.one(x,i,"DIV"),s.setAttribute("aria-polite","assertive")),i.id=this.id,i.className+=" "+this.classAttr.join(" "),i.lang=this.lang,this.augmentWidgets(i),this.ready().then(m.bind(function(e){e.appendChild(i).then(m.bind(function(){this.renderResolver.fulfill(this.sandbox)},this)),e.style({cssText:"",border:"none",maxWidth:"100%",minWidth:this.dimensions.MIN_WIDTH+"px"}),this.layout(m.bind(function(){this.srcEl&&this.srcEl.parentNode&&this.srcEl.parentNode.removeChild(this.srcEl),this.predefinedWidth=this.width,this.predefinedHeight=this.height,this.width=e.width(this.width),this.height=e.height(this.height)},this)).then(m.bind(function(){this.width0&&e.insertNewTweets(t)})},loadMore:function(){var e=this,t=b.all(L,this.element,"LI").pop(),n=t&&t.getAttribute("data-tweet-id");this.requestTweets(n,!1,function(t){var r=b.one(P,e.element,"P"),i=t.childNodes[0];r.style.cssText="",i&&i.getAttribute("data-tweet-id")==n&&t.removeChild(i);if(t.childNodes.length>0){e.appendTweets(t);return}y.add(e.element,"no-more"),r.focus()})},forceLoad:function(){var e=this,t=!!b.all(k,this.element,"OL").length;this.requestTweets(1,!0,function(n){n.childNodes.length&&(e[t?"insertNewTweets":"appendTweets"](n),y.add(e.element,"has-tweets"))})},schedulePolling:function(e){var t=this;if(this.pollInterval===null)return;e=twttr.widgets.poll||e||this.pollInterval||1e4,e>-1&&window.setTimeout(function(){this.isUpdating||t.update(),t.schedulePolling()},e)},updateCursors:function(e,t){(t||{}).initial?(this.cursors.maxPosition=e.maxPosition,this.cursors.minPosition=e.minPosition):(t||{}).newer?this.cursors.maxPosition=e.maxPosition||this.cursors.maxPosition:this.cursors.minPosition=e.minPosition||this.cursors.minPosition},requestTweets:function(e,t,r){var i=this,s={id:this.widgetId,instanceId:this.instanceId,screenName:this.widgetScreenName,userId:this.widgetUserId,withReplies:this.widgetShowReplies,dnt:this.dnt,lang:this.lang};t&&this.cursors.maxPosition?s.minPosition=this.cursors.maxPosition:!t&&this.cursors.minPosition?s.maxPosition=this.cursors.minPosition:t?s.sinceId=e:s.maxId=e,s.complete=function(){this.isUpdating=!1},s.error=function(e){if(e&&e.headers){if(e.headers.status=="404"){i.pollInterval=null;return}if(e.headers.status=="503"){i.pollInterval*=1.5;return}}},s.success=function(e){var s=i.sandbox.createDocumentFragment(),o=i.sandbox.createElement("div"),u,f=[],l,c;i.updateCursors(e.headers,{newer:t}),e&&e.headers&&e.headers.xPolling&&/\d+/.test(e.headers.xPolling)&&(i.pollInterval=e.headers.xPolling*1e3);if(e&&e.body!==undefined){o.innerHTML=e.body;if(o.children[0]&&o.children[0].tagName!="LI")return;l=i.getTweetDetails(o);for(c in l)l.hasOwnProperty(c)&&f.push(c);f.length&&(u=i.baseScribeData(),u.item_ids=f,u.item_details=l,u.event_initiator=t?E.CLIENT_SIDE_APP:E.CLIENT_SIDE_USER,this.timelineType&&a.enqueue({page:this.timelineType+"_timeline",component:"timeline",element:"initial",action:f.length?"results":"no_results"},u,!0,this.dnt),a.enqueue({page:"timeline",component:"timeline",element:t?"newer":"older",action:"results"},u,!0,i.dnt),a.flush()),n.retinize(o),i.constrainMedia(o);while(o.children[0])s.appendChild(o.children[0]);r(s)}},u.timelinePoll(m.aug(s,this.override))},insertNewTweets:function(e){var t=this,n=b.one(C,this.element,"DIV"),r=b.one(k,n,"OL"),i=r.offsetHeight,o;r.insertBefore(e,r.firstChild),o=r.offsetHeight-i;if(n.scrollTop>40||this.mouseIsOver()){n.scrollTop=n.scrollTop+o,this.updateTimeStamps(),this.showNewTweetNotifier();return}y.remove(this.element,H),r.style.cssText="margin-top: -"+o+"px",window.setTimeout(function(){n.scrollTop=0,y.add(t.element,H),c.cssTransitions()?r.style.cssText="":s.animate(function(e){ee&&(r=t.children[n-1]);n--)t.removeChild(r)},showNewTweetNotifier:function(){var e=this,t=b.one(x,this.element,"DIV"),n=t.children[0];t.style.cssText="",t.removeChild(n),t.appendChild(n),y.add(this.element,j),window.setTimeout(function(){y.add(e.element,B)},10),this.newNoticeDisplayTime=+(new Date),window.setTimeout(function(){e.hideNewTweetNotifier()},5e3)},hideNewTweetNotifier:function(e){var t=this;if(!e&&this.mouseOverNotifier)return;y.remove(this.element,B),window.setTimeout(function(){y.remove(t.element,j)},500)},augmentWidgets:function(e){var t=b.one(D,e,"A");if(!t)return;t.setAttribute("data-related",this.related),t.setAttribute("data-partner",this.partner),t.setAttribute("data-dnt",this.dnt),t.setAttribute("data-search-query",this.searchQuery),t.setAttribute("data-profile-id",this.profileId),this.width<250&&t.setAttribute("data-show-screen-name","false"),twttr.widgets.load(t.parentNode)},discardStaticOverflow:function(e){var t=b.one(k,e,"OL"),n;if(this.staticTimeline){this.height=0;while(n=t.children[this.tweetLimit])t.removeChild(n)}},hideStreamScrollBar:function(){var e=b.one(C,this.element,"DIV"),t=b.one(k,this.element,"OL"),n;e.style.width="",n=this.element.offsetWidth-t.offsetWidth,n>0&&(e.style.width=this.element.offsetWidth+n+"px")},readTranslations:function(){var e=this.element,t="data-dt-";this.datetime=new r(m.compact({phrases:{now:e.getAttribute(t+"now"),s:e.getAttribute(t+"s"),m:e.getAttribute(t+"m"),h:e.getAttribute(t+"h"),second:e.getAttribute(t+"second"),seconds:e.getAttribute(t+"seconds"),minute:e.getAttribute(t+"minute"),minutes:e.getAttribute(t+"minutes"),hour:e.getAttribute(t+"hour"),hours:e.getAttribute(t+"hours")},months:e.getAttribute(t+"months").split("|"),formats:{abbr:e.getAttribute(t+"abbr"),shortdate:e.getAttribute(t+"short"),longdate:e.getAttribute(t+"long")}}))},updateTimeStamps:function(){var e=b.all(_,this.element,"A"),t,n,r=0,i,s;for(;t=e[r];r++){i=t.getAttribute("data-datetime"),s=i&&this.datetime.timeAgo(i,this.i18n),n=t.getElementsByTagName("TIME")[0];if(!s)continue;if(n&&n.innerHTML){n.innerHTML=s;continue}t.innerHTML=s}},mouseIsOver:function(){return this.mouseOver},addUrlParams:function(e){var t=this,n={tw_w:this.widgetId,related:this.related,partner:this.partner,query:this.searchQuery,profile_id:this.profileId,original_referer:o.url(),tw_p:"embeddedtimeline"};return this.addUrlParams=f(n,function(e){var n=b.ancestor("."+L,e,t.element);return n&&{tw_i:n.getAttribute("data-tweet-id")}}),this.addUrlParams(e)},showNSFW:function(e){var t=b.one("nsfw",e,"DIV"),r,i,s=0,o,u,a,f;if(!t)return;i=n.scaleDimensions(t.getAttribute("data-width"),t.getAttribute("data-height"),this.contentWidth(),t.getAttribute("data-height")),r=!!(u=t.getAttribute("data-player")),r?a=this.sandbox.createElement("iframe"):(a=this.sandbox.createElement("img"),u=t.getAttribute(c.retina()?"data-image-2x":"data-image"),a.alt=t.getAttribute("data-alt"),f=this.sandbox.createElement("a"),f.href=t.getAttribute("data-href"),f.appendChild(a)),a.title=t.getAttribute("data-title"),a.src=u,a.width=i.width,a.height=i.height,o=b.ancestor("."+O,t,e),s=i.height-t.offsetHeight,t.parentNode.replaceChild(r?a:f,t),o.style.cssText="height:"+(o.offsetHeight+s)+"px"},toggleExpando:function(e){var r=b.one(O,e,"DIV"),i=r&&r.children[0],s=i&&i.getAttribute("data-expanded-media"),o,u=0,a=b.one(M,e,"A"),f=a&&a.getElementsByTagName("B")[0],l=f&&(f.innerText||f.textContent),c;if(!f)return;this.layout(function(){f.innerHTML=a.getAttribute("data-toggled-text"),a.setAttribute("data-toggled-text",l)});if(y.present(e,A)){this.layout(function(){y.remove(e,A)});if(!r){t.doLayout();return}this.layout(function(){r.style.cssText="",i.innerHTML=""}),t.doLayout();return}s&&(o=this.sandbox.createElement("DIV"),o.innerHTML=s,n.retinize(o),u=this.constrainMedia(o),this.layout(function(){i.appendChild(o)})),r&&this.layout(function(){c=Math.max(i.offsetHeight,u),r.style.cssText="height:"+c+"px"}),this.layout(function(){y.add(e,A)}),t.doLayout()},recalculateStreamHeight:function(e){var t=b.one(T,this.element,"DIV"),n=b.one(N,this.element,"DIV"),r=b.one(C,this.element,"DIV");this.layout(m.bind(function(){var i=t.offsetHeight+(n?n.offsetHeight:0),s=e||this.sandbox.height();r.style.cssText="height:"+(s-i-2)+"px",this.noscrollbar&&this.hideStreamScrollBar()},this))},handleResize:function(e,n){var r=Math.min(this.dimensions.DEFAULT_WIDTH,Math.max(this.dimensions.MIN_WIDTH,Math.min(this.predefinedWidth||this.dimensions.DEFAULT_WIDTH,e)));if(r==this.width&&n==this.height)return;this.width=r,this.height=n,this.setNarrow(),this.constrainMedia(this.element,this.contentWidth(r)),this.staticTimeline?this.layout(m.bind(function(){this.height=this.element.offsetHeight,this.sandbox.height(this.height)},this)):this.recalculateStreamHeight(n),t.doLayoutAsync()}}),e(I)})});
-provide("tfw/widget/embed",function(e){using("tfw/widget/base","tfw/widget/syndicatedbase","util/datetime","tfw/util/params","dom/classname","dom/get","util/env","util/promise","util/util","util/throttle","util/twitter","tfw/util/article","tfw/util/data","tfw/util/tracking",function(t,n,r,i,s,o,u,a,f,l,c,h,p,d){function w(e,t,n,r){var i=o.one("subject",e,"BLOCKQUOTE"),s=o.one("reply",e,"BLOCKQUOTE"),u=i&&i.getAttribute("data-tweet-id"),a=s&&s.getAttribute("data-tweet-id"),l={},c={};if(!u)return;l[u]={item_type:0},d.enqueue({page:"tweet",section:"subject",component:"tweet",action:"results"},f.aug({},t,{item_ids:[u],item_details:l}),!0,r);if(!a)return;c[a]={item_type:0},d.enqueue({page:"tweet",section:"conversation",component:"tweet",action:"results"},f.aug({},t,{item_ids:[a],item_details:c,associations:{4:{association_id:u,association_type:4}}}),!0,r)}function E(e,t,n){var r={};if(!e)return;r[e]={item_type:0},d.enqueue({page:"tweet",section:"subject",component:"rawembedcode",action:"no_results"},{client_version:v,widget_origin:h.url(),widget_frame:h.frameUrl(),message:t,item_ids:[e],item_details:r},!0,n)}function S(e,t,n,r){g[e]=g[e]||[],g[e].push({s:n,f:r,lang:t})}function x(){b.length&&twttr.widgets.load(b)}function T(e){if(!e)return;var t,r,i;n.apply(this,[e]),t=this.params(),r=this.srcEl&&this.srcEl.getElementsByTagName("A"),i=r&&r[r.length-1],this.hideThread=(t.conversation||this.dataAttr("conversation"))=="none"||~f.indexOf(this.classAttr,"tw-hide-thread"),this.hideCard=(t.cards||this.dataAttr("cards"))=="hidden"||~f.indexOf(this.classAttr,"tw-hide-media");if((t.align||this.attr("align"))=="left"||~f.indexOf(this.classAttr,"tw-align-left"))this.align="left";else if((t.align||this.attr("align"))=="right"||~f.indexOf(this.classAttr,"tw-align-right"))this.align="right";else if((t.align||this.attr("align"))=="center"||~f.indexOf(this.classAttr,"tw-align-center"))this.align="center",this.containerWidth>this.dimensions.MIN_WIDTH*(1/.7)&&this.width>this.containerWidth*.7&&(this.width=this.containerWidth*.7);this.narrow=t.narrow||this.width<=this.dimensions.NARROW_WIDTH,this.narrow&&this.classAttr.push("var-narrow"),this.tweetId=t.tweetId||i&&c.status(i.href)}var v="2.0",m="tweetembed",g={},y=[],b=[];T.prototype=new n,f.aug(T.prototype,{renderedClassNames:"twitter-tweet twitter-tweet-rendered",dimensions:{DEFAULT_HEIGHT:"0",DEFAULT_WIDTH:"500",NARROW_WIDTH:"350",MIN_WIDTH:"220",MIN_HEIGHT:"0",WIDE_MEDIA_PADDING:32,NARROW_MEDIA_PADDING:32},create:function(e){var t=this.sandbox.createElement("div"),r;t.innerHTML=e,r=t.children[0]||!1;if(!r)return;return this.theme=="dark"&&this.classAttr.push("thm-dark"),this.linkColor&&this.addSiteStyles(),s.present(r,"media-forward")&&(this.fullBleedPhoto=!0),this.augmentWidgets(r),n.retinize(r),r.id=this.id,r.className+=" "+this.classAttr.join(" "),r.lang=this.lang,this.sandbox.setTitle(r.getAttribute("data-iframe-title")||"Tweet"),this.sandbox.appendChild(r).then(f.bind(function(){this.renderResolver.fulfill(this.sandbox)},this)),this.sandbox.style({cssText:"",display:"block",maxWidth:"99%",minWidth:this.dimensions.MIN_WIDTH+"px",padding:"0",borderRadius:"5px",margin:"10px 0",border:"#ddd 1px solid",borderTopColor:"#eee",borderBottomColor:"#bbb",boxShadow:"0 1px 3px rgba(0,0,0,0.15)",position:"absolute",visibility:"hidden"}),this.layout(f.bind(function(){this.predefinedWidth=this.width,this.width=this.sandbox.width(this.width)},this),"Insert Sandbox"),this.setNarrow().then(f.bind(function(){this.constrainMedia(r,this.contentWidth(this.width))},this)),w(r,this.baseScribeData(),this.partner,this.dnt),r},render:function(e,n){var r="",i=this.tweetId,s,o;return i?(o=new a(function(e){s=e}),this.hideCard&&(r+="c"),this.hideThread&&(r+="t"),r&&(i+="-"+r),this.rendered().then(f.bind(function(e){this.srcEl&&this.srcEl.parentNode&&this.layout(f.bind(function(){this.srcEl&&this.srcEl.parentNode&&this.srcEl.parentNode.removeChild(this.srcEl)},this),"Remove Embed Code"),this.align=="center"?e.style({margin:"7px auto",cssFloat:"none"}):this.align&&(this.width==this.dimensions.DEFAULT_WIDTH&&(this.predefinedWidth=this.width=this.dimensions.NARROW_WIDTH),e.style({cssFloat:this.align})),this.layout(f.bind(function(){this.height=this.sandbox.height(this.element.offsetHeight)},this)).then(f.bind(function(){return t.doLayoutAsync(),this.layout(f.bind(function(){this.height=this.sandbox.height(this.element.offsetHeight)},this))},this)).then(f.bind(function(){e.onresize(f.bind(this.handleResize,this))},this)),e.style({position:"static",visibility:"visible"}),t.doLayoutAsync()},this)),S(i,this.lang,f.bind(function(n){this.ready().then(f.bind(function(){this.element=this.create(n),this.readTimestampTranslations(),this.updateTimeStamps(),this.bindIntentHandlers(),t.doLayoutAsync(),s.fulfill(this.sandbox.element())},this))},this),f.bind(function(){E(this.tweetId,this.partner,this.dnt),s.fulfill(this.srcEl)},this)),y.push(o),n&&o.then(n),o):a.fulfill(this.srcEl)},augmentWidgets:function(e){var t=o.one("twitter-follow-button",e,"A");if(!t)return;t.setAttribute("data-related",this.related),t.setAttribute("data-partner",this.partner),t.setAttribute("data-dnt",this.dnt),t.setAttribute("data-show-screen-name","false"),b.push(t.parentNode)},addUrlParams:function(e){var t=this,n={related:this.related,partner:this.partner,original_referer:h.url(),tw_p:m};return this.addUrlParams=i(n,function(e){var n=o.ancestor(".tweet",e,t.element);return{tw_i:n.getAttribute("data-tweet-id")}}),this.addUrlParams(e)},baseScribeData:function(){return{client_version:v,widget_origin:h.url(),widget_frame:h.frameUrl(),message:this.partner}},handleResize:function(e){var n=Math.min(this.dimensions.DEFAULT_WIDTH,Math.max(this.dimensions.MIN_WIDTH,Math.min(this.predefinedWidth||this.dimensions.DEFAULT_WIDTH,e)));if(n==this.width)return;this.width=n,this.setNarrow(),this.constrainMedia(this.element,this.contentWidth(n)),this.layout(f.bind(function(){this.height=this.element.offsetHeight,this.sandbox.height(this.height)},this),"Embed Resize"),t.doLayoutAsync()},readTimestampTranslations:function(){var e=this.element,t="data-dt-",n=e.getAttribute(t+"months")||"";this.datetime=new r(f.compact({phrases:{AM:e.getAttribute(t+"am"),PM:e.getAttribute(t+"pm")},months:n.split("|"),formats:{full:e.getAttribute(t+"full")}}))},updateTimeStamps:function(){var e=o.one("long-permalink",this.element,"A"),n=e.getAttribute("data-datetime"),r=n&&this.datetime.localTimeStamp(n),i=e.getElementsByTagName("TIME")[0];if(!r)return;this.layout(function(){if(i&&i.innerHTML){i.innerHTML=r;return}e.innerHTML=r},"Update Timestamp"),t.doLayoutAsync()}}),T.fetchAndRender=function(){var e=g,n=[],r,i;g={};if(e.keys)n=e.keys();else for(r in e)e.hasOwnProperty(r)&&n.push(r);if(!n.length)return;d.initPostLogging(),i=e[n[0]][0].lang,p.tweets({ids:n.sort(),lang:i,complete:function(n){f.forIn(n,function(t,n){var r=e[t];f.forEach(r,function(e){e.s&&e.s.call(this,n)}),delete e[t]}),t.doLayout(),f.forIn(e,function(e,t){f.forEach(t,function(t){t.f&&t.f.call(this,e)})}),t.doLayout()}}),a.every.apply(null,y).then(function(){x(),d.flush()})},t.afterLoad(T.fetchAndRender),e(T)})});
-provide("dom/textsize",function(e){function n(e,t,n){var r=[],i=0,s;for(;s=n[i];i++)r.push(s[0]),r.push(s[1]);return e+t+r.join(":")}function r(e){var t=e||"";return t.replace(/([A-Z])/g,function(e){return"-"+e.toLowerCase()})}var t={};e(function(e,i,s){var o=document.createElement("span"),u={},a="",f,l=0,c=0,h=[];s=s||[],i=i||"",a=n(e,i,s);if(t[a])return t[a];o.className=i+" twitter-measurement";try{for(;f=s[l];l++)o.style[f[0]]=f[1]}catch(p){for(;f=s[c];c++)h.push(r(f[0])+":"+f[1]);o.setAttribute("style",h.join(";")+";")}return o.innerHTML=e,document.body.appendChild(o),u.width=o.clientWidth||o.offsetWidth,u.height=o.clientHeight||o.offsetHeight,document.body.removeChild(o),delete o,t[a]=u})});
-provide("tfw/widget/tweetbase",function(e){using("util/util","tfw/widget/base","util/querystring","util/twitter",function(t,n,r,i){function s(e){if(!e)return;var t;n.apply(this,[e]),t=this.params(),this.text=t.text||this.dataAttr("text"),this.text&&/\+/.test(this.text)&&!/ /.test(this.text)&&(this.text=this.text.replace(/\+/g," ")),this.align=t.align||this.dataAttr("align")||"",this.via=t.via||this.dataAttr("via"),this.placeid=t.placeid||this.dataAttr("placeid"),this.hashtags=t.hashtags||this.dataAttr("hashtags"),this.screen_name=i.screenName(t.screen_name||t.screenName||this.dataAttr("button-screen-name")),this.url=t.url||this.dataAttr("url")}s.prototype=new n,t.aug(s.prototype,{parameters:function(){var e={text:this.text,url:this.url,related:this.related,lang:this.lang,placeid:this.placeid,original_referer:location.href,id:this.id,screen_name:this.screen_name,hashtags:this.hashtags,partner:this.partner,dnt:this.dnt,_:+(new Date)};return t.compact(e),r.encode(e)}}),e(s)})});
-provide("tfw/widget/tweetbutton",function(e){using("tfw/widget/tweetbase","util/util","util/querystring","util/uri","util/twitter","dom/textsize",function(t,n,r,i,s,o){function l(e){t.apply(this,[e]);var r=this.params(),o=r.count||this.dataAttr("count"),l=r.size||this.dataAttr("size"),c=i.getScreenNameFromPage();this.classAttr.push("twitter-tweet-button");if(r.type=="hashtag"||~n.indexOf(this.classAttr,"twitter-hashtag-button"))this.type="hashtag",this.classAttr.push("twitter-hashtag-button");else if(r.type=="mention"||~n.indexOf(this.classAttr,"twitter-mention-button"))this.type="mention",this.classAttr.push("twitter-mention-button");this.counturl=r.counturl||this.dataAttr("counturl"),this.searchlink=r.searchlink||this.dataAttr("searchlink"),this.button_hashtag=s.hashTag(r.button_hashtag||r.hashtag||this.dataAttr("button-hashtag"),!1),this.size=l=="large"?"l":"m",this.type?(this.count="none",c&&(this.related=this.related?c+","+this.related:c)):(this.text=this.text||u,this.url=this.url||i.getCanonicalURL()||a,this.count=~n.indexOf(f,o)?o:"horizontal",this.count=this.count=="vertical"&&this.size=="l"?"none":this.count,this.via=this.via||c)}var u=document.title,a=encodeURI(location.href),f=["vertical","horizontal","none"];l.prototype=new t,n.aug(l.prototype,{parameters:function(){var e={text:this.text,url:this.url,via:this.via,related:this.related,count:this.count,lang:this.lang,counturl:this.counturl,searchlink:this.searchlink,placeid:this.placeid,original_referer:location.href,id:this.id,size:this.size,type:this.type,screen_name:this.screen_name,button_hashtag:this.button_hashtag,hashtags:this.hashtags,align:this.align,partner:this.partner,dnt:this.dnt,_:+(new Date)};return n.compact(e),r.encode(e)},height:function(){return this.count=="vertical"?62:this.size=="m"?20:28},width:function(){var e={ver:8,cnt:14,btn:24,xlcnt:18,xlbtn:38},t=this.count=="vertical",r=this.type=="hashtag"&&this.button_hashtag?"Tweet %{hashtag}":this.type=="mention"&&this.screen_name?"Tweet to %{name}":"Tweet",i=this._(r,{name:"@"+this.screen_name,hashtag:"#"+this.button_hashtag}),s=this._("K"),u=this._("100K+"),a=(t?"8888":"88888")+s,f=0,l=0,c=0,h=0,p=this.styles.base,d=p;return~n.indexOf(["ja","ko"],this.lang)?a+=this._("10k unit"):a=a.length>u.length?a:u,t?(d=p.concat(this.styles.vbubble),h=e.ver,c=e.btn):this.size=="l"?(p=d=p.concat(this.styles.large),c=e.xlbtn,h=e.xlcnt):(c=e.btn,h=e.cnt),this.count!="none"&&(l=o(a,"",d).width+h),f=o(i,"",p.concat(this.styles.button)).width+c,t?f>l?f:l:this.calculatedWidth=f+l},render:function(e,t){var r=twttr.widgets.config.assetUrl()+"/widgets/tweet_button.1387492107.html#"+this.parameters(),i;return this.count&&this.classAttr.push("twitter-count-"+this.count),i=this.create(r,this.dimensions(),{title:this._("Twitter Tweet Button")}).then(n.bind(function(e){return this.element=e},this)),t&&i.then(t),i}}),e(l)})});
-provide("tfw/widget/follow",function(e){using("util/util","tfw/widget/base","util/querystring","util/uri","util/twitter","util/promise","dom/textsize",function(t,n,r,i,s,o,u){function a(e){if(!e)return;var t,r,i,o;n.apply(this,[e]),t=this.params(),r=t.size||this.dataAttr("size"),i=t.showScreenName||this.dataAttr("show-screen-name"),o=t.count||this.dataAttr("count"),this.classAttr.push("twitter-follow-button"),this.showScreenName=i!="false",this.showCount=t.showCount!==!1&&this.dataAttr("show-count")!="false",o=="none"&&(this.showCount=!1),this.explicitWidth=t.width||this.dataAttr("width")||"",this.screenName=t.screen_name||t.screenName||s.screenName(this.attr("href")),this.preview=t.preview||this.dataAttr("preview")||"",this.align=t.align||this.dataAttr("align")||"",this.size=r=="large"?"l":"m"}a.prototype=new n,t.aug(a.prototype,{parameters:function(){var e={screen_name:this.screenName,lang:this.lang,show_count:this.showCount,show_screen_name:this.showScreenName,align:this.align,id:this.id,preview:this.preview,size:this.size,partner:this.partner,dnt:this.dnt,_:+(new Date)};return t.compact(e),r.encode(e)},width:function(){if(this.calculatedWidth)return this.calculatedWidth;if(this.explicitWidth)return this.explicitWidth;var e={cnt:13,btn:24,xlcnt:22,xlbtn:38},n=this.showScreenName?"Follow %{screen_name}":"Follow",r=this._(n,{screen_name:"@"+this.screenName}),i=~t.indexOf(["ja","ko"],this.lang)?this._("10k unit"):this._("M"),s=this._("%{followers_count} followers",{followers_count:"88888"+i}),o=0,a=0,f,l,c=this.styles.base;return this.size=="l"?(c=c.concat(this.styles.large),f=e.xlbtn,l=e.xlcnt):(f=e.btn,l=e.cnt),this.showCount&&(a=u(s,"",c).width+l),o=u(r,"",c.concat(this.styles.button)).width+f,this.calculatedWidth=o+a},render:function(e,n){if(!this.screenName)return o.reject("Missing Screen Name").then(n);var r=twttr.widgets.config.assetUrl()+"/widgets/follow_button.1387492107.html#"+this.parameters(),i=this.create(r,this.dimensions(),{title:this._("Twitter Follow Button")}).then(t.bind(function(e){return this.element=e},this));return n&&i.then(n),i}}),e(a)})});
-!function(){window.twttr=window.twttr||{},twttr.host=twttr.host||"platform.twitter.com",using("util/domready","util/env",function(e,t){function n(e){return(e||!/^http\:$/.test(window.location.protocol))&&!twttr.ignoreSSL?"https":"http"}if(t.ie6())return;if(twttr.widgets&&twttr.widgets.loaded)return twttr.widgets.load(),!1;if(twttr.init)return!1;twttr.init=!0,twttr._e=twttr._e||[],twttr.ready=twttr.ready||function(e){twttr.widgets&&twttr.widgets.loaded?e(twttr):twttr._e.push(e)},using.path.length||(using.path=n()+"://"+twttr.host+"/js"),twttr.ignoreSSL=twttr.ignoreSSL||!1;var r=[];twttr.events={bind:function(e,t){return r.push([e,t])}},e(function(){using("tfw/widget/base","tfw/widget/follow","tfw/widget/tweetbutton","tfw/widget/embed","tfw/widget/timeline","tfw/widget/intent","tfw/util/article","util/events","util/util",function(e,t,i,s,o,u,a,f,l){function m(e){var t=twttr.host;return n(e)=="https"&&twttr.secureHost&&(t=twttr.secureHost),n(e)+"://"+t}function g(){using("tfw/hub/client",function(e){twttr.events.hub=e.init(p),e.init(p,!0)})}var c,h,p={widgets:{"a.twitter-share-button":i,"a.twitter-mention-button":i,"a.twitter-hashtag-button":i,"a.twitter-follow-button":t,"blockquote.twitter-tweet":s,"a.twitter-timeline":o,"div.twitter-timeline":o,body:u}},d=twttr.events&&twttr.events.hub?twttr.events:{},v;p.assetUrl=m,twttr.widgets=twttr.widgets||{},l.aug(twttr.widgets,{config:{assetUrl:m},load:function(t){e.init(p),e.embed(t),twttr.widgets.loaded=!0},createShareButton:function(t,n,r,s){if(!t||!n)return r&&r(!1);s=l.aug({},s||{},{url:t,targetEl:n});var o=new i(s);e.doLayout(),o.render(p,r)},createHashtagButton:function(t,n,r,s){if(!t||!n)return r&&r(!1);s=l.aug({},s||{},{hashtag:t,targetEl:n,type:"hashtag"});var o=new i(s);e.doLayout(),o.render(p,r)},createMentionButton:function(t,n,r,s){if(!t||!n)return r&&r(!1);s=l.aug({},s||{},{screenName:t,targetEl:n,type:"mention"});var o=new i(s);e.doLayout(),o.render(p,r)},createFollowButton:function(n,r,i,s){if(!n||!r)return i&&i(!1);s=l.aug({},s||{},{screenName:n,targetEl:r});var o=new t(s);e.doLayout(),o.render(p,i)},createTweet:function(t,n,r,i){if(!t||!n)return r&&r(!1);i=l.aug({},i||{},{tweetId:t,targetEl:n});var o=new s(i);e.doLayout(),o.render(p,r),s.fetchAndRender()},createTimeline:function(t,n,r,i){if(!t||!n)return r&&r(!1);i=l.aug({},i||{},{widgetId:t,targetEl:n});var s=new o(i);e.doLayout(),s.render(p,r)}}),l.aug(twttr.events,d,f.Emitter),v=twttr.events.bind,twttr.events.bind=function(e,t){g(),this.bind=v,this.bind(e,t)};for(c=0;h=r[c];c++)twttr.events.bind(h[0],h[1]);for(c=0;h=twttr._e[c];c++)h(twttr);twttr.ready=function(e){e(twttr)},/twitter\.com(\:\d+)?$/.test(document.location.host)&&(twttr.widgets.createTimelinePreview=function(t,n,r){if(!p||!n)return r&&r(!1);var i=new o({previewParams:t,targetEl:n,linkColor:t.link_color,theme:t.theme,height:t.height});e.doLayout(),i.render(p,r)}),twttr.widgets.createTweetEmbed=twttr.widgets.createTweet,twttr.widgets.load()})})})}()});
\ No newline at end of file
diff --git a/params.json b/params.json
deleted file mode 100644
index e7e1a7e..0000000
--- a/params.json
+++ /dev/null
@@ -1 +0,0 @@
-{"tagline":"Protocol Language","body":"### What is Scribble?\r\nScribble is a language to describe application-level protocols among communicating systems. A protocol represents an agreement on how participating systems interact with each other. Without a protocol, it is hard to do a meaningful interaction: participants simply cannot communicate effectively, since they do not know when to expect the other parties to send their data, or whether the other party is ready to receive a datum it is sending. In fact it is not clear what kinds of data is to be used for each interaction. It is too costly to carry out communications based on guess works and with inevitable communication mismatch (synchronisation bugs). Simply, it is not feasible as an engineering practice.\r\n\r\nScribble presents a stratified description language:\r\n\r\n* The bottom layer is the type layer, in which we describe the bare skeleton of conversations structures as types for interactions (known in the literature as session type).\r\n* The assertion layer allows elaboration of a type-layer description using assertions.\r\n* Finally the third layer, protocol document layer, allows description of multiple protocols and constraints over them.\r\n\r\nThe latest version of the language specification can be found [here](langref.html)\r\n\r\n### A Simple Example\r\n\r\n```\r\nprotocol HelloWorld (role Me) {\r\n Me introduces World;\r\n\r\n Hello from Me to World;\r\n}\r\n```\r\n\r\n### How can it be used?\r\nThe development and validation of programs against protocol descriptions could proceed as follows:\r\n\r\n* A programmer specifies a set of protocols to be used in her application.\r\n* She can verify that those protocols are valid, free from livelocks and deadlocks.\r\n* She develops her application referring to those protocols, potentially using communication constructs available in the chosen programming language.\r\n* She validates her programs against protocols using a protocol checker, which detects lack of conformance.\r\n* At the execution time, a local monitor can validate messages with respect to given protocols, optionally blocking invalid messages from being delivered.\r\n\r\n","google":"","note":"Don't delete this file! It's used internally to help with page regeneration.","name":"Scribble"}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..c35336b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,145 @@
+
+
+
+ 4.0.0
+ org.scribble
+ website
+ war
+
+
+
+
+
+
+ 1.0.0-SNAPSHOT
+
+ Scribble Project Website
+ Scribble Project Website (uses asciidoc@jBake)
+
+ http://scribble.github.io
+ 2015
+
+ Scribble
+ http://www.scribble.org/
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+
+
+
+
+ UTF-8
+ github
+ 1.5.2
+ 0.0.9
+ 0.11
+ 2.3.2
+ 2.3.20
+ 1.4.2
+
+
+
+ website
+
+
+
+ br.com.ingenieux
+ jbake-maven-plugin
+ ${jbake-maven-plugin.version}
+
+
+ default-generate
+ generate-resources
+
+ generate
+
+
+
+
+
+
+
+ org.jbake
+ jbake-core
+ ${jbake-core.version}
+
+
+
+ org.asciidoctor
+ asciidoctorj
+ ${asciidoctorj.version}
+
+
+
+ org.freemarker
+ freemarker
+ ${freemarker.version}
+
+
+
+ org.pegdown
+ pegdown
+ ${pegdown.version}
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.6
+
+
+ false
+
+
+
+
+ com.github.github
+ site-maven-plugin
+ ${site-maven-plugin.version}
+
+ Creating site for ${project.version}
+ ${project.build.directory}/${project.build.finalName}
+ scribble.github.io
+ scribble
+ refs/heads/master
+ true
+
+ **/*.jsp
+ **/web.xml
+
+
+
+
+
+ site
+
+ site
+
+
+
+
+
+
diff --git a/scribble-java.html b/scribble-java.html
deleted file mode 100644
index afcab8d..0000000
--- a/scribble-java.html
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
-
-
-
-
-
-
- Scribble Java
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Java Implementation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/scribble-python.html b/scribble-python.html
deleted file mode 100644
index 62d34a1..0000000
--- a/scribble-python.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
- Scribble Python
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Python Implementation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/README.adoc b/src/main/README.adoc
new file mode 100644
index 0000000..0e08f5d
--- /dev/null
+++ b/src/main/README.adoc
@@ -0,0 +1 @@
+NOTE: `webapp` is just a dummy implementation, all the content is in the `jbake` directory. It's there in case we will need some Java smart logic in the future because Maven jbake plugin produces a war file during the build.
diff --git a/CNAME b/src/main/jbake/assets/CNAME
similarity index 100%
rename from CNAME
rename to src/main/jbake/assets/CNAME
diff --git a/src/main/jbake/assets/README.adoc b/src/main/jbake/assets/README.adoc
new file mode 100644
index 0000000..69f3774
--- /dev/null
+++ b/src/main/jbake/assets/README.adoc
@@ -0,0 +1,20 @@
+:title: scribble.github.io
+
+== Scribble Website
+
+image:https://travis-ci.org/scribble/scribble.github.io.svg?branch=pages["Build Status", link="https://travis-ci.org/scribble/scribble.github.io"]
+
+=== Description
+When pushing a commit into this branch, the site is automatically built and published to http://scribble.github.io
+Most likely the content of the website you are looking for is link:src/main/jbake/content/[here].
+
+=== Building the site on localhost
+. `git clone https://github.com/scribble/scribble.github.io`
+. `git checkout pages`
+. Provided Maven is installed, run one of following commands:
+
+* `mvn jbake:generate` Simply runs jbake and generate the site into `target/website` dir.
+* `mvn jbake:watch` Polls a folder and run jbake whenever changes happen.
+* `mvn jbake:inline` Same as watch, but also launches an embedded winstone container that by default listens on http://localhost:8080. Additionally you may want to use `-Djbake.port=X` `-Djbake.listenAddress=Y`.
+
+NOTE: `mvn install` will most likely fail on localhost, because it needs the OAuth token for GitHub.
diff --git a/src/main/jbake/assets/css/asciidoctor.css b/src/main/jbake/assets/css/asciidoctor.css
new file mode 100644
index 0000000..c687f36
--- /dev/null
+++ b/src/main/jbake/assets/css/asciidoctor.css
@@ -0,0 +1,512 @@
+@import url(//cdnjs.cloudflare.com/ajax/libs/font-awesome/3.2.0/css/font-awesome.css);
+/* ========================================================================== Embedded content ========================================================================== */
+/** Remove border when inside `a` element in IE 8/9. */
+img { border: 0; }
+
+/** Correct overflow displayed oddly in IE 9. */
+svg:not(:root) { overflow: hidden; }
+
+/* ========================================================================== Figures ========================================================================== */
+/** Address margin not present in IE 8/9 and Safari 5. */
+figure { margin: 0; }
+
+/* ========================================================================== Forms ========================================================================== */
+/** Define consistent border, margin, and padding. */
+fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
+
+/** 1. Correct `color` not being inherited in IE 8/9. 2. Remove padding so people aren't caught out if they zero out fieldsets. */
+legend { border: 0; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Correct font family not being inherited in all browsers. 2. Correct font size not being inherited in all browsers. 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome. */
+button, input, select, textarea { font-family: inherit; /* 1 */ font-size: 100%; /* 2 */ margin: 0; /* 3 */ }
+
+/** Address Firefox 4+ setting `line-height` on `input` using `!important` in the UA stylesheet. */
+button, input { line-height: normal; }
+
+/** Address inconsistent `text-transform` inheritance for `button` and `select`. All other form control elements do not inherit `text-transform` values. Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+. Correct `select` style inheritance in Firefox 4+ and Opera. */
+button, select { text-transform: none; }
+
+/** 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` and `video` controls. 2. Correct inability to style clickable `input` types in iOS. 3. Improve usability and consistency of cursor style between image-type `input` and others. */
+button, html input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; /* 2 */ cursor: pointer; /* 3 */ }
+
+/** Re-set default cursor for disabled elements. */
+button[disabled], html input[disabled] { cursor: default; }
+
+/** 1. Address box sizing set to `content-box` in IE 8/9. 2. Remove excess padding in IE 8/9. */
+input[type="checkbox"], input[type="radio"] { box-sizing: border-box; /* 1 */ padding: 0; /* 2 */ }
+
+/** 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome (include `-moz` to future-proof). */
+input[type="search"] { -webkit-appearance: textfield; /* 1 */ -moz-box-sizing: content-box; -webkit-box-sizing: content-box; /* 2 */ box-sizing: content-box; }
+
+/** Remove inner padding and search cancel button in Safari 5 and Chrome on OS X. */
+input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
+
+/** Remove inner padding and border in Firefox 4+. */
+button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; }
+
+/** 1. Remove default vertical scrollbar in IE 8/9. 2. Improve readability and alignment in all browsers. */
+textarea { overflow: auto; /* 1 */ vertical-align: top; /* 2 */ }
+
+/* ========================================================================== Tables ========================================================================== */
+/** Remove most spacing between table cells. */
+table { border-collapse: collapse; border-spacing: 0; }
+
+*, *:before, *:after { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; }
+
+img, object, embed { max-width: 100%; height: auto; }
+
+object, embed { height: 100%; }
+
+img { -ms-interpolation-mode: bicubic; }
+
+#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { max-width: none !important; }
+
+.left { float: left !important; }
+
+.right { float: right !important; }
+
+.text-left { text-align: left !important; }
+
+.text-right { text-align: right !important; }
+
+.text-center { text-align: center !important; }
+
+.text-justify { text-align: justify !important; }
+
+.hide { display: none; }
+
+.antialiased, body { -webkit-font-smoothing: antialiased; }
+
+img { display: inline-block; vertical-align: middle; }
+
+textarea { height: auto; min-height: 50px; }
+
+select { width: 100%; }
+
+p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { font-size: 1.21875em; line-height: 1.6; }
+
+.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { line-height: 1.4; color: #7a2518; font-weight: 300; margin-top: 0.2em; margin-bottom: 0.5em; }
+
+/* Lists */
+ul, ol, dl { font-size: 1em; line-height: 1.6; margin-bottom: 1.25em; list-style-position: outside; font-family: inherit; }
+
+ul, ol { margin-left: 1.5em; }
+
+/* Unordered Lists */
+ul li ul, ul li ol { margin-left: 1.25em; margin-bottom: 0; font-size: 1em; /* Override nested font-size change */ }
+ul.square li ul, ul.circle li ul, ul.disc li ul { list-style: inherit; }
+ul.square { list-style-type: square; }
+ul.circle { list-style-type: circle; }
+ul.disc { list-style-type: disc; }
+ul.no-bullet { list-style: none; }
+
+/* Ordered Lists */
+ol li ul, ol li ol { margin-left: 1.25em; margin-bottom: 0; }
+
+/* Definition Lists */
+dl dt { margin-bottom: 0.3125em; font-weight: bold; }
+dl dd { margin-bottom: 1.25em; }
+
+/* Abbreviations */
+abbr, acronym { text-transform: uppercase; font-size: 90%; color: #222222; border-bottom: 1px dotted #dddddd; cursor: help; }
+
+abbr { text-transform: none; }
+
+/* Blockquotes */
+blockquote { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
+blockquote cite { display: block; font-size: inherit; color: #555555; }
+blockquote cite:before { content: "\2014 \0020"; }
+blockquote cite a, blockquote cite a:visited { color: #555555; }
+
+blockquote, blockquote p { line-height: 1.6; color: #6f6f6f; }
+
+/* Microformats */
+.vcard { display: inline-block; margin: 0 0 1.25em 0; border: 1px solid #dddddd; padding: 0.625em 0.75em; }
+.vcard li { margin: 0; display: block; }
+.vcard .fn { font-weight: bold; font-size: 0.9375em; }
+
+.vevent .summary { font-weight: bold; }
+.vevent abbr { cursor: auto; text-decoration: none; font-weight: bold; border: none; padding: 0 0.0625em; }
+
+@media only screen and (min-width: 768px) { h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { line-height: 1.4; }
+ h1 { font-size: 2.75em; }
+ h2 { font-size: 2.3125em; }
+ h3, #toctitle, .sidebarblock > .content > .title { font-size: 1.6875em; }
+ h4 { font-size: 1.4375em; } }
+/* Print styles. Inlined to avoid required HTTP connection: www.phpied.com/delay-loading-your-print-css/ Credit to Paul Irish and HTML5 Boilerplate (html5boilerplate.com)
+*/
+.print-only { display: none !important; }
+
+@media print { * { background: transparent !important; color: #000 !important; /* Black prints faster: h5bp.com/s */ box-shadow: none !important; text-shadow: none !important; }
+ a, a:visited { text-decoration: underline; }
+ a[href]:after { content: " (" attr(href) ")"; }
+ abbr[title]:after { content: " (" attr(title) ")"; }
+ .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; }
+ pre, blockquote { border: 1px solid #999; page-break-inside: avoid; }
+ thead { display: table-header-group; /* h5bp.com/t */ }
+ tr, img { page-break-inside: avoid; }
+ img { max-width: 100% !important; }
+ @page { margin: 0.5cm; }
+ p, h2, h3, #toctitle, .sidebarblock > .content > .title { orphans: 3; widows: 3; }
+ h2, h3, #toctitle, .sidebarblock > .content > .title { page-break-after: avoid; }
+ .hide-on-print { display: none !important; }
+ .print-only { display: block !important; }
+ .hide-for-print { display: none !important; }
+ .show-for-print { display: inherit !important; } }
+/* Tables */
+table { background: white; margin-bottom: 1.25em; border: solid 1px #dddddd; }
+table thead, table tfoot { background: whitesmoke; font-weight: bold; }
+table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { padding: 0.5em 0.625em 0.625em; font-size: inherit; color: #222222; text-align: left; }
+table tr th, table tr td { padding: 0.5625em 0.625em; font-size: inherit; color: #222222; }
+table tr.even, table tr.alt, table tr:nth-of-type(even) { background: #f9f9f9; }
+table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { display: table-cell; line-height: 1.6; }
+
+.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { content: " "; display: table; }
+.clearfix:after, .float-group:after { clear: both; }
+
+*:not(pre) > code { font-size: 0.9375em; padding: 1px 3px 0; white-space: nowrap; background-color: #f2f2f2; border: 1px solid #cccccc; -webkit-border-radius: 4px; border-radius: 4px; text-shadow: none; }
+
+/*pre, pre > code { line-height: 1.4; color: inherit; font-family: Consolas, "Liberation Mono", Courier, monospace; font-weight: normal; }*/
+
+kbd.keyseq { color: #555555; }
+
+kbd:not(.keyseq) { display: inline-block; color: #222222; font-size: 0.75em; line-height: 1.4; background-color: #F7F7F7; border: 1px solid #ccc; -webkit-border-radius: 3px; border-radius: 3px; -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; margin: -0.15em 0.15em 0 0.15em; padding: 0.2em 0.6em 0.2em 0.5em; vertical-align: middle; white-space: nowrap; }
+
+kbd kbd:first-child { margin-left: 0; }
+
+kbd kbd:last-child { margin-right: 0; }
+
+.menuseq, .menu { color: #090909; }
+
+p a > code:hover { color: #561309; }
+
+#header, #content, #footnotes, #footer { width: 100%; margin-left: auto; margin-right: auto; margin-top: 0; margin-bottom: 0; max-width: 62.5em; *zoom: 1; position: relative; padding-left: 0.9375em; padding-right: 0.9375em; }
+#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { content: " "; display: table; }
+#header:after, #content:after, #footnotes:after, #footer:after { clear: both; }
+
+#header { margin-bottom: 2.5em; }
+#header > h1 { color: black; font-weight: normal; border-bottom: 1px solid #dddddd; margin-bottom: -28px; padding-bottom: 32px; }
+#header span { color: #6f6f6f; }
+#header #revnumber { text-transform: capitalize; }
+#header br { display: none; }
+#header br + span { padding-left: 3px; }
+#header br + span:before { content: "\2013 \0020"; }
+#header br + span.author { padding-left: 0; }
+#header br + span.author:before { content: ", "; }
+
+#toc { border-bottom: 3px double #ebebeb; padding-bottom: 1.25em; }
+#toc > ul { margin-left: 0.25em; }
+#toc ul.sectlevel0 > li > a { font-style: italic; }
+#toc ul.sectlevel0 ul.sectlevel1 { margin-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
+#toc ul { list-style-type: none; }
+
+#toctitle { color: #7a2518; }
+
+@media only screen and (min-width: 1280px) { body.toc2 { padding-left: 20em; }
+ #toc.toc2 { position: fixed; width: 20em; left: 0; top: 0; border-right: 1px solid #ebebeb; border-bottom: 0; z-index: 1000; padding: 1em; height: 100%; overflow: auto; }
+ #toc.toc2 #toctitle { margin-top: 0; }
+ #toc.toc2 > ul { font-size: .95em; }
+ #toc.toc2 ul ul { margin-left: 0; padding-left: 1.25em; }
+ #toc.toc2 ul.sectlevel0 ul.sectlevel1 { padding-left: 0; margin-top: 0.5em; margin-bottom: 0.5em; }
+ body.toc2.toc-right { padding-left: 0; padding-right: 20em; }
+ body.toc2.toc-right #toc.toc2 { border-right: 0; border-left: 1px solid #ebebeb; left: auto; right: 0; } }
+#content #toc { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; border-width: 0; -webkit-border-radius: 4px; border-radius: 4px; }
+#content #toc > :first-child { margin-top: 0; }
+#content #toc > :last-child { margin-bottom: 0; }
+#content #toc a { text-decoration: none; }
+
+#content #toctitle { font-weight: bold; font-family: "Helvetica Neue", "Helvetica", Helvetica, Arial, sans-serif; font-size: 1em; padding-left: 0.125em; }
+
+#footer { max-width: 100%; background-color: #222222; padding: 1.25em; }
+
+#footer-text { color: #dddddd; line-height: 1.44; }
+
+.sect1 { padding-bottom: 1.25em; }
+
+.sect1 + .sect1 { border-top: 3px double #ebebeb; }
+
+#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { position: absolute; width: 1em; margin-left: -1em; display: block; text-decoration: none; visibility: hidden; text-align: center; font-weight: normal; }
+#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { content: '\00A7'; font-size: .85em; vertical-align: text-top; display: block; margin-top: 0.05em; }
+#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { visibility: visible; }
+#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { color: #ba3925; text-decoration: none; }
+#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { color: #a53221; }
+
+.imageblock, .literalblock, .listingblock, .verseblock, .videoblock { margin-bottom: 1.25em; }
+
+.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { text-align: left; font-weight: bold; }
+
+.tableblock > caption { text-align: left; font-weight: bold; white-space: nowrap; overflow: visible; max-width: 0; }
+
+table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { font-size: inherit; }
+
+.admonitionblock > table { border: 0; background: none; width: 100%; }
+.admonitionblock > table td.icon { text-align: center; width: 80px; }
+.admonitionblock > table td.icon img { max-width: none; }
+.admonitionblock > table td.icon .title { font-weight: bold; text-transform: uppercase; }
+.admonitionblock > table td.content { padding-left: 1.125em; padding-right: 1.25em; border-left: 1px solid #dddddd; color: #6f6f6f; }
+.admonitionblock > table td.content > :last-child > :last-child { margin-bottom: 0; }
+
+.exampleblock > .content { border-style: solid; border-width: 1px; border-color: #e6e6e6; margin-bottom: 1.25em; padding: 1.25em; background: white; -webkit-border-radius: 4px; border-radius: 4px; }
+.exampleblock > .content > :first-child { margin-top: 0; }
+.exampleblock > .content > :last-child { margin-bottom: 0; }
+.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { color: #333333; }
+.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { line-height: 1; margin-bottom: 0.625em; }
+.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { line-height: 1.4; }
+
+.exampleblock.result > .content { -webkit-box-shadow: 0 1px 8px #d9d9d9; box-shadow: 0 1px 8px #d9d9d9; }
+
+.sidebarblock { border-style: solid; border-width: 1px; border-color: #d9d9d9; margin-bottom: 1.25em; padding: 1.25em; background: #f2f2f2; -webkit-border-radius: 4px; border-radius: 4px; }
+.sidebarblock > :first-child { margin-top: 0; }
+.sidebarblock > :last-child { margin-bottom: 0; }
+.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { color: #333333; }
+.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { line-height: 1; margin-bottom: 0.625em; }
+.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { line-height: 1.4; }
+.sidebarblock > .content > .title { color: #7a2518; margin-top: 0; line-height: 1.6; }
+
+.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { margin-bottom: 0; }
+
+/*.literalblock > .content pre, .listingblock > .content pre { background: none; border-width: 1px 0; border-style: dotted; border-color: #bfbfbf; -webkit-border-radius: 4px; border-radius: 4px; padding: 0.75em 0.75em 0.5em 0.75em; word-wrap: break-word; }*/
+.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap { overflow-x: auto; white-space: pre; word-wrap: normal; }
+.literalblock > .content pre > code, .listingblock > .content pre > code { display: block; }
+@media only screen { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.8em; } }
+@media only screen and (min-width: 768px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 0.9em; } }
+@media only screen and (min-width: 1280px) { .literalblock > .content pre, .listingblock > .content pre { font-size: 1em; } }
+
+.listingblock > .content { position: relative; }
+
+.listingblock:hover code[class*=" language-"]:before { text-transform: uppercase; font-size: 0.9em; color: #999; position: absolute; top: 0.375em; right: 0.375em; }
+
+.listingblock:hover code.asciidoc:before { content: "asciidoc"; }
+.listingblock:hover code.clojure:before { content: "clojure"; }
+.listingblock:hover code.css:before { content: "css"; }
+.listingblock:hover code.groovy:before { content: "groovy"; }
+.listingblock:hover code.html:before { content: "html"; }
+.listingblock:hover code.java:before { content: "java"; }
+.listingblock:hover code.javascript:before { content: "javascript"; }
+.listingblock:hover code.python:before { content: "python"; }
+.listingblock:hover code.ruby:before { content: "ruby"; }
+.listingblock:hover code.scss:before { content: "scss"; }
+.listingblock:hover code.xml:before { content: "xml"; }
+.listingblock:hover code.yaml:before { content: "yaml"; }
+
+.listingblock.terminal pre .command:before { content: attr(data-prompt); padding-right: 0.5em; color: #999; }
+
+.listingblock.terminal pre .command:not([data-prompt]):before { content: '$'; }
+
+table.pyhltable { border: 0; margin-bottom: 0; }
+
+table.pyhltable td { vertical-align: top; padding-top: 0; padding-bottom: 0; }
+
+table.pyhltable td.code { padding-left: .75em; padding-right: 0; }
+
+.highlight.pygments .lineno, table.pyhltable td:not(.code) { color: #999; padding-left: 0; padding-right: .5em; border-right: 1px solid #dddddd; }
+
+.highlight.pygments .lineno { display: inline-block; margin-right: .25em; }
+
+table.pyhltable .linenodiv { background-color: transparent !important; padding-right: 0 !important; }
+
+.quoteblock { margin: 0 0 1.25em; padding: 0.5625em 1.25em 0 1.1875em; border-left: 1px solid #dddddd; }
+.quoteblock blockquote { margin: 0 0 1.25em 0; padding: 0 0 0.5625em 0; border: 0; }
+.quoteblock blockquote > .paragraph:last-child p { margin-bottom: 0; }
+.quoteblock .attribution { margin-top: -.25em; padding-bottom: 0.5625em; font-size: inherit; color: #555555; }
+.quoteblock .attribution br { display: none; }
+.quoteblock .attribution cite { display: block; margin-bottom: 0.625em; }
+
+table thead th, table tfoot th { font-weight: bold; }
+
+table.tableblock.grid-all { border-collapse: separate; border-spacing: 1px; -webkit-border-radius: 4px; border-radius: 4px; border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; }
+
+table.tableblock.frame-topbot, table.tableblock.frame-none { border-left: 0; border-right: 0; }
+
+table.tableblock.frame-sides, table.tableblock.frame-none { border-top: 0; border-bottom: 0; }
+
+table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child { margin-bottom: 0; }
+
+th.tableblock.halign-left, td.tableblock.halign-left { text-align: left; }
+
+th.tableblock.halign-right, td.tableblock.halign-right { text-align: right; }
+
+th.tableblock.halign-center, td.tableblock.halign-center { text-align: center; }
+
+th.tableblock.valign-top, td.tableblock.valign-top { vertical-align: top; }
+
+th.tableblock.valign-bottom, td.tableblock.valign-bottom { vertical-align: bottom; }
+
+th.tableblock.valign-middle, td.tableblock.valign-middle { vertical-align: middle; }
+
+p.tableblock.header { color: #222222; font-weight: bold; }
+
+td > div.verse { white-space: pre; }
+
+ol { margin-left: 1.75em; }
+
+ul li ol { margin-left: 1.5em; }
+
+dl dd { margin-left: 1.125em; }
+
+dl dd:last-child, dl dd:last-child > :last-child { margin-bottom: 0; }
+
+ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { margin-bottom: 0.625em; }
+
+ul.unstyled, ol.unnumbered, ul.checklist, ul.none { list-style-type: none; }
+
+ul.unstyled, ol.unnumbered, ul.checklist { margin-left: 0.625em; }
+
+ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { margin-right: 0.25em; }
+
+ul.checklist li > p:first-child > input[type="checkbox"]:first-child { position: relative; top: 1px; }
+
+ul.inline { margin: 0 auto 0.625em auto; margin-left: -1.375em; margin-right: 0; padding: 0; list-style: none; overflow: hidden; }
+ul.inline > li { list-style: none; float: left; margin-left: 1.375em; display: block; }
+ul.inline > li > * { display: block; }
+
+.unstyled dl dt { font-weight: normal; font-style: normal; }
+
+ol.arabic { list-style-type: decimal; }
+
+ol.decimal { list-style-type: decimal-leading-zero; }
+
+ol.loweralpha { list-style-type: lower-alpha; }
+
+ol.upperalpha { list-style-type: upper-alpha; }
+
+ol.lowerroman { list-style-type: lower-roman; }
+
+ol.upperroman { list-style-type: upper-roman; }
+
+ol.lowergreek { list-style-type: lower-greek; }
+
+.hdlist > table, .colist > table { border: 0; background: none; }
+.hdlist > table > tbody > tr, .colist > table > tbody > tr { background: none; }
+
+td.hdlist1 { padding-right: .8em; font-weight: bold; }
+
+td.hdlist1, td.hdlist2 { vertical-align: top; }
+
+.literalblock + .colist, .listingblock + .colist { margin-top: -0.5em; }
+
+.colist > table tr > td:first-of-type { padding: 0 .8em; line-height: 1; }
+.colist > table tr > td:last-of-type { padding: 0.25em 0; }
+
+.qanda > ol > li > p > em:only-child { color: #00467f; }
+
+.thumb, .th { line-height: 0; display: inline-block; border: solid 4px white; -webkit-box-shadow: 0 0 0 1px #dddddd; box-shadow: 0 0 0 1px #dddddd; }
+
+.imageblock.left, .imageblock[style*="float: left"] { margin: 0.25em 0.625em 1.25em 0; }
+.imageblock.right, .imageblock[style*="float: right"] { margin: 0.25em 0 1.25em 0.625em; }
+.imageblock > .title { margin-bottom: 0; }
+.imageblock.thumb, .imageblock.th { border-width: 6px; }
+.imageblock.thumb > .title, .imageblock.th > .title { padding: 0 0.125em; }
+
+.image.left, .image.right { margin-top: 0.25em; margin-bottom: 0.25em; display: inline-block; line-height: 0; }
+.image.left { margin-right: 0.625em; }
+.image.right { margin-left: 0.625em; }
+
+a.image { text-decoration: none; }
+
+span.footnote, span.footnoteref { vertical-align: super; font-size: 0.875em; }
+span.footnote a, span.footnoteref a { text-decoration: none; }
+
+#footnotes { padding-top: 0.75em; padding-bottom: 0.75em; margin-bottom: 0.625em; }
+#footnotes hr { width: 20%; min-width: 6.25em; margin: -.25em 0 .75em 0; border-width: 1px 0 0 0; }
+#footnotes .footnote { padding: 0 0.375em; line-height: 1.3; font-size: 0.875em; margin-left: 1.2em; text-indent: -1.2em; margin-bottom: .2em; }
+#footnotes .footnote a:first-of-type { font-weight: bold; text-decoration: none; }
+#footnotes .footnote:last-of-type { margin-bottom: 0; }
+
+#content #footnotes { margin-top: -0.625em; margin-bottom: 0; padding: 0.75em 0; }
+
+.gist .file-data > table { border: none; background: #fff; width: 100%; margin-bottom: 0; }
+.gist .file-data > table td.line-data { width: 99%; }
+
+div.unbreakable { page-break-inside: avoid; }
+
+.big { font-size: larger; }
+
+.small { font-size: smaller; }
+
+.underline { text-decoration: underline; }
+
+.overline { text-decoration: overline; }
+
+.line-through { text-decoration: line-through; }
+
+.aqua { color: #00bfbf; }
+
+.aqua-background { background-color: #00fafa; }
+
+.black { color: black; }
+
+.black-background { background-color: black; }
+
+.blue { color: #0000bf; }
+
+.blue-background { background-color: #0000fa; }
+
+.fuchsia { color: #bf00bf; }
+
+.fuchsia-background { background-color: #fa00fa; }
+
+.gray { color: #606060; }
+
+.gray-background { background-color: #7d7d7d; }
+
+.green { color: #006000; }
+
+.green-background { background-color: #007d00; }
+
+.lime { color: #00bf00; }
+
+.lime-background { background-color: #00fa00; }
+
+.maroon { color: #600000; }
+
+.maroon-background { background-color: #7d0000; }
+
+.navy { color: #000060; }
+
+.navy-background { background-color: #00007d; }
+
+.olive { color: #606000; }
+
+.olive-background { background-color: #7d7d00; }
+
+.purple { color: #600060; }
+
+.purple-background { background-color: #7d007d; }
+
+.red { color: #bf0000; }
+
+.red-background { background-color: #fa0000; }
+
+.silver { color: #909090; }
+
+.silver-background { background-color: #bcbcbc; }
+
+.teal { color: #006060; }
+
+.teal-background { background-color: #007d7d; }
+
+.white { color: #bfbfbf; }
+
+.white-background { background-color: #fafafa; }
+
+.yellow { color: #bfbf00; }
+
+.yellow-background { background-color: #fafa00; }
+
+span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
+
+.admonitionblock td.icon [class^="icon-"]:before { font-size: 2.5em; text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); cursor: default; }
+.admonitionblock td.icon .icon-note:before { content: "\f05a"; color: #005498; color: #003f72; }
+.admonitionblock td.icon .icon-tip:before { content: "\f0eb"; text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); color: #111; }
+.admonitionblock td.icon .icon-warning:before { content: "\f071"; color: #bf6900; }
+.admonitionblock td.icon .icon-caution:before { content: "\f06d"; color: #bf3400; }
+.admonitionblock td.icon .icon-important:before { content: "\f06a"; color: #bf0000; }
+
+.conum { display: inline-block; color: white !important; background-color: #222222; -webkit-border-radius: 100px; border-radius: 100px; text-align: center; width: 20px; height: 20px; font-size: 12px; font-weight: bold; line-height: 20px; font-family: Arial, sans-serif; font-style: normal; position: relative; top: -2px; letter-spacing: -1px; }
+.conum * { color: white !important; }
+.conum + b { display: none; }
+.conum:after { content: attr(data-value); }
+.conum:not([data-value]):empty { display: none; }
+
+.literalblock > .content > pre, .listingblock > .content > pre { -webkit-border-radius: 0; border-radius: 0; }
diff --git a/src/main/jbake/assets/css/base.css b/src/main/jbake/assets/css/base.css
new file mode 100644
index 0000000..68fe241
--- /dev/null
+++ b/src/main/jbake/assets/css/base.css
@@ -0,0 +1,52 @@
+/* Sticky footer styles
+-------------------------------------------------- */
+
+html,
+body {
+ height: 100%;
+ /* The html and body elements cannot have any padding or margin. */
+}
+
+/* Wrapper for page content to push down footer */
+#wrap {
+ min-height: 100%;
+ height: auto !important;
+ height: 100%;
+ /* Negative indent footer by it's height */
+ margin: 0 auto -60px;
+}
+
+/* Set the fixed height of the footer here */
+#push,
+#footer {
+ height: 60px;
+}
+#footer {
+ background-color: #f5f5f5;
+ padding: 0;
+}
+
+/* Lastly, apply responsive CSS fixes as necessary */
+@media (max-width: 767px) {
+ #footer {
+ margin-left: -20px;
+ margin-right: -20px;
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+}
+
+/* Custom page CSS
+-------------------------------------------------- */
+/* Not required for template or sticky footer method. */
+
+#wrap > .container {
+ padding-top: 60px;
+}
+.container .credit {
+ margin: 20px 0;
+}
+
+/*code {
+ font-size: 80%;
+}*/
\ No newline at end of file
diff --git a/src/main/jbake/assets/css/bootstrap.min.css b/src/main/jbake/assets/css/bootstrap.min.css
new file mode 100644
index 0000000..679272d
--- /dev/null
+++ b/src/main/jbake/assets/css/bootstrap.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v3.1.1 (http://getbootstrap.com)
+ * Copyright 2011-2014 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ */
+
+/*! normalize.css v3.0.0 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background:0 0}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}@media print{*{text-shadow:none!important;color:#000!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.table td,.table th{background-color:#fff!important}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table-bordered th,.table-bordered td{border:1px solid #ddd!important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:before,:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:62.5%;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive,.thumbnail>img,.thumbnail a>img,.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);border:0}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:400;line-height:1;color:#999}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:200;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}cite{font-style:normal}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-muted{color:#999}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #999}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#999}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}blockquote:before,blockquote:after{content:""}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;white-space:nowrap;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{margin-right:auto;margin-left:auto;padding-left:15px;padding-right:15px}.row{margin-left:-15px;margin-right:-15px}.col-xs-1,.col-sm-1,.col-md-1,.col-lg-1,.col-xs-2,.col-sm-2,.col-md-2,.col-lg-2,.col-xs-3,.col-sm-3,.col-md-3,.col-lg-3,.col-xs-4,.col-sm-4,.col-md-4,.col-lg-4,.col-xs-5,.col-sm-5,.col-md-5,.col-lg-5,.col-xs-6,.col-sm-6,.col-md-6,.col-lg-6,.col-xs-7,.col-sm-7,.col-md-7,.col-lg-7,.col-xs-8,.col-sm-8,.col-md-8,.col-lg-8,.col-xs-9,.col-sm-9,.col-md-9,.col-lg-9,.col-xs-10,.col-sm-10,.col-md-10,.col-lg-10,.col-xs-11,.col-sm-11,.col-md-11,.col-lg-11,.col-xs-12,.col-sm-12,.col-md-12,.col-lg-12{position:relative;min-height:1px;padding-left:15px;padding-right:15px}.col-xs-1,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9,.col-xs-10,.col-xs-11,.col-xs-12{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:0}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:0}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:0}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:0}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:0}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:0}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:0}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:0}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{max-width:100%;background-color:transparent}th{text-align:left}.table{width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd)>td,.table-striped>tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover>tbody>tr:hover>td,.table-hover>tbody>tr:hover>th{background-color:#f5f5f5}table col[class*=col-]{position:static;float:none;display:table-column}table td[class*=col-],table th[class*=col-]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}@media (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;overflow-x:scroll;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd;-webkit-overflow-scrolling:touch}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=radio],input[type=checkbox]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=radio]:focus,input[type=checkbox]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}input[type=date]{line-height:34px}.form-group{margin-bottom:15px}.radio,.checkbox{display:block;min-height:20px;margin-top:10px;margin-bottom:10px;padding-left:20px}.radio label,.checkbox label{display:inline;font-weight:400;cursor:pointer}.radio input[type=radio],.radio-inline input[type=radio],.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox]{float:left;margin-left:-20px}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:400;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type=radio][disabled],input[type=checkbox][disabled],.radio[disabled],.radio-inline[disabled],.checkbox[disabled],.checkbox-inline[disabled],fieldset[disabled] input[type=radio],fieldset[disabled] input[type=checkbox],fieldset[disabled] .radio,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}textarea.input-sm,select[multiple].input-sm{height:auto}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg{height:46px;line-height:46px}textarea.input-lg,select[multiple].input-lg{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.has-feedback .form-control-feedback{position:absolute;top:25px;right:0;display:block;width:34px;height:34px;line-height:34px;text-align:center}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.form-control-static{margin-bottom:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.form-inline .radio input[type=radio],.form-inline .checkbox input[type=checkbox]{float:none;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .control-label,.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}.form-horizontal .form-control-static{padding-top:7px}@media (min-width:768px){.form-horizontal .control-label{text-align:right}}.form-horizontal .has-feedback .form-control-feedback{top:0;right:15px}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;vertical-align:middle;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{color:#333;background-color:#ebebeb;border-color:#adadad}.btn-default:active,.btn-default.active,.open .dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{color:#fff;background-color:#3276b1;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open .dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{color:#fff;background-color:#47a447;border-color:#398439}.btn-success:active,.btn-success.active,.open .dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{color:#fff;background-color:#39b3d7;border-color:#269abc}.btn-info:active,.btn-info.active,.open .dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{color:#fff;background-color:#ed9c28;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open .dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{color:#fff;background-color:#d2322d;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open .dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:400;cursor:pointer;border-radius:0}.btn-link,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#999;text-decoration:none}.btn-lg,.btn-group-lg>.btn{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm,.btn-group-sm>.btn{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs,.btn-group-xs>.btn{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%;padding-left:0;padding-right:0}.btn-block+.btn-block{margin-top:5px}input[type=submit].btn-block,input[type=reset].btn-block,input[type=button].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition:height .35s ease;transition:height .35s ease}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;font-size:14px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{text-decoration:none;color:#262626;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;outline:0;background-color:#428bca}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#999}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);cursor:not-allowed}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{left:auto;right:0}.dropdown-menu-left{left:0;right:auto}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#999}.dropdown-backdrop{position:fixed;left:0;right:0;bottom:0;top:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}@media (min-width:768px){.navbar-right .dropdown-menu{left:auto;right:0}.navbar-right .dropdown-menu-left{left:0;right:auto}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group>.btn,.btn-group-vertical>.btn{position:relative;float:left}.btn-group>.btn:hover,.btn-group-vertical>.btn:hover,.btn-group>.btn:focus,.btn-group-vertical>.btn:focus,.btn-group>.btn:active,.btn-group-vertical>.btn:active,.btn-group>.btn.active,.btn-group-vertical>.btn.active{z-index:2}.btn-group>.btn:focus,.btn-group-vertical>.btn:focus{outline:0}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child>.btn:last-child,.btn-group>.btn-group:first-child>.dropdown-toggle{border-bottom-right-radius:0;border-top-right-radius:0}.btn-group>.btn-group:last-child>.btn:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-left:8px;padding-right:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-left:12px;padding-right:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-left-radius:4px;border-top-right-radius:0;border-top-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-right-radius:0;border-top-left-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{float:none;display:table-cell;width:1%}.btn-group-justified>.btn-group .btn{width:100%}[data-toggle=buttons]>.btn>input[type=radio],[data-toggle=buttons]>.btn>input[type=checkbox]{display:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-left:0;padding-right:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn,select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn,select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn{height:auto}.input-group-addon,.input-group-btn,.input-group .form-control{display:table-cell}.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child),.input-group .form-control:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group-btn:last-child>.btn-group:not(:last-child)>.btn{border-bottom-right-radius:0;border-top-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:first-child>.btn-group:not(:first-child)>.btn{border-bottom-left-radius:0;border-top-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:hover,.input-group-btn>.btn:focus,.input-group-btn>.btn:active{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{margin-bottom:0;padding-left:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:hover,.nav>li.disabled>a:focus{color:#999;text-decoration:none;background-color:transparent;cursor:not-allowed}.nav .open>a,.nav .open>a:hover,.nav .open>a:focus{background-color:#eee;border-color:#428bca}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{color:#555;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:hover,.nav-pills>li.active>a:focus{color:#fff;background-color:#428bca}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{text-align:center;margin-bottom:5px}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:hover,.nav-tabs-justified>.active>a:focus{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-right-radius:0;border-top-left-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{max-height:340px;overflow-x:visible;padding-right:15px;padding-left:15px;border-top:1px solid transparent;box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse,.navbar-fixed-bottom .navbar-collapse{padding-left:0;padding-right:0}}.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container>.navbar-header,.container-fluid>.navbar-header,.container>.navbar-collapse,.container-fluid>.navbar-collapse{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;padding:15px;font-size:18px;line-height:20px;height:50px}.navbar-brand:hover,.navbar-brand:focus{text-decoration:none}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;margin-right:15px;padding:9px 10px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;box-shadow:none}.navbar-nav .open .dropdown-menu>li>a,.navbar-nav .open .dropdown-menu .dropdown-header{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:hover,.navbar-nav .open .dropdown-menu>li>a:focus{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}.navbar-nav.navbar-right:last-child{margin-right:-15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important}}.navbar-form{margin-left:-15px;margin-right:-15px;padding:10px 15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .radio,.navbar-form .checkbox{display:inline-block;margin-top:0;margin-bottom:0;padding-left:0;vertical-align:middle}.navbar-form .radio input[type=radio],.navbar-form .checkbox input[type=checkbox]{float:none;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-form{width:auto;border:0;margin-left:0;margin-right:0;padding-top:0;padding-bottom:0;-webkit-box-shadow:none;box-shadow:none}.navbar-form.navbar-right:last-child{margin-right:-15px}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-right-radius:0;border-top-left-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-left:15px;margin-right:15px}.navbar-text.navbar-right:last-child{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:hover,.navbar-default .navbar-brand:focus{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:hover,.navbar-default .navbar-nav>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:hover,.navbar-default .navbar-nav>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:hover,.navbar-default .navbar-nav>.disabled>a:focus{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:hover,.navbar-default .navbar-toggle:focus{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:hover,.navbar-default .navbar-nav>.open>a:focus{background-color:#e7e7e7;color:#555}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:hover,.navbar-inverse .navbar-brand:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:hover,.navbar-inverse .navbar-nav>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:hover,.navbar-inverse .navbar-nav>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:hover,.navbar-inverse .navbar-nav>.disabled>a:focus{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:hover,.navbar-inverse .navbar-toggle:focus{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:hover,.navbar-inverse .navbar-nav>.open>a:focus{background-color:#080808;color:#fff}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#fff}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{content:"/\00a0";padding:0 5px;color:#ccc}.breadcrumb>.active{color:#999}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;line-height:1.42857143;text-decoration:none;color:#428bca;background-color:#fff;border:1px solid #ddd;margin-left:-1px}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-bottom-left-radius:4px;border-top-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-bottom-right-radius:4px;border-top-right-radius:4px}.pagination>li>a:hover,.pagination>li>span:hover,.pagination>li>a:focus,.pagination>li>span:focus{color:#2a6496;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>span,.pagination>.active>a:hover,.pagination>.active>span:hover,.pagination>.active>a:focus,.pagination>.active>span:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca;cursor:default}.pagination>.disabled>span,.pagination>.disabled>span:hover,.pagination>.disabled>span:focus,.pagination>.disabled>a,.pagination>.disabled>a:hover,.pagination>.disabled>a:focus{color:#999;background-color:#fff;border-color:#ddd;cursor:not-allowed}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-bottom-left-radius:6px;border-top-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-bottom-right-radius:6px;border-top-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-bottom-left-radius:3px;border-top-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-bottom-right-radius:3px;border-top-right-radius:3px}.pager{padding-left:0;margin:20px 0;list-style:none;text-align:center}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#999;background-color:#fff;cursor:not-allowed}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}.label[href]:hover,.label[href]:focus{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#999}.label-default[href]:hover,.label-default[href]:focus{background-color:gray}.label-primary{background-color:#428bca}.label-primary[href]:hover,.label-primary[href]:focus{background-color:#3071a9}.label-success{background-color:#5cb85c}.label-success[href]:hover,.label-success[href]:focus{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:hover,.label-info[href]:focus{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:hover,.label-warning[href]:focus{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:hover,.label-danger[href]:focus{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;color:#fff;line-height:1;vertical-align:baseline;white-space:nowrap;text-align:center;background-color:#999;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}a.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#428bca;background-color:#fff}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron h1,.jumbotron .h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.container .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron{padding-left:60px;padding-right:60px}.jumbotron h1,.jumbotron .h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.thumbnail>img,.thumbnail a>img{margin-left:auto;margin-right:auto}a.thumbnail:hover,a.thumbnail:focus,a.thumbnail.active{border-color:#428bca}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable{padding-right:35px}.alert-dismissable .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{background-color:#dff0d8;border-color:#d6e9c6;color:#3c763d}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{background-color:#d9edf7;border-color:#bce8f1;color:#31708f}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{background-color:#fcf8e3;border-color:#faebcc;color:#8a6d3b}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{background-color:#f2dede;border-color:#ebccd1;color:#a94442}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{overflow:hidden;height:20px;margin-bottom:20px;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#428bca;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;transition:width .6s ease}.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media,.media-body{overflow:hidden;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{padding-left:0;list-style:none}.list-group{margin-bottom:20px;padding-left:0}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-right-radius:4px;border-top-left-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:hover,a.list-group-item:focus{text-decoration:none;background-color:#f5f5f5}a.list-group-item.active,a.list-group-item.active:hover,a.list-group-item.active:focus{z-index:2;color:#fff;background-color:#428bca;border-color:#428bca}a.list-group-item.active .list-group-item-heading,a.list-group-item.active:hover .list-group-item-heading,a.list-group-item.active:focus .list-group-item-heading{color:inherit}a.list-group-item.active .list-group-item-text,a.list-group-item.active:hover .list-group-item-text,a.list-group-item.active:focus .list-group-item-text{color:#e1edf7}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:hover,a.list-group-item-success:focus{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:hover,a.list-group-item-success.active:focus{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:hover,a.list-group-item-info:focus{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:hover,a.list-group-item-info.active:focus{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:hover,a.list-group-item-warning:focus{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:hover,a.list-group-item-warning.active:focus{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:hover,a.list-group-item-danger:focus{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:hover,a.list-group-item-danger.active:focus{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-right-radius:3px;border-top-left-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{margin-bottom:0}.panel>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-right-radius:3px;border-top-left-radius:3px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.table:first-child,.panel>.table-responsive:first-child>.table:first-child{border-top-right-radius:3px;border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table:last-child,.panel>.table-responsive:last-child>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child th,.panel>.table>tbody:first-child>tr:first-child td{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{border:0;margin-bottom:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px;overflow:hidden}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse .panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse .panel-body{border-top-color:#ddd}.panel-default>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#428bca}.panel-primary>.panel-heading{color:#fff;background-color:#428bca;border-color:#428bca}.panel-primary>.panel-heading+.panel-collapse .panel-body{border-top-color:#428bca}.panel-primary>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#428bca}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse .panel-body{border-top-color:#d6e9c6}.panel-success>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse .panel-body{border-top-color:#bce8f1}.panel-info>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse .panel-body{border-top-color:#faebcc}.panel-warning>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse .panel-body{border-top-color:#ebccd1}.panel-danger>.panel-footer+.panel-collapse .panel-body{border-bottom-color:#ebccd1}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.5;filter:alpha(opacity=50)}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal{display:none;overflow:auto;overflow-y:scroll;position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-moz-transition:-moz-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);transform:translate(0,0)}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);background-clip:padding-box;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0;filter:alpha(opacity=0)}.modal-backdrop.in{opacity:.5;filter:alpha(opacity=50)}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5;min-height:16.42857143px}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:20px}.modal-footer{margin-top:15px;padding:19px 20px 20px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-left:5px;margin-bottom:0}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1030;display:block;visibility:visible;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0)}.tooltip.in{opacity:.9;filter:alpha(opacity=90)}.tooltip.top{margin-top:-3px;padding:5px 0}.tooltip.right{margin-left:3px;padding:0 5px}.tooltip.bottom{margin-top:3px;padding:5px 0}.tooltip.left{margin-left:-3px;padding:0 5px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{bottom:0;left:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;right:5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;left:5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;right:5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);white-space:normal}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{margin:0;padding:8px 14px;font-size:14px;font-weight:400;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{border-width:10px;content:""}.popover.top>.arrow{left:50%;margin-left:-11px;border-bottom-width:0;border-top-color:#999;border-top-color:rgba(0,0,0,.25);bottom:-11px}.popover.top>.arrow:after{content:" ";bottom:1px;margin-left:-10px;border-bottom-width:0;border-top-color:#fff}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-left-width:0;border-right-color:#999;border-right-color:rgba(0,0,0,.25)}.popover.right>.arrow:after{content:" ";left:1px;bottom:-10px;border-left-width:0;border-right-color:#fff}.popover.bottom>.arrow{left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25);top:-11px}.popover.bottom>.arrow:after{content:" ";top:1px;margin-left:-10px;border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{content:" ";right:1px;border-right-width:0;border-left-color:#fff;bottom:-10px}.carousel{position:relative}.carousel-inner{position:relative;overflow:hidden;width:100%}.carousel-inner>.item{display:none;position:relative;-webkit-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;left:0;bottom:0;width:15%;opacity:.5;filter:alpha(opacity=50);font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-control.left{background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.5) 0),color-stop(rgba(0,0,0,.0001) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1)}.carousel-control.right{left:auto;right:0;background-image:-webkit-linear-gradient(left,color-stop(rgba(0,0,0,.0001) 0),color-stop(rgba(0,0,0,.5) 100%));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1)}.carousel-control:hover,.carousel-control:focus{outline:0;color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-control .icon-prev,.carousel-control .icon-next,.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .icon-prev,.carousel-control .glyphicon-chevron-left{left:50%}.carousel-control .icon-next,.carousel-control .glyphicon-chevron-right{right:50%}.carousel-control .icon-prev,.carousel-control .icon-next{width:20px;height:20px;margin-top:-10px;margin-left:-10px;font-family:serif}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;margin-left:-30%;padding-left:0;list-style:none;text-align:center}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;border:1px solid #fff;border-radius:10px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0)}.carousel-indicators .active{margin:0;width:12px;height:12px;background-color:#fff}.carousel-caption{position:absolute;left:15%;right:15%;bottom:20px;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-prev,.carousel-control .icon-next{width:30px;height:30px;margin-top:-15px;margin-left:-15px;font-size:30px}.carousel-caption{left:20%;right:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.clearfix:before,.clearfix:after,.container:before,.container:after,.container-fluid:before,.container-fluid:after,.row:before,.row:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after,.btn-toolbar:before,.btn-toolbar:after,.btn-group-vertical>.btn-group:before,.btn-group-vertical>.btn-group:after,.nav:before,.nav:after,.navbar:before,.navbar:after,.navbar-header:before,.navbar-header:after,.navbar-collapse:before,.navbar-collapse:after,.pager:before,.pager:after,.panel-body:before,.panel-body:after,.modal-footer:before,.modal-footer:after{content:" ";display:table}.clearfix:after,.container:after,.container-fluid:after,.row:after,.form-horizontal .form-group:after,.btn-toolbar:after,.btn-group-vertical>.btn-group:after,.nav:after,.navbar:after,.navbar-header:after,.navbar-collapse:after,.pager:after,.panel-body:after,.modal-footer:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-xs,.visible-sm,.visible-md,.visible-lg{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}th.visible-xs,td.visible-xs{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}th.visible-sm,td.visible-sm{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}th.visible-md,td.visible-md{display:table-cell!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}th.visible-lg,td.visible-lg{display:table-cell!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}th.visible-print,td.visible-print{display:table-cell!important}}@media print{.hidden-print{display:none!important}}
\ No newline at end of file
diff --git a/src/main/jbake/assets/css/prettify.css b/src/main/jbake/assets/css/prettify.css
new file mode 100644
index 0000000..805d39b
--- /dev/null
+++ b/src/main/jbake/assets/css/prettify.css
@@ -0,0 +1 @@
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
diff --git a/css/bootstrap.css b/src/main/jbake/assets/css/styles.css
similarity index 55%
rename from css/bootstrap.css
rename to src/main/jbake/assets/css/styles.css
index bbda4ee..ed9939e 100644
--- a/css/bootstrap.css
+++ b/src/main/jbake/assets/css/styles.css
@@ -1,15 +1,14 @@
-/*!
- * Bootstrap v3.0.0
- *
- * Copyright 2013 Twitter, Inc
- * Licensed under the Apache License v2.0
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Designed and built with all the love in the world by @mdo and @fat.
- */
-
-/*! normalize.css v2.1.0 | MIT License | git.io/normalize */
-
+@import url(//fonts.googleapis.com/css?family=Open+Sans:300,400,600);
+@import url(//fonts.googleapis.com/css?family=Exo+2:400,300,600);
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+html {
+ font-family: sans-serif;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+}
+body {
+ margin: 0;
+}
article,
aside,
details,
@@ -19,206 +18,178 @@ footer,
header,
hgroup,
main,
+menu,
nav,
section,
summary {
display: block;
}
-
audio,
canvas,
+progress,
video {
display: inline-block;
+ vertical-align: baseline;
}
-
audio:not([controls]) {
display: none;
height: 0;
}
-
-[hidden] {
+[hidden],
+template {
display: none;
}
-
-html {
- font-family: sans-serif;
- -webkit-text-size-adjust: 100%;
- -ms-text-size-adjust: 100%;
-}
-
-body {
- margin: 0;
-}
-
-a:focus {
- outline: thin dotted;
+a {
+ background-color: transparent;
}
-
a:active,
a:hover {
outline: 0;
}
-
-h1 {
- margin: 0.67em 0;
- font-size: 2em;
-}
-
abbr[title] {
border-bottom: 1px dotted;
}
-
b,
strong {
font-weight: bold;
}
-
dfn {
font-style: italic;
}
-
-hr {
- height: 0;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
}
-
mark {
- color: #000;
background: #ff0;
+ color: #000;
}
-
-code,
-kbd,
-pre,
-samp {
- font-family: monospace, serif;
- font-size: 1em;
-}
-
-pre {
- white-space: pre-wrap;
-}
-
-q {
- quotes: "\201C" "\201D" "\2018" "\2019";
-}
-
small {
font-size: 80%;
}
-
sub,
sup {
- position: relative;
font-size: 75%;
line-height: 0;
+ position: relative;
vertical-align: baseline;
}
-
sup {
top: -0.5em;
}
-
sub {
bottom: -0.25em;
}
-
img {
border: 0;
}
-
svg:not(:root) {
overflow: hidden;
}
-
figure {
- margin: 0;
+ margin: 1em 40px;
}
-
-fieldset {
- padding: 0.35em 0.625em 0.75em;
- margin: 0 2px;
- border: 1px solid #c0c0c0;
+hr {
+ -moz-box-sizing: content-box;
+ box-sizing: content-box;
+ height: 0;
}
-
-legend {
- padding: 0;
- border: 0;
+pre {
+ overflow: auto;
+}
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
}
-
button,
input,
+optgroup,
select,
textarea {
+ color: inherit;
+ font: inherit;
margin: 0;
- font-family: inherit;
- font-size: 100%;
}
-
-button,
-input {
- line-height: normal;
+button {
+ overflow: visible;
}
-
button,
select {
text-transform: none;
}
-
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
- cursor: pointer;
-webkit-appearance: button;
+ cursor: pointer;
}
-
button[disabled],
html input[disabled] {
cursor: default;
}
-
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+input {
+ line-height: normal;
+}
input[type="checkbox"],
input[type="radio"] {
- padding: 0;
box-sizing: border-box;
+ padding: 0;
+}
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
}
-
input[type="search"] {
- -webkit-box-sizing: content-box;
- -moz-box-sizing: content-box;
- box-sizing: content-box;
-webkit-appearance: textfield;
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box;
+ box-sizing: content-box;
}
-
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
-
-button::-moz-focus-inner,
-input::-moz-focus-inner {
- padding: 0;
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+legend {
border: 0;
+ padding: 0;
}
-
textarea {
overflow: auto;
- vertical-align: top;
}
-
+optgroup {
+ font-weight: bold;
+}
table {
border-collapse: collapse;
border-spacing: 0;
}
-
+td,
+th {
+ padding: 0;
+}
+/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
@media print {
- * {
- color: #000 !important;
- text-shadow: none !important;
+ *,
+ *:before,
+ *:after {
background: transparent !important;
+ color: #000 !important;
box-shadow: none !important;
+ text-shadow: none !important;
}
a,
a:visited {
@@ -230,9 +201,8 @@ table {
abbr[title]:after {
content: " (" attr(title) ")";
}
- .ir a:after,
- a[href^="javascript:"]:after,
- a[href^="#"]:after {
+ a[href^="#"]:after,
+ a[href^="javascript:"]:after {
content: "";
}
pre,
@@ -250,9 +220,6 @@ table {
img {
max-width: 100% !important;
}
- @page {
- margin: 2cm .5cm;
- }
p,
h2,
h3 {
@@ -263,13 +230,12 @@ table {
h3 {
page-break-after: avoid;
}
+ select {
+ background: #fff !important;
+ }
.navbar {
display: none;
}
- .table td,
- .table th {
- background-color: #fff !important;
- }
.btn > .caret,
.dropup > .btn > .caret {
border-top-color: #000 !important;
@@ -280,2947 +246,2965 @@ table {
.table {
border-collapse: collapse !important;
}
+ .table td,
+ .table th {
+ background-color: #fff !important;
+ }
.table-bordered th,
.table-bordered td {
border: 1px solid #ddd !important;
}
}
-
-*,
-*:before,
-*:after {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('fonts/glyphicons-halflings-regular.eot');
+ src: url('fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('fonts/glyphicons-halflings-regular.woff') format('woff'), url('fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}
-
-html {
- font-size: 62.5%;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+.glyphicon {
+ position: relative;
+ top: 1px;
+ display: inline-block;
+ font-family: 'Glyphicons Halflings';
+ font-style: normal;
+ font-weight: normal;
+ line-height: 1;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
}
-
-body {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-size: 14px;
- line-height: 1.428571429;
- color: #333333;
- background-color: #ffffff;
+.glyphicon-asterisk:before {
+ content: "\2a";
}
-
-input,
-button,
-select,
-textarea {
- font-family: inherit;
- font-size: inherit;
- line-height: inherit;
+.glyphicon-plus:before {
+ content: "\2b";
}
-
-button,
-input,
-select[multiple],
-textarea {
- background-image: none;
+.glyphicon-euro:before,
+.glyphicon-eur:before {
+ content: "\20ac";
}
-
-a {
- color: #428bca;
- text-decoration: none;
+.glyphicon-minus:before {
+ content: "\2212";
}
-
-a:hover,
-a:focus {
- color: #2a6496;
- text-decoration: underline;
+.glyphicon-cloud:before {
+ content: "\2601";
}
-
-a:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
+.glyphicon-envelope:before {
+ content: "\2709";
}
-
-img {
- vertical-align: middle;
+.glyphicon-pencil:before {
+ content: "\270f";
}
-
-.img-responsive {
- display: block;
- height: auto;
- max-width: 100%;
+.glyphicon-glass:before {
+ content: "\e001";
}
-
-.img-rounded {
- border-radius: 6px;
+.glyphicon-music:before {
+ content: "\e002";
}
-
-.img-thumbnail {
- display: inline-block;
- height: auto;
- max-width: 100%;
- padding: 4px;
- line-height: 1.428571429;
- background-color: #ffffff;
- border: 1px solid #dddddd;
- border-radius: 4px;
- -webkit-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
+.glyphicon-search:before {
+ content: "\e003";
}
-
-.img-circle {
- border-radius: 50%;
+.glyphicon-heart:before {
+ content: "\e005";
}
-
-hr {
- margin-top: 20px;
- margin-bottom: 20px;
- border: 0;
- border-top: 1px solid #eeeeee;
+.glyphicon-star:before {
+ content: "\e006";
}
-
-.sr-only {
- position: absolute;
- width: 1px;
- height: 1px;
- padding: 0;
- margin: -1px;
- overflow: hidden;
- clip: rect(0 0 0 0);
- border: 0;
+.glyphicon-star-empty:before {
+ content: "\e007";
}
-
-p {
- margin: 0 0 10px;
+.glyphicon-user:before {
+ content: "\e008";
}
-
-.lead {
- margin-bottom: 20px;
- font-size: 16.099999999999998px;
- font-weight: 200;
- line-height: 1.4;
+.glyphicon-film:before {
+ content: "\e009";
}
-
-@media (min-width: 768px) {
- .lead {
- font-size: 21px;
- }
+.glyphicon-th-large:before {
+ content: "\e010";
}
-
-small {
- font-size: 85%;
+.glyphicon-th:before {
+ content: "\e011";
}
-
-cite {
- font-style: normal;
+.glyphicon-th-list:before {
+ content: "\e012";
}
-
-.text-muted {
- color: #999999;
+.glyphicon-ok:before {
+ content: "\e013";
}
-
-.text-primary {
- color: #428bca;
+.glyphicon-remove:before {
+ content: "\e014";
}
-
-.text-warning {
- color: #c09853;
+.glyphicon-zoom-in:before {
+ content: "\e015";
}
-
-.text-danger {
- color: #b94a48;
+.glyphicon-zoom-out:before {
+ content: "\e016";
}
-
-.text-success {
- color: #468847;
+.glyphicon-off:before {
+ content: "\e017";
}
-
-.text-info {
- color: #3a87ad;
+.glyphicon-signal:before {
+ content: "\e018";
}
-
-.text-left {
- text-align: left;
+.glyphicon-cog:before {
+ content: "\e019";
}
-
-.text-right {
- text-align: right;
+.glyphicon-trash:before {
+ content: "\e020";
}
-
-.text-center {
- text-align: center;
+.glyphicon-home:before {
+ content: "\e021";
}
-
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-.h1,
-.h2,
-.h3,
-.h4,
-.h5,
-.h6 {
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
- font-weight: 500;
- line-height: 1.1;
+.glyphicon-file:before {
+ content: "\e022";
}
-
-h1 small,
-h2 small,
-h3 small,
-h4 small,
-h5 small,
-h6 small,
-.h1 small,
-.h2 small,
-.h3 small,
-.h4 small,
-.h5 small,
-.h6 small {
- font-weight: normal;
- line-height: 1;
- color: #999999;
+.glyphicon-time:before {
+ content: "\e023";
}
-
-h1,
-h2,
-h3 {
- margin-top: 20px;
- margin-bottom: 10px;
+.glyphicon-road:before {
+ content: "\e024";
}
-
-h4,
-h5,
-h6 {
- margin-top: 10px;
- margin-bottom: 10px;
+.glyphicon-download-alt:before {
+ content: "\e025";
}
-
-h1,
-.h1 {
- font-size: 36px;
+.glyphicon-download:before {
+ content: "\e026";
}
-
-h2,
-.h2 {
- font-size: 30px;
+.glyphicon-upload:before {
+ content: "\e027";
}
-
-h3,
-.h3 {
- font-size: 24px;
+.glyphicon-inbox:before {
+ content: "\e028";
}
-
-h4,
-.h4 {
- font-size: 18px;
+.glyphicon-play-circle:before {
+ content: "\e029";
}
-
-h5,
-.h5 {
- font-size: 14px;
+.glyphicon-repeat:before {
+ content: "\e030";
}
-
-h6,
-.h6 {
- font-size: 12px;
+.glyphicon-refresh:before {
+ content: "\e031";
}
-
-h1 small,
-.h1 small {
- font-size: 24px;
+.glyphicon-list-alt:before {
+ content: "\e032";
}
-
-h2 small,
-.h2 small {
- font-size: 18px;
+.glyphicon-lock:before {
+ content: "\e033";
}
-
-h3 small,
-.h3 small,
-h4 small,
-.h4 small {
- font-size: 14px;
+.glyphicon-flag:before {
+ content: "\e034";
}
-
-.page-header {
- padding-bottom: 9px;
- margin: 40px 0 20px;
- border-bottom: 1px solid #eeeeee;
+.glyphicon-headphones:before {
+ content: "\e035";
}
-
-ul,
-ol {
- margin-top: 0;
- margin-bottom: 10px;
+.glyphicon-volume-off:before {
+ content: "\e036";
}
-
-ul ul,
-ol ul,
-ul ol,
-ol ol {
- margin-bottom: 0;
+.glyphicon-volume-down:before {
+ content: "\e037";
}
-
-.list-unstyled {
- padding-left: 0;
- list-style: none;
+.glyphicon-volume-up:before {
+ content: "\e038";
}
-
-.list-inline {
- padding-left: 0;
- list-style: none;
+.glyphicon-qrcode:before {
+ content: "\e039";
}
-
-.list-inline > li {
- display: inline-block;
- padding-right: 5px;
- padding-left: 5px;
+.glyphicon-barcode:before {
+ content: "\e040";
}
-
-dl {
- margin-bottom: 20px;
+.glyphicon-tag:before {
+ content: "\e041";
}
-
-dt,
-dd {
- line-height: 1.428571429;
+.glyphicon-tags:before {
+ content: "\e042";
}
-
-dt {
- font-weight: bold;
+.glyphicon-book:before {
+ content: "\e043";
}
-
-dd {
- margin-left: 0;
+.glyphicon-bookmark:before {
+ content: "\e044";
}
-
-@media (min-width: 768px) {
- .dl-horizontal dt {
- float: left;
- width: 160px;
- overflow: hidden;
- clear: left;
- text-align: right;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- .dl-horizontal dd {
- margin-left: 180px;
- }
- .dl-horizontal dd:before,
- .dl-horizontal dd:after {
- display: table;
- content: " ";
- }
- .dl-horizontal dd:after {
- clear: both;
- }
- .dl-horizontal dd:before,
- .dl-horizontal dd:after {
- display: table;
- content: " ";
- }
- .dl-horizontal dd:after {
- clear: both;
- }
+.glyphicon-print:before {
+ content: "\e045";
}
-
-abbr[title],
-abbr[data-original-title] {
- cursor: help;
- border-bottom: 1px dotted #999999;
+.glyphicon-camera:before {
+ content: "\e046";
}
-
-abbr.initialism {
- font-size: 90%;
- text-transform: uppercase;
+.glyphicon-font:before {
+ content: "\e047";
}
-
-blockquote {
- padding: 10px 20px;
- margin: 0 0 20px;
- border-left: 5px solid #eeeeee;
+.glyphicon-bold:before {
+ content: "\e048";
}
-
-blockquote p {
- font-size: 17.5px;
- font-weight: 300;
- line-height: 1.25;
+.glyphicon-italic:before {
+ content: "\e049";
}
-
-blockquote p:last-child {
- margin-bottom: 0;
+.glyphicon-text-height:before {
+ content: "\e050";
}
-
-blockquote small {
- display: block;
- line-height: 1.428571429;
- color: #999999;
+.glyphicon-text-width:before {
+ content: "\e051";
}
-
-blockquote small:before {
- content: '\2014 \00A0';
+.glyphicon-align-left:before {
+ content: "\e052";
}
-
-blockquote.pull-right {
- padding-right: 15px;
- padding-left: 0;
- border-right: 5px solid #eeeeee;
- border-left: 0;
+.glyphicon-align-center:before {
+ content: "\e053";
}
-
-blockquote.pull-right p,
-blockquote.pull-right small {
- text-align: right;
+.glyphicon-align-right:before {
+ content: "\e054";
}
-
-blockquote.pull-right small:before {
- content: '';
+.glyphicon-align-justify:before {
+ content: "\e055";
}
-
-blockquote.pull-right small:after {
- content: '\00A0 \2014';
+.glyphicon-list:before {
+ content: "\e056";
}
-
-q:before,
-q:after,
-blockquote:before,
-blockquote:after {
- content: "";
+.glyphicon-indent-left:before {
+ content: "\e057";
}
-
-address {
- display: block;
- margin-bottom: 20px;
- font-style: normal;
- line-height: 1.428571429;
+.glyphicon-indent-right:before {
+ content: "\e058";
}
-
-code,
-pre {
- font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+.glyphicon-facetime-video:before {
+ content: "\e059";
}
-
-code {
- padding: 2px 4px;
- font-size: 90%;
- color: #c7254e;
- white-space: nowrap;
- background-color: #f9f2f4;
- border-radius: 4px;
+.glyphicon-picture:before {
+ content: "\e060";
}
-
-pre {
- display: block;
- padding: 9.5px;
- margin: 0 0 10px;
- font-size: 13px;
- line-height: 1.428571429;
- color: #333333;
- word-break: break-all;
- word-wrap: break-word;
- background-color: #f5f5f5;
- border: 1px solid #cccccc;
- border-radius: 4px;
+.glyphicon-map-marker:before {
+ content: "\e062";
}
-
-pre.prettyprint {
- margin-bottom: 20px;
+.glyphicon-adjust:before {
+ content: "\e063";
}
-
-pre code {
- padding: 0;
- font-size: inherit;
- color: inherit;
- white-space: pre-wrap;
- background-color: transparent;
- border: 0;
+.glyphicon-tint:before {
+ content: "\e064";
}
-
-.pre-scrollable {
- max-height: 340px;
- overflow-y: scroll;
+.glyphicon-edit:before {
+ content: "\e065";
}
-
-.container {
- padding-right: 15px;
- padding-left: 15px;
- margin-right: auto;
- margin-left: auto;
+.glyphicon-share:before {
+ content: "\e066";
}
-
-.container:before,
-.container:after {
- display: table;
- content: " ";
+.glyphicon-check:before {
+ content: "\e067";
}
-
-.container:after {
- clear: both;
+.glyphicon-move:before {
+ content: "\e068";
}
-
-.container:before,
-.container:after {
- display: table;
- content: " ";
+.glyphicon-step-backward:before {
+ content: "\e069";
}
-
-.container:after {
- clear: both;
+.glyphicon-fast-backward:before {
+ content: "\e070";
}
-
-.row {
- margin-right: -15px;
- margin-left: -15px;
+.glyphicon-backward:before {
+ content: "\e071";
}
-
-.row:before,
-.row:after {
- display: table;
- content: " ";
+.glyphicon-play:before {
+ content: "\e072";
}
-
-.row:after {
- clear: both;
+.glyphicon-pause:before {
+ content: "\e073";
}
-
-.row:before,
-.row:after {
- display: table;
- content: " ";
+.glyphicon-stop:before {
+ content: "\e074";
}
-
-.row:after {
- clear: both;
+.glyphicon-forward:before {
+ content: "\e075";
}
-
-.col-xs-1,
-.col-xs-2,
-.col-xs-3,
-.col-xs-4,
-.col-xs-5,
-.col-xs-6,
-.col-xs-7,
-.col-xs-8,
-.col-xs-9,
-.col-xs-10,
-.col-xs-11,
-.col-xs-12,
-.col-sm-1,
-.col-sm-2,
-.col-sm-3,
-.col-sm-4,
-.col-sm-5,
-.col-sm-6,
-.col-sm-7,
-.col-sm-8,
-.col-sm-9,
-.col-sm-10,
-.col-sm-11,
-.col-sm-12,
-.col-md-1,
-.col-md-2,
-.col-md-3,
-.col-md-4,
-.col-md-5,
-.col-md-6,
-.col-md-7,
-.col-md-8,
-.col-md-9,
-.col-md-10,
-.col-md-11,
-.col-md-12,
-.col-lg-1,
-.col-lg-2,
-.col-lg-3,
-.col-lg-4,
-.col-lg-5,
-.col-lg-6,
-.col-lg-7,
-.col-lg-8,
-.col-lg-9,
-.col-lg-10,
-.col-lg-11,
-.col-lg-12 {
- position: relative;
- min-height: 1px;
- padding-right: 15px;
- padding-left: 15px;
+.glyphicon-fast-forward:before {
+ content: "\e076";
}
-
-.col-xs-1,
-.col-xs-2,
-.col-xs-3,
-.col-xs-4,
-.col-xs-5,
-.col-xs-6,
-.col-xs-7,
-.col-xs-8,
-.col-xs-9,
-.col-xs-10,
-.col-xs-11 {
- float: left;
+.glyphicon-step-forward:before {
+ content: "\e077";
}
-
-.col-xs-1 {
- width: 8.333333333333332%;
+.glyphicon-eject:before {
+ content: "\e078";
}
-
-.col-xs-2 {
- width: 16.666666666666664%;
+.glyphicon-chevron-left:before {
+ content: "\e079";
}
-
-.col-xs-3 {
- width: 25%;
+.glyphicon-chevron-right:before {
+ content: "\e080";
}
-
-.col-xs-4 {
- width: 33.33333333333333%;
+.glyphicon-plus-sign:before {
+ content: "\e081";
}
-
-.col-xs-5 {
- width: 41.66666666666667%;
+.glyphicon-minus-sign:before {
+ content: "\e082";
}
-
-.col-xs-6 {
- width: 50%;
+.glyphicon-remove-sign:before {
+ content: "\e083";
}
-
-.col-xs-7 {
- width: 58.333333333333336%;
+.glyphicon-ok-sign:before {
+ content: "\e084";
}
-
-.col-xs-8 {
- width: 66.66666666666666%;
+.glyphicon-question-sign:before {
+ content: "\e085";
}
-
-.col-xs-9 {
- width: 75%;
+.glyphicon-info-sign:before {
+ content: "\e086";
}
-
-.col-xs-10 {
- width: 83.33333333333334%;
+.glyphicon-screenshot:before {
+ content: "\e087";
}
-
-.col-xs-11 {
- width: 91.66666666666666%;
+.glyphicon-remove-circle:before {
+ content: "\e088";
}
-
-.col-xs-12 {
- width: 100%;
+.glyphicon-ok-circle:before {
+ content: "\e089";
}
-
-@media (min-width: 768px) {
- .container {
- max-width: 750px;
- }
- .col-sm-1,
- .col-sm-2,
- .col-sm-3,
- .col-sm-4,
- .col-sm-5,
- .col-sm-6,
- .col-sm-7,
- .col-sm-8,
- .col-sm-9,
- .col-sm-10,
- .col-sm-11 {
- float: left;
- }
- .col-sm-1 {
- width: 8.333333333333332%;
- }
- .col-sm-2 {
- width: 16.666666666666664%;
- }
- .col-sm-3 {
- width: 25%;
- }
- .col-sm-4 {
- width: 33.33333333333333%;
- }
- .col-sm-5 {
- width: 41.66666666666667%;
- }
- .col-sm-6 {
- width: 50%;
- }
- .col-sm-7 {
- width: 58.333333333333336%;
- }
- .col-sm-8 {
- width: 66.66666666666666%;
- }
- .col-sm-9 {
- width: 75%;
- }
- .col-sm-10 {
- width: 83.33333333333334%;
- }
- .col-sm-11 {
- width: 91.66666666666666%;
- }
- .col-sm-12 {
- width: 100%;
- }
- .col-sm-push-1 {
- left: 8.333333333333332%;
- }
- .col-sm-push-2 {
- left: 16.666666666666664%;
- }
- .col-sm-push-3 {
- left: 25%;
- }
- .col-sm-push-4 {
- left: 33.33333333333333%;
- }
- .col-sm-push-5 {
- left: 41.66666666666667%;
- }
- .col-sm-push-6 {
- left: 50%;
- }
- .col-sm-push-7 {
- left: 58.333333333333336%;
- }
- .col-sm-push-8 {
- left: 66.66666666666666%;
- }
- .col-sm-push-9 {
- left: 75%;
- }
- .col-sm-push-10 {
- left: 83.33333333333334%;
- }
- .col-sm-push-11 {
- left: 91.66666666666666%;
- }
- .col-sm-pull-1 {
- right: 8.333333333333332%;
- }
- .col-sm-pull-2 {
- right: 16.666666666666664%;
- }
- .col-sm-pull-3 {
- right: 25%;
- }
- .col-sm-pull-4 {
- right: 33.33333333333333%;
- }
- .col-sm-pull-5 {
- right: 41.66666666666667%;
- }
- .col-sm-pull-6 {
- right: 50%;
- }
- .col-sm-pull-7 {
- right: 58.333333333333336%;
- }
- .col-sm-pull-8 {
- right: 66.66666666666666%;
- }
- .col-sm-pull-9 {
- right: 75%;
- }
- .col-sm-pull-10 {
- right: 83.33333333333334%;
- }
- .col-sm-pull-11 {
- right: 91.66666666666666%;
- }
- .col-sm-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-sm-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-sm-offset-3 {
- margin-left: 25%;
- }
- .col-sm-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-sm-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-sm-offset-6 {
- margin-left: 50%;
- }
- .col-sm-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-sm-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-sm-offset-9 {
- margin-left: 75%;
- }
- .col-sm-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-sm-offset-11 {
- margin-left: 91.66666666666666%;
- }
+.glyphicon-ban-circle:before {
+ content: "\e090";
}
-
-@media (min-width: 992px) {
- .container {
- max-width: 970px;
- }
- .col-md-1,
- .col-md-2,
- .col-md-3,
- .col-md-4,
- .col-md-5,
- .col-md-6,
- .col-md-7,
- .col-md-8,
- .col-md-9,
- .col-md-10,
- .col-md-11 {
- float: left;
- }
- .col-md-1 {
- width: 8.333333333333332%;
- }
- .col-md-2 {
- width: 16.666666666666664%;
- }
- .col-md-3 {
- width: 25%;
- }
- .col-md-4 {
- width: 33.33333333333333%;
- }
- .col-md-5 {
- width: 41.66666666666667%;
- }
- .col-md-6 {
- width: 50%;
- }
- .col-md-7 {
- width: 58.333333333333336%;
- }
- .col-md-8 {
- width: 66.66666666666666%;
- }
- .col-md-9 {
- width: 75%;
- }
- .col-md-10 {
- width: 83.33333333333334%;
- }
- .col-md-11 {
- width: 91.66666666666666%;
- }
- .col-md-12 {
- width: 100%;
- }
- .col-md-push-0 {
- left: auto;
- }
- .col-md-push-1 {
- left: 8.333333333333332%;
- }
- .col-md-push-2 {
- left: 16.666666666666664%;
- }
- .col-md-push-3 {
- left: 25%;
- }
- .col-md-push-4 {
- left: 33.33333333333333%;
- }
- .col-md-push-5 {
- left: 41.66666666666667%;
- }
- .col-md-push-6 {
- left: 50%;
- }
- .col-md-push-7 {
- left: 58.333333333333336%;
- }
- .col-md-push-8 {
- left: 66.66666666666666%;
- }
- .col-md-push-9 {
- left: 75%;
- }
- .col-md-push-10 {
- left: 83.33333333333334%;
- }
- .col-md-push-11 {
- left: 91.66666666666666%;
- }
- .col-md-pull-0 {
- right: auto;
- }
- .col-md-pull-1 {
- right: 8.333333333333332%;
- }
- .col-md-pull-2 {
- right: 16.666666666666664%;
- }
- .col-md-pull-3 {
- right: 25%;
- }
- .col-md-pull-4 {
- right: 33.33333333333333%;
- }
- .col-md-pull-5 {
- right: 41.66666666666667%;
- }
- .col-md-pull-6 {
- right: 50%;
- }
- .col-md-pull-7 {
- right: 58.333333333333336%;
- }
- .col-md-pull-8 {
- right: 66.66666666666666%;
- }
- .col-md-pull-9 {
- right: 75%;
- }
- .col-md-pull-10 {
- right: 83.33333333333334%;
- }
- .col-md-pull-11 {
- right: 91.66666666666666%;
- }
- .col-md-offset-0 {
- margin-left: 0;
- }
- .col-md-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-md-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-md-offset-3 {
- margin-left: 25%;
- }
- .col-md-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-md-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-md-offset-6 {
- margin-left: 50%;
- }
- .col-md-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-md-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-md-offset-9 {
- margin-left: 75%;
- }
- .col-md-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-md-offset-11 {
- margin-left: 91.66666666666666%;
- }
+.glyphicon-arrow-left:before {
+ content: "\e091";
+}
+.glyphicon-arrow-right:before {
+ content: "\e092";
+}
+.glyphicon-arrow-up:before {
+ content: "\e093";
+}
+.glyphicon-arrow-down:before {
+ content: "\e094";
+}
+.glyphicon-share-alt:before {
+ content: "\e095";
+}
+.glyphicon-resize-full:before {
+ content: "\e096";
+}
+.glyphicon-resize-small:before {
+ content: "\e097";
+}
+.glyphicon-exclamation-sign:before {
+ content: "\e101";
+}
+.glyphicon-gift:before {
+ content: "\e102";
+}
+.glyphicon-leaf:before {
+ content: "\e103";
+}
+.glyphicon-fire:before {
+ content: "\e104";
+}
+.glyphicon-eye-open:before {
+ content: "\e105";
+}
+.glyphicon-eye-close:before {
+ content: "\e106";
+}
+.glyphicon-warning-sign:before {
+ content: "\e107";
+}
+.glyphicon-plane:before {
+ content: "\e108";
+}
+.glyphicon-calendar:before {
+ content: "\e109";
+}
+.glyphicon-random:before {
+ content: "\e110";
+}
+.glyphicon-comment:before {
+ content: "\e111";
+}
+.glyphicon-magnet:before {
+ content: "\e112";
+}
+.glyphicon-chevron-up:before {
+ content: "\e113";
+}
+.glyphicon-chevron-down:before {
+ content: "\e114";
+}
+.glyphicon-retweet:before {
+ content: "\e115";
+}
+.glyphicon-shopping-cart:before {
+ content: "\e116";
+}
+.glyphicon-folder-close:before {
+ content: "\e117";
+}
+.glyphicon-folder-open:before {
+ content: "\e118";
+}
+.glyphicon-resize-vertical:before {
+ content: "\e119";
+}
+.glyphicon-resize-horizontal:before {
+ content: "\e120";
+}
+.glyphicon-hdd:before {
+ content: "\e121";
+}
+.glyphicon-bullhorn:before {
+ content: "\e122";
+}
+.glyphicon-bell:before {
+ content: "\e123";
+}
+.glyphicon-certificate:before {
+ content: "\e124";
+}
+.glyphicon-thumbs-up:before {
+ content: "\e125";
+}
+.glyphicon-thumbs-down:before {
+ content: "\e126";
+}
+.glyphicon-hand-right:before {
+ content: "\e127";
+}
+.glyphicon-hand-left:before {
+ content: "\e128";
+}
+.glyphicon-hand-up:before {
+ content: "\e129";
+}
+.glyphicon-hand-down:before {
+ content: "\e130";
+}
+.glyphicon-circle-arrow-right:before {
+ content: "\e131";
+}
+.glyphicon-circle-arrow-left:before {
+ content: "\e132";
+}
+.glyphicon-circle-arrow-up:before {
+ content: "\e133";
+}
+.glyphicon-circle-arrow-down:before {
+ content: "\e134";
+}
+.glyphicon-globe:before {
+ content: "\e135";
+}
+.glyphicon-wrench:before {
+ content: "\e136";
+}
+.glyphicon-tasks:before {
+ content: "\e137";
+}
+.glyphicon-filter:before {
+ content: "\e138";
+}
+.glyphicon-briefcase:before {
+ content: "\e139";
+}
+.glyphicon-fullscreen:before {
+ content: "\e140";
+}
+.glyphicon-dashboard:before {
+ content: "\e141";
+}
+.glyphicon-paperclip:before {
+ content: "\e142";
+}
+.glyphicon-heart-empty:before {
+ content: "\e143";
+}
+.glyphicon-link:before {
+ content: "\e144";
+}
+.glyphicon-phone:before {
+ content: "\e145";
+}
+.glyphicon-pushpin:before {
+ content: "\e146";
+}
+.glyphicon-usd:before {
+ content: "\e148";
+}
+.glyphicon-gbp:before {
+ content: "\e149";
+}
+.glyphicon-sort:before {
+ content: "\e150";
+}
+.glyphicon-sort-by-alphabet:before {
+ content: "\e151";
+}
+.glyphicon-sort-by-alphabet-alt:before {
+ content: "\e152";
+}
+.glyphicon-sort-by-order:before {
+ content: "\e153";
+}
+.glyphicon-sort-by-order-alt:before {
+ content: "\e154";
+}
+.glyphicon-sort-by-attributes:before {
+ content: "\e155";
}
-
-@media (min-width: 1200px) {
- .container {
- max-width: 1170px;
- }
- .col-lg-1,
- .col-lg-2,
- .col-lg-3,
- .col-lg-4,
- .col-lg-5,
- .col-lg-6,
- .col-lg-7,
- .col-lg-8,
- .col-lg-9,
- .col-lg-10,
- .col-lg-11 {
- float: left;
- }
- .col-lg-1 {
- width: 8.333333333333332%;
- }
- .col-lg-2 {
- width: 16.666666666666664%;
- }
- .col-lg-3 {
- width: 25%;
- }
- .col-lg-4 {
- width: 33.33333333333333%;
- }
- .col-lg-5 {
- width: 41.66666666666667%;
- }
- .col-lg-6 {
- width: 50%;
- }
- .col-lg-7 {
- width: 58.333333333333336%;
- }
- .col-lg-8 {
- width: 66.66666666666666%;
- }
- .col-lg-9 {
- width: 75%;
- }
- .col-lg-10 {
- width: 83.33333333333334%;
- }
- .col-lg-11 {
- width: 91.66666666666666%;
- }
- .col-lg-12 {
- width: 100%;
- }
- .col-lg-push-0 {
- left: auto;
- }
- .col-lg-push-1 {
- left: 8.333333333333332%;
- }
- .col-lg-push-2 {
- left: 16.666666666666664%;
- }
- .col-lg-push-3 {
- left: 25%;
- }
- .col-lg-push-4 {
- left: 33.33333333333333%;
- }
- .col-lg-push-5 {
- left: 41.66666666666667%;
- }
- .col-lg-push-6 {
- left: 50%;
- }
- .col-lg-push-7 {
- left: 58.333333333333336%;
- }
- .col-lg-push-8 {
- left: 66.66666666666666%;
- }
- .col-lg-push-9 {
- left: 75%;
- }
- .col-lg-push-10 {
- left: 83.33333333333334%;
- }
- .col-lg-push-11 {
- left: 91.66666666666666%;
- }
- .col-lg-pull-0 {
- right: auto;
- }
- .col-lg-pull-1 {
- right: 8.333333333333332%;
- }
- .col-lg-pull-2 {
- right: 16.666666666666664%;
- }
- .col-lg-pull-3 {
- right: 25%;
- }
- .col-lg-pull-4 {
- right: 33.33333333333333%;
- }
- .col-lg-pull-5 {
- right: 41.66666666666667%;
- }
- .col-lg-pull-6 {
- right: 50%;
- }
- .col-lg-pull-7 {
- right: 58.333333333333336%;
- }
- .col-lg-pull-8 {
- right: 66.66666666666666%;
- }
- .col-lg-pull-9 {
- right: 75%;
- }
- .col-lg-pull-10 {
- right: 83.33333333333334%;
- }
- .col-lg-pull-11 {
- right: 91.66666666666666%;
- }
- .col-lg-offset-0 {
- margin-left: 0;
- }
- .col-lg-offset-1 {
- margin-left: 8.333333333333332%;
- }
- .col-lg-offset-2 {
- margin-left: 16.666666666666664%;
- }
- .col-lg-offset-3 {
- margin-left: 25%;
- }
- .col-lg-offset-4 {
- margin-left: 33.33333333333333%;
- }
- .col-lg-offset-5 {
- margin-left: 41.66666666666667%;
- }
- .col-lg-offset-6 {
- margin-left: 50%;
- }
- .col-lg-offset-7 {
- margin-left: 58.333333333333336%;
- }
- .col-lg-offset-8 {
- margin-left: 66.66666666666666%;
- }
- .col-lg-offset-9 {
- margin-left: 75%;
- }
- .col-lg-offset-10 {
- margin-left: 83.33333333333334%;
- }
- .col-lg-offset-11 {
- margin-left: 91.66666666666666%;
- }
+.glyphicon-sort-by-attributes-alt:before {
+ content: "\e156";
}
-
-table {
- max-width: 100%;
- background-color: transparent;
+.glyphicon-unchecked:before {
+ content: "\e157";
}
-
-th {
- text-align: left;
+.glyphicon-expand:before {
+ content: "\e158";
}
-
-.table {
- width: 100%;
- margin-bottom: 20px;
+.glyphicon-collapse-down:before {
+ content: "\e159";
}
-
-.table thead > tr > th,
-.table tbody > tr > th,
-.table tfoot > tr > th,
-.table thead > tr > td,
-.table tbody > tr > td,
-.table tfoot > tr > td {
- padding: 8px;
- line-height: 1.428571429;
- vertical-align: top;
- border-top: 1px solid #dddddd;
+.glyphicon-collapse-up:before {
+ content: "\e160";
}
-
-.table thead > tr > th {
- vertical-align: bottom;
- border-bottom: 2px solid #dddddd;
+.glyphicon-log-in:before {
+ content: "\e161";
}
-
-.table caption + thead tr:first-child th,
-.table colgroup + thead tr:first-child th,
-.table thead:first-child tr:first-child th,
-.table caption + thead tr:first-child td,
-.table colgroup + thead tr:first-child td,
-.table thead:first-child tr:first-child td {
- border-top: 0;
+.glyphicon-flash:before {
+ content: "\e162";
}
-
-.table tbody + tbody {
- border-top: 2px solid #dddddd;
+.glyphicon-log-out:before {
+ content: "\e163";
}
-
-.table .table {
- background-color: #ffffff;
+.glyphicon-new-window:before {
+ content: "\e164";
}
-
-.table-condensed thead > tr > th,
-.table-condensed tbody > tr > th,
-.table-condensed tfoot > tr > th,
-.table-condensed thead > tr > td,
-.table-condensed tbody > tr > td,
-.table-condensed tfoot > tr > td {
- padding: 5px;
+.glyphicon-record:before {
+ content: "\e165";
}
-
-.table-bordered {
- border: 1px solid #dddddd;
+.glyphicon-save:before {
+ content: "\e166";
}
-
-.table-bordered > thead > tr > th,
-.table-bordered > tbody > tr > th,
-.table-bordered > tfoot > tr > th,
-.table-bordered > thead > tr > td,
-.table-bordered > tbody > tr > td,
-.table-bordered > tfoot > tr > td {
- border: 1px solid #dddddd;
+.glyphicon-open:before {
+ content: "\e167";
}
-
-.table-bordered > thead > tr > th,
-.table-bordered > thead > tr > td {
- border-bottom-width: 2px;
+.glyphicon-saved:before {
+ content: "\e168";
}
-
-.table-striped > tbody > tr:nth-child(odd) > td,
-.table-striped > tbody > tr:nth-child(odd) > th {
- background-color: #f9f9f9;
+.glyphicon-import:before {
+ content: "\e169";
}
-
-.table-hover > tbody > tr:hover > td,
-.table-hover > tbody > tr:hover > th {
- background-color: #f5f5f5;
+.glyphicon-export:before {
+ content: "\e170";
}
-
-table col[class*="col-"] {
- display: table-column;
- float: none;
+.glyphicon-send:before {
+ content: "\e171";
}
-
-table td[class*="col-"],
-table th[class*="col-"] {
- display: table-cell;
- float: none;
+.glyphicon-floppy-disk:before {
+ content: "\e172";
+}
+.glyphicon-floppy-saved:before {
+ content: "\e173";
+}
+.glyphicon-floppy-remove:before {
+ content: "\e174";
+}
+.glyphicon-floppy-save:before {
+ content: "\e175";
+}
+.glyphicon-floppy-open:before {
+ content: "\e176";
+}
+.glyphicon-credit-card:before {
+ content: "\e177";
+}
+.glyphicon-transfer:before {
+ content: "\e178";
+}
+.glyphicon-cutlery:before {
+ content: "\e179";
+}
+.glyphicon-header:before {
+ content: "\e180";
+}
+.glyphicon-compressed:before {
+ content: "\e181";
+}
+.glyphicon-earphone:before {
+ content: "\e182";
+}
+.glyphicon-phone-alt:before {
+ content: "\e183";
+}
+.glyphicon-tower:before {
+ content: "\e184";
+}
+.glyphicon-stats:before {
+ content: "\e185";
+}
+.glyphicon-sd-video:before {
+ content: "\e186";
+}
+.glyphicon-hd-video:before {
+ content: "\e187";
+}
+.glyphicon-subtitles:before {
+ content: "\e188";
}
-
-.table > thead > tr > td.active,
-.table > tbody > tr > td.active,
-.table > tfoot > tr > td.active,
-.table > thead > tr > th.active,
-.table > tbody > tr > th.active,
-.table > tfoot > tr > th.active,
-.table > thead > tr.active > td,
-.table > tbody > tr.active > td,
-.table > tfoot > tr.active > td,
-.table > thead > tr.active > th,
-.table > tbody > tr.active > th,
-.table > tfoot > tr.active > th {
- background-color: #f5f5f5;
+.glyphicon-sound-stereo:before {
+ content: "\e189";
}
-
-.table > thead > tr > td.success,
-.table > tbody > tr > td.success,
-.table > tfoot > tr > td.success,
-.table > thead > tr > th.success,
-.table > tbody > tr > th.success,
-.table > tfoot > tr > th.success,
-.table > thead > tr.success > td,
-.table > tbody > tr.success > td,
-.table > tfoot > tr.success > td,
-.table > thead > tr.success > th,
-.table > tbody > tr.success > th,
-.table > tfoot > tr.success > th {
- background-color: #dff0d8;
- border-color: #d6e9c6;
+.glyphicon-sound-dolby:before {
+ content: "\e190";
}
-
-.table-hover > tbody > tr > td.success:hover,
-.table-hover > tbody > tr > th.success:hover,
-.table-hover > tbody > tr.success:hover > td {
- background-color: #d0e9c6;
- border-color: #c9e2b3;
+.glyphicon-sound-5-1:before {
+ content: "\e191";
}
-
-.table > thead > tr > td.danger,
-.table > tbody > tr > td.danger,
-.table > tfoot > tr > td.danger,
-.table > thead > tr > th.danger,
-.table > tbody > tr > th.danger,
-.table > tfoot > tr > th.danger,
-.table > thead > tr.danger > td,
-.table > tbody > tr.danger > td,
-.table > tfoot > tr.danger > td,
-.table > thead > tr.danger > th,
-.table > tbody > tr.danger > th,
-.table > tfoot > tr.danger > th {
- background-color: #f2dede;
- border-color: #eed3d7;
+.glyphicon-sound-6-1:before {
+ content: "\e192";
}
-
-.table-hover > tbody > tr > td.danger:hover,
-.table-hover > tbody > tr > th.danger:hover,
-.table-hover > tbody > tr.danger:hover > td {
- background-color: #ebcccc;
- border-color: #e6c1c7;
+.glyphicon-sound-7-1:before {
+ content: "\e193";
}
-
-.table > thead > tr > td.warning,
-.table > tbody > tr > td.warning,
-.table > tfoot > tr > td.warning,
-.table > thead > tr > th.warning,
-.table > tbody > tr > th.warning,
-.table > tfoot > tr > th.warning,
-.table > thead > tr.warning > td,
-.table > tbody > tr.warning > td,
-.table > tfoot > tr.warning > td,
-.table > thead > tr.warning > th,
-.table > tbody > tr.warning > th,
-.table > tfoot > tr.warning > th {
- background-color: #fcf8e3;
- border-color: #fbeed5;
+.glyphicon-copyright-mark:before {
+ content: "\e194";
}
-
-.table-hover > tbody > tr > td.warning:hover,
-.table-hover > tbody > tr > th.warning:hover,
-.table-hover > tbody > tr.warning:hover > td {
- background-color: #faf2cc;
- border-color: #f8e5be;
+.glyphicon-registration-mark:before {
+ content: "\e195";
}
-
-@media (max-width: 768px) {
- .table-responsive {
- width: 100%;
- margin-bottom: 15px;
- overflow-x: scroll;
- overflow-y: hidden;
- border: 1px solid #dddddd;
- }
- .table-responsive > .table {
- margin-bottom: 0;
- background-color: #fff;
- }
- .table-responsive > .table > thead > tr > th,
- .table-responsive > .table > tbody > tr > th,
- .table-responsive > .table > tfoot > tr > th,
- .table-responsive > .table > thead > tr > td,
- .table-responsive > .table > tbody > tr > td,
- .table-responsive > .table > tfoot > tr > td {
- white-space: nowrap;
- }
- .table-responsive > .table-bordered {
- border: 0;
- }
- .table-responsive > .table-bordered > thead > tr > th:first-child,
- .table-responsive > .table-bordered > tbody > tr > th:first-child,
- .table-responsive > .table-bordered > tfoot > tr > th:first-child,
- .table-responsive > .table-bordered > thead > tr > td:first-child,
- .table-responsive > .table-bordered > tbody > tr > td:first-child,
- .table-responsive > .table-bordered > tfoot > tr > td:first-child {
- border-left: 0;
- }
- .table-responsive > .table-bordered > thead > tr > th:last-child,
- .table-responsive > .table-bordered > tbody > tr > th:last-child,
- .table-responsive > .table-bordered > tfoot > tr > th:last-child,
- .table-responsive > .table-bordered > thead > tr > td:last-child,
- .table-responsive > .table-bordered > tbody > tr > td:last-child,
- .table-responsive > .table-bordered > tfoot > tr > td:last-child {
- border-right: 0;
- }
- .table-responsive > .table-bordered > thead > tr:last-child > th,
- .table-responsive > .table-bordered > tbody > tr:last-child > th,
- .table-responsive > .table-bordered > tfoot > tr:last-child > th,
- .table-responsive > .table-bordered > thead > tr:last-child > td,
- .table-responsive > .table-bordered > tbody > tr:last-child > td,
- .table-responsive > .table-bordered > tfoot > tr:last-child > td {
- border-bottom: 0;
- }
+.glyphicon-cloud-download:before {
+ content: "\e197";
}
-
-fieldset {
- padding: 0;
- margin: 0;
- border: 0;
+.glyphicon-cloud-upload:before {
+ content: "\e198";
}
-
-legend {
- display: block;
- width: 100%;
- padding: 0;
- margin-bottom: 20px;
- font-size: 21px;
- line-height: inherit;
- color: #333333;
- border: 0;
- border-bottom: 1px solid #e5e5e5;
+.glyphicon-tree-conifer:before {
+ content: "\e199";
}
-
-label {
- display: inline-block;
- margin-bottom: 5px;
- font-weight: bold;
+.glyphicon-tree-deciduous:before {
+ content: "\e200";
}
-
-input[type="search"] {
+* {
-webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
-
-input[type="radio"],
-input[type="checkbox"] {
- margin: 4px 0 0;
- margin-top: 1px \9;
- /* IE8-9 */
-
- line-height: normal;
+*:before,
+*:after {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
-
-input[type="file"] {
- display: block;
+html {
+ font-size: 10px;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
-
-select[multiple],
-select[size] {
- height: auto;
+body {
+ font-family: 'Open Sans', sans-serif;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #333333;
+ background-color: #ffffff;
}
-
-select optgroup {
+input,
+button,
+select,
+textarea {
font-family: inherit;
font-size: inherit;
- font-style: inherit;
+ line-height: inherit;
}
-
-input[type="file"]:focus,
-input[type="radio"]:focus,
-input[type="checkbox"]:focus {
- outline: thin dotted #333;
+a {
+ color: #428bca;
+ text-decoration: none;
+}
+a:hover,
+a:focus {
+ color: #1e3756;
+ text-decoration: underline;
+}
+a:focus {
+ outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
-
-input[type="number"]::-webkit-outer-spin-button,
-input[type="number"]::-webkit-inner-spin-button {
- height: auto;
-}
-
-.form-control:-moz-placeholder {
- color: #999999;
+figure {
+ margin: 0;
}
-
-.form-control::-moz-placeholder {
- color: #999999;
+img {
+ vertical-align: middle;
}
-
-.form-control:-ms-input-placeholder {
- color: #999999;
+.img-responsive,
+.thumbnail > img,
+.thumbnail a > img,
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ display: block;
+ max-width: 100%;
+ height: auto;
}
-
-.form-control::-webkit-input-placeholder {
- color: #999999;
+.img-rounded {
+ border-radius: 6px;
}
-
-.form-control {
- display: block;
- width: 100%;
- height: 34px;
- padding: 6px 12px;
- font-size: 14px;
- line-height: 1.428571429;
- color: #555555;
- vertical-align: middle;
+.img-thumbnail {
+ padding: 4px;
+ line-height: 1.42857143;
background-color: #ffffff;
- border: 1px solid #cccccc;
+ border: 1px solid #dddddd;
border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
- transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
-}
-
-.form-control:focus {
- border-color: #66afe9;
- outline: 0;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
-}
-
-.form-control[disabled],
-.form-control[readonly],
-fieldset[disabled] .form-control {
- cursor: not-allowed;
- background-color: #eeeeee;
-}
-
-textarea.form-control {
+ -webkit-transition: all 0.2s ease-in-out;
+ -o-transition: all 0.2s ease-in-out;
+ transition: all 0.2s ease-in-out;
+ display: inline-block;
+ max-width: 100%;
height: auto;
}
-
-.form-group {
- margin-bottom: 15px;
+.img-circle {
+ border-radius: 50%;
}
-
-.radio,
-.checkbox {
- display: block;
- min-height: 20px;
- padding-left: 20px;
- margin-top: 10px;
- margin-bottom: 10px;
- vertical-align: middle;
+hr {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 0;
+ border-top: 1px solid #eeeeee;
}
-
-.radio label,
-.checkbox label {
- display: inline;
- margin-bottom: 0;
- font-weight: normal;
- cursor: pointer;
+.sr-only {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
}
-
-.radio input[type="radio"],
-.radio-inline input[type="radio"],
-.checkbox input[type="checkbox"],
-.checkbox-inline input[type="checkbox"] {
- float: left;
- margin-left: -20px;
+.sr-only-focusable:active,
+.sr-only-focusable:focus {
+ position: static;
+ width: auto;
+ height: auto;
+ margin: 0;
+ overflow: visible;
+ clip: auto;
}
-
-.radio + .radio,
-.checkbox + .checkbox {
- margin-top: -5px;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+.h1,
+.h2,
+.h3,
+.h4,
+.h5,
+.h6 {
+ font-family: inherit;
+ font-weight: 400;
+ line-height: 1.1;
+ color: inherit;
}
-
-.radio-inline,
-.checkbox-inline {
- display: inline-block;
- padding-left: 20px;
- margin-bottom: 0;
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small,
+.h1 small,
+.h2 small,
+.h3 small,
+.h4 small,
+.h5 small,
+.h6 small,
+h1 .small,
+h2 .small,
+h3 .small,
+h4 .small,
+h5 .small,
+h6 .small,
+.h1 .small,
+.h2 .small,
+.h3 .small,
+.h4 .small,
+.h5 .small,
+.h6 .small {
font-weight: normal;
- vertical-align: middle;
- cursor: pointer;
+ line-height: 1;
+ color: #777777;
}
-
-.radio-inline + .radio-inline,
-.checkbox-inline + .checkbox-inline {
- margin-top: 0;
- margin-left: 10px;
+h1,
+.h1,
+h2,
+.h2,
+h3,
+.h3 {
+ margin-top: 20px;
+ margin-bottom: 10px;
}
-
-input[type="radio"][disabled],
-input[type="checkbox"][disabled],
-.radio[disabled],
-.radio-inline[disabled],
-.checkbox[disabled],
-.checkbox-inline[disabled],
-fieldset[disabled] input[type="radio"],
-fieldset[disabled] input[type="checkbox"],
-fieldset[disabled] .radio,
-fieldset[disabled] .radio-inline,
-fieldset[disabled] .checkbox,
-fieldset[disabled] .checkbox-inline {
- cursor: not-allowed;
+h1 small,
+.h1 small,
+h2 small,
+.h2 small,
+h3 small,
+.h3 small,
+h1 .small,
+.h1 .small,
+h2 .small,
+.h2 .small,
+h3 .small,
+.h3 .small {
+ font-size: 65%;
}
-
-.input-sm {
- height: 30px;
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
+h4,
+.h4,
+h5,
+.h5,
+h6,
+.h6 {
+ margin-top: 10px;
+ margin-bottom: 10px;
}
-
-select.input-sm {
- height: 30px;
- line-height: 30px;
+h4 small,
+.h4 small,
+h5 small,
+.h5 small,
+h6 small,
+.h6 small,
+h4 .small,
+.h4 .small,
+h5 .small,
+.h5 .small,
+h6 .small,
+.h6 .small {
+ font-size: 75%;
}
-
-textarea.input-sm {
- height: auto;
+h1,
+.h1 {
+ font-size: 36px;
}
-
-.input-lg {
- height: 45px;
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
+h2,
+.h2 {
+ font-size: 30px;
}
-
-select.input-lg {
- height: 45px;
- line-height: 45px;
+h3,
+.h3 {
+ font-size: 24px;
}
-
-textarea.input-lg {
- height: auto;
+h4,
+.h4 {
+ font-size: 18px;
}
-
-.has-warning .help-block,
-.has-warning .control-label {
- color: #c09853;
+h5,
+.h5 {
+ font-size: 14px;
}
-
-.has-warning .form-control {
- border-color: #c09853;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+h6,
+.h6 {
+ font-size: 12px;
}
-
-.has-warning .form-control:focus {
- border-color: #a47e3c;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #dbc59e;
+p {
+ margin: 0 0 10px;
}
-
-.has-warning .input-group-addon {
- color: #c09853;
- background-color: #fcf8e3;
- border-color: #c09853;
+.lead {
+ margin-bottom: 20px;
+ font-size: 16px;
+ font-weight: 300;
+ line-height: 1.4;
}
-
-.has-error .help-block,
-.has-error .control-label {
- color: #b94a48;
+@media (min-width: 768px) {
+ .lead {
+ font-size: 21px;
+ }
}
-
-.has-error .form-control {
- border-color: #b94a48;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+small,
+.small {
+ font-size: 85%;
}
-
-.has-error .form-control:focus {
- border-color: #953b39;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #d59392;
+mark,
+.mark {
+ background-color: #fcf8e3;
+ padding: .2em;
}
-
-.has-error .input-group-addon {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #b94a48;
+.text-left {
+ text-align: left;
}
-
-.has-success .help-block,
-.has-success .control-label {
- color: #468847;
+.text-right {
+ text-align: right;
}
-
-.has-success .form-control {
- border-color: #468847;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+.text-center {
+ text-align: center;
}
-
-.has-success .form-control:focus {
- border-color: #356635;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #7aba7b;
+.text-justify {
+ text-align: justify;
}
-
-.has-success .input-group-addon {
- color: #468847;
- background-color: #dff0d8;
- border-color: #468847;
+.text-nowrap {
+ white-space: nowrap;
}
-
-.form-control-static {
- padding-top: 7px;
- margin-bottom: 0;
+.text-lowercase {
+ text-transform: lowercase;
}
-
-.help-block {
- display: block;
- margin-top: 5px;
- margin-bottom: 10px;
- color: #737373;
+.text-uppercase {
+ text-transform: uppercase;
}
-
-@media (min-width: 768px) {
- .form-inline .form-group {
- display: inline-block;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .form-inline .form-control {
- display: inline-block;
- }
- .form-inline .radio,
- .form-inline .checkbox {
- display: inline-block;
- padding-left: 0;
- margin-top: 0;
- margin-bottom: 0;
- }
- .form-inline .radio input[type="radio"],
- .form-inline .checkbox input[type="checkbox"] {
- float: none;
- margin-left: 0;
- }
+.text-capitalize {
+ text-transform: capitalize;
}
-
-.form-horizontal .control-label,
-.form-horizontal .radio,
-.form-horizontal .checkbox,
-.form-horizontal .radio-inline,
-.form-horizontal .checkbox-inline {
- padding-top: 7px;
- margin-top: 0;
- margin-bottom: 0;
+.text-muted {
+ color: #777777;
}
-
-.form-horizontal .form-group {
- margin-right: -15px;
- margin-left: -15px;
+.text-primary {
+ color: #428bca;
}
-
-.form-horizontal .form-group:before,
-.form-horizontal .form-group:after {
- display: table;
- content: " ";
+a.text-primary:hover {
+ color: #3d8b40;
}
-
-.form-horizontal .form-group:after {
- clear: both;
+.text-success {
+ color: #3c763d;
}
-
-.form-horizontal .form-group:before,
-.form-horizontal .form-group:after {
- display: table;
- content: " ";
+a.text-success:hover {
+ color: #2b542c;
}
-
-.form-horizontal .form-group:after {
- clear: both;
+.text-info {
+ color: #31708f;
}
-
-@media (min-width: 768px) {
- .form-horizontal .control-label {
- text-align: right;
- }
+a.text-info:hover {
+ color: #245269;
}
-
-.btn {
- display: inline-block;
- padding: 6px 12px;
- margin-bottom: 0;
- font-size: 14px;
- font-weight: normal;
- line-height: 1.428571429;
- text-align: center;
- white-space: nowrap;
- vertical-align: middle;
- cursor: pointer;
- border: 1px solid transparent;
- border-radius: 4px;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- -o-user-select: none;
- user-select: none;
+.text-warning {
+ color: #8a6d3b;
}
-
-.btn:focus {
- outline: thin dotted #333;
- outline: 5px auto -webkit-focus-ring-color;
- outline-offset: -2px;
+a.text-warning:hover {
+ color: #66512c;
}
-
-.btn:hover,
-.btn:focus {
- color: #333333;
- text-decoration: none;
+.text-danger {
+ color: #a94442;
}
-
-.btn:active,
-.btn.active {
- background-image: none;
- outline: 0;
- -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+a.text-danger:hover {
+ color: #843534;
}
-
-.btn.disabled,
-.btn[disabled],
-fieldset[disabled] .btn {
- pointer-events: none;
- cursor: not-allowed;
- opacity: 0.65;
- filter: alpha(opacity=65);
- -webkit-box-shadow: none;
- box-shadow: none;
+.bg-primary {
+ color: #fff;
+ background-color: #428bca;
}
-
-.btn-default {
- color: #333333;
- background-color: #ffffff;
- border-color: #cccccc;
+a.bg-primary:hover {
+ background-color: #3d8b40;
}
-
-.btn-default:hover,
-.btn-default:focus,
-.btn-default:active,
-.btn-default.active,
-.open .dropdown-toggle.btn-default {
- color: #333333;
- background-color: #ebebeb;
- border-color: #adadad;
+.bg-success {
+ background-color: #dff0d8;
}
-
-.btn-default:active,
-.btn-default.active,
-.open .dropdown-toggle.btn-default {
- background-image: none;
+a.bg-success:hover {
+ background-color: #c1e2b3;
}
-
-.btn-default.disabled,
-.btn-default[disabled],
-fieldset[disabled] .btn-default,
-.btn-default.disabled:hover,
-.btn-default[disabled]:hover,
-fieldset[disabled] .btn-default:hover,
-.btn-default.disabled:focus,
-.btn-default[disabled]:focus,
-fieldset[disabled] .btn-default:focus,
-.btn-default.disabled:active,
-.btn-default[disabled]:active,
-fieldset[disabled] .btn-default:active,
-.btn-default.disabled.active,
-.btn-default[disabled].active,
-fieldset[disabled] .btn-default.active {
- background-color: #ffffff;
- border-color: #cccccc;
+.bg-info {
+ background-color: #d9edf7;
}
-
-.btn-primary {
- color: #ffffff;
- background-color: #428bca;
- border-color: #357ebd;
+a.bg-info:hover {
+ background-color: #afd9ee;
}
-
-.btn-primary:hover,
-.btn-primary:focus,
-.btn-primary:active,
-.btn-primary.active,
-.open .dropdown-toggle.btn-primary {
- color: #ffffff;
- background-color: #3276b1;
- border-color: #285e8e;
+.bg-warning {
+ background-color: #fcf8e3;
}
-
-.btn-primary:active,
-.btn-primary.active,
-.open .dropdown-toggle.btn-primary {
- background-image: none;
+a.bg-warning:hover {
+ background-color: #f7ecb5;
}
-
-.btn-primary.disabled,
-.btn-primary[disabled],
-fieldset[disabled] .btn-primary,
-.btn-primary.disabled:hover,
-.btn-primary[disabled]:hover,
-fieldset[disabled] .btn-primary:hover,
-.btn-primary.disabled:focus,
-.btn-primary[disabled]:focus,
-fieldset[disabled] .btn-primary:focus,
-.btn-primary.disabled:active,
-.btn-primary[disabled]:active,
-fieldset[disabled] .btn-primary:active,
-.btn-primary.disabled.active,
-.btn-primary[disabled].active,
-fieldset[disabled] .btn-primary.active {
- background-color: #428bca;
- border-color: #357ebd;
+.bg-danger {
+ background-color: #f2dede;
}
-
-.btn-warning {
- color: #ffffff;
- background-color: #f0ad4e;
- border-color: #eea236;
+a.bg-danger:hover {
+ background-color: #e4b9b9;
}
-
-.btn-warning:hover,
-.btn-warning:focus,
-.btn-warning:active,
-.btn-warning.active,
-.open .dropdown-toggle.btn-warning {
- color: #ffffff;
- background-color: #ed9c28;
- border-color: #d58512;
+.page-header {
+ padding-bottom: 9px;
+ margin: 40px 0 20px;
+ border-bottom: 1px solid #eeeeee;
}
-
-.btn-warning:active,
-.btn-warning.active,
-.open .dropdown-toggle.btn-warning {
- background-image: none;
+ul,
+ol {
+ margin-top: 0;
+ margin-bottom: 10px;
}
-
-.btn-warning.disabled,
-.btn-warning[disabled],
-fieldset[disabled] .btn-warning,
-.btn-warning.disabled:hover,
-.btn-warning[disabled]:hover,
-fieldset[disabled] .btn-warning:hover,
-.btn-warning.disabled:focus,
-.btn-warning[disabled]:focus,
-fieldset[disabled] .btn-warning:focus,
-.btn-warning.disabled:active,
-.btn-warning[disabled]:active,
-fieldset[disabled] .btn-warning:active,
-.btn-warning.disabled.active,
-.btn-warning[disabled].active,
-fieldset[disabled] .btn-warning.active {
- background-color: #f0ad4e;
- border-color: #eea236;
+ul ul,
+ol ul,
+ul ol,
+ol ol {
+ margin-bottom: 0;
}
-
-.btn-danger {
- color: #ffffff;
- background-color: #d9534f;
- border-color: #d43f3a;
+.list-unstyled {
+ padding-left: 0;
+ list-style: none;
}
-
-.btn-danger:hover,
-.btn-danger:focus,
-.btn-danger:active,
-.btn-danger.active,
-.open .dropdown-toggle.btn-danger {
- color: #ffffff;
- background-color: #d2322d;
- border-color: #ac2925;
+.list-inline {
+ padding-left: 0;
+ list-style: none;
+ margin-left: -5px;
}
-
-.btn-danger:active,
-.btn-danger.active,
-.open .dropdown-toggle.btn-danger {
- background-image: none;
+.list-inline > li {
+ display: inline-block;
+ padding-left: 5px;
+ padding-right: 5px;
}
-
-.btn-danger.disabled,
-.btn-danger[disabled],
-fieldset[disabled] .btn-danger,
-.btn-danger.disabled:hover,
-.btn-danger[disabled]:hover,
-fieldset[disabled] .btn-danger:hover,
-.btn-danger.disabled:focus,
-.btn-danger[disabled]:focus,
-fieldset[disabled] .btn-danger:focus,
-.btn-danger.disabled:active,
-.btn-danger[disabled]:active,
-fieldset[disabled] .btn-danger:active,
-.btn-danger.disabled.active,
-.btn-danger[disabled].active,
-fieldset[disabled] .btn-danger.active {
- background-color: #d9534f;
- border-color: #d43f3a;
+dl {
+ margin-top: 0;
+ margin-bottom: 20px;
}
-
-.btn-success {
- color: #ffffff;
- background-color: #5cb85c;
- border-color: #4cae4c;
+dt,
+dd {
+ line-height: 1.42857143;
}
-
-.btn-success:hover,
-.btn-success:focus,
-.btn-success:active,
-.btn-success.active,
-.open .dropdown-toggle.btn-success {
- color: #ffffff;
- background-color: #47a447;
- border-color: #398439;
+dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 0;
+}
+@media (min-width: 768px) {
+ .dl-horizontal dt {
+ float: left;
+ width: 160px;
+ clear: left;
+ text-align: right;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ .dl-horizontal dd {
+ margin-left: 180px;
+ }
}
-
-.btn-success:active,
-.btn-success.active,
-.open .dropdown-toggle.btn-success {
- background-image: none;
+abbr[title],
+abbr[data-original-title] {
+ cursor: help;
+ border-bottom: 1px dotted #777777;
}
-
-.btn-success.disabled,
-.btn-success[disabled],
-fieldset[disabled] .btn-success,
-.btn-success.disabled:hover,
-.btn-success[disabled]:hover,
-fieldset[disabled] .btn-success:hover,
-.btn-success.disabled:focus,
-.btn-success[disabled]:focus,
-fieldset[disabled] .btn-success:focus,
-.btn-success.disabled:active,
-.btn-success[disabled]:active,
-fieldset[disabled] .btn-success:active,
-.btn-success.disabled.active,
-.btn-success[disabled].active,
-fieldset[disabled] .btn-success.active {
- background-color: #5cb85c;
- border-color: #4cae4c;
+.initialism {
+ font-size: 90%;
+ text-transform: uppercase;
}
-
-.btn-info {
- color: #ffffff;
- background-color: #5bc0de;
- border-color: #46b8da;
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 20px;
+ font-size: 17.5px;
+ border-left: 5px solid #eeeeee;
}
-
-.btn-info:hover,
-.btn-info:focus,
-.btn-info:active,
-.btn-info.active,
-.open .dropdown-toggle.btn-info {
- color: #ffffff;
- background-color: #39b3d7;
- border-color: #269abc;
+blockquote p:last-child,
+blockquote ul:last-child,
+blockquote ol:last-child {
+ margin-bottom: 0;
}
-
-.btn-info:active,
-.btn-info.active,
-.open .dropdown-toggle.btn-info {
- background-image: none;
+blockquote footer,
+blockquote small,
+blockquote .small {
+ display: block;
+ font-size: 80%;
+ line-height: 1.42857143;
+ color: #777777;
}
-
-.btn-info.disabled,
-.btn-info[disabled],
-fieldset[disabled] .btn-info,
-.btn-info.disabled:hover,
-.btn-info[disabled]:hover,
-fieldset[disabled] .btn-info:hover,
-.btn-info.disabled:focus,
-.btn-info[disabled]:focus,
-fieldset[disabled] .btn-info:focus,
-.btn-info.disabled:active,
-.btn-info[disabled]:active,
-fieldset[disabled] .btn-info:active,
-.btn-info.disabled.active,
-.btn-info[disabled].active,
-fieldset[disabled] .btn-info.active {
- background-color: #5bc0de;
- border-color: #46b8da;
+blockquote footer:before,
+blockquote small:before,
+blockquote .small:before {
+ content: '\2014 \00A0';
}
-
-.btn-link {
- font-weight: normal;
- color: #428bca;
- cursor: pointer;
- border-radius: 0;
+.blockquote-reverse,
+blockquote.pull-right {
+ padding-right: 15px;
+ padding-left: 0;
+ border-right: 5px solid #eeeeee;
+ border-left: 0;
+ text-align: right;
}
-
-.btn-link,
-.btn-link:active,
-.btn-link[disabled],
-fieldset[disabled] .btn-link {
- background-color: transparent;
- -webkit-box-shadow: none;
- box-shadow: none;
+.blockquote-reverse footer:before,
+blockquote.pull-right footer:before,
+.blockquote-reverse small:before,
+blockquote.pull-right small:before,
+.blockquote-reverse .small:before,
+blockquote.pull-right .small:before {
+ content: '';
}
-
-.btn-link,
-.btn-link:hover,
-.btn-link:focus,
-.btn-link:active {
- border-color: transparent;
+.blockquote-reverse footer:after,
+blockquote.pull-right footer:after,
+.blockquote-reverse small:after,
+blockquote.pull-right small:after,
+.blockquote-reverse .small:after,
+blockquote.pull-right .small:after {
+ content: '\00A0 \2014';
}
-
-.btn-link:hover,
-.btn-link:focus {
- color: #2a6496;
- text-decoration: underline;
- background-color: transparent;
+address {
+ margin-bottom: 20px;
+ font-style: normal;
+ line-height: 1.42857143;
}
-
-.btn-link[disabled]:hover,
-fieldset[disabled] .btn-link:hover,
-.btn-link[disabled]:focus,
-fieldset[disabled] .btn-link:focus {
- color: #999999;
- text-decoration: none;
+code,
+kbd,
+pre,
+samp {
+ font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
-
-.btn-lg {
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
+code {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #c7254e;
+ background-color: #f9f2f4;
+ border-radius: 4px;
}
-
-.btn-sm,
-.btn-xs {
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
+kbd {
+ padding: 2px 4px;
+ font-size: 90%;
+ color: #ffffff;
+ background-color: #333333;
border-radius: 3px;
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.25);
}
-
-.btn-xs {
- padding: 1px 5px;
+kbd kbd {
+ padding: 0;
+ font-size: 100%;
+ font-weight: bold;
+ box-shadow: none;
}
-
-.btn-block {
+pre {
display: block;
- width: 100%;
- padding-right: 0;
- padding-left: 0;
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ line-height: 1.42857143;
+ word-break: break-all;
+ word-wrap: break-word;
+ color: #333333;
+ background-color: #f5f5f5;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
}
-
-.btn-block + .btn-block {
- margin-top: 5px;
+pre code {
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+ white-space: pre-wrap;
+ background-color: transparent;
+ border-radius: 0;
}
-
-input[type="submit"].btn-block,
-input[type="reset"].btn-block,
-input[type="button"].btn-block {
- width: 100%;
+.pre-scrollable {
+ max-height: 340px;
+ overflow-y: scroll;
}
-
-.fade {
- opacity: 0;
- -webkit-transition: opacity 0.15s linear;
- transition: opacity 0.15s linear;
+.container {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
}
-
-.fade.in {
- opacity: 1;
+.blog-container {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-bottom: 30px;
+ padding-top: 30px;
}
-
-.collapse {
- display: none;
+.blog-mainbanner {
+ text-align: center;
}
-
-.collapse.in {
- display: block;
+@media (min-width: 768px) {
+ .container {
+ width: 750px;
+ }
+ .blog-container {
+ padding-left: 40px;
+ padding-right: 40px;
+ }
+}
+@media (min-width: 992px) {
+ .container {
+ width: 970px;
+ }
}
-.collapsing {
- position: relative;
- height: 0;
- overflow: hidden;
- -webkit-transition: height 0.35s ease;
- transition: height 0.35s ease;
+@media (min-width: 1200px) {
+ .container {
+ width: 1250px;
+ }
+ .blog-container {
+ padding-left: 135px;
+ padding-right: 135px;
+ }
+
}
-@font-face {
- font-family: 'Glyphicons Halflings';
- src: url('../fonts/glyphicons-halflings-regular.eot');
- src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg');
+@media (min-width: 1500px) {
+ .container {
+ width: 1250px;
+ }
+ .blog-container {
+ padding-left: 300px;
+ padding-right: 300px;
+ }
}
-.glyphicon {
+.container-fluid {
+ margin-right: auto;
+ margin-left: auto;
+ padding-left: 15px;
+ padding-right: 15px;
+}
+.row {
+ margin-left: -15px;
+ margin-right: -15px;
+}
+.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
position: relative;
- top: 1px;
- display: inline-block;
- font-family: 'Glyphicons Halflings';
- -webkit-font-smoothing: antialiased;
- font-style: normal;
- font-weight: normal;
- line-height: 1;
+ min-height: 1px;
+ padding-left: 15px;
+ padding-right: 15px;
}
-
-.glyphicon-asterisk:before {
- content: "\2a";
+.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
+ float: left;
}
-
-.glyphicon-plus:before {
- content: "\2b";
+.col-xs-12 {
+ width: 100%;
}
-
-.glyphicon-euro:before {
- content: "\20ac";
+.col-xs-11 {
+ width: 91.66666667%;
+}
+.col-xs-10 {
+ width: 83.33333333%;
}
-
-.glyphicon-minus:before {
- content: "\2212";
+.col-xs-9 {
+ width: 75%;
}
-
-.glyphicon-cloud:before {
- content: "\2601";
+.col-xs-8 {
+ width: 66.66666667%;
}
-
-.glyphicon-envelope:before {
- content: "\2709";
+.col-xs-7 {
+ width: 58.33333333%;
}
-
-.glyphicon-pencil:before {
- content: "\270f";
+.col-xs-6 {
+ width: 50%;
}
-
-.glyphicon-glass:before {
- content: "\e001";
+.col-xs-5 {
+ width: 41.66666667%;
}
-
-.glyphicon-music:before {
- content: "\e002";
+.col-xs-4 {
+ width: 33.33333333%;
}
-
-.glyphicon-search:before {
- content: "\e003";
+.col-xs-3 {
+ width: 25%;
}
-
-.glyphicon-heart:before {
- content: "\e005";
+.col-xs-2 {
+ width: 16.66666667%;
}
-
-.glyphicon-star:before {
- content: "\e006";
+.col-xs-1 {
+ width: 8.33333333%;
}
-
-.glyphicon-star-empty:before {
- content: "\e007";
+.col-xs-pull-12 {
+ right: 100%;
}
-
-.glyphicon-user:before {
- content: "\e008";
+.col-xs-pull-11 {
+ right: 91.66666667%;
}
-
-.glyphicon-film:before {
- content: "\e009";
+.col-xs-pull-10 {
+ right: 83.33333333%;
}
-
-.glyphicon-th-large:before {
- content: "\e010";
+.col-xs-pull-9 {
+ right: 75%;
}
-
-.glyphicon-th:before {
- content: "\e011";
+.col-xs-pull-8 {
+ right: 66.66666667%;
}
-
-.glyphicon-th-list:before {
- content: "\e012";
+.col-xs-pull-7 {
+ right: 58.33333333%;
}
-
-.glyphicon-ok:before {
- content: "\e013";
+.col-xs-pull-6 {
+ right: 50%;
}
-
-.glyphicon-remove:before {
- content: "\e014";
+.col-xs-pull-5 {
+ right: 41.66666667%;
}
-
-.glyphicon-zoom-in:before {
- content: "\e015";
+.col-xs-pull-4 {
+ right: 33.33333333%;
}
-
-.glyphicon-zoom-out:before {
- content: "\e016";
+.col-xs-pull-3 {
+ right: 25%;
}
-
-.glyphicon-off:before {
- content: "\e017";
+.col-xs-pull-2 {
+ right: 16.66666667%;
}
-
-.glyphicon-signal:before {
- content: "\e018";
+.col-xs-pull-1 {
+ right: 8.33333333%;
}
-
-.glyphicon-cog:before {
- content: "\e019";
+.col-xs-pull-0 {
+ right: auto;
}
-
-.glyphicon-trash:before {
- content: "\e020";
+.col-xs-push-12 {
+ left: 100%;
}
-
-.glyphicon-home:before {
- content: "\e021";
+.col-xs-push-11 {
+ left: 91.66666667%;
}
-
-.glyphicon-file:before {
- content: "\e022";
+.col-xs-push-10 {
+ left: 83.33333333%;
}
-
-.glyphicon-time:before {
- content: "\e023";
+.col-xs-push-9 {
+ left: 75%;
}
-
-.glyphicon-road:before {
- content: "\e024";
+.col-xs-push-8 {
+ left: 66.66666667%;
}
-
-.glyphicon-download-alt:before {
- content: "\e025";
+.col-xs-push-7 {
+ left: 58.33333333%;
}
-
-.glyphicon-download:before {
- content: "\e026";
+.col-xs-push-6 {
+ left: 50%;
}
-
-.glyphicon-upload:before {
- content: "\e027";
+.col-xs-push-5 {
+ left: 41.66666667%;
}
-
-.glyphicon-inbox:before {
- content: "\e028";
+.col-xs-push-4 {
+ left: 33.33333333%;
}
-
-.glyphicon-play-circle:before {
- content: "\e029";
+.col-xs-push-3 {
+ left: 25%;
}
-
-.glyphicon-repeat:before {
- content: "\e030";
+.col-xs-push-2 {
+ left: 16.66666667%;
}
-
-.glyphicon-refresh:before {
- content: "\e031";
+.col-xs-push-1 {
+ left: 8.33333333%;
}
-
-.glyphicon-list-alt:before {
- content: "\e032";
+.col-xs-push-0 {
+ left: auto;
}
-
-.glyphicon-flag:before {
- content: "\e034";
+.col-xs-offset-12 {
+ margin-left: 100%;
}
-
-.glyphicon-headphones:before {
- content: "\e035";
+.col-xs-offset-11 {
+ margin-left: 91.66666667%;
}
-
-.glyphicon-volume-off:before {
- content: "\e036";
+.col-xs-offset-10 {
+ margin-left: 83.33333333%;
}
-
-.glyphicon-volume-down:before {
- content: "\e037";
+.col-xs-offset-9 {
+ margin-left: 75%;
}
-
-.glyphicon-volume-up:before {
- content: "\e038";
+.col-xs-offset-8 {
+ margin-left: 66.66666667%;
}
-
-.glyphicon-qrcode:before {
- content: "\e039";
+.col-xs-offset-7 {
+ margin-left: 58.33333333%;
}
-
-.glyphicon-barcode:before {
- content: "\e040";
+.col-xs-offset-6 {
+ margin-left: 50%;
}
-
-.glyphicon-tag:before {
- content: "\e041";
+.col-xs-offset-5 {
+ margin-left: 41.66666667%;
}
-
-.glyphicon-tags:before {
- content: "\e042";
+.col-xs-offset-4 {
+ margin-left: 33.33333333%;
}
-
-.glyphicon-book:before {
- content: "\e043";
+.col-xs-offset-3 {
+ margin-left: 25%;
}
-
-.glyphicon-print:before {
- content: "\e045";
+.col-xs-offset-2 {
+ margin-left: 16.66666667%;
}
-
-.glyphicon-font:before {
- content: "\e047";
+.col-xs-offset-1 {
+ margin-left: 8.33333333%;
}
-
-.glyphicon-bold:before {
- content: "\e048";
+.col-xs-offset-0 {
+ margin-left: 0%;
}
-
-.glyphicon-italic:before {
- content: "\e049";
+@media (min-width: 768px) {
+ .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
+ float: left;
+ }
+ .col-sm-12 {
+ width: 100%;
+ }
+ .col-sm-11 {
+ width: 91.66666667%;
+ }
+ .col-sm-10 {
+ width: 83.33333333%;
+ }
+ .col-sm-9 {
+ width: 75%;
+ }
+ .col-sm-8 {
+ width: 66.66666667%;
+ }
+ .col-sm-7 {
+ width: 58.33333333%;
+ }
+ .col-sm-6 {
+ width: 50%;
+ }
+ .col-sm-5 {
+ width: 41.66666667%;
+ }
+ .col-sm-4 {
+ width: 33.33333333%;
+ }
+ .col-sm-3 {
+ width: 25%;
+ }
+ .col-sm-2 {
+ width: 16.66666667%;
+ }
+ .col-sm-1 {
+ width: 8.33333333%;
+ }
+ .col-sm-pull-12 {
+ right: 100%;
+ }
+ .col-sm-pull-11 {
+ right: 91.66666667%;
+ }
+ .col-sm-pull-10 {
+ right: 83.33333333%;
+ }
+ .col-sm-pull-9 {
+ right: 75%;
+ }
+ .col-sm-pull-8 {
+ right: 66.66666667%;
+ }
+ .col-sm-pull-7 {
+ right: 58.33333333%;
+ }
+ .col-sm-pull-6 {
+ right: 50%;
+ }
+ .col-sm-pull-5 {
+ right: 41.66666667%;
+ }
+ .col-sm-pull-4 {
+ right: 33.33333333%;
+ }
+ .col-sm-pull-3 {
+ right: 25%;
+ }
+ .col-sm-pull-2 {
+ right: 16.66666667%;
+ }
+ .col-sm-pull-1 {
+ right: 8.33333333%;
+ }
+ .col-sm-pull-0 {
+ right: auto;
+ }
+ .col-sm-push-12 {
+ left: 100%;
+ }
+ .col-sm-push-11 {
+ left: 91.66666667%;
+ }
+ .col-sm-push-10 {
+ left: 83.33333333%;
+ }
+ .col-sm-push-9 {
+ left: 75%;
+ }
+ .col-sm-push-8 {
+ left: 66.66666667%;
+ }
+ .col-sm-push-7 {
+ left: 58.33333333%;
+ }
+ .col-sm-push-6 {
+ left: 50%;
+ }
+ .col-sm-push-5 {
+ left: 41.66666667%;
+ }
+ .col-sm-push-4 {
+ left: 33.33333333%;
+ }
+ .col-sm-push-3 {
+ left: 25%;
+ }
+ .col-sm-push-2 {
+ left: 16.66666667%;
+ }
+ .col-sm-push-1 {
+ left: 8.33333333%;
+ }
+ .col-sm-push-0 {
+ left: auto;
+ }
+ .col-sm-offset-12 {
+ margin-left: 100%;
+ }
+ .col-sm-offset-11 {
+ margin-left: 91.66666667%;
+ }
+ .col-sm-offset-10 {
+ margin-left: 83.33333333%;
+ }
+ .col-sm-offset-9 {
+ margin-left: 75%;
+ }
+ .col-sm-offset-8 {
+ margin-left: 66.66666667%;
+ }
+ .col-sm-offset-7 {
+ margin-left: 58.33333333%;
+ }
+ .col-sm-offset-6 {
+ margin-left: 50%;
+ }
+ .col-sm-offset-5 {
+ margin-left: 41.66666667%;
+ }
+ .col-sm-offset-4 {
+ margin-left: 33.33333333%;
+ }
+ .col-sm-offset-3 {
+ margin-left: 25%;
+ }
+ .col-sm-offset-2 {
+ margin-left: 16.66666667%;
+ }
+ .col-sm-offset-1 {
+ margin-left: 8.33333333%;
+ }
+ .col-sm-offset-0 {
+ margin-left: 0%;
+ }
}
-
-.glyphicon-text-height:before {
- content: "\e050";
+@media (min-width: 992px) {
+ .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
+ float: left;
+ }
+ .col-md-12 {
+ width: 100%;
+ }
+ .col-md-11 {
+ width: 91.66666667%;
+ }
+ .col-md-10 {
+ width: 83.33333333%;
+ }
+ .col-md-9 {
+ width: 75%;
+ }
+ .col-md-8 {
+ width: 66.66666667%;
+ }
+ .col-md-7 {
+ width: 58.33333333%;
+ }
+ .col-md-6 {
+ width: 50%;
+ }
+ .col-md-5 {
+ width: 41.66666667%;
+ }
+ .col-md-4 {
+ width: 33.33333333%;
+ }
+ .col-md-3 {
+ width: 25%;
+ }
+ .col-md-2 {
+ width: 16.66666667%;
+ }
+ .col-md-1 {
+ width: 8.33333333%;
+ }
+ .col-md-pull-12 {
+ right: 100%;
+ }
+ .col-md-pull-11 {
+ right: 91.66666667%;
+ }
+ .col-md-pull-10 {
+ right: 83.33333333%;
+ }
+ .col-md-pull-9 {
+ right: 75%;
+ }
+ .col-md-pull-8 {
+ right: 66.66666667%;
+ }
+ .col-md-pull-7 {
+ right: 58.33333333%;
+ }
+ .col-md-pull-6 {
+ right: 50%;
+ }
+ .col-md-pull-5 {
+ right: 41.66666667%;
+ }
+ .col-md-pull-4 {
+ right: 33.33333333%;
+ }
+ .col-md-pull-3 {
+ right: 25%;
+ }
+ .col-md-pull-2 {
+ right: 16.66666667%;
+ }
+ .col-md-pull-1 {
+ right: 8.33333333%;
+ }
+ .col-md-pull-0 {
+ right: auto;
+ }
+ .col-md-push-12 {
+ left: 100%;
+ }
+ .col-md-push-11 {
+ left: 91.66666667%;
+ }
+ .col-md-push-10 {
+ left: 83.33333333%;
+ }
+ .col-md-push-9 {
+ left: 75%;
+ }
+ .col-md-push-8 {
+ left: 66.66666667%;
+ }
+ .col-md-push-7 {
+ left: 58.33333333%;
+ }
+ .col-md-push-6 {
+ left: 50%;
+ }
+ .col-md-push-5 {
+ left: 41.66666667%;
+ }
+ .col-md-push-4 {
+ left: 33.33333333%;
+ }
+ .col-md-push-3 {
+ left: 25%;
+ }
+ .col-md-push-2 {
+ left: 16.66666667%;
+ }
+ .col-md-push-1 {
+ left: 8.33333333%;
+ }
+ .col-md-push-0 {
+ left: auto;
+ }
+ .col-md-offset-12 {
+ margin-left: 100%;
+ }
+ .col-md-offset-11 {
+ margin-left: 91.66666667%;
+ }
+ .col-md-offset-10 {
+ margin-left: 83.33333333%;
+ }
+ .col-md-offset-9 {
+ margin-left: 75%;
+ }
+ .col-md-offset-8 {
+ margin-left: 66.66666667%;
+ }
+ .col-md-offset-7 {
+ margin-left: 58.33333333%;
+ }
+ .col-md-offset-6 {
+ margin-left: 50%;
+ }
+ .col-md-offset-5 {
+ margin-left: 41.66666667%;
+ }
+ .col-md-offset-4 {
+ margin-left: 33.33333333%;
+ }
+ .col-md-offset-3 {
+ margin-left: 25%;
+ }
+ .col-md-offset-2 {
+ margin-left: 16.66666667%;
+ }
+ .col-md-offset-1 {
+ margin-left: 8.33333333%;
+ }
+ .col-md-offset-0 {
+ margin-left: 0%;
+ }
}
-
-.glyphicon-text-width:before {
- content: "\e051";
+@media (min-width: 1200px) {
+ .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
+ float: left;
+ }
+ .col-lg-12 {
+ width: 100%;
+ }
+ .col-lg-11 {
+ width: 91.66666667%;
+ }
+ .col-lg-10 {
+ width: 83.33333333%;
+ }
+ .col-lg-9 {
+ width: 75%;
+ }
+ .col-lg-8 {
+ width: 66.66666667%;
+ }
+ .col-lg-7 {
+ width: 58.33333333%;
+ }
+ .col-lg-6 {
+ width: 50%;
+ }
+ .col-lg-5 {
+ width: 41.66666667%;
+ }
+ .col-lg-4 {
+ width: 33.33333333%;
+ }
+ .col-lg-3 {
+ width: 25%;
+ }
+ .col-lg-2 {
+ width: 16.66666667%;
+ }
+ .col-lg-1 {
+ width: 8.33333333%;
+ }
+ .col-lg-pull-12 {
+ right: 100%;
+ }
+ .col-lg-pull-11 {
+ right: 91.66666667%;
+ }
+ .col-lg-pull-10 {
+ right: 83.33333333%;
+ }
+ .col-lg-pull-9 {
+ right: 75%;
+ }
+ .col-lg-pull-8 {
+ right: 66.66666667%;
+ }
+ .col-lg-pull-7 {
+ right: 58.33333333%;
+ }
+ .col-lg-pull-6 {
+ right: 50%;
+ }
+ .col-lg-pull-5 {
+ right: 41.66666667%;
+ }
+ .col-lg-pull-4 {
+ right: 33.33333333%;
+ }
+ .col-lg-pull-3 {
+ right: 25%;
+ }
+ .col-lg-pull-2 {
+ right: 16.66666667%;
+ }
+ .col-lg-pull-1 {
+ right: 8.33333333%;
+ }
+ .col-lg-pull-0 {
+ right: auto;
+ }
+ .col-lg-push-12 {
+ left: 100%;
+ }
+ .col-lg-push-11 {
+ left: 91.66666667%;
+ }
+ .col-lg-push-10 {
+ left: 83.33333333%;
+ }
+ .col-lg-push-9 {
+ left: 75%;
+ }
+ .col-lg-push-8 {
+ left: 66.66666667%;
+ }
+ .col-lg-push-7 {
+ left: 58.33333333%;
+ }
+ .col-lg-push-6 {
+ left: 50%;
+ }
+ .col-lg-push-5 {
+ left: 41.66666667%;
+ }
+ .col-lg-push-4 {
+ left: 33.33333333%;
+ }
+ .col-lg-push-3 {
+ left: 25%;
+ }
+ .col-lg-push-2 {
+ left: 16.66666667%;
+ }
+ .col-lg-push-1 {
+ left: 8.33333333%;
+ }
+ .col-lg-push-0 {
+ left: auto;
+ }
+ .col-lg-offset-12 {
+ margin-left: 100%;
+ }
+ .col-lg-offset-11 {
+ margin-left: 91.66666667%;
+ }
+ .col-lg-offset-10 {
+ margin-left: 83.33333333%;
+ }
+ .col-lg-offset-9 {
+ margin-left: 75%;
+ }
+ .col-lg-offset-8 {
+ margin-left: 66.66666667%;
+ }
+ .col-lg-offset-7 {
+ margin-left: 58.33333333%;
+ }
+ .col-lg-offset-6 {
+ margin-left: 50%;
+ }
+ .col-lg-offset-5 {
+ margin-left: 41.66666667%;
+ }
+ .col-lg-offset-4 {
+ margin-left: 33.33333333%;
+ }
+ .col-lg-offset-3 {
+ margin-left: 25%;
+ }
+ .col-lg-offset-2 {
+ margin-left: 16.66666667%;
+ }
+ .col-lg-offset-1 {
+ margin-left: 8.33333333%;
+ }
+ .col-lg-offset-0 {
+ margin-left: 0%;
+ }
}
-
-.glyphicon-align-left:before {
- content: "\e052";
+table {
+ background-color: transparent;
}
-
-.glyphicon-align-center:before {
- content: "\e053";
+caption {
+ padding-top: 8px;
+ padding-bottom: 8px;
+ color: #777777;
+ text-align: left;
}
-
-.glyphicon-align-right:before {
- content: "\e054";
+th {
+ text-align: left;
}
-
-.glyphicon-align-justify:before {
- content: "\e055";
+.table {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 20px;
}
-
-.glyphicon-list:before {
- content: "\e056";
+.table > thead > tr > th,
+.table > tbody > tr > th,
+.table > tfoot > tr > th,
+.table > thead > tr > td,
+.table > tbody > tr > td,
+.table > tfoot > tr > td {
+ padding: 8px;
+ line-height: 1.42857143;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
}
-
-.glyphicon-indent-left:before {
- content: "\e057";
+.table > thead > tr > th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dddddd;
}
-
-.glyphicon-indent-right:before {
- content: "\e058";
+.table > caption + thead > tr:first-child > th,
+.table > colgroup + thead > tr:first-child > th,
+.table > thead:first-child > tr:first-child > th,
+.table > caption + thead > tr:first-child > td,
+.table > colgroup + thead > tr:first-child > td,
+.table > thead:first-child > tr:first-child > td {
+ border-top: 0;
}
-
-.glyphicon-facetime-video:before {
- content: "\e059";
+.table > tbody + tbody {
+ border-top: 2px solid #dddddd;
}
-
-.glyphicon-picture:before {
- content: "\e060";
+.table .table {
+ background-color: #ffffff;
}
-
-.glyphicon-map-marker:before {
- content: "\e062";
+.table-condensed > thead > tr > th,
+.table-condensed > tbody > tr > th,
+.table-condensed > tfoot > tr > th,
+.table-condensed > thead > tr > td,
+.table-condensed > tbody > tr > td,
+.table-condensed > tfoot > tr > td {
+ padding: 5px;
}
-
-.glyphicon-adjust:before {
- content: "\e063";
+.table-bordered {
+ border: 1px solid #dddddd;
}
-
-.glyphicon-tint:before {
- content: "\e064";
+.table-bordered > thead > tr > th,
+.table-bordered > tbody > tr > th,
+.table-bordered > tfoot > tr > th,
+.table-bordered > thead > tr > td,
+.table-bordered > tbody > tr > td,
+.table-bordered > tfoot > tr > td {
+ border: 1px solid #dddddd;
}
-
-.glyphicon-edit:before {
- content: "\e065";
+.table-bordered > thead > tr > th,
+.table-bordered > thead > tr > td {
+ border-bottom-width: 2px;
}
-
-.glyphicon-share:before {
- content: "\e066";
+.table-striped > tbody > tr:nth-child(odd) {
+ background-color: #f9f9f9;
}
-
-.glyphicon-check:before {
- content: "\e067";
+.table-hover > tbody > tr:hover {
+ background-color: #f5f5f5;
}
-
-.glyphicon-move:before {
- content: "\e068";
+table col[class*="col-"] {
+ position: static;
+ float: none;
+ display: table-column;
}
-
-.glyphicon-step-backward:before {
- content: "\e069";
+table td[class*="col-"],
+table th[class*="col-"] {
+ position: static;
+ float: none;
+ display: table-cell;
}
-
-.glyphicon-fast-backward:before {
- content: "\e070";
+.table > thead > tr > td.active,
+.table > tbody > tr > td.active,
+.table > tfoot > tr > td.active,
+.table > thead > tr > th.active,
+.table > tbody > tr > th.active,
+.table > tfoot > tr > th.active,
+.table > thead > tr.active > td,
+.table > tbody > tr.active > td,
+.table > tfoot > tr.active > td,
+.table > thead > tr.active > th,
+.table > tbody > tr.active > th,
+.table > tfoot > tr.active > th {
+ background-color: #f5f5f5;
}
-
-.glyphicon-backward:before {
- content: "\e071";
+.table-hover > tbody > tr > td.active:hover,
+.table-hover > tbody > tr > th.active:hover,
+.table-hover > tbody > tr.active:hover > td,
+.table-hover > tbody > tr:hover > .active,
+.table-hover > tbody > tr.active:hover > th {
+ background-color: #e8e8e8;
}
-
-.glyphicon-play:before {
- content: "\e072";
+.table > thead > tr > td.success,
+.table > tbody > tr > td.success,
+.table > tfoot > tr > td.success,
+.table > thead > tr > th.success,
+.table > tbody > tr > th.success,
+.table > tfoot > tr > th.success,
+.table > thead > tr.success > td,
+.table > tbody > tr.success > td,
+.table > tfoot > tr.success > td,
+.table > thead > tr.success > th,
+.table > tbody > tr.success > th,
+.table > tfoot > tr.success > th {
+ background-color: #dff0d8;
}
-
-.glyphicon-pause:before {
- content: "\e073";
+.table-hover > tbody > tr > td.success:hover,
+.table-hover > tbody > tr > th.success:hover,
+.table-hover > tbody > tr.success:hover > td,
+.table-hover > tbody > tr:hover > .success,
+.table-hover > tbody > tr.success:hover > th {
+ background-color: #d0e9c6;
}
-
-.glyphicon-stop:before {
- content: "\e074";
+.table > thead > tr > td.info,
+.table > tbody > tr > td.info,
+.table > tfoot > tr > td.info,
+.table > thead > tr > th.info,
+.table > tbody > tr > th.info,
+.table > tfoot > tr > th.info,
+.table > thead > tr.info > td,
+.table > tbody > tr.info > td,
+.table > tfoot > tr.info > td,
+.table > thead > tr.info > th,
+.table > tbody > tr.info > th,
+.table > tfoot > tr.info > th {
+ background-color: #d9edf7;
}
-
-.glyphicon-forward:before {
- content: "\e075";
+.table-hover > tbody > tr > td.info:hover,
+.table-hover > tbody > tr > th.info:hover,
+.table-hover > tbody > tr.info:hover > td,
+.table-hover > tbody > tr:hover > .info,
+.table-hover > tbody > tr.info:hover > th {
+ background-color: #c4e3f3;
}
-
-.glyphicon-fast-forward:before {
- content: "\e076";
+.table > thead > tr > td.warning,
+.table > tbody > tr > td.warning,
+.table > tfoot > tr > td.warning,
+.table > thead > tr > th.warning,
+.table > tbody > tr > th.warning,
+.table > tfoot > tr > th.warning,
+.table > thead > tr.warning > td,
+.table > tbody > tr.warning > td,
+.table > tfoot > tr.warning > td,
+.table > thead > tr.warning > th,
+.table > tbody > tr.warning > th,
+.table > tfoot > tr.warning > th {
+ background-color: #fcf8e3;
}
-
-.glyphicon-step-forward:before {
- content: "\e077";
+.table-hover > tbody > tr > td.warning:hover,
+.table-hover > tbody > tr > th.warning:hover,
+.table-hover > tbody > tr.warning:hover > td,
+.table-hover > tbody > tr:hover > .warning,
+.table-hover > tbody > tr.warning:hover > th {
+ background-color: #faf2cc;
}
-
-.glyphicon-eject:before {
- content: "\e078";
+.table > thead > tr > td.danger,
+.table > tbody > tr > td.danger,
+.table > tfoot > tr > td.danger,
+.table > thead > tr > th.danger,
+.table > tbody > tr > th.danger,
+.table > tfoot > tr > th.danger,
+.table > thead > tr.danger > td,
+.table > tbody > tr.danger > td,
+.table > tfoot > tr.danger > td,
+.table > thead > tr.danger > th,
+.table > tbody > tr.danger > th,
+.table > tfoot > tr.danger > th {
+ background-color: #f2dede;
}
-
-.glyphicon-chevron-left:before {
- content: "\e079";
+.table-hover > tbody > tr > td.danger:hover,
+.table-hover > tbody > tr > th.danger:hover,
+.table-hover > tbody > tr.danger:hover > td,
+.table-hover > tbody > tr:hover > .danger,
+.table-hover > tbody > tr.danger:hover > th {
+ background-color: #ebcccc;
}
-
-.glyphicon-chevron-right:before {
- content: "\e080";
+.table-responsive {
+ overflow-x: auto;
+ min-height: 0.01%;
}
-
-.glyphicon-plus-sign:before {
- content: "\e081";
+@media screen and (max-width: 767px) {
+ .table-responsive {
+ width: 100%;
+ margin-bottom: 15px;
+ overflow-y: hidden;
+ -ms-overflow-style: -ms-autohiding-scrollbar;
+ border: 1px solid #dddddd;
+ }
+ .table-responsive > .table {
+ margin-bottom: 0;
+ }
+ .table-responsive > .table > thead > tr > th,
+ .table-responsive > .table > tbody > tr > th,
+ .table-responsive > .table > tfoot > tr > th,
+ .table-responsive > .table > thead > tr > td,
+ .table-responsive > .table > tbody > tr > td,
+ .table-responsive > .table > tfoot > tr > td {
+ white-space: nowrap;
+ }
+ .table-responsive > .table-bordered {
+ border: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:first-child,
+ .table-responsive > .table-bordered > tbody > tr > th:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+ .table-responsive > .table-bordered > thead > tr > td:first-child,
+ .table-responsive > .table-bordered > tbody > tr > td:first-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
+ }
+ .table-responsive > .table-bordered > thead > tr > th:last-child,
+ .table-responsive > .table-bordered > tbody > tr > th:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+ .table-responsive > .table-bordered > thead > tr > td:last-child,
+ .table-responsive > .table-bordered > tbody > tr > td:last-child,
+ .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
+ }
+ .table-responsive > .table-bordered > tbody > tr:last-child > th,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > th,
+ .table-responsive > .table-bordered > tbody > tr:last-child > td,
+ .table-responsive > .table-bordered > tfoot > tr:last-child > td {
+ border-bottom: 0;
+ }
}
-
-.glyphicon-minus-sign:before {
- content: "\e082";
+fieldset {
+ padding: 0;
+ margin: 0;
+ border: 0;
+ min-width: 0;
}
-
-.glyphicon-remove-sign:before {
- content: "\e083";
+legend {
+ display: block;
+ width: 100%;
+ padding: 0;
+ margin-bottom: 20px;
+ font-size: 21px;
+ line-height: inherit;
+ color: #333333;
+ border: 0;
+ border-bottom: 1px solid #e5e5e5;
}
-
-.glyphicon-ok-sign:before {
- content: "\e084";
+label {
+ display: inline-block;
+ max-width: 100%;
+ margin-bottom: 5px;
+ font-weight: bold;
}
-
-.glyphicon-question-sign:before {
- content: "\e085";
+input[type="search"] {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
}
-
-.glyphicon-info-sign:before {
- content: "\e086";
+input[type="radio"],
+input[type="checkbox"] {
+ margin: 4px 0 0;
+ margin-top: 1px \9;
+ line-height: normal;
}
-
-.glyphicon-screenshot:before {
- content: "\e087";
+input[type="file"] {
+ display: block;
}
-
-.glyphicon-remove-circle:before {
- content: "\e088";
+input[type="range"] {
+ display: block;
+ width: 100%;
}
-
-.glyphicon-ok-circle:before {
- content: "\e089";
+select[multiple],
+select[size] {
+ height: auto;
}
-
-.glyphicon-ban-circle:before {
- content: "\e090";
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
}
-
-.glyphicon-arrow-left:before {
- content: "\e091";
+output {
+ display: block;
+ padding-top: 7px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555555;
}
-
-.glyphicon-arrow-right:before {
- content: "\e092";
+.form-control {
+ display: block;
+ width: 100%;
+ height: 34px;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ color: #555555;
+ background-color: #ffffff;
+ background-image: none;
+ border: 1px solid #cccccc;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ -webkit-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
}
-
-.glyphicon-arrow-up:before {
- content: "\e093";
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
+ box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
}
-
-.glyphicon-arrow-down:before {
- content: "\e094";
+.form-control::-moz-placeholder {
+ color: #999999;
+ opacity: 1;
}
-
-.glyphicon-share-alt:before {
- content: "\e095";
+.form-control:-ms-input-placeholder {
+ color: #999999;
}
-
-.glyphicon-resize-full:before {
- content: "\e096";
+.form-control::-webkit-input-placeholder {
+ color: #999999;
}
-
-.glyphicon-resize-small:before {
- content: "\e097";
+.form-control[disabled],
+.form-control[readonly],
+fieldset[disabled] .form-control {
+ cursor: not-allowed;
+ background-color: #eeeeee;
+ opacity: 1;
}
-
-.glyphicon-exclamation-sign:before {
- content: "\e101";
+textarea.form-control {
+ height: auto;
}
-
-.glyphicon-gift:before {
- content: "\e102";
+input[type="search"] {
+ -webkit-appearance: none;
}
-
-.glyphicon-leaf:before {
- content: "\e103";
+@media screen and (-webkit-min-device-pixel-ratio: 0) {
+ input[type="date"],
+ input[type="time"],
+ input[type="datetime-local"],
+ input[type="month"] {
+ line-height: 34px;
+ }
+ input[type="date"].input-sm,
+ input[type="time"].input-sm,
+ input[type="datetime-local"].input-sm,
+ input[type="month"].input-sm {
+ line-height: 30px;
+ }
+ input[type="date"].input-lg,
+ input[type="time"].input-lg,
+ input[type="datetime-local"].input-lg,
+ input[type="month"].input-lg {
+ line-height: 46px;
+ }
}
-
-.glyphicon-eye-open:before {
- content: "\e105";
+.form-group {
+ margin-bottom: 15px;
}
-
-.glyphicon-eye-close:before {
- content: "\e106";
+.radio,
+.checkbox {
+ position: relative;
+ display: block;
+ margin-top: 10px;
+ margin-bottom: 10px;
}
-
-.glyphicon-warning-sign:before {
- content: "\e107";
+.radio label,
+.checkbox label {
+ min-height: 20px;
+ padding-left: 20px;
+ margin-bottom: 0;
+ font-weight: normal;
+ cursor: pointer;
}
-
-.glyphicon-plane:before {
- content: "\e108";
+.radio input[type="radio"],
+.radio-inline input[type="radio"],
+.checkbox input[type="checkbox"],
+.checkbox-inline input[type="checkbox"] {
+ position: absolute;
+ margin-left: -20px;
+ margin-top: 4px \9;
}
-
-.glyphicon-random:before {
- content: "\e110";
+.radio + .radio,
+.checkbox + .checkbox {
+ margin-top: -5px;
}
-
-.glyphicon-comment:before {
- content: "\e111";
+.radio-inline,
+.checkbox-inline {
+ display: inline-block;
+ padding-left: 20px;
+ margin-bottom: 0;
+ vertical-align: middle;
+ font-weight: normal;
+ cursor: pointer;
}
-
-.glyphicon-magnet:before {
- content: "\e112";
+.radio-inline + .radio-inline,
+.checkbox-inline + .checkbox-inline {
+ margin-top: 0;
+ margin-left: 10px;
}
-
-.glyphicon-chevron-up:before {
- content: "\e113";
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"].disabled,
+input[type="checkbox"].disabled,
+fieldset[disabled] input[type="radio"],
+fieldset[disabled] input[type="checkbox"] {
+ cursor: not-allowed;
}
-
-.glyphicon-chevron-down:before {
- content: "\e114";
+.radio-inline.disabled,
+.checkbox-inline.disabled,
+fieldset[disabled] .radio-inline,
+fieldset[disabled] .checkbox-inline {
+ cursor: not-allowed;
}
-
-.glyphicon-retweet:before {
- content: "\e115";
+.radio.disabled label,
+.checkbox.disabled label,
+fieldset[disabled] .radio label,
+fieldset[disabled] .checkbox label {
+ cursor: not-allowed;
}
-
-.glyphicon-shopping-cart:before {
- content: "\e116";
+.form-control-static {
+ padding-top: 7px;
+ padding-bottom: 7px;
+ margin-bottom: 0;
}
-
-.glyphicon-folder-close:before {
- content: "\e117";
+.form-control-static.input-lg,
+.form-control-static.input-sm {
+ padding-left: 0;
+ padding-right: 0;
}
-
-.glyphicon-folder-open:before {
- content: "\e118";
+.input-sm,
+.form-group-sm .form-control {
+ height: 30px;
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-
-.glyphicon-resize-vertical:before {
- content: "\e119";
+select.input-sm,
+select.form-group-sm .form-control {
+ height: 30px;
+ line-height: 30px;
}
-
-.glyphicon-resize-horizontal:before {
- content: "\e120";
+textarea.input-sm,
+textarea.form-group-sm .form-control,
+select[multiple].input-sm,
+select[multiple].form-group-sm .form-control {
+ height: auto;
}
-
-.glyphicon-hdd:before {
- content: "\e121";
+.input-lg,
+.form-group-lg .form-control {
+ height: 46px;
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
}
-
-.glyphicon-bullhorn:before {
- content: "\e122";
+select.input-lg,
+select.form-group-lg .form-control {
+ height: 46px;
+ line-height: 46px;
}
-
-.glyphicon-certificate:before {
- content: "\e124";
+textarea.input-lg,
+textarea.form-group-lg .form-control,
+select[multiple].input-lg,
+select[multiple].form-group-lg .form-control {
+ height: auto;
}
-
-.glyphicon-thumbs-up:before {
- content: "\e125";
+.has-feedback {
+ position: relative;
}
-
-.glyphicon-thumbs-down:before {
- content: "\e126";
+.has-feedback .form-control {
+ padding-right: 42.5px;
}
-
-.glyphicon-hand-right:before {
- content: "\e127";
+.form-control-feedback {
+ position: absolute;
+ top: 0;
+ right: 0;
+ z-index: 2;
+ display: block;
+ width: 34px;
+ height: 34px;
+ line-height: 34px;
+ text-align: center;
+ pointer-events: none;
}
-
-.glyphicon-hand-left:before {
- content: "\e128";
+.input-lg + .form-control-feedback {
+ width: 46px;
+ height: 46px;
+ line-height: 46px;
+}
+.input-sm + .form-control-feedback {
+ width: 30px;
+ height: 30px;
+ line-height: 30px;
}
-
-.glyphicon-hand-up:before {
- content: "\e129";
+.has-success .help-block,
+.has-success .control-label,
+.has-success .radio,
+.has-success .checkbox,
+.has-success .radio-inline,
+.has-success .checkbox-inline,
+.has-success.radio label,
+.has-success.checkbox label,
+.has-success.radio-inline label,
+.has-success.checkbox-inline label {
+ color: #3c763d;
}
-
-.glyphicon-hand-down:before {
- content: "\e130";
+.has-success .form-control {
+ border-color: #3c763d;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-
-.glyphicon-circle-arrow-right:before {
- content: "\e131";
+.has-success .form-control:focus {
+ border-color: #2b542c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
}
-
-.glyphicon-circle-arrow-left:before {
- content: "\e132";
+.has-success .input-group-addon {
+ color: #3c763d;
+ border-color: #3c763d;
+ background-color: #dff0d8;
}
-
-.glyphicon-circle-arrow-up:before {
- content: "\e133";
+.has-success .form-control-feedback {
+ color: #3c763d;
}
-
-.glyphicon-circle-arrow-down:before {
- content: "\e134";
+.has-warning .help-block,
+.has-warning .control-label,
+.has-warning .radio,
+.has-warning .checkbox,
+.has-warning .radio-inline,
+.has-warning .checkbox-inline,
+.has-warning.radio label,
+.has-warning.checkbox label,
+.has-warning.radio-inline label,
+.has-warning.checkbox-inline label {
+ color: #8a6d3b;
}
-
-.glyphicon-globe:before {
- content: "\e135";
+.has-warning .form-control {
+ border-color: #8a6d3b;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-
-.glyphicon-tasks:before {
- content: "\e137";
+.has-warning .form-control:focus {
+ border-color: #66512c;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
}
-
-.glyphicon-filter:before {
- content: "\e138";
+.has-warning .input-group-addon {
+ color: #8a6d3b;
+ border-color: #8a6d3b;
+ background-color: #fcf8e3;
}
-
-.glyphicon-fullscreen:before {
- content: "\e140";
+.has-warning .form-control-feedback {
+ color: #8a6d3b;
}
-
-.glyphicon-dashboard:before {
- content: "\e141";
+.has-error .help-block,
+.has-error .control-label,
+.has-error .radio,
+.has-error .checkbox,
+.has-error .radio-inline,
+.has-error .checkbox-inline,
+.has-error.radio label,
+.has-error.checkbox label,
+.has-error.radio-inline label,
+.has-error.checkbox-inline label {
+ color: #a94442;
}
-
-.glyphicon-heart-empty:before {
- content: "\e143";
+.has-error .form-control {
+ border-color: #a94442;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
-
-.glyphicon-link:before {
- content: "\e144";
+.has-error .form-control:focus {
+ border-color: #843534;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
}
-
-.glyphicon-phone:before {
- content: "\e145";
+.has-error .input-group-addon {
+ color: #a94442;
+ border-color: #a94442;
+ background-color: #f2dede;
}
-
-.glyphicon-usd:before {
- content: "\e148";
+.has-error .form-control-feedback {
+ color: #a94442;
}
-
-.glyphicon-gbp:before {
- content: "\e149";
+.has-feedback label ~ .form-control-feedback {
+ top: 25px;
}
-
-.glyphicon-sort:before {
- content: "\e150";
+.has-feedback label.sr-only ~ .form-control-feedback {
+ top: 0;
}
-
-.glyphicon-sort-by-alphabet:before {
- content: "\e151";
+.help-block {
+ display: block;
+ margin-top: 5px;
+ margin-bottom: 10px;
+ color: #737373;
}
-
-.glyphicon-sort-by-alphabet-alt:before {
- content: "\e152";
+@media (min-width: 768px) {
+ .form-inline .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+ .form-inline .form-control-static {
+ display: inline-block;
+ }
+ .form-inline .input-group {
+ display: inline-table;
+ vertical-align: middle;
+ }
+ .form-inline .input-group .input-group-addon,
+ .form-inline .input-group .input-group-btn,
+ .form-inline .input-group .form-control {
+ width: auto;
+ }
+ .form-inline .input-group > .form-control {
+ width: 100%;
+ }
+ .form-inline .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio,
+ .form-inline .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .form-inline .radio label,
+ .form-inline .checkbox label {
+ padding-left: 0;
+ }
+ .form-inline .radio input[type="radio"],
+ .form-inline .checkbox input[type="checkbox"] {
+ position: relative;
+ margin-left: 0;
+ }
+ .form-inline .has-feedback .form-control-feedback {
+ top: 0;
+ }
}
-
-.glyphicon-sort-by-order:before {
- content: "\e153";
+.form-horizontal .radio,
+.form-horizontal .checkbox,
+.form-horizontal .radio-inline,
+.form-horizontal .checkbox-inline {
+ margin-top: 0;
+ margin-bottom: 0;
+ padding-top: 7px;
}
-
-.glyphicon-sort-by-order-alt:before {
- content: "\e154";
+.form-horizontal .radio,
+.form-horizontal .checkbox {
+ min-height: 27px;
}
-
-.glyphicon-sort-by-attributes:before {
- content: "\e155";
+.form-horizontal .form-group {
+ margin-left: -15px;
+ margin-right: -15px;
}
-
-.glyphicon-sort-by-attributes-alt:before {
- content: "\e156";
+@media (min-width: 768px) {
+ .form-horizontal .control-label {
+ text-align: right;
+ margin-bottom: 0;
+ padding-top: 7px;
+ }
}
-
-.glyphicon-unchecked:before {
- content: "\e157";
+.form-horizontal .has-feedback .form-control-feedback {
+ right: 15px;
}
-
-.glyphicon-expand:before {
- content: "\e158";
+@media (min-width: 768px) {
+ .form-horizontal .form-group-lg .control-label {
+ padding-top: 14.3px;
+ }
}
-
-.glyphicon-collapse-down:before {
- content: "\e159";
+@media (min-width: 768px) {
+ .form-horizontal .form-group-sm .control-label {
+ padding-top: 6px;
+ }
}
-
-.glyphicon-collapse-up:before {
- content: "\e160";
+.btn {
+ display: inline-block;
+ margin-bottom: 0;
+ font-weight: normal;
+ text-align: center;
+ vertical-align: middle;
+ touch-action: manipulation;
+ cursor: pointer;
+ background-image: none;
+ border: 1px solid transparent;
+ white-space: nowrap;
+ padding: 6px 12px;
+ font-size: 14px;
+ line-height: 1.42857143;
+ border-radius: 4px;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+.btn:focus,
+.btn:active:focus,
+.btn.active:focus,
+.btn.focus,
+.btn:active.focus,
+.btn.active.focus {
+ outline: thin dotted;
+ outline: 5px auto -webkit-focus-ring-color;
+ outline-offset: -2px;
}
-
-.glyphicon-log-in:before {
- content: "\e161";
+.btn:hover,
+.btn:focus,
+.btn.focus {
+ color: #4d5258;
+ text-decoration: none;
}
-
-.glyphicon-flash:before {
- content: "\e162";
+.btn:active,
+.btn.active {
+ outline: 0;
+ background-image: none;
+ -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
}
-
-.glyphicon-log-out:before {
- content: "\e163";
+.btn.disabled,
+.btn[disabled],
+fieldset[disabled] .btn {
+ cursor: not-allowed;
+ pointer-events: none;
+ opacity: 0.65;
+ filter: alpha(opacity=65);
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-
-.glyphicon-new-window:before {
- content: "\e164";
+.btn-default {
+ color: #4d5258;
+ background-color: #eeeeee;
+ border-color: #b7b7b7;
}
-
-.glyphicon-record:before {
- content: "\e165";
+.btn-default:hover,
+.btn-default:focus,
+.btn-default.focus,
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+ color: #4d5258;
+ background-color: #d5d5d5;
+ border-color: #989898;
}
-
-.glyphicon-save:before {
- content: "\e166";
+.btn-default:active,
+.btn-default.active,
+.open > .dropdown-toggle.btn-default {
+ background-image: none;
}
-
-.glyphicon-open:before {
- content: "\e167";
+.btn-default.disabled,
+.btn-default[disabled],
+fieldset[disabled] .btn-default,
+.btn-default.disabled:hover,
+.btn-default[disabled]:hover,
+fieldset[disabled] .btn-default:hover,
+.btn-default.disabled:focus,
+.btn-default[disabled]:focus,
+fieldset[disabled] .btn-default:focus,
+.btn-default.disabled.focus,
+.btn-default[disabled].focus,
+fieldset[disabled] .btn-default.focus,
+.btn-default.disabled:active,
+.btn-default[disabled]:active,
+fieldset[disabled] .btn-default:active,
+.btn-default.disabled.active,
+.btn-default[disabled].active,
+fieldset[disabled] .btn-default.active {
+ background-color: #eeeeee;
+ border-color: #b7b7b7;
}
-
-.glyphicon-saved:before {
- content: "\e168";
+.btn-default .badge {
+ color: #eeeeee;
+ background-color: #4d5258;
}
-
-.glyphicon-import:before {
- content: "\e169";
+.btn-primary {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #449d48;
}
-
-.glyphicon-export:before {
- content: "\e170";
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary.focus,
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+ color: #ffffff;
+ background-color: #3d8b40;
+ border-color: #327334;
}
-
-.glyphicon-send:before {
- content: "\e171";
+.btn-primary:active,
+.btn-primary.active,
+.open > .dropdown-toggle.btn-primary {
+ background-image: none;
}
-
-.glyphicon-floppy-disk:before {
- content: "\e172";
+.btn-primary.disabled,
+.btn-primary[disabled],
+fieldset[disabled] .btn-primary,
+.btn-primary.disabled:hover,
+.btn-primary[disabled]:hover,
+fieldset[disabled] .btn-primary:hover,
+.btn-primary.disabled:focus,
+.btn-primary[disabled]:focus,
+fieldset[disabled] .btn-primary:focus,
+.btn-primary.disabled.focus,
+.btn-primary[disabled].focus,
+fieldset[disabled] .btn-primary.focus,
+.btn-primary.disabled:active,
+.btn-primary[disabled]:active,
+fieldset[disabled] .btn-primary:active,
+.btn-primary.disabled.active,
+.btn-primary[disabled].active,
+fieldset[disabled] .btn-primary.active {
+ background-color: #428bca;
+ border-color: #449d48;
}
-
-.glyphicon-floppy-saved:before {
- content: "\e173";
+.btn-primary .badge {
+ color: #428bca;
+ background-color: #ffffff;
}
-
-.glyphicon-floppy-remove:before {
- content: "\e174";
+.btn-success {
+ color: #ffffff;
+ background-color: #5cb85c;
+ border-color: #4cae4c;
}
-
-.glyphicon-floppy-save:before {
- content: "\e175";
+.btn-success:hover,
+.btn-success:focus,
+.btn-success.focus,
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+ color: #ffffff;
+ background-color: #449d44;
+ border-color: #398439;
}
-
-.glyphicon-floppy-open:before {
- content: "\e176";
+.btn-success:active,
+.btn-success.active,
+.open > .dropdown-toggle.btn-success {
+ background-image: none;
}
-
-.glyphicon-credit-card:before {
- content: "\e177";
+.btn-success.disabled,
+.btn-success[disabled],
+fieldset[disabled] .btn-success,
+.btn-success.disabled:hover,
+.btn-success[disabled]:hover,
+fieldset[disabled] .btn-success:hover,
+.btn-success.disabled:focus,
+.btn-success[disabled]:focus,
+fieldset[disabled] .btn-success:focus,
+.btn-success.disabled.focus,
+.btn-success[disabled].focus,
+fieldset[disabled] .btn-success.focus,
+.btn-success.disabled:active,
+.btn-success[disabled]:active,
+fieldset[disabled] .btn-success:active,
+.btn-success.disabled.active,
+.btn-success[disabled].active,
+fieldset[disabled] .btn-success.active {
+ background-color: #5cb85c;
+ border-color: #4cae4c;
}
-
-.glyphicon-transfer:before {
- content: "\e178";
+.btn-success .badge {
+ color: #5cb85c;
+ background-color: #ffffff;
}
-
-.glyphicon-cutlery:before {
- content: "\e179";
+.btn-info {
+ color: #ffffff;
+ background-color: #5bc0de;
+ border-color: #46b8da;
}
-
-.glyphicon-header:before {
- content: "\e180";
+.btn-info:hover,
+.btn-info:focus,
+.btn-info.focus,
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+ color: #ffffff;
+ background-color: #31b0d5;
+ border-color: #269abc;
}
-
-.glyphicon-compressed:before {
- content: "\e181";
+.btn-info:active,
+.btn-info.active,
+.open > .dropdown-toggle.btn-info {
+ background-image: none;
}
-
-.glyphicon-earphone:before {
- content: "\e182";
+.btn-info.disabled,
+.btn-info[disabled],
+fieldset[disabled] .btn-info,
+.btn-info.disabled:hover,
+.btn-info[disabled]:hover,
+fieldset[disabled] .btn-info:hover,
+.btn-info.disabled:focus,
+.btn-info[disabled]:focus,
+fieldset[disabled] .btn-info:focus,
+.btn-info.disabled.focus,
+.btn-info[disabled].focus,
+fieldset[disabled] .btn-info.focus,
+.btn-info.disabled:active,
+.btn-info[disabled]:active,
+fieldset[disabled] .btn-info:active,
+.btn-info.disabled.active,
+.btn-info[disabled].active,
+fieldset[disabled] .btn-info.active {
+ background-color: #5bc0de;
+ border-color: #46b8da;
}
-
-.glyphicon-phone-alt:before {
- content: "\e183";
+.btn-info .badge {
+ color: #5bc0de;
+ background-color: #ffffff;
}
-
-.glyphicon-tower:before {
- content: "\e184";
+.btn-warning {
+ color: #ffffff;
+ background-color: #f0ad4e;
+ border-color: #eea236;
}
-
-.glyphicon-stats:before {
- content: "\e185";
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning.focus,
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+ color: #ffffff;
+ background-color: #ec971f;
+ border-color: #d58512;
}
-
-.glyphicon-sd-video:before {
- content: "\e186";
+.btn-warning:active,
+.btn-warning.active,
+.open > .dropdown-toggle.btn-warning {
+ background-image: none;
}
-
-.glyphicon-hd-video:before {
- content: "\e187";
+.btn-warning.disabled,
+.btn-warning[disabled],
+fieldset[disabled] .btn-warning,
+.btn-warning.disabled:hover,
+.btn-warning[disabled]:hover,
+fieldset[disabled] .btn-warning:hover,
+.btn-warning.disabled:focus,
+.btn-warning[disabled]:focus,
+fieldset[disabled] .btn-warning:focus,
+.btn-warning.disabled.focus,
+.btn-warning[disabled].focus,
+fieldset[disabled] .btn-warning.focus,
+.btn-warning.disabled:active,
+.btn-warning[disabled]:active,
+fieldset[disabled] .btn-warning:active,
+.btn-warning.disabled.active,
+.btn-warning[disabled].active,
+fieldset[disabled] .btn-warning.active {
+ background-color: #f0ad4e;
+ border-color: #eea236;
}
-
-.glyphicon-subtitles:before {
- content: "\e188";
+.btn-warning .badge {
+ color: #f0ad4e;
+ background-color: #ffffff;
}
-
-.glyphicon-sound-stereo:before {
- content: "\e189";
+.btn-danger {
+ color: #ffffff;
+ background-color: #d9534f;
+ border-color: #d43f3a;
}
-
-.glyphicon-sound-dolby:before {
- content: "\e190";
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger.focus,
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+ color: #ffffff;
+ background-color: #c9302c;
+ border-color: #ac2925;
}
-
-.glyphicon-sound-5-1:before {
- content: "\e191";
+.btn-danger:active,
+.btn-danger.active,
+.open > .dropdown-toggle.btn-danger {
+ background-image: none;
}
-
-.glyphicon-sound-6-1:before {
- content: "\e192";
+.btn-danger.disabled,
+.btn-danger[disabled],
+fieldset[disabled] .btn-danger,
+.btn-danger.disabled:hover,
+.btn-danger[disabled]:hover,
+fieldset[disabled] .btn-danger:hover,
+.btn-danger.disabled:focus,
+.btn-danger[disabled]:focus,
+fieldset[disabled] .btn-danger:focus,
+.btn-danger.disabled.focus,
+.btn-danger[disabled].focus,
+fieldset[disabled] .btn-danger.focus,
+.btn-danger.disabled:active,
+.btn-danger[disabled]:active,
+fieldset[disabled] .btn-danger:active,
+.btn-danger.disabled.active,
+.btn-danger[disabled].active,
+fieldset[disabled] .btn-danger.active {
+ background-color: #d9534f;
+ border-color: #d43f3a;
}
-
-.glyphicon-sound-7-1:before {
- content: "\e193";
+.btn-danger .badge {
+ color: #d9534f;
+ background-color: #ffffff;
}
-
-.glyphicon-copyright-mark:before {
- content: "\e194";
+.btn-link {
+ color: #428bca;
+ font-weight: normal;
+ border-radius: 0;
}
-
-.glyphicon-registration-mark:before {
- content: "\e195";
+.btn-link,
+.btn-link:active,
+.btn-link.active,
+.btn-link[disabled],
+fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-
-.glyphicon-cloud-download:before {
- content: "\e197";
+.btn-link,
+.btn-link:hover,
+.btn-link:focus,
+.btn-link:active {
+ border-color: transparent;
}
-
-.glyphicon-cloud-upload:before {
- content: "\e198";
+.btn-link:hover,
+.btn-link:focus {
+ color: #1e3756;
+ text-decoration: underline;
+ background-color: transparent;
+}
+.btn-link[disabled]:hover,
+fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus,
+fieldset[disabled] .btn-link:focus {
+ color: #777777;
+ text-decoration: none;
}
-
-.glyphicon-tree-conifer:before {
- content: "\e199";
+.btn-lg,
+.btn-group-lg > .btn {
+ padding: 10px 16px;
+ font-size: 18px;
+ line-height: 1.33;
+ border-radius: 6px;
}
-
-.glyphicon-tree-deciduous:before {
- content: "\e200";
+.btn-sm,
+.btn-group-sm > .btn {
+ padding: 5px 10px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-
-.glyphicon-briefcase:before {
- content: "\1f4bc";
+.btn-xs,
+.btn-group-xs > .btn {
+ padding: 1px 5px;
+ font-size: 12px;
+ line-height: 1.5;
+ border-radius: 3px;
}
-
-.glyphicon-calendar:before {
- content: "\1f4c5";
+.btn-block {
+ display: block;
+ width: 100%;
}
-
-.glyphicon-pushpin:before {
- content: "\1f4cc";
+.btn-block + .btn-block {
+ margin-top: 5px;
}
-
-.glyphicon-paperclip:before {
- content: "\1f4ce";
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+ width: 100%;
}
-
-.glyphicon-camera:before {
- content: "\1f4f7";
+.fade {
+ opacity: 0;
+ -webkit-transition: opacity 0.15s linear;
+ -o-transition: opacity 0.15s linear;
+ transition: opacity 0.15s linear;
}
-
-.glyphicon-lock:before {
- content: "\1f512";
+.fade.in {
+ opacity: 1;
}
-
-.glyphicon-bell:before {
- content: "\1f514";
+.collapse {
+ display: none;
+ visibility: hidden;
}
-
-.glyphicon-bookmark:before {
- content: "\1f516";
+.collapse.in {
+ display: block;
+ visibility: visible;
}
-
-.glyphicon-fire:before {
- content: "\1f525";
+tr.collapse.in {
+ display: table-row;
}
-
-.glyphicon-wrench:before {
- content: "\1f527";
+tbody.collapse.in {
+ display: table-row-group;
+}
+.collapsing {
+ position: relative;
+ height: 0;
+ overflow: hidden;
+ -webkit-transition-property: height, visibility;
+ transition-property: height, visibility;
+ -webkit-transition-duration: 0.35s;
+ transition-duration: 0.35s;
+ -webkit-transition-timing-function: ease;
+ transition-timing-function: ease;
}
-
.caret {
display: inline-block;
width: 0;
height: 0;
margin-left: 2px;
vertical-align: middle;
- border-top: 4px solid #000000;
+ border-top: 4px solid;
border-right: 4px solid transparent;
- border-bottom: 0 dotted;
border-left: 4px solid transparent;
- content: "";
}
-
.dropdown {
position: relative;
}
-
.dropdown-toggle:focus {
outline: 0;
}
-
.dropdown-menu {
position: absolute;
top: 100%;
@@ -3231,143 +3215,160 @@ input[type="button"].btn-block {
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
- font-size: 14px;
list-style: none;
+ font-size: 14px;
+ text-align: left;
background-color: #ffffff;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
- box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
background-clip: padding-box;
}
-
.dropdown-menu.pull-right {
right: 0;
left: auto;
}
-
.dropdown-menu .divider {
height: 1px;
margin: 9px 0;
overflow: hidden;
background-color: #e5e5e5;
}
-
.dropdown-menu > li > a {
display: block;
padding: 3px 20px;
clear: both;
font-weight: normal;
- line-height: 1.428571429;
- color: #333333;
+ line-height: 1.42857143;
+ color: #999999;
white-space: nowrap;
}
-
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
- color: #ffffff;
text-decoration: none;
- background-color: #428bca;
+ color: #428bca;
+ background-color: #eeeeee;
}
-
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
color: #ffffff;
text-decoration: none;
- background-color: #428bca;
outline: 0;
+ background-color: #428bca;
}
-
.dropdown-menu > .disabled > a,
.dropdown-menu > .disabled > a:hover,
.dropdown-menu > .disabled > a:focus {
- color: #999999;
+ color: #777777;
}
-
.dropdown-menu > .disabled > a:hover,
.dropdown-menu > .disabled > a:focus {
text-decoration: none;
- cursor: not-allowed;
background-color: transparent;
background-image: none;
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
+ cursor: not-allowed;
}
-
.open > .dropdown-menu {
display: block;
}
-
.open > a {
outline: 0;
}
-
+.dropdown-menu-right {
+ left: auto;
+ right: 0;
+}
+.dropdown-menu-left {
+ left: 0;
+ right: auto;
+}
.dropdown-header {
display: block;
padding: 3px 20px;
font-size: 12px;
- line-height: 1.428571429;
- color: #999999;
+ line-height: 1.42857143;
+ color: #777777;
+ white-space: nowrap;
}
-
.dropdown-backdrop {
position: fixed;
- top: 0;
+ left: 0;
right: 0;
bottom: 0;
- left: 0;
+ top: 0;
z-index: 990;
}
-
.pull-right > .dropdown-menu {
right: 0;
left: auto;
}
-
.dropup .caret,
.navbar-fixed-bottom .dropdown .caret {
- border-top: 0 dotted;
- border-bottom: 4px solid #000000;
+ border-top: 0;
+ border-bottom: 4px solid;
content: "";
}
-
.dropup .dropdown-menu,
.navbar-fixed-bottom .dropdown .dropdown-menu {
top: auto;
bottom: 100%;
margin-bottom: 1px;
}
-
@media (min-width: 768px) {
.navbar-right .dropdown-menu {
- right: 0;
left: auto;
+ right: 0;
+ }
+ .navbar-right .dropdown-menu-left {
+ left: 0;
+ right: auto;
}
}
-.btn-default .caret {
- border-top-color: #333333;
+.dropdown-submenu {
+ position:relative;
}
-
-.btn-primary .caret,
-.btn-success .caret,
-.btn-warning .caret,
-.btn-danger .caret,
-.btn-info .caret {
- border-top-color: #fff;
+.dropdown-submenu>.dropdown-menu {
+ top:0;
+ left:100%;
+ margin-top:-6px;
+ margin-left:-1px;
+ -webkit-border-radius:0 6px 6px 6px;
+ -moz-border-radius:0 6px 6px 6px;
+ border-radius:0 6px 6px 6px;
}
-
-.dropup .btn-default .caret {
- border-bottom-color: #333333;
+.dropdown-submenu:hover>.dropdown-menu {
+ display:block;
}
-
-.dropup .btn-primary .caret,
-.dropup .btn-success .caret,
-.dropup .btn-warning .caret,
-.dropup .btn-danger .caret,
-.dropup .btn-info .caret {
- border-bottom-color: #fff;
+.dropdown-submenu>a:after {
+ display:block;
+ content:" ";
+ float:right;
+ width:0;
+ height:0;
+ border-color:transparent;
+ border-style:solid;
+ border-width:5px 0 5px 5px;
+ border-left-color:#cccccc;
+ margin-top:5px;
+ margin-right:-10px;
+}
+.dropdown-submenu:hover>a:after {
+ border-left-color:#ffffff;
+}
+.dropdown-submenu.pull-left {
+ float:none;
+}
+.dropdown-submenu.pull-left>.dropdown-menu {
+ left:-100%;
+ margin-left:10px;
+ -webkit-border-radius:6px 0 6px 6px;
+ -moz-border-radius:6px 0 6px 6px;
+ border-radius:6px 0 6px 6px;
}
.btn-group,
@@ -3376,13 +3377,11 @@ input[type="button"].btn-block {
display: inline-block;
vertical-align: middle;
}
-
.btn-group > .btn,
.btn-group-vertical > .btn {
position: relative;
float: left;
}
-
.btn-group > .btn:hover,
.btn-group-vertical > .btn:hover,
.btn-group > .btn:focus,
@@ -3393,175 +3392,95 @@ input[type="button"].btn-block {
.btn-group-vertical > .btn.active {
z-index: 2;
}
-
-.btn-group > .btn:focus,
-.btn-group-vertical > .btn:focus {
- outline: none;
-}
-
.btn-group .btn + .btn,
.btn-group .btn + .btn-group,
.btn-group .btn-group + .btn,
.btn-group .btn-group + .btn-group {
margin-left: -1px;
}
-
-.btn-toolbar:before,
-.btn-toolbar:after {
- display: table;
- content: " ";
-}
-
-.btn-toolbar:after {
- clear: both;
-}
-
-.btn-toolbar:before,
-.btn-toolbar:after {
- display: table;
- content: " ";
-}
-
-.btn-toolbar:after {
- clear: both;
+.btn-toolbar {
+ margin-left: -5px;
}
-
-.btn-toolbar .btn-group {
+.btn-toolbar .btn-group,
+.btn-toolbar .input-group {
float: left;
}
-
-.btn-toolbar > .btn + .btn,
-.btn-toolbar > .btn-group + .btn,
-.btn-toolbar > .btn + .btn-group,
-.btn-toolbar > .btn-group + .btn-group {
+.btn-toolbar > .btn,
+.btn-toolbar > .btn-group,
+.btn-toolbar > .input-group {
margin-left: 5px;
}
-
.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) {
border-radius: 0;
}
-
.btn-group > .btn:first-child {
margin-left: 0;
}
-
.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) {
- border-top-right-radius: 0;
border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
}
-
.btn-group > .btn:last-child:not(:first-child),
.btn-group > .dropdown-toggle:not(:first-child) {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
-
.btn-group > .btn-group {
float: left;
}
-
.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
-
.btn-group > .btn-group:first-child > .btn:last-child,
.btn-group > .btn-group:first-child > .dropdown-toggle {
- border-top-right-radius: 0;
border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
}
-
.btn-group > .btn-group:last-child > .btn:first-child {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
-
.btn-group .dropdown-toggle:active,
.btn-group.open .dropdown-toggle {
outline: 0;
}
-
-.btn-group-xs > .btn {
- padding: 5px 10px;
- padding: 1px 5px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-
-.btn-group-sm > .btn {
- padding: 5px 10px;
- font-size: 12px;
- line-height: 1.5;
- border-radius: 3px;
-}
-
-.btn-group-lg > .btn {
- padding: 10px 16px;
- font-size: 18px;
- line-height: 1.33;
- border-radius: 6px;
-}
-
.btn-group > .btn + .dropdown-toggle {
- padding-right: 8px;
padding-left: 8px;
+ padding-right: 8px;
}
-
.btn-group > .btn-lg + .dropdown-toggle {
- padding-right: 12px;
padding-left: 12px;
+ padding-right: 12px;
}
-
.btn-group.open .dropdown-toggle {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
- box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+ box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
+}
+.btn-group.open .dropdown-toggle.btn-link {
+ -webkit-box-shadow: none;
+ box-shadow: none;
}
-
.btn .caret {
margin-left: 0;
}
-
.btn-lg .caret {
border-width: 5px 5px 0;
border-bottom-width: 0;
}
-
.dropup .btn-lg .caret {
border-width: 0 5px 5px;
}
-
.btn-group-vertical > .btn,
-.btn-group-vertical > .btn-group {
+.btn-group-vertical > .btn-group,
+.btn-group-vertical > .btn-group > .btn {
display: block;
float: none;
width: 100%;
max-width: 100%;
}
-
-.btn-group-vertical > .btn-group:before,
-.btn-group-vertical > .btn-group:after {
- display: table;
- content: " ";
-}
-
-.btn-group-vertical > .btn-group:after {
- clear: both;
-}
-
-.btn-group-vertical > .btn-group:before,
-.btn-group-vertical > .btn-group:after {
- display: table;
- content: " ";
-}
-
-.btn-group-vertical > .btn-group:after {
- clear: both;
-}
-
.btn-group-vertical > .btn-group > .btn {
float: none;
}
-
.btn-group-vertical > .btn + .btn,
.btn-group-vertical > .btn + .btn-group,
.btn-group-vertical > .btn-group + .btn,
@@ -3569,96 +3488,97 @@ input[type="button"].btn-block {
margin-top: -1px;
margin-left: 0;
}
-
.btn-group-vertical > .btn:not(:first-child):not(:last-child) {
border-radius: 0;
}
-
.btn-group-vertical > .btn:first-child:not(:last-child) {
border-top-right-radius: 4px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
-
.btn-group-vertical > .btn:last-child:not(:first-child) {
- border-top-right-radius: 0;
border-bottom-left-radius: 4px;
+ border-top-right-radius: 0;
border-top-left-radius: 0;
}
-
.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn {
border-radius: 0;
}
-
-.btn-group-vertical > .btn-group:first-child > .btn:last-child,
-.btn-group-vertical > .btn-group:first-child > .dropdown-toggle {
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child,
+.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle {
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
-
-.btn-group-vertical > .btn-group:last-child > .btn:first-child {
+.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child {
border-top-right-radius: 0;
border-top-left-radius: 0;
}
-
.btn-group-justified {
display: table;
width: 100%;
- border-collapse: separate;
table-layout: fixed;
+ border-collapse: separate;
}
-
-.btn-group-justified .btn {
- display: table-cell;
+.btn-group-justified > .btn,
+.btn-group-justified > .btn-group {
float: none;
+ display: table-cell;
width: 1%;
}
-
-[data-toggle="buttons"] > .btn > input[type="radio"],
-[data-toggle="buttons"] > .btn > input[type="checkbox"] {
- display: none;
+.btn-group-justified > .btn-group .btn {
+ width: 100%;
+}
+.btn-group-justified > .btn-group .dropdown-menu {
+ left: auto;
+}
+[data-toggle="buttons"] > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="radio"],
+[data-toggle="buttons"] > .btn input[type="checkbox"],
+[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] {
+ position: absolute;
+ clip: rect(0, 0, 0, 0);
+ pointer-events: none;
}
-
.input-group {
position: relative;
display: table;
border-collapse: separate;
}
-
-.input-group.col {
+.input-group[class*="col-"] {
float: none;
- padding-right: 0;
padding-left: 0;
+ padding-right: 0;
}
-
.input-group .form-control {
+ position: relative;
+ z-index: 2;
+ float: left;
width: 100%;
margin-bottom: 0;
}
-
.input-group-lg > .form-control,
.input-group-lg > .input-group-addon,
.input-group-lg > .input-group-btn > .btn {
- height: 45px;
+ height: 46px;
padding: 10px 16px;
font-size: 18px;
line-height: 1.33;
border-radius: 6px;
}
-
select.input-group-lg > .form-control,
select.input-group-lg > .input-group-addon,
select.input-group-lg > .input-group-btn > .btn {
- height: 45px;
- line-height: 45px;
+ height: 46px;
+ line-height: 46px;
}
-
textarea.input-group-lg > .form-control,
textarea.input-group-lg > .input-group-addon,
-textarea.input-group-lg > .input-group-btn > .btn {
+textarea.input-group-lg > .input-group-btn > .btn,
+select[multiple].input-group-lg > .form-control,
+select[multiple].input-group-lg > .input-group-addon,
+select[multiple].input-group-lg > .input-group-btn > .btn {
height: auto;
}
-
.input-group-sm > .form-control,
.input-group-sm > .input-group-addon,
.input-group-sm > .input-group-btn > .btn {
@@ -3668,3138 +3588,3667 @@ textarea.input-group-lg > .input-group-btn > .btn {
line-height: 1.5;
border-radius: 3px;
}
-
select.input-group-sm > .form-control,
select.input-group-sm > .input-group-addon,
select.input-group-sm > .input-group-btn > .btn {
height: 30px;
line-height: 30px;
}
-
textarea.input-group-sm > .form-control,
textarea.input-group-sm > .input-group-addon,
-textarea.input-group-sm > .input-group-btn > .btn {
+textarea.input-group-sm > .input-group-btn > .btn,
+select[multiple].input-group-sm > .form-control,
+select[multiple].input-group-sm > .input-group-addon,
+select[multiple].input-group-sm > .input-group-btn > .btn {
height: auto;
}
-
.input-group-addon,
.input-group-btn,
.input-group .form-control {
display: table-cell;
}
-
.input-group-addon:not(:first-child):not(:last-child),
.input-group-btn:not(:first-child):not(:last-child),
.input-group .form-control:not(:first-child):not(:last-child) {
border-radius: 0;
}
-
.input-group-addon,
.input-group-btn {
width: 1%;
white-space: nowrap;
vertical-align: middle;
}
-
.input-group-addon {
padding: 6px 12px;
font-size: 14px;
font-weight: normal;
line-height: 1;
+ color: #555555;
text-align: center;
background-color: #eeeeee;
border: 1px solid #cccccc;
border-radius: 4px;
}
-
.input-group-addon.input-sm {
padding: 5px 10px;
font-size: 12px;
border-radius: 3px;
}
-
.input-group-addon.input-lg {
padding: 10px 16px;
font-size: 18px;
border-radius: 6px;
}
-
.input-group-addon input[type="radio"],
.input-group-addon input[type="checkbox"] {
margin-top: 0;
}
-
.input-group .form-control:first-child,
.input-group-addon:first-child,
.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group > .btn,
.input-group-btn:first-child > .dropdown-toggle,
-.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) {
- border-top-right-radius: 0;
+.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle),
+.input-group-btn:last-child > .btn-group:not(:last-child) > .btn {
border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
}
-
.input-group-addon:first-child {
border-right: 0;
}
-
.input-group .form-control:last-child,
.input-group-addon:last-child,
.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group > .btn,
.input-group-btn:last-child > .dropdown-toggle,
-.input-group-btn:first-child > .btn:not(:first-child) {
+.input-group-btn:first-child > .btn:not(:first-child),
+.input-group-btn:first-child > .btn-group:not(:first-child) > .btn {
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
-
-.input-group-addon:last-child {
- border-left: 0;
+.input-group-addon:last-child {
+ border-left: 0;
+}
+.input-group-btn {
+ position: relative;
+ font-size: 0;
+ white-space: nowrap;
+}
+.input-group-btn > .btn {
+ position: relative;
+}
+.input-group-btn > .btn + .btn {
+ margin-left: -1px;
+}
+.input-group-btn > .btn:hover,
+.input-group-btn > .btn:focus,
+.input-group-btn > .btn:active {
+ z-index: 2;
+}
+.input-group-btn:first-child > .btn,
+.input-group-btn:first-child > .btn-group {
+ margin-right: -1px;
+}
+.input-group-btn:last-child > .btn,
+.input-group-btn:last-child > .btn-group {
+ margin-left: -1px;
+}
+.nav {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+}
+.nav > li {
+ position: relative;
+ display: block;
+}
+.nav > li > a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+}
+#toc .nav > li > a {
+ padding: 0px 0px;
+}
+.nav > li > a:hover,
+.nav > li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
+}
+.nav > li.disabled > a {
+ color: #777777;
+}
+.nav > li.disabled > a:hover,
+.nav > li.disabled > a:focus {
+ color: #777777;
+ text-decoration: none;
+ background-color: transparent;
+ cursor: not-allowed;
+}
+.nav .open > a,
+.nav .open > a:hover,
+.nav .open > a:focus {
+ background-color: #eeeeee;
+ border-color: #428bca;
+}
+.nav .nav-divider {
+ height: 1px;
+ margin: 9px 0;
+ overflow: hidden;
+ background-color: #e5e5e5;
+}
+.nav > li > a > img {
+ max-width: none;
+}
+.nav-tabs {
+ border-bottom: 1px solid #dddddd;
+}
+.nav-tabs > li {
+ float: left;
+ margin-bottom: -1px;
+}
+.nav-tabs > li > a {
+ margin-right: 2px;
+ line-height: 1.42857143;
+ border: 1px solid transparent;
+ border-radius: 4px 4px 0 0;
+}
+.nav-tabs > li > a:hover {
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.nav-tabs > li.active > a,
+.nav-tabs > li.active > a:hover,
+.nav-tabs > li.active > a:focus {
+ color: #555555;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.nav-tabs.nav-justified {
+ width: 100%;
+ border-bottom: 0;
+}
+.nav-tabs.nav-justified > li {
+ float: none;
+}
+.nav-tabs.nav-justified > li > a {
+ text-align: center;
+ margin-bottom: 5px;
+}
+.nav-tabs.nav-justified > .dropdown .dropdown-menu {
+ top: auto;
+ left: auto;
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+ .nav-tabs.nav-justified > li > a {
+ margin-bottom: 0;
+ }
+}
+.nav-tabs.nav-justified > li > a {
+ margin-right: 0;
+ border-radius: 4px;
+}
+.nav-tabs.nav-justified > .active > a,
+.nav-tabs.nav-justified > .active > a:hover,
+.nav-tabs.nav-justified > .active > a:focus {
+ border: 1px solid #dddddd;
+}
+@media (min-width: 768px) {
+ .nav-tabs.nav-justified > li > a {
+ border-bottom: 1px solid #dddddd;
+ border-radius: 4px 4px 0 0;
+ }
+ .nav-tabs.nav-justified > .active > a,
+ .nav-tabs.nav-justified > .active > a:hover,
+ .nav-tabs.nav-justified > .active > a:focus {
+ border-bottom-color: #ffffff;
+ }
+}
+.nav-pills > li {
+ float: left;
+}
+.nav-pills > li > a {
+ border-radius: 4px;
+}
+.nav-pills > li + li {
+ margin-left: 2px;
+}
+.nav-pills > li.active > a,
+.nav-pills > li.active > a:hover,
+.nav-pills > li.active > a:focus {
+ color: #ffffff;
+ background-color: #428bca;
+}
+.nav-stacked > li {
+ float: none;
+}
+.nav-stacked > li + li {
+ margin-top: 2px;
+ margin-left: 0;
+}
+.nav-justified {
+ width: 100%;
+}
+.nav-justified > li {
+ float: none;
+}
+.nav-justified > li > a {
+ text-align: center;
+ margin-bottom: 5px;
+}
+.nav-justified > .dropdown .dropdown-menu {
+ top: auto;
+ left: auto;
+}
+@media (min-width: 768px) {
+ .nav-justified > li {
+ display: table-cell;
+ width: 1%;
+ }
+ .nav-justified > li > a {
+ margin-bottom: 0;
+ }
+}
+.nav-tabs-justified {
+ border-bottom: 0;
+}
+.nav-tabs-justified > li > a {
+ margin-right: 0;
+ border-radius: 4px;
+}
+.nav-tabs-justified > .active > a,
+.nav-tabs-justified > .active > a:hover,
+.nav-tabs-justified > .active > a:focus {
+ border: 1px solid #dddddd;
+}
+@media (min-width: 768px) {
+ .nav-tabs-justified > li > a {
+ border-bottom: 1px solid #dddddd;
+ border-radius: 4px 4px 0 0;
+ }
+ .nav-tabs-justified > .active > a,
+ .nav-tabs-justified > .active > a:hover,
+ .nav-tabs-justified > .active > a:focus {
+ border-bottom-color: #ffffff;
+ }
+}
+.tab-content > .tab-pane {
+ display: none;
+ visibility: hidden;
+}
+.tab-content > .active {
+ display: block;
+ visibility: visible;
+}
+.nav-tabs .dropdown-menu {
+ margin-top: -1px;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.navbar {
+ position: relative;
+ min-height: 66px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+}
+@media (min-width: 768px) {
+ .navbar {
+ border-radius: 4px;
+ }
+}
+@media (min-width: 768px) {
+ .navbar-header {
+ float: left;
+ }
+}
+.navbar-collapse {
+ overflow-x: visible;
+ padding-right: 15px;
+ padding-left: 15px;
+ border-top: 1px solid transparent;
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
+ -webkit-overflow-scrolling: touch;
+}
+.navbar-collapse.in {
+ overflow-y: auto;
+}
+@media (min-width: 768px) {
+ .navbar-collapse {
+ width: auto;
+ border-top: 0;
+ box-shadow: none;
+ }
+ .navbar-collapse.collapse {
+ display: block !important;
+ visibility: visible !important;
+ height: auto !important;
+ padding-bottom: 0;
+ overflow: visible !important;
+ }
+ .navbar-collapse.in {
+ overflow-y: visible;
+ }
+ .navbar-fixed-top .navbar-collapse,
+ .navbar-static-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ padding-left: 0;
+ padding-right: 0;
+ }
+}
+.navbar-fixed-top .navbar-collapse,
+.navbar-fixed-bottom .navbar-collapse {
+ max-height: 340px;
+}
+@media (max-device-width: 480px) and (orientation: landscape) {
+ .navbar-fixed-top .navbar-collapse,
+ .navbar-fixed-bottom .navbar-collapse {
+ max-height: 200px;
+ }
+}
+.container > .navbar-header,
+.container-fluid > .navbar-header,
+.container > .navbar-collapse,
+.container-fluid > .navbar-collapse {
+ margin-right: -15px;
+ margin-left: -15px;
+}
+@media (min-width: 768px) {
+ .container > .navbar-header,
+ .container-fluid > .navbar-header,
+ .container > .navbar-collapse,
+ .container-fluid > .navbar-collapse {
+ margin-right: 0;
+ margin-left: 0;
+ }
+}
+.navbar-static-top {
+ z-index: 1000;
+ border-width: 0 0 1px;
}
-
-.input-group-btn {
- position: relative;
- white-space: nowrap;
+@media (min-width: 768px) {
+ .navbar-static-top {
+ border-radius: 0;
+ }
}
-
-.input-group-btn > .btn {
- position: relative;
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+ position: fixed;
+ right: 0;
+ left: 0;
+ z-index: 1030;
}
-
-.input-group-btn > .btn + .btn {
- margin-left: -4px;
+@media (min-width: 768px) {
+ .navbar-fixed-top,
+ .navbar-fixed-bottom {
+ border-radius: 0;
+ }
}
-
-.input-group-btn > .btn:hover,
-.input-group-btn > .btn:active {
- z-index: 2;
+.navbar-fixed-top {
+ top: 0;
+ border-width: 0 0 1px;
}
-
-.nav {
- padding-left: 0;
+.navbar-fixed-bottom {
+ bottom: 0;
margin-bottom: 0;
- list-style: none;
+ border-width: 1px 0 0;
}
-
-.nav:before,
-.nav:after {
- display: table;
- content: " ";
+.navbar-brand {
+ float: left;
+ padding: 23px 15px;
+ font-size: 18px;
+ line-height: 20px;
+ height: 66px;
}
-
-.nav:after {
- clear: both;
+.navbar-brand:hover,
+.navbar-brand:focus {
+ text-decoration: none;
}
-
-.nav:before,
-.nav:after {
- display: table;
- content: " ";
+.navbar-brand > img {
+ display: block;
}
-
-.nav:after {
- clear: both;
+@media (min-width: 768px) {
+ .navbar > .container .navbar-brand,
+ .navbar > .container-fluid .navbar-brand {
+ margin-left: -15px;
+ }
}
-
-.nav > li {
+.navbar-toggle {
position: relative;
- display: block;
+ float: right;
+ margin-right: 15px;
+ padding: 9px 10px;
+ margin-top: 16px;
+ margin-bottom: 16px;
+ background-color: transparent;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 4px;
}
-
-.nav > li > a {
- position: relative;
+.navbar-toggle:focus {
+ outline: 0;
+}
+.navbar-toggle .icon-bar {
display: block;
- padding: 10px 15px;
+ width: 22px;
+ height: 2px;
+ border-radius: 1px;
}
-
-.nav > li > a:hover,
-.nav > li > a:focus {
- text-decoration: none;
- background-color: #eeeeee;
+.navbar-toggle .icon-bar + .icon-bar {
+ margin-top: 4px;
}
-
-.nav > li.disabled > a {
- color: #999999;
+@media (min-width: 768px) {
+ .navbar-toggle {
+ display: none;
+ }
}
-
-.nav > li.disabled > a:hover,
-.nav > li.disabled > a:focus {
- color: #999999;
- text-decoration: none;
- cursor: not-allowed;
- background-color: transparent;
+.navbar-nav {
+ margin: 11.5px -15px;
}
-
-.nav .open > a,
-.nav .open > a:hover,
-.nav .open > a:focus {
- background-color: #eeeeee;
- border-color: #428bca;
+.navbar-nav > li > a {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ line-height: 20px;
}
-
-.nav .nav-divider {
- height: 1px;
- margin: 9px 0;
- overflow: hidden;
- background-color: #e5e5e5;
+@media (max-width: 767px) {
+ .navbar-nav .open .dropdown-menu {
+ position: static;
+ float: none;
+ width: auto;
+ margin-top: 0;
+ background-color: transparent;
+ border: 0;
+ box-shadow: none;
+ }
+ .navbar-nav .open .dropdown-menu > li > a,
+ .navbar-nav .open .dropdown-menu .dropdown-header {
+ padding: 5px 15px 5px 25px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a {
+ line-height: 20px;
+ }
+ .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-nav .open .dropdown-menu > li > a:focus {
+ background-image: none;
+ }
}
-
-.nav > li > a > img {
- max-width: none;
+@media (min-width: 768px) {
+ .navbar-nav {
+ float: left;
+ margin: 0;
+ }
+ .navbar-nav > li {
+ float: left;
+ }
+ .navbar-nav > li > a {
+ padding-top: 23px;
+ padding-bottom: 23px;
+ }
}
-
-.nav-tabs {
- border-bottom: 1px solid #dddddd;
+.navbar-form {
+ margin-left: -15px;
+ margin-right: -15px;
+ padding: 10px 15px;
+ border-top: 1px solid transparent;
+ border-bottom: 1px solid transparent;
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+ margin-top: 16px;
+ margin-bottom: 16px;
}
-
-.nav-tabs > li {
- float: left;
- margin-bottom: -1px;
+@media (min-width: 768px) {
+ .navbar-form .form-group {
+ display: inline-block;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control {
+ display: inline-block;
+ width: auto;
+ vertical-align: middle;
+ }
+ .navbar-form .form-control-static {
+ display: inline-block;
+ }
+ .navbar-form .input-group {
+ display: inline-table;
+ vertical-align: middle;
+ }
+ .navbar-form .input-group .input-group-addon,
+ .navbar-form .input-group .input-group-btn,
+ .navbar-form .input-group .form-control {
+ width: auto;
+ }
+ .navbar-form .input-group > .form-control {
+ width: 100%;
+ }
+ .navbar-form .control-label {
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio,
+ .navbar-form .checkbox {
+ display: inline-block;
+ margin-top: 0;
+ margin-bottom: 0;
+ vertical-align: middle;
+ }
+ .navbar-form .radio label,
+ .navbar-form .checkbox label {
+ padding-left: 0;
+ }
+ .navbar-form .radio input[type="radio"],
+ .navbar-form .checkbox input[type="checkbox"] {
+ position: relative;
+ margin-left: 0;
+ }
+ .navbar-form .has-feedback .form-control-feedback {
+ top: 0;
+ }
}
-
-.nav-tabs > li > a {
- margin-right: 2px;
- line-height: 1.428571429;
- border: 1px solid transparent;
- border-radius: 4px 4px 0 0;
+@media (max-width: 767px) {
+ .navbar-form .form-group {
+ margin-bottom: 5px;
+ }
+ .navbar-form .form-group:last-child {
+ margin-bottom: 0;
+ }
}
-
-.nav-tabs > li > a:hover {
- border-color: #eeeeee #eeeeee #dddddd;
+@media (min-width: 768px) {
+ .navbar-form {
+ width: auto;
+ border: 0;
+ margin-left: 0;
+ margin-right: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
}
-
-.nav-tabs > li.active > a,
-.nav-tabs > li.active > a:hover,
-.nav-tabs > li.active > a:focus {
- color: #555555;
- cursor: default;
- background-color: #ffffff;
- border: 1px solid #dddddd;
- border-bottom-color: transparent;
+.navbar-nav > li > .dropdown-menu {
+ margin-top: 0;
+ border-top-right-radius: 0;
+ border-top-left-radius: 0;
+}
+.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.navbar-btn {
+ margin-top: 16px;
+ margin-bottom: 16px;
}
-
-.nav-tabs.nav-justified {
- width: 100%;
- border-bottom: 0;
+.navbar-btn.btn-sm {
+ margin-top: 18px;
+ margin-bottom: 18px;
}
-
-.nav-tabs.nav-justified > li {
- float: none;
+.navbar-btn.btn-xs {
+ margin-top: 22px;
+ margin-bottom: 22px;
}
-
-.nav-tabs.nav-justified > li > a {
- text-align: center;
+.navbar-text {
+ margin-top: 23px;
+ margin-bottom: 23px;
}
-
@media (min-width: 768px) {
- .nav-tabs.nav-justified > li {
- display: table-cell;
- width: 1%;
+ .navbar-text {
+ float: left;
+ margin-left: 15px;
+ margin-right: 15px;
}
}
-
-.nav-tabs.nav-justified > li > a {
- margin-right: 0;
- border-bottom: 1px solid #dddddd;
+@media (min-width: 768px) {
+ .navbar-left {
+ float: left !important;
+ }
+ .navbar-right {
+ float: right !important;
+ margin-right: -15px;
+ }
+ .navbar-right ~ .navbar-right {
+ margin-right: 0;
+ }
}
-
-.nav-tabs.nav-justified > .active > a {
- border-bottom-color: #ffffff;
+.navbar-default {
+ background-color: #f8f8f8;
+ border-color: #e7e7e7;
}
-
-.nav-pills > li {
- float: left;
+.navbar-default .navbar-brand {
+ color: #777777;
}
-
-.nav-pills > li > a {
- border-radius: 5px;
+.navbar-default .navbar-brand:hover,
+.navbar-default .navbar-brand:focus {
+ color: #5e5e5e;
+ background-color: transparent;
}
-
-.nav-pills > li + li {
- margin-left: 2px;
+.navbar-default .navbar-text {
+ color: #777777;
}
-
-.nav-pills > li.active > a,
-.nav-pills > li.active > a:hover,
-.nav-pills > li.active > a:focus {
- color: #ffffff;
- background-color: #428bca;
+.navbar-default .navbar-nav > li > a {
+ color: #777777;
}
-
-.nav-stacked > li {
- float: none;
+.navbar-default .navbar-nav > li > a:hover,
+.navbar-default .navbar-nav > li > a:focus {
+ color: #333333;
+ background-color: transparent;
}
-
-.nav-stacked > li + li {
- margin-top: 2px;
- margin-left: 0;
+.navbar-default .navbar-nav > .active > a,
+.navbar-default .navbar-nav > .active > a:hover,
+.navbar-default .navbar-nav > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
}
-
-.nav-justified {
- width: 100%;
+.navbar-default .navbar-nav > .disabled > a,
+.navbar-default .navbar-nav > .disabled > a:hover,
+.navbar-default .navbar-nav > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
}
-
-.nav-justified > li {
- float: none;
+.navbar-default .navbar-toggle {
+ border-color: #dddddd;
}
-
-.nav-justified > li > a {
- text-align: center;
+.navbar-default .navbar-toggle:hover,
+.navbar-default .navbar-toggle:focus {
+ background-color: #dddddd;
}
-
-@media (min-width: 768px) {
- .nav-justified > li {
- display: table-cell;
- width: 1%;
+.navbar-default .navbar-toggle .icon-bar {
+ background-color: #888888;
+}
+.navbar-default .navbar-collapse,
+.navbar-default .navbar-form {
+ border-color: #e7e7e7;
+}
+.navbar-default .navbar-nav > .open > a,
+.navbar-default .navbar-nav > .open > a:hover,
+.navbar-default .navbar-nav > .open > a:focus {
+ background-color: #e7e7e7;
+ color: #555555;
+}
+@media (max-width: 767px) {
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a {
+ color: #777777;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #333333;
+ background-color: transparent;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #555555;
+ background-color: #e7e7e7;
+ }
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #cccccc;
+ background-color: transparent;
}
}
-
-.nav-tabs-justified {
- border-bottom: 0;
+.navbar-default .navbar-link {
+ color: #777777;
}
-
-.nav-tabs-justified > li > a {
- margin-right: 0;
- border-bottom: 1px solid #dddddd;
+.navbar-default .navbar-link:hover {
+ color: #333333;
}
-
-.nav-tabs-justified > .active > a {
- border-bottom-color: #ffffff;
+.navbar-default .btn-link {
+ color: #777777;
}
-
-.tabbable:before,
-.tabbable:after {
- display: table;
- content: " ";
+.navbar-default .btn-link:hover,
+.navbar-default .btn-link:focus {
+ color: #333333;
}
-
-.tabbable:after {
- clear: both;
+.navbar-default .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-default .btn-link:hover,
+.navbar-default .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-default .btn-link:focus {
+ color: #cccccc;
}
-
-.tabbable:before,
-.tabbable:after {
- display: table;
- content: " ";
+.navbar-inverse {
+ background-color: #222222;
+ border-color: #080808;
}
-
-.tabbable:after {
- clear: both;
+.navbar-inverse .navbar-brand {
+ color: #9d9d9d;
}
-
-.tab-content > .tab-pane,
-.pill-content > .pill-pane {
- display: none;
+.navbar-inverse .navbar-brand:hover,
+.navbar-inverse .navbar-brand:focus {
+ color: #ffffff;
+ background-color: transparent;
}
-
-.tab-content > .active,
-.pill-content > .active {
- display: block;
+.navbar-inverse .navbar-text {
+ color: #9d9d9d;
}
-
-.nav .caret {
- border-top-color: #428bca;
- border-bottom-color: #428bca;
+.navbar-inverse .navbar-nav > li > a {
+ color: #9d9d9d;
}
-
-.nav a:hover .caret {
- border-top-color: #2a6496;
- border-bottom-color: #2a6496;
+.navbar-inverse .navbar-nav > li > a:hover,
+.navbar-inverse .navbar-nav > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
}
-
-.nav-tabs .dropdown-menu {
- margin-top: -1px;
- border-top-right-radius: 0;
- border-top-left-radius: 0;
+.navbar-inverse .navbar-nav > .active > a,
+.navbar-inverse .navbar-nav > .active > a:hover,
+.navbar-inverse .navbar-nav > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
}
-
-.navbar {
- position: relative;
- z-index: 1000;
- min-height: 50px;
- margin-bottom: 20px;
- border: 1px solid transparent;
+.navbar-inverse .navbar-nav > .disabled > a,
+.navbar-inverse .navbar-nav > .disabled > a:hover,
+.navbar-inverse .navbar-nav > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
}
-
-.navbar:before,
-.navbar:after {
- display: table;
- content: " ";
+.navbar-inverse .navbar-toggle {
+ border-color: #333333;
}
-
-.navbar:after {
- clear: both;
+.navbar-inverse .navbar-toggle:hover,
+.navbar-inverse .navbar-toggle:focus {
+ background-color: #333333;
}
-
-.navbar:before,
-.navbar:after {
- display: table;
- content: " ";
+.navbar-inverse .navbar-toggle .icon-bar {
+ background-color: #ffffff;
+}
+.navbar-inverse .navbar-collapse,
+.navbar-inverse .navbar-form {
+ border-color: #101010;
+}
+.navbar-inverse .navbar-nav > .open > a,
+.navbar-inverse .navbar-nav > .open > a:hover,
+.navbar-inverse .navbar-nav > .open > a:focus {
+ background-color: #080808;
+ color: #ffffff;
+}
+@media (max-width: 767px) {
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
+ border-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
+ color: #9d9d9d;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
+ color: #ffffff;
+ background-color: transparent;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
+ color: #ffffff;
+ background-color: #080808;
+ }
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
+ .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
+ color: #444444;
+ background-color: transparent;
+ }
}
-
-.navbar:after {
- clear: both;
+.navbar-inverse .navbar-link {
+ color: #9d9d9d;
}
-
-@media (min-width: 768px) {
- .navbar {
- border-radius: 4px;
- }
+.navbar-inverse .navbar-link:hover {
+ color: #ffffff;
}
-
-.navbar-header:before,
-.navbar-header:after {
- display: table;
- content: " ";
+.navbar-inverse .btn-link {
+ color: #9d9d9d;
}
-
-.navbar-header:after {
- clear: both;
+.navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link:focus {
+ color: #ffffff;
}
-
-.navbar-header:before,
-.navbar-header:after {
- display: table;
- content: " ";
+.navbar-inverse .btn-link[disabled]:hover,
+fieldset[disabled] .navbar-inverse .btn-link:hover,
+.navbar-inverse .btn-link[disabled]:focus,
+fieldset[disabled] .navbar-inverse .btn-link:focus {
+ color: #444444;
}
-
-.navbar-header:after {
- clear: both;
+.breadcrumb {
+ padding: 8px 15px;
+ margin-bottom: 20px;
+ list-style: none;
+ background-color: #f5f5f5;
+ border-radius: 4px;
}
-
-@media (min-width: 768px) {
- .navbar-header {
- float: left;
- }
+.breadcrumb > li {
+ display: inline-block;
}
-
-.navbar-collapse {
- max-height: 340px;
- padding-right: 15px;
- padding-left: 15px;
- overflow-x: visible;
- border-top: 1px solid transparent;
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
- -webkit-overflow-scrolling: touch;
+.breadcrumb > li + li:before {
+ content: "/\00a0";
+ padding: 0 5px;
+ color: #cccccc;
}
-
-.navbar-collapse:before,
-.navbar-collapse:after {
- display: table;
- content: " ";
+.breadcrumb > .active {
+ color: #777777;
}
-
-.navbar-collapse:after {
- clear: both;
+.pagination {
+ display: inline-block;
+ padding-left: 0;
+ margin: 20px 0;
+ border-radius: 4px;
}
-
-.navbar-collapse:before,
-.navbar-collapse:after {
- display: table;
- content: " ";
+.pagination > li {
+ display: inline;
}
-
-.navbar-collapse:after {
- clear: both;
+.pagination > li > a,
+.pagination > li > span {
+ position: relative;
+ float: left;
+ padding: 6px 12px;
+ line-height: 1.42857143;
+ text-decoration: none;
+ color: #428bca;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ margin-left: -1px;
}
-
-.navbar-collapse.in {
- overflow-y: auto;
+.pagination > li:first-child > a,
+.pagination > li:first-child > span {
+ margin-left: 0;
+ border-bottom-left-radius: 4px;
+ border-top-left-radius: 4px;
}
-
-@media (min-width: 768px) {
- .navbar-collapse {
- width: auto;
- border-top: 0;
- box-shadow: none;
- }
- .navbar-collapse.collapse {
- display: block !important;
- height: auto !important;
- padding-bottom: 0;
- overflow: visible !important;
- }
- .navbar-collapse.in {
- overflow-y: visible;
- }
- .navbar-collapse .navbar-nav.navbar-left:first-child {
- margin-left: -15px;
- }
- .navbar-collapse .navbar-nav.navbar-right:last-child {
- margin-right: -15px;
- }
- .navbar-collapse .navbar-text:last-child {
- margin-right: 0;
- }
+.pagination > li:last-child > a,
+.pagination > li:last-child > span {
+ border-bottom-right-radius: 4px;
+ border-top-right-radius: 4px;
}
-
-.container > .navbar-header,
-.container > .navbar-collapse {
- margin-right: -15px;
- margin-left: -15px;
+.pagination > li > a:hover,
+.pagination > li > span:hover,
+.pagination > li > a:focus,
+.pagination > li > span:focus {
+ color: #1e3756;
+ background-color: #eeeeee;
+ border-color: #dddddd;
}
-
-@media (min-width: 768px) {
- .container > .navbar-header,
- .container > .navbar-collapse {
- margin-right: 0;
- margin-left: 0;
- }
+.pagination > .active > a,
+.pagination > .active > span,
+.pagination > .active > a:hover,
+.pagination > .active > span:hover,
+.pagination > .active > a:focus,
+.pagination > .active > span:focus {
+ z-index: 2;
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
+ cursor: default;
}
-
-.navbar-static-top {
- border-width: 0 0 1px;
+.pagination > .disabled > span,
+.pagination > .disabled > span:hover,
+.pagination > .disabled > span:focus,
+.pagination > .disabled > a,
+.pagination > .disabled > a:hover,
+.pagination > .disabled > a:focus {
+ color: #777777;
+ background-color: #ffffff;
+ border-color: #dddddd;
+ cursor: not-allowed;
}
-
-@media (min-width: 768px) {
- .navbar-static-top {
- border-radius: 0;
- }
+.pagination-lg > li > a,
+.pagination-lg > li > span {
+ padding: 10px 16px;
+ font-size: 18px;
}
-
-.navbar-fixed-top,
-.navbar-fixed-bottom {
- position: fixed;
- right: 0;
- left: 0;
- border-width: 0 0 1px;
+.pagination-lg > li:first-child > a,
+.pagination-lg > li:first-child > span {
+ border-bottom-left-radius: 6px;
+ border-top-left-radius: 6px;
}
-
-@media (min-width: 768px) {
- .navbar-fixed-top,
- .navbar-fixed-bottom {
- border-radius: 0;
- }
+.pagination-lg > li:last-child > a,
+.pagination-lg > li:last-child > span {
+ border-bottom-right-radius: 6px;
+ border-top-right-radius: 6px;
}
-
-.navbar-fixed-top {
- top: 0;
- z-index: 1030;
+.pagination-sm > li > a,
+.pagination-sm > li > span {
+ padding: 5px 10px;
+ font-size: 12px;
}
-
-.navbar-fixed-bottom {
- bottom: 0;
- margin-bottom: 0;
+.pagination-sm > li:first-child > a,
+.pagination-sm > li:first-child > span {
+ border-bottom-left-radius: 3px;
+ border-top-left-radius: 3px;
}
-
-.navbar-brand {
- float: left;
- padding: 15px 15px;
- font-size: 18px;
- line-height: 20px;
+.pagination-sm > li:last-child > a,
+.pagination-sm > li:last-child > span {
+ border-bottom-right-radius: 3px;
+ border-top-right-radius: 3px;
}
-
-.navbar-brand:hover,
-.navbar-brand:focus {
- text-decoration: none;
+.pager {
+ padding-left: 0;
+ margin: 20px 0;
+ list-style: none;
+ text-align: center;
}
-
-@media (min-width: 768px) {
- .navbar > .container .navbar-brand {
- margin-left: -15px;
- }
+.pager li {
+ display: inline;
}
-
-.navbar-toggle {
- position: relative;
- float: right;
- padding: 9px 10px;
- margin-top: 8px;
- margin-right: 15px;
- margin-bottom: 8px;
- background-color: transparent;
- border: 1px solid transparent;
- border-radius: 4px;
+.pager li > a,
+.pager li > span {
+ display: inline-block;
+ padding: 5px 14px;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 15px;
}
-
-.navbar-toggle .icon-bar {
- display: block;
- width: 22px;
- height: 2px;
- border-radius: 1px;
+.pager li > a:hover,
+.pager li > a:focus {
+ text-decoration: none;
+ background-color: #eeeeee;
}
-
-.navbar-toggle .icon-bar + .icon-bar {
- margin-top: 4px;
+.pager .next > a,
+.pager .next > span {
+ float: right;
}
-
-@media (min-width: 768px) {
- .navbar-toggle {
- display: none;
- }
+.pager .previous > a,
+.pager .previous > span {
+ float: left;
}
-
-.navbar-nav {
- margin: 7.5px -15px;
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+ color: #777777;
+ background-color: #ffffff;
+ cursor: not-allowed;
}
-
-.navbar-nav > li > a {
- padding-top: 10px;
- padding-bottom: 10px;
- line-height: 20px;
+.label {
+ display: inline;
+ padding: .2em .6em .3em;
+ font-size: 75%;
+ font-weight: bold;
+ line-height: 1;
+ color: #ffffff;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ border-radius: .25em;
}
-
-@media (max-width: 767px) {
- .navbar-nav .open .dropdown-menu {
- position: static;
- float: none;
- width: auto;
- margin-top: 0;
- background-color: transparent;
- border: 0;
- box-shadow: none;
- }
- .navbar-nav .open .dropdown-menu > li > a,
- .navbar-nav .open .dropdown-menu .dropdown-header {
- padding: 5px 15px 5px 25px;
- }
- .navbar-nav .open .dropdown-menu > li > a {
- line-height: 20px;
- }
- .navbar-nav .open .dropdown-menu > li > a:hover,
- .navbar-nav .open .dropdown-menu > li > a:focus {
- background-image: none;
- }
+a.label:hover,
+a.label:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
}
-
-@media (min-width: 768px) {
- .navbar-nav {
- float: left;
- margin: 0;
- }
- .navbar-nav > li {
- float: left;
- }
- .navbar-nav > li > a {
- padding-top: 15px;
- padding-bottom: 15px;
- }
+.label:empty {
+ display: none;
}
-
-@media (min-width: 768px) {
- .navbar-left {
- float: left !important;
- }
- .navbar-right {
- float: right !important;
- }
+.btn .label {
+ position: relative;
+ top: -1px;
}
-
-.navbar-form {
- padding: 10px 15px;
- margin-top: 8px;
- margin-right: -15px;
- margin-bottom: 8px;
- margin-left: -15px;
- border-top: 1px solid transparent;
- border-bottom: 1px solid transparent;
- -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
+.label-default {
+ background-color: #777777;
}
-
-@media (min-width: 768px) {
- .navbar-form .form-group {
- display: inline-block;
- margin-bottom: 0;
- vertical-align: middle;
- }
- .navbar-form .form-control {
- display: inline-block;
- }
- .navbar-form .radio,
- .navbar-form .checkbox {
- display: inline-block;
- padding-left: 0;
- margin-top: 0;
- margin-bottom: 0;
- }
- .navbar-form .radio input[type="radio"],
- .navbar-form .checkbox input[type="checkbox"] {
- float: none;
- margin-left: 0;
- }
+.label-default[href]:hover,
+.label-default[href]:focus {
+ background-color: #5e5e5e;
}
-
-@media (max-width: 767px) {
- .navbar-form .form-group {
- margin-bottom: 5px;
- }
+.label-primary {
+ background-color: #428bca;
}
-
-@media (min-width: 768px) {
- .navbar-form {
- width: auto;
- padding-top: 0;
- padding-bottom: 0;
- margin-right: 0;
- margin-left: 0;
- border: 0;
- -webkit-box-shadow: none;
- box-shadow: none;
- }
+.label-primary[href]:hover,
+.label-primary[href]:focus {
+ background-color: #3d8b40;
}
-
-.navbar-nav > li > .dropdown-menu {
- margin-top: 0;
- border-top-right-radius: 0;
- border-top-left-radius: 0;
+.label-success {
+ background-color: #5cb85c;
}
-
-.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu {
- border-bottom-right-radius: 0;
- border-bottom-left-radius: 0;
+.label-success[href]:hover,
+.label-success[href]:focus {
+ background-color: #449d44;
}
-
-.navbar-nav.pull-right > li > .dropdown-menu,
-.navbar-nav > li > .dropdown-menu.pull-right {
- right: 0;
- left: auto;
+.label-info {
+ background-color: #5bc0de;
}
-
-.navbar-btn {
- margin-top: 8px;
- margin-bottom: 8px;
+.label-info[href]:hover,
+.label-info[href]:focus {
+ background-color: #31b0d5;
}
-
-.navbar-text {
- float: left;
- margin-top: 15px;
- margin-bottom: 15px;
+.label-warning {
+ background-color: #f0ad4e;
}
-
-@media (min-width: 768px) {
- .navbar-text {
- margin-right: 15px;
- margin-left: 15px;
- }
+.label-warning[href]:hover,
+.label-warning[href]:focus {
+ background-color: #ec971f;
}
-
-.navbar-default {
- background-color: #f8f8f8;
- border-color: #e7e7e7;
+.label-danger {
+ background-color: #d9534f;
}
-
-.navbar-default .navbar-brand {
- color: #777777;
+.label-danger[href]:hover,
+.label-danger[href]:focus {
+ background-color: #c9302c;
}
-
-.navbar-default .navbar-brand:hover,
-.navbar-default .navbar-brand:focus {
- color: #5e5e5e;
- background-color: transparent;
+.badge {
+ display: inline-block;
+ min-width: 10px;
+ padding: 3px 7px;
+ font-size: 12px;
+ font-weight: bold;
+ color: #ffffff;
+ line-height: 1;
+ vertical-align: baseline;
+ white-space: nowrap;
+ text-align: center;
+ background-color: #777777;
+ border-radius: 10px;
}
-
-.navbar-default .navbar-text {
- color: #777777;
+.badge:empty {
+ display: none;
}
-
-.navbar-default .navbar-nav > li > a {
- color: #777777;
+.btn .badge {
+ position: relative;
+ top: -1px;
}
-
-.navbar-default .navbar-nav > li > a:hover,
-.navbar-default .navbar-nav > li > a:focus {
- color: #333333;
- background-color: transparent;
+.btn-xs .badge {
+ top: 0;
+ padding: 1px 5px;
}
-
-.navbar-default .navbar-nav > .active > a,
-.navbar-default .navbar-nav > .active > a:hover,
-.navbar-default .navbar-nav > .active > a:focus {
- color: #555555;
- background-color: #e7e7e7;
+a.badge:hover,
+a.badge:focus {
+ color: #ffffff;
+ text-decoration: none;
+ cursor: pointer;
}
-
-.navbar-default .navbar-nav > .disabled > a,
-.navbar-default .navbar-nav > .disabled > a:hover,
-.navbar-default .navbar-nav > .disabled > a:focus {
- color: #cccccc;
- background-color: transparent;
+.list-group-item.active > .badge,
+.nav-pills > .active > a > .badge {
+ color: #428bca;
+ background-color: #ffffff;
}
-
-.navbar-default .navbar-toggle {
- border-color: #dddddd;
+.list-group-item > .badge {
+ float: right;
}
-
-.navbar-default .navbar-toggle:hover,
-.navbar-default .navbar-toggle:focus {
- background-color: #dddddd;
+.list-group-item > .badge + .badge {
+ margin-right: 5px;
+}
+.nav-pills > li > a > .badge {
+ margin-left: 3px;
}
-
-.navbar-default .navbar-toggle .icon-bar {
- background-color: #cccccc;
+.jumbotron {
+ padding: 30px 15px;
+ margin-bottom: 30px;
+ color: inherit;
+ background-color: #eeeeee;
}
-
-.navbar-default .navbar-collapse,
-.navbar-default .navbar-form {
- border-color: #e6e6e6;
+.jumbotron h1,
+.jumbotron .h1 {
+ color: inherit;
}
-
-.navbar-default .navbar-nav > .dropdown > a:hover .caret,
-.navbar-default .navbar-nav > .dropdown > a:focus .caret {
- border-top-color: #333333;
- border-bottom-color: #333333;
+.jumbotron p {
+ margin-bottom: 15px;
+ font-size: 21px;
+ font-weight: 200;
}
-
-.navbar-default .navbar-nav > .open > a,
-.navbar-default .navbar-nav > .open > a:hover,
-.navbar-default .navbar-nav > .open > a:focus {
- color: #555555;
- background-color: #e7e7e7;
+.jumbotron > hr {
+ border-top-color: #d5d5d5;
}
-
-.navbar-default .navbar-nav > .open > a .caret,
-.navbar-default .navbar-nav > .open > a:hover .caret,
-.navbar-default .navbar-nav > .open > a:focus .caret {
- border-top-color: #555555;
- border-bottom-color: #555555;
+.container .jumbotron,
+.container-fluid .jumbotron {
+ border-radius: 6px;
}
-
-.navbar-default .navbar-nav > .dropdown > a .caret {
- border-top-color: #777777;
- border-bottom-color: #777777;
+.jumbotron .container {
+ max-width: 100%;
}
-
-@media (max-width: 767px) {
- .navbar-default .navbar-nav .open .dropdown-menu > li > a {
- color: #777777;
- }
- .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus {
- color: #333333;
- background-color: transparent;
+@media screen and (min-width: 768px) {
+ .jumbotron {
+ padding: 48px 0;
}
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a,
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus {
- color: #555555;
- background-color: #e7e7e7;
+ .container .jumbotron,
+ .container-fluid .jumbotron {
+ padding-left: 60px;
+ padding-right: 60px;
}
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a,
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover,
- .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus {
- color: #cccccc;
- background-color: transparent;
+ .jumbotron h1,
+ .jumbotron .h1 {
+ font-size: 63px;
}
}
-
-.navbar-default .navbar-link {
- color: #777777;
+.thumbnail {
+ display: block;
+ padding: 4px;
+ margin-bottom: 20px;
+ line-height: 1.42857143;
+ background-color: #ffffff;
+ border: 1px solid #dddddd;
+ border-radius: 4px;
+ -webkit-transition: border 0.2s ease-in-out;
+ -o-transition: border 0.2s ease-in-out;
+ transition: border 0.2s ease-in-out;
}
-
-.navbar-default .navbar-link:hover {
+.thumbnail > img,
+.thumbnail a > img {
+ margin-left: auto;
+ margin-right: auto;
+}
+a.thumbnail:hover,
+a.thumbnail:focus,
+a.thumbnail.active {
+ border-color: #428bca;
+}
+.thumbnail .caption {
+ padding: 9px;
color: #333333;
}
-
-.navbar-inverse {
- background-color: #222222;
- border-color: #080808;
+.alert {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
}
-
-.navbar-inverse .navbar-brand {
- color: #999999;
+.alert h4 {
+ margin-top: 0;
+ color: inherit;
}
-
-.navbar-inverse .navbar-brand:hover,
-.navbar-inverse .navbar-brand:focus {
- color: #ffffff;
- background-color: transparent;
+.alert .alert-link {
+ font-weight: bold;
}
-
-.navbar-inverse .navbar-text {
- color: #999999;
+.alert > p,
+.alert > ul {
+ margin-bottom: 0;
}
-
-.navbar-inverse .navbar-nav > li > a {
- color: #999999;
+.alert > p + p {
+ margin-top: 5px;
}
-
-.navbar-inverse .navbar-nav > li > a:hover,
-.navbar-inverse .navbar-nav > li > a:focus {
- color: #ffffff;
- background-color: transparent;
+.alert-dismissable,
+.alert-dismissible {
+ padding-right: 35px;
}
-
-.navbar-inverse .navbar-nav > .active > a,
-.navbar-inverse .navbar-nav > .active > a:hover,
-.navbar-inverse .navbar-nav > .active > a:focus {
- color: #ffffff;
- background-color: #080808;
+.alert-dismissable .close,
+.alert-dismissible .close {
+ position: relative;
+ top: -2px;
+ right: -21px;
+ color: inherit;
}
-
-.navbar-inverse .navbar-nav > .disabled > a,
-.navbar-inverse .navbar-nav > .disabled > a:hover,
-.navbar-inverse .navbar-nav > .disabled > a:focus {
- color: #444444;
- background-color: transparent;
+.alert-success {
+ background-color: #dff0d8;
+ border-color: #d6e9c6;
+ color: #3c763d;
}
-
-.navbar-inverse .navbar-toggle {
- border-color: #333333;
+.alert-success hr {
+ border-top-color: #c9e2b3;
}
-
-.navbar-inverse .navbar-toggle:hover,
-.navbar-inverse .navbar-toggle:focus {
- background-color: #333333;
+.alert-success .alert-link {
+ color: #2b542c;
}
-
-.navbar-inverse .navbar-toggle .icon-bar {
- background-color: #ffffff;
+.alert-info {
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+ color: #31708f;
}
-
-.navbar-inverse .navbar-collapse,
-.navbar-inverse .navbar-form {
- border-color: #101010;
+.alert-info hr {
+ border-top-color: #a6e1ec;
}
-
-.navbar-inverse .navbar-nav > .open > a,
-.navbar-inverse .navbar-nav > .open > a:hover,
-.navbar-inverse .navbar-nav > .open > a:focus {
- color: #ffffff;
- background-color: #080808;
+.alert-info .alert-link {
+ color: #245269;
}
-
-.navbar-inverse .navbar-nav > .dropdown > a:hover .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
+.alert-warning {
+ background-color: #fcf8e3;
+ border-color: #faebcc;
+ color: #8a6d3b;
}
-
-.navbar-inverse .navbar-nav > .dropdown > a .caret {
- border-top-color: #999999;
- border-bottom-color: #999999;
+.alert-warning hr {
+ border-top-color: #f7e1b5;
}
-
-.navbar-inverse .navbar-nav > .open > a .caret,
-.navbar-inverse .navbar-nav > .open > a:hover .caret,
-.navbar-inverse .navbar-nav > .open > a:focus .caret {
- border-top-color: #ffffff;
- border-bottom-color: #ffffff;
+.alert-warning .alert-link {
+ color: #66512c;
}
-
-@media (max-width: 767px) {
- .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
- border-color: #080808;
- }
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
- color: #999999;
+.alert-danger {
+ background-color: #f2dede;
+ border-color: #ebccd1;
+ color: #a94442;
+}
+.alert-danger hr {
+ border-top-color: #e4b9c0;
+}
+.alert-danger .alert-link {
+ color: #843534;
+}
+@-webkit-keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
}
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
- color: #ffffff;
- background-color: transparent;
+ to {
+ background-position: 0 0;
}
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
- color: #ffffff;
- background-color: #080808;
+}
+@keyframes progress-bar-stripes {
+ from {
+ background-position: 40px 0;
}
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
- .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
- color: #444444;
- background-color: transparent;
+ to {
+ background-position: 0 0;
}
}
-
-.navbar-inverse .navbar-link {
- color: #999999;
+.progress {
+ overflow: hidden;
+ height: 20px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+.progress-bar {
+ float: left;
+ width: 0%;
+ height: 100%;
+ font-size: 12px;
+ line-height: 20px;
+ color: #ffffff;
+ text-align: center;
+ background-color: #428bca;
+ -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+ -webkit-transition: width 0.6s ease;
+ -o-transition: width 0.6s ease;
+ transition: width 0.6s ease;
+}
+.progress-striped .progress-bar,
+.progress-bar-striped {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-size: 40px 40px;
+}
+.progress.active .progress-bar,
+.progress-bar.active {
+ -webkit-animation: progress-bar-stripes 2s linear infinite;
+ -o-animation: progress-bar-stripes 2s linear infinite;
+ animation: progress-bar-stripes 2s linear infinite;
+}
+.progress-bar-success {
+ background-color: #5cb85c;
+}
+.progress-striped .progress-bar-success {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-info {
+ background-color: #5bc0de;
+}
+.progress-striped .progress-bar-info {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-warning {
+ background-color: #f0ad4e;
+}
+.progress-striped .progress-bar-warning {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+.progress-bar-danger {
+ background-color: #d9534f;
+}
+.progress-striped .progress-bar-danger {
+ background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+ background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
}
-
-.navbar-inverse .navbar-link:hover {
- color: #ffffff;
+.media {
+ margin-top: 15px;
}
-
-.breadcrumb {
- padding: 8px 15px;
- margin-bottom: 20px;
- list-style: none;
- background-color: #f5f5f5;
- border-radius: 4px;
+.media:first-child {
+ margin-top: 0;
}
-
-.breadcrumb > li {
- display: inline-block;
+.media-right,
+.media > .pull-right {
+ padding-left: 10px;
}
-
-.breadcrumb > li + li:before {
- padding: 0 5px;
- color: #cccccc;
- content: "/\00a0";
+.media-left,
+.media > .pull-left {
+ padding-right: 10px;
}
-
-.breadcrumb > .active {
- color: #999999;
+.media-left,
+.media-right,
+.media-body {
+ display: table-cell;
+ vertical-align: top;
}
-
-.pagination {
- display: inline-block;
+.media-middle {
+ vertical-align: middle;
+}
+.media-bottom {
+ vertical-align: bottom;
+}
+.media-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+.media-list {
padding-left: 0;
- margin: 20px 0;
- border-radius: 4px;
+ list-style: none;
}
-
-.pagination > li {
- display: inline;
+.list-group {
+ margin-bottom: 20px;
+ padding-left: 0;
}
-
-.pagination > li > a,
-.pagination > li > span {
+.list-group-item {
position: relative;
- float: left;
- padding: 6px 12px;
- margin-left: -1px;
- line-height: 1.428571429;
- text-decoration: none;
+ display: block;
+ padding: 10px 15px;
+ margin-bottom: -1px;
background-color: #ffffff;
border: 1px solid #dddddd;
}
-
-.pagination > li:first-child > a,
-.pagination > li:first-child > span {
- margin-left: 0;
- border-bottom-left-radius: 4px;
+.list-group-item:first-child {
+ border-top-right-radius: 4px;
border-top-left-radius: 4px;
}
-
-.pagination > li:last-child > a,
-.pagination > li:last-child > span {
- border-top-right-radius: 4px;
+.list-group-item:last-child {
+ margin-bottom: 0;
border-bottom-right-radius: 4px;
+ border-bottom-left-radius: 4px;
}
-
-.pagination > li > a:hover,
-.pagination > li > span:hover,
-.pagination > li > a:focus,
-.pagination > li > span:focus {
+a.list-group-item {
+ color: #555555;
+}
+a.list-group-item .list-group-item-heading {
+ color: #333333;
+}
+a.list-group-item:hover,
+a.list-group-item:focus {
+ text-decoration: none;
+ color: #555555;
+ background-color: #f5f5f5;
+}
+.list-group-item.disabled,
+.list-group-item.disabled:hover,
+.list-group-item.disabled:focus {
background-color: #eeeeee;
+ color: #777777;
+ cursor: not-allowed;
}
-
-.pagination > .active > a,
-.pagination > .active > span,
-.pagination > .active > a:hover,
-.pagination > .active > span:hover,
-.pagination > .active > a:focus,
-.pagination > .active > span:focus {
+.list-group-item.disabled .list-group-item-heading,
+.list-group-item.disabled:hover .list-group-item-heading,
+.list-group-item.disabled:focus .list-group-item-heading {
+ color: inherit;
+}
+.list-group-item.disabled .list-group-item-text,
+.list-group-item.disabled:hover .list-group-item-text,
+.list-group-item.disabled:focus .list-group-item-text {
+ color: #777777;
+}
+.list-group-item.active,
+.list-group-item.active:hover,
+.list-group-item.active:focus {
z-index: 2;
color: #ffffff;
- cursor: default;
background-color: #428bca;
border-color: #428bca;
}
-
-.pagination > .disabled > span,
-.pagination > .disabled > a,
-.pagination > .disabled > a:hover,
-.pagination > .disabled > a:focus {
- color: #999999;
- cursor: not-allowed;
- background-color: #ffffff;
- border-color: #dddddd;
+.list-group-item.active .list-group-item-heading,
+.list-group-item.active:hover .list-group-item-heading,
+.list-group-item.active:focus .list-group-item-heading,
+.list-group-item.active .list-group-item-heading > small,
+.list-group-item.active:hover .list-group-item-heading > small,
+.list-group-item.active:focus .list-group-item-heading > small,
+.list-group-item.active .list-group-item-heading > .small,
+.list-group-item.active:hover .list-group-item-heading > .small,
+.list-group-item.active:focus .list-group-item-heading > .small {
+ color: inherit;
}
-
-.pagination-lg > li > a,
-.pagination-lg > li > span {
- padding: 10px 16px;
- font-size: 18px;
+.list-group-item.active .list-group-item-text,
+.list-group-item.active:hover .list-group-item-text,
+.list-group-item.active:focus .list-group-item-text {
+ color: #d9eeda;
}
-
-.pagination-lg > li:first-child > a,
-.pagination-lg > li:first-child > span {
- border-bottom-left-radius: 6px;
- border-top-left-radius: 6px;
+.list-group-item-success {
+ color: #3c763d;
+ background-color: #dff0d8;
}
-
-.pagination-lg > li:last-child > a,
-.pagination-lg > li:last-child > span {
- border-top-right-radius: 6px;
- border-bottom-right-radius: 6px;
+a.list-group-item-success {
+ color: #3c763d;
}
-
-.pagination-sm > li > a,
-.pagination-sm > li > span {
- padding: 5px 10px;
- font-size: 12px;
+a.list-group-item-success .list-group-item-heading {
+ color: inherit;
}
-
-.pagination-sm > li:first-child > a,
-.pagination-sm > li:first-child > span {
- border-bottom-left-radius: 3px;
- border-top-left-radius: 3px;
+a.list-group-item-success:hover,
+a.list-group-item-success:focus {
+ color: #3c763d;
+ background-color: #d0e9c6;
}
-
-.pagination-sm > li:last-child > a,
-.pagination-sm > li:last-child > span {
- border-top-right-radius: 3px;
- border-bottom-right-radius: 3px;
+a.list-group-item-success.active,
+a.list-group-item-success.active:hover,
+a.list-group-item-success.active:focus {
+ color: #fff;
+ background-color: #3c763d;
+ border-color: #3c763d;
}
-
-.pager {
- padding-left: 0;
- margin: 20px 0;
- text-align: center;
- list-style: none;
+.list-group-item-info {
+ color: #31708f;
+ background-color: #d9edf7;
}
-
-.pager:before,
-.pager:after {
- display: table;
- content: " ";
+a.list-group-item-info {
+ color: #31708f;
}
-
-.pager:after {
- clear: both;
+a.list-group-item-info .list-group-item-heading {
+ color: inherit;
}
-
-.pager:before,
-.pager:after {
- display: table;
- content: " ";
+a.list-group-item-info:hover,
+a.list-group-item-info:focus {
+ color: #31708f;
+ background-color: #c4e3f3;
}
-
-.pager:after {
- clear: both;
+a.list-group-item-info.active,
+a.list-group-item-info.active:hover,
+a.list-group-item-info.active:focus {
+ color: #fff;
+ background-color: #31708f;
+ border-color: #31708f;
}
-
-.pager li {
- display: inline;
+.list-group-item-warning {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
}
-
-.pager li > a,
-.pager li > span {
- display: inline-block;
- padding: 5px 14px;
- background-color: #ffffff;
- border: 1px solid #dddddd;
- border-radius: 15px;
+a.list-group-item-warning {
+ color: #8a6d3b;
}
-
-.pager li > a:hover,
-.pager li > a:focus {
- text-decoration: none;
- background-color: #eeeeee;
+a.list-group-item-warning .list-group-item-heading {
+ color: inherit;
}
-
-.pager .next > a,
-.pager .next > span {
- float: right;
+a.list-group-item-warning:hover,
+a.list-group-item-warning:focus {
+ color: #8a6d3b;
+ background-color: #faf2cc;
}
-
-.pager .previous > a,
-.pager .previous > span {
- float: left;
+a.list-group-item-warning.active,
+a.list-group-item-warning.active:hover,
+a.list-group-item-warning.active:focus {
+ color: #fff;
+ background-color: #8a6d3b;
+ border-color: #8a6d3b;
}
-
-.pager .disabled > a,
-.pager .disabled > a:hover,
-.pager .disabled > a:focus,
-.pager .disabled > span {
- color: #999999;
- cursor: not-allowed;
+.list-group-item-danger {
+ color: #a94442;
+ background-color: #f2dede;
+}
+a.list-group-item-danger {
+ color: #a94442;
+}
+a.list-group-item-danger .list-group-item-heading {
+ color: inherit;
+}
+a.list-group-item-danger:hover,
+a.list-group-item-danger:focus {
+ color: #a94442;
+ background-color: #ebcccc;
+}
+a.list-group-item-danger.active,
+a.list-group-item-danger.active:hover,
+a.list-group-item-danger.active:focus {
+ color: #fff;
+ background-color: #a94442;
+ border-color: #a94442;
+}
+.list-group-item-heading {
+ margin-top: 0;
+ margin-bottom: 5px;
+}
+.list-group-item-text {
+ margin-bottom: 0;
+ line-height: 1.3;
+}
+.panel {
+ margin-bottom: 20px;
background-color: #ffffff;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+.panel-body {
+ padding: 15px;
+}
+.panel-heading {
+ padding: 10px 15px;
+ border-bottom: 1px solid transparent;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
+}
+.panel-heading > .dropdown .dropdown-toggle {
+ color: inherit;
+}
+.panel-title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 16px;
+ color: inherit;
}
-
-.label {
- display: inline;
- padding: .2em .6em .3em;
- font-size: 75%;
- font-weight: bold;
- line-height: 1;
- color: #ffffff;
- text-align: center;
- white-space: nowrap;
- vertical-align: baseline;
- border-radius: .25em;
+.panel-title > a {
+ color: inherit;
}
-
-.label[href]:hover,
-.label[href]:focus {
- color: #ffffff;
- text-decoration: none;
- cursor: pointer;
+.panel-footer {
+ padding: 10px 15px;
+ background-color: #f5f5f5;
+ border-top: 1px solid #dddddd;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
}
-
-.label:empty {
- display: none;
+.panel > .list-group,
+.panel > .panel-collapse > .list-group {
+ margin-bottom: 0;
}
-
-.label-default {
- background-color: #999999;
+.panel > .list-group .list-group-item,
+.panel > .panel-collapse > .list-group .list-group-item {
+ border-width: 1px 0;
+ border-radius: 0;
}
-
-.label-default[href]:hover,
-.label-default[href]:focus {
- background-color: #808080;
+.panel > .list-group:first-child .list-group-item:first-child,
+.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child {
+ border-top: 0;
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
}
-
-.label-primary {
- background-color: #428bca;
+.panel > .list-group:last-child .list-group-item:last-child,
+.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child {
+ border-bottom: 0;
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
}
-
-.label-primary[href]:hover,
-.label-primary[href]:focus {
- background-color: #3071a9;
+.panel-heading + .list-group .list-group-item:first-child {
+ border-top-width: 0;
}
-
-.label-success {
- background-color: #5cb85c;
+.list-group + .panel-footer {
+ border-top-width: 0;
}
-
-.label-success[href]:hover,
-.label-success[href]:focus {
- background-color: #449d44;
+.panel > .table,
+.panel > .table-responsive > .table,
+.panel > .panel-collapse > .table {
+ margin-bottom: 0;
}
-
-.label-info {
- background-color: #5bc0de;
+.panel > .table caption,
+.panel > .table-responsive > .table caption,
+.panel > .panel-collapse > .table caption {
+ padding-left: 15px;
+ padding-right: 15px;
}
-
-.label-info[href]:hover,
-.label-info[href]:focus {
- background-color: #31b0d5;
+.panel > .table:first-child,
+.panel > .table-responsive:first-child > .table:first-child {
+ border-top-right-radius: 3px;
+ border-top-left-radius: 3px;
}
-
-.label-warning {
- background-color: #f0ad4e;
+.panel > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child {
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
}
-
-.label-warning[href]:hover,
-.label-warning[href]:focus {
- background-color: #ec971f;
+.panel > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child {
+ border-top-left-radius: 3px;
}
-
-.label-danger {
- background-color: #d9534f;
+.panel > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child,
+.panel > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child,
+.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child,
+.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child {
+ border-top-right-radius: 3px;
}
-
-.label-danger[href]:hover,
-.label-danger[href]:focus {
- background-color: #c9302c;
+.panel > .table:last-child,
+.panel > .table-responsive:last-child > .table:last-child {
+ border-bottom-right-radius: 3px;
+ border-bottom-left-radius: 3px;
}
-
-.badge {
- display: inline-block;
- min-width: 10px;
- padding: 3px 7px;
- font-size: 12px;
- font-weight: bold;
- line-height: 1;
- color: #ffffff;
- text-align: center;
- white-space: nowrap;
- vertical-align: baseline;
- background-color: #999999;
- border-radius: 10px;
+.panel > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child {
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
}
-
-.badge:empty {
- display: none;
+.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child {
+ border-bottom-left-radius: 3px;
}
-
-a.badge:hover,
-a.badge:focus {
- color: #ffffff;
- text-decoration: none;
- cursor: pointer;
+.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child,
+.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child,
+.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child,
+.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child {
+ border-bottom-right-radius: 3px;
}
-
-.btn .badge {
- position: relative;
- top: -1px;
+.panel > .panel-body + .table,
+.panel > .panel-body + .table-responsive,
+.panel > .table + .panel-body,
+.panel > .table-responsive + .panel-body {
+ border-top: 1px solid #dddddd;
}
-
-a.list-group-item.active > .badge,
-.nav-pills > .active > a > .badge {
- color: #428bca;
- background-color: #ffffff;
+.panel > .table > tbody:first-child > tr:first-child th,
+.panel > .table > tbody:first-child > tr:first-child td {
+ border-top: 0;
}
-
-.nav-pills > li > a > .badge {
- margin-left: 3px;
+.panel > .table-bordered,
+.panel > .table-responsive > .table-bordered {
+ border: 0;
}
-
-.jumbotron {
- padding: 30px;
- margin-bottom: 30px;
- font-size: 21px;
- font-weight: 200;
- line-height: 2.1428571435;
- color: inherit;
- background-color: #eeeeee;
+.panel > .table-bordered > thead > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:first-child,
+.panel > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child,
+.panel > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child,
+.panel > .table-bordered > thead > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:first-child,
+.panel > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child,
+.panel > .table-bordered > tfoot > tr > td:first-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child {
+ border-left: 0;
}
-
-.jumbotron h1 {
- line-height: 1;
- color: inherit;
+.panel > .table-bordered > thead > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > th:last-child,
+.panel > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child,
+.panel > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child,
+.panel > .table-bordered > thead > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > thead > tr > td:last-child,
+.panel > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child,
+.panel > .table-bordered > tfoot > tr > td:last-child,
+.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child {
+ border-right: 0;
}
-
-.jumbotron p {
- line-height: 1.4;
+.panel > .table-bordered > thead > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > td,
+.panel > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td,
+.panel > .table-bordered > thead > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > thead > tr:first-child > th,
+.panel > .table-bordered > tbody > tr:first-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th {
+ border-bottom: 0;
}
-
-.container .jumbotron {
- border-radius: 6px;
+.panel > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td,
+.panel > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td,
+.panel > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th,
+.panel > .table-bordered > tfoot > tr:last-child > th,
+.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th {
+ border-bottom: 0;
}
-
-@media screen and (min-width: 768px) {
- .jumbotron {
- padding-top: 48px;
- padding-bottom: 48px;
- }
- .container .jumbotron {
- padding-right: 60px;
- padding-left: 60px;
- }
- .jumbotron h1 {
- font-size: 63px;
- }
+.panel > .table-responsive {
+ border: 0;
+ margin-bottom: 0;
}
-
-.thumbnail {
- display: inline-block;
- display: block;
- height: auto;
- max-width: 100%;
- padding: 4px;
- line-height: 1.428571429;
- background-color: #ffffff;
- border: 1px solid #dddddd;
+.panel-group {
+ margin-bottom: 20px;
+}
+.panel-group .panel {
+ margin-bottom: 0;
border-radius: 4px;
- -webkit-transition: all 0.2s ease-in-out;
- transition: all 0.2s ease-in-out;
}
-
-.thumbnail > img {
- display: block;
- height: auto;
- max-width: 100%;
+.panel-group .panel + .panel {
+ margin-top: 5px;
}
-
-a.thumbnail:hover,
-a.thumbnail:focus {
- border-color: #428bca;
+.panel-group .panel-heading {
+ border-bottom: 0;
}
-
-.thumbnail > img {
- margin-right: auto;
- margin-left: auto;
+.panel-group .panel-heading + .panel-collapse > .panel-body,
+.panel-group .panel-heading + .panel-collapse > .list-group {
+ border-top: 1px solid #dddddd;
}
-
-.thumbnail .caption {
- padding: 9px;
+.panel-group .panel-footer {
+ border-top: 0;
+}
+.panel-group .panel-footer + .panel-collapse .panel-body {
+ border-bottom: 1px solid #dddddd;
+}
+.panel-default {
+ border-color: #dddddd;
+}
+.panel-default > .panel-heading {
color: #333333;
+ background-color: #f5f5f5;
+ border-color: #dddddd;
}
-
-.alert {
- padding: 15px;
- margin-bottom: 20px;
- border: 1px solid transparent;
- border-radius: 4px;
+.panel-default > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #dddddd;
}
-
-.alert h4 {
- margin-top: 0;
- color: inherit;
+.panel-default > .panel-heading .badge {
+ color: #f5f5f5;
+ background-color: #333333;
}
-
-.alert .alert-link {
- font-weight: bold;
+.panel-default > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #dddddd;
}
-
-.alert > p,
-.alert > ul {
- margin-bottom: 0;
+.panel-primary {
+ border-color: #428bca;
}
-
-.alert > p + p {
- margin-top: 5px;
+.panel-primary > .panel-heading {
+ color: #ffffff;
+ background-color: #428bca;
+ border-color: #428bca;
}
-
-.alert-dismissable {
- padding-right: 35px;
+.panel-primary > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #428bca;
}
-
-.alert-dismissable .close {
- position: relative;
- top: -2px;
- right: -21px;
- color: inherit;
+.panel-primary > .panel-heading .badge {
+ color: #428bca;
+ background-color: #ffffff;
}
-
-.alert-success {
- color: #468847;
+.panel-primary > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #428bca;
+}
+.panel-success {
+ border-color: #d6e9c6;
+}
+.panel-success > .panel-heading {
+ color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
-
-.alert-success hr {
- border-top-color: #c9e2b3;
+.panel-success > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #d6e9c6;
+}
+.panel-success > .panel-heading .badge {
+ color: #dff0d8;
+ background-color: #3c763d;
+}
+.panel-success > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #d6e9c6;
}
-
-.alert-success .alert-link {
- color: #356635;
+.panel-info {
+ border-color: #bce8f1;
}
-
-.alert-info {
- color: #3a87ad;
+.panel-info > .panel-heading {
+ color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
-
-.alert-info hr {
- border-top-color: #a6e1ec;
+.panel-info > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #bce8f1;
}
-
-.alert-info .alert-link {
- color: #2d6987;
+.panel-info > .panel-heading .badge {
+ color: #d9edf7;
+ background-color: #31708f;
}
-
-.alert-warning {
- color: #c09853;
- background-color: #fcf8e3;
- border-color: #fbeed5;
+.panel-info > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #bce8f1;
}
-
-.alert-warning hr {
- border-top-color: #f8e5be;
+.panel-warning {
+ border-color: #faebcc;
}
-
-.alert-warning .alert-link {
- color: #a47e3c;
+.panel-warning > .panel-heading {
+ color: #8a6d3b;
+ background-color: #fcf8e3;
+ border-color: #faebcc;
}
-
-.alert-danger {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #eed3d7;
+.panel-warning > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #faebcc;
}
-
-.alert-danger hr {
- border-top-color: #e6c1c7;
+.panel-warning > .panel-heading .badge {
+ color: #fcf8e3;
+ background-color: #8a6d3b;
}
-
-.alert-danger .alert-link {
- color: #953b39;
+.panel-warning > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #faebcc;
}
-
-@-webkit-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
+.panel-danger {
+ border-color: #ebccd1;
}
-
-@-moz-keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
+.panel-danger > .panel-heading {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1;
}
-
-@-o-keyframes progress-bar-stripes {
- from {
- background-position: 0 0;
- }
- to {
- background-position: 40px 0;
- }
+.panel-danger > .panel-heading + .panel-collapse > .panel-body {
+ border-top-color: #ebccd1;
}
-
-@keyframes progress-bar-stripes {
- from {
- background-position: 40px 0;
- }
- to {
- background-position: 0 0;
- }
+.panel-danger > .panel-heading .badge {
+ color: #f2dede;
+ background-color: #a94442;
}
-
-.progress {
- height: 20px;
- margin-bottom: 20px;
+.panel-danger > .panel-footer + .panel-collapse > .panel-body {
+ border-bottom-color: #ebccd1;
+}
+.embed-responsive {
+ position: relative;
+ display: block;
+ height: 0;
+ padding: 0;
overflow: hidden;
- background-color: #f5f5f5;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
}
-
-.progress-bar {
- float: left;
- width: 0;
+.embed-responsive .embed-responsive-item,
+.embed-responsive iframe,
+.embed-responsive embed,
+.embed-responsive object,
+.embed-responsive video {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
height: 100%;
- font-size: 12px;
- color: #ffffff;
- text-align: center;
- background-color: #428bca;
- -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
- -webkit-transition: width 0.6s ease;
- transition: width 0.6s ease;
-}
-
-.progress-striped .progress-bar {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-size: 40px 40px;
+ width: 100%;
+ border: 0;
}
-
-.progress.active .progress-bar {
- -webkit-animation: progress-bar-stripes 2s linear infinite;
- -moz-animation: progress-bar-stripes 2s linear infinite;
- -ms-animation: progress-bar-stripes 2s linear infinite;
- -o-animation: progress-bar-stripes 2s linear infinite;
- animation: progress-bar-stripes 2s linear infinite;
+.embed-responsive.embed-responsive-16by9 {
+ padding-bottom: 56.25%;
}
-
-.progress-bar-success {
- background-color: #5cb85c;
+.embed-responsive.embed-responsive-4by3 {
+ padding-bottom: 75%;
}
-
-.progress-striped .progress-bar-success {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+.well {
+ min-height: 20px;
+ padding: 19px;
+ margin-bottom: 20px;
+ background-color: #f5f5f5;
+ border: 1px solid #e3e3e3;
+ border-radius: 4px;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
}
-
-.progress-bar-info {
- background-color: #5bc0de;
+.well blockquote {
+ border-color: #ddd;
+ border-color: rgba(0, 0, 0, 0.15);
}
-
-.progress-striped .progress-bar-info {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+.well-lg {
+ padding: 24px;
+ border-radius: 6px;
}
-
-.progress-bar-warning {
- background-color: #f0ad4e;
+.well-sm {
+ padding: 9px;
+ border-radius: 3px;
}
-
-.progress-striped .progress-bar-warning {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+.close {
+ float: right;
+ font-size: 21px;
+ font-weight: bold;
+ line-height: 1;
+ color: #000000;
+ text-shadow: 0 1px 0 #ffffff;
+ opacity: 0.2;
+ filter: alpha(opacity=20);
}
-
-.progress-bar-danger {
- background-color: #d9534f;
+.close:hover,
+.close:focus {
+ color: #000000;
+ text-decoration: none;
+ cursor: pointer;
+ opacity: 0.5;
+ filter: alpha(opacity=50);
}
-
-.progress-striped .progress-bar-danger {
- background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
- background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
- background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+button.close {
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+ -webkit-appearance: none;
}
-
-.media,
-.media-body {
+.modal-open {
overflow: hidden;
- zoom: 1;
-}
-
-.media,
-.media .media {
- margin-top: 15px;
}
-
-.media:first-child {
- margin-top: 0;
-}
-
-.media-object {
- display: block;
-}
-
-.media-heading {
- margin: 0 0 5px;
+.modal {
+ display: none;
+ overflow: hidden;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ -webkit-overflow-scrolling: touch;
+ outline: 0;
}
-
-.media > .pull-left {
- margin-right: 10px;
+.modal.fade .modal-dialog {
+ -webkit-transform: translate(0, -25%);
+ -ms-transform: translate(0, -25%);
+ -o-transform: translate(0, -25%);
+ transform: translate(0, -25%);
+ -webkit-transition: -webkit-transform 0.3s ease-out;
+ -moz-transition: -moz-transform 0.3s ease-out;
+ -o-transition: -o-transform 0.3s ease-out;
+ transition: transform 0.3s ease-out;
}
-
-.media > .pull-right {
- margin-left: 10px;
+.modal.in .modal-dialog {
+ -webkit-transform: translate(0, 0);
+ -ms-transform: translate(0, 0);
+ -o-transform: translate(0, 0);
+ transform: translate(0, 0);
}
-
-.media-list {
- padding-left: 0;
- list-style: none;
+.modal-open .modal {
+ overflow-x: hidden;
+ overflow-y: auto;
}
-
-.list-group {
- padding-left: 0;
- margin-bottom: 20px;
+.modal-dialog {
+ position: relative;
+ width: auto;
+ margin: 10px;
}
-
-.list-group-item {
+.modal-content {
position: relative;
- display: block;
- padding: 10px 15px;
- margin-bottom: -1px;
background-color: #ffffff;
- border: 1px solid #dddddd;
-}
-
-.list-group-item:first-child {
- border-top-right-radius: 4px;
- border-top-left-radius: 4px;
-}
-
-.list-group-item:last-child {
- margin-bottom: 0;
- border-bottom-right-radius: 4px;
- border-bottom-left-radius: 4px;
-}
-
-.list-group-item > .badge {
- float: right;
+ border: 1px solid #999999;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
+ background-clip: padding-box;
+ outline: 0;
}
-
-.list-group-item > .badge + .badge {
- margin-right: 5px;
+.modal-backdrop {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ background-color: #000000;
}
-
-a.list-group-item {
- color: #555555;
+.modal-backdrop.fade {
+ opacity: 0;
+ filter: alpha(opacity=0);
}
-
-a.list-group-item .list-group-item-heading {
- color: #333333;
+.modal-backdrop.in {
+ opacity: 0.5;
+ filter: alpha(opacity=50);
}
-
-a.list-group-item:hover,
-a.list-group-item:focus {
- text-decoration: none;
- background-color: #f5f5f5;
+.modal-header {
+ padding: 15px;
+ border-bottom: 1px solid #e5e5e5;
+ min-height: 16.42857143px;
}
-
-.list-group-item.active,
-.list-group-item.active:hover,
-.list-group-item.active:focus {
- z-index: 2;
- color: #ffffff;
- background-color: #428bca;
- border-color: #428bca;
+.modal-header .close {
+ margin-top: -2px;
}
-
-.list-group-item.active .list-group-item-heading,
-.list-group-item.active:hover .list-group-item-heading,
-.list-group-item.active:focus .list-group-item-heading {
- color: inherit;
+.modal-title {
+ margin: 0;
+ line-height: 1.42857143;
}
-
-.list-group-item.active .list-group-item-text,
-.list-group-item.active:hover .list-group-item-text,
-.list-group-item.active:focus .list-group-item-text {
- color: #e1edf7;
+.modal-body {
+ position: relative;
+ padding: 15px;
}
-
-.list-group-item-heading {
- margin-top: 0;
- margin-bottom: 5px;
+.modal-footer {
+ padding: 15px;
+ text-align: right;
+ border-top: 1px solid #e5e5e5;
}
-
-.list-group-item-text {
+.modal-footer .btn + .btn {
+ margin-left: 5px;
margin-bottom: 0;
- line-height: 1.3;
}
-
-.panel {
- margin-bottom: 20px;
- background-color: #ffffff;
- border: 1px solid transparent;
- border-radius: 4px;
- -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05);
+.modal-footer .btn-group .btn + .btn {
+ margin-left: -1px;
}
-
-.panel-body {
- padding: 15px;
+.modal-footer .btn-block + .btn-block {
+ margin-left: 0;
}
-
-.panel-body:before,
-.panel-body:after {
- display: table;
- content: " ";
+.modal-scrollbar-measure {
+ position: absolute;
+ top: -9999px;
+ width: 50px;
+ height: 50px;
+ overflow: scroll;
}
-
-.panel-body:after {
- clear: both;
+@media (min-width: 768px) {
+ .modal-dialog {
+ width: 600px;
+ margin: 30px auto;
+ }
+ .modal-content {
+ -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
+ }
+ .modal-sm {
+ width: 300px;
+ }
}
-
-.panel-body:before,
-.panel-body:after {
- display: table;
- content: " ";
+@media (min-width: 992px) {
+ .modal-lg {
+ width: 900px;
+ }
}
-
-.panel-body:after {
- clear: both;
+.tooltip {
+ position: absolute;
+ z-index: 1070;
+ display: block;
+ visibility: visible;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 12px;
+ font-weight: normal;
+ line-height: 1.4;
+ opacity: 0;
+ filter: alpha(opacity=0);
}
-
-.panel > .list-group {
- margin-bottom: 0;
+.tooltip.in {
+ opacity: 0.9;
+ filter: alpha(opacity=90);
}
-
-.panel > .list-group .list-group-item {
- border-width: 1px 0;
+.tooltip.top {
+ margin-top: -3px;
+ padding: 5px 0;
}
-
-.panel > .list-group .list-group-item:first-child {
- border-top-right-radius: 0;
- border-top-left-radius: 0;
+.tooltip.right {
+ margin-left: 3px;
+ padding: 0 5px;
}
-
-.panel > .list-group .list-group-item:last-child {
- border-bottom: 0;
+.tooltip.bottom {
+ margin-top: 3px;
+ padding: 5px 0;
}
-
-.panel-heading + .list-group .list-group-item:first-child {
- border-top-width: 0;
+.tooltip.left {
+ margin-left: -3px;
+ padding: 0 5px;
}
-
-.panel > .table {
- margin-bottom: 0;
+.tooltip-inner {
+ max-width: 200px;
+ padding: 3px 8px;
+ color: #ffffff;
+ text-align: center;
+ text-decoration: none;
+ background-color: #000000;
+ border-radius: 4px;
}
-
-.panel > .panel-body + .table {
- border-top: 1px solid #dddddd;
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
}
-
-.panel-heading {
- padding: 10px 15px;
- border-bottom: 1px solid transparent;
- border-top-right-radius: 3px;
- border-top-left-radius: 3px;
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
}
-
-.panel-title {
- margin-top: 0;
- margin-bottom: 0;
- font-size: 16px;
+.tooltip.top-left .tooltip-arrow {
+ bottom: 0;
+ right: 5px;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
}
-
-.panel-title > a {
- color: inherit;
+.tooltip.top-right .tooltip-arrow {
+ bottom: 0;
+ left: 5px;
+ margin-bottom: -5px;
+ border-width: 5px 5px 0;
+ border-top-color: #000000;
}
-
-.panel-footer {
- padding: 10px 15px;
- background-color: #f5f5f5;
- border-top: 1px solid #dddddd;
- border-bottom-right-radius: 3px;
- border-bottom-left-radius: 3px;
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ margin-top: -5px;
+ border-width: 5px 5px 5px 0;
+ border-right-color: #000000;
}
-
-.panel-group .panel {
- margin-bottom: 0;
- overflow: hidden;
- border-radius: 4px;
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ margin-top: -5px;
+ border-width: 5px 0 5px 5px;
+ border-left-color: #000000;
}
-
-.panel-group .panel + .panel {
- margin-top: 5px;
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ margin-left: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
}
-
-.panel-group .panel-heading {
- border-bottom: 0;
+.tooltip.bottom-left .tooltip-arrow {
+ top: 0;
+ right: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
}
-
-.panel-group .panel-heading + .panel-collapse .panel-body {
- border-top: 1px solid #dddddd;
+.tooltip.bottom-right .tooltip-arrow {
+ top: 0;
+ left: 5px;
+ margin-top: -5px;
+ border-width: 0 5px 5px;
+ border-bottom-color: #000000;
}
-
-.panel-group .panel-footer {
- border-top: 0;
+.popover {
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1060;
+ display: none;
+ max-width: 276px;
+ padding: 1px;
+ font-family: 'Open Sans', sans-serif;
+ font-size: 14px;
+ font-weight: normal;
+ line-height: 1.42857143;
+ text-align: left;
+ background-color: #ffffff;
+ background-clip: padding-box;
+ border: 1px solid #cccccc;
+ border: 1px solid rgba(0, 0, 0, 0.2);
+ border-radius: 6px;
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+ white-space: normal;
}
-
-.panel-group .panel-footer + .panel-collapse .panel-body {
- border-bottom: 1px solid #dddddd;
+.popover.top {
+ margin-top: -10px;
}
-
-.panel-default {
- border-color: #dddddd;
+.popover.right {
+ margin-left: 10px;
}
-
-.panel-default > .panel-heading {
- color: #333333;
- background-color: #f5f5f5;
- border-color: #dddddd;
+.popover.bottom {
+ margin-top: 10px;
}
-
-.panel-default > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #dddddd;
+.popover.left {
+ margin-left: -10px;
}
-
-.panel-default > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #dddddd;
+.popover-title {
+ margin: 0;
+ padding: 8px 14px;
+ font-size: 14px;
+ background-color: #f7f7f7;
+ border-bottom: 1px solid #ebebeb;
+ border-radius: 5px 5px 0 0;
}
-
-.panel-primary {
- border-color: #428bca;
+.popover-content {
+ padding: 9px 14px;
}
-
-.panel-primary > .panel-heading {
- color: #ffffff;
- background-color: #428bca;
- border-color: #428bca;
+.popover > .arrow,
+.popover > .arrow:after {
+ position: absolute;
+ display: block;
+ width: 0;
+ height: 0;
+ border-color: transparent;
+ border-style: solid;
}
-
-.panel-primary > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #428bca;
+.popover > .arrow {
+ border-width: 11px;
}
-
-.panel-primary > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #428bca;
+.popover > .arrow:after {
+ border-width: 10px;
+ content: "";
}
-
-.panel-success {
- border-color: #d6e9c6;
+.popover.top > .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-bottom-width: 0;
+ border-top-color: #999999;
+ border-top-color: rgba(0, 0, 0, 0.25);
+ bottom: -11px;
}
-
-.panel-success > .panel-heading {
- color: #468847;
- background-color: #dff0d8;
- border-color: #d6e9c6;
+.popover.top > .arrow:after {
+ content: " ";
+ bottom: 1px;
+ margin-left: -10px;
+ border-bottom-width: 0;
+ border-top-color: #ffffff;
}
-
-.panel-success > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #d6e9c6;
+.popover.right > .arrow {
+ top: 50%;
+ left: -11px;
+ margin-top: -11px;
+ border-left-width: 0;
+ border-right-color: #999999;
+ border-right-color: rgba(0, 0, 0, 0.25);
}
-
-.panel-success > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #d6e9c6;
+.popover.right > .arrow:after {
+ content: " ";
+ left: 1px;
+ bottom: -10px;
+ border-left-width: 0;
+ border-right-color: #ffffff;
}
-
-.panel-warning {
- border-color: #fbeed5;
+.popover.bottom > .arrow {
+ left: 50%;
+ margin-left: -11px;
+ border-top-width: 0;
+ border-bottom-color: #999999;
+ border-bottom-color: rgba(0, 0, 0, 0.25);
+ top: -11px;
}
-
-.panel-warning > .panel-heading {
- color: #c09853;
- background-color: #fcf8e3;
- border-color: #fbeed5;
+.popover.bottom > .arrow:after {
+ content: " ";
+ top: 1px;
+ margin-left: -10px;
+ border-top-width: 0;
+ border-bottom-color: #ffffff;
}
-
-.panel-warning > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #fbeed5;
+.popover.left > .arrow {
+ top: 50%;
+ right: -11px;
+ margin-top: -11px;
+ border-right-width: 0;
+ border-left-color: #999999;
+ border-left-color: rgba(0, 0, 0, 0.25);
}
-
-.panel-warning > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #fbeed5;
+.popover.left > .arrow:after {
+ content: " ";
+ right: 1px;
+ border-right-width: 0;
+ border-left-color: #ffffff;
+ bottom: -10px;
}
-
-.panel-danger {
- border-color: #eed3d7;
+.carousel {
+ position: relative;
}
-
-.panel-danger > .panel-heading {
- color: #b94a48;
- background-color: #f2dede;
- border-color: #eed3d7;
+.carousel-inner {
+ position: relative;
+ overflow: hidden;
+ width: 100%;
}
-
-.panel-danger > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #eed3d7;
+.carousel-inner > .item {
+ display: none;
+ position: relative;
+ -webkit-transition: 0.6s ease-in-out left;
+ -o-transition: 0.6s ease-in-out left;
+ transition: 0.6s ease-in-out left;
}
-
-.panel-danger > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #eed3d7;
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+ line-height: 1;
}
-
-.panel-info {
- border-color: #bce8f1;
+@media all and (transform-3d), (-webkit-transform-3d) {
+ .carousel-inner > .item {
+ transition: transform 0.6s ease-in-out;
+ backface-visibility: hidden;
+ perspective: 1000;
+ }
+ .carousel-inner > .item.next,
+ .carousel-inner > .item.active.right {
+ transform: translate3d(100%, 0, 0);
+ left: 0;
+ }
+ .carousel-inner > .item.prev,
+ .carousel-inner > .item.active.left {
+ transform: translate3d(-100%, 0, 0);
+ left: 0;
+ }
+ .carousel-inner > .item.next.left,
+ .carousel-inner > .item.prev.right,
+ .carousel-inner > .item.active {
+ transform: translate3d(0, 0, 0);
+ left: 0;
+ }
}
-
-.panel-info > .panel-heading {
- color: #3a87ad;
- background-color: #d9edf7;
- border-color: #bce8f1;
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ display: block;
}
-
-.panel-info > .panel-heading + .panel-collapse .panel-body {
- border-top-color: #bce8f1;
+.carousel-inner > .active {
+ left: 0;
}
-
-.panel-info > .panel-footer + .panel-collapse .panel-body {
- border-bottom-color: #bce8f1;
+.carousel-inner > .next,
+.carousel-inner > .prev {
+ position: absolute;
+ top: 0;
+ width: 100%;
}
-
-.well {
- min-height: 20px;
- padding: 19px;
- margin-bottom: 20px;
- background-color: #f5f5f5;
- border: 1px solid #e3e3e3;
- border-radius: 4px;
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+.carousel-inner > .next {
+ left: 100%;
}
-
-.well blockquote {
- border-color: #ddd;
- border-color: rgba(0, 0, 0, 0.15);
+.carousel-inner > .prev {
+ left: -100%;
}
-
-.well-lg {
- padding: 24px;
- border-radius: 6px;
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+ left: 0;
}
-
-.well-sm {
- padding: 9px;
- border-radius: 3px;
+.carousel-inner > .active.left {
+ left: -100%;
}
-
-.close {
- float: right;
- font-size: 21px;
- font-weight: bold;
- line-height: 1;
- color: #000000;
- text-shadow: 0 1px 0 #ffffff;
- opacity: 0.2;
- filter: alpha(opacity=20);
+.carousel-inner > .active.right {
+ left: 100%;
}
-
-.close:hover,
-.close:focus {
- color: #000000;
- text-decoration: none;
- cursor: pointer;
+.carousel-control {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 15%;
opacity: 0.5;
filter: alpha(opacity=50);
+ font-size: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
-
-button.close {
- padding: 0;
- cursor: pointer;
- background: transparent;
- border: 0;
- -webkit-appearance: none;
-}
-
-.modal-open {
- overflow: hidden;
-}
-
-body.modal-open,
-.modal-open .navbar-fixed-top,
-.modal-open .navbar-fixed-bottom {
- margin-right: 15px;
+.carousel-control.left {
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0%, rgba(0, 0, 0, 0.0001) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
}
-
-.modal {
- position: fixed;
- top: 0;
+.carousel-control.right {
+ left: auto;
right: 0;
- bottom: 0;
- left: 0;
- z-index: 1040;
- display: none;
- overflow: auto;
- overflow-y: scroll;
+ background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-image: -o-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0%, rgba(0, 0, 0, 0.5) 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
}
-
-.modal.fade .modal-dialog {
- -webkit-transform: translate(0, -25%);
- -ms-transform: translate(0, -25%);
- transform: translate(0, -25%);
- -webkit-transition: -webkit-transform 0.3s ease-out;
- -moz-transition: -moz-transform 0.3s ease-out;
- -o-transition: -o-transform 0.3s ease-out;
- transition: transform 0.3s ease-out;
+.carousel-control:hover,
+.carousel-control:focus {
+ outline: 0;
+ color: #ffffff;
+ text-decoration: none;
+ opacity: 0.9;
+ filter: alpha(opacity=90);
}
-
-.modal.in .modal-dialog {
- -webkit-transform: translate(0, 0);
- -ms-transform: translate(0, 0);
- transform: translate(0, 0);
+.carousel-control .icon-prev,
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-left,
+.carousel-control .glyphicon-chevron-right {
+ position: absolute;
+ top: 50%;
+ z-index: 5;
+ display: inline-block;
}
-
-.modal-dialog {
- z-index: 1050;
- width: auto;
- padding: 10px;
- margin-right: auto;
- margin-left: auto;
+.carousel-control .icon-prev,
+.carousel-control .glyphicon-chevron-left {
+ left: 50%;
+ margin-left: -10px;
}
-
-.modal-content {
- position: relative;
- background-color: #ffffff;
- border: 1px solid #999999;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 6px;
- outline: none;
- -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
- box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5);
- background-clip: padding-box;
+.carousel-control .icon-next,
+.carousel-control .glyphicon-chevron-right {
+ right: 50%;
+ margin-right: -10px;
}
-
-.modal-backdrop {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1030;
- background-color: #000000;
+.carousel-control .icon-prev,
+.carousel-control .icon-next {
+ width: 20px;
+ height: 20px;
+ margin-top: -10px;
+ font-family: serif;
}
-
-.modal-backdrop.fade {
- opacity: 0;
- filter: alpha(opacity=0);
+.carousel-control .icon-prev:before {
+ content: '\2039';
}
-
-.modal-backdrop.in {
- opacity: 0.5;
- filter: alpha(opacity=50);
+.carousel-control .icon-next:before {
+ content: '\203a';
}
-
-.modal-header {
- min-height: 16.428571429px;
- padding: 15px;
- border-bottom: 1px solid #e5e5e5;
+.carousel-indicators {
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ z-index: 15;
+ width: 60%;
+ margin-left: -30%;
+ padding-left: 0;
+ list-style: none;
+ text-align: center;
}
-
-.modal-header .close {
- margin-top: -2px;
+.carousel-indicators li {
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ margin: 1px;
+ text-indent: -999px;
+ border: 1px solid #ffffff;
+ border-radius: 10px;
+ cursor: pointer;
+ background-color: #000 \9;
+ background-color: rgba(0, 0, 0, 0);
}
-
-.modal-title {
+.carousel-indicators .active {
margin: 0;
- line-height: 1.428571429;
-}
-
-.modal-body {
- position: relative;
- padding: 20px;
+ width: 12px;
+ height: 12px;
+ background-color: #ffffff;
}
-
-.modal-footer {
- padding: 19px 20px 20px;
- margin-top: 15px;
- text-align: right;
- border-top: 1px solid #e5e5e5;
+.carousel-caption {
+ position: absolute;
+ left: 15%;
+ right: 15%;
+ bottom: 20px;
+ z-index: 10;
+ padding-top: 20px;
+ padding-bottom: 20px;
+ color: #ffffff;
+ text-align: center;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
}
-
-.modal-footer:before,
-.modal-footer:after {
- display: table;
- content: " ";
+.carousel-caption .btn {
+ text-shadow: none;
}
-
-.modal-footer:after {
- clear: both;
+@media screen and (min-width: 768px) {
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-prev,
+ .carousel-control .icon-next {
+ width: 30px;
+ height: 30px;
+ margin-top: -15px;
+ font-size: 30px;
+ }
+ .carousel-control .glyphicon-chevron-left,
+ .carousel-control .icon-prev {
+ margin-left: -15px;
+ }
+ .carousel-control .glyphicon-chevron-right,
+ .carousel-control .icon-next {
+ margin-right: -15px;
+ }
+ .carousel-caption {
+ left: 20%;
+ right: 20%;
+ padding-bottom: 30px;
+ }
+ .carousel-indicators {
+ bottom: 20px;
+ }
}
-
+.clearfix:before,
+.clearfix:after,
+.dl-horizontal dd:before,
+.dl-horizontal dd:after,
+.container:before,
+.container:after,
+.container-fluid:before,
+.container-fluid:after,
+.row:before,
+.row:after,
+.form-horizontal .form-group:before,
+.form-horizontal .form-group:after,
+.btn-toolbar:before,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:before,
+.btn-group-vertical > .btn-group:after,
+.nav:before,
+.nav:after,
+.navbar:before,
+.navbar:after,
+.navbar-header:before,
+.navbar-header:after,
+.navbar-collapse:before,
+.navbar-collapse:after,
+.pager:before,
+.pager:after,
+.panel-body:before,
+.panel-body:after,
.modal-footer:before,
.modal-footer:after {
- display: table;
content: " ";
+ display: table;
}
-
+.clearfix:after,
+.dl-horizontal dd:after,
+.container:after,
+.container-fluid:after,
+.row:after,
+.form-horizontal .form-group:after,
+.btn-toolbar:after,
+.btn-group-vertical > .btn-group:after,
+.nav:after,
+.navbar:after,
+.navbar-header:after,
+.navbar-collapse:after,
+.pager:after,
+.panel-body:after,
.modal-footer:after {
clear: both;
}
-
-.modal-footer .btn + .btn {
- margin-bottom: 0;
- margin-left: 5px;
-}
-
-.modal-footer .btn-group .btn + .btn {
- margin-left: -1px;
-}
-
-.modal-footer .btn-block + .btn-block {
- margin-left: 0;
+.center-block {
+ display: block;
+ margin-left: auto;
+ margin-right: auto;
}
-
-@media screen and (min-width: 768px) {
- .modal-dialog {
- right: auto;
- left: 50%;
- width: 600px;
- padding-top: 30px;
- padding-bottom: 30px;
- }
- .modal-content {
- -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
- }
+.pull-right {
+ float: right !important;
}
-
-.tooltip {
- position: absolute;
- z-index: 1030;
- display: block;
- font-size: 12px;
- line-height: 1.4;
- opacity: 0;
- filter: alpha(opacity=0);
- visibility: visible;
+.pull-left {
+ float: left !important;
}
-
-.tooltip.in {
- opacity: 0.9;
- filter: alpha(opacity=90);
+.hide {
+ display: none !important;
}
-
-.tooltip.top {
- padding: 5px 0;
- margin-top: -3px;
+.show {
+ display: block !important;
}
-
-.tooltip.right {
- padding: 0 5px;
- margin-left: 3px;
+.invisible {
+ visibility: hidden;
}
-
-.tooltip.bottom {
- padding: 5px 0;
- margin-top: 3px;
+.text-hide {
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
}
-
-.tooltip.left {
- padding: 0 5px;
- margin-left: -3px;
+.hidden {
+ display: none !important;
+ visibility: hidden !important;
}
-
-.tooltip-inner {
- max-width: 200px;
- padding: 3px 8px;
- color: #ffffff;
- text-align: center;
- text-decoration: none;
- background-color: #000000;
- border-radius: 4px;
+.affix {
+ position: fixed;
}
-
-.tooltip-arrow {
- position: absolute;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
+@-ms-viewport {
+ width: device-width;
}
-
-.tooltip.top .tooltip-arrow {
- bottom: 0;
- left: 50%;
- margin-left: -5px;
- border-top-color: #000000;
- border-width: 5px 5px 0;
+.visible-xs,
+.visible-sm,
+.visible-md,
+.visible-lg {
+ display: none !important;
}
-
-.tooltip.top-left .tooltip-arrow {
- bottom: 0;
- left: 5px;
- border-top-color: #000000;
- border-width: 5px 5px 0;
+.visible-xs-block,
+.visible-xs-inline,
+.visible-xs-inline-block,
+.visible-sm-block,
+.visible-sm-inline,
+.visible-sm-inline-block,
+.visible-md-block,
+.visible-md-inline,
+.visible-md-inline-block,
+.visible-lg-block,
+.visible-lg-inline,
+.visible-lg-inline-block {
+ display: none !important;
}
-
-.tooltip.top-right .tooltip-arrow {
- right: 5px;
- bottom: 0;
- border-top-color: #000000;
- border-width: 5px 5px 0;
+@media (max-width: 767px) {
+ .visible-xs {
+ display: block !important;
+ }
+ table.visible-xs {
+ display: table;
+ }
+ tr.visible-xs {
+ display: table-row !important;
+ }
+ th.visible-xs,
+ td.visible-xs {
+ display: table-cell !important;
+ }
}
-
-.tooltip.right .tooltip-arrow {
- top: 50%;
- left: 0;
- margin-top: -5px;
- border-right-color: #000000;
- border-width: 5px 5px 5px 0;
+@media (max-width: 767px) {
+ .visible-xs-block {
+ display: block !important;
+ }
}
-
-.tooltip.left .tooltip-arrow {
- top: 50%;
- right: 0;
- margin-top: -5px;
- border-left-color: #000000;
- border-width: 5px 0 5px 5px;
+@media (max-width: 767px) {
+ .visible-xs-inline {
+ display: inline !important;
+ }
}
-
-.tooltip.bottom .tooltip-arrow {
- top: 0;
- left: 50%;
- margin-left: -5px;
- border-bottom-color: #000000;
- border-width: 0 5px 5px;
+@media (max-width: 767px) {
+ .visible-xs-inline-block {
+ display: inline-block !important;
+ }
}
-
-.tooltip.bottom-left .tooltip-arrow {
- top: 0;
- left: 5px;
- border-bottom-color: #000000;
- border-width: 0 5px 5px;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm {
+ display: block !important;
+ }
+ table.visible-sm {
+ display: table;
+ }
+ tr.visible-sm {
+ display: table-row !important;
+ }
+ th.visible-sm,
+ td.visible-sm {
+ display: table-cell !important;
+ }
}
-
-.tooltip.bottom-right .tooltip-arrow {
- top: 0;
- right: 5px;
- border-bottom-color: #000000;
- border-width: 0 5px 5px;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm-block {
+ display: block !important;
+ }
}
-
-.popover {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 1010;
- display: none;
- max-width: 276px;
- padding: 1px;
- text-align: left;
- white-space: normal;
- background-color: #ffffff;
- border: 1px solid #cccccc;
- border: 1px solid rgba(0, 0, 0, 0.2);
- border-radius: 6px;
- -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
- background-clip: padding-box;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm-inline {
+ display: inline !important;
+ }
}
-
-.popover.top {
- margin-top: -10px;
+@media (min-width: 768px) and (max-width: 991px) {
+ .visible-sm-inline-block {
+ display: inline-block !important;
+ }
}
-
-.popover.right {
- margin-left: 10px;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md {
+ display: block !important;
+ }
+ table.visible-md {
+ display: table;
+ }
+ tr.visible-md {
+ display: table-row !important;
+ }
+ th.visible-md,
+ td.visible-md {
+ display: table-cell !important;
+ }
}
-
-.popover.bottom {
- margin-top: 10px;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md-block {
+ display: block !important;
+ }
}
-
-.popover.left {
- margin-left: -10px;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md-inline {
+ display: inline !important;
+ }
}
-
-.popover-title {
- padding: 8px 14px;
- margin: 0;
- font-size: 14px;
- font-weight: normal;
- line-height: 18px;
- background-color: #f7f7f7;
- border-bottom: 1px solid #ebebeb;
- border-radius: 5px 5px 0 0;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .visible-md-inline-block {
+ display: inline-block !important;
+ }
}
-
-.popover-content {
- padding: 9px 14px;
+@media (min-width: 1200px) {
+ .visible-lg {
+ display: block !important;
+ }
+ table.visible-lg {
+ display: table;
+ }
+ tr.visible-lg {
+ display: table-row !important;
+ }
+ th.visible-lg,
+ td.visible-lg {
+ display: table-cell !important;
+ }
}
-
-.popover .arrow,
-.popover .arrow:after {
- position: absolute;
- display: block;
- width: 0;
- height: 0;
- border-color: transparent;
- border-style: solid;
+@media (min-width: 1200px) {
+ .visible-lg-block {
+ display: block !important;
+ }
}
-
-.popover .arrow {
- border-width: 11px;
+@media (min-width: 1200px) {
+ .visible-lg-inline {
+ display: inline !important;
+ }
}
-
-.popover .arrow:after {
- border-width: 10px;
- content: "";
+@media (min-width: 1200px) {
+ .visible-lg-inline-block {
+ display: inline-block !important;
+ }
}
-
-.popover.top .arrow {
- bottom: -11px;
- left: 50%;
- margin-left: -11px;
- border-top-color: #999999;
- border-top-color: rgba(0, 0, 0, 0.25);
- border-bottom-width: 0;
+@media (max-width: 767px) {
+ .hidden-xs {
+ display: none !important;
+ }
}
-
-.popover.top .arrow:after {
- bottom: 1px;
- margin-left: -10px;
- border-top-color: #ffffff;
- border-bottom-width: 0;
- content: " ";
+@media (min-width: 768px) and (max-width: 991px) {
+ .hidden-sm {
+ display: none !important;
+ }
}
-
-.popover.right .arrow {
- top: 50%;
- left: -11px;
- margin-top: -11px;
- border-right-color: #999999;
- border-right-color: rgba(0, 0, 0, 0.25);
- border-left-width: 0;
+@media (min-width: 992px) and (max-width: 1199px) {
+ .hidden-md {
+ display: none !important;
+ }
}
-
-.popover.right .arrow:after {
- bottom: -10px;
- left: 1px;
- border-right-color: #ffffff;
- border-left-width: 0;
- content: " ";
+@media (min-width: 1200px) {
+ .hidden-lg {
+ display: none !important;
+ }
}
-
-.popover.bottom .arrow {
- top: -11px;
- left: 50%;
- margin-left: -11px;
- border-bottom-color: #999999;
- border-bottom-color: rgba(0, 0, 0, 0.25);
- border-top-width: 0;
+.visible-print {
+ display: none !important;
}
-
-.popover.bottom .arrow:after {
- top: 1px;
- margin-left: -10px;
- border-bottom-color: #ffffff;
- border-top-width: 0;
- content: " ";
+@media print {
+ .visible-print {
+ display: block !important;
+ }
+ table.visible-print {
+ display: table;
+ }
+ tr.visible-print {
+ display: table-row !important;
+ }
+ th.visible-print,
+ td.visible-print {
+ display: table-cell !important;
+ }
}
-
-.popover.left .arrow {
- top: 50%;
- right: -11px;
- margin-top: -11px;
- border-left-color: #999999;
- border-left-color: rgba(0, 0, 0, 0.25);
- border-right-width: 0;
+.visible-print-block {
+ display: none !important;
}
-
-.popover.left .arrow:after {
- right: 1px;
- bottom: -10px;
- border-left-color: #ffffff;
- border-right-width: 0;
- content: " ";
+@media print {
+ .visible-print-block {
+ display: block !important;
+ }
}
-
-.carousel {
- position: relative;
+.visible-print-inline {
+ display: none !important;
}
-
-.carousel-inner {
- position: relative;
- width: 100%;
- overflow: hidden;
+@media print {
+ .visible-print-inline {
+ display: inline !important;
+ }
}
-
-.carousel-inner > .item {
- position: relative;
- display: none;
- -webkit-transition: 0.6s ease-in-out left;
- transition: 0.6s ease-in-out left;
+.visible-print-inline-block {
+ display: none !important;
}
-
-.carousel-inner > .item > img,
-.carousel-inner > .item > a > img {
- display: block;
- height: auto;
- max-width: 100%;
- line-height: 1;
+@media print {
+ .visible-print-inline-block {
+ display: inline-block !important;
+ }
}
-
-.carousel-inner > .active,
-.carousel-inner > .next,
-.carousel-inner > .prev {
- display: block;
+@media print {
+ .hidden-print {
+ display: none !important;
+ }
}
-
-.carousel-inner > .active {
- left: 0;
+.reset {
+ margin: 0;
+ padding: 0;
}
-
-.carousel-inner > .next,
-.carousel-inner > .prev {
- position: absolute;
- top: 0;
+.full-width {
width: 100%;
}
-
-.carousel-inner > .next {
- left: 100%;
-}
-
-.carousel-inner > .prev {
- left: -100%;
+body {
+ margin-top: 60px;
+ font-weight: 400;
+ font-family: 'Open Sans', sans-serif;
+ color: #484848;
+ position: relative;
}
-
-.carousel-inner > .next.left,
-.carousel-inner > .prev.right {
- left: 0;
+.alt {
+ color: #999999;
}
-
-.carousel-inner > .active.left {
- left: -100%;
+strong {
+ font-weight: 600;
}
-
-.carousel-inner > .active.right {
- left: 100%;
+section {
+ padding: 30px 0;
}
-
-.carousel-control {
- position: absolute;
- top: 0;
- bottom: 0;
- left: 0;
- width: 15%;
- font-size: 20px;
- color: #ffffff;
- text-align: center;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
- opacity: 0.5;
- filter: alpha(opacity=50);
+footer {
+ padding: 60px 0;
}
-
-.carousel-control.left {
- background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.5)), to(rgba(0, 0, 0, 0.0001)));
- background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%));
- background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);
- background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-family: 'Exo 2', sans-serif;
}
-
-.carousel-control.right {
- right: 0;
- left: auto;
- background-image: -webkit-gradient(linear, 0 top, 100% top, from(rgba(0, 0, 0, 0.0001)), to(rgba(0, 0, 0, 0.5)));
- background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%));
- background-image: -moz-linear-gradient(left, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);
- background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%);
- background-repeat: repeat-x;
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);
+p {
+ line-height: 1.5;
}
-
-.carousel-control:hover,
-.carousel-control:focus {
- color: #ffffff;
- text-decoration: none;
- opacity: 0.9;
- filter: alpha(opacity=90);
+.btn {
+ font-family: 'Exo 2', sans-serif;
}
-
-.carousel-control .icon-prev,
-.carousel-control .icon-next,
-.carousel-control .glyphicon-chevron-left,
-.carousel-control .glyphicon-chevron-right {
- position: absolute;
- top: 50%;
- left: 50%;
- z-index: 5;
- display: inline-block;
+.btn-default {
+ background-image: -webkit-linear-gradient(top, #fafafa 0%, #ededed 100%);
+ background-image: -o-linear-gradient(top, #fafafa 0%, #ededed 100%);
+ background-image: linear-gradient(to bottom, #fafafa 0%, #ededed 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffafafa', endColorstr='#ffededed', GradientType=0);
}
-
-.carousel-control .icon-prev,
-.carousel-control .icon-next {
- width: 20px;
- height: 20px;
- margin-top: -10px;
- margin-left: -10px;
- font-family: serif;
+.btn-default:hover {
+ background-image: none;
+ background-color: #eee;
}
-
-.carousel-control .icon-prev:before {
- content: '\2039';
+table {
+ font-weight: 400;
}
-
-.carousel-control .icon-next:before {
- content: '\203a';
+.section-header {
+ text-transform: uppercase;
+ border-bottom: 1px solid #dddddd;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ margin-top: 30px;
+}
+.text-content h1,
+.text-content h2,
+.text-content h3,
+.text-content h4,
+.text-content h5,
+.text-content h6 {
+ font-weight: 400;
+}
+.text-content h2 {
+ text-transform: uppercase;
+ border-bottom: 1px solid #dddddd;
+ margin-bottom: 20px;
+ padding-bottom: 5px;
+ margin-top: 30px;
}
-
-.carousel-indicators {
- position: absolute;
- bottom: 10px;
- left: 50%;
- z-index: 15;
- width: 60%;
- padding-left: 0;
- margin-left: -30%;
- text-align: center;
- list-style: none;
+.text-content h2 :first-child {
+ margin-top: 0;
}
-
-.carousel-indicators li {
- display: inline-block;
- width: 10px;
- height: 10px;
- margin: 1px;
- text-indent: -999px;
- cursor: pointer;
- border: 1px solid #ffffff;
- border-radius: 10px;
+.text-content h3 {
+ margin-top: 25px;
+ margin-bottom: 15px;
}
-
-.carousel-indicators .active {
- width: 12px;
- height: 12px;
- margin: 0;
- background-color: #ffffff;
+.text-content p {
+ font-weight: 400;
}
-
-.carousel-caption {
- position: absolute;
- right: 15%;
- bottom: 20px;
- left: 15%;
- z-index: 10;
- padding-top: 20px;
- padding-bottom: 20px;
- color: #ffffff;
- text-align: center;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
+.text-content p.lead {
+ font-size: 18px;
+ font-weight: 300;
}
-
-.carousel-caption .btn {
- text-shadow: none;
+.text-content h4 {
+ margin-top: 20px;
}
-
-@media screen and (min-width: 768px) {
- .carousel-control .icon-prev,
- .carousel-control .icon-next {
- width: 30px;
- height: 30px;
- margin-top: -15px;
- margin-left: -15px;
- font-size: 30px;
- }
- .carousel-caption {
- right: 20%;
- left: 20%;
- padding-bottom: 30px;
- }
- .carousel-indicators {
- bottom: 20px;
- }
+.text-content h5 {
+ font-weight: bold;
+ margin-top: 20px;
}
-
-.clearfix:before,
-.clearfix:after {
- display: table;
- content: " ";
+.navbar {
+ border: none;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
+ background-color: #fff;
}
-
-.clearfix:after {
- clear: both;
+.nav a {
+ color: #999999;
+ font-weight: 600;
+ font-family: 'Exo 2', sans-serif;
}
-
-.pull-right {
- float: right !important;
+.nav a:hover {
+ color: #428bca;
}
-
-.pull-left {
- float: left !important;
+.nav li.active > a {
+ color: #428bca;
}
-
-.hide {
- display: none !important;
+.nav > li.active > a {
+ border-right: 3px solid #428bca;
}
-
-.show {
- display: block !important;
+.navbar-brand {
+ background: url(../img/scribble.svg) no-repeat 15px center;
+ text-indent: -999999px;
+ margin-top: 8px;
+ margin-right: 10px;
+ width: 225px;
+ height: 50px;
}
-
-.invisible {
- visibility: hidden;
+.navbar-toggle {
+ border-color: #999999;
}
-
-.text-hide {
- font: 0/0 a;
- color: transparent;
- text-shadow: none;
- background-color: transparent;
- border: 0;
+.navbar-toggle .icon-bar {
+ background-color: #999999;
}
-
-.affix {
- position: fixed;
+.dropdown-menu li a {
+ line-height: 1.5714285714;
}
-
-@-ms-viewport {
- width: device-width;
+.main-banner {
+ background: #428bca url(../img/banner-bg.jpg) repeat-x left top;
+ color: #fff;
+ padding: 60px 0;
}
-
-@media screen and (max-width: 400px) {
- @-ms-viewport {
- width: 320px;
+@media (max-width: 640px) {
+ .main-banner {
+ padding: 40px 0;
}
}
-
-.hidden {
- display: none !important;
- visibility: hidden !important;
+.main-banner h1 {
+ font-weight: 300;
+ line-height: 1.1;
+ margin-top: 0;
+ font-size: 40px;
+ margin-bottom: 6px;
+ text-shadow: 0px 3px 5px rgba(0, 0, 0, 0.15);
}
-
-.visible-xs {
- display: none !important;
+.main-banner p {
+ font-size: 18px;
+ color: rgba(255, 255, 255, 0.5);
+ margin-left: 2px;
}
-
-tr.visible-xs {
- display: none !important;
+.homepage .main-banner {
+ padding: 25px 0;
+ text-align: center;
}
-
-th.visible-xs,
-td.visible-xs {
- display: none !important;
+.homepage .main-banner h1 {
+ font-size: 60px;
+ margin-bottom: 20px;
+}
+@media (max-width: 991px) {
+ .homepage .main-banner h1 {
+ /* 991px */
+ font-size: 56px;
+ }
}
-
@media (max-width: 767px) {
- .visible-xs {
- display: block !important;
+ .homepage .main-banner h1 {
+ /* 767px */
+ margin-left: 15px;
+ margin-right: 15px;
+ margin-top: 5px;
+ font-size: 48px;
+ margin-bottom: 40px;
}
- tr.visible-xs {
- display: table-row !important;
+}
+@media (max-width: 640px) {
+ .homepage .main-banner h1 {
+ font-size: 36px;
+ margin-bottom: 25px;
+ margin-left: 0;
+ margin-right: 0;
}
- th.visible-xs,
- td.visible-xs {
- display: table-cell !important;
+}
+@media (min-width: 768px) {
+ .homepage .main-banner h1 span {
+ /* 768px */
+ white-space: nowrap;
}
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .visible-xs.visible-sm {
- display: block !important;
+.homepage .main-banner .buttons {
+ margin-bottom: 10px;
+}
+.homepage .main-banner .buttons .btn-lg {
+ font-size: 20px;
+ border: 2px solid #fff;
+ width: 215px;
+ padding: 14px;
+ opacity: 0.9;
+ margin-right: 7px;
+ margin-left: 7px;
+ margin-bottom: 10px;
+}
+.homepage .main-banner .buttons .btn-lg.btn-primary {
+ background-color: #fff;
+ color: #428bca;
+}
+.homepage .main-banner .buttons .btn-lg.btn-default {
+ background-color: transparent;
+ background-image: none;
+ border-color: #fff;
+ color: #fff;
+}
+.homepage .main-banner .buttons .btn-lg:hover {
+ opacity: 1;
+ -webkit-transition: 0.1s linear;
+ -moz-transition: 0.1s linear;
+ transition: 0.1s linear;
+}
+.homepage .main-banner .version {
+ color: rgba(255, 255, 255, 0.5);
+ font-size: 14px;
+ font-style: normal;
+}
+.homepage .main-banner .version .fa {
+ margin-right: 5px;
+}
+/* Animations */
+@-webkit-keyframes fadeInUpWebkit {
+ from {
+ opacity: 0;
+ -webkit-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
}
- tr.visible-xs.visible-sm {
- display: table-row !important;
+ to {
+ opacity: 1;
+ -webkit-transform: none;
+ transform: none;
}
- th.visible-xs.visible-sm,
- td.visible-xs.visible-sm {
- display: table-cell !important;
+}
+@-moz-keyframes fadeInUpMoz {
+ from {
+ opacity: 0;
+ -moz-transform: translate3d(0, 100%, 0);
+ transform: translate3d(0, 100%, 0);
+ }
+ to {
+ opacity: 1;
+ -moz-transform: none;
+ transform: none;
}
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .visible-xs.visible-md {
- display: block !important;
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translate3d(0, 100%, 0);
}
- tr.visible-xs.visible-md {
- display: table-row !important;
+ to {
+ opacity: 1;
+ transform: none;
}
- th.visible-xs.visible-md,
- td.visible-xs.visible-md {
- display: table-cell !important;
+}
+@-webkit-keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
}
}
-
-@media (min-width: 1200px) {
- .visible-xs.visible-lg {
- display: block !important;
+@-moz-keyframes fadeIn {
+ from {
+ opacity: 0;
}
- tr.visible-xs.visible-lg {
- display: table-row !important;
+ to {
+ opacity: 1;
}
- th.visible-xs.visible-lg,
- td.visible-xs.visible-lg {
- display: table-cell !important;
+}
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
}
}
-
-.visible-sm {
- display: none !important;
+.fade-in-up {
+ opacity: 0;
+ -webkit-animation: fadeInUpWebkit ease 1;
+ -moz-animation: fadeInUpMoz ease-in 1;
+ animation: fadeInUp ease-in 1;
+ -webkit-animation-fill-mode: forwards;
+ -moz-animation-fill-mode: forwards;
+ animation-fill-mode: forwards;
+ -webkit-animation-duration: 1s;
+ -moz-animation-duration: 1s;
+ animation-duration: 1s;
+}
+.fade-in-up.one {
+ -webkit-animation-delay: 0.6s;
+ -moz-animation-delay: 0.6s;
+ animation-delay: 0.6s;
+}
+.fade-in {
+ opacity: 0;
+ /* make things invisible upon start */
+ -webkit-animation: fadeIn ease 1;
+ /* call our keyframe named fadeIn, use animattion ease-in and repeat it only 1 time */
+ -moz-animation: fadeIn ease-in 1;
+ animation: fadeIn ease-in 1;
+ -webkit-animation-fill-mode: forwards;
+ /* this makes sure that after animation is done we remain at the last keyframe value (opacity: 1)*/
+ -moz-animation-fill-mode: forwards;
+ animation-fill-mode: forwards;
+ -webkit-animation-duration: 1s;
+ -moz-animation-duration: 1s;
+ animation-duration: 1s;
+}
+.fade-in.two {
+ -webkit-animation-delay: 1.4s;
+ -moz-animation-delay: 1.4s;
+ animation-delay: 1.4s;
+}
+.fade-in.three {
+ -webkit-animation-delay: 2s;
+ -moz-animation-delay: 2s;
+ animation-delay: 2s;
+}
+.boxes {
+ padding-top: 1px;
+ padding-bottom: 0;
+}
+.boxes .row {
+ margin-left: 0;
+ margin-right: 0;
}
-
-tr.visible-sm {
- display: none !important;
+.boxes .box {
+ padding: 0;
}
-
-th.visible-sm,
-td.visible-sm {
- display: none !important;
+.boxes .box .content {
+ background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e2e2e2 100%);
+ background-image: -o-linear-gradient(top, #f5f5f5 0%, #e2e2e2 100%);
+ background-image: linear-gradient(to bottom, #f5f5f5 0%, #e2e2e2 100%);
+ background-repeat: repeat-x;
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe2e2e2', GradientType=0);
+ border: 1px solid #fff;
+ height: 200px;
+ text-align: center;
+ background-repeat: no-repeat;
+ background-position: center top;
+ background-size: auto 200px;
}
-
-@media (max-width: 767px) {
- .visible-sm.visible-xs {
- display: block !important;
+.boxes .box.server-side .content {
+ background-image: url(../img/box-server.jpg);
+}
+@media only screen and (-Webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) {
+ .boxes .box.server-side .content {
+ background-image: url(../img/box-server@2x.jpg);
}
- tr.visible-sm.visible-xs {
- display: table-row !important;
+}
+.boxes .box.services .content {
+ background-image: url(../img/box-services.jpg);
+}
+@media only screen and (-Webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) {
+ .boxes .box.services .content {
+ background-image: url(../img/box-services@2x.jpg);
}
- th.visible-sm.visible-xs,
- td.visible-sm.visible-xs {
- display: table-cell !important;
+}
+.boxes .box.cloud .content {
+ background-image: url(../img/box-cloud.jpg);
+}
+@media only screen and (-Webkit-min-device-pixel-ratio: 1.5), only screen and (-moz-min-device-pixel-ratio: 1.5), only screen and (-o-min-device-pixel-ratio: 3/2), only screen and (min-device-pixel-ratio: 1.5) {
+ .boxes .box.cloud .content {
+ background-image: url(../img/box-cloud@2x.jpg);
}
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .visible-sm {
- display: block !important;
+.boxes .box h3 {
+ font-size: 20px;
+ margin-top: 20px;
+ margin-bottom: 2px;
+}
+@media (max-width: 991px) {
+ .boxes .box h3 {
+ /* 991px */
+ font-size: 18px;
}
- tr.visible-sm {
- display: table-row !important;
+}
+.boxes .box p,
+.boxes .box a {
+ font-size: 14px;
+}
+.boxes .box p {
+ color: #999999;
+}
+.description {
+ background: url(../img/home-separator.png) no-repeat center bottom;
+ background-size: 100% 1px;
+}
+.description p {
+ font-size: 20px;
+ line-height: 1.6;
+ text-align: center;
+ padding: 0 20%;
+ margin: 5px 0;
+ font-weight: 300;
+}
+@media (max-width: 1199px) {
+ .description p {
+ /* 1199px */
+ padding: 0 14%;
}
- th.visible-sm,
- td.visible-sm {
- display: table-cell !important;
+}
+@media (max-width: 991px) {
+ .description p {
+ /* 991px */
+ padding: 0 30px;
}
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .visible-sm.visible-md {
- display: block !important;
+@media (max-width: 767px) {
+ .description p {
+ /* 768px */
+ padding: 0 15px;
+ font-size: 16px;
}
- tr.visible-sm.visible-md {
- display: table-row !important;
+}
+.main-features h2 {
+ font-size: 36px;
+ font-weight: 300;
+ text-align: center;
+ margin-bottom: 50px;
+}
+@media (max-width: 767px) {
+ .main-features h2 {
+ /* 768px */
+ font-size: 30px;
+ margin-bottom: 40px;
+ margin-top: 10px;
}
- th.visible-sm.visible-md,
- td.visible-sm.visible-md {
- display: table-cell !important;
+}
+.main-features ul {
+ padding-left: 0;
+}
+.main-features ul li {
+ list-style: none;
+ text-align: center;
+ margin-bottom: 30px;
+}
+.main-features ul li .fa {
+ font-size: 40px;
+}
+.main-features ul li h4 {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 5px;
+}
+.main-features ul li h4 span {
+ color: #999999;
+ font-weight: 400;
+}
+@media (min-width: 992px) {
+ #sidebar {
+ margin-top: 6px;
}
}
-
@media (min-width: 1200px) {
- .visible-sm.visible-lg {
- display: block !important;
- }
- tr.visible-sm.visible-lg {
- display: table-row !important;
+ #preamble .affix {
+ top: 95px;
+ width: calc(1270px/4 - 30px);
}
- th.visible-sm.visible-lg,
- td.visible-sm.visible-lg {
- display: table-cell !important;
+}
+@media (max-width: 1199px) {
+ #preamble .affix {
+ top: 95px;
+ width: calc(970px/4 - 30px);
}
}
-
-.visible-md {
- display: none !important;
+#preamble > .nav > li > .nav {
+ display: none;
}
-
-tr.visible-md {
- display: none !important;
+.sidebar > .nav > li.active > .nav {
+ display: block;
}
-
-th.visible-md,
-td.visible-md {
- display: none !important;
+.sidebar a {
+ font-weight: 400;
+}
+.sidebar .active > a {
+ border-right: 2px solid #428bca;
+}
+.sidebar > .nav > li > a {
+ text-transform: uppercase;
+}
+.sidebar .nav > li > a {
+ padding: 8px 15px;
+}
+.sidebar .nav > li > .nav {
+ margin-bottom: 10px;
+}
+.sidebar .nav > li > .nav li a {
+ padding: 4px 15px 4px 35px;
+}
+.sidebar .nav > li a.btn {
+ margin-bottom: 15px;
+ margin-top: -6px;
+ color: #484848;
+ text-transform: none;
+}
+.imageblock {
+ margin-bottom: 10px;
+}
+.imageblock + .paragraph {
+ margin-top: 15px;
+}
+.paragraph + .imageblock {
+ margin-top: 17px;
+}
+.downloads .btn.github {
+ width: calc(50% - 15px);
+ margin-bottom: 10px;
}
-
@media (max-width: 767px) {
- .visible-md.visible-xs {
- display: block !important;
- }
- tr.visible-md.visible-xs {
- display: table-row !important;
- }
- th.visible-md.visible-xs,
- td.visible-md.visible-xs {
- display: table-cell !important;
+ .downloads .btn.github {
+ width: 100%;
}
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .visible-md.visible-sm {
- display: block !important;
- }
- tr.visible-md.visible-sm {
- display: table-row !important;
- }
- th.visible-md.visible-sm,
- td.visible-md.visible-sm {
- display: table-cell !important;
- }
+.downloads .download-buttons {
+ margin-bottom: 10px;
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .visible-md {
- display: block !important;
- }
- tr.visible-md {
- display: table-row !important;
- }
- th.visible-md,
- td.visible-md {
- display: table-cell !important;
- }
+.downloads .download-buttons .btn span {
+ color: #999999;
+ font-size: 0.75em;
}
-
-@media (min-width: 1200px) {
- .visible-md.visible-lg {
- display: block !important;
- }
- tr.visible-md.visible-lg {
- display: table-row !important;
+@media (max-width: 767px) {
+ .downloads .download-buttons .btn {
+ margin-bottom: 10px;
}
- th.visible-md.visible-lg,
- td.visible-md.visible-lg {
- display: table-cell !important;
+}
+.builds tr.FAILURE td {
+ background-color: #f2dede;
+ border-color: #ebccd1;
+ color: #a94442;
+}
+/* Adjusting anchor position */
+.downloads,
+.documentation,
+.scripting,
+.community {
+ /* General styles */
+ /* Downloads page */
+ /* Documentation page */
+}
+.downloads h2,
+.documentation h2,
+.scripting h2,
+.community h2,
+.downloads h3,
+.documentation h3,
+.scripting h3,
+.community h3,
+.downloads h4,
+.documentation h4,
+.scripting h4,
+.community h4 {
+ padding-top: 86px;
+}
+.downloads h3,
+.documentation h3,
+.scripting h3,
+.community h3,
+.downloads h4,
+.documentation h4,
+.scripting h4,
+.community h4 {
+ margin-top: -76px;
+}
+.downloads h2,
+.documentation h2,
+.scripting h2,
+.community h2 {
+ margin-top: -66px;
+}
+.downloads h2.first,
+.documentation h2.first,
+.scripting h2.first,
+.community h2.first,
+.downloads table + h2,
+.documentation table + h2,
+.scripting table + h2,
+.community table + h2,
+.downloads p.lead + h3,
+.documentation p.lead + h3,
+.scripting p.lead + h3,
+.community p.lead + h3 {
+ margin-top: -86px;
+}
+.downloads .text-content > .sect1:first-child > h2,
+.documentation .text-content > .sect1:first-child > h2,
+.scripting .text-content > .sect1:first-child > h2,
+.community .text-content > .sect1:first-child > h2,
+.downloads h2 + .sect2 > h3,
+.documentation h2 + .sect2 > h3,
+.scripting h2 + .sect2 > h3,
+.community h2 + .sect2 > h3,
+.downloads h2 + .sectionbody > .sect2:first-child > h3,
+.documentation h2 + .sectionbody > .sect2:first-child > h3,
+.scripting h2 + .sectionbody > .sect2:first-child > h3,
+.community h2 + .sectionbody > .sect2:first-child > h3 {
+ margin-top: -86px;
+}
+.blog .main-banner {
+ text-align: center;
+}
+.blog .main-banner h1 {
+ background: url(../img/Scribble.png) no-repeat center top;
+ background-size: 100px 100px;
+ padding-top: 105px;
+}
+.panel {
+ border-bottom: 1px solid #dddddd;
+ padding-bottom: 20px;
+ border-radius: 0;
+ margin-bottom: 30px;
+ box-shadow: none;
+ -webkit-box-shadow: none;
+}
+.blog {
+ font-size: 10px;
+}
+@media (max-width: 640px) {
+ .blog {
+ font-size: 9px;
}
}
-
-.visible-lg {
- display: none !important;
+.blog h1 {
+ margin-bottom: 5px;
+ font-size: 3.6em;
+}
+.blog h2 {
+ font-size: 2.7em;
+ margin-bottom: 20px;
+}
+.blog h3 {
+ font-size: 2.3em;
+}
+.blog .author,
+.blog .date {
+ display: inline-block;
+ color: #999999;
+ font-size: 1.4em;
+}
+.blog-post .author,
+.blog-post .date {
+ margin-bottom: 25px;
+}
+.blog-post .author + .date:before {
+ content: ".";
+ vertical-align: super;
+ margin-left: 3px;
+ margin-right: 3px;
+}
+.blog-post p {
+ font-family: 'Open Sans', sans-serif;
+ font-weight: 300;
+ font-size: 1.8em;
+}
+.blog-post ul li p {
+ margin-bottom: 0;
}
-
-tr.visible-lg {
- display: none !important;
+.blog-post .imageblock,
+.blog-post .videoblock {
+ margin-top: 15px;
}
-
-th.visible-lg,
-td.visible-lg {
- display: none !important;
+.blog-post-listings h3 {
+ margin-bottom: 0;
}
-
-@media (max-width: 767px) {
- .visible-lg.visible-xs {
- display: block !important;
- }
- tr.visible-lg.visible-xs {
- display: table-row !important;
- }
- th.visible-lg.visible-xs,
- td.visible-lg.visible-xs {
- display: table-cell !important;
- }
+.blog-post-item h4 {
+ margin-bottom: 5px;
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .visible-lg.visible-sm {
- display: block !important;
- }
- tr.visible-lg.visible-sm {
- display: table-row !important;
- }
- th.visible-lg.visible-sm,
- td.visible-lg.visible-sm {
- display: table-cell !important;
- }
+.blog-pagination .older,
+.blog-pagination .newer {
+ font-size: 1.8em;
+ color: #999999;
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .visible-lg.visible-md {
- display: block !important;
- }
- tr.visible-lg.visible-md {
- display: table-row !important;
- }
- th.visible-lg.visible-md,
- td.visible-lg.visible-md {
- display: table-cell !important;
- }
+.dropup {
+ margin-top: 6px!important;
}
-
-@media (min-width: 1200px) {
- .visible-lg {
- display: block !important;
- }
- tr.visible-lg {
- display: table-row !important;
- }
- th.visible-lg,
- td.visible-lg {
- display: table-cell !important;
- }
+#tabnav-panel.tabnav-opened {
+ margin-bottom: -7px;
}
-
-.hidden-xs {
- display: block !important;
+.note {
+ padding: 15px;
+ margin-bottom: 20px;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ background-color: #d9edf7;
+ border-color: #bce8f1;
+ color: #31708f;
+ margin-bottom: 10px;
}
-
-tr.hidden-xs {
- display: table-row !important;
+.note h4 {
+ margin-top: 0;
+ color: inherit;
}
-
-th.hidden-xs,
-td.hidden-xs {
- display: table-cell !important;
+.note .alert-link {
+ font-weight: bold;
}
-
-@media (max-width: 767px) {
- .hidden-xs {
- display: none !important;
- }
- tr.hidden-xs {
- display: none !important;
- }
- th.hidden-xs,
- td.hidden-xs {
- display: none !important;
- }
+.note > p,
+.note > ul {
+ margin-bottom: 0;
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .hidden-xs.hidden-sm {
- display: none !important;
- }
- tr.hidden-xs.hidden-sm {
- display: none !important;
- }
- th.hidden-xs.hidden-sm,
- td.hidden-xs.hidden-sm {
- display: none !important;
- }
+.note > p + p {
+ margin-top: 5px;
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .hidden-xs.hidden-md {
- display: none !important;
- }
- tr.hidden-xs.hidden-md {
- display: none !important;
- }
- th.hidden-xs.hidden-md,
- td.hidden-xs.hidden-md {
- display: none !important;
- }
+.note hr {
+ border-top-color: #a6e1ec;
}
-
-@media (min-width: 1200px) {
- .hidden-xs.hidden-lg {
- display: none !important;
- }
- tr.hidden-xs.hidden-lg {
- display: none !important;
- }
- th.hidden-xs.hidden-lg,
- td.hidden-xs.hidden-lg {
- display: none !important;
- }
+.note .alert-link {
+ color: #245269;
}
-
-.hidden-sm {
- display: block !important;
+.note .fa {
+ font-size: 16px;
+ color: #31708f;
+ margin-right: 10px;
}
-
-tr.hidden-sm {
- display: table-row !important;
+.admonitionblock td.icon .icon-note:before {
+ content: "\f05a";
}
-
-th.hidden-sm,
-td.hidden-sm {
- display: table-cell !important;
+.quoteblock blockquote {
+ padding: 5px 20px;
}
-
-@media (max-width: 767px) {
- .hidden-sm.hidden-xs {
- display: none !important;
- }
- tr.hidden-sm.hidden-xs {
- display: none !important;
- }
- th.hidden-sm.hidden-xs,
- td.hidden-sm.hidden-xs {
- display: none !important;
- }
+.quoteblock p {
+ font-size: 14px;
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .hidden-sm {
- display: none !important;
- }
- tr.hidden-sm {
- display: none !important;
- }
- th.hidden-sm,
- td.hidden-sm {
- display: none !important;
- }
+table.tableblock {
+ width: 100%;
+ max-width: 100%;
+ margin-bottom: 20px;
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .hidden-sm.hidden-md {
- display: none !important;
- }
- tr.hidden-sm.hidden-md {
- display: none !important;
- }
- th.hidden-sm.hidden-md,
- td.hidden-sm.hidden-md {
- display: none !important;
- }
+table.tableblock > thead > tr > th,
+table.tableblock > tbody > tr > th,
+table.tableblock > tfoot > tr > th,
+table.tableblock > thead > tr > td,
+table.tableblock > tbody > tr > td,
+table.tableblock > tfoot > tr > td {
+ padding: 8px;
+ line-height: 1.42857143;
+ vertical-align: top;
+ border-top: 1px solid #dddddd;
}
-
-@media (min-width: 1200px) {
- .hidden-sm.hidden-lg {
- display: none !important;
- }
- tr.hidden-sm.hidden-lg {
- display: none !important;
- }
- th.hidden-sm.hidden-lg,
- td.hidden-sm.hidden-lg {
- display: none !important;
- }
+table.tableblock > thead > tr > th {
+ vertical-align: bottom;
+ border-bottom: 2px solid #dddddd;
}
-
-.hidden-md {
- display: block !important;
+table.tableblock > caption + thead > tr:first-child > th,
+table.tableblock > colgroup + thead > tr:first-child > th,
+table.tableblock > thead:first-child > tr:first-child > th,
+table.tableblock > caption + thead > tr:first-child > td,
+table.tableblock > colgroup + thead > tr:first-child > td,
+table.tableblock > thead:first-child > tr:first-child > td {
+ border-top: 0;
}
-
-tr.hidden-md {
- display: table-row !important;
+table.tableblock > tbody + tbody {
+ border-top: 2px solid #dddddd;
}
-
-th.hidden-md,
-td.hidden-md {
- display: table-cell !important;
+table.tableblock .table {
+ background-color: #ffffff;
}
-
-@media (max-width: 767px) {
- .hidden-md.hidden-xs {
- display: none !important;
- }
- tr.hidden-md.hidden-xs {
- display: none !important;
- }
- th.hidden-md.hidden-xs,
- td.hidden-md.hidden-xs {
- display: none !important;
- }
+table.tableblock > tbody > tr:nth-child(odd) {
+ background-color: #f9f9f9;
}
-
-@media (min-width: 768px) and (max-width: 991px) {
- .hidden-md.hidden-sm {
- display: none !important;
- }
- tr.hidden-md.hidden-sm {
- display: none !important;
- }
- th.hidden-md.hidden-sm,
- td.hidden-md.hidden-sm {
- display: none !important;
- }
+.colist {
+ margin-bottom: 10px;
}
-
-@media (min-width: 992px) and (max-width: 1199px) {
- .hidden-md {
- display: none !important;
- }
- tr.hidden-md {
- display: none !important;
- }
- th.hidden-md,
- td.hidden-md {
- display: none !important;
- }
+.colist.arabic tr td:first-child {
+ padding-right: 10px;
+ vertical-align: top;
}
-
-@media (min-width: 1200px) {
- .hidden-md.hidden-lg {
- display: none !important;
- }
- tr.hidden-md.hidden-lg {
- display: none !important;
- }
- th.hidden-md.hidden-lg,
- td.hidden-md.hidden-lg {
- display: none !important;
- }
+#tabnav-panel.tabnav-opened {
+ height: 220px;
+ background: #f2f8ff;
}
-.hidden-lg {
- display: block !important;
+#tabnav .tabcontent {
+ padding-top: 30px;
+ height: 100%;
+ clear: both;
+ background: #f2f8ff;
+ padding: 20px 25px;
}
-
-tr.hidden-lg {
- display: table-row !important;
+*:focus {
+ outline: 0;
}
-
-th.hidden-lg,
-td.hidden-lg {
- display: table-cell !important;
+::selection {
+ background: #e1eef4;
}
-
-@media (max-width: 767px) {
- .hidden-lg.hidden-xs {
- display: none !important;
- }
- tr.hidden-lg.hidden-xs {
- display: none !important;
- }
- th.hidden-lg.hidden-xs,
- td.hidden-lg.hidden-xs {
- display: none !important;
- }
+::-moz-selection {
+ background: #e1eef4;
}
-@media (min-width: 768px) and (max-width: 991px) {
- .hidden-lg.hidden-sm {
- display: none !important;
- }
- tr.hidden-lg.hidden-sm {
- display: none !important;
- }
- th.hidden-lg.hidden-sm,
- td.hidden-lg.hidden-sm {
- display: none !important;
- }
+/* ----- fork on github banner ----- */
+#forkongithubp {
+ text-align: center;
}
-@media (min-width: 992px) and (max-width: 1199px) {
- .hidden-lg.hidden-md {
- display: none !important;
- }
- tr.hidden-lg.hidden-md {
- display: none !important;
- }
- th.hidden-lg.hidden-md,
- td.hidden-lg.hidden-md {
- display: none !important;
- }
+#forkongithub a {
+ color:#428bca;
+}
+.bg-grey { background-color: #333 !important; }
+#forkongithub a::before, #forkongithub a::after {
+ content:""; width:100%; display:block; position:absolute;
+ top:1px; left:0; height:1px; background:#fff;
+}
+#forkongithub a::after { bottom:1px; top:auto; }
+
+@media screen and (min-width:992px) {
+ #forkongithub {
+ position:fixed;
+ display:block;
+ top:0;
+ right:0;
+ width:200px;
+ overflow:hidden;
+ height:200px;
+ z-index:9999;
+ }
+ #forkongithub a {
+ color:#fff;
+ text-decoration:none;
+ font-family:arial,sans-serif;
+ text-align:center;
+ font-weight:700;
+ font-size:1.5rem;
+ line-height:3rem;
+ padding:5px 40px;
+ width:200px;
+ position:absolute;
+ top:50px;
+ right:-75px;
+ transform:rotate(45deg);
+ -webkit-transform:rotate(45deg);
+ -ms-transform:rotate(45deg);
+ -moz-transform:rotate(45deg);
+ -o-transform:rotate(45deg);
+ box-shadow:4px 4px 10px rgba(0,0,0,0.8);
+ box-sizing: content-box;
+ }
+ .bg-grey { background-color: #444 !important; }
+}
+
+
+#toc {
+ border: 0;
+ display: none;
}
@media (min-width: 1200px) {
- .hidden-lg {
- display: none !important;
- }
- tr.hidden-lg {
- display: none !important;
- }
- th.hidden-lg,
- td.hidden-lg {
- display: none !important;
+ #toc {
+ display: block;
}
}
-.visible-print {
- display: none !important;
+#toc ul {
+ text-transform: none;
+ white-space: nowrap;
+ padding-left: 5px;
+ line-height: 2.5;
}
-tr.visible-print {
- display: none !important;
+#toc .nav li a {
+ padding-right : 5px;
}
-th.visible-print,
-td.visible-print {
- display: none !important;
+#toc ul.sectlevel1 {
+ text-transform: uppercase;
}
-@media print {
- .visible-print {
- display: block !important;
- }
- tr.visible-print {
- display: table-row !important;
- }
- th.visible-print,
- td.visible-print {
- display: table-cell !important;
- }
- .hidden-print {
- display: none !important;
- }
- tr.hidden-print {
- display: none !important;
- }
- th.hidden-print,
- td.hidden-print {
- display: none !important;
- }
-}
\ No newline at end of file
+/* red hat tabzilla */
+.dropup {
+ right: 180px;
+}
+
+#toctitle {
+ visibility: hidden;
+}
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-aea.js b/src/main/jbake/assets/docs/google-code-prettify/lang-aea.js
new file mode 100644
index 0000000..784ebb2
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-aea.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Onno Hommes.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["apollo","agc","aea"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-agc.js b/src/main/jbake/assets/docs/google-code-prettify/lang-agc.js
new file mode 100644
index 0000000..784ebb2
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-agc.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Onno Hommes.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["apollo","agc","aea"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-apollo.js b/src/main/jbake/assets/docs/google-code-prettify/lang-apollo.js
new file mode 100644
index 0000000..784ebb2
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-apollo.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Onno Hommes.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\s/,
+null],["typ",/^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\=?|BLOCK|BNKSUM|E?CADR|COUNT\*?|2?DEC\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\s/,null],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[!-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["apollo","agc","aea"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-basic.js b/src/main/jbake/assets/docs/google-code-prettify/lang-basic.js
new file mode 100644
index 0000000..2d6151d
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-basic.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2013 Peter Kofler
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:"(?:[^\\"\r\n]|\\.)*(?:"|$))/,null,'"'],["pln",/^\s+/,null," \r\n\t\u00a0"]],[["com",/^REM[^\r\n]*/,null],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,null],["pln",/^[A-Z][A-Z0-9]?(?:\$|%)?/i,null],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?/i,
+null,"0123456789"],["pun",/^.[^\s\w\.$%"]*/,null]]),["basic","cbm"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-cbm.js b/src/main/jbake/assets/docs/google-code-prettify/lang-cbm.js
new file mode 100644
index 0000000..2d6151d
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-cbm.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2013 Peter Kofler
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:"(?:[^\\"\r\n]|\\.)*(?:"|$))/,null,'"'],["pln",/^\s+/,null," \r\n\t\u00a0"]],[["com",/^REM[^\r\n]*/,null],["kwd",/^\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\b/,null],["pln",/^[A-Z][A-Z0-9]?(?:\$|%)?/i,null],["lit",/^(?:\d+(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?/i,
+null,"0123456789"],["pun",/^.[^\s\w\.$%"]*/,null]]),["basic","cbm"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-cl.js b/src/main/jbake/assets/docs/google-code-prettify/lang-cl.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-cl.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-clj.js b/src/main/jbake/assets/docs/google-code-prettify/lang-clj.js
new file mode 100644
index 0000000..d1173b1
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-clj.js
@@ -0,0 +1,17 @@
+/*
+ Copyright (C) 2011 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^[\(\{\[]+/,null,"([{"],["clo",/^[\)\}\]]+/,null,")]}"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\b/,
+null],["typ",/^:[0-9a-zA-Z\-]+/]]),["clj"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-css.js b/src/main/jbake/assets/docs/google-code-prettify/lang-css.js
new file mode 100644
index 0000000..90d175d
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-css.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[["str",/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],["str",/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']+)\)/i],["kwd",/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],
+["com",/^(?:\x3c!--|--\x3e)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#(?:[0-9a-f]{3}){1,2}\b/i],["pln",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],["pun",/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^\)\"\']+/]]),["css-str"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-dart.js b/src/main/jbake/assets/docs/google-code-prettify/lang-dart.js
new file mode 100644
index 0000000..da142a4
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-dart.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2013 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"]],[["com",/^#!(?:.*)/],["kwd",/^\b(?:import|library|part of|part|as|show|hide)\b/i],["com",/^\/\/(?:.*)/],["com",/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],["kwd",/^\b(?:class|interface)\b/i],["kwd",/^\b(?:assert|async|await|break|case|catch|continue|default|do|else|finally|for|if|in|is|new|return|super|switch|sync|this|throw|try|while)\b/i],["kwd",/^\b(?:abstract|const|extends|factory|final|get|implements|native|operator|set|static|typedef|var)\b/i],
+["typ",/^\b(?:bool|double|Dynamic|int|num|Object|String|void)\b/i],["kwd",/^\b(?:false|null|true)\b/i],["str",/^r?[\']{3}[\s|\S]*?[^\\][\']{3}/],["str",/^r?[\"]{3}[\s|\S]*?[^\\][\"]{3}/],["str",/^r?\'(\'|(?:[^\n\r\f])*?[^\\]\')/],["str",/^r?\"(\"|(?:[^\n\r\f])*?[^\\]\")/],["typ",/^[A-Z]\w*/],["pln",/^[a-z_$][a-z0-9_]*/i],["pun",/^[~!%^&*+=|?:<>/-]/],["lit",/^\b0x[0-9a-f]+/i],["lit",/^\b\d+(?:\.\d*)?(?:e[+-]?\d+)?/i],["lit",
+/^\b\.\d+(?:e[+-]?\d+)?/i],["pun",/^[(){}\[\],.;]/]]),["dart"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-el.js b/src/main/jbake/assets/docs/google-code-prettify/lang-el.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-el.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-erl.js b/src/main/jbake/assets/docs/google-code-prettify/lang-erl.js
new file mode 100644
index 0000000..e7da9b0
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-erl.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2013 Andrew Allen
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\x0B\x0C\r ]+/,null,"\t\n\x0B\f\r "],["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["lit",/^[a-z][a-zA-Z0-9_]*/],["lit",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])+\'?/,null,"'"],["lit",/^\?[^ \t\n({]+/,null,"?"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\n]*/],["kwd",/^(?:module|attributes|do|let|in|letrec|apply|call|primop|case|of|end|when|fun|try|catch|receive|after|char|integer|float,atom,string,var)\b/],
+["kwd",/^-[a-z_]+/],["typ",/^[A-Z_][a-zA-Z0-9_]*/],["pun",/^[.,;]/]]),["erlang","erl"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-erlang.js b/src/main/jbake/assets/docs/google-code-prettify/lang-erlang.js
new file mode 100644
index 0000000..e7da9b0
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-erlang.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2013 Andrew Allen
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\x0B\x0C\r ]+/,null,"\t\n\x0B\f\r "],["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["lit",/^[a-z][a-zA-Z0-9_]*/],["lit",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])+\'?/,null,"'"],["lit",/^\?[^ \t\n({]+/,null,"?"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\n]*/],["kwd",/^(?:module|attributes|do|let|in|letrec|apply|call|primop|case|of|end|when|fun|try|catch|receive|after|char|integer|float,atom,string,var)\b/],
+["kwd",/^-[a-z_]+/],["typ",/^[A-Z_][a-zA-Z0-9_]*/],["pun",/^[.,;]/]]),["erlang","erl"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-fs.js b/src/main/jbake/assets/docs/google-code-prettify/lang-fs.js
new file mode 100644
index 0000000..c012a3f
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-fs.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])(?:\'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
+["lit",/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^(?:[a-z_][\w']*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],["pun",/^[^\t\n\r \xA0\"\'\w]+/]]),["fs","ml"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-go.js b/src/main/jbake/assets/docs/google-code-prettify/lang-go.js
new file mode 100644
index 0000000..1f6934a
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-go.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["pln",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])+(?:\'|$)|`[^`]*(?:`|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\r\n]*|\/\*[\s\S]*?\*\/)/],["pln",/^(?:[^\/\"\'`]|\/(?![\/\*]))+/i]]),["go"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-hs.js b/src/main/jbake/assets/docs/google-code-prettify/lang-hs.js
new file mode 100644
index 0000000..2002221
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-hs.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\x0B\x0C\r ]+/,null,"\t\n\x0B\f\r "],["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])\'?/,null,"'"],["lit",/^(?:0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^(?:(?:--+(?:[^\r\n\x0C]*)?)|(?:\{-(?:[^-]|-+[^-\}])*-\}))/],["kwd",/^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\']|$)/,
+null],["pln",/^(?:[A-Z][\w\']*\.)*[a-zA-Z][\w\']*/],["pun",/^[^\t\n\x0B\x0C\r a-zA-Z0-9\'\"]+/]]),["hs"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lasso.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lasso.js
new file mode 100644
index 0000000..415ca67
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lasso.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2013 Eric Knibbe
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\`[^\`]*(?:\`|$)/,null,"`"],["lit",/^0x[\da-f]+|\d+/i,null,"0123456789"],["atn",/^#\d+|[#$][a-z_][\w.]*|#![ \S]+lasso9\b/i,null,"#$"]],[["tag",/^[[\]]|<\?(?:lasso(?:script)?|=)|\?>|noprocess\b|no_square_brackets\b/i],["com",/^\/\/[^\r\n]*|\/\*[\s\S]*?\*\//],
+["atn",/^-(?!infinity)[a-z_][\w.]*|\.\s*'[a-z_][\w.]*'/i],["lit",/^\d*\.\d+(?:e[-+]?\d+)?|infinity\b|NaN\b/i],["atv",/^::\s*[a-z_][\w.]*/i],["lit",/^(?:true|false|none|minimal|full|all|void|and|or|not|bw|nbw|ew|new|cn|ncn|lt|lte|gt|gte|eq|neq|rx|nrx|ft)\b/i],["kwd",/^(?:error_code|error_msg|error_pop|error_push|error_reset|cache|database_names|database_schemanames|database_tablenames|define_tag|define_type|email_batch|encode_set|html_comment|handle|handle_error|header|if|inline|iterate|ljax_target|link|link_currentaction|link_currentgroup|link_currentrecord|link_detail|link_firstgroup|link_firstrecord|link_lastgroup|link_lastrecord|link_nextgroup|link_nextrecord|link_prevgroup|link_prevrecord|log|loop|namespace_using|output_none|portal|private|protect|records|referer|referrer|repeating|resultset|rows|search_args|search_arguments|select|sort_args|sort_arguments|thread_atomic|value_list|while|abort|case|else|if_empty|if_false|if_null|if_true|loop_abort|loop_continue|loop_count|params|params_up|return|return_value|run_children|soap_definetag|soap_lastrequest|soap_lastresponse|tag_name|ascending|average|by|define|descending|do|equals|frozen|group|handle_failure|import|in|into|join|let|match|max|min|on|order|parent|protected|provide|public|require|returnhome|skip|split_thread|sum|take|thread|to|trait|type|where|with|yield|yieldhome)\b/i],
+["typ",/^(?:array|date|decimal|duration|integer|map|pair|string|tag|xml|null|boolean|bytes|keyword|list|locale|queue|set|stack|staticarray|local|var|variable|global|data|self|inherited|currentcapture|givenblock)\b|^\.\.?/i],["pln",/^[a-z_][\w.]*(?:=\s*(?=\())?/i],["pun",/^:=|[-+*\/%=<>&|!?\\]/]]),["lasso","ls","lassoscript"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lassoscript.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lassoscript.js
new file mode 100644
index 0000000..415ca67
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lassoscript.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2013 Eric Knibbe
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\`[^\`]*(?:\`|$)/,null,"`"],["lit",/^0x[\da-f]+|\d+/i,null,"0123456789"],["atn",/^#\d+|[#$][a-z_][\w.]*|#![ \S]+lasso9\b/i,null,"#$"]],[["tag",/^[[\]]|<\?(?:lasso(?:script)?|=)|\?>|noprocess\b|no_square_brackets\b/i],["com",/^\/\/[^\r\n]*|\/\*[\s\S]*?\*\//],
+["atn",/^-(?!infinity)[a-z_][\w.]*|\.\s*'[a-z_][\w.]*'/i],["lit",/^\d*\.\d+(?:e[-+]?\d+)?|infinity\b|NaN\b/i],["atv",/^::\s*[a-z_][\w.]*/i],["lit",/^(?:true|false|none|minimal|full|all|void|and|or|not|bw|nbw|ew|new|cn|ncn|lt|lte|gt|gte|eq|neq|rx|nrx|ft)\b/i],["kwd",/^(?:error_code|error_msg|error_pop|error_push|error_reset|cache|database_names|database_schemanames|database_tablenames|define_tag|define_type|email_batch|encode_set|html_comment|handle|handle_error|header|if|inline|iterate|ljax_target|link|link_currentaction|link_currentgroup|link_currentrecord|link_detail|link_firstgroup|link_firstrecord|link_lastgroup|link_lastrecord|link_nextgroup|link_nextrecord|link_prevgroup|link_prevrecord|log|loop|namespace_using|output_none|portal|private|protect|records|referer|referrer|repeating|resultset|rows|search_args|search_arguments|select|sort_args|sort_arguments|thread_atomic|value_list|while|abort|case|else|if_empty|if_false|if_null|if_true|loop_abort|loop_continue|loop_count|params|params_up|return|return_value|run_children|soap_definetag|soap_lastrequest|soap_lastresponse|tag_name|ascending|average|by|define|descending|do|equals|frozen|group|handle_failure|import|in|into|join|let|match|max|min|on|order|parent|protected|provide|public|require|returnhome|skip|split_thread|sum|take|thread|to|trait|type|where|with|yield|yieldhome)\b/i],
+["typ",/^(?:array|date|decimal|duration|integer|map|pair|string|tag|xml|null|boolean|bytes|keyword|list|locale|queue|set|stack|staticarray|local|var|variable|global|data|self|inherited|currentcapture|givenblock)\b|^\.\.?/i],["pln",/^[a-z_][\w.]*(?:=\s*(?=\())?/i],["pun",/^:=|[-+*\/%=<>&|!?\\]/]]),["lasso","ls","lassoscript"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-latex.js b/src/main/jbake/assets/docs/google-code-prettify/lang-latex.js
new file mode 100644
index 0000000..efc758c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-latex.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2011 Martin S.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\r\n]*/,null,"%"]],[["kwd",/^\\[a-zA-Z@]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[{}()\[\]=]+/]]),["latex","tex"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lgt.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lgt.js
new file mode 100644
index 0000000..2959d75
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lgt.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2014 Paulo Moura
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["lit",/^[a-z][a-zA-Z0-9_]*/],["lit",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])+\'?/,null,"'"],["lit",/^(?:0'.|0b[0-1]+|0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\r\n]*/,null,"%"],["com",/^\/\*[\s\S]*?\*\//],["kwd",/^\s*:-\s(c(a(lls|tegory)|oinductive)|p(ublic|r(ot(ocol|ected)|ivate))|e(l(if|se)|n(coding|sure_loaded)|xport)|i(f|n(clude|itialization|fo))|alias|d(ynamic|iscontiguous)|m(eta_(non_terminal|predicate)|od(e|ule)|ultifile)|reexport|s(et_(logtalk|prolog)_flag|ynchronized)|o(bject|p)|use(s|_module))/],
+["kwd",/^\s*:-\s(e(lse|nd(if|_(category|object|protocol)))|built_in|dynamic|synchronized|threaded)/],["typ",/^[A-Z_][a-zA-Z0-9_]*/],["pun",/^[.,;{}:^<>=\\/+*?#!-]/]]),["logtalk","lgt"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lisp.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lisp.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lisp.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-ll.js b/src/main/jbake/assets/docs/google-code-prettify/lang-ll.js
new file mode 100644
index 0000000..7604d96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-ll.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2013 Nikhil Dabas
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^!?\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["com",/^;[^\r\n]*/,null,";"]],[["pln",/^[%@!](?:[-a-zA-Z$._][-a-zA-Z$._0-9]*|\d+)/],["kwd",/^[A-Za-z_][0-9A-Za-z_]*/,null],["lit",/^\d+\.\d+/],["lit",/^(?:\d+|0[xX][a-fA-F0-9]+)/],["pun",/^[()\[\]{},=*<>:]|\.\.\.$/]]),["llvm","ll"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-llvm.js b/src/main/jbake/assets/docs/google-code-prettify/lang-llvm.js
new file mode 100644
index 0000000..7604d96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-llvm.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2013 Nikhil Dabas
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^!?\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["com",/^;[^\r\n]*/,null,";"]],[["pln",/^[%@!](?:[-a-zA-Z$._][-a-zA-Z$._0-9]*|\d+)/],["kwd",/^[A-Za-z_][0-9A-Za-z_]*/,null],["lit",/^\d+\.\d+/],["lit",/^(?:\d+|0[xX][a-fA-F0-9]+)/],["pun",/^[()\[\]{},=*<>:]|\.\.\.$/]]),["llvm","ll"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-logtalk.js b/src/main/jbake/assets/docs/google-code-prettify/lang-logtalk.js
new file mode 100644
index 0000000..2959d75
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-logtalk.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2014 Paulo Moura
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^\"(?:[^\"\\\n\x0C\r]|\\[\s\S])*(?:\"|$)/,null,'"'],["lit",/^[a-z][a-zA-Z0-9_]*/],["lit",/^\'(?:[^\'\\\n\x0C\r]|\\[^&])+\'?/,null,"'"],["lit",/^(?:0'.|0b[0-1]+|0o[0-7]+|0x[\da-f]+|\d+(?:\.\d+)?(?:e[+\-]?\d+)?)/i,null,"0123456789"]],[["com",/^%[^\r\n]*/,null,"%"],["com",/^\/\*[\s\S]*?\*\//],["kwd",/^\s*:-\s(c(a(lls|tegory)|oinductive)|p(ublic|r(ot(ocol|ected)|ivate))|e(l(if|se)|n(coding|sure_loaded)|xport)|i(f|n(clude|itialization|fo))|alias|d(ynamic|iscontiguous)|m(eta_(non_terminal|predicate)|od(e|ule)|ultifile)|reexport|s(et_(logtalk|prolog)_flag|ynchronized)|o(bject|p)|use(s|_module))/],
+["kwd",/^\s*:-\s(e(lse|nd(if|_(category|object|protocol)))|built_in|dynamic|synchronized|threaded)/],["typ",/^[A-Z_][a-zA-Z0-9_]*/],["pun",/^[.,;{}:^<>=\\/+*?#!-]/]]),["logtalk","lgt"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-ls.js b/src/main/jbake/assets/docs/google-code-prettify/lang-ls.js
new file mode 100644
index 0000000..415ca67
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-ls.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2013 Eric Knibbe
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\`[^\`]*(?:\`|$)/,null,"`"],["lit",/^0x[\da-f]+|\d+/i,null,"0123456789"],["atn",/^#\d+|[#$][a-z_][\w.]*|#![ \S]+lasso9\b/i,null,"#$"]],[["tag",/^[[\]]|<\?(?:lasso(?:script)?|=)|\?>|noprocess\b|no_square_brackets\b/i],["com",/^\/\/[^\r\n]*|\/\*[\s\S]*?\*\//],
+["atn",/^-(?!infinity)[a-z_][\w.]*|\.\s*'[a-z_][\w.]*'/i],["lit",/^\d*\.\d+(?:e[-+]?\d+)?|infinity\b|NaN\b/i],["atv",/^::\s*[a-z_][\w.]*/i],["lit",/^(?:true|false|none|minimal|full|all|void|and|or|not|bw|nbw|ew|new|cn|ncn|lt|lte|gt|gte|eq|neq|rx|nrx|ft)\b/i],["kwd",/^(?:error_code|error_msg|error_pop|error_push|error_reset|cache|database_names|database_schemanames|database_tablenames|define_tag|define_type|email_batch|encode_set|html_comment|handle|handle_error|header|if|inline|iterate|ljax_target|link|link_currentaction|link_currentgroup|link_currentrecord|link_detail|link_firstgroup|link_firstrecord|link_lastgroup|link_lastrecord|link_nextgroup|link_nextrecord|link_prevgroup|link_prevrecord|log|loop|namespace_using|output_none|portal|private|protect|records|referer|referrer|repeating|resultset|rows|search_args|search_arguments|select|sort_args|sort_arguments|thread_atomic|value_list|while|abort|case|else|if_empty|if_false|if_null|if_true|loop_abort|loop_continue|loop_count|params|params_up|return|return_value|run_children|soap_definetag|soap_lastrequest|soap_lastresponse|tag_name|ascending|average|by|define|descending|do|equals|frozen|group|handle_failure|import|in|into|join|let|match|max|min|on|order|parent|protected|provide|public|require|returnhome|skip|split_thread|sum|take|thread|to|trait|type|where|with|yield|yieldhome)\b/i],
+["typ",/^(?:array|date|decimal|duration|integer|map|pair|string|tag|xml|null|boolean|bytes|keyword|list|locale|queue|set|stack|staticarray|local|var|variable|global|data|self|inherited|currentcapture|givenblock)\b|^\.\.?/i],["pln",/^[a-z_][\w.]*(?:=\s*(?=\())?/i],["pun",/^:=|[-+*\/%=<>&|!?\\]/]]),["lasso","ls","lassoscript"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lsp.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lsp.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lsp.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-lua.js b/src/main/jbake/assets/docs/google-code-prettify/lang-lua.js
new file mode 100644
index 0000000..afb2901
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-lua.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])*(?:\'|$))/,null,"\"'"]],[["com",/^--(?:\[(=*)\[[\s\S]*?(?:\]\1\]|$)|[^\r\n]*)/],["str",/^\[(=*)\[[\s\S]*?(?:\]\1\]|$)/],["kwd",/^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],
+["pln",/^[a-z_]\w*/i],["pun",/^[^\w\t\n\r \xA0][^\w\t\n\r \xA0\"\'\-\+=]*/]]),["lua"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-matlab.js b/src/main/jbake/assets/docs/google-code-prettify/lang-matlab.js
new file mode 100644
index 0000000..a0522a5
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-matlab.js
@@ -0,0 +1,29 @@
+/*
+
+ Copyright (c) 2013 by Amro
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+var a=window.PR,b=[[a.PR_PLAIN,/^[ \t\r\n\v\f\xA0]+/,null," \t\r\n\x0B\f\u00a0"],[a.PR_COMMENT,/^%\{[^%]*%+(?:[^\}%][^%]*%+)*\}/,null],[a.PR_COMMENT,/^%[^\r\n]*/,null,"%"],["syscmd",/^![^\r\n]*/,null,"!"]],c=[["linecont",/^\.\.\.\s*[\r\n]/,null],["err",/^\?\?\? [^\r\n]*/,null],["wrn",/^Warning: [^\r\n]*/,null],["codeoutput",/^>>\s+/,null],["codeoutput",/^octave:\d+>\s+/,null],["lang-matlab-operators",/^((?:[a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z][a-zA-Z0-9_]*)*|\)|\]|\}|\.)')/,null],["lang-matlab-identifiers",
+/^([a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z][a-zA-Z0-9_]*)*)(?!')/,null],[a.PR_STRING,/^'(?:[^']|'')*'/,null],[a.PR_LITERAL,/^[+\-]?\.?\d+(?:\.\d*)?(?:[Ee][+\-]?\d+)?[ij]?/,null],[a.PR_TAG,/^(?:\{|\}|\(|\)|\[|\])/,null],[a.PR_PUNCTUATION,/^(?:<|>|=|~|@|&|;|,|:|!|\-|\+|\*|\^|\.|\||\\|\/)/,null]],d=[["lang-matlab-identifiers",/^([a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z][a-zA-Z0-9_]*)*)/,null],[a.PR_TAG,/^(?:\{|\}|\(|\)|\[|\])/,null],[a.PR_PUNCTUATION,/^(?:<|>|=|~|@|&|;|,|:|!|\-|\+|\*|\^|\.|\||\\|\/)/,null],["transpose",
+/^'/,null]];
+a.registerLangHandler(a.createSimpleLexer([],[[a.PR_KEYWORD,/^\b(?:break|case|catch|classdef|continue|else|elseif|end|for|function|global|if|otherwise|parfor|persistent|return|spmd|switch|try|while)\b/,null],["const",/^\b(?:true|false|inf|Inf|nan|NaN|eps|pi|ans|nargin|nargout|varargin|varargout)\b/,null],[a.PR_TYPE,/^\b(?:cell|struct|char|double|single|logical|u?int(?:8|16|32|64)|sparse)\b/,null],["fun",/^\b(?:abs|accumarray|acos(?:d|h)?|acot(?:d|h)?|acsc(?:d|h)?|actxcontrol(?:list|select)?|actxGetRunningServer|actxserver|addlistener|addpath|addpref|addtodate|airy|align|alim|all|allchild|alpha|alphamap|amd|ancestor|and|angle|annotation|any|area|arrayfun|asec(?:d|h)?|asin(?:d|h)?|assert|assignin|atan(?:2|d|h)?|audiodevinfo|audioplayer|audiorecorder|aufinfo|auread|autumn|auwrite|avifile|aviinfo|aviread|axes|axis|balance|bar(?:3|3h|h)?|base2dec|beep|BeginInvoke|bench|bessel(?:h|i|j|k|y)|beta|betainc|betaincinv|betaln|bicg|bicgstab|bicgstabl|bin2dec|bitand|bitcmp|bitget|bitmax|bitnot|bitor|bitset|bitshift|bitxor|blanks|blkdiag|bone|box|brighten|brush|bsxfun|builddocsearchdb|builtin|bvp4c|bvp5c|bvpget|bvpinit|bvpset|bvpxtend|calendar|calllib|callSoapService|camdolly|cameratoolbar|camlight|camlookat|camorbit|campan|campos|camproj|camroll|camtarget|camup|camva|camzoom|cart2pol|cart2sph|cast|cat|caxis|cd|cdf2rdf|cdfepoch|cdfinfo|cdflib(?:.(?:close|closeVar|computeEpoch|computeEpoch16|create|createAttr|createVar|delete|deleteAttr|deleteAttrEntry|deleteAttrgEntry|deleteVar|deleteVarRecords|epoch16Breakdown|epochBreakdown|getAttrEntry|getAttrgEntry|getAttrMaxEntry|getAttrMaxgEntry|getAttrName|getAttrNum|getAttrScope|getCacheSize|getChecksum|getCompression|getCompressionCacheSize|getConstantNames|getConstantValue|getCopyright|getFileBackward|getFormat|getLibraryCopyright|getLibraryVersion|getMajority|getName|getNumAttrEntries|getNumAttrgEntries|getNumAttributes|getNumgAttributes|getReadOnlyMode|getStageCacheSize|getValidate|getVarAllocRecords|getVarBlockingFactor|getVarCacheSize|getVarCompression|getVarData|getVarMaxAllocRecNum|getVarMaxWrittenRecNum|getVarName|getVarNum|getVarNumRecsWritten|getVarPadValue|getVarRecordData|getVarReservePercent|getVarsMaxWrittenRecNum|getVarSparseRecords|getVersion|hyperGetVarData|hyperPutVarData|inquire|inquireAttr|inquireAttrEntry|inquireAttrgEntry|inquireVar|open|putAttrEntry|putAttrgEntry|putVarData|putVarRecordData|renameAttr|renameVar|setCacheSize|setChecksum|setCompression|setCompressionCacheSize|setFileBackward|setFormat|setMajority|setReadOnlyMode|setStageCacheSize|setValidate|setVarAllocBlockRecords|setVarBlockingFactor|setVarCacheSize|setVarCompression|setVarInitialRecs|setVarPadValue|SetVarReservePercent|setVarsCacheSize|setVarSparseRecords))?|cdfread|cdfwrite|ceil|cell2mat|cell2struct|celldisp|cellfun|cellplot|cellstr|cgs|checkcode|checkin|checkout|chol|cholinc|cholupdate|circshift|cla|clabel|class|clc|clear|clearvars|clf|clipboard|clock|close|closereq|cmopts|cmpermute|cmunique|colamd|colon|colorbar|colordef|colormap|colormapeditor|colperm|Combine|comet|comet3|commandhistory|commandwindow|compan|compass|complex|computer|cond|condeig|condest|coneplot|conj|containers.Map|contour(?:3|c|f|slice)?|contrast|conv|conv2|convhull|convhulln|convn|cool|copper|copyfile|copyobj|corrcoef|cos(?:d|h)?|cot(?:d|h)?|cov|cplxpair|cputime|createClassFromWsdl|createSoapMessage|cross|csc(?:d|h)?|csvread|csvwrite|ctranspose|cumprod|cumsum|cumtrapz|curl|customverctrl|cylinder|daqread|daspect|datacursormode|datatipinfo|date|datenum|datestr|datetick|datevec|dbclear|dbcont|dbdown|dblquad|dbmex|dbquit|dbstack|dbstatus|dbstep|dbstop|dbtype|dbup|dde23|ddeget|ddesd|ddeset|deal|deblank|dec2base|dec2bin|dec2hex|decic|deconv|del2|delaunay|delaunay3|delaunayn|DelaunayTri|delete|demo|depdir|depfun|det|detrend|deval|diag|dialog|diary|diff|diffuse|dir|disp|display|dither|divergence|dlmread|dlmwrite|dmperm|doc|docsearch|dos|dot|dragrect|drawnow|dsearch|dsearchn|dynamicprops|echo|echodemo|edit|eig|eigs|ellipj|ellipke|ellipsoid|empty|enableNETfromNetworkDrive|enableservice|EndInvoke|enumeration|eomday|eq|erf|erfc|erfcinv|erfcx|erfinv|error|errorbar|errordlg|etime|etree|etreeplot|eval|evalc|evalin|event.(?:EventData|listener|PropertyEvent|proplistener)|exifread|exist|exit|exp|expint|expm|expm1|export2wsdlg|eye|ezcontour|ezcontourf|ezmesh|ezmeshc|ezplot|ezplot3|ezpolar|ezsurf|ezsurfc|factor|factorial|fclose|feather|feature|feof|ferror|feval|fft|fft2|fftn|fftshift|fftw|fgetl|fgets|fieldnames|figure|figurepalette|fileattrib|filebrowser|filemarker|fileparts|fileread|filesep|fill|fill3|filter|filter2|find|findall|findfigs|findobj|findstr|finish|fitsdisp|fitsinfo|fitsread|fitswrite|fix|flag|flipdim|fliplr|flipud|floor|flow|fminbnd|fminsearch|fopen|format|fplot|fprintf|frame2im|fread|freqspace|frewind|fscanf|fseek|ftell|FTP|full|fullfile|func2str|functions|funm|fwrite|fzero|gallery|gamma|gammainc|gammaincinv|gammaln|gca|gcbf|gcbo|gcd|gcf|gco|ge|genpath|genvarname|get|getappdata|getenv|getfield|getframe|getpixelposition|getpref|ginput|gmres|gplot|grabcode|gradient|gray|graymon|grid|griddata(?:3|n)?|griddedInterpolant|gsvd|gt|gtext|guidata|guide|guihandles|gunzip|gzip|h5create|h5disp|h5info|h5read|h5readatt|h5write|h5writeatt|hadamard|handle|hankel|hdf|hdf5|hdf5info|hdf5read|hdf5write|hdfinfo|hdfread|hdftool|help|helpbrowser|helpdesk|helpdlg|helpwin|hess|hex2dec|hex2num|hgexport|hggroup|hgload|hgsave|hgsetget|hgtransform|hidden|hilb|hist|histc|hold|home|horzcat|hostid|hot|hsv|hsv2rgb|hypot|ichol|idivide|ifft|ifft2|ifftn|ifftshift|ilu|im2frame|im2java|imag|image|imagesc|imapprox|imfinfo|imformats|import|importdata|imread|imwrite|ind2rgb|ind2sub|inferiorto|info|inline|inmem|inpolygon|input|inputdlg|inputname|inputParser|inspect|instrcallback|instrfind|instrfindall|int2str|integral(?:2|3)?|interp(?:1|1q|2|3|ft|n)|interpstreamspeed|intersect|intmax|intmin|inv|invhilb|ipermute|isa|isappdata|iscell|iscellstr|ischar|iscolumn|isdir|isempty|isequal|isequaln|isequalwithequalnans|isfield|isfinite|isfloat|isglobal|ishandle|ishghandle|ishold|isinf|isinteger|isjava|iskeyword|isletter|islogical|ismac|ismatrix|ismember|ismethod|isnan|isnumeric|isobject|isocaps|isocolors|isonormals|isosurface|ispc|ispref|isprime|isprop|isreal|isrow|isscalar|issorted|isspace|issparse|isstr|isstrprop|isstruct|isstudent|isunix|isvarname|isvector|javaaddpath|javaArray|javachk|javaclasspath|javacomponent|javaMethod|javaMethodEDT|javaObject|javaObjectEDT|javarmpath|jet|keyboard|kron|lasterr|lasterror|lastwarn|lcm|ldivide|ldl|le|legend|legendre|length|libfunctions|libfunctionsview|libisloaded|libpointer|libstruct|license|light|lightangle|lighting|lin2mu|line|lines|linkaxes|linkdata|linkprop|linsolve|linspace|listdlg|listfonts|load|loadlibrary|loadobj|log|log10|log1p|log2|loglog|logm|logspace|lookfor|lower|ls|lscov|lsqnonneg|lsqr|lt|lu|luinc|magic|makehgtform|mat2cell|mat2str|material|matfile|matlab.io.MatFile|matlab.mixin.(?:Copyable|Heterogeneous(?:.getDefaultScalarElement)?)|matlabrc|matlabroot|max|maxNumCompThreads|mean|median|membrane|memmapfile|memory|menu|mesh|meshc|meshgrid|meshz|meta.(?:class(?:.fromName)?|DynamicProperty|EnumeratedValue|event|MetaData|method|package(?:.(?:fromName|getAllPackages))?|property)|metaclass|methods|methodsview|mex(?:.getCompilerConfigurations)?|MException|mexext|mfilename|min|minres|minus|mislocked|mkdir|mkpp|mldivide|mlint|mlintrpt|mlock|mmfileinfo|mmreader|mod|mode|more|move|movefile|movegui|movie|movie2avi|mpower|mrdivide|msgbox|mtimes|mu2lin|multibandread|multibandwrite|munlock|namelengthmax|nargchk|narginchk|nargoutchk|native2unicode|nccreate|ncdisp|nchoosek|ncinfo|ncread|ncreadatt|ncwrite|ncwriteatt|ncwriteschema|ndgrid|ndims|ne|NET(?:.(?:addAssembly|Assembly|convertArray|createArray|createGeneric|disableAutoRelease|enableAutoRelease|GenericClass|invokeGenericMethod|NetException|setStaticProperty))?|netcdf.(?:abort|close|copyAtt|create|defDim|defGrp|defVar|defVarChunking|defVarDeflate|defVarFill|defVarFletcher32|delAtt|endDef|getAtt|getChunkCache|getConstant|getConstantNames|getVar|inq|inqAtt|inqAttID|inqAttName|inqDim|inqDimID|inqDimIDs|inqFormat|inqGrpName|inqGrpNameFull|inqGrpParent|inqGrps|inqLibVers|inqNcid|inqUnlimDims|inqVar|inqVarChunking|inqVarDeflate|inqVarFill|inqVarFletcher32|inqVarID|inqVarIDs|open|putAtt|putVar|reDef|renameAtt|renameDim|renameVar|setChunkCache|setDefaultFormat|setFill|sync)|newplot|nextpow2|nnz|noanimate|nonzeros|norm|normest|not|notebook|now|nthroot|null|num2cell|num2hex|num2str|numel|nzmax|ode(?:113|15i|15s|23|23s|23t|23tb|45)|odeget|odeset|odextend|onCleanup|ones|open|openfig|opengl|openvar|optimget|optimset|or|ordeig|orderfields|ordqz|ordschur|orient|orth|pack|padecoef|pagesetupdlg|pan|pareto|parseSoapResponse|pascal|patch|path|path2rc|pathsep|pathtool|pause|pbaspect|pcg|pchip|pcode|pcolor|pdepe|pdeval|peaks|perl|perms|permute|pie|pink|pinv|planerot|playshow|plot|plot3|plotbrowser|plotedit|plotmatrix|plottools|plotyy|plus|pol2cart|polar|poly|polyarea|polyder|polyeig|polyfit|polyint|polyval|polyvalm|pow2|power|ppval|prefdir|preferences|primes|print|printdlg|printopt|printpreview|prod|profile|profsave|propedit|propertyeditor|psi|publish|PutCharArray|PutFullMatrix|PutWorkspaceData|pwd|qhull|qmr|qr|qrdelete|qrinsert|qrupdate|quad|quad2d|quadgk|quadl|quadv|questdlg|quit|quiver|quiver3|qz|rand|randi|randn|randperm|RandStream(?:.(?:create|getDefaultStream|getGlobalStream|list|setDefaultStream|setGlobalStream))?|rank|rat|rats|rbbox|rcond|rdivide|readasync|real|reallog|realmax|realmin|realpow|realsqrt|record|rectangle|rectint|recycle|reducepatch|reducevolume|refresh|refreshdata|regexp|regexpi|regexprep|regexptranslate|rehash|rem|Remove|RemoveAll|repmat|reset|reshape|residue|restoredefaultpath|rethrow|rgb2hsv|rgb2ind|rgbplot|ribbon|rmappdata|rmdir|rmfield|rmpath|rmpref|rng|roots|rose|rosser|rot90|rotate|rotate3d|round|rref|rsf2csf|run|save|saveas|saveobj|savepath|scatter|scatter3|schur|sec|secd|sech|selectmoveresize|semilogx|semilogy|sendmail|serial|set|setappdata|setdiff|setenv|setfield|setpixelposition|setpref|setstr|setxor|shading|shg|shiftdim|showplottool|shrinkfaces|sign|sin(?:d|h)?|size|slice|smooth3|snapnow|sort|sortrows|sound|soundsc|spalloc|spaugment|spconvert|spdiags|specular|speye|spfun|sph2cart|sphere|spinmap|spline|spones|spparms|sprand|sprandn|sprandsym|sprank|spring|sprintf|spy|sqrt|sqrtm|squeeze|ss2tf|sscanf|stairs|startup|std|stem|stem3|stopasync|str2double|str2func|str2mat|str2num|strcat|strcmp|strcmpi|stream2|stream3|streamline|streamparticles|streamribbon|streamslice|streamtube|strfind|strjust|strmatch|strncmp|strncmpi|strread|strrep|strtok|strtrim|struct2cell|structfun|strvcat|sub2ind|subplot|subsasgn|subsindex|subspace|subsref|substruct|subvolume|sum|summer|superclasses|superiorto|support|surf|surf2patch|surface|surfc|surfl|surfnorm|svd|svds|swapbytes|symamd|symbfact|symmlq|symrcm|symvar|system|tan(?:d|h)?|tar|tempdir|tempname|tetramesh|texlabel|text|textread|textscan|textwrap|tfqmr|throw|tic|Tiff(?:.(?:getTagNames|getVersion))?|timer|timerfind|timerfindall|times|timeseries|title|toc|todatenum|toeplitz|toolboxdir|trace|transpose|trapz|treelayout|treeplot|tril|trimesh|triplequad|triplot|TriRep|TriScatteredInterp|trisurf|triu|tscollection|tsearch|tsearchn|tstool|type|typecast|uibuttongroup|uicontextmenu|uicontrol|uigetdir|uigetfile|uigetpref|uiimport|uimenu|uiopen|uipanel|uipushtool|uiputfile|uiresume|uisave|uisetcolor|uisetfont|uisetpref|uistack|uitable|uitoggletool|uitoolbar|uiwait|uminus|undocheckout|unicode2native|union|unique|unix|unloadlibrary|unmesh|unmkpp|untar|unwrap|unzip|uplus|upper|urlread|urlwrite|usejava|userpath|validateattributes|validatestring|vander|var|vectorize|ver|verctrl|verLessThan|version|vertcat|VideoReader(?:.isPlatformSupported)?|VideoWriter(?:.getProfiles)?|view|viewmtx|visdiff|volumebounds|voronoi|voronoin|wait|waitbar|waitfor|waitforbuttonpress|warndlg|warning|waterfall|wavfinfo|wavplay|wavread|wavrecord|wavwrite|web|weekday|what|whatsnew|which|whitebg|who|whos|wilkinson|winopen|winqueryreg|winter|wk1finfo|wk1read|wk1write|workspace|xlabel|xlim|xlsfinfo|xlsread|xlswrite|xmlread|xmlwrite|xor|xslt|ylabel|ylim|zeros|zip|zlabel|zlim|zoom)\b/,null],
+["fun_tbx",/^\b(?:addedvarplot|andrewsplot|anova(?:1|2|n)|ansaribradley|aoctool|barttest|bbdesign|beta(?:cdf|fit|inv|like|pdf|rnd|stat)|bino(?:cdf|fit|inv|pdf|rnd|stat)|biplot|bootci|bootstrp|boxplot|candexch|candgen|canoncorr|capability|capaplot|caseread|casewrite|categorical|ccdesign|cdfplot|chi2(?:cdf|gof|inv|pdf|rnd|stat)|cholcov|Classification(?:BaggedEnsemble|Discriminant(?:.(?:fit|make|template))?|Ensemble|KNN(?:.(?:fit|template))?|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|classify|classregtree|cluster|clusterdata|cmdscale|combnk|Compact(?:Classification(?:Discriminant|Ensemble|Tree)|Regression(?:Ensemble|Tree)|TreeBagger)|confusionmat|controlchart|controlrules|cophenet|copula(?:cdf|fit|param|pdf|rnd|stat)|cordexch|corr|corrcov|coxphfit|createns|crosstab|crossval|cvpartition|datasample|dataset|daugment|dcovary|dendrogram|dfittool|disttool|dummyvar|dwtest|ecdf|ecdfhist|ev(?:cdf|fit|inv|like|pdf|rnd|stat)|ExhaustiveSearcher|exp(?:cdf|fit|inv|like|pdf|rnd|stat)|factoran|fcdf|ff2n|finv|fitdist|fitensemble|fpdf|fracfact|fracfactgen|friedman|frnd|fstat|fsurfht|fullfact|gagerr|gam(?:cdf|fit|inv|like|pdf|rnd|stat)|GeneralizedLinearModel(?:.fit)?|geo(?:cdf|inv|mean|pdf|rnd|stat)|gev(?:cdf|fit|inv|like|pdf|rnd|stat)|gline|glmfit|glmval|glyphplot|gmdistribution(?:.fit)?|gname|gp(?:cdf|fit|inv|like|pdf|rnd|stat)|gplotmatrix|grp2idx|grpstats|gscatter|haltonset|harmmean|hist3|histfit|hmm(?:decode|estimate|generate|train|viterbi)|hougen|hyge(?:cdf|inv|pdf|rnd|stat)|icdf|inconsistent|interactionplot|invpred|iqr|iwishrnd|jackknife|jbtest|johnsrnd|KDTreeSearcher|kmeans|knnsearch|kruskalwallis|ksdensity|kstest|kstest2|kurtosis|lasso|lassoglm|lassoPlot|leverage|lhsdesign|lhsnorm|lillietest|LinearModel(?:.fit)?|linhyptest|linkage|logn(?:cdf|fit|inv|like|pdf|rnd|stat)|lsline|mad|mahal|maineffectsplot|manova1|manovacluster|mdscale|mhsample|mle|mlecov|mnpdf|mnrfit|mnrnd|mnrval|moment|multcompare|multivarichart|mvn(?:cdf|pdf|rnd)|mvregress|mvregresslike|mvt(?:cdf|pdf|rnd)|NaiveBayes(?:.fit)?|nan(?:cov|max|mean|median|min|std|sum|var)|nbin(?:cdf|fit|inv|pdf|rnd|stat)|ncf(?:cdf|inv|pdf|rnd|stat)|nct(?:cdf|inv|pdf|rnd|stat)|ncx2(?:cdf|inv|pdf|rnd|stat)|NeighborSearcher|nlinfit|nlintool|nlmefit|nlmefitsa|nlparci|nlpredci|nnmf|nominal|NonLinearModel(?:.fit)?|norm(?:cdf|fit|inv|like|pdf|rnd|stat)|normplot|normspec|ordinal|outlierMeasure|parallelcoords|paretotails|partialcorr|pcacov|pcares|pdf|pdist|pdist2|pearsrnd|perfcurve|perms|piecewisedistribution|plsregress|poiss(?:cdf|fit|inv|pdf|rnd|tat)|polyconf|polytool|prctile|princomp|ProbDist(?:Kernel|Parametric|UnivKernel|UnivParam)?|probplot|procrustes|qqplot|qrandset|qrandstream|quantile|randg|random|randsample|randtool|range|rangesearch|ranksum|rayl(?:cdf|fit|inv|pdf|rnd|stat)|rcoplot|refcurve|refline|regress|Regression(?:BaggedEnsemble|Ensemble|PartitionedEnsemble|PartitionedModel|Tree(?:.(?:fit|template))?)|regstats|relieff|ridge|robustdemo|robustfit|rotatefactors|rowexch|rsmdemo|rstool|runstest|sampsizepwr|scatterhist|sequentialfs|signrank|signtest|silhouette|skewness|slicesample|sobolset|squareform|statget|statset|stepwise|stepwisefit|surfht|tabulate|tblread|tblwrite|tcdf|tdfread|tiedrank|tinv|tpdf|TreeBagger|treedisp|treefit|treeprune|treetest|treeval|trimmean|trnd|tstat|ttest|ttest2|unid(?:cdf|inv|pdf|rnd|stat)|unif(?:cdf|inv|it|pdf|rnd|stat)|vartest(?:2|n)?|wbl(?:cdf|fit|inv|like|pdf|rnd|stat)|wblplot|wishrnd|x2fx|xptread|zscore|ztest)\b/,
+null],["fun_tbx",/^\b(?:adapthisteq|analyze75info|analyze75read|applycform|applylut|axes2pix|bestblk|blockproc|bwarea|bwareaopen|bwboundaries|bwconncomp|bwconvhull|bwdist|bwdistgeodesic|bweuler|bwhitmiss|bwlabel|bwlabeln|bwmorph|bwpack|bwperim|bwselect|bwtraceboundary|bwulterode|bwunpack|checkerboard|col2im|colfilt|conndef|convmtx2|corner|cornermetric|corr2|cp2tform|cpcorr|cpselect|cpstruct2pairs|dct2|dctmtx|deconvblind|deconvlucy|deconvreg|deconvwnr|decorrstretch|demosaic|dicom(?:anon|dict|info|lookup|read|uid|write)|edge|edgetaper|entropy|entropyfilt|fan2para|fanbeam|findbounds|fliptform|freqz2|fsamp2|fspecial|ftrans2|fwind1|fwind2|getheight|getimage|getimagemodel|getline|getneighbors|getnhood|getpts|getrangefromclass|getrect|getsequence|gray2ind|graycomatrix|graycoprops|graydist|grayslice|graythresh|hdrread|hdrwrite|histeq|hough|houghlines|houghpeaks|iccfind|iccread|iccroot|iccwrite|idct2|ifanbeam|im2bw|im2col|im2double|im2int16|im2java2d|im2single|im2uint16|im2uint8|imabsdiff|imadd|imadjust|ImageAdapter|imageinfo|imagemodel|imapplymatrix|imattributes|imbothat|imclearborder|imclose|imcolormaptool|imcomplement|imcontour|imcontrast|imcrop|imdilate|imdisplayrange|imdistline|imdivide|imellipse|imerode|imextendedmax|imextendedmin|imfill|imfilter|imfindcircles|imfreehand|imfuse|imgca|imgcf|imgetfile|imhandles|imhist|imhmax|imhmin|imimposemin|imlincomb|imline|immagbox|immovie|immultiply|imnoise|imopen|imoverview|imoverviewpanel|impixel|impixelinfo|impixelinfoval|impixelregion|impixelregionpanel|implay|impoint|impoly|impositionrect|improfile|imputfile|impyramid|imreconstruct|imrect|imregconfig|imregionalmax|imregionalmin|imregister|imresize|imroi|imrotate|imsave|imscrollpanel|imshow|imshowpair|imsubtract|imtool|imtophat|imtransform|imview|ind2gray|ind2rgb|interfileinfo|interfileread|intlut|ippl|iptaddcallback|iptcheckconn|iptcheckhandle|iptcheckinput|iptcheckmap|iptchecknargin|iptcheckstrs|iptdemos|iptgetapi|iptGetPointerBehavior|iptgetpref|ipticondir|iptnum2ordinal|iptPointerManager|iptprefs|iptremovecallback|iptSetPointerBehavior|iptsetpref|iptwindowalign|iradon|isbw|isflat|isgray|isicc|isind|isnitf|isrgb|isrset|lab2double|lab2uint16|lab2uint8|label2rgb|labelmatrix|makecform|makeConstrainToRectFcn|makehdr|makelut|makeresampler|maketform|mat2gray|mean2|medfilt2|montage|nitfinfo|nitfread|nlfilter|normxcorr2|ntsc2rgb|openrset|ordfilt2|otf2psf|padarray|para2fan|phantom|poly2mask|psf2otf|qtdecomp|qtgetblk|qtsetblk|radon|rangefilt|reflect|regionprops|registration.metric.(?:MattesMutualInformation|MeanSquares)|registration.optimizer.(?:OnePlusOneEvolutionary|RegularStepGradientDescent)|rgb2gray|rgb2ntsc|rgb2ycbcr|roicolor|roifill|roifilt2|roipoly|rsetwrite|std2|stdfilt|strel|stretchlim|subimage|tformarray|tformfwd|tforminv|tonemap|translate|truesize|uintlut|viscircles|warp|watershed|whitepoint|wiener2|xyz2double|xyz2uint16|ycbcr2rgb)\b/,
+null],["fun_tbx",/^\b(?:bintprog|color|fgoalattain|fminbnd|fmincon|fminimax|fminsearch|fminunc|fseminf|fsolve|fzero|fzmult|gangstr|ktrlink|linprog|lsqcurvefit|lsqlin|lsqnonlin|lsqnonneg|optimget|optimset|optimtool|quadprog)\b/,null],["ident",/^[a-zA-Z][a-zA-Z0-9_]*(?:\.[a-zA-Z][a-zA-Z0-9_]*)*/,null]]),["matlab-identifiers"]);a.registerLangHandler(a.createSimpleLexer([],d),["matlab-operators"]);a.registerLangHandler(a.createSimpleLexer(b,c),["matlab"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-ml.js b/src/main/jbake/assets/docs/google-code-prettify/lang-ml.js
new file mode 100644
index 0000000..c012a3f
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-ml.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^#(?:if[\t\n\r \xA0]+(?:[a-z_$][\w\']*|``[^\r\n\t`]*(?:``|$))|else|endif|light)/i,null,"#"],["str",/^(?:\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\]|\\[\s\S])(?:\'|$))/,null,"\"'"]],[["com",/^(?:\/\/[^\r\n]*|\(\*[\s\S]*?\*\))/],["kwd",/^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\b/],
+["lit",/^[+\-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^(?:[a-z_][\w']*[!?#]?|``[^\r\n\t`]*(?:``|$))/i],["pun",/^[^\t\n\r \xA0\"\'\w]+/]]),["fs","ml"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-mumps.js b/src/main/jbake/assets/docs/google-code-prettify/lang-mumps.js
new file mode 100644
index 0000000..6d51258
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-mumps.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2011 Kitware Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^"]|\\.)*")/,null,'"']],[["com",/^;[^\r\n]*/,null,";"],["dec",/^(?:\$(?:D|DEVICE|EC|ECODE|ES|ESTACK|ET|ETRAP|H|HOROLOG|I|IO|J|JOB|K|KEY|P|PRINCIPAL|Q|QUIT|ST|STACK|S|STORAGE|SY|SYSTEM|T|TEST|TL|TLEVEL|TR|TRESTART|X|Y|Z[A-Z]*|A|ASCII|C|CHAR|D|DATA|E|EXTRACT|F|FIND|FN|FNUMBER|G|GET|J|JUSTIFY|L|LENGTH|NA|NAME|O|ORDER|P|PIECE|QL|QLENGTH|QS|QSUBSCRIPT|Q|QUERY|R|RANDOM|RE|REVERSE|S|SELECT|ST|STACK|T|TEXT|TR|TRANSLATE|NaN))\b/i,
+null],["kwd",/^(?:[^\$]B|BREAK|C|CLOSE|D|DO|E|ELSE|F|FOR|G|GOTO|H|HALT|H|HANG|I|IF|J|JOB|K|KILL|L|LOCK|M|MERGE|N|NEW|O|OPEN|Q|QUIT|R|READ|S|SET|TC|TCOMMIT|TRE|TRESTART|TRO|TROLLBACK|TS|TSTART|U|USE|V|VIEW|W|WRITE|X|XECUTE)\b/i,null],["lit",/^[+-]?(?:(?:\.\d+|\d+(?:\.\d*)?)(?:E[+\-]?\d+)?)/i],["pln",/^[a-z][a-zA-Z0-9]*/i],["pun",/^[^\w\t\n\r\xA0\"\$;%\^]|_/]]),["mumps"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-n.js b/src/main/jbake/assets/docs/google-code-prettify/lang-n.js
new file mode 100644
index 0000000..9b3910c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-n.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2011 Zimin A.V.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*\'|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"],["pln",/^\s+/,null," \r\n\t\u00a0"]],[["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null],["str",/^<#(?:[^#>])*(?:#>|$)/,null],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null],["com",/^\/\/[^\r\n]*/,
+null],["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
+null],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,null],["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^@[A-Z]+[a-z][A-Za-z_$@0-9]*/,null],["pln",/^'?[A-Za-z_$][a-z_$@0-9]*/i,null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pun",/^.[^\s\w\.$@\'\"\`\/\#]*/,null]]),["n","nemerle"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-nemerle.js b/src/main/jbake/assets/docs/google-code-prettify/lang-nemerle.js
new file mode 100644
index 0000000..9b3910c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-nemerle.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2011 Zimin A.V.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*\'|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,'"'],["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"],["pln",/^\s+/,null," \r\n\t\u00a0"]],[["str",/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null],["str",/^<#(?:[^#>])*(?:#>|$)/,null],["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null],["com",/^\/\/[^\r\n]*/,
+null],["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null],["kwd",/^(?:abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield)\b/,
+null],["typ",/^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\b/,null],["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^@[A-Z]+[a-z][A-Za-z_$@0-9]*/,null],["pln",/^'?[A-Za-z_$][a-z_$@0-9]*/i,null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pun",/^.[^\s\w\.$@\'\"\`\/\#]*/,null]]),["n","nemerle"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-pascal.js b/src/main/jbake/assets/docs/google-code-prettify/lang-pascal.js
new file mode 100644
index 0000000..c76a11c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-pascal.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2013 Peter Kofler
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["str",/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$))/,null,"'"],["pln",/^\s+/,null," \r\n\t\u00a0"]],[["com",/^\(\*[\s\S]*?(?:\*\)|$)|^\{[\s\S]*?(?:\}|$)/,null],["kwd",/^(?:ABSOLUTE|AND|ARRAY|ASM|ASSEMBLER|BEGIN|CASE|CONST|CONSTRUCTOR|DESTRUCTOR|DIV|DO|DOWNTO|ELSE|END|EXTERNAL|FOR|FORWARD|FUNCTION|GOTO|IF|IMPLEMENTATION|IN|INLINE|INTERFACE|INTERRUPT|LABEL|MOD|NOT|OBJECT|OF|OR|PACKED|PROCEDURE|PROGRAM|RECORD|REPEAT|SET|SHL|SHR|THEN|TO|TYPE|UNIT|UNTIL|USES|VAR|VIRTUAL|WHILE|WITH|XOR)\b/i,
+null],["lit",/^(?:true|false|self|nil)/i,null],["pln",/^[a-z][a-z0-9]*/i,null],["lit",/^(?:\$[a-f0-9]+|(?:\d+(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?)/i,null,"0123456789"],["pun",/^.[^\s\w\.$@\'\/]*/,null]]),["pascal"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-proto.js b/src/main/jbake/assets/docs/google-code-prettify/lang-proto.js
new file mode 100644
index 0000000..3215ff6
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-proto.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2006 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.sourceDecorator({keywords:"bytes,default,double,enum,extend,extensions,false,group,import,max,message,option,optional,package,repeated,required,returns,rpc,service,syntax,to,true",types:/^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\b/,cStyleComments:!0}),["proto"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-r.js b/src/main/jbake/assets/docs/google-code-prettify/lang-r.js
new file mode 100644
index 0000000..6ce16e8
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-r.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2012 Jeffrey B. Arnold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"]],[["com",/^#.*/],["kwd",/^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![A-Za-z0-9_.])/],["lit",/^0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?/],["lit",/^[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?[Li]?/],["lit",/^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\.\.(?:\.|[0-9]+))(?![A-Za-z0-9_.])/],
+["pun",/^(?:<-|->>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|\*|\+|\^|\/|!|%.*?%|=|~|\$|@|:{1,3}|[\[\](){};,?])/],["pln",/^(?:[A-Za-z]+[A-Za-z0-9_.]*|\.[a-zA-Z_][0-9a-zA-Z\._]*)(?![A-Za-z0-9_.])/],["str",/^`.+`/]]),["r","s","R","S","Splus"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-rd.js b/src/main/jbake/assets/docs/google-code-prettify/lang-rd.js
new file mode 100644
index 0000000..113141c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-rd.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2012 Jeffrey Arnold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\r\n]*/,null,"%"]],[["lit",/^\\(?:cr|l?dots|R|tab)\b/],["kwd",/^\\[a-zA-Z@]+/],["kwd",/^#(?:ifn?def|endif)/],["pln",/^\\[{}]/],["pun",/^[{}()\[\]]+/]]),["Rd","rd"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-rkt.js b/src/main/jbake/assets/docs/google-code-prettify/lang-rkt.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-rkt.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-rust.js b/src/main/jbake/assets/docs/google-code-prettify/lang-rust.js
new file mode 100644
index 0000000..4385677
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-rust.js
@@ -0,0 +1,20 @@
+/*
+
+ Copyright (C) 2015 Chris Morgan
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([],[["pln",/^[\t\n\r \xA0]+/],["com",/^\/\/.*/],["com",/^\/\*[\s\S]*?(?:\*\/|$)/],["str",/^b"(?:[^\\]|\\(?:.|x[\da-fA-F]{2}))*?"/],["str",/^"(?:[^\\]|\\(?:.|x[\da-fA-F]{2}|u\{\[\da-fA-F]{1,6}\}))*?"/],["str",/^b?r(#*)\"[\s\S]*?\"\1/],["str",/^b'([^\\]|\\(.|x[\da-fA-F]{2}))'/],["str",/^'([^\\]|\\(.|x[\da-fA-F]{2}|u\{[\da-fA-F]{1,6}\}))'/],["tag",/^'\w+?\b/],["kwd",/^(?:match|if|else|as|break|box|continue|extern|fn|for|in|if|impl|let|loop|pub|return|super|unsafe|where|while|use|mod|trait|struct|enum|type|move|mut|ref|static|const|crate)\b/],
+["kwd",/^(?:alignof|become|do|offsetof|priv|pure|sizeof|typeof|unsized|yield|abstract|virtual|final|override|macro)\b/],["typ",/^(?:[iu](8|16|32|64|size)|char|bool|f32|f64|str|Self)\b/],["typ",/^(?:Copy|Send|Sized|Sync|Drop|Fn|FnMut|FnOnce|Box|ToOwned|Clone|PartialEq|PartialOrd|Eq|Ord|AsRef|AsMut|Into|From|Default|Iterator|Extend|IntoIterator|DoubleEndedIterator|ExactSizeIterator|Option|Some|None|Result|Ok|Err|SliceConcatExt|String|ToString|Vec)\b/],["lit",/^(self|true|false|null)\b/],
+["lit",/^\d[0-9_]*(?:[iu](?:size|8|16|32|64))?/],["lit",/^0x[a-fA-F0-9_]+(?:[iu](?:size|8|16|32|64))?/],["lit",/^0o[0-7_]+(?:[iu](?:size|8|16|32|64))?/],["lit",/^0b[01_]+(?:[iu](?:size|8|16|32|64))?/],["lit",/^\d[0-9_]*\.(?![^\s\d.])/],["lit",/^\d[0-9_]*(?:\.\d[0-9_]*)(?:[eE][+-]?[0-9_]+)?(?:f32|f64)?/],["lit",/^\d[0-9_]*(?:\.\d[0-9_]*)?(?:[eE][+-]?[0-9_]+)(?:f32|f64)?/],["lit",/^\d[0-9_]*(?:\.\d[0-9_]*)?(?:[eE][+-]?[0-9_]+)?(?:f32|f64)/],
+["atn",/^[a-z_]\w*!/i],["pln",/^[a-z_]\w*/i],["atv",/^#!?\[[\s\S]*?\]/],["pun",/^[+\-/*=^&|!<>%[\](){}?:.,;]/],["pln",/./]]),["rust"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-s.js b/src/main/jbake/assets/docs/google-code-prettify/lang-s.js
new file mode 100644
index 0000000..6ce16e8
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-s.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2012 Jeffrey B. Arnold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"]],[["com",/^#.*/],["kwd",/^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![A-Za-z0-9_.])/],["lit",/^0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?/],["lit",/^[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?[Li]?/],["lit",/^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\.\.(?:\.|[0-9]+))(?![A-Za-z0-9_.])/],
+["pun",/^(?:<-|->>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|\*|\+|\^|\/|!|%.*?%|=|~|\$|@|:{1,3}|[\[\](){};,?])/],["pln",/^(?:[A-Za-z]+[A-Za-z0-9_.]*|\.[a-zA-Z_][0-9a-zA-Z\._]*)(?![A-Za-z0-9_.])/],["str",/^`.+`/]]),["r","s","R","S","Splus"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-scala.js b/src/main/jbake/assets/docs/google-code-prettify/lang-scala.js
new file mode 100644
index 0000000..3347dd6
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-scala.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2010 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:(?:""(?:""?(?!")|[^\\"]|\\.)*"{0,3})|(?:[^"\r\n\\]|\\.)*"?))/,null,'"'],["lit",/^`(?:[^\r\n\\`]|\\.)*`?/,null,"`"],["pun",/^[!#%&()*+,\-:;<=>?@\[\\\]^{|}~]+/,null,"!#%&()*+,-:;<=>?@[\\]^{|}~"]],[["str",/^'(?:[^\r\n\\']|\\(?:'|[^\r\n']+))'/],["lit",/^'[a-zA-Z_$][\w$]*(?!['$\w])/],["kwd",/^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\b/],
+["lit",/^(?:true|false|null|this)\b/],["lit",/^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\.[0-9]+)?(?:E[+\-]?[0-9]+)?F?|L?))|\\.[0-9]+(?:E[+\-]?[0-9]+)?F?)/i],["typ",/^[$_]*[A-Z][_$A-Z0-9]*[a-z][\w$]*/],["pln",/^[$a-zA-Z_][\w$]*/],["com",/^\/(?:\/.*|\*(?:\/|\**[^*/])*(?:\*+\/?)?)/],["pun",/^(?:\.+|\/)/]]),["scala"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-scm.js b/src/main/jbake/assets/docs/google-code-prettify/lang-scm.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-scm.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-splus.js b/src/main/jbake/assets/docs/google-code-prettify/lang-splus.js
new file mode 100644
index 0000000..6ce16e8
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-splus.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2012 Jeffrey B. Arnold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"'],["str",/^\'(?:[^\'\\]|\\[\s\S])*(?:\'|$)/,null,"'"]],[["com",/^#.*/],["kwd",/^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![A-Za-z0-9_.])/],["lit",/^0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?/],["lit",/^[+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+)([eE][+-]?[0-9]+)?[Li]?/],["lit",/^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\.\.(?:\.|[0-9]+))(?![A-Za-z0-9_.])/],
+["pun",/^(?:<-|->>?|-|==|<=|>=|<|>|&&?|!=|\|\|?|\*|\+|\^|\/|!|%.*?%|=|~|\$|@|:{1,3}|[\[\](){};,?])/],["pln",/^(?:[A-Za-z]+[A-Za-z0-9_.]*|\.[a-zA-Z_][0-9a-zA-Z\._]*)(?![A-Za-z0-9_.])/],["str",/^`.+`/]]),["r","s","R","S","Splus"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-sql.js b/src/main/jbake/assets/docs/google-code-prettify/lang-sql.js
new file mode 100644
index 0000000..a7d292c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-sql.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^(?:"(?:[^\"\\]|\\.)*"|'(?:[^\'\\]|\\.)*')/,null,"\"'"]],[["com",/^(?:--[^\r\n]*|\/\*[\s\S]*?(?:\*\/|$))/],["kwd",/^(?:ADD|ALL|ALTER|AND|ANY|APPLY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONNECT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOLLOWING|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|MATCH|MATCHED|MERGE|NATURAL|NATIONAL|NOCHECK|NONCLUSTERED|NOCYCLE|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PARTITION|PERCENT|PIVOT|PLAN|PRECEDING|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|ROWS?|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|START|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNBOUNDED|UNION|UNIQUE|UNPIVOT|UPDATE|UPDATETEXT|USE|USER|USING|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WITHIN|WRITETEXT|XML)(?=[^\w-]|$)/i,
+null],["lit",/^[+-]?(?:0x[\da-f]+|(?:(?:\.\d+|\d+(?:\.\d*)?)(?:e[+\-]?\d+)?))/i],["pln",/^[a-z_][\w-]*/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0+\-\"\']*/]]),["sql"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-ss.js b/src/main/jbake/assets/docs/google-code-prettify/lang-ss.js
new file mode 100644
index 0000000..2f18c96
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-ss.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2008 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\(+/,null,"("],["clo",/^\)+/,null,")"],["com",/^;[^\r\n]*/,null,";"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\b/,
+null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),"cl el lisp lsp scm ss rkt".split(" "));
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-swift.js b/src/main/jbake/assets/docs/google-code-prettify/lang-swift.js
new file mode 100644
index 0000000..eb5c314
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-swift.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2015 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[ \n\r\t\v\f\0]+/,null," \n\r\t\v\f\x00"],["str",/^"(?:[^"\\]|(?:\\.)|(?:\\\((?:[^"\\)]|\\.)*\)))*"/,null,'"']],[["lit",/^(?:(?:0x[\da-fA-F][\da-fA-F_]*\.[\da-fA-F][\da-fA-F_]*[pP]?)|(?:\d[\d_]*\.\d[\d_]*[eE]?))[+-]?\d[\d_]*/,null],["lit",/^-?(?:(?:0(?:(?:b[01][01_]*)|(?:o[0-7][0-7_]*)|(?:x[\da-fA-F][\da-fA-F_]*)))|(?:\d[\d_]*))/,null],["lit",/^(?:true|false|nil)\b/,null],["kwd",/^\b(?:__COLUMN__|__FILE__|__FUNCTION__|__LINE__|#available|#else|#elseif|#endif|#if|#line|arch|arm|arm64|associativity|as|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|dynamicType|else|enum|extension|fallthrough|final|for|func|get|guard|import|indirect|infix|init|inout|internal|i386|if|in|iOS|iOSApplicationExtension|is|lazy|left|let|mutating|none|nonmutating|operator|optional|OSX|OSXApplicationExtension|override|postfix|precedence|prefix|private|protocol|Protocol|public|required|rethrows|return|right|safe|self|set|static|struct|subscript|super|switch|throw|try|Type|typealias|unowned|unsafe|var|weak|watchOS|while|willSet|x86_64)\b/,
+null],["com",/^\/\/.*?[\n\r]/,null],["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null],["pun",/^<<=|<=|<<|>>=|>=|>>|===|==|\.\.\.|&&=|\.\.<|!==|!=|&=|~=|~|\(|\)|\[|\]|{|}|@|#|;|\.|,|:|\|\|=|\?\?|\|\||&&|&\*|&\+|&-|&=|\+=|-=|\/=|\*=|\^=|%=|\|=|->|`|==|\+\+|--|\/|\+|!|\*|%|<|>|&|\||\^|\?|=|-|_/,null],["typ",/^\b(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null]]),["swift"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-tcl.js b/src/main/jbake/assets/docs/google-code-prettify/lang-tcl.js
new file mode 100644
index 0000000..1d75c3f
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-tcl.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2012 Pyrios
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["opn",/^\{+/,null,"{"],["clo",/^\}+/,null,"}"],["com",/^#[^\r\n]*/,null,"#"],["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["str",/^\"(?:[^\"\\]|\\[\s\S])*(?:\"|$)/,null,'"']],[["kwd",/^(?:after|append|apply|array|break|case|catch|continue|error|eval|exec|exit|expr|for|foreach|if|incr|info|proc|return|set|switch|trace|uplevel|upvar|while)\b/,null],["lit",/^[+\-]?(?:[0#]x[0-9a-f]+|\d+\/\d+|(?:\.\d+|\d+(?:\.\d*)?)(?:[ed][+\-]?\d+)?)/i],
+["lit",/^\'(?:-*(?:\w|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?)?/],["pln",/^-*(?:[a-z_]|\\[\x21-\x7e])(?:[\w-]*|\\[\x21-\x7e])[=!?]?/i],["pun",/^[^\w\t\n\r \xA0()\"\\\';]+/]]),["tcl"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-tex.js b/src/main/jbake/assets/docs/google-code-prettify/lang-tex.js
new file mode 100644
index 0000000..efc758c
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-tex.js
@@ -0,0 +1,17 @@
+/*
+
+ Copyright (C) 2011 Martin S.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"],["com",/^%[^\r\n]*/,null,"%"]],[["kwd",/^\\[a-zA-Z@]+/],["kwd",/^\\./],["typ",/^[$&]/],["lit",/[+-]?(?:\.\d+|\d+(?:\.\d*)?)(cm|em|ex|in|pc|pt|bp|mm)/i],["pun",/^[{}()\[\]=]+/]]),["latex","tex"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-vb.js b/src/main/jbake/assets/docs/google-code-prettify/lang-vb.js
new file mode 100644
index 0000000..e34086f
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-vb.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2009 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'"\u201c\u201d'],["com",/^[\'\u2018\u2019](?:_(?:\r\n?|[^\r]?)|[^\r\n_\u2028\u2029])*/,null,"'\u2018\u2019"]],[["kwd",/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,
+null],["com",/^REM\b[^\r\n\u2028\u2029]*/i],["lit",/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*(?:\[[%&@!#]+\])?|\[(?:[a-z]|_\w)\w*\])/i],["pun",/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],["pun",/^(?:\[|\])/]]),["vb",
+"vbs"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-vbs.js b/src/main/jbake/assets/docs/google-code-prettify/lang-vbs.js
new file mode 100644
index 0000000..e34086f
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-vbs.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2009 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0\u2028\u2029]+/,null,"\t\n\r \u00a0\u2028\u2029"],["str",/^(?:[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})(?:[\"\u201C\u201D]c|$)|[\"\u201C\u201D](?:[^\"\u201C\u201D]|[\"\u201C\u201D]{2})*(?:[\"\u201C\u201D]|$))/i,null,'"\u201c\u201d'],["com",/^[\'\u2018\u2019](?:_(?:\r\n?|[^\r]?)|[^\r\n_\u2028\u2029])*/,null,"'\u2018\u2019"]],[["kwd",/^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\b/i,
+null],["com",/^REM\b[^\r\n\u2028\u2029]*/i],["lit",/^(?:True\b|False\b|Nothing\b|\d+(?:E[+\-]?\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\d*\.\d+(?:E[+\-]?\d+)?[FRD]?|#\s+(?:\d+[\-\/]\d+[\-\/]\d+(?:\s+\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)?|\d+:\d+(?::\d+)?(\s*(?:AM|PM))?)\s+#)/i],["pln",/^(?:(?:[a-z]|_\w)\w*(?:\[[%&@!#]+\])?|\[(?:[a-z]|_\w)\w*\])/i],["pun",/^[^\w\t\n\r \"\'\[\]\xA0\u2018\u2019\u201C\u201D\u2028\u2029]+/],["pun",/^(?:\[|\])/]]),["vb",
+"vbs"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-vhd.js b/src/main/jbake/assets/docs/google-code-prettify/lang-vhd.js
new file mode 100644
index 0000000..f67a4a3
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-vhd.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2010 benoit@ryder.fr
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[BOX]?"(?:[^\"]|"")*"|'.')/i],["com",/^--[^\r\n]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
+null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w\\.]+#(?:[+\-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:E[+\-]?\d+(?:_\d+)*)?)/i],
+["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0\-\"\']*/]]),["vhdl","vhd"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-vhdl.js b/src/main/jbake/assets/docs/google-code-prettify/lang-vhdl.js
new file mode 100644
index 0000000..f67a4a3
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-vhdl.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2010 benoit@ryder.fr
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\r \xA0]+/,null,"\t\n\r \u00a0"]],[["str",/^(?:[BOX]?"(?:[^\"]|"")*"|'.')/i],["com",/^--[^\r\n]*/],["kwd",/^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\w-]|$)/i,
+null],["typ",/^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\w-]|$)/i,null],["typ",/^\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\w-]|$)/i,null],["lit",/^\d+(?:_\d+)*(?:#[\w\\.]+#(?:[+\-]?\d+(?:_\d+)*)?|(?:\.\d+(?:_\d+)*)?(?:E[+\-]?\d+(?:_\d+)*)?)/i],
+["pln",/^(?:[a-z]\w*|\\[^\\]*\\)/i],["pun",/^[^\w\t\n\r \xA0\"\'][^\w\t\n\r \xA0\-\"\']*/]]),["vhdl","vhd"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-wiki.js b/src/main/jbake/assets/docs/google-code-prettify/lang-wiki.js
new file mode 100644
index 0000000..d03fccd
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-wiki.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2009 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t \xA0a-gi-z0-9]+/,null,"\t \u00a0abcdefgijklmnopqrstuvwxyz0123456789"],["pun",/^[=*~\^\[\]]+/,null,"=*~^[]"]],[["lang-wiki.meta",/(?:^^|\r\n?|\n)(#[a-z]+)\b/],["lit",/^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\b/],["lang-",/^\{\{\{([\s\S]+?)\}\}\}/],["lang-",/^`([^\r\n`]+)`/],["str",/^https?:\/\/[^\/?#\s]*(?:\/[^?#\s]*)?(?:\?[^#\s]*)?(?:#\S*)?/i],["pln",/^(?:\r\n|[\s\S])[^#=*~^A-Zh\{`\[\r\n]*/]]),["wiki"]);
+PR.registerLangHandler(PR.createSimpleLexer([["kwd",/^#[a-z]+/i,null,"#"]],[]),["wiki.meta"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-xq.js b/src/main/jbake/assets/docs/google-code-prettify/lang-xq.js
new file mode 100644
index 0000000..a6d8537
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-xq.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2011 Patrick Wied
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["var pln",/^\$[A-Za-z0-9_\-]+/,null,"$"]],[["pln",/^[\s=][<>][\s=]/],["lit",/^\@[\w-]+/],["tag",/^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["com",/^\(:[\s\S]*?:\)/],["pln",/^[\/\{\};,\[\]\(\)]$/],["str",/^(?:\"(?:[^\"\\\{]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\\{]|\\[\s\S])*(?:\'|$))/,null,"\"'"],["kwd",/^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\b/],
+["typ",/^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\b/,null],["fun pln",/^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\b/],
+["pln",/^[A-Za-z0-9_\-\:]+/],["pln",/^[\t\n\r \xA0]+/]]),["xq","xquery"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-xquery.js b/src/main/jbake/assets/docs/google-code-prettify/lang-xquery.js
new file mode 100644
index 0000000..a6d8537
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-xquery.js
@@ -0,0 +1,19 @@
+/*
+
+ Copyright (C) 2011 Patrick Wied
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["var pln",/^\$[A-Za-z0-9_\-]+/,null,"$"]],[["pln",/^[\s=][<>][\s=]/],["lit",/^\@[\w-]+/],["tag",/^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],["com",/^\(:[\s\S]*?:\)/],["pln",/^[\/\{\};,\[\]\(\)]$/],["str",/^(?:\"(?:[^\"\\\{]|\\[\s\S])*(?:\"|$)|\'(?:[^\'\\\{]|\\[\s\S])*(?:\'|$))/,null,"\"'"],["kwd",/^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\b/],
+["typ",/^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\b/,null],["fun pln",/^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\b/],
+["pln",/^[A-Za-z0-9_\-\:]+/],["pln",/^[\t\n\r \xA0]+/]]),["xq","xquery"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-yaml.js b/src/main/jbake/assets/docs/google-code-prettify/lang-yaml.js
new file mode 100644
index 0000000..a2b4b07
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-yaml.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2015 ribrdb @ code.google.com
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:|>?]+/,null,":|>?"],["dec",/^%(?:YAML|TAG)[^#\r\n]+/,null,"%"],["typ",/^[&]\S+/,null,"&"],["typ",/^!\S*/,null,"!"],["str",/^"(?:[^\\"]|\\.)*(?:"|$)/,null,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,null,"'"],["com",/^#[^\r\n]*/,null,"#"],["pln",/^\s+/,null," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\r\n]|$)/],["pun",/^-/],["kwd",/^[\w-]+:[ \r\n]/],["pln",
+/^\w+/]]),["yaml","yml"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/lang-yml.js b/src/main/jbake/assets/docs/google-code-prettify/lang-yml.js
new file mode 100644
index 0000000..a2b4b07
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/lang-yml.js
@@ -0,0 +1,18 @@
+/*
+
+ Copyright (C) 2015 ribrdb @ code.google.com
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+PR.registerLangHandler(PR.createSimpleLexer([["pun",/^[:|>?]+/,null,":|>?"],["dec",/^%(?:YAML|TAG)[^#\r\n]+/,null,"%"],["typ",/^[&]\S+/,null,"&"],["typ",/^!\S*/,null,"!"],["str",/^"(?:[^\\"]|\\.)*(?:"|$)/,null,'"'],["str",/^'(?:[^']|'')*(?:'|$)/,null,"'"],["com",/^#[^\r\n]*/,null,"#"],["pln",/^\s+/,null," \t\r\n"]],[["dec",/^(?:---|\.\.\.)(?:[\r\n]|$)/],["pun",/^-/],["kwd",/^[\w-]+:[ \r\n]/],["pln",
+/^\w+/]]),["yaml","yml"]);
diff --git a/src/main/jbake/assets/docs/google-code-prettify/prettify.css b/src/main/jbake/assets/docs/google-code-prettify/prettify.css
new file mode 100644
index 0000000..d44b3a2
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/prettify.css
@@ -0,0 +1 @@
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
\ No newline at end of file
diff --git a/src/main/jbake/assets/docs/google-code-prettify/prettify.js b/src/main/jbake/assets/docs/google-code-prettify/prettify.js
new file mode 100644
index 0000000..3b74b5b
--- /dev/null
+++ b/src/main/jbake/assets/docs/google-code-prettify/prettify.js
@@ -0,0 +1,46 @@
+!function(){/*
+
+ Copyright (C) 2006 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+window.PR_SHOULD_USE_CONTINUATION=!0;
+(function(){function T(a){function d(e){var b=e.charCodeAt(0);if(92!==b)return b;var a=e.charAt(1);return(b=w[a])?b:"0"<=a&&"7">=a?parseInt(e.substring(1),8):"u"===a||"x"===a?parseInt(e.substring(2),16):e.charCodeAt(1)}function f(e){if(32>e)return(16>e?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return"\\"===e||"-"===e||"]"===e||"^"===e?"\\"+e:e}function b(e){var b=e.substring(1,e.length-1).match(/\\u[0-9A-Fa-f]{4}|\\x[0-9A-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\s\S]|-|[^-\\]/g);e=
+[];var a="^"===b[0],c=["["];a&&c.push("^");for(var a=a?1:0,g=b.length;ak||122k||90k||122h[0]&&(h[1]+1>h[0]&&c.push("-"),c.push(f(h[1])));c.push("]");return c.join("")}function v(e){for(var a=e.source.match(/(?:\[(?:[^\x5C\x5D]|\\[\s\S])*\]|\\u[A-Fa-f0-9]{4}|\\x[A-Fa-f0-9]{2}|\\[0-9]+|\\[^ux0-9]|\(\?[:!=]|[\(\)\^]|[^\x5B\x5C\(\)\^]+)/g),c=a.length,d=[],g=0,h=0;g/,null])):d.push(["com",/^#[^\r\n]*/,null,"#"]));a.cStyleComments&&(f.push(["com",/^\/\/[^\r\n]*/,null]),f.push(["com",/^\/\*[\s\S]*?(?:\*\/|$)/,null]));if(b=a.regexLiterals){var v=(b=1|\\/=?|::?|<=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*("+
+("/(?=[^/*"+b+"])(?:[^/\\x5B\\x5C"+b+"]|\\x5C"+v+"|\\x5B(?:[^\\x5C\\x5D"+b+"]|\\x5C"+v+")*(?:\\x5D|$))+/")+")")])}(b=a.types)&&f.push(["typ",b]);b=(""+a.keywords).replace(/^ | $/g,"");b.length&&f.push(["kwd",new RegExp("^(?:"+b.replace(/[\s,]+/g,"|")+")\\b"),null]);d.push(["pln",/^\s+/,null," \r\n\t\u00a0"]);b="^.[^\\s\\w.$@'\"`/\\\\]*";a.regexLiterals&&(b+="(?!s*/)");f.push(["lit",/^@[a-z_$][a-z_$@0-9]*/i,null],["typ",/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],["pln",/^[a-z_$][a-z_$@0-9]*/i,
+null],["lit",/^(?:0x[a-f0-9]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+\-]?\d+)?)[a-z]*/i,null,"0123456789"],["pln",/^\\[\s\S]?/,null],["pun",new RegExp(b),null]);return G(d,f)}function L(a,d,f){function b(a){var c=a.nodeType;if(1==c&&!A.test(a.className))if("br"===a.nodeName)v(a),a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)b(a);else if((3==c||4==c)&&f){var d=a.nodeValue,q=d.match(n);q&&(c=d.substring(0,q.index),a.nodeValue=c,(d=d.substring(q.index+q[0].length))&&
+a.parentNode.insertBefore(l.createTextNode(d),a.nextSibling),v(a),c||a.parentNode.removeChild(a))}}function v(a){function b(a,c){var d=c?a.cloneNode(!1):a,k=a.parentNode;if(k){var k=b(k,1),e=a.nextSibling;k.appendChild(d);for(var f=e;f;f=e)e=f.nextSibling,k.appendChild(f)}return d}for(;!a.nextSibling;)if(a=a.parentNode,!a)return;a=b(a.nextSibling,0);for(var d;(d=a.parentNode)&&1===d.nodeType;)a=d;c.push(a)}for(var A=/(?:^|\s)nocode(?:\s|$)/,n=/\r\n?|\n/,l=a.ownerDocument,m=l.createElement("li");a.firstChild;)m.appendChild(a.firstChild);
+for(var c=[m],p=0;p=+v[1],d=/\n/g,A=a.a,n=A.length,f=0,l=a.c,m=l.length,b=0,c=a.g,p=c.length,w=0;c[p]=n;var r,e;for(e=r=0;e=h&&(b+=2);f>=k&&(w+=2)}}finally{g&&(g.style.display=a)}}catch(x){E.console&&console.log(x&&x.stack||x)}}var E=window,C=["break,continue,do,else,for,if,return,while"],
+F=[[C,"auto,case,char,const,default,double,enum,extern,float,goto,inline,int,long,register,restrict,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],H=[F,"alignas,alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,delegate,dynamic_cast,explicit,export,friend,generic,late_check,mutable,namespace,noexcept,noreturn,nullptr,property,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],
+O=[F,"abstract,assert,boolean,byte,extends,finally,final,implements,import,instanceof,interface,null,native,package,strictfp,super,synchronized,throws,transient"],P=[F,"abstract,add,alias,as,ascending,async,await,base,bool,by,byte,checked,decimal,delegate,descending,dynamic,event,finally,fixed,foreach,from,get,global,group,implicit,in,interface,internal,into,is,join,let,lock,null,object,out,override,orderby,params,partial,readonly,ref,remove,sbyte,sealed,select,set,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,value,var,virtual,where,yield"],
+F=[F,"abstract,async,await,constructor,debugger,enum,eval,export,function,get,implements,instanceof,interface,let,null,set,undefined,var,with,yield,Infinity,NaN"],Q=[C,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],R=[C,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],C=[C,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],
+S=/^(DIR|FILE|array|vector|(de|priority_)?queue|(forward_)?list|stack|(const_)?(reverse_)?iterator|(unordered_)?(multi)?(set|map)|bitset|u?(int|float)\d*)\b/,W=/\S/,X=y({keywords:[H,P,O,F,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",Q,R,C],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),I={};t(X,["default-code"]);t(G([],[["pln",/^[^]+/],["dec",
+/^]*(?:>|$)/],["com",/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^
]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^
+
+
+
+
+
+
+ lchannels is a Scala library for
+ type-safe session programming . It
+ provides linear I/O channels , which allow to transfer
+ data locally, or over a network. Its API allows
+ to (1) define
+ protocols , represented as a set
+ of continuation-passing style classes ,
+ and (2) easily write programs that interact according
+ to such protocols. The Scala type checker can examine the
+ resulting code and spot many protocol violations at
+ compile time .
+
+
+ An example
+
+
+ Consider a simple ATM, which is supposed to implement the
+ following session protocol:
+
+
+
+
+ the user provides his/her card number and PIN;
+
+
+ if the card number or pin are wrong, the ATM notifies the
+ failure and quits the session;
+
+
+ otherwise, the user can choose between:
+
+
+ checking the account balance. In this case, the
+ ATM answers accordingly, and quits;
+
+
+ quitting the session.
+
+
+
+
+
+
+
+
+ Such a protocol can be represented with the
+ following continuation-passing style classes
+ (where Out[A] is an output channel
+ endpoint carrying an A-typed message, and
+ the cont fields describe how the session
+ continues):
+
+
+ case class Authenticate(card: String, pin: String)(val cont: Out[Response])
+
+sealed abstract class Response // Authentication response from the ATM
+case class Failure() extends Response
+case class Success()(val cont: Out[Menu]) extends Response
+
+sealed abstract class Menu // Choices available to authenticated user
+case class CheckBalance()(val cont: Out[Balance]) extends Menu
+case class Quit() extends Menu
+
+case class Balance(amount: Int) // User account balance
+
+
+
+ Then, the ATM can be implemented with the following Scala code
+ (where the method In[A].? allows to receive
+ an A-typed message,
+ while Out[A].! and Out[A].!!
+ allow to send an A-typed message):
+
+
+ def atm(c: In[Authenticate]) = {
+ c ? { auth =>
+ if (!authenticated(auth.card, auth.pin)) {
+ auth.cont ! Failure()
+ } else {
+ ( auth.cont !! Success()_ ) ? {
+ case m @ CheckBalance() => m.cont ! Balance(42)
+ case Quit() => ()
+} } } }
+
+
+
+ The Scala compiler will ensure that the ATM program does not
+ send the wrong type of message
+ (e.g., trying to send Balance
+ when Success/Failure are expected),
+ and that it does not forget to handle some valid user requests
+ (e.g., it will warn if the Quit case is missing).
+ This way, many protocol violations are detected at compile
+ time ; other protocol safety errors (e.g., duplicate
+ outputs) are detected
+ at runtime --- and in this case, lchannels
+ raises errors and prevents out-of-protocol messages to be sent.
+
+
+
+ Notably, In[A] and Out[A]
+ abstract the underlying message transport :
+ the atm() method above can interact with an user
+ process e.g. via local data queues, or through a TCP/IP
+ socket. As a result, the protocol logic and its
+ low-level details are decoupled.
+
+
+ Documentation and publications
+
+
+ The theory and practice of session programming
+ with lchannels is presented in the following
+ papers: they explain how protocols
+ (formalised as session types ) can be
+ represented in Scala using lchannels with
+ continuation-passing style classes , and discusses
+ the main features of the library.
+
+
+
+
+
+
+ NOTE: newer versions
+ of lchannels available on GitHub
+ have some differences and
+ improvements .
+
+
+ Downloading, compiling and testing
+
+
+ You can download lchannels
+ from its
+ GitHub repository .
+
+
+
+ To compile an test the code, you can
+ follow these instructions .
+
+
+
+
+
+
diff --git a/src/main/jbake/assets/docs/lchannels-web/instructions.html b/src/main/jbake/assets/docs/lchannels-web/instructions.html
new file mode 100644
index 0000000..a482800
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels-web/instructions.html
@@ -0,0 +1,619 @@
+
+
+
+
+ lchannels - Session programming in Scala
+
+
+
+
+
+
+
+
+
+
+
+
+ NOTE: unless otherwise noted,
+ the references below point to
+ specific sections and figures in the on-line
+ technical report available at:
+ http://mrg.doc.ic.ac.uk/publications/lightweight-session-programming-in-scala/paper.pdf .
+
+
+ Requirements
+
+
+ These instructions assume an Unix-like operating
+ system with Java 8 as default
+ JRE/JDK .
+ They have been tested on Ubuntu (14.04 and 15.10),
+ Debian GNU/Linux (Sid, April 2016),
+ and Mac OS X (10.10 and 10.11).
+ They should also work on Windows with minor adaptations.
+
+
+
+ For some examples, and to generate the benchmark plots,
+ you will also need
+ Python 2.7.x
+ and
+ matplotlib .
+
+
+ Initial setup on Ubuntu (14.04 and later)
+
+
+ If you use Ubuntu (or recent versions of Debian GNU/Linux),
+ this section provides detailed
+ instructions that should be followed before
+ compiling the sources .
+
+
+
+
+ If you are using Ubuntu 14.04 ,
+ you might first need to
+ enable the software repositories for Java 8. Open a terminal
+ and execute the following commands:
+
+ sudo add-apt-repository ppa:openjdk-r/ppa # Not needed on Ubuntu ≥ 14.10
+sudo apt-get update # Not needed on Ubuntu ≥ 14.10
+
+
+
+ On all Ubuntu/Debian versions ,
+ you need to ensure that Java 8 is
+ installed and selected as the default JRE/JDK. Thus, you should
+ execute the following commands:
+
+ sudo apt-get install openjdk-8-jdk # Alternatively: oracle-java8-jdk
+
+sudo update-alternatives --config java # Ensure that Java 8 is selected
+sudo update-alternatives --config javac # Ensure that Java 8 is selected
+
+
+
+ Moreover, in order to generate the benchmark plots, you might also
+ need to execute:
+
+ sudo apt-get install python-matplotlib
+
+
+
+
+ Compiling the sources
+
+
+
+ You will need
+ sbt
+ 0.13.x :
+ the installation instructions for various OSs are available
+ here .
+
+
+
+
+ Open a terminal, and cd inside the
+ lchannels/
+ directory.
+ NOTE : from now on, all commands must be launched from this position.
+
+
+ Execute the following command:
+ sbt compile
+
+ This will cause sbt to automatically
+ download all missing dependencies
+ (including Scala 2.11.8 )
+ and compile lchannels.
+
+
+
+ Examples
+
+
+ To launch the examples, execute:
+
+
+ sbt "project examples" run
+
+
+
+ You will get a prompt similar to the following:
+
+
+ Multiple main classes detected, select one to run:
+
+ [1] lchannels.examples.calc.Local
+ [2] lchannels.examples.calc.Queue
+ [3] lchannels.examples.chat.demo.Local
+ [4] lchannels.examples.game.a.Actor
+ [5] lchannels.examples.game.b.Actor
+ [6] lchannels.examples.game.c.Actor
+ [7] lchannels.examples.game.demo.Local
+ [8] lchannels.examples.game.demo.Queue
+ [9] lchannels.examples.game.server.Actor
+ [10] lchannels.examples.greeting.ActorClient
+ [11] lchannels.examples.greeting.ActorServer
+ [12] lchannels.examples.greeting.Local
+ [13] lchannels.examples.greeting.Queue
+ [14] lchannels.examples.greeting.SocketClient
+ [15] lchannels.examples.greeting.StreamClient
+ [16] lchannels.examples.http.server.Server
+ [17] lchannels.examples.scribblegreeting.ActorClient
+ [18] lchannels.examples.scribblegreeting.ActorServer
+ [19] lchannels.examples.scribblegreeting.Local
+ [20] lchannels.examples.scribblegreeting.SocketClient
+ [21] lchannels.examples.sleepingbarber.demo.Local
+ [22] lchannels.examples.threebuyer.alice.Actor
+ [23] lchannels.examples.threebuyer.bob.Actor
+ [24] lchannels.examples.threebuyer.carol.Actor
+ [25] lchannels.examples.threebuyer.demo.Local
+ [26] lchannels.examples.threebuyer.seller.Actor
+
+Enter number:
+
+
+
+ The prompt allows to choose among:
+
+
+
+
+ lchannels.examples.calc : a client and server that
+ perform simple mathematical operations.
+ (Source)
+
+
+ lchannels.examples.chat :
+ the "chat server with frontend" case study, described in §1,
+ §6.1 and §B.1 of the technical report.
+ (Source)
+
+
+ lchannels.examples.greeting :
+ client and server implementing the "greeting protocol" of §2.1
+ and Example 4.2.
+ (Source)
+
+
+ lchannels.examples.scribblegreeting :
+ similar to the above, but based on session classes that have
+ been automatically generated from
+ this
+ protocol specification , using
+ an extended version of the Scribble tool .
+ (Source)
+
+
+ lchannels.examples.sleepingbarber :
+ the "sleeping barber" of Example 4.3.
+ (Source)
+
+
+ lchannels.examples.http : HTTP
+ server supporting the GET method. The implementation is
+ based on session classes that have been automatically
+ generated from
+ this
+ protocol specification , using
+ an extended version of the Scribble tool .
+ (Source)
+
+
+ lchannels.examples.sleepingbarber :
+ the "sleeping barber" of Example 4.3.
+ (Source)
+
+
+
+ lchannels.examples.game :
+ three players connect to a game server, that creates a multiparty
+ session allowing the players to interact directly.
+ (Source)
+ This example shows how type-safe multiparty sessions,
+ and distributed multiparty delegation, can be implemented
+ on top of the binary channel endpoints provided by
+ lchannels. This is based on the theoretical
+ results presented in
+ this
+ technical report . Note that the protocol classes
+ used in this example
+ (collected
+ in these files )
+ are automatically generated from
+ this
+ protocol specification , using
+ an extended version of the Scribble tool .
+
+
+ lchannels.examples.threebuyer :
+ two buyers connect to a seller to purchase a book, involving
+ a third buyer if the price is too high.
+ (Source)
+ This example is also based on the
+ theoretical results presented in
+ this
+ technical report . The protocol classes
+ are automatically generated from
+ this
+ protocol specification , using
+ an extended version of the Scribble tool .
+
+
+
+ Running the examples
+
+
+
+ Browsing the source code
+
+
+ The source code for all examples can be found under
+ examples/src/... .
+
+
+
+ The main differences wrt. the code snippets in the technical report
+ are the addition of log messages,
+ and in some cases the presence of parameters that were omitted
+ for simplicity of exposition.
+ For instance:
+
+
+
+
+ some methods for creating instances
+ of In/Out
+ channels take
+ implicit parameters ,
+ such as the ExecutionContext for
+ running Scala's Future.onComplete()
+ callbacks. As customary, such implicit parameters have been
+ mostly omitted in the technical report (but of course, they are
+ reported in the API documentation );
+
+
+ the chat server code shown in §6.1.2 invokes
+ LocalChannel.factory() to create
+ new pairs of channels; the corresponding code in
+ ChatServer.scala
+ (line 200), instead, uses sfactory,
+ which is a ChatServer parameter
+ that can be instantiated e.g. with
+ LocalChannel.factory,
+ ActorChannel.factory, etc. :
+ it allows to decouple the chat server implementation from the
+ channel transport.
+
+
+
+ Benchmarks
+
+
+ The benchmarks (§6.2 of the technical report) can be run by executing:
+
+
+ ./scripts/benchmark.sh
+
+
+
+ The command above launches a reduced version of the
+ benchmarks, that should terminate pretty quickly.
+
+
+
+ If you want to replicate the measurements in §6.2 of the
+ technical report (which take much longer and can be stressful
+ for the Java GC) you can execute instead:
+
+
+ ./scripts/benchmark.sh 10 2000000 # Meaning: 10 JVM invocations, ~2000000 message exchanges
+
+
+
+ In both cases above, you will obtain:
+
+
+
+ a bench/ directory, with several
+ sub-directories: each one contains the results of the
+ benchmarks for a distinct JVM invocation;
+
+
+ the cumulative benchmark results, created by aggregating the
+ measurements under bench/*/:
+ pingpong.csv,
+ ring.csv,
+ ring-stream.csv
+ and chameneos.csv. Such aggregated results
+ have been used to generate Figure 9 in the technical report;
+
+
+ one .pdf file for
+ each .csv file above, containing the
+ plot of the benchmark results.
+
+
+
+
+ The source code for the benchmarks can be found under
+ benchmarks/src/... .
+
+
+ API documentation and source code pointers
+
+ The API documentation for lchannels is
+ automatically generated from source code comments, with:
+
+ sbt "project lchannels" doc
+
+
+
+ After running the command above, the documentation
+ can be browsed by
+ opening lchannels/target/scala-2.11/api/index.html.
+ For reference, a (possibly outdated) copy of the documentation
+ is also
+ available online
+ (last update: May 3, 2016).
+
+
+
+ Here are some selected links:
+
+
+
+
+
+ Some other features and details:
+
+
+
+
+ Miscellaneous remarks
+
+
+
+ You might notice that
+ the directory examples/lib/
+ contains a file
+ called akka-typed-experimental_2.11-2.4.2.jar.
+ It is a slightly modified version of the
+ corresponding
+ (experimental) Akka module , automatically loaded when
+ running the lchannels
+ examples . The modification
+ consists in the following patch to the Akka 2.4.2 source code :
+
+
+--- a/akka-typed/src/main/scala/akka/typed/ActorRef.scala
++++ b/akka-typed/src/main/scala/akka/typed/ActorRef.scala
+@@ -63,7 +63,7 @@ trait ScalaActorRef[-T] { this: ActorRef[T] ⇒
+}
+
+object ActorRef {
+- private class Combined[T](val untypedRef: akka.actor.ActorRef) extends ActorRef[T] with ScalaActorRef[T]
++ private class Combined[T](val untypedRef: akka.actor.ActorRef) extends ActorRef[T] with ScalaActorRef[T] with Serializable
+
+implicit def toScalaActorRef[T](ref: ActorRef[T]): ScalaActorRef[T] = ref.asInstanceOf[ScalaActorRef[T]]
+
+
+
+ We applied such a patch to enable serialization
+ of typed actor references; it will become unnecessary if/when
+ Akka Typed will become stable.
+
+
+
+ Troubleshooting
+
+
+
+
+
+
+
diff --git a/src/main/jbake/assets/docs/lchannels-web/stylesheets/github-light.css b/src/main/jbake/assets/docs/lchannels-web/stylesheets/github-light.css
new file mode 100644
index 0000000..d64ba5d
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels-web/stylesheets/github-light.css
@@ -0,0 +1,124 @@
+/*
+The MIT License (MIT)
+
+Copyright (c) 2015 GitHub, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+*/
+
+.pl-c /* comment */ {
+ color: #969896;
+}
+
+.pl-c1 /* constant, markup.raw, meta.diff.header, meta.module-reference, meta.property-name, support, support.constant, support.variable, variable.other.constant */,
+.pl-s .pl-v /* string variable */ {
+ color: #0086b3;
+}
+
+.pl-e /* entity */,
+.pl-en /* entity.name */ {
+ color: #795da3;
+}
+
+.pl-s .pl-s1 /* string source */,
+.pl-smi /* storage.modifier.import, storage.modifier.package, storage.type.java, variable.other, variable.parameter.function */ {
+ color: #333;
+}
+
+.pl-ent /* entity.name.tag */ {
+ color: #63a35c;
+}
+
+.pl-k /* keyword, storage, storage.type */ {
+ color: #a71d5d;
+}
+
+.pl-pds /* punctuation.definition.string, string.regexp.character-class */,
+.pl-s /* string */,
+.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */,
+.pl-sr /* string.regexp */,
+.pl-sr .pl-cce /* string.regexp constant.character.escape */,
+.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */,
+.pl-sr .pl-sre /* string.regexp source.ruby.embedded */ {
+ color: #183691;
+}
+
+.pl-v /* variable */ {
+ color: #ed6a43;
+}
+
+.pl-id /* invalid.deprecated */ {
+ color: #b52a1d;
+}
+
+.pl-ii /* invalid.illegal */ {
+ background-color: #b52a1d;
+ color: #f8f8f8;
+}
+
+.pl-sr .pl-cce /* string.regexp constant.character.escape */ {
+ color: #63a35c;
+ font-weight: bold;
+}
+
+.pl-ml /* markup.list */ {
+ color: #693a17;
+}
+
+.pl-mh /* markup.heading */,
+.pl-mh .pl-en /* markup.heading entity.name */,
+.pl-ms /* meta.separator */ {
+ color: #1d3e81;
+ font-weight: bold;
+}
+
+.pl-mq /* markup.quote */ {
+ color: #008080;
+}
+
+.pl-mi /* markup.italic */ {
+ color: #333;
+ font-style: italic;
+}
+
+.pl-mb /* markup.bold */ {
+ color: #333;
+ font-weight: bold;
+}
+
+.pl-md /* markup.deleted, meta.diff.header.from-file */ {
+ background-color: #ffecec;
+ color: #bd2c00;
+}
+
+.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ {
+ background-color: #eaffea;
+ color: #55a532;
+}
+
+.pl-mdr /* meta.diff.range */ {
+ color: #795da3;
+ font-weight: bold;
+}
+
+.pl-mo /* meta.output */ {
+ color: #1d3e81;
+}
+
diff --git a/src/main/jbake/assets/docs/lchannels-web/stylesheets/normalize.css b/src/main/jbake/assets/docs/lchannels-web/stylesheets/normalize.css
new file mode 100644
index 0000000..30366a6
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels-web/stylesheets/normalize.css
@@ -0,0 +1,424 @@
+/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
+
+/**
+ * 1. Set default font family to sans-serif.
+ * 2. Prevent iOS text size adjust after orientation change, without disabling
+ * user zoom.
+ */
+
+html {
+ font-family: sans-serif; /* 1 */
+ -ms-text-size-adjust: 100%; /* 2 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/**
+ * Remove default margin.
+ */
+
+body {
+ margin: 0;
+}
+
+/* HTML5 display definitions
+ ========================================================================== */
+
+/**
+ * Correct `block` display not defined for any HTML5 element in IE 8/9.
+ * Correct `block` display not defined for `details` or `summary` in IE 10/11
+ * and Firefox.
+ * Correct `block` display not defined for `main` in IE 11.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+main,
+menu,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/**
+ * 1. Correct `inline-block` display not defined in IE 8/9.
+ * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
+ */
+
+audio,
+canvas,
+progress,
+video {
+ display: inline-block; /* 1 */
+ vertical-align: baseline; /* 2 */
+}
+
+/**
+ * Prevent modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/**
+ * Address `[hidden]` styling not present in IE 8/9/10.
+ * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
+ */
+
+[hidden],
+template {
+ display: none;
+}
+
+/* Links
+ ========================================================================== */
+
+/**
+ * Remove the gray background color from active links in IE 10.
+ */
+
+a {
+ background-color: transparent;
+}
+
+/**
+ * Improve readability when focused and also mouse hovered in all browsers.
+ */
+
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* Text-level semantics
+ ========================================================================== */
+
+/**
+ * Address styling not present in IE 8/9/10/11, Safari, and Chrome.
+ */
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/**
+ * Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/**
+ * Address styling not present in Safari and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/**
+ * Address variable `h1` font-size and margin within `section` and `article`
+ * contexts in Firefox 4+, Safari, and Chrome.
+ */
+
+h1 {
+ font-size: 2em;
+ margin: 0.67em 0;
+}
+
+/**
+ * Address styling not present in IE 8/9.
+ */
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+/**
+ * Address inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* Embedded content
+ ========================================================================== */
+
+/**
+ * Remove border when inside `a` element in IE 8/9/10.
+ */
+
+img {
+ border: 0;
+}
+
+/**
+ * Correct overflow not hidden in IE 9/10/11.
+ */
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* Grouping content
+ ========================================================================== */
+
+/**
+ * Address margin not present in IE 8/9 and Safari.
+ */
+
+figure {
+ margin: 1em 40px;
+}
+
+/**
+ * Address differences between Firefox and other browsers.
+ */
+
+hr {
+ box-sizing: content-box;
+ height: 0;
+}
+
+/**
+ * Contain overflow in all browsers.
+ */
+
+pre {
+ overflow: auto;
+}
+
+/**
+ * Address odd `em`-unit font size rendering in all browsers.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, monospace;
+ font-size: 1em;
+}
+
+/* Forms
+ ========================================================================== */
+
+/**
+ * Known limitation: by default, Chrome and Safari on OS X allow very limited
+ * styling of `select`, unless a `border` property is set.
+ */
+
+/**
+ * 1. Correct color not being inherited.
+ * Known issue: affects color of disabled elements.
+ * 2. Correct font properties not being inherited.
+ * 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
+ */
+
+button,
+input,
+optgroup,
+select,
+textarea {
+ color: inherit; /* 1 */
+ font: inherit; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/**
+ * Address `overflow` set to `hidden` in IE 8/9/10/11.
+ */
+
+button {
+ overflow: visible;
+}
+
+/**
+ * Address inconsistent `text-transform` inheritance for `button` and `select`.
+ * All other form control elements do not inherit `text-transform` values.
+ * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
+ * Correct `select` style inheritance in Firefox.
+ */
+
+button,
+select {
+ text-transform: none;
+}
+
+/**
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Correct inability to style clickable `input` types in iOS.
+ * 3. Improve usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/**
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+html input[disabled] {
+ cursor: default;
+}
+
+/**
+ * Remove inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/**
+ * Address Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+input {
+ line-height: normal;
+}
+
+/**
+ * It's recommended that you don't attempt to style these elements.
+ * Firefox's implementation doesn't respect box-sizing, padding, or width.
+ *
+ * 1. Address box sizing set to `content-box` in IE 8/9/10.
+ * 2. Remove excess padding in IE 8/9/10.
+ */
+
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Fix the cursor style for Chrome's increment/decrement buttons. For certain
+ * `font-size` values of the `input`, it causes the cursor style of the
+ * decrement button to change from `default` to `text`.
+ */
+
+input[type="number"]::-webkit-inner-spin-button,
+input[type="number"]::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+ * 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+ * 2. Address `box-sizing` set to `border-box` in Safari and Chrome
+ * (include `-moz` to future-proof).
+ */
+
+input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */ /* 2 */
+ box-sizing: content-box;
+}
+
+/**
+ * Remove inner padding and search cancel button in Safari and Chrome on OS X.
+ * Safari (but not Chrome) clips the cancel button when the search input has
+ * padding (and `textfield` appearance).
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/**
+ * 1. Correct `color` not being inherited in IE 8/9/10/11.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/**
+ * Remove default vertical scrollbar in IE 8/9/10/11.
+ */
+
+textarea {
+ overflow: auto;
+}
+
+/**
+ * Don't inherit the `font-weight` (applied by a rule above).
+ * NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
+ */
+
+optgroup {
+ font-weight: bold;
+}
+
+/* Tables
+ ========================================================================== */
+
+/**
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+td,
+th {
+ padding: 0;
+}
diff --git a/src/main/jbake/assets/docs/lchannels-web/stylesheets/stylesheet.css b/src/main/jbake/assets/docs/lchannels-web/stylesheets/stylesheet.css
new file mode 100644
index 0000000..019868a
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels-web/stylesheets/stylesheet.css
@@ -0,0 +1,245 @@
+* {
+ box-sizing: border-box; }
+
+body {
+ padding: 0;
+ margin: 0;
+ font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-size: 12px;
+ line-height: 1.3;
+ color: #606c71; }
+
+a {
+ color: #1e6bb8;
+ text-decoration: none; }
+ a:hover {
+ text-decoration: underline; }
+
+.btn {
+ display: inline-block;
+ margin-bottom: 1rem;
+ color: rgba(255, 255, 255, 0.7);
+ background-color: rgba(255, 255, 255, 0.08);
+ border-color: rgba(255, 255, 255, 0.2);
+ border-style: solid;
+ border-width: 1px;
+ border-radius: 0.3rem;
+ transition: color 0.2s, background-color 0.2s, border-color 0.2s; }
+ .btn + .btn {
+ margin-left: 1rem; }
+
+.btn:hover {
+ color: rgba(255, 255, 255, 0.8);
+ text-decoration: none;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: rgba(255, 255, 255, 0.3); }
+
+@media screen and (min-width: 64em) {
+ .btn {
+ padding: 0.75rem 1rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .btn {
+ padding: 0.6rem 0.9rem;
+ font-size: 0.9rem; } }
+
+@media screen and (max-width: 42em) {
+ .btn {
+ display: block;
+ width: 100%;
+ padding: 0.75rem;
+ font-size: 0.9rem; }
+ .btn + .btn {
+ margin-top: 1rem;
+ margin-left: 0; } }
+
+.page-header {
+ color: #fff;
+ text-align: center;
+ background-color: #159957;
+ background-image: linear-gradient(120deg, #155799, #159957); }
+
+@media screen and (min-width: 64em) {
+ .page-header {
+ padding: 5rem 6rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .page-header {
+ padding: 3rem 4rem; } }
+
+@media screen and (max-width: 42em) {
+ .page-header {
+ padding: 2rem 1rem; } }
+
+.project-name {
+ margin-top: 0;
+ margin-bottom: 0.1rem; }
+
+@media screen and (min-width: 64em) {
+ .project-name {
+ font-size: 3.25rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .project-name {
+ font-size: 2.25rem; } }
+
+@media screen and (max-width: 42em) {
+ .project-name {
+ font-size: 1.75rem; } }
+
+.project-tagline {
+ margin-bottom: 2rem;
+ font-weight: normal;
+ opacity: 0.7; }
+
+@media screen and (min-width: 64em) {
+ .project-tagline {
+ font-size: 1.25rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .project-tagline {
+ font-size: 1.15rem; } }
+
+@media screen and (max-width: 42em) {
+ .project-tagline {
+ font-size: 1rem; } }
+
+.main-content :first-child {
+ margin-top: 0; }
+.main-content img {
+ max-width: 100%; }
+.main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 {
+ margin-top: 2rem;
+ margin-bottom: 1rem;
+ font-weight: normal;
+ color: #159957; }
+.main-content p {
+ margin-bottom: 1em; }
+.main-content code {
+ padding: 2px 4px;
+ font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+ font-size: 0.9rem;
+ color: #383e41;
+ background-color: #f3f6fa;
+ border-radius: 0.3rem; }
+.main-content pre {
+ padding: 0.8rem;
+ margin-top: 0;
+ margin-bottom: 1rem;
+ font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace;
+ color: #567482;
+ word-wrap: normal;
+ background-color: #f3f6fa;
+ border: solid 1px #dce6f0;
+ border-radius: 0.3rem; }
+ .main-content pre > code {
+ padding: 0;
+ margin: 0;
+ font-size: 0.9rem;
+ color: #567482;
+ word-break: normal;
+ white-space: pre;
+ background: transparent;
+ border: 0; }
+.main-content .highlight {
+ margin-bottom: 1rem; }
+ .main-content .highlight pre {
+ margin-bottom: 0;
+ word-break: normal; }
+.main-content .highlight pre, .main-content pre {
+ padding: 0.8rem;
+ overflow: auto;
+ font-size: 0.9rem;
+ line-height: 1.45;
+ border-radius: 0.3rem; }
+.main-content pre code, .main-content pre tt {
+ display: inline;
+ max-width: initial;
+ padding: 0;
+ margin: 0;
+ overflow: initial;
+ line-height: inherit;
+ word-wrap: normal;
+ background-color: transparent;
+ border: 0; }
+ .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after {
+ content: normal; }
+.main-content ul, .main-content ol {
+ margin-top: 0; }
+.main-content blockquote {
+ padding: 0 1rem;
+ margin-left: 0;
+ color: #819198;
+ border-left: 0.3rem solid #dce6f0; }
+ .main-content blockquote > :first-child {
+ margin-top: 0; }
+ .main-content blockquote > :last-child {
+ margin-bottom: 0; }
+.main-content table {
+ display: block;
+ width: 100%;
+ overflow: auto;
+ word-break: normal;
+ word-break: keep-all; }
+ .main-content table th {
+ font-weight: bold; }
+ .main-content table th, .main-content table td {
+ padding: 0.5rem 1rem;
+ border: 1px solid #e9ebec; }
+.main-content dl {
+ padding: 0; }
+ .main-content dl dt {
+ padding: 0;
+ margin-top: 1rem;
+ font-size: 1rem;
+ font-weight: bold; }
+ .main-content dl dd {
+ padding: 0;
+ margin-bottom: 1rem; }
+.main-content hr {
+ height: 2px;
+ padding: 0;
+ margin: 1rem 0;
+ background-color: #eff0f1;
+ border: 0; }
+
+@media screen and (min-width: 64em) {
+ .main-content {
+ max-width: 64rem;
+ padding: 2rem 6rem;
+ margin: 0 auto;
+ font-size: 1rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .main-content {
+ padding: 2rem 4rem;
+ font-size: 1rem; } }
+
+@media screen and (max-width: 42em) {
+ .main-content {
+ padding: 2rem 1rem;
+ font-size: 1rem; } }
+
+.site-footer {
+ padding-top: 2rem;
+ margin-top: 2rem;
+ border-top: solid 1px #eff0f1; }
+
+.site-footer-owner {
+ display: block;
+ font-weight: bold; }
+
+.site-footer-credits {
+ color: #819198; }
+
+@media screen and (min-width: 64em) {
+ .site-footer {
+ font-size: 1rem; } }
+
+@media screen and (min-width: 42em) and (max-width: 64em) {
+ .site-footer {
+ font-size: 1rem; } }
+
+@media screen and (max-width: 42em) {
+ .site-footer {
+ font-size: 0.9rem; } }
diff --git a/src/main/jbake/assets/docs/lchannels/.gitignore b/src/main/jbake/assets/docs/lchannels/.gitignore
new file mode 100644
index 0000000..c58d83b
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/.gitignore
@@ -0,0 +1,17 @@
+*.class
+*.log
+
+# sbt specific
+.cache
+.history
+.lib/
+dist/*
+target/
+lib_managed/
+src_managed/
+project/boot/
+project/plugins/project/
+
+# Scala-IDE specific
+.scala_dependencies
+.worksheet
diff --git a/src/main/jbake/assets/docs/lchannels/LICENSE b/src/main/jbake/assets/docs/lchannels/LICENSE
new file mode 100644
index 0000000..9f05217
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/LICENSE
@@ -0,0 +1,23 @@
+Copyright (c) 2016, Alceste Scalas and Imperial College London
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/jbake/assets/docs/lchannels/README.md b/src/main/jbake/assets/docs/lchannels/README.md
new file mode 100644
index 0000000..1910370
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/README.md
@@ -0,0 +1,5 @@
+# lchannels
+Session programming in Scala
+
+For more information and documentation, please refer to:
+http://alcestes.github.io/lchannels/
diff --git a/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Chameneos.scala b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Chameneos.scala
new file mode 100644
index 0000000..a650fcd
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Chameneos.scala
@@ -0,0 +1,603 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.benchmarks.chameneos
+
+import lchannels._
+
+import scala.concurrent.ExecutionContext
+import scala.concurrent.duration.Duration
+
+import java.util.concurrent.{LinkedBlockingQueue => Fifo}
+
+/** The color of a chameneos */
+sealed abstract class Color { def next: Color }
+case class Red() extends Color { override def next = Green() }
+case class Green() extends Color { override def next = Blue() }
+case class Blue() extends Color { override def next = Red() }
+
+/** lchannels-based implementation (medium-independent). */
+object LChannelsImpl {
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos-broker interaction (S_cham is defined below):
+ // S = ?Start(S_cham).end & ?Wait(dual(S_Cham)).end & Closed()
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Response
+ case class Start(c: Out[Greeting]) extends Response
+ case class Wait(c: In[Greeting]) extends Response
+ case class Closed() extends Response
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos interaction:
+ // S_cham = !Greet((String, Color)) . ?Answer((String, Color)) . end
+ // dual(S_cham) = ?Greet((String, Color)) . !Answer((String, Color)) . end
+ ////////////////////////////////////////////////////////////////////////////
+ case class Greeting(name: String, color: Color)(val cont: Out[Answer])
+ case class Answer(name: String, color: Color)
+ ////////////////////////////////////////////////////////////////////////////
+
+ class Broker(meetings: Int,
+ rfactory: () => (In[Response], Out[Response]),
+ cfactory: () => (In[Greeting], Out[Greeting]))
+ (implicit ec: ExecutionContext) extends Runnable {
+ // Queue of requests from chameneos
+ private val requests = new Fifo[Out[Response]]()
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ private var active = false
+ /** Allow the broker to provide connections */
+ def activate() = synchronized {
+ active = true
+ notifyAll()
+ }
+
+ /** If [[activate]] was called, return a connection to the broker. */
+ def connect(): In[Response] = synchronized {
+ while (!active) wait() // Wait for activate()
+ val (in, out) = rfactory()
+ requests.put(out)
+ in
+ }
+
+ override def run() = loop(meetings)
+
+ @scala.annotation.tailrec
+ private def loop(meetings: Int): Unit = {
+ try {
+ val r1 = requests.take()
+ val r2 = requests.take()
+
+ if (meetings == 0) {
+ r1 ! Closed(); r2 ! Closed()
+ } else {
+ val (cin, cout) = cfactory() // Used for interaction btwn chameneos
+ r1 ! Start(cout)
+ r2 ! Wait(cin)
+ }
+ } catch {
+ case _: InterruptedException => return // quit() was invoked
+ }
+ loop(if (meetings == 0) 0 else meetings-1)
+ }
+ }
+
+ class Chameneos(name: String, var color: Color, broker: Broker)
+ (implicit ec: ExecutionContext) extends Runnable {
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ @scala.annotation.tailrec
+ override final def run() = {
+ broker.connect().receive() match {
+ case Start(c) => {
+ (c !! Greeting(name, color)_).receive()
+ color = color.next
+ run()
+ }
+ case Wait(c) => {
+ val m = c.receive(); m.cont ! Answer(name, color)
+ color = color.next
+ run()
+ }
+ case Closed() => ()
+ }
+ }
+ }
+}
+
+/** Promise/Future-based implementation */
+object PromiseFutureImpl {
+ import scala.concurrent.{Await, Future, Promise}
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos-broker interaction (T_cham is defined below):
+ // T = ?Start(T_cham).end & ?Wait(dual(T_Cham)).end & Closed()
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Response
+ case class Start(c: Promise[Greeting]) extends Response
+ case class Wait(c: Future[Greeting]) extends Response
+ case class Closed() extends Response
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos interaction:
+ // T_cham = !Greet((String, Color)) . ?Answer((String, Color)) . end
+ // dual(T_cham) = ?Greet((String, Color)) . !Answer((String, Color)) . end
+ ////////////////////////////////////////////////////////////////////////////
+ case class Greeting(name: String, color: Color, cont: Promise[Answer])
+ case class Answer(name: String, color: Color)
+ ////////////////////////////////////////////////////////////////////////////
+
+ class Broker(meetings: Int)
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Queue of requests from chameneos
+ private val requests = new Fifo[Promise[Response]]()
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ private var active = false
+ /** Allow the broker to provide connections */
+ def activate() = synchronized {
+ active = true
+ notifyAll()
+ }
+
+ /** If [[activate]] was called, return a connection to the broker. */
+ def connect(): Future[Response] = synchronized {
+ while (!active) wait() // Wait for activate()
+ val out = Promise[Response]; val in = out.future
+ requests.put(out)
+ in
+ }
+
+ override def run() = loop(meetings)
+
+ @scala.annotation.tailrec
+ private def loop(meetings: Int): Unit = {
+ try {
+ val r1 = requests.take()
+ val r2 = requests.take()
+
+ if (meetings == 0) {
+ r1.success(Closed()); r2.success(Closed())
+ } else {
+ // Used for interaction btwn chameneos
+ val cout = Promise[Greeting]; val cin = cout.future
+ r1.success(Start(cout))
+ r2.success(Wait(cin))
+ }
+ } catch {
+ case _: InterruptedException => return // quit() was invoked
+ }
+ loop(if (meetings == 0) 0 else meetings-1)
+ }
+ }
+
+ class Chameneos(name: String, var color: Color, broker: Broker)
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ @scala.annotation.tailrec
+ override final def run() = {
+ Await.result(broker.connect(), timeout) match {
+ case Start(c) => {
+ val aout = Promise[Answer]; val ain = aout.future
+ c.success(Greeting(name, color, aout))
+ Await.result(ain, timeout)
+ color = color.next
+ run()
+ }
+ case Wait(c) => {
+ val m = Await.result(c, timeout)
+ m.cont.success(Answer(name, color))
+ color = color.next
+ run()
+ }
+ case Closed() => ()
+ }
+ }
+ }
+}
+
+/** Scala Channel-based implementation */
+object ScalaChannelsImpl {
+ import scala.concurrent.Channel
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos-broker interaction (T_cham is defined below):
+ // T = ?Start(T_cham).end & ?Wait(dual(T_Cham)).end & Closed()
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Response
+ case class Start(cw: Channel[Greeting], cr: Channel[Answer]) extends Response
+ case class Wait(cr: Channel[Greeting], cw: Channel[Answer]) extends Response
+ case class Closed() extends Response
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos interaction:
+ // T_cham = !Greet((String, Color)) . ?Answer((String, Color)) . end
+ // dual(T_cham) = ?Greet((String, Color)) . !Answer((String, Color)) . end
+ ////////////////////////////////////////////////////////////////////////////
+ case class Greeting(name: String, color: Color)
+ case class Answer(name: String, color: Color)
+ ////////////////////////////////////////////////////////////////////////////
+
+ class Broker(meetings: Int)
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Queue of requests from chameneos
+ private val requests = new Fifo[Channel[Response]]()
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ private var active = false
+ /** Allow the broker to provide connections */
+ def activate() = synchronized {
+ active = true
+ notifyAll()
+ }
+
+ /** If [[activate]] was called, return a connection to the broker. */
+ def connect(): Channel[Response] = synchronized {
+ while (!active) wait() // Wait for activate()
+ val c = new Channel[Response]()
+ requests.put(c)
+ c
+ }
+
+ override def run() = loop(meetings)
+
+ @scala.annotation.tailrec
+ private def loop(meetings: Int): Unit = {
+ try {
+ val r1 = requests.take()
+ val r2 = requests.take()
+
+ if (meetings == 0) {
+ r1.write(Closed()); r2.write(Closed())
+ } else {
+ // Used for interaction btwn chameneos
+ val cg = new Channel[Greeting]
+ val ca = new Channel[Answer]
+ r1.write(Start(cg, ca))
+ r2.write(Wait(cg, ca))
+ }
+ } catch {
+ case _: InterruptedException => return // quit() was invoked
+ }
+ loop(if (meetings == 0) 0 else meetings-1)
+ }
+ }
+
+ class Chameneos(name: String, var color: Color, broker: Broker)
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ @scala.annotation.tailrec
+ override final def run() = {
+ broker.connect().read match {
+ case Start(cg, ca) => {
+ cg.write(Greeting(name, color))
+ ca.read
+ color = color.next
+ run()
+ }
+ case Wait(cg, ca) => {
+ cg.read
+ ca.write(Answer(name, color))
+ color = color.next
+ run()
+ }
+ case Closed() => ()
+ }
+ }
+ }
+}
+
+object JavaBlockingQueuesImpl {
+ import java.util.concurrent.{BlockingQueue => BQueue}
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos-broker interaction (T_cham is defined below):
+ // T = ?Start(T_cham).end & ?Wait(dual(T_Cham)).end & Closed()
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Response
+ case class Start(cw: BQueue[Greeting], cr: BQueue[Answer]) extends Response
+ case class Wait(cr: BQueue[Greeting], cw: BQueue[Answer]) extends Response
+ case class Closed() extends Response
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type for chameneos interaction:
+ // T_cham = !Greet((String, Color)) . ?Answer((String, Color)) . end
+ // dual(T_cham) = ?Greet((String, Color)) . !Answer((String, Color)) . end
+ ////////////////////////////////////////////////////////////////////////////
+ case class Greeting(name: String, color: Color)
+ case class Answer(name: String, color: Color)
+ ////////////////////////////////////////////////////////////////////////////
+
+ class Broker(meetings: Int,
+ rfactory: () => BQueue[Response],
+ gfactory: () => BQueue[Greeting],
+ afactory: () => BQueue[Answer])
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Queue of requests from chameneos
+ private val requests = new Fifo[BQueue[Response]]()
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ private var active = false
+ /** Allow the broker to provide connections */
+ def activate() = synchronized {
+ active = true
+ notifyAll()
+ }
+
+ /** If [[activate]] was called, return a connection to the broker. */
+ def connect(): BQueue[Response] = synchronized {
+ while (!active) wait() // Wait for activate()
+ val c = rfactory()
+ requests.put(c)
+ c
+ }
+
+ override def run() = loop(meetings)
+
+ @scala.annotation.tailrec
+ private def loop(meetings: Int): Unit = {
+ try {
+ val r1 = requests.take()
+ val r2 = requests.take()
+
+ if (meetings == 0) {
+ r1.put(Closed()); r2.put(Closed())
+ } else {
+ // Used for interaction btwn chameneos
+ val cg = gfactory()
+ val ca = afactory()
+ r1.put(Start(cg, ca))
+ r2.put(Wait(cg, ca))
+ }
+ } catch {
+ case _: InterruptedException => return // quit() was invoked
+ }
+ loop(if (meetings == 0) 0 else meetings-1)
+ }
+ }
+
+ class Chameneos(name: String, var color: Color, broker: Broker)
+ (implicit ec: ExecutionContext,
+ timeout: Duration) extends Runnable {
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ @scala.annotation.tailrec
+ override final def run() = {
+ broker.connect().take() match {
+ case Start(cg, ca) => {
+ cg.put(Greeting(name, color))
+ ca.take()
+ color = color.next
+ run()
+ }
+ case Wait(cg, ca) => {
+ cg.take()
+ ca.put(Answer(name, color))
+ color = color.next
+ run()
+ }
+ case Closed() => ()
+ }
+ }
+ }
+}
+
+object Benchmark {
+ import benchmarks.{BenchmarkResult, BenchmarkResults}
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ import java.util.concurrent.BlockingQueue
+
+ import LChannelsImpl.{Response => LResponse, Greeting => LGreeting}
+ import JavaBlockingQueuesImpl.{
+ Response => JQResponse, Greeting => JQGreeting, Answer => JQAnswer
+ }
+
+ /** Perform benchmarks. */
+ def apply(msgCount: Int, nChameneos: Int, reps: Int): BenchmarkResults = {
+ import scala.collection.mutable.{Buffer => MBuffer}
+ case class Bench(title: String, f: () => Long, results: MBuffer[Long])
+
+ implicit val as = akka.actor.ActorSystem("RingBenchmark",
+ defaultExecutionContext = Some(global))
+
+ implicit val timeout = 3600.seconds
+
+ // Each chameneos involved in a meeting sends/receives 3 messages:
+ // 1. Response from broker, with channel towards another chameneos
+ // 2. Greeting to/from other chameneos
+ // 3. Answer from/to other chameneos
+ // NOTE: here we are not counting the final Closed() messages
+ val meetings = msgCount / (3 * 2)
+
+ println(f"*** Chameneos benchmark (${nChameneos} chameneos, ${meetings} meeting(s))")
+
+ val benchmarks = List(
+ Bench("lchannels (Promise/Future)",
+ () => lBenchmark(LocalChannel.factory, LocalChannel.factory,
+ nChameneos, meetings),
+ MBuffer()),
+ Bench("Promise/Future",
+ () => pfBenchmark(nChameneos, meetings),
+ MBuffer()),
+ Bench("Scala channels",
+ () => scBenchmark(nChameneos, meetings),
+ MBuffer()),
+ Bench("ArrayBlockingQueues",
+ () => jqBenchmark(nChameneos, meetings,
+ () => new java.util.concurrent.ArrayBlockingQueue[JQResponse](1),
+ () => new java.util.concurrent.ArrayBlockingQueue[JQGreeting](1),
+ () => new java.util.concurrent.ArrayBlockingQueue[JQAnswer](1)),
+ MBuffer()),
+ Bench("lchannels (queues)",
+ () => lBenchmark(QueueChannel.factory, QueueChannel.factory,
+ nChameneos, meetings),
+ MBuffer()),
+ Bench("LinkedTransferQueues",
+ () => jqBenchmark(nChameneos, meetings,
+ () => new java.util.concurrent.LinkedTransferQueue[JQResponse](),
+ () => new java.util.concurrent.LinkedTransferQueue[JQGreeting](),
+ () => new java.util.concurrent.LinkedTransferQueue[JQAnswer]()),
+ MBuffer()),
+ Bench("lchannels (actors)",
+ () => lBenchmark(ActorChannel.factory, ActorChannel.factory,
+ nChameneos, meetings),
+ MBuffer())
+ )
+
+ val rnd = new scala.util.Random()
+
+ val pfRes = for (i <- 0 until reps) yield {
+ print(f"\r Repetition: ${i+1}/${reps}")
+ // Execute benchmarks in random order at each iteration, for fairness
+ for (b <- rnd.shuffle(benchmarks)) {
+ System.gc(); System.runFinalization() // Best time to garbage collect
+ b.results += b.f()
+ }
+ }
+ println(" (Done)")
+
+ // Shut down the actor system
+ as.terminate()
+
+ for (b <- benchmarks) yield BenchmarkResult(b.title, b.results.iterator)
+ }
+
+ // Convenience function to provide an initial color for a chameneos
+ private def colorMap(i: Int) = i % 3 match {
+ case 0 => Red()
+ case 1 => Green()
+ case 2 => Blue()
+ }
+
+ private def lBenchmark(rfactory: () => (In[LResponse], Out[LResponse]),
+ cfactory: () => (In[LGreeting], Out[LGreeting]),
+ nChameneos: Int, meetings: Int)
+ (implicit ec: ExecutionContext,
+ d: Duration): Long = {
+ import LChannelsImpl.{Broker, Chameneos}
+ val broker = new Broker(meetings, rfactory, cfactory)
+
+ val chameneos = for (i <- 0 until nChameneos) yield {
+ new Chameneos(f"Chameneos ${i}", colorMap(i), broker)
+ }
+
+ val startTime = System.nanoTime()
+ broker.activate()
+ for (c <- chameneos) c.join()
+ val endTime = System.nanoTime() - startTime
+ broker.quit()
+
+ endTime
+ }
+
+ private def pfBenchmark(nChameneos: Int, meetings: Int)
+ (implicit ec: ExecutionContext,
+ d: Duration): Long = {
+ import PromiseFutureImpl.{Broker, Chameneos}
+ val broker = new Broker(meetings)
+
+ val chameneos = for (i <- 0 until nChameneos) yield {
+ new Chameneos(f"Chameneos ${i}", colorMap(i), broker)
+ }
+
+ val startTime = System.nanoTime()
+ broker.activate()
+ for (c <- chameneos) c.join()
+ val endTime = System.nanoTime() - startTime
+ broker.quit()
+
+ endTime
+ }
+
+ private def scBenchmark(nChameneos: Int, meetings: Int)
+ (implicit ec: ExecutionContext,
+ d: Duration): Long = {
+ import ScalaChannelsImpl.{Broker, Chameneos}
+ val broker = new Broker(meetings)
+
+ val chameneos = for (i <- 0 until nChameneos) yield {
+ new Chameneos(f"Chameneos ${i}", colorMap(i), broker)
+ }
+
+ val startTime = System.nanoTime()
+ broker.activate()
+ for (c <- chameneos) c.join()
+ val endTime = System.nanoTime() - startTime
+ broker.quit()
+
+ endTime
+ }
+
+ private def jqBenchmark(nChameneos: Int, meetings: Int,
+ rfactory: () => BlockingQueue[JQResponse],
+ gfactory: () => BlockingQueue[JQGreeting],
+ afactory: () => BlockingQueue[JQAnswer])
+ (implicit ec: ExecutionContext,
+ d: Duration): Long = {
+ import JavaBlockingQueuesImpl.{Broker, Chameneos}
+ val broker = new Broker(meetings, rfactory, gfactory, afactory)
+
+ val chameneos = for (i <- 0 until nChameneos) yield {
+ new Chameneos(f"Chameneos ${i}", colorMap(i), broker)
+ }
+
+ val startTime = System.nanoTime()
+ broker.activate()
+ for (c <- chameneos) c.join()
+ val endTime = System.nanoTime() - startTime
+ broker.quit()
+
+ endTime
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/PingPong.scala b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/PingPong.scala
new file mode 100644
index 0000000..1ea73d9
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/PingPong.scala
@@ -0,0 +1,435 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.benchmarks.pingpong
+
+import lchannels._
+import scala.concurrent.{blocking, Promise, Future}
+import scala.concurrent.duration.Duration
+
+/** lchannels-based implementation (medium-independent). */
+object LChannelsImpl {
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type:
+ // rec X . ?Ping(String) . (!Pong(String). X (+) Stop())
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Request
+ case class Ping(msg: String)(val cont: Out[Pong]) extends Request
+ case class Stop() extends Request
+
+ case class Pong(msg: String)(val cont: Out[Request])
+ ////////////////////////////////////////////////////////////////////////////
+
+ @scala.annotation.tailrec
+ def pinger(c: Out[Request], msg: String, ts: Long, n: Int): Long = {
+ // Update timestamp if not yet set, thus registering actual start time
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ val pingResponse = c !! Ping(msg)_
+ pingResponse.receive() match {
+ case pong => pinger(pong.cont, msg, ts2, n-1)
+ }
+ } else {
+ c ! Stop()
+ ts2
+ }
+ }
+
+ @scala.annotation.tailrec
+ def ponger(c: In[Request]): Long = {
+ c.receive() match {
+ case ping @ Ping(msg) => ponger(ping.cont !! Pong(msg)_)
+ case Stop() => System.nanoTime()
+ }
+ }
+}
+
+/** Promise/Future-based implementation. */
+object PromiseFutureImpl {
+ import scala.concurrent.Await
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type:
+ // rec X . ?Ping(String) . !Pong(String). X
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Request
+ case class Ping(msg: String)(val cont: Promise[Pong]) extends Request
+ case class Stop() extends Request
+ case class Pong(msg: String)(val cont: Promise[Request])
+ ////////////////////////////////////////////////////////////////////////////
+
+ @scala.annotation.tailrec
+ def pinger(p: Promise[Request], msg: String, ts: Long, n: Int)
+ (implicit d: Duration): Long = {
+ // Update timestamp if not yet set, thus registering actual start time
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ val p2 = Promise[Pong]; val f2 = p2.future
+ p success Ping(msg)(p2)
+ val pong = Await.result(f2, d)
+ pinger(pong.cont, msg, ts2, n-1)
+ } else {
+ p success Stop()
+ ts2
+ }
+ }
+
+ @scala.annotation.tailrec
+ def ponger(f: Future[Request])
+ (implicit d: Duration): Long = {
+ Await.result(f, d) match {
+ case ping @ Ping(msg) => {
+ val p2 = Promise[Request]; val f2 = p2.future
+ ping.cont success Pong(msg)(p2)
+ ponger(f2)
+ }
+ case Stop() => System.nanoTime()
+ }
+ }
+}
+
+/** Scala Channel-based implementation. */
+object ScalaChannelsImpl {
+ import scala.concurrent.Channel
+
+ sealed abstract class Request
+ case class Ping(msg: String) extends Request
+ case class Stop() extends Request
+
+ case class Pong(msg: String)
+
+ @scala.annotation.tailrec
+ def pinger(in: Channel[Pong], out: Channel[Request],
+ msg: String, ts: Long, n: Int): Long = {
+ // Update timestamp if not yet set, thus registering actual start time
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ out.write(Ping(msg))
+ in.read
+ pinger(in, out, msg, ts2, n-1)
+ } else {
+ out.write(Stop())
+ ts2
+ }
+ }
+
+ @scala.annotation.tailrec
+ def ponger(in: Channel[Request], out: Channel[Pong]): Long = {
+ in.read match {
+ case Ping(msg) => out.write(Pong(msg)); ponger(in, out)
+ case Stop() => System.nanoTime()
+ }
+ }
+}
+
+/** Java blocking queues-based implementation */
+object JavaBlockingQueuesImpl {
+ import java.util.concurrent.BlockingQueue
+
+ sealed abstract class Request
+ case class Ping(msg: String) extends Request
+ case class Stop() extends Request
+
+ case class Pong(msg: String)
+
+ @scala.annotation.tailrec
+ def pinger(in: BlockingQueue[Pong], out: BlockingQueue[Request],
+ msg: String, ts: Long, n: Int): Long = {
+ // Update timestamp if not yet set, thus registering actual start time
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ out.put(Ping(msg))
+ in.take()
+ pinger(in, out, msg, ts2, n-1)
+ } else {
+ out.put(Stop())
+ ts2
+ }
+ }
+
+ @scala.annotation.tailrec
+ def ponger(in: BlockingQueue[Request], out: BlockingQueue[Pong]): Long = {
+ in.take() match {
+ case Ping(msg) => out.put(Pong(msg)); ponger(in, out)
+ case Stop() => System.nanoTime()
+ }
+ }
+}
+
+/** Akka Typed implementation, optimized with actor reusage. */
+object AkkaTypedImpl {
+ import akka.typed.{
+ Behavior, ActorRef, Props
+ }
+ import akka.typed.ScalaDSL.{
+ ContextAware, Same, Stopped, Total
+ }
+
+ sealed abstract class Request
+ case class Ping(msg: String, replyTo: ActorRef[Pong]) extends Request
+ case class Stop() extends Request
+
+ case class Pong(msg: String, replyTo: ActorRef[Request])
+
+ def pingerBeh(msg: String, n: Int): Behavior[Pong] = {
+ ContextAware[Pong] { ctx =>
+ Total[Pong] {
+ case Pong(_, replyTo) => {
+ if (n > 0) {
+ val contBeh = ctx.spawnAnonymous(Props(pingerBeh(msg, n-1)))
+ replyTo ! Ping(msg, contBeh)
+ Same
+ } else {
+ replyTo ! Stop()
+ Stopped
+ }
+ }
+ }
+ }
+ }
+
+ def pongerBeh(endTS: Promise[Long]): Behavior[Request] = {
+ ContextAware[Request] { ctx =>
+ Total[Request] {
+ case Ping(msg, replyTo) => {
+ val contBeh = ctx.spawnAnonymous(Props(pongerBeh(endTS)))
+ replyTo ! Pong(msg, contBeh)
+ Same
+ }
+ case Stop() => {
+ endTS success System.nanoTime()
+ Stopped
+ }
+ }
+ }
+ }
+
+ def pingerBehOpt(msg: String, n: Int): Behavior[Pong] = {
+ ContextAware[Pong] { ctx =>
+ Total[Pong] {
+ case Pong(_, replyTo) => {
+ if (n > 0) {
+ replyTo ! Ping(msg, ctx.self)
+ pingerBehOpt(msg, n-1)
+ } else {
+ replyTo ! Stop()
+ Stopped
+ }
+ }
+ }
+ }
+ }
+
+ def pongerBehOpt(endTS: Promise[Long]): Behavior[Request] = {
+ ContextAware[Request] { ctx =>
+ Total[Request] {
+ case Ping(msg, replyTo) => {
+ replyTo ! Pong(msg, ctx.self)
+ pongerBehOpt(endTS)
+ }
+ case Stop() => {
+ endTS success System.nanoTime()
+ Stopped
+ }
+ }
+ }
+ }
+
+ def benchmark(msg: String, exchanges: Int, maxDuration: Duration)
+ (implicit as: akka.actor.ActorSystem): Long = {
+ benchmarkImpl(msg, exchanges, maxDuration, pingerBeh, pongerBeh)
+ }
+
+ def benchmarkOpt(msg: String, exchanges: Int, maxDuration: Duration)
+ (implicit as: akka.actor.ActorSystem): Long = {
+ benchmarkImpl(msg, exchanges, maxDuration, pingerBehOpt, pongerBehOpt)
+ }
+
+ private def benchmarkImpl(msg: String, exchanges: Int, maxDuration: Duration,
+ pingerB: (String, Int) => Behavior[Pong],
+ pongerB: Promise[Long] => Behavior[Request])
+ (implicit as: akka.actor.ActorSystem)= {
+ import akka.typed.Ops
+ import scala.concurrent.Await
+
+ val endTS = Promise[Long]
+
+ val pingRef = Ops.ActorSystemOps(as).spawn(
+ Props(pingerB(msg, exchanges - 1))) // We will send the first Ping
+ val pongRef = Ops.ActorSystemOps(as).spawn(
+ Props(pongerB(endTS)))
+
+ val startTS = System.nanoTime()
+ pongRef ! Ping(msg, pingRef)
+
+ Await.result(endTS.future, maxDuration) - startTS
+ }
+}
+
+object Benchmark {
+ import lchannels.benchmarks.{BenchmarkResult, BenchmarkResults}
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ import java.util.concurrent.BlockingQueue
+
+ import LChannelsImpl.Request
+
+ /** Perform benchmarks. */
+ def apply(msgCount: Int, reps: Int, msg: String): BenchmarkResults = {
+ import scala.collection.mutable.{Buffer => MBuffer}
+
+ case class Bench(title: String, f: () => Long, results: MBuffer[Long])
+
+ implicit val as = akka.actor.ActorSystem("PingPingBenchmark",
+ defaultExecutionContext = Some(global))
+
+ // implicit val timeout = 5.seconds
+ implicit val timeout = Duration.Inf
+
+ val maxWait = 3600.seconds
+ val exchanges = msgCount / 2
+
+ println(f"*** Ping-Pong benchmark (${exchanges} message exchanges)")
+
+ val benchmarks = List(
+ Bench("lchannels (Promise/Future)",
+ () => lBench(LocalChannel.parallel, msg, exchanges, maxWait),
+ MBuffer()),
+ Bench("Promise/Future", () => pfBench(msg, exchanges, maxWait),
+ MBuffer()),
+ Bench("Scala channels", () => scBench(msg, exchanges, maxWait),
+ MBuffer()),
+ Bench("ArrayBlockingQueues", () => {
+ jqBench(msg, exchanges,
+ () => new java.util.concurrent.ArrayBlockingQueue[JavaBlockingQueuesImpl.Pong](exchanges+1),
+ () => new java.util.concurrent.ArrayBlockingQueue[JavaBlockingQueuesImpl.Request](exchanges+1),
+ maxWait)
+ },
+ MBuffer()),
+ Bench("lchannels (queues)",
+ () => lBench(QueueChannel.parallel, msg, exchanges, maxWait),
+ MBuffer()),
+ Bench("LinkedTransferQueues", () => {
+ jqBench(msg, exchanges,
+ () => new java.util.concurrent.LinkedTransferQueue[JavaBlockingQueuesImpl.Pong](),
+ () => new java.util.concurrent.LinkedTransferQueue[JavaBlockingQueuesImpl.Request](),
+ maxWait)
+ },
+ MBuffer()),
+ Bench("lchannels (actors)",
+ () => lBench(ActorChannel.parallel, msg, exchanges, maxWait),
+ MBuffer())
+ // Bench("Akka Typed",
+ // () => AkkaTypedImpl.benchmark(msg, exchanges, maxWait),
+ // MBuffer()),
+ // Bench("Akka Typed (optim)",
+ // () => AkkaTypedImpl.benchmarkOpt(msg, exchanges, maxWait),
+ // MBuffer())
+ )
+
+ val rnd = new scala.util.Random()
+
+ val pfRes = for (i <- 0 until reps) yield {
+ print(f"\r Repetition: ${i+1}/${reps}")
+ // Execute benchmarks in random order at each iteration, for fairness
+ for (b <- rnd.shuffle(benchmarks)) {
+ System.gc(); System.runFinalization() // Best time to garbage collect
+ b.results += b.f()
+ }
+ }
+ println(" (Done)")
+
+ // Shut down the actor system
+ as.terminate()
+
+ for (b <- benchmarks) yield BenchmarkResult(b.title, b.results.iterator)
+ }
+
+ private def lBench(parallel: (In[Request] => Long,
+ Out[Request] => Long) => (Future[Long], Future[Long]),
+ msg: String,
+ exchanges: Int,
+ maxWait: Duration)
+ (implicit d: Duration): Long = {
+ val (pong, ping) = parallel(
+ LChannelsImpl.ponger(_), LChannelsImpl.pinger(_, msg, 0, exchanges)
+ )
+ val startTS = Await.result(ping, maxWait)
+ val endTS = Await.result(pong, maxWait)
+ endTS - startTS
+ }
+
+ private def pfBench(msg: String, exchanges: Int, maxWait: Duration)
+ (implicit d: Duration): Long = {
+ val p = Promise[PromiseFutureImpl.Request]; val f = p.future
+
+ val ping = Future {
+ blocking { PromiseFutureImpl.pinger(p, msg, 0, exchanges) }
+ }
+ val pong = Future {
+ blocking { PromiseFutureImpl.ponger(f) }
+ }
+
+ val startTS = Await.result(ping, maxWait)
+ val endTS = Await.result(pong, maxWait)
+ endTS - startTS
+ }
+
+ private def scBench(msg: String, exchanges: Int, maxWait: Duration): Long = {
+ import scala.concurrent.Channel
+ import ScalaChannelsImpl.{Request, Pong, pinger, ponger}
+
+ val c1 = new Channel[Pong]()
+ val c2 = new Channel[Request]()
+
+ val ping = Future { blocking { pinger(c1, c2, msg, 0, exchanges) } }
+ val pong = Future { blocking { ponger(c2, c1) } }
+
+ val startTS = Await.result(ping, maxWait)
+ val endTS = Await.result(pong, maxWait)
+ endTS - startTS
+ }
+
+ private def jqBench(msg: String, exchanges: Int,
+ qFactoryPong: () => BlockingQueue[JavaBlockingQueuesImpl.Pong],
+ qFactoryRequest: () => BlockingQueue[JavaBlockingQueuesImpl.Request],
+ maxWait: Duration): Long = {
+ import JavaBlockingQueuesImpl.{pinger, ponger}
+
+ // Make the queues big enough to contain all the message exchanges
+ val c1 = qFactoryPong()
+ val c2 = qFactoryRequest()
+
+ val ping = Future { blocking { pinger(c1, c2, msg, 0, exchanges) } }
+ val pong = Future { blocking { ponger(c2, c1) } }
+
+ val startTS = Await.result(ping, maxWait)
+ val endTS = Await.result(pong, maxWait)
+ endTS - startTS
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Ring.scala b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Ring.scala
new file mode 100644
index 0000000..a27383a
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/Ring.scala
@@ -0,0 +1,583 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.benchmarks.ring
+
+import lchannels._
+import scala.concurrent.{blocking, Promise, Future}
+import scala.concurrent.duration.Duration
+
+/** lchannels-based implementation (medium-independent) */
+object LChannelsImpl {
+ ////////////////////////////////////////////////////////////////////////////
+ // Session type:
+ // rec X . !Token(String) . X (+) !Stop() . End
+ ////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Command
+ case class Fwd(msg: String)(val cont: In[Command]) extends Command
+ case class Stop() extends Command
+ ////////////////////////////////////////////////////////////////////////////
+
+ @scala.annotation.tailrec
+ def forwarder(in: In[Command],
+ out: Out[Command]): Unit = {
+ in.receive() match {
+ case Stop() => out ! Stop()
+ case m @ Fwd(msg) => forwarder(m.cont, out !! Fwd(msg)_)
+ }
+ }
+
+ @scala.annotation.tailrec
+ def master(in: In[Command],
+ out: Out[Command],
+ msg: String, ts: Long, n: Int): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ val outCont = out !! Fwd(msg)_
+ in.receive() match {
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ case m @ Fwd(_) => master(m.cont, outCont, msg, ts2, n-1)
+ }
+ } else {
+ out ! Stop()
+ in.receive() match {
+ case Stop() => System.nanoTime() - ts2
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def masterStream(in: In[Command],
+ out: Out[Command],
+ msg: String, ts: Long, msgCount: Int,
+ sent: Int, recvd: Int): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+
+ if (sent < msgCount) {
+ masterStream(in, out !! Fwd(msg)_, msg, ts2, msgCount, sent+1, recvd)
+ } else if (recvd < msgCount) {
+ in.receive() match {
+ case m @ Fwd(_) => {
+ masterStream(m.cont, out, msg, ts2, msgCount, sent, recvd+1)
+ }
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ } else {
+ out ! Stop()
+ in.receive() match {
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ case Stop() => System.nanoTime() - ts2
+ }
+ }
+ }
+}
+
+/** Promise/Future-based implementation */
+object PromiseFutureImpl {
+ import scala.concurrent.Await
+
+ sealed abstract class Command
+ case class Fwd(msg: String, cont: Future[Command])
+ extends Command
+ case class Stop()
+ extends Command
+
+ @scala.annotation.tailrec
+ def forwarder(in: Future[Command],
+ out: Promise[Command])
+ (implicit d: Duration): Unit = {
+ Await.result(in, d) match {
+ case Stop() => out.success(Stop())
+ case Fwd(msg, cont) => {
+ val p = Promise[Command]; val f = p.future
+ out.success(Fwd(msg, f))
+ forwarder(cont, p)
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def master(in: Future[Command],
+ out: Promise[Command],
+ msg: String, ts: Long, n: Int)
+ (implicit d: Duration): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ val p = Promise[Command]; val f = p.future
+ out.success(Fwd(msg, f))
+ Await.result(in, d) match {
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ case Fwd(_, cont) => {
+ master(cont, p, msg, ts2, n-1)
+ }
+ }
+ } else {
+ out.success(Stop())
+ Await.result(in, d) match {
+ case Stop() => System.nanoTime() - ts2
+ case Fwd(_, _) => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def masterStream(in: Future[Command],
+ out: Promise[Command],
+ msg: String, ts: Long, msgCount: Int, sent: Int, recvd: Int)
+ (implicit d: Duration): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+
+ if (sent < msgCount) {
+ val cout = Promise[Command]; val cin = cout.future
+ out.success(Fwd(msg, cin))
+ masterStream(in, cout, msg, ts2, msgCount, sent+1, recvd)
+ } else if (recvd < msgCount) {
+ Await.result(in, d) match {
+ case Fwd(_msg, cont) => {
+ masterStream(cont, out, msg, ts2, msgCount, sent, recvd+1)
+ }
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ } else {
+ out.success(Stop())
+ Await.result(in, d) match {
+ case Fwd(_,_) => throw new RuntimeException("BUG in ring benchmark!")
+ case Stop() => System.nanoTime() - ts2
+ }
+ }
+ }
+}
+
+/** Scala Channel-based implementation */
+object ScalaChannelsImpl {
+ import scala.concurrent.Channel
+
+ sealed abstract class Command
+ case class Fwd(msg: String)
+ extends Command
+ case class Stop()
+ extends Command
+
+ @scala.annotation.tailrec
+ def forwarder(in: Channel[Command],
+ out: Channel[Command]): Unit = {
+ in.read match {
+ case Stop() => out.write(Stop())
+ case Fwd(msg) => {
+ out.write(Fwd(msg))
+ forwarder(in, out)
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def master(in: Channel[Command],
+ out: Channel[Command],
+ msg: String, ts: Long, n: Int): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ out.write(Fwd(msg))
+ in.read match {
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ case Fwd(_) => {
+ master(in, out, msg, ts2, n-1)
+ }
+ }
+ } else {
+ out.write(Stop())
+ in.read match {
+ case Stop() => System.nanoTime() - ts2
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def masterStream(in: Channel[Command],
+ out: Channel[Command],
+ msg: String, ts: Long, msgCount: Int, sent: Int, recvd: Int)
+ (implicit d: Duration): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+
+ if (sent < msgCount) {
+ out.write(Fwd(msg))
+ masterStream(in, out, msg, ts2, msgCount, sent+1, recvd)
+ } else if (recvd < msgCount) {
+ in.read match {
+ case Fwd(_) => {
+ masterStream(in, out, msg, ts2, msgCount, sent, recvd+1)
+ }
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ } else {
+ out.write(Stop())
+ in.read match {
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ case Stop() => System.nanoTime() - ts2
+ }
+ }
+ }
+}
+
+/** Java blocking queues-based implementation */
+object JavaBlockingQueuesImpl {
+ import java.util.concurrent.BlockingQueue
+
+ sealed abstract class Command
+ case class Fwd(msg: String)
+ extends Command
+ case class Stop()
+ extends Command
+
+ @scala.annotation.tailrec
+ def forwarder(in: BlockingQueue[Command],
+ out: BlockingQueue[Command]): Unit = {
+ in.take() match {
+ case Stop() => out.put(Stop())
+ case Fwd(msg) => {
+ out.put(Fwd(msg))
+ forwarder(in, out)
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def master(in: BlockingQueue[Command],
+ out: BlockingQueue[Command],
+ msg: String, ts: Long, n: Int): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+ if (n > 0) {
+ out.put(Fwd(msg))
+ in.take() match {
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ case Fwd(_) => {
+ master(in, out, msg, ts2, n-1)
+ }
+ }
+ } else {
+ out.put(Stop())
+ in.take() match {
+ case Stop() => System.nanoTime() - ts2
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ }
+ }
+
+ @scala.annotation.tailrec
+ def masterStream(in: BlockingQueue[Command],
+ out: BlockingQueue[Command],
+ msg: String, ts: Long, msgCount: Int, sent: Int, recvd: Int)
+ (implicit d: Duration): Long = {
+ val ts2 = if (ts == 0) System.nanoTime() else ts
+
+ if (sent < msgCount) {
+ out.put(Fwd(msg))
+ masterStream(in, out, msg, ts2, msgCount, sent+1, recvd)
+ } else if (recvd < msgCount) {
+ in.take() match {
+ case Fwd(_) => {
+ masterStream(in, out, msg, ts2, msgCount, sent, recvd+1)
+ }
+ case Stop() => throw new RuntimeException("BUG in ring benchmark!")
+ }
+ } else {
+ out.put(Stop())
+ in.take() match {
+ case Fwd(_) => throw new RuntimeException("BUG in ring benchmark!")
+ case Stop() => System.nanoTime() - ts2
+ }
+ }
+ }
+}
+
+/** Akka Typed implementation, optimized with actor reusage. */
+object AkkaTypedImpl {
+ import akka.typed.{Behavior, ActorRef, Props}
+ import akka.typed.ScalaDSL.{Same, Stopped, Total}
+
+ sealed abstract class Command
+ case class Fwd(msg: String) extends Command
+ case class Stop() extends Command
+
+ def forwarderBeh(fwdTo: ActorRef[Command]) = Total[Command] {
+ case m @ Fwd(_) => { fwdTo ! m; Same }
+ case Stop() => { fwdTo ! Stop(); Stopped }
+ }
+
+ def masterBeh(fwdToP: Future[ActorRef[Command]],
+ n: Int, endTS: Promise[Long])
+ (implicit d: Duration): Behavior[Command] = Total[Command] {
+ case m @ Fwd(msg) => {
+ val fwdTo = scala.concurrent.Await.result(fwdToP, d) // FIXME: optimize?
+ if (n > 0) {
+ fwdTo ! m
+ masterBeh(fwdToP, n-1, endTS)
+ } else {
+ fwdTo ! Stop()
+ Same
+ }
+ }
+ case Stop() => {
+ endTS success System.nanoTime()
+ Stopped
+ }
+ }
+
+ def benchmark(msg: String, exchanges: Int, ringSize: Int,
+ maxDuration: Duration)
+ (implicit as: akka.actor.ActorSystem) = {
+ import akka.typed.Ops
+ import scala.concurrent.Await
+ import scala.concurrent.duration._
+ import scala.collection.mutable.{Buffer => MBuffer}
+
+ // implicit val timeout = 10.seconds
+ implicit val timeout = Duration.Inf
+
+ val endTS = Promise[Long]
+ val fwdToP = Promise[ActorRef[Command]]
+
+ val masterRef = Ops.ActorSystemOps(as).spawn(
+ Props(masterBeh(fwdToP.future, exchanges, endTS))
+ )
+
+ val forwarderRefs = MBuffer[ActorRef[Command]](masterRef)
+
+ for (j <- 1 until ringSize) {
+ val r = Ops.ActorSystemOps(as).spawn(
+ Props(forwarderBeh(forwarderRefs(j-1)))
+ )
+ forwarderRefs += r
+ }
+
+ fwdToP success forwarderRefs(ringSize-1)
+
+ val startTS = System.nanoTime()
+ masterRef ! Fwd(msg)
+
+ Await.result(endTS.future, maxDuration) - startTS
+ }
+}
+
+object Benchmark {
+ import benchmarks.{BenchmarkResult, BenchmarkResults}
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ import java.util.concurrent.BlockingQueue
+
+ import LChannelsImpl.Command
+
+ /** Type of ring benchmark. */
+ sealed abstract class RingType
+ /** Standard ring benchmark. */
+ case class Standard() extends RingType {
+ override def toString = "Standard"
+ }
+ /** Ring benchmark where all messages are sent at once. */
+ case class Streaming() extends RingType {
+ override def toString = "Streaming"
+ }
+
+ /** Perform benchmarks. */
+ def apply(which: RingType, msgCount: Int, ringSize: Int,
+ repetitions: Int, msg: String): BenchmarkResults = {
+ import scala.collection.mutable.{Buffer => MBuffer}
+ case class Bench(title: String, f: () => Long, results: MBuffer[Long])
+
+ implicit val as = akka.actor.ActorSystem("RingBenchmark",
+ defaultExecutionContext = Some(global))
+
+ implicit val timeout = 3600.seconds
+ val maxWait = 3600.seconds
+ val loops = msgCount / ringSize
+
+ println(f"*** Ring benchmark (${which}, ${ringSize} processes, ${loops} message loops)")
+
+ val benchmarks = List(
+ Bench("lchannels (Promise/Future)",
+ () => lBenchmark(which, LocalChannel.factory,
+ msg, loops, ringSize, maxWait),
+ MBuffer()),
+ Bench("Promise/Future",
+ () => pfBenchmark(which, msg, loops, ringSize, maxWait),
+ MBuffer()),
+ Bench("Scala channels",
+ () => scBenchmark(which, msg, loops, ringSize, maxWait),
+ MBuffer()),
+ Bench("ArrayBlockingQueues",
+ () => jqBenchmark(which, msg, loops, ringSize,
+ () => new java.util.concurrent.ArrayBlockingQueue[JavaBlockingQueuesImpl.Command](loops+1),
+ maxWait),
+ MBuffer()),
+ Bench("lchannels (queues)",
+ () => lBenchmark(which, QueueChannel.factory,
+ msg, loops, ringSize, maxWait),
+ MBuffer()),
+ Bench("LinkedTransferQueues",
+ () => jqBenchmark(which, msg, loops, ringSize,
+ () => new java.util.concurrent.LinkedTransferQueue[JavaBlockingQueuesImpl.Command](),
+ maxWait),
+ MBuffer()),
+ Bench("lchannels (actors)",
+ () => lBenchmark(which, ActorChannel.factory,
+ msg, loops, ringSize, maxWait),
+ MBuffer())
+ )
+ // Bench("Akka Typed",
+ // () => AkkaTypedImpl.benchmark(msg, exchanges, ringSize, maxWait),
+ // MBuffer())
+
+ val rnd = new scala.util.Random()
+
+ val pfRes = for (i <- 0 until repetitions) {
+ print(f"\r Repetition: ${i+1}/${repetitions}")
+ // Execute benchmarks in random order at each iteration, for fairness
+ for (b <- rnd.shuffle(benchmarks)) {
+ System.gc(); System.runFinalization() // Best time to garbage collect
+ b.results += b.f()
+ }
+ }
+ println(" (Done)")
+
+ // Shut down the actor system
+ as.terminate()
+
+ for (b <- benchmarks) yield BenchmarkResult(b.title, b.results.iterator)
+ }
+
+ private def lBenchmark(which: RingType,
+ factory: () => (In[Command], Out[Command]),
+ msg: String, exchanges: Int, ringSize: Int,
+ maxWait: Duration)
+ (implicit d: Duration): Long = {
+ val channels = for (j <- 0 until ringSize) yield factory()
+ for (j <- 0 until ringSize-1) {
+ new Thread(new Runnable {
+ def run() = LChannelsImpl.forwarder(channels(j)._1, channels(j+1)._2)
+ }, f"Forwarder ${j+1}").start()
+ }
+ val res = Future {
+ blocking {
+ which match {
+ case Standard() => LChannelsImpl.master(channels(ringSize-1)._1,
+ channels(0)._2,
+ msg, 0, exchanges)
+ case Streaming() => LChannelsImpl.masterStream(channels(ringSize-1)._1,
+ channels(0)._2,
+ msg, 0, exchanges, 0, 0)
+ }
+ }
+ }
+ Await.result(res, maxWait)
+ }
+
+ private def pfBenchmark(which: RingType, msg: String,
+ exchanges: Int, ringSize: Int, maxWait: Duration)
+ (implicit d: Duration): Long = {
+ val channels = for (j <- 0 until ringSize) yield {
+ val p = Promise[PromiseFutureImpl.Command]; val f = p.future
+ (f, p)
+ }
+ for (j <- 0 until ringSize-1) {
+ new Thread(new Runnable {
+ def run() = PromiseFutureImpl.forwarder(channels(j)._1, channels(j+1)._2)
+ }, f"Forwarder ${j+1}").start()
+ }
+ val res = Future {
+ blocking {
+ which match {
+ case Standard() => PromiseFutureImpl.master(channels(ringSize-1)._1,
+ channels(0)._2,
+ msg, 0, exchanges)
+ case Streaming() => PromiseFutureImpl.masterStream(channels(ringSize-1)._1,
+ channels(0)._2,
+ msg, 0, exchanges, 0, 0)
+ }
+ }
+ }
+ Await.result(res, maxWait)
+ }
+
+ private def scBenchmark(which: RingType, msg: String,
+ exchanges: Int, ringSize: Int, maxWait: Duration)
+ (implicit d: Duration): Long = {
+ import scala.concurrent.Channel
+
+ val channels = for (j <- 0 until ringSize) yield {
+ new Channel[ScalaChannelsImpl.Command]
+ }
+ for (j <- 0 until ringSize-1) {
+ new Thread(new Runnable {
+ def run() = ScalaChannelsImpl.forwarder(channels(j), channels(j+1))
+ }, f"Forwarder ${j+1}").start()
+ }
+ val res = Future {
+ blocking {
+ which match {
+ case Standard() => ScalaChannelsImpl.master(channels(ringSize-1),
+ channels(0),
+ msg, 0, exchanges)
+ case Streaming() => ScalaChannelsImpl.masterStream(channels(ringSize-1),
+ channels(0),
+ msg, 0, exchanges, 0, 0)
+ }
+ }
+ }
+ Await.result(res, maxWait)
+ }
+
+ private def jqBenchmark(which: RingType, msg: String, exchanges: Int,
+ ringSize: Int,
+ qFactory: () => BlockingQueue[JavaBlockingQueuesImpl.Command],
+ maxWait: Duration)
+ (implicit d: Duration): Long = {
+ val channels = for (j <- 0 until ringSize) yield {
+ // NOTE: qFactory must return queues big enough for all message exchanges
+ qFactory()
+ }
+ for (j <- 0 until ringSize-1) {
+ new Thread(new Runnable {
+ def run() = JavaBlockingQueuesImpl.forwarder(channels(j), channels(j+1))
+ }, f"Forwarder ${j+1}").start()
+ }
+ val res = Future {
+ blocking {
+ which match {
+ case Standard() => JavaBlockingQueuesImpl.master(channels(ringSize-1),
+ channels(0),
+ msg, 0, exchanges)
+ case Streaming() => JavaBlockingQueuesImpl.masterStream(channels(ringSize-1),
+ channels(0),
+ msg, 0, exchanges, 0, 0)
+ }
+ }
+ }
+ Await.result(res, maxWait)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/package.scala b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/package.scala
new file mode 100644
index 0000000..41da177
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/benchmarks/src/main/scala/package.scala
@@ -0,0 +1,107 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+package object benchmarks {
+ private val defaultMsgCount = 25600
+
+ case class BenchmarkResult(title: String, data: Iterator[Long])
+ type BenchmarkResults = List[BenchmarkResult]
+
+ def main(args: Array[String]): Unit = {
+ val savePath = if (args.length < 1) {
+ throw new IllegalArgumentException("You must provide the path for saving benchmark results")
+ } else {
+ args(0)
+ }
+
+ val msgCount = if (args.length >= 2) args(1).toInt else defaultMsgCount
+
+ run(savePath, msgCount)
+ }
+
+ def run(savePath: String, msgCount: Int) {
+ import java.nio.file.Paths
+
+ case class Benchmark(title: String, benchf: () => BenchmarkResults,
+ filename: String)
+
+ val repetitions = 30 // 30 should be the minimum for one JVM invocation
+
+ println(f"Starting benchmarks: ${msgCount} minimum message transmissions, ${repetitions} repetitions")
+
+ val benchmarks = List(
+ Benchmark(title = f"Ping-pong (${msgCount} message exchanges)",
+ benchf = () => pingpong.Benchmark(msgCount*2, repetitions, "Token"),
+ filename = "pingpong.csv"),
+ Benchmark(title = f"Ring (${1000} processes, ${msgCount/1000} loops)",
+ benchf = () => ring.Benchmark(ring.Benchmark.Standard(),
+ msgCount, 1000, repetitions, "Token"),
+ filename = "ring.csv"),
+ Benchmark(title = f"Streaming (${16} processes, ${msgCount*3/2} msgs sent/recvd)",
+ benchf = () => ring.Benchmark(ring.Benchmark.Streaming(),
+ msgCount*3/2, 16, repetitions, "Token"),
+ filename = "ring-stream.csv"),
+ Benchmark(title = f"Chameneos (${256} chameneos, ${(msgCount*6) / (3 * 2)} meetings)",
+ benchf = () => chameneos.Benchmark(msgCount*6, 256, repetitions),
+ filename = "chameneos.csv")
+ )
+
+ for (b <- benchmarks) {
+ val fname = Paths.get(savePath, b.filename).toString()
+ runAndSave(b.title, b.benchf, fname)
+ println(f" Results saved in: ${fname}")
+ }
+ }
+
+ private def runAndSave(title: String, benchf: () => BenchmarkResults,
+ filename: String) : Unit = {
+ import java.io.{File, PrintWriter}
+
+ val delim = ","
+
+ val out = new PrintWriter(new File(filename))
+
+ val res = benchf()
+
+ val nbench = res.length
+ val titles = res.map(x => x.title)
+
+ out.write(title ++ "\n")
+
+ // Save benchmarks as values in column under their respective titles
+ out.write(titles.mkString(delim) ++ "\n")
+ while (res(0).data.hasNext) {
+ out.write(
+ (for (i <- 0 until nbench) yield res(i).data.next()).mkString(delim)
+ )
+ out.write("\n")
+ }
+
+ out.close()
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/build.sbt b/src/main/jbake/assets/docs/lchannels/build.sbt
new file mode 100644
index 0000000..1c21797
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/build.sbt
@@ -0,0 +1,54 @@
+// Kludge to avoid building an empty .jar for the root project
+Keys.`package` := {
+ (Keys.`package` in (lchannels, Compile)).value
+ (Keys.`package` in (examples, Compile)).value
+ (Keys.`package` in (benchmarks, Compile)).value
+}
+
+lazy val commonSettings = Seq(
+ version := "0.0.2",
+ scalaVersion := "2.11.8",
+ scalacOptions ++= Seq(
+ "-target:jvm-1.8", "-unchecked", "-deprecation", "-feature", "-Ywarn-unused-import"
+ ),
+ // ScalaDoc setup
+ autoAPIMappings := true,
+ scalacOptions in (Compile,doc) ++= Seq(
+ "-no-link-warnings" // Workaround for ScalaDoc @throws links issues
+ )
+)
+
+lazy val lchannels = (project in file("lchannels")).
+ settings(commonSettings: _*).
+ settings(
+ name := "lchannels",
+
+ libraryDependencies ++= Seq(
+ "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.2",
+ "com.typesafe.akka" %% "akka-remote" % "2.4.2"
+ )
+ )
+
+lazy val examples = (project in file("examples")).
+ dependsOn(lchannels).
+ settings(commonSettings: _*).
+ settings(
+ name := "lchannels-examples",
+
+ libraryDependencies ++= Seq(
+ "org.slf4j" % "slf4j-simple" % "1.7.16",
+ "com.typesafe.scala-logging" %% "scala-logging" % "3.1.0"
+ )
+ )
+
+lazy val benchmarks = (project in file("benchmarks")).
+ dependsOn(lchannels).
+ settings(commonSettings: _*).
+ settings(
+ name := "lchannels-benchmarks"
+ // Depending on the benchmark size and duration, you might want
+ // to add the following options:
+ //
+ // fork := true, // Fork a JVM, running inside benchmarks/ dir
+ // javaOptions ++= Seq("-Xms1024m", "-Xmx1024m") // Enlarge heap size
+ )
diff --git a/src/main/jbake/assets/docs/lchannels/examples/lib/akka-typed-experimental_2.11-2.4.2.jar b/src/main/jbake/assets/docs/lchannels/examples/lib/akka-typed-experimental_2.11-2.4.2.jar
new file mode 100644
index 0000000..25fb107
Binary files /dev/null and b/src/main/jbake/assets/docs/lchannels/examples/lib/akka-typed-experimental_2.11-2.4.2.jar differ
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/resources/application.conf b/src/main/jbake/assets/docs/lchannels/examples/src/main/resources/application.conf
new file mode 100644
index 0000000..7ad8e9e
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/resources/application.conf
@@ -0,0 +1,199 @@
+GreetingServerSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31337
+ }
+ }
+ }
+}
+
+GreetingClientSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31338
+ }
+ }
+ }
+}
+
+GameServerSys {
+ akka {
+ loglevel = WARNING
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31340
+ }
+ }
+ }
+}
+
+GameClientASys {
+ akka {
+ loglevel = WARNING
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31341
+ }
+ }
+ }
+}
+
+GameClientBSys {
+ akka {
+ loglevel = WARNING
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31342
+ }
+ }
+ }
+}
+
+GameClientCSys {
+ akka {
+ loglevel = WARNING
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31343
+ }
+ }
+ }
+}
+
+ThreeBuyerSellerSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31350
+ }
+ }
+ }
+}
+
+ThreeBuyerAliceSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31351
+ }
+ }
+ }
+}
+
+ThreeBuyerBobSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31352
+ }
+ }
+ }
+}
+
+ThreeBuyerCarolSys {
+ akka {
+ loglevel = OFF # To preserve readability, when ActorSystems disconnect
+ log-dead-letters-during-shutdown = off
+
+ actor {
+ provider = "akka.remote.RemoteActorRefProvider"
+ warn-about-java-serializer-usage = false
+ }
+
+ remote {
+ enabled-transports = ["akka.remote.netty.tcp"]
+ netty.tcp {
+ hostname = "127.0.0.1"
+ port = 31353
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Calc.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Calc.scala
new file mode 100644
index 0000000..166bb7e
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Calc.scala
@@ -0,0 +1,204 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.calc
+
+import scala.concurrent.{ExecutionContext}
+import scala.concurrent.duration.Duration
+
+import lchannels._
+
+/////////////////////////////////////////////////////////////////////////////
+// Session type:
+// ?Welcome(Str) . rec X . (+) { !Negate(Int) . ?Answer(Int) . X ,
+// !Add(Int) . !Add2(Int) . ?Answer(Int) . X ,
+// !Quit() }
+/////////////////////////////////////////////////////////////////////////////
+sealed case class Welcome(message: String)(val cont: Out[Choice])
+
+sealed abstract class Choice
+case class Negate(value: Integer)(val cont: Out[Answer]) extends Choice
+case class Add(value1: Integer)(val cont: In[Add2]) extends Choice
+case class Quit() extends Choice
+
+sealed case class Add2(value2: Integer)(val cont: Out[Answer])
+
+sealed case class Answer(value: Integer)(val cont: Out[Choice])
+/////////////////////////////////////////////////////////////////////////////
+
+import lchannels.{StreamOut, StreamManager}
+import java.io.{
+ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter,
+ InputStream, OutputStream
+}
+
+class CalcStreamManager(in: InputStream, out: OutputStream)
+ (implicit ec: ExecutionContext)
+ extends StreamManager(in, out) {
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val welcomeR = """WELCOME (.+)""".r
+ private val answerR = """ANSWER (-?\d+)""".r
+
+ override def destreamer() = inb.readLine() match {
+ case welcomeR(msg) => Welcome(msg)(StreamOut[Choice](this))
+ case answerR(n) => Answer(n.toInt)(StreamOut[Choice](this))
+ case unknown => {
+ close()
+ throw new java.net.ProtocolException(f"Unknown message: '${unknown}'")
+ }
+ }
+
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+
+ override def streamer(x: Any) = x match {
+ case Negate(n) => outb.write(f"NEGATE ${n}\n"); outb.flush()
+ case Add(n) => outb.write(f"ADD ${n}\n"); outb.flush()
+ case Add2(n) => outb.write(f"ADD2 ${n}\n"); outb.flush()
+ case Quit() => outb.write("QUIT\n"); outb.flush(); close()
+ }
+}
+
+class CalcSocketManager(socket: java.net.Socket)
+ extends SocketManager(socket) {
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val welcomeR = """WELCOME (.+)""".r
+ private val answerR = """ANSWER (-?\d+)""".r
+
+ override def destreamer() = inb.readLine() match {
+ case welcomeR(msg) => Welcome(msg)(SocketOut[Choice](this))
+ case answerR(n) => Answer(n.toInt)(SocketOut[Choice](this))
+ case unknown => {
+ close()
+ throw new java.net.ProtocolException(f"Unknown message: '${unknown}'")
+ }
+ }
+
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+
+ override def streamer(x: Any) = x match {
+ case Negate(n) => outb.write(f"NEGATE ${n}\n"); outb.flush()
+ case Add(n) => outb.write(f"ADD ${n}\n"); outb.flush()
+ case Add2(n) => outb.write(f"ADD2 ${n}\n"); outb.flush()
+ case Quit() => outb.write("QUIT\n"); outb.flush(); close()
+ }
+}
+
+object Server {
+ def apply(c: Out[Welcome])
+ (implicit timeout: Duration): Unit = {
+ println(f"[S] Sending welcome to ${c}...")
+ val c2 = c !! Welcome("Welcome to SessionCalc 0.1")_
+ subHandler(c2)
+
+ def subHandler(c: In[Choice]) {
+ println("[S] Now waiting for a choice... ")
+ c ? {
+ case Quit() => {
+ println(f"[S] Got Quit(), finishing")
+ }
+ case m @ Negate(value) => {
+ println(f"[S] Got 'Negate(${value})', answering ${-value}")
+ val c2 = m.cont !! Answer(-value)_
+ println("[S] Performing recursive call...")
+ subHandler(c2)
+ }
+ case m @ Add(val1) => {
+ println(f"[S] Got Add(${val1}), waiting for 2nd value...")
+ m.cont.receive match {
+ case m @ Add2(val2) => {
+ println(f"[S] Got Add2(${val2}), answering ${val1 + val2}...")
+ val c3 = m.cont !! Answer(val1 + val2)_
+ println("[S] Performing recursive call...")
+ subHandler(c3)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+object Client {
+ def apply(c: In[Welcome])
+ (implicit timeout: Duration): Unit = {
+ val welcome = c.receive
+ println(f"[C] Got '${welcome.message}'")
+
+ println(f"[C] Sending Negate(42)...")
+ val ans = welcome.cont !! Negate(42)_
+ println("[C] ...done. Now waiting for answer...")
+ val neg = ans.receive
+ println(f"[C] ...done: got ${neg.value}")
+
+ println("[C] Now trying to add 7 and 5...")
+
+ val ans2 = neg.cont !! Add(7)_ !! Add2(5)_
+ println("[C] ...done. Now waiting for answer...")
+ val sum = ans2.receive
+ println(f"[C] ...done: got ${sum.value}")
+
+ println("[C] Now quitting")
+ sum.cont ! Quit()
+ }
+}
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import lchannels.LocalChannel.parallel
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ implicit val timeout = 5.seconds
+
+ println("[*] Spawning local server and client...")
+ val (s, c) = parallel[Welcome, Unit, Unit](
+ Client(_), Server(_)
+ )
+
+ Await.result(s, 10.seconds) // Wait for server termination
+}
+
+object Queue extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import lchannels.QueueChannel.parallel
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ implicit val timeout = 5.seconds
+
+ println("[*] Spawning local server and client (using queue-based channels)...")
+ val (s, c) = parallel[Welcome, Unit, Unit](
+ Client(_), Server(_)
+ )
+
+ Await.result(s, 10.seconds) // Wait for server termination
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Greeting.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Greeting.scala
new file mode 100644
index 0000000..2285d5c
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/Greeting.scala
@@ -0,0 +1,288 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.greeting
+
+import scala.concurrent.{blocking, ExecutionContext, Future}
+import scala.concurrent.duration.{Duration, DurationInt}
+import lchannels._
+
+/////////////////////////////////////////////////////////////////////////////
+// Session type:
+// rec X . (+) { !Greet(String) . & { ?Hello(String) . X , ?Bye(String) } ,
+// !Quit() }
+/////////////////////////////////////////////////////////////////////////////
+sealed abstract class Start
+case class Greet(whom: String)(val cont: Out[Greeting]) extends Start
+case class Quit() extends Start
+
+sealed abstract class Greeting
+case class Hello(who:String)(val cont: Out[Start]) extends Greeting
+case class Bye(who:String) extends Greeting
+/////////////////////////////////////////////////////////////////////////////
+
+/** Greeting protocol server. */
+object Server {
+ def apply(c: In[Start])
+ (implicit timeout: Duration): Unit = {
+ println(f"[S] Awaiting request from ${c}...")
+ c ? {
+ case m @ Greet(whom) => {
+ println(f"[S] Got 'Greet(${whom})', answering Hello")
+ val c2in = m.cont !! Hello(whom)_
+ println("[S] Performing recursion...")
+ apply(c2in)
+ }
+ case Quit() => {
+ println(f"[S] Got Quit(), finishing")
+ }
+ }
+ }
+
+ def serve(factory: () => (In[Start], Out[Start]))
+ (implicit ctx: ExecutionContext,
+ timeout: Duration): Out[Start] = {
+ val (cin, cout) = factory()
+ Future { blocking { apply(cin)(timeout) } }
+ cout
+ }
+ def serve()(implicit ctx: ExecutionContext,
+ timeout: Duration): Out[Start] = {
+ serve(LocalChannel.factory)
+ }
+}
+
+object Client1 {
+ def apply(c: Out[Start])
+ (implicit timeout: Duration): Unit = {
+ println("[C1] Sending Greet(\"Jack\")...")
+ val repc = c !! Greet("Jack")_
+ println("[C1] ...done. Now waiting for answer...")
+ repc ? {
+ case m @ Hello(who) => {
+ println(f"[C1] Received 'Hello(${who})', now quitting...")
+ m.cont ! Quit()
+ println("[C1] ...done.")
+ }
+ case Bye(who) => {
+ println(f"[C1] Received 'Bye(${who})', doing nothing")
+ }
+ }
+ }
+}
+
+object Client2 {
+ def apply(c: Out[Quit])
+ (implicit timeout: Duration): Unit = {
+ println(f"[C2] Sending ${Quit()}")
+ c ! Quit()
+ }
+}
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import lchannels.LocalChannel.parallel
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ implicit val timeout = 10.seconds
+
+ println("[*] Spawning local server and client 1...")
+ val (c1, s1) = parallel[Start, Unit, Unit](
+ Server(_), Client1(_)
+ )
+
+ Await.result(s1, 10.seconds) // Wait for server termination
+
+ println("\n[*] Spawning local server and client 2...")
+ val (c2, s2) = parallel[Quit, Unit, Unit](
+ Server(_), Client2(_)
+ )
+
+ Await.result(s2, 10.seconds) // Wait for server termination
+}
+
+object Queue extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import lchannels.QueueChannel.parallel
+ import scala.concurrent.Await
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ implicit val timeout = 10.seconds
+
+ println("[*] Spawning local server and client 1 (using queue-based channels)...")
+ val (c1, s1) = parallel[Start, Unit, Unit](
+ Server(_), Client1(_)
+ )
+
+ Await.result(s1, 10.seconds) // Wait for server termination
+
+ println("\n[*] Spawning local server and client 2 (using queue-based channels)...")
+ val (c2, s2) = parallel[Quit, Unit, Unit](
+ Server(_), Client2(_)
+ )
+
+ Await.result(s2, 10.seconds) // Wait for server termination
+}
+
+object StreamClient extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import java.io.{
+ InputStream, OutputStream,
+ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter
+ }
+ import java.net.Socket
+
+ import scala.concurrent.ExecutionContext.Implicits.global
+ implicit val timeout = 30.seconds
+
+ class HelloStreamManager(in: InputStream, out: OutputStream)
+ extends StreamManager(in, out) {
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+
+ override def streamer(x: Any) = x match {
+ case Greet(name) => outb.write(f"GREET ${name}\n"); outb.flush()
+ case Quit() => outb.write("QUIT\n"); outb.flush(); close() // End
+ }
+
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val helloR = """HELLO (.+)""".r // Matches Hello(name)
+ private val byeR = """BYE (.+)""".r // Matches Bye(name)
+
+ override def destreamer() = inb.readLine() match {
+ case helloR(name) => Hello(name)(StreamOut[Start](this))
+ case byeR(name) => close(); Bye(name) // Session end: close streams
+ case e => { close(); throw new Exception(f"Bad message: '${e}'") }
+ }
+ }
+
+ println("[*] Connecting to 127.0.0.1:1337...")
+ val conn = new Socket("127.0.0.1", 1337) // Host & port of greeting server
+ val strm = new HelloStreamManager(conn.getInputStream, conn.getOutputStream)
+ val c = StreamOut[Start](strm) // Output endpoint, towards greeting server
+ Client1(c)
+}
+
+object SocketClient extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import java.io.{
+ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter
+ }
+ import java.net.Socket
+
+ implicit val timeout = 30.seconds
+
+ class HelloSocketManager(socket: Socket)
+ extends SocketManager(socket) {
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+
+ override def streamer(x: Any) = x match {
+ case Greet(name) => outb.write(f"GREET ${name}\n"); outb.flush()
+ case Quit() => outb.write("QUIT\n"); outb.flush(); close() // End
+ }
+
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val helloR = """HELLO (.+)""".r // Matches Hello(name)
+ private val byeR = """BYE (.+)""".r // Matches Bye(name)
+
+ override def destreamer() = inb.readLine() match {
+ case helloR(name) => Hello(name)(SocketOut[Start](this))
+ case byeR(name) => close(); Bye(name) // Session end: close streams
+ case e => { close(); throw new Exception(f"Bad message: '${e}'") }
+ }
+ }
+
+ println("[*] Connecting to 127.0.0.1:1337...")
+ val conn = new Socket("127.0.0.1", 1337) // Host & port of greeting server
+ val sktm = new HelloSocketManager(conn)
+ val c = SocketOut[Start](sktm) // Output endpoint, towards greeting server
+ Client1(c)
+}
+
+object ActorServer extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration.Duration
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GreetingServerSys",
+ config = Some(config.getConfig("GreetingServerSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = Duration.Inf
+
+ // We give a human-readable name ("greeting") to the server actor
+ val (in, out) = ActorChannel.factory[Start]("start");
+ println(f"[*] Greeting server listening on: ${out.path}")
+ Server(in)
+
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
+
+object ActorClient extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GreetingClientSys",
+ config = Some(config.getConfig("GreetingClientSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 10.seconds
+
+ val serverPath = "akka.tcp://GreetingServerSys@127.0.0.1:31337/user/start"
+ println(f"[*] Connecting to ${serverPath}...")
+ val c = ActorOut[Start](serverPath)
+ Client1(c)
+
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/ScribbleGreeting.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/ScribbleGreeting.scala
new file mode 100644
index 0000000..c50d084
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/ScribbleGreeting.scala
@@ -0,0 +1,327 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** Implementation of the
+ * [[https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Greeting.scr greeting Scribble protocol]]
+ *
+ * @author Alceste Scalas
+ */
+package lchannels.examples.scribblegreeting
+
+import scala.concurrent.{blocking, ExecutionContext, Future}
+import scala.concurrent.duration.{Duration, DurationInt}
+import lchannels._
+
+/////////////////////////////////////////////////////////////////////////////
+// Global type:
+// μ(X).C->S{Greet(String).S->C{Hello(String).X, Bye(String).end}, Quit(Unit).end}
+/////////////////////////////////////////////////////////////////////////////
+
+/** Server-side types (autogenerated by Scribble). */
+package object S {
+ // Local type for role S:
+ // μ(X).C&{Greet(String).C⊕{Hello(String).X, Bye(String).end}, Quit(Unit).end}
+
+ // Input message types for multiparty sessions
+ sealed abstract class MsgMPGreetOrQuit
+ case class Greet(p: String, cont: MPByeOrHello) extends MsgMPGreetOrQuit
+ case class Quit(p: Unit) extends MsgMPGreetOrQuit
+
+ // Output message types for multiparty sessions
+ case class Hello(p: String)
+ case class Bye(p: String)
+
+ // Multiparty session classes
+ case class MPGreetOrQuit(c: In[binary.GreetOrQuit]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.Greet(p) => {
+ Greet(p, MPByeOrHello(m.cont))
+ }
+ case m @ binary.Quit(p) => {
+ Quit(p)
+ }
+ }
+ }
+ }
+ case class MPByeOrHello(c: Out[binary.ByeOrHello]) {
+ def send(v: Bye) = {
+ val cnt = c ! binary.Bye(v.p)
+ ()
+ }
+ def send(v: Hello) = {
+ val cnt = c !! binary.Hello(v.p)_
+ MPGreetOrQuit(cnt)
+ }
+ }
+}
+
+
+/** Client-side types (autogenerated by Scribble). */
+package object C {
+ // Local type for role C:
+ // μ(X).S⊕{Greet(String).S&{Hello(String).X, Bye(String).end}, Quit(Unit).end}
+
+ // Input message types for multiparty sessions
+ sealed abstract class MsgMPByeOrHello
+ case class Hello(p: String, cont: MPGreetOrQuit) extends MsgMPByeOrHello
+ case class Bye(p: String) extends MsgMPByeOrHello
+
+ // Output message types for multiparty sessions
+ case class Greet(p: String)
+ case class Quit(p: Unit)
+
+ // Multiparty session classes
+ case class MPGreetOrQuit(S: Out[binary.GreetOrQuit]) {
+ def send(v: Greet) = {
+ val cnt = S !! binary.Greet(v.p)_
+ MPByeOrHello(cnt)
+ }
+ def send(v: Quit) = {
+ val cnt = S ! binary.Quit(v.p)
+ ()
+ }
+ }
+ case class MPByeOrHello(S: In[binary.ByeOrHello]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ S.receive(timeout) match {
+ case m @ binary.Bye(p) => {
+ Bye(p)
+ }
+ case m @ binary.Hello(p) => {
+ Hello(p, MPGreetOrQuit(m.cont))
+ }
+ }
+ }
+ }
+}
+
+/** Classes representing messages (with continuations) in binary sessions
+ * (autogenerated by Scribble).
+ */
+package object binary {
+ sealed abstract class GreetOrQuit
+ case class Greet(p: String)(val cont: Out[ByeOrHello]) extends GreetOrQuit
+ case class Quit(p: Unit) extends GreetOrQuit
+
+ sealed abstract class ByeOrHello
+ case class Bye(p: String) extends ByeOrHello
+ case class Hello(p: String)(val cont: Out[GreetOrQuit]) extends ByeOrHello
+}
+
+/** Greeting protocol server implementation. */
+object Server {
+ import S._
+
+ def apply(c: MPGreetOrQuit)
+ (implicit timeout: Duration): Unit = {
+ println(f"[S] Awaiting request from ${c}...")
+ c.receive match {
+ case Greet(whom, cont) => {
+ println(f"[S] Got 'Greet(${whom})', answering Hello")
+ val c2in = cont.send(Hello(whom))
+ println("[S] Performing recursion...")
+ apply(c2in)
+ }
+ case Quit(()) => {
+ println(f"[S] Got Quit(), finishing")
+ }
+ }
+ }
+
+ def serve(factory: () => (In[binary.GreetOrQuit], Out[binary.GreetOrQuit]))
+ (implicit ctx: ExecutionContext,
+ timeout: Duration): Out[binary.GreetOrQuit] = {
+ val (cin, cout) = factory()
+ val session = MPGreetOrQuit(cin) // Wrap binary chan in session object
+ Future { blocking { apply(session)(timeout) } }
+ cout
+ }
+ def serve()(implicit ctx: ExecutionContext,
+ timeout: Duration): Out[binary.GreetOrQuit] = {
+ serve(LocalChannel.factory)
+ }
+}
+
+/** Greeting protocol client implementation (no. 1). */
+object Client1 {
+ import C._
+
+ def apply(c: MPGreetOrQuit)
+ (implicit timeout: Duration): Unit = {
+ println("[C1] Sending Greet(\"Jack\")...")
+ val repc = c.send(Greet("Jack"))
+ println("[C1] ...done. Now waiting for answer...")
+ repc.receive match {
+ case Hello(who, cont) => {
+ println(f"[C1] Received 'Hello(${who})', now quitting...")
+ cont.send(Quit(()))
+ println("[C1] ...done.")
+ }
+ case Bye(who) => {
+ println(f"[C1] Received 'Bye(${who})', doing nothing")
+ }
+ }
+ }
+}
+
+/** Greeting protocol client implementation (no. 2). */
+object Client2 {
+ import C._
+
+ def apply(c: MPGreetOrQuit)
+ (implicit timeout: Duration): Unit = {
+ println(f"[C2] Sending ${Quit(())}")
+ c.send(Quit(()))
+ }
+}
+
+/** Client-server demo using local channels */
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.{Await, Future}
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import scala.concurrent.duration._
+
+ implicit val timeout = 10.seconds
+
+ // Create channel for client/server interaction...
+ val (in1, out1) = LocalChannel.factory[binary.GreetOrQuit]
+ // ...and spawn server and client (note: we wrap channels in sesison objs)
+ println("[*] Spawning server and client 1...")
+ val s1 = Future { Server(S.MPGreetOrQuit(in1)) }
+ val C1 = Future { Client1(C.MPGreetOrQuit(out1)) }
+
+ Await.result(s1, 10.seconds) // Wait for server termination
+
+ // Create channel for client/server interaction...
+ val (in2, out2) = LocalChannel.factory[binary.GreetOrQuit]
+ // ...and spawn server and client (note: we wrap channels in sesison objs)
+ println("[*] Spawning server and client 2...")
+ val s2 = Future { Server(S.MPGreetOrQuit(in2)) }
+ val c2 = Future { Client2(C.MPGreetOrQuit(out2)) }
+
+ Await.result(s2, 10.seconds) // Wait for server termination
+}
+
+/** Client demo, interacing through a textual protocol over sockets */
+object SocketClient extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import java.io.{
+ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter
+ }
+ import java.net.Socket
+
+ implicit val timeout = 30.seconds
+
+ class HelloSocketManager(socket: Socket)
+ extends SocketManager(socket) {
+ import binary._
+
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+
+ override def streamer(x: Any) = x match {
+ case Greet(name) => outb.write(f"GREET ${name}\n"); outb.flush()
+ case Quit(()) => outb.write("QUIT\n"); outb.flush(); close() // End
+ }
+
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val helloR = """HELLO (.+)""".r // Matches Hello(name)
+ private val byeR = """BYE (.+)""".r // Matches Bye(name)
+
+ override def destreamer() = inb.readLine() match {
+ case helloR(name) => Hello(name)(SocketOut[GreetOrQuit](this))
+ case byeR(name) => close(); Bye(name) // Session end: close streams
+ case e => { close(); throw new Exception(f"Bad message: '${e}'") }
+ }
+ }
+
+ println("[*] Connecting to 127.0.0.1:1337...")
+ val conn = new Socket("127.0.0.1", 1337) // Host & port of greeting server
+ val sktm = new HelloSocketManager(conn)
+ val c = SocketOut[binary.GreetOrQuit](sktm) // Output endpoint, towards greeting server
+ Client1(C.MPGreetOrQuit(c))
+}
+
+/** Server demo, using actor-based channels */
+object ActorServer extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration.Duration
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GreetingServerSys",
+ config = Some(config.getConfig("GreetingServerSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = Duration.Inf
+
+ // We give a human-readable name ("greeting") to the server actor
+ val (in, out) = ActorChannel.factory[binary.GreetOrQuit]("start");
+ println(f"[*] Greeting server listening on: ${out.path}")
+ Server(S.MPGreetOrQuit(in))
+
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
+
+/** Client demo, using actor-based channels */
+object ActorClient extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GreetingClientSys",
+ config = Some(config.getConfig("GreetingClientSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 10.seconds
+
+ val serverPath = "akka.tcp://GreetingServerSys@127.0.0.1:31337/user/start"
+ println(f"[*] Connecting to ${serverPath}...")
+ val c = ActorOut[binary.GreetOrQuit](serverPath)
+ Client1(C.MPGreetOrQuit(c))
+
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/AuthServer.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/AuthServer.scala
new file mode 100644
index 0000000..27f78b9
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/AuthServer.scala
@@ -0,0 +1,150 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.auth
+
+import lchannels._
+
+import lchannels.examples.chat.protocol.public._
+
+import scala.concurrent.{Channel, ExecutionContext}
+import scala.concurrent.duration.Duration
+import scala.util.{Try, Success, Failure}
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels.examples.chat.protocol.public.auth._
+import lchannels.examples.chat.protocol.internal.auth.{
+ GetAuthentication => IntGetAuth, Authentication => IntAuth
+}
+import lchannels.examples.chat.protocol.internal.session.{
+ CreateSession => IntCreateSession
+}
+
+/** Chat server frontend */
+class AuthServer(chan: In[IntGetAuth], sessionSrv: Out[IntCreateSession],
+ factory: () => (In[Authenticate], Out[Authenticate]))
+ (implicit ec: ExecutionContext, timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // FIFO queue with requests from clients
+ private val requests: Channel[Authenticate] = new Channel()
+
+ private val authenticator = new Authenticator(requests, sessionSrv)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ override def run(): Unit = {
+ logDebug("started")
+ serverLoop(chan)
+ logDebug("quitting authenticator")
+ authenticator.quit()
+ }
+
+ // Server loop for frontend interaction
+ private def serverLoop(chan: In[IntGetAuth])
+ (implicit timeout: Duration): Unit = {
+ try {
+ chan ? { req =>
+ logDebug(f"got ${req}, creating new authentication channels")
+ val (in, out) = factory()
+ in.future.onComplete(queueRequest) // Async. add request to FIFO
+ logDebug("sending authentication channel")
+ serverLoop(req.cont !! IntAuth(out)_)
+ }
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving main loop")
+ }
+ }
+ }
+
+ private def queueRequest(req: Try[Authenticate]): Unit = req match {
+ case Success(r) => {
+ logDebug(f"queueing ${r}")
+ requests.write(r)
+ }
+ case Failure(e) => logDebug("got failure, not enqueuing")
+ }
+}
+
+private class Authenticator(requests: Channel[Authenticate],
+ sessionSrv: Out[IntCreateSession])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ private val accounts = List(
+ ("alice", "password"), ("bob", "password"), ("carol", "password")
+ )
+
+ override def run() = {
+ logDebug("started")
+ serverLoop(sessionSrv)
+ }
+
+ def serverLoop(sessionSrv: Out[IntCreateSession]): Unit = {
+ try {
+ serverLoop(serve(requests.read, sessionSrv))
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving main loop")
+ }
+ }
+ }
+
+ private def serve(req: Authenticate,
+ sessionSrv: Out[IntCreateSession])
+ (implicit timeout: Duration): Out[IntCreateSession] = {
+ if (accounts.contains((req.username, req.password))) {
+ logDebug(f"login successful for ${req.username}, getting new session")
+ (sessionSrv !! IntCreateSession(req.username)_) ? { newSess =>
+ logDebug(f"forwarding new session")
+ req.cont ! auth.Success(newSess.channel)
+ newSess.cont
+ }
+ } else {
+ logDebug(f"login failed for ${req.username}")
+ req.cont ! auth.Failure()
+ sessionSrv
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/ChatServer.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/ChatServer.scala
new file mode 100644
index 0000000..4876ef5
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/ChatServer.scala
@@ -0,0 +1,538 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.server
+
+import lchannels._
+
+import scala.collection.mutable.{Map => MMap}
+import scala.concurrent.{Channel, ExecutionContext}
+import scala.concurrent.duration.Duration
+import scala.util.{Try, Success, Failure}
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels.examples.chat.protocol.public._
+import lchannels.examples.chat.protocol.internal.session.{
+ GetSession => IntGetSession, GetSessionResult => IntGetSessionResult,
+ Success => IntSuccess, Failure => IntFailure,
+ CreateSession => IntCreateSession, NewSession => IntNewSession
+}
+
+object ChatServer {
+ import lchannels.examples.chat.frontend.Frontend
+ import lchannels.examples.chat.auth.AuthServer
+ import lchannels.examples.chat.protocol.internal.auth.{
+ GetAuthentication => IntGetAuth
+ }
+ def apply(isfactory: () => (In[IntGetSession], Out[IntGetSession]),
+ ffactory: () => (In[GetSession], Out[GetSession]),
+ iafactory: () => (In[IntGetAuth], Out[IntGetAuth]),
+ afactory: () => (In[auth.Authenticate], Out[auth.Authenticate]),
+ lfactory: () => (In[IntCreateSession], Out[IntCreateSession]),
+ sfactory: () => (In[session.Command], Out[session.Command]),
+ rfactory: () => (In[room.Messages], Out[room.Messages]),
+ cfactory: () => (In[roomctl.Control], Out[roomctl.Control]))
+ (implicit ec: ExecutionContext, timeout: Duration): Frontend = {
+ val (isin, isout) = isfactory() // Frontend - chat server channels
+ val (ain, aout) = iafactory() // Frontend - auth server channels
+ val (lin, lout) = lfactory() // Auth - chat server channels
+
+ val chat = new ChatServer(isin, lin, sfactory, rfactory, cfactory)
+ val auth = new AuthServer(ain, lout, afactory)
+
+ class OverFrontend(isout: Out[IntGetSession], aout: Out[IntGetAuth],
+ ffactory: () => (In[GetSession], Out[GetSession]))
+ extends Frontend(isout, aout, ffactory) {
+ override def quit() = {
+ super.quit()
+ chat.quit()
+ auth.quit()
+ }
+ }
+
+ new OverFrontend(isout, aout, ffactory)
+ }
+
+ def apply()(implicit ec: ExecutionContext, timeout: Duration): Frontend = {
+ apply(LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory,
+ LocalChannel.factory)
+ }
+}
+
+protected case class RoomSubscription(id: Int, // Random
+ msgc: Out[room.Messages])
+protected case class Session(username: String,
+ chan: In[session.Command],
+ rooms: MMap[String, // Room joined by username
+ RoomSubscription])
+
+private case class SessionCommand(id: Int, cmd: session.Command)
+
+/** Chat server frontend */
+class ChatServer(frontend: In[IntGetSession],
+ authSrv: In[IntCreateSession],
+ sfactory: () => (In[session.Command], Out[session.Command]),
+ rfactory: () => (In[room.Messages], Out[room.Messages]),
+ cfactory: () => (In[roomctl.Control], Out[roomctl.Control]))
+ (implicit ec: ExecutionContext, timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // Server name, used for system messages
+ protected[server] val serverName = "ChatServer"
+
+ // Active sessions: id -> session_info
+ type SessionsMap = MMap[Int, Session]
+ private var sessions: SessionsMap = MMap()
+
+ private val requests: Channel[SessionCommand] = new Channel()
+
+ private val sessionHandler = new SessionHandler(this, requests,
+ rfactory, cfactory)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = {
+ thread.interrupt()
+ createSessionThread.interrupt()
+ }
+
+ // Internal session handling thread
+ private val createSessionThread = {
+ val t = new Thread {
+ override def run {
+ logDebug("running createSessionLoop")
+ createSessionLoop(authSrv)
+ }
+ }
+ t.start
+ t
+ }
+
+ override def run(): Unit = {
+ logDebug("started")
+ frontendLoop(frontend)
+ logDebug("terminating session handler thread")
+ sessionHandler.quit()
+ }
+
+ // NOTE: frontendLoop is not tail-recursive --- left as is for simplicity
+ private def frontendLoop(frontend: In[IntGetSession])
+ (implicit timeout: Duration): Unit = {
+ try {
+ frontend ? { req =>
+ logDebug(f"got ${req}")
+ // Will be initialized later
+ var res: Out[IntGetSession] => IntGetSessionResult = null
+
+ sessions.synchronized {
+ if (sessions.keySet.contains(req.id)) {
+ logDebug(f"active session found, recovering")
+ val sessc = recoverSession(req.id)
+ logDebug(f"answering with active session")
+ res = IntSuccess(sessc)_
+ } else {
+ logDebug(f"no active session found")
+ res = IntFailure()
+ }
+ }
+
+ frontendLoop(req.cont !! res)
+ }
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving frontend loop")
+ }
+ }
+ }
+
+ private val rnd = new scala.util.Random()
+
+ private def createSessionLoop(authSrv: In[IntCreateSession])
+ (implicit timeout: Duration): Unit = {
+ try {
+ authSrv ? { req =>
+ logDebug(f"got ${req}, preparing new session")
+ // Will be initialized later
+ var res: Out[IntCreateSession] => IntNewSession = null
+
+ sessions.synchronized {
+ // Recover old sessions owned by the same user
+ val cleanedChans = {
+ for (id <- sessions.keys if (sessions(id).username == req.username))
+ yield recoverSession(id)
+ }
+ assert(cleanedChans.size <= 1) // At most 1 session should be recovered
+ if (cleanedChans == 1) {
+ // Some session was cleaned and replaced, let's use the channel
+ res = IntNewSession(cleanedChans.iterator.next())_
+ } else {
+ // No session was cleaned, let's start a new one from scratch
+ val outc = createSession(req.username)
+ res = IntNewSession(outc)_
+ }
+ }
+ logDebug(f"sending new session ${res}")
+ createSessionLoop(req.cont !! res)
+ }
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving session creation loop")
+ }
+ }
+ }
+
+ private def createSession(username: String): Out[session.Command] = {
+ val id = allocUniqueSessionId()
+ val (in, out) = sfactory()
+ // Async. add request to FIFO
+ in.future.onComplete {
+ case Success(cmd) => queueRequest(Success(SessionCommand(id, cmd)))
+ case Failure(e) => queueRequest(Failure(e))
+ }
+ // Add the new session to the list of known sessions
+ sessions(id) = Session(username, in,
+ MMap[String, RoomSubscription]())
+ out
+ }
+
+ protected[server] def queueRequest(req: Try[SessionCommand]): Unit = req match {
+ case Success(r) => {
+ logDebug(f"queueing ${r}")
+ requests.write(r)
+ }
+ case Failure(e) => logDebug("got failure, not enqueuing")
+ }
+
+ // Clean the given *existing* session, assigning its content to a new id.
+ // Return the new output channel
+ private def recoverSession(id: Int): Out[session.Command] = {
+ // NOTE: for simplicity, we just delete and recreate the session
+ val username = sessions(id).username
+ deleteSession(id)
+ createSession(username)
+ }
+
+ protected[server] def deleteSession(id: Int) = {
+ sessions.remove(id) match {
+ case Some(session) => {
+ // Close all message channels, before getting rid of them
+ for (room <- session.rooms.keys) {
+ deleteChatRoom(id, room)
+ }
+ }
+ case None => ()
+ }
+ }
+
+ // Throw a NoSuchElementException if the given session id does not exist
+ protected[server] def assertSessionId(id: Int): Unit = {
+ sessions(id)
+ }
+
+ // Throw a NoSuchElementException if session id, chat and subscr. don't exist
+ protected[server] def assertSessionIdAndChat(id: Int, roomName: String,
+ subscrId: Int): Unit = {
+ sessions.synchronized {
+ if (sessions(id).rooms(roomName).id == subscrId) {
+ ()
+ } else {
+ throw new NoSuchElementException()
+ }
+ }
+ }
+
+ private def allocUniqueSessionId(): Int = sessions.synchronized {
+ val id = rnd.nextInt()
+ if (sessions.keySet.contains(id)) {
+ allocUniqueSessionId()
+ } else {
+ sessions(id) = null // FIXME: kludge: "lock" the ID with a temp entry
+ id
+ }
+ }
+
+ // Add a chat room subscription to the given session.
+ // Return the subscription id
+ protected[server] def addChatRoom(id: Int, rname: String,
+ msgc: Out[room.Messages]): Int = {
+ val subscrId = rnd.nextInt()
+ sessions.synchronized {
+ sessions(id).rooms(rname) = RoomSubscription(subscrId, msgc)
+ }
+ subscrId
+ }
+
+ // Remove the given chat room from the given session
+ protected[server] def deleteChatRoom(id: Int,
+ rname: String) = sessions.synchronized {
+ val rSub = sessions(id).rooms.remove(rname).get // Session+room must exist
+ rSub.msgc ! room.Quit()
+ }
+
+ // Return the username owning the given (existing) session id
+ protected[server] def getUsername(id: Int): String = {
+ sessions(id).username
+ }
+
+ // Dispatch a message to all users of the given chatroom
+ protected[server] def dispatchMessage(rName: String, username: String,
+ text: String): Unit = {
+ sessions.synchronized {
+ val subscrS = for ((_, s) <- sessions if s.rooms.keySet.contains(rName))
+ yield s
+ for (subS <- subscrS) {
+ val subC = subS.rooms(rName)
+ // Dispatch the message to the subscribed room channel...
+ val msgc2 = subC.msgc !! room.NewMessage(username, text)_
+ // ... and update the subscription with the continuation
+ subS.rooms(rName) = RoomSubscription(subC.id, msgc2)
+ }
+ }
+ }
+}
+
+private case class RoomCtlRequest(id: Int, // Session id
+ room: String,
+ subscrId: Int, // Room subscription id
+ cmd: roomctl.Control)
+
+private class SessionHandler(chatServer: ChatServer,
+ requests: Channel[SessionCommand],
+ rfactory: () => (In[room.Messages],
+ Out[room.Messages]),
+ cfactory: () => (In[roomctl.Control],
+ Out[roomctl.Control]))
+ (implicit ec: ExecutionContext,
+ timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // Server name, used for system messages
+ protected[server] val serverName = chatServer.serverName
+
+ private val roomRequests: Channel[RoomCtlRequest] = new Channel()
+
+ private val roomHandler = new RoomHandler(this, roomRequests)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ override def run() = {
+ logDebug("started")
+ serverLoop()
+ logDebug("quitting room handler")
+ roomHandler.quit()
+ }
+
+ def serverLoop(): Unit = {
+ try {
+ val req = requests.read
+ logDebug(f"dequeued ${req}")
+ try {
+ chatServer.assertSessionId(req.id)
+ req.cmd match {
+ case session.Quit() => {
+ chatServer.deleteSession(req.id)
+ serverLoop()
+ }
+ case m @ session.Ping(msg) => {
+ val c = m.cont !! session.Pong(msg)_
+ reschedule(req.id, c)
+ }
+ case m @ session.GetId() => {
+ val c = m.cont !! session.Id(req.id)_
+ reschedule(req.id, c)
+ }
+ case m @ session.Join(room) => {
+ val (rin, cout) = joinRoom(req.id, room)
+ val c = m.cont !! session.ChatRoom(rin, cout)_
+ chatServer.dispatchMessage(room, chatServer.serverName,
+ f"${chatServer.getUsername(req.id)} joined ${room}")
+ reschedule(req.id, c)
+ }
+ }
+ } catch {
+ case e: NoSuchElementException => {
+ // We tried to operate on a non-existing session:
+ // let's loop without rescheduling (i.e., we ignore the channel)
+ serverLoop()
+ }
+ }
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving session handler loop")
+ }
+ }
+
+ def reschedule(id: Int, c: In[session.Command]): Unit = {
+ // Async. add next control message to FIFO, and then loop
+ c.future.onComplete {
+ case Success(cmd) => {
+ chatServer.queueRequest(Success(SessionCommand(id, cmd)))
+ }
+ case Failure(e) => chatServer.queueRequest(Failure(e))
+ }
+ serverLoop()
+ }
+ }
+
+ private def joinRoom(id: Int, rname: String): (In[room.Messages],
+ Out[roomctl.Control]) = {
+ val (rin, rout) = rfactory()
+ val (cin, cout) = cfactory()
+
+ val subscrId = chatServer.addChatRoom(id, rname, rout)
+
+ // Async. add control messages to FIFO
+ cin.future.onComplete {
+ case Success(ctl) => {
+ queueRoomCtlRequest(Success(RoomCtlRequest(id, rname, subscrId, ctl)))
+ }
+ case Failure(e) => queueRoomCtlRequest(Failure(e))
+ }
+ (rin, cout)
+ }
+
+ protected[server] def queueRoomCtlRequest(req: Try[RoomCtlRequest]): Unit = {
+ req match {
+ case Success(r) => {
+ logDebug(f"queueing ${r}")
+ roomRequests.write(r)
+ }
+ case Failure(e) => logDebug("got failure, not enqueuing")
+ }
+ }
+
+ // Throw a NoSuchElementException if session id, chat and subscr. don't exist
+ protected[server] def assertSessionIdAndChat(id: Int, roomName: String,
+ subscrId: Int): Unit = {
+ chatServer.assertSessionIdAndChat(id, roomName, subscrId)
+ }
+
+ // Dispatch a message to all users of the given chatroom
+ protected[server] def dispatchMessage(room: String, username: String,
+ text: String): Unit = {
+ chatServer.dispatchMessage(room, username, text)
+ }
+
+ // Return the username owning the given (existing) session id
+ protected[server] def getUsername(id: Int): String = {
+ chatServer.getUsername(id)
+ }
+
+ // Remove the given chat room from the given session
+ protected[server] def deleteChatRoom(id: Int, rname: String) {
+ chatServer.deleteChatRoom(id, rname)
+ }
+}
+
+private class RoomHandler(sessionHandler: SessionHandler,
+ requests: Channel[RoomCtlRequest])
+ (implicit ec: ExecutionContext,
+ timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ override def run() = {
+ logDebug("started")
+ serverLoop()
+ }
+
+ private def serverLoop(): Unit = {
+ try {
+ requests.read match { case RoomCtlRequest(sId, rname, subscrId, ctl) =>
+ try {
+ sessionHandler.assertSessionIdAndChat(sId, rname, subscrId)
+ ctl match {
+ case roomctl.Quit() => {
+ sessionHandler.deleteChatRoom(sId, rname)
+ sessionHandler.dispatchMessage(rname, sessionHandler.serverName,
+ f"${sessionHandler.getUsername(sId)} left ${rname}")
+ serverLoop()
+ }
+ case m @ roomctl.Ping(msg) => {
+ val c = m.cont !! roomctl.Pong(msg)_
+ reschedule(sId, rname, subscrId, c)
+ }
+ case m @ roomctl.SendMessage(text) => {
+ val username = sessionHandler.getUsername(sId)
+ sessionHandler.dispatchMessage(rname, username, text)
+ reschedule(sId, rname, subscrId, m.cont)
+ }
+ }
+ } catch {
+ case e: NoSuchElementException => {
+ // We tried to operate on a non-existing session or chat subscr.:
+ // let's loop without rescheduling (i.e., we ignore the channel)
+ serverLoop()
+ }
+ }
+ }
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving room handling loop")
+ }
+ }
+
+ def reschedule(sId: Int, roomName: String, subscrId: Int,
+ c: In[roomctl.Control]): Unit = {
+ // Async. add next request to FIFO, and then loop
+ c.future.onComplete {
+ case Success(ctl) => {
+ sessionHandler.queueRoomCtlRequest(
+ Success(RoomCtlRequest(sId, roomName, subscrId, ctl)))
+ }
+ case Failure(e) => sessionHandler.queueRoomCtlRequest(Failure(e))
+ }
+ serverLoop()
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Client.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Client.scala
new file mode 100644
index 0000000..7f67fe1
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Client.scala
@@ -0,0 +1,132 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.demo
+
+import scala.concurrent.duration.Duration
+
+import lchannels._
+import lchannels.examples.chat.protocol.public._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Client(frontend: Out[GetSession], val spec: ClientSpec)
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ import scala.concurrent.duration._
+
+ private def logTrace(msg: String) = logger.trace(f"${spec.username}: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${spec.username}: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"${spec.username}: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${spec.username}: ${msg}")
+ private def logError(msg: String) = logger.error(f"${spec.username}: ${msg}")
+
+ private val roomName = "TestRoom"
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ logInfo("started, getting session")
+ (frontend !! GetSession(0)_) ? { // Session 0 should not exist
+ case Active(srv) => loggedIn(srv)
+ case New(authSrv) => authenticate(authSrv)
+ }
+ logInfo("terminating")
+ }
+
+ private def loggedIn(srv: Out[session.Command]): Unit = {
+ (srv !! session. Join(roomName)_) ? { case session.ChatRoom(msgc, ctl) =>
+ logInfo(f"joined chatroom ${roomName}")
+ chat(msgc, ctl, spec.msgCount)
+ }
+ }
+
+ private def chat(msgc: In[room.Messages],
+ ctlc: Out[roomctl.Control], msgCount: Int): Unit = {
+ if (msgCount <= 0) {
+ logInfo(f"leaving chatroom ${roomName}")
+ ctlc ! roomctl.Quit()
+ receiveMessages(msgc, Duration.Inf) // Receive until Quit()
+ } else {
+ val text = f"This is message no. ${(spec.msgCount - msgCount) + 1}"
+ logInfo(f"sending message: '${text}'")
+ val ctlc2 = ctlc !! roomctl.SendMessage(text)_
+ receiveMessages(msgc, spec.msgDelay) match {
+ case Some(msgc2) => chat(msgc2, ctlc2, msgCount - 1)
+ case None => {
+ logInfo(f"leaving chatroom ${roomName}")
+ ctlc2 ! roomctl.Quit()
+ }
+ }
+ }
+ }
+
+ private def receiveMessages(msgc: In[room.Messages],
+ maxWait: Duration): Option[In[room.Messages]] = {
+ logDebug({
+ val wait = if (maxWait.isFinite) f"${maxWait.toMillis/1000.0}" else "Inf"
+ f"waiting for messages (maxWait: ${wait} secs)"
+ })
+ val tStart = System.nanoTime()
+ try {
+ msgc.receive(maxWait) match {
+ case room.Quit() => {
+ logInfo("received Quit() from message channel")
+ None
+ }
+ case m @ room.Ping(msg) => {
+ logInfo(f"received Ping(${msg})")
+ val msgc2 = m.cont !! room.Pong(msg)_
+ val elapsed = System.nanoTime - tStart
+ receiveMessages(msgc2, maxWait - Duration.fromNanos(elapsed))
+ }
+ case m @ room.NewMessage(username, text) => {
+ logInfo(f"received message: ${username} says '${text}'")
+ val elapsed = System.nanoTime - tStart
+ receiveMessages(m.cont, maxWait - Duration.fromNanos(elapsed))
+ }
+ }
+ } catch {
+ case e: scala.concurrent.TimeoutException => {
+ logDebug("stopped waiting for messages")
+ Some(msgc) // We keep receiving on the same channel
+ }
+ }
+ }
+
+ private def authenticate(srv: Out[auth.Authenticate]): Unit = {
+ logInfo("authenticating")
+ (srv !! auth.Authenticate(spec.username, spec.password)_) ? {
+ case auth.Failure() => logError("authentication failed")
+ case auth.Success(srv) => {
+ logInfo("authentication successful")
+ loggedIn(srv)
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Demo.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Demo.scala
new file mode 100644
index 0000000..1cfa249
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Demo.scala
@@ -0,0 +1,65 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.demo
+
+import scala.concurrent.duration.Duration
+
+protected case class ClientSpec(username: String, password: String,
+ msgDelay: Duration, msgCount: Int)
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import lchannels.examples.chat.server.ChatServer
+ import scala.concurrent.ExecutionContext.Implicits.global
+
+ val timeout = 30.seconds
+
+ val clientSpecs = List(
+ ClientSpec("alice", "password", 1.seconds, 10),
+ ClientSpec("bob", "password", 2.seconds, 8),
+ ClientSpec("carol", "password", 3.seconds, 6)
+ )
+
+ val frontend = ChatServer()(global, timeout)
+
+ println("***Spawning clients...")
+ val clients = for (cs <- clientSpecs) yield {
+ new Client(frontend.connect(), cs)(timeout)
+ }
+
+ println("*** Waiting for clients to terminate...")
+ for (c <- clients) {
+ c.join();
+ println(f" **** ${c.spec.username} has terminated")
+ }
+ println("*** All clients have terminated. Now closing the frontend...")
+ frontend.quit()
+ println("*** ...done.")
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Frontend.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Frontend.scala
new file mode 100644
index 0000000..50e7906
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Frontend.scala
@@ -0,0 +1,132 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.frontend
+
+import lchannels._
+
+import scala.annotation.tailrec
+import scala.concurrent.ExecutionContext
+import scala.concurrent.duration.Duration
+import scala.util.{Try, Success, Failure}
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels.examples.chat.protocol.public._
+import lchannels.examples.chat.protocol.internal.session.{
+ GetSession => IntGetSession, Success => IntSuccess, Failure => IntFailure
+}
+import lchannels.examples.chat.protocol.internal.auth.{
+ GetAuthentication => IntGetAuth
+}
+
+/** Chat server frontend */
+class Frontend(sessionSrv: Out[IntGetSession],
+ authSrv: Out[IntGetAuth],
+ factory: () => (In[GetSession], Out[GetSession]))
+ (implicit ec: ExecutionContext,
+ timeout: Duration)
+ extends Runnable with StrictLogging {
+ import scala.concurrent.Channel
+
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // FIFO queue with requests from clients
+ private val requests: Channel[GetSession] = new Channel()
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ /** Return a channel for interacting with the frontend. */
+ def connect(): Out[GetSession] = {
+ logDebug("new client connection")
+ val (in, out) = factory()
+ in.future.onComplete(queueRequest) // Async. add request to FIFO
+ out
+ }
+
+ private def queueRequest(req: Try[GetSession]): Unit = req match {
+ case Success(r) => {
+ logDebug(f"queueing ${r}")
+ requests.write(r)
+ }
+ case Failure(e) => logDebug("got failure, not enqueuing")
+ }
+
+ override def run(): Unit = {
+ logDebug("started")
+ serverLoop(sessionSrv, authSrv)
+ logDebug("quitting")
+ }
+
+ @tailrec
+ private def serverLoop(sessionSrv: Out[IntGetSession],
+ authSrv: Out[IntGetAuth])
+ (implicit timeout: Duration): Unit = {
+ var req: GetSession = null
+
+ try {
+ req = requests.read
+ } catch {
+ case _: InterruptedException => {
+ logDebug("interrupted, leaving main loop")
+ return
+ }
+ }
+
+ logDebug(f"dequeuing ${req}, now serving")
+ val (sessionSrv2, authSrv2) = serve(req, sessionSrv, authSrv)
+ serverLoop(sessionSrv2, authSrv2)
+ }
+
+ private def serve(req: GetSession,
+ sessionSrv: Out[IntGetSession],
+ authSrv: Out[IntGetAuth])
+ (implicit timeout: Duration): (Out[IntGetSession],
+ Out[IntGetAuth]) = {
+ logDebug(f"trying to retrieve active session")
+ (sessionSrv !! IntGetSession(req.id)_) ? {
+ case ssrv @ IntSuccess(sessc) => {
+ logDebug(f"got active session channel, forwarding to client")
+ req.cont ! Active(sessc)
+ (ssrv.cont, authSrv)
+ }
+ case ssrv @ IntFailure() => {
+ logDebug(f"no active session, getting authentication channel")
+ (authSrv !! IntGetAuth()_) ? { auth =>
+ logDebug(f"forwarding authentication channel to client")
+ req.cont ! New(auth.channel)
+ (ssrv.cont, auth.cont)
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Internal.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Internal.scala
new file mode 100644
index 0000000..1e39983
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Internal.scala
@@ -0,0 +1,72 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** Internal definitions, e.g. protocols not intended for client usage
+ *
+ * @author Alceste Scalas
+ */
+package lchannels.examples.chat.protocol.internal
+
+import lchannels._
+
+package session {
+ ////////////////////////////////////////////////////////////////////////////
+ // Type for retrieving existing chat sessions from session server:
+ // rec X . ?GetSession(Int) . (!Session(S_act).X (+) Failure().end)
+ ////////////////////////////////////////////////////////////////////////////
+ import lchannels.examples.chat.protocol.public.session.{
+ Command => PubCommand
+ }
+
+ case class GetSession(id: Int)(val cont: Out[GetSessionResult])
+
+ sealed abstract class GetSessionResult
+ case class Success(channel: Out[PubCommand])
+ (val cont: Out[GetSession]) extends GetSessionResult
+ case class Failure()(val cont: Out[GetSession]) extends GetSessionResult
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Type for creating chat sessions on chat server (used by auth server):
+ // rec X . ?CreateSession(String).!NewSession(S_act).X
+ ////////////////////////////////////////////////////////////////////////////
+ case class CreateSession(username: String)(val cont: Out[NewSession])
+
+ case class NewSession(channel: Out[PubCommand])(val cont: Out[CreateSession])
+}
+
+package auth {
+ ////////////////////////////////////////////////////////////////////////////
+ // Type for getting authentication channels from auth server:
+ // rec X . ?NewAuth() . !Session(S_auth).X
+ ////////////////////////////////////////////////////////////////////////////
+ import lchannels.examples.chat.protocol.public.auth.{
+ Authenticate => PubAuthenticate
+ }
+
+ case class GetAuthentication()(val cont: Out[Authentication])
+
+ case class Authentication(channel: Out[PubAuthenticate])
+ (val cont: Out[GetAuthentication])
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Protocol.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Protocol.scala
new file mode 100644
index 0000000..5412d89
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/chat/Protocol.scala
@@ -0,0 +1,101 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.chat.protocol.public
+
+import lchannels._
+
+///////////////////////////////////////////////////////////////////////////////
+// Session type S_front:
+// rec X . ?GetSession() . (!Active(S_act).end (+) !New(S_auth).end) . X
+///////////////////////////////////////////////////////////////////////////////
+case class GetSession(id: Int)(val cont:Out[GetSessionResult])
+
+sealed abstract class GetSessionResult
+case class Active(chat: Out[session.Command]) extends GetSessionResult
+case class New(authc: Out[auth.Authenticate]) extends GetSessionResult
+
+package session {
+ /////////////////////////////////////////////////////////////////////////////
+ // Session type S_act:
+ // rec X . &( ?GetId().!Id(Int).X,
+ // ?Ping().!Pong().X,
+ // ?Join(String).!ChatRoom(S_cmsgs, S_cctl).X,
+ // ?Quit.end )
+ /////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Command
+ case class GetId()(val cont: Out[Id]) extends session.Command
+ case class Ping(msg: String)(val cont: Out[Pong]) extends session.Command
+ case class Join(room: String)(val cont: Out[ChatRoom]) extends session.Command
+ case class Quit() extends session.Command
+
+ case class Id(id: Int)(val cont: Out[Command])
+
+ case class Pong(msg: String)(val cont: Out[Command])
+
+ case class ChatRoom(msgs: In[room.Messages], ctl: Out[roomctl.Control])
+ (val cont: Out[Command])
+}
+
+package room {
+ /////////////////////////////////////////////////////////////////////////////
+ // Session type S_r:
+ // rec X . !NewMessage(String, String).X (+) !Ping().?Pong().X (+) !Quit()
+ /////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Messages
+ case class NewMessage(username: String, text: String)
+ (val cont: In[Messages]) extends Messages
+ case class Ping(msg: String)(val cont: Out[Pong]) extends Messages
+ case class Quit() extends Messages
+
+ case class Pong(msg: String)(val cont: Out[Messages])
+}
+
+package roomctl {
+ /////////////////////////////////////////////////////////////////////////////
+ // Session type S_rctl:
+ // rec X . !NewMessage(String).X (+) !Ping().?Pong().X (+) !Quit().end
+ /////////////////////////////////////////////////////////////////////////////
+ sealed abstract class Control
+ case class SendMessage(text: String)(val cont: In[Control]) extends Control
+ case class Ping(msg: String)(val cont: Out[Pong]) extends Control
+ case class Quit() extends Control
+
+ case class Pong(msg: String)(val cont: Out[Control])
+}
+
+package auth {
+ /////////////////////////////////////////////////////////////////////////////
+ // Session type S_auth:
+ // ?Authenticate(Credentials) . (!Success(S_act).end (+) !Failure().end)
+ /////////////////////////////////////////////////////////////////////////////
+ case class Authenticate(username: String, password: String)
+ (val cont: Out[AuthenticateResult])
+
+ sealed abstract class AuthenticateResult
+ case class Success(service: Out[session.Command]) extends AuthenticateResult
+ case class Failure() extends AuthenticateResult
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientA.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientA.scala
new file mode 100644
index 0000000..242105d
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientA.scala
@@ -0,0 +1,131 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.game.a
+
+import lchannels._
+import lchannels.examples.game.protocol.binary
+import lchannels.examples.game.protocol.a._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Client(name: String, s: In[binary.PlayA], wait: Duration)
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${name}: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${name}: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"${name}: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${name}: ${msg}")
+ private def logError(msg: String) = logger.error(f"${name}: ${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ val c = MPPlayA(s) // Wrap the channel in a multiparty session obj
+
+ logInfo("Started. Waiting for multiparty session...")
+ val game = c.receive.p
+ logInfo("...done. Waiting for info...")
+ val info = game.receive
+ logInfo(f"...got '${info.p}'. Sending info to B...")
+ val gloop = info.cont.send(InfoAB(info.p + f", ${name}"))
+ logInfo("...done. Starting game loop.")
+ loop(gloop, 1)
+ }
+
+ @scala.annotation.tailrec
+ private def loop(g: MPMov1ABOrMov2AB, loopn: Int): Unit = {
+ logInfo(f"Delay: ${wait}")
+ Thread.sleep(wait.toMillis)
+ logInfo(f"Sending Mov1AB(${loopn}) to B, and waiting C's move")
+ g.send(Mov1AB(loopn)).receive match {
+ case Mov1CA(p, cont) => {
+ logInfo(f"Got Mov1CA(${p}), sending Mov2AB(true)")
+ val g2 = cont.send(Mov2AB(true))
+ g2.receive match {
+ case Mov1CA(p, cont) => {
+ logInfo(f"Got Mov1CA(${p}), looping")
+ loop(cont, loopn+1)
+ }
+ case Mov2CA(p, cont) => {
+ logInfo(f"Got Mov2CA(${p}), looping")
+ loop(cont, loopn+1)
+ }
+ }
+ }
+ case Mov2CA(p, cont) => {
+ logInfo(f"Got Mov1CA(${p}), sending Mov1AB(${loopn+1})")
+ val g2 = cont.send(Mov1AB(loopn+1))
+ g2.receive match {
+ case Mov1CA(p, cont) => {
+ logInfo(f"Got Mov1CA(${p}), looping")
+ loop(cont, loopn+2)
+ }
+ case Mov2CA(p, cont) => {
+ logInfo(f"Got Mov2CA(${p}), looping")
+ loop(cont, loopn+2)
+ }
+ }
+ }
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ import binary.actor.{ConnectA => Connect}
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GameClientASys",
+ config = Some(config.getConfig("GameClientASys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 60.seconds
+
+ val serverPath = "akka.tcp://GameServerSys@127.0.0.1:31340/user/a"
+ println(f"[*] Connecting to ${serverPath}...")
+ val c: Out[Connect] = ActorOut[Connect](serverPath)
+ val c2 = c !! Connect()_
+
+ val client = new Client("Alice", c2, 3.seconds)(30.seconds)
+
+ client.join()
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientB.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientB.scala
new file mode 100644
index 0000000..ca5ba86
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientB.scala
@@ -0,0 +1,111 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.game.b
+
+import lchannels._
+import lchannels.examples.game.protocol.binary
+import lchannels.examples.game.protocol.b._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Client(name: String, s: In[binary.PlayB], wait: Duration)
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${name}: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${name}: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"${name}: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${name}: ${msg}")
+ private def logError(msg: String) = logger.error(f"${name}: ${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ val c = MPPlayB(s) // Wrap the channel in a multiparty session obj
+
+ logInfo("Started. Waiting for multiparty session...")
+ val game = c.receive.p
+ logInfo("...done. Sending name to C, and waiting for A's info...")
+ val info = game.send(InfoBC(name)).receive
+ logInfo(f"...got InfoCA(${info.p}). Starting game loop.")
+ loop(info.cont)
+ }
+
+ @scala.annotation.tailrec
+ private def loop(g: MPMov1ABOrMov2AB): Unit = {
+ logInfo(f"Delay: ${wait}")
+ Thread.sleep(wait.toMillis)
+ logInfo("Waiting for A's move...")
+ g.receive match {
+ case Mov1AB(p, cont) => {
+ logInfo(f"Got Mov1AB(${p}), sending Mov1BC(${p}) and looping")
+ val g2 = cont.send(Mov1BC(p))
+ loop(g2)
+ }
+ case Mov2AB(p, cont) => {
+ logInfo(f"Got Mov2AB(${p}), sending Mov2BC(${p}) and looping")
+ val g2 = cont.send(Mov2BC(p))
+ loop(g2)
+ }
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ import binary.actor.{ConnectB => Connect}
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GameClientBSys",
+ config = Some(config.getConfig("GameClientBSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 60.seconds
+
+ val serverPath = "akka.tcp://GameServerSys@127.0.0.1:31340/user/b"
+ println(f"[*] Connecting to ${serverPath}...")
+ val c: Out[Connect] = ActorOut[Connect](serverPath)
+ val c2 = c !! Connect()_
+
+ val client = new Client("Bob", c2, 2.seconds)(30.seconds)
+
+ client.join()
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientC.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientC.scala
new file mode 100644
index 0000000..c053bb2
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/ClientC.scala
@@ -0,0 +1,112 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.game.c
+
+import lchannels._
+import lchannels.examples.game.protocol.binary
+import lchannels.examples.game.protocol.c._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Client(name: String, s: In[binary.PlayC], wait: Duration)
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${name}: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${name}: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"${name}: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${name}: ${msg}")
+ private def logError(msg: String) = logger.error(f"${name}: ${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ val c = MPPlayC(s) // Wrap the channel in a multiparty session obj
+
+ logInfo("Started. Waiting for multiparty session...")
+ val game = c.receive.p
+ logInfo("...done. Waiting for B's info...")
+ val info = game.receive
+ logInfo(f"...got InfoBC(${info.p}): sending to A, and starting game loop.")
+ val info2 = info.cont.send(InfoCA(f"${info.p}, ${name}"))
+ loop(info2)
+ }
+
+ @scala.annotation.tailrec
+ private def loop(g: MPMov1BCOrMov2BC): Unit = {
+ logInfo(f"Delay: ${wait}")
+ Thread.sleep(wait.toMillis)
+ logInfo("Waiting for B's move...")
+ g.receive match {
+ case Mov1BC(p, cont) => {
+ logInfo(f"Got Mov1BC(${p}), sending Mov1CA(${p}) and looping")
+ val g2 = cont.send(Mov1CA(p))
+ loop(g2)
+ }
+ case Mov2BC(p, cont) => {
+ logInfo(f"Got Mov2BC(${p}), sending Mov2CA(${p}) and looping")
+ val g2 = cont.send(Mov2CA(p))
+ loop(g2)
+ }
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ import binary.actor.{ConnectC => Connect}
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GameClientCSys",
+ config = Some(config.getConfig("GameClientCSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 60.seconds
+
+ val serverPath = "akka.tcp://GameServerSys@127.0.0.1:31340/user/c"
+ println(f"[*] Connecting to ${serverPath}...")
+ val c: Out[Connect] = ActorOut[Connect](serverPath)
+ val c2 = c !! Connect()_
+
+ val client = new Client("Carol", c2, 1.seconds)(30.seconds)
+
+ client.join()
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Demo.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Demo.scala
new file mode 100644
index 0000000..11b6085
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Demo.scala
@@ -0,0 +1,68 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.game.demo
+
+import lchannels._
+import lchannels.examples.game.protocol.binary.{PlayA, PlayB, PlayC}
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ Demo.start(LocalChannel.factory[PlayA],
+ LocalChannel.factory[PlayB],
+ LocalChannel.factory[PlayC])
+}
+
+object Queue extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+ import scala.concurrent.ExecutionContext.Implicits.global
+
+ Demo.start(QueueChannel.factory[PlayA],
+ QueueChannel.factory[PlayB],
+ QueueChannel.factory[PlayC])
+}
+
+object Demo {
+ def start(afactory: () => (In[PlayA], Out[PlayA]),
+ bfactory: () => (In[PlayB], Out[PlayB]),
+ cfactory: () => (In[PlayC], Out[PlayC])) = {
+ import scala.concurrent.duration._
+ implicit val timeout = 10.seconds
+
+ // Client/server channels for players A, B, C
+ val (ca, sa) = afactory()
+ val (cb, sb) = bfactory()
+ val (cc, sc) = cfactory()
+
+ val server = new lchannels.examples.game.server.Server(sa, sb, sc)
+ val a = new lchannels.examples.game.a.Client("Alice", ca, 3.seconds)
+ val b = new lchannels.examples.game.b.Client("Bob", cb, 1.second)
+ val c = new lchannels.examples.game.c.Client("Carol", cc, 2.seconds)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Server.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Server.scala
new file mode 100644
index 0000000..d9b3207
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/Server.scala
@@ -0,0 +1,137 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.game.server
+
+import lchannels._
+import lchannels.examples.game.protocol.binary
+import lchannels.examples.game.protocol.a
+import lchannels.examples.game.protocol.b
+import lchannels.examples.game.protocol.c
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Server(ca: Out[binary.PlayA],
+ cb: Out[binary.PlayB],
+ cc: Out[binary.PlayC])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"Server: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"Server: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"Server: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"Server: ${msg}")
+ private def logError(msg: String) = logger.error(f"Server: ${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ logInfo("Starting. Creating binary channels for multiparty game...")
+
+ // We need to create a pair of channel endpoints for each pair of
+ // connected roles in the multiparty game session. We use the create()
+ // method because:
+ //
+ // (1) ca.create() ensures that the returned channel endpoints use the
+ // same message transport of ca (and similarly for cb and cc);
+ //
+ // (2) we need *both* input/output endpoints, so that later we can
+ // instantiate a.MPInfoCA, b.MPInfoBC and c.MPInfoBC, and send
+ // them to the session participants;
+ //
+ // (3) we do not *want* to use e.g. ca.!!(...), because it would only
+ // return *one* endpoint, and later we would not be able to
+ // instantiate MPInfoCA, b.MPInfoBC, c.MPInfoBC;
+ //
+ // (3) anyway, we *cannot* meaningfully use e.g. ca.!!(...): since
+ // the carried type has no continuation, it is quite difficult to
+ // come up with a "reasonable" value for "..." that is accepted by
+ // the Scala compiler. E.g., the following is accepted, but
+ // is visibly bogus: ca !! ((_:In[Int]) => null)
+ val (abi, abo) = ca.create[binary.InfoAB]
+ val (bci, bco) = cb.create[binary.InfoBC]
+ val (cai, cao) = cc.create[binary.InfoCA]
+
+ // We now instantiate multiparty session objects (i.e., n-uples of
+ // binary linear channels), and send them to our clients via channels
+ // ca, cb, cc.
+ //
+ // Note that the types of the arguments of a.MPInfoCA, bMPInfoBC and
+ // c.MPInfoCA ensure that the channel endpoints above are used in the
+ // correct way: if the wrong channel is used somewhere, the resulting
+ // code does not compile
+ logInfo("...and sending multiparty objects to clients.")
+ ca ! binary.PlayA(a.MPInfoCA(abo, cai))
+ cb ! binary.PlayB(b.MPInfoBC(abi, bco))
+ cc ! binary.PlayC(c.MPInfoBC(cao, bci))
+
+ logInfo("Quitting.")
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("GameServerSys",
+ config = Some(config.getConfig("GameServerSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 120.seconds
+
+ // We give a human-readable name to the connection endpoints
+ val (ai, ao) = ActorChannel.factory[binary.actor.ConnectA]("a");
+ val (bi, bo) = ActorChannel.factory[binary.actor.ConnectB]("b");
+ val (ci, co) = ActorChannel.factory[binary.actor.ConnectC]("c");
+ println(f"[*] Waiting connections on: ${ao.path}, ${bo.path}, ${co.path}")
+
+ val ac = ai.receive
+ println(f"[*] Player A connected")
+ val bc = bi.receive
+ println(f"[*] Player B connected")
+ val cc = ci.receive
+ println(f"[*] Player C connected. Launching server thread...")
+
+ val server = new Server(ac.cont, bc.cont, cc.cont)(30.seconds)
+
+ server.join()
+ println(f"[*] Delaying termination to complete game delegation")
+ Thread.sleep(10000)
+ println(f"[*] Quitting")
+ as.terminate()
+}
\ No newline at end of file
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/Binary.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/Binary.scala
new file mode 100644
index 0000000..4a92d59
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/Binary.scala
@@ -0,0 +1,70 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Binary protocol classes for the multiparty game.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Game.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.game.protocol.binary
+
+import lchannels._
+import lchannels.examples.game.protocol.a
+import lchannels.examples.game.protocol.b
+import lchannels.examples.game.protocol.c
+
+case class InfoAB(p: String)(val cont: In[Mov1ABOrMov2AB])
+
+case class PlayA(p: a.MPInfoCA)
+
+sealed abstract class Mov1ABOrMov2AB
+case class Mov1AB(p: Int)(val cont: In[Mov1ABOrMov2AB]) extends Mov1ABOrMov2AB
+case class Mov2AB(p: Boolean)(val cont: In[Mov1ABOrMov2AB]) extends Mov1ABOrMov2AB
+case class InfoCA(p: String)(val cont: In[Mov1CAOrMov2CA])
+
+sealed abstract class Mov1CAOrMov2CA
+case class Mov1CA(p: Int)(val cont: In[Mov1CAOrMov2CA]) extends Mov1CAOrMov2CA
+case class Mov2CA(p: Boolean)(val cont: In[Mov1CAOrMov2CA]) extends Mov1CAOrMov2CA
+
+case class PlayB(p: b.MPInfoBC)
+
+case class InfoBC(p: String)(val cont: In[Mov1BCOrMov2BC])
+sealed abstract class Mov1BCOrMov2BC
+case class Mov1BC(p: Int)(val cont: In[Mov1BCOrMov2BC]) extends Mov1BCOrMov2BC
+case class Mov2BC(p: Boolean)(val cont: In[Mov1BCOrMov2BC]) extends Mov1BCOrMov2BC
+
+case class PlayC(p: c.MPInfoBC)
+// No more protocol classes needed for player C: they are shared with A and B
+
+/** Binary protocol classes for establishing a client/server connection
+ * (used e.g. in the actor-based demo)
+ */
+package object actor {
+ case class ConnectA()(val cont: Out[PlayA])
+ case class ConnectB()(val cont: Out[PlayB])
+ case class ConnectC()(val cont: Out[PlayC])
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientA.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientA.scala
new file mode 100644
index 0000000..70afbe8
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientA.scala
@@ -0,0 +1,102 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for game player A.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Game.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.game.protocol.a
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.game.protocol.binary
+
+// Input message types for multiparty sessions
+case class PlayA(p: MPInfoCA)
+case class InfoCA(p: String, cont: MPInfoAB)
+
+sealed abstract class MsgMPMov1CAOrMov2CA
+case class Mov1CA(p: Int, cont: MPMov1ABOrMov2AB) extends MsgMPMov1CAOrMov2CA
+case class Mov2CA(p: Boolean, cont: MPMov1ABOrMov2AB) extends MsgMPMov1CAOrMov2CA
+
+// Output message types for multiparty sessions
+case class InfoAB(p: String)
+case class Mov1AB(p: Int)
+case class Mov2AB(p: Boolean)
+
+// Multiparty session classes
+case class MPPlayA(q: In[binary.PlayA]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ q.receive(timeout) match {
+ case m @ binary.PlayA(p) => {
+ PlayA(p)
+ }
+ }
+ }
+}
+
+case class MPInfoCA(b: Out[binary.InfoAB], c: In[binary.InfoCA]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.InfoCA(p) => {
+ InfoCA(p, MPInfoAB(b, m.cont))
+ }
+ }
+ }
+}
+
+case class MPInfoAB(b: Out[binary.InfoAB], c: In[binary.Mov1CAOrMov2CA]) {
+ def send(v: InfoAB) = {
+ val cnt = b !! binary.InfoAB(v.p)_
+ MPMov1ABOrMov2AB(cnt, c)
+ }
+}
+
+case class MPMov1ABOrMov2AB(b: Out[binary.Mov1ABOrMov2AB], c: In[binary.Mov1CAOrMov2CA]) {
+ def send(v: Mov1AB) = {
+ val cnt = b !! binary.Mov1AB(v.p)_
+ MPMov1CAOrMov2CA(cnt, c)
+ }
+ def send(v: Mov2AB) = {
+ val cnt = b !! binary.Mov2AB(v.p)_
+ MPMov1CAOrMov2CA(cnt, c)
+ }
+}
+
+case class MPMov1CAOrMov2CA(b: Out[binary.Mov1ABOrMov2AB], c: In[binary.Mov1CAOrMov2CA]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.Mov1CA(p) => {
+ Mov1CA(p, MPMov1ABOrMov2AB(b, m.cont))
+ }
+ case m @ binary.Mov2CA(p) => {
+ Mov2CA(p, MPMov1ABOrMov2AB(b, m.cont))
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientB.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientB.scala
new file mode 100644
index 0000000..f211f90
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientB.scala
@@ -0,0 +1,99 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for game player B.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Game.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.game.protocol.b
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.game.protocol.binary
+
+// Input message types for multiparty sessions
+case class PlayB(p: MPInfoBC)
+case class InfoAB(p: String, cont: MPMov1ABOrMov2AB)
+sealed abstract class MsgMPMov1ABOrMov2AB
+case class Mov1AB(p: Int, cont: MPMov1BC) extends MsgMPMov1ABOrMov2AB
+case class Mov2AB(p: Boolean, cont: MPMov2BC) extends MsgMPMov1ABOrMov2AB
+
+// Output message types for multiparty sessions
+case class InfoBC(p: String)
+case class Mov1BC(p: Int)
+case class Mov2BC(p: Boolean)
+
+// Multiparty session classes
+case class MPPlayB(q: In[binary.PlayB]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ q.receive(timeout) match {
+ case m @ binary.PlayB(p) => {
+ PlayB(p)
+ }
+ }
+ }
+}
+case class MPInfoBC(a: In[binary.InfoAB], c: Out[binary.InfoBC]) {
+ def send(v: InfoBC) = {
+ val cnt = c !! binary.InfoBC(v.p)_
+ MPInfoAB(a, cnt)
+ }
+}
+case class MPInfoAB(a: In[binary.InfoAB], c: Out[binary.Mov1BCOrMov2BC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ a.receive(timeout) match {
+ case m @ binary.InfoAB(p) => {
+ InfoAB(p, MPMov1ABOrMov2AB(m.cont, c))
+ }
+ }
+ }
+}
+case class MPMov1ABOrMov2AB(a: In[binary.Mov1ABOrMov2AB], c: Out[binary.Mov1BCOrMov2BC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ a.receive(timeout) match {
+ case m @ binary.Mov1AB(p) => {
+ Mov1AB(p, MPMov1BC(m.cont, c))
+ }
+ case m @ binary.Mov2AB(p) => {
+ Mov2AB(p, MPMov2BC(m.cont, c))
+ }
+ }
+ }
+}
+case class MPMov1BC(a: In[binary.Mov1ABOrMov2AB], c: Out[binary.Mov1BCOrMov2BC]) {
+ def send(v: Mov1BC) = {
+ val cnt = c !! binary.Mov1BC(v.p)_
+ MPMov1ABOrMov2AB(a, cnt)
+ }
+}
+case class MPMov2BC(a: In[binary.Mov1ABOrMov2AB], c: Out[binary.Mov1BCOrMov2BC]) {
+ def send(v: Mov2BC) = {
+ val cnt = c !! binary.Mov2BC(v.p)_
+ MPMov1ABOrMov2AB(a, cnt)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientC.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientC.scala
new file mode 100644
index 0000000..7e8ff79
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/game/protocol/ClientC.scala
@@ -0,0 +1,97 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for game player C.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Game.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.game.protocol.c
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.game.protocol.binary
+
+// Input message types for multiparty sessions
+case class PlayC(p: MPInfoBC)
+case class InfoBC(p: String, cont: MPInfoCA)
+sealed abstract class MsgMPMov1BCOrMov2BC
+case class Mov1BC(p: Int, cont: MPMov1CAOrMov2CA) extends MsgMPMov1BCOrMov2BC
+case class Mov2BC(p: Boolean, cont: MPMov1CAOrMov2CA) extends MsgMPMov1BCOrMov2BC
+
+// Output message types for multiparty sessions
+case class InfoCA(p: String)
+case class Mov1CA(p: Int)
+case class Mov2CA(p: Boolean)
+
+// Multiparty session classes
+case class MPPlayC(q: In[binary.PlayC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ q.receive(timeout) match {
+ case m @ binary.PlayC(p) => {
+ PlayC(p)
+ }
+ }
+ }
+}
+case class MPInfoBC(a: Out[binary.InfoCA], b: In[binary.InfoBC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ b.receive(timeout) match {
+ case m @ binary.InfoBC(p) => {
+ InfoBC(p, MPInfoCA(a, m.cont))
+ }
+ }
+ }
+}
+case class MPInfoCA(a: Out[binary.InfoCA], b: In[binary.Mov1BCOrMov2BC]) {
+ def send(v: InfoCA) = {
+ val cnt = a !! binary.InfoCA(v.p)_
+ MPMov1BCOrMov2BC(cnt, b)
+ }
+}
+case class MPMov1BCOrMov2BC(a: Out[binary.Mov1CAOrMov2CA], b: In[binary.Mov1BCOrMov2BC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ b.receive(timeout) match {
+ case m @ binary.Mov1BC(p) => {
+ Mov1BC(p, MPMov1CAOrMov2CA(a, m.cont))
+ }
+ case m @ binary.Mov2BC(p) => {
+ Mov2BC(p, MPMov1CAOrMov2CA(a, m.cont))
+ }
+ }
+ }
+}
+case class MPMov1CAOrMov2CA(a: Out[binary.Mov1CAOrMov2CA], b: In[binary.Mov1BCOrMov2BC]) {
+ def send(v: Mov1CA) = {
+ val cnt = a !! binary.Mov1CA(v.p)_
+ MPMov1BCOrMov2BC(cnt, b)
+ }
+ def send(v: Mov2CA) = {
+ val cnt = a !! binary.Mov2CA(v.p)_
+ MPMov1BCOrMov2BC(cnt, b)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/Server.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/Server.scala
new file mode 100644
index 0000000..1ec7bac
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/Server.scala
@@ -0,0 +1,235 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.http.server
+
+import lchannels._
+import lchannels.examples.http.protocol.binary
+import lchannels.examples.http.protocol.server._
+import lchannels.examples.http.protocol.server.{Server => ServerName} // Clash
+import lchannels.examples.http.protocol.types._
+
+import scala.concurrent.duration._
+import java.net.Socket
+import java.nio.file.{Path, Paths}
+import java.time.ZonedDateTime;
+import com.typesafe.scalalogging.StrictLogging
+
+object Server extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import java.net.{InetAddress, ServerSocket}
+
+ val root = java.nio.file.FileSystems.getDefault().getPath("").toAbsolutePath
+
+ val address = InetAddress.getByName(null)
+ val port = 8080
+ val ssocket = new ServerSocket(port, 0, address)
+
+ println(f"[*] HTTP server listening on: http://${address.getHostAddress}:${port}/")
+ println(f"[*] Root directory: ${root}")
+ println(f"[*] Press Ctrl+C to terminate")
+
+ implicit val timeout = 30.seconds
+ accept(1)
+
+ @scala.annotation.tailrec
+ def accept(nextWorkerId: Int) {
+ val client = ssocket.accept()
+ println(f"[*] Connection from ${client.getInetAddress}, spawning worker")
+ new Worker(nextWorkerId, client, root)
+ accept(nextWorkerId + 1)
+ }
+}
+
+class Worker(id: Int, socket: Socket, root: Path)
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(msg)
+ private def logDebug(msg: String) = logger.debug(msg)
+ private def logInfo(msg: String) = logger.info(msg)
+ private def logWarn(msg: String) = logger.warn(msg)
+ private def logError(msg: String) = logger.error(msg)
+
+ private val serverName = "lchannels HTTP server"
+ private val pslash = Paths.get("/") // Used to relativize request paths
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run(): Unit = {
+ logInfo("Started.")
+
+ // Socket manager for the HTTP connection
+ val sktmgr = new binary.HttpServerSocketManager(socket, true, logInfo)
+ // Create a SocketChannel (with the correct type) from the client socket...
+ val c = SocketIn[binary.Request](sktmgr)
+ // ...and wrap it with a multiparty (in this case, binary) session object,
+ // to hide continuation-passing
+ val r = MPRequest(c)
+
+ val (rpath, cont) = {
+ try getRequest(r)
+ catch {
+ case sktmgr.ConnectionClosed(msg) => { logInfo(msg); return }
+ case e: java.util.concurrent.TimeoutException => {
+ logInfo(f"Timeout error: ${e.getMessage}")
+ sktmgr.close()
+ logInfo("Terminating.")
+ return
+ }
+ }
+ }
+
+ val path = root.resolve(pslash.relativize(Paths.get(rpath)))
+ logInfo(f"Resolved request path: ${path}")
+ // TODO: we should reject paths like e.g. ../../../../etc/passwd
+
+ val cont2 = cont.send(HttpVersion(Http11))
+
+ val file = path.toFile
+
+ if (!file.exists || !file.canRead) {
+ notFound(cont2, rpath)
+ } else {
+ logInfo("Resource found.")
+ val cont3 = cont2.send(Code200("OK"))
+ .send(ServerName(serverName))
+ .send(Date(ZonedDateTime.now))
+ if (file.isFile) {
+ serveFile(cont3, path)
+ } else if (file.isDirectory) {
+ serveDirectory(cont3, rpath, file)
+ } else {
+ throw new RuntimeException(f"BUG: unsupported resource type: ${path}")
+ }
+ }
+
+ logInfo("Terminating.")
+ }
+
+ private def getRequest(c: MPRequest)(implicit timeout: Duration) = {
+ val req = c.receive
+ logInfo(f"Method: ${req.p.method}; path: ${req.p.path}; version: ${req.p.version}")
+ val cont = choices(req.cont)
+ (req.p.path, cont)
+ }
+
+ @scala.annotation.tailrec
+ private def choices(c: MPRequestChoice)
+ (implicit timeout: Duration): MPHttpVersion = c.receive match {
+ case Accept(p, cont) => {
+ logInfo(f"Client accepts: ${p}")
+ choices(cont)
+ }
+ case AcceptEncodings(p, cont) => {
+ logInfo(f"Client encodings: ${p}")
+ choices(cont)
+ }
+ case AcceptLanguage(p, cont) => {
+ logInfo(f"Client languages: ${p}")
+ choices(cont)
+ }
+ case Connection(p, cont) => {
+ logInfo(f"Client connection: ${p}")
+ choices(cont)
+ }
+ case DoNotTrack(p, cont) => {
+ logInfo(f"Client Do Not Track flag: ${p}")
+ choices(cont)
+ }
+ case Host(p, cont) => {
+ logInfo(f"Client host: ${p}")
+ choices(cont)
+ }
+ case RequestBody(p, cont) => {
+ logInfo(f"Client request body: ${p}")
+ cont
+ }
+ case UpgradeIR(p, cont) => {
+ logInfo(f"Client upgrade insecure requests: ${p}")
+ choices(cont)
+ }
+ case UserAgent(p, cont) => {
+ logInfo(f"Client user agent: ${p}")
+ choices(cont)
+ }
+ }
+
+ private def notFound(c: MPCode200OrCode404, res: String) = {
+ logInfo(f"Resource not found: ${res}")
+ c.send(Code404("Not Found"))
+ .send(ServerName(serverName))
+ .send(Date(ZonedDateTime.now))
+ .send(ResponseBody(
+ Body("text/plain", f"Resource ${res} not found".getBytes("UTF-8"))))
+ }
+
+ private def serveFile(c: MPResponseChoice, file: Path) = {
+ val filename = file.getFileName().toString()
+ val contentType = {
+ if (filename.endsWith(".html")) "text/html"
+ else if (filename.endsWith(".css")) "text/css"
+ else "text/plain" // TODO: we assume content is human-readable
+ }
+ logInfo(f"Serving file: ${file} (content type: ${contentType}")
+
+ // TODO: for simplicity, we assume all files are UTF-8
+ c.send(ResponseBody(
+ Body(f"${contentType}; charset=utf-8", java.nio.file.Files.readAllBytes(file))))
+ }
+
+ private def serveDirectory(c: MPResponseChoice, rpath: String, dir: java.io.File) = {
+ logInfo(f"Serving directory: ${dir}")
+
+ val list = dir.listFiles.foldLeft(""){(a,i) =>
+ a + f"""|
+ |
+ | ${i.getName}${if (i.isFile) "" else "/"}
+ |
+ | \n""".stripMargin
+ }
+ val html = f"""|
+ |
+ |
+ |
+ | Contents of ${rpath}
+ |
+ |
+ | Contents of ${rpath}
+ |
+ | Page generated by ${serverName}
+ |
+ |\n""".stripMargin
+
+ c.send(ResponseBody(
+ Body("text/html", html.getBytes("UTF-8"))))
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Binary.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Binary.scala
new file mode 100644
index 0000000..ae327f8
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Binary.scala
@@ -0,0 +1,177 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Binary protocol classes for the HTTP server.
+ * The classes in this package have been automatically generated from the
+ * Scribble HTTP protocol definition:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Http.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.http.protocol.binary
+
+import lchannels._
+import lchannels.examples.http.protocol.types._
+
+import java.time.ZonedDateTime;
+
+case class Request(p: RequestLine)(val cont: In[RequestChoice])
+
+sealed abstract class RequestChoice
+case class Accept(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+case class AcceptEncodings(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+case class AcceptLanguage(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+case class Connection(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+case class DoNotTrack(p: Boolean)(val cont: In[RequestChoice]) extends RequestChoice
+case class Host(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+case class RequestBody(p: Body)(val cont: Out[HttpVersion]) extends RequestChoice
+case class UpgradeIR(p: Boolean)(val cont: In[RequestChoice]) extends RequestChoice
+case class UserAgent(p: String)(val cont: In[RequestChoice]) extends RequestChoice
+
+case class HttpVersion(p: Version)(val cont: In[Code200OrCode404])
+
+sealed abstract class Code200OrCode404
+case class Code200(p: String)(val cont: In[ResponseChoice]) extends Code200OrCode404
+case class Code404(p: String)(val cont: In[ResponseChoice]) extends Code200OrCode404
+
+sealed abstract class ResponseChoice
+case class AcceptRanges(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class ContentLength(p: Int)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class ContentType(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class Date(p: ZonedDateTime)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class ETag(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class LastModified(p: ZonedDateTime)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class ResponseBody(p: Body) extends ResponseChoice
+case class Server(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class StrictTS(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class Vary(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+case class Via(p: String)(val cont: In[ResponseChoice]) extends ResponseChoice
+
+import java.net.Socket
+import java.io.{
+ BufferedReader, BufferedWriter, InputStreamReader, OutputStreamWriter
+}
+import java.time.format.DateTimeFormatter.{RFC_1123_DATE_TIME => RFCDate}
+
+/** Socket manager for the HTTP protocol.
+ *
+ * @param socket the socket managed by the instance
+ * @param relaxHeaders if true, skip unmanaged HTTP headers (otherwise, error)
+ * @param logger logging function, used to report e.g. skipped headers and other info
+ */
+class HttpServerSocketManager(socket: Socket,
+ relaxHeaders: Boolean,
+ logger: (String) => Unit) extends SocketManager(socket) {
+ case class ConnectionClosed(msg: String) extends java.io.IOException(msg)
+ case class ProtocolError(msg: String) extends java.io.IOException(msg)
+
+ private val outb = new BufferedWriter(new OutputStreamWriter(out))
+ private var requestStarted = false // Remembers whether GET/POST/... was seen
+
+ private val crlf = "\r\n"
+
+ override def streamer(x: Any) = x match {
+ case HttpVersion(v) => outb.write(f"${v} ")
+ case Code200(msg) => outb.write(f"200 ${msg}${crlf}"); outb.flush()
+ case Code404(msg) => outb.write(f"404 ${msg}${crlf}"); outb.flush()
+
+ case Date(date) => outb.write(f"Date: ${date.format(RFCDate)}${crlf}"); outb.flush()
+ case Server(server) => outb.write(f"Server: ${server}${crlf}"); outb.flush()
+ case ResponseBody(body) => {
+ outb.write(f"Content-Type: ${body.contentType}${crlf}"); outb.flush()
+ outb.write(f"Content-Length: ${body.contents.size}${crlf}${crlf}"); outb.flush()
+ out.write(body.contents) // NOTE: bypass outb, to preserve encoding
+ outb.close()
+ }
+
+ case e => { close(); throw new RuntimeException(f"BUG: unsupported message: '${e}'") }
+ }
+
+ private val inb = new BufferedReader(new InputStreamReader(in))
+ private val requestR = """(\S+) (\S+) (\S+)""".r // Start of HTTP request
+ private val acceptR = """Accept: (.+)""".r // Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
+ private val acceptEncR = """Accept-Encoding: (.+)""".r // Accept-Encoding: gzip, deflate
+ private val acceptLangR = """Accept-Language: (.+)""".r // Accept-Language: en-GB,en;q=0.5
+ private val connectionR = """Connection: (\S+)""".r // Connection: keep-alive
+ private val dntR = """DNT: (\d)""".r // DNT: 1
+ private val hostR = """Host: (\S+)""".r // Host: www.doc.ic.ac.uk
+ private val upgradeirR = """Upgrade-Insecure-Requests: (\d)""".r // Upgrade-Insecure-Requests: 1
+ private val useragentR = """User-Agent: (.+)""".r // User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0
+
+ private val genericHeaderR = """(\S+): (.+)""".r // Generic regex for unsupported headers
+
+ override def destreamer(): Any = {
+ import java.net.URI
+
+ val line = inb.readLine()
+
+ if (!requestStarted) {
+ line match {
+ case requestR(method, uri, version) => {
+ requestStarted = true;
+ val m = Method(method)
+ val path = new URI(uri).getPath
+ val v = Version(version)
+ return Request(RequestLine(m, path, v))(SocketIn[RequestChoice](this))
+ }
+
+ case null => {
+ close()
+ throw ConnectionClosed("Connection closed by client")
+ }
+ case e => {
+ close()
+ throw new ProtocolError(f"Unexpected initial message: '${e}'")
+ }
+ }
+ }
+
+ // If we are here, then requestStarted was false
+ line match {
+ case acceptR(fmts) => Accept(fmts)(SocketIn[RequestChoice](this))
+ case acceptEncR(encs) => AcceptEncodings(encs)(SocketIn[RequestChoice](this))
+ case acceptLangR(langs) => AcceptLanguage(langs)(SocketIn[RequestChoice](this))
+ case connectionR(conn) => Connection(conn)(SocketIn[RequestChoice](this))
+ case dntR(dnt) => DoNotTrack(dnt == 1)(SocketIn[RequestChoice](this))
+ case hostR(host) => Host(host)(SocketIn[RequestChoice](this))
+ case upgradeirR(up) => UpgradeIR(up == 1)(SocketIn[RequestChoice](this))
+ case useragentR(ua) => UserAgent(ua)(SocketIn[RequestChoice](this))
+
+ case genericHeaderR(h, _) if relaxHeaders => {
+ // Ignore this header, and keep looking for something supported
+ logger(f"Skipping unsupported HTTP header '${h}'")
+ destreamer()
+ }
+
+ case "" => {
+ // The request body should now follow.
+ // TODO: in this HTTP fragment, we assume GET with Content-Length=0
+ RequestBody(Body("text/html", Array[Byte]()))(SocketOut[HttpVersion](this))
+ }
+
+ case e => { close(); throw new ProtocolError(f"Unexpected message: '${e}'") }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Server.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Server.scala
new file mode 100644
index 0000000..e477f72
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Server.scala
@@ -0,0 +1,180 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** HTTP protocol server.
+ * The classes in this package have been automatically generated from the
+ * Scribble HTTP protocol definition:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/Http.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.http.protocol.server
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.http.protocol.binary
+import lchannels.examples.http.protocol.types._
+
+import java.time.ZonedDateTime
+
+// Input message types for multiparty sessions
+case class Request(p: RequestLine, cont: MPRequestChoice)
+
+sealed abstract class MsgMPRequestChoice
+case class AcceptEncodings(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class Accept(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class DoNotTrack(p: Boolean, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class UpgradeIR(p: Boolean, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class Connection(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class UserAgent(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class AcceptLanguage(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class Host(p: String, cont: MPRequestChoice) extends MsgMPRequestChoice
+case class RequestBody(p: Body, cont: MPHttpVersion) extends MsgMPRequestChoice
+
+// Output message types for multiparty sessions
+case class HttpVersion(p: Version)
+case class Code404(p: String)
+case class Code200(p: String)
+case class ETag(p: String)
+case class Server(p: String)
+case class ContentLength(p: Int)
+case class ContentType(p: String)
+case class Vary(p: String)
+case class Via(p: String)
+case class StrictTS(p: String)
+case class ResponseBody(p: Body)
+case class AcceptRanges(p: String)
+case class LastModified(p: ZonedDateTime)
+case class Date(p: ZonedDateTime)
+
+// Multiparty session classes
+case class MPRequest(c: In[binary.Request]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.Request(p) => {
+ Request(p, MPRequestChoice(m.cont))
+ }
+ }
+ }
+}
+
+case class MPRequestChoice(c: In[binary.RequestChoice]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.Accept(p) => {
+ Accept(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.AcceptEncodings(p) => {
+ AcceptEncodings(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.AcceptLanguage(p) => {
+ AcceptLanguage(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.Connection(p) => {
+ Connection(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.DoNotTrack(p) => {
+ DoNotTrack(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.Host(p) => {
+ Host(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.RequestBody(p) => {
+ RequestBody(p, MPHttpVersion(m.cont))
+ }
+ case m @ binary.UpgradeIR(p) => {
+ UpgradeIR(p, MPRequestChoice(m.cont))
+ }
+ case m @ binary.UserAgent(p) => {
+ UserAgent(p, MPRequestChoice(m.cont))
+ }
+ }
+ }
+}
+
+case class MPHttpVersion(c: Out[binary.HttpVersion]) {
+ def send(v: HttpVersion) = {
+ val cnt = c !! binary.HttpVersion(v.p)_
+ MPCode200OrCode404(cnt)
+ }
+}
+
+case class MPCode200OrCode404(c: Out[binary.Code200OrCode404]) {
+ def send(v: Code200) = {
+ val cnt = c !! binary.Code200(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: Code404) = {
+ val cnt = c !! binary.Code404(v.p)_
+ MPResponseChoice(cnt)
+ }
+}
+
+case class MPResponseChoice(c: Out[binary.ResponseChoice]) {
+ def send(v: AcceptRanges) = {
+ val cnt = c !! binary.AcceptRanges(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: ContentLength) = {
+ val cnt = c !! binary.ContentLength(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: ContentType) = {
+ val cnt = c !! binary.ContentType(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: Date) = {
+ val cnt = c !! binary.Date(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: ETag) = {
+ val cnt = c !! binary.ETag(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: LastModified) = {
+ val cnt = c !! binary.LastModified(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: ResponseBody) = {
+ val cnt = c ! binary.ResponseBody(v.p)
+ ()
+ }
+ def send(v: Server) = {
+ val cnt = c !! binary.Server(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: StrictTS) = {
+ val cnt = c !! binary.StrictTS(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: Vary) = {
+ val cnt = c !! binary.Vary(v.p)_
+ MPResponseChoice(cnt)
+ }
+ def send(v: Via) = {
+ val cnt = c !! binary.Via(v.p)_
+ MPResponseChoice(cnt)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Types.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Types.scala
new file mode 100644
index 0000000..3d99773
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/http/protocol/Types.scala
@@ -0,0 +1,60 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Auxiliary types for the HTTP protocol server.
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.http.protocol.types
+
+sealed abstract class Version
+case object Http11 extends Version {
+ override def toString() = "HTTP/1.1"
+}
+
+object Version {
+ /** Construct Version object from a string, as found in an HTTP request. */
+ def apply(v: String) = v match {
+ case "HTTP/1.1" => Http11
+ case _ => throw new RuntimeException(f"Unsupported HTTP version: ${v}")
+ }
+}
+
+sealed abstract class Method
+case object GET extends Method {
+ override def toString() = "GET"
+}
+
+object Method {
+ /** Construct method object from a string, as found in an HTTP request. */
+ def apply(v: String) = v match {
+ case "GET" => GET
+ case _ => throw new RuntimeException(f"Unsupported method: ${v}")
+ }
+}
+
+case class RequestLine(method: Method, path: String, version: Version)
+
+case class Body(contentType: String, contents: Array[Byte])
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Barber.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Barber.scala
new file mode 100644
index 0000000..a4a9b72
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Barber.scala
@@ -0,0 +1,83 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.sleepingbarber.barbershop
+
+import scala.concurrent.duration.Duration
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels._
+
+import lchannels.examples.sleepingbarber.customer.{
+ Description => CustDescription, Cut => CustCut
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Session type:
+// S_barber = rec X.!Available.?Customer(S_cut).X (S_cut: see Customer.scala)
+//////////////////////////////////////////////////////////////////////////////
+protected[barbershop] case class Available()(val cont: Out[Serve])
+
+protected[barbershop] case class Serve(chan: In[CustDescription])
+ (val cont: Out[Available])
+//////////////////////////////////////////////////////////////////////////////
+
+class Barber(shop: Out[Available])
+ (implicit d: Duration) extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def quit() = thread.interrupt()
+
+ override def run(): Unit = {
+ logInfo("started, entering main loop")
+ loop(shop)
+ logInfo("quitting")
+ }
+
+ // NOTE: loop() is not tail-recursive --- left as it is for simplicity
+ private def loop(shop: Out[Available]): Unit= try {
+ logInfo("signaling availability, and waiting for customer")
+ (shop !! Available()_) ? { customer =>
+ logInfo("got customer, waiting for haircut description")
+ customer.chan ? { descr =>
+ logInfo("performing haircut, and waiting for payment...")
+ (descr.cont !! CustCut()_) ? { pay =>
+ logInfo("payment received, customer dismissed")
+ }
+ }
+ loop(customer.cont)
+ }
+ } catch {
+ case _: InterruptedException => logInfo("interrupted, leaving main loop")
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Customer.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Customer.scala
new file mode 100644
index 0000000..6366ec0
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Customer.scala
@@ -0,0 +1,92 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.sleepingbarber.customer
+
+import scala.concurrent.duration.Duration
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels._
+
+import lchannels.examples.sleepingbarber.barbershop
+
+//////////////////////////////////////////////////////////////////////////////
+// Session type:
+// S_cust = ?Full.end & ?Seat.?Ready.S_cut
+// where S_cut = !Description(String).?Haircut.!Pay.end
+//////////////////////////////////////////////////////////////////////////////
+sealed abstract class WaitingRoom
+case class Full() extends WaitingRoom
+case class Seat()(val cont: In[Ready]) extends WaitingRoom
+
+case class Ready()(val cont: Out[Description])
+
+case class Description(text: String)(val cont: Out[Cut])
+
+case class Cut()(val cont: Out[Pay])
+
+case class Pay(amount: Int)
+////////////////////////////////////////////////////////////////////////////
+
+class Customer(name: String, shop: barbershop.Shop)
+ (implicit d: Duration) extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${name}: ${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${name}: ${msg}")
+ private def logInfo(msg: String) = logger.info(f"${name}: ${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${name}: ${msg}")
+ private def logError(msg: String) = logger.error(f"${name}: ${msg}")
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run(): Unit = {
+ logInfo("started, entering in shop")
+ loop()
+ logInfo("leaving the shop")
+ }
+
+ private def loop(): Unit = {
+ shop.enter() ? {
+ case Full() => {
+ logInfo("waiting is room full, will retry within 3 seconds")
+ Thread.sleep(new scala.util.Random().nextInt(30) * 100)
+ loop()
+ }
+ case m @ Seat() => {
+ logInfo("got a seat, waiting...")
+ m.cont ? { ready =>
+ logInfo("barber is ready, describing cut")
+ (ready.cont !! Description("Fancy hairdo")_) ? { cut =>
+ logInfo("cut done, paying")
+ cut.cont ! Pay(42)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Demo.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Demo.scala
new file mode 100644
index 0000000..f791138
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Demo.scala
@@ -0,0 +1,55 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.sleepingbarber.demo
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+
+ implicit val timeout = 10.seconds
+
+ val customers = List(
+ "Alice", "Bob", "Carol", "Dave", "Erin", "Frank", "George", "Horace",
+ "Isabelle", "John", "Karen", "Louis", "Mark", "Nick", "Oliver", "Paul",
+ "Quentin", "Rebecca", "Sarah", "Thomas", "Ursula", "Valeri", "William",
+ "Xavier", "Yasemin", "Zacharias"
+ )
+
+ val maxSeats = 4
+
+ val shop = lchannels.examples.sleepingbarber.barbershop.Shop(maxSeats)
+
+ val customerObjs = for (name <- customers)
+ yield new lchannels.examples.sleepingbarber.customer.Customer(name, shop)
+
+ for (c <- customerObjs) c.join()
+ println("*** All customers served - shutting down barbershop")
+
+ shop.quit()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Shop.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Shop.scala
new file mode 100644
index 0000000..9b51620
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/sleepingbarber/Shop.scala
@@ -0,0 +1,115 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.sleepingbarber.barbershop
+
+import scala.concurrent.duration.Duration
+import java.util.concurrent.atomic.AtomicInteger
+
+import com.typesafe.scalalogging.StrictLogging
+
+import lchannels._
+import lchannels.util.Fifo
+
+import lchannels.examples.sleepingbarber.customer.{
+ WaitingRoom, Full, Seat, Ready
+}
+
+class Shop(nSeats: Int,
+ bfactory: () => (In[Available], Out[Available]),
+ cfactory: () => (In[WaitingRoom], Out[WaitingRoom]))
+ (implicit timeout: Duration) extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(f"${msg}")
+ private def logDebug(msg: String) = logger.debug(f"${msg}")
+ private def logInfo(msg: String) = logger.info(f"${msg}")
+ private def logWarn(msg: String) = logger.warn(f"${msg}")
+ private def logError(msg: String) = logger.error(f"${msg}")
+
+ var barber: Barber = null // Will be initialized in run()
+
+ private val seats: Fifo[Out[Ready]] = Fifo()
+ private val peopleWaiting = new AtomicInteger(0) // Keep in synch. with seats
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t}
+ def quit() = thread.interrupt()
+
+ def enter(): In[WaitingRoom] = {
+ logDebug("a customer is entering")
+ val (in, out) = cfactory()
+ val nPeople = peopleWaiting.getAndIncrement()
+ if (nPeople >= nSeats) {
+ logDebug(f"waiting room is full: ${nPeople+1} customers, ${nSeats} seats")
+ peopleWaiting.getAndDecrement()
+ out ! Full()
+ } else {
+ logDebug(f"a seat is available (${nPeople} customer(s) sitting)")
+ val r = out !! Seat()_
+ logDebug(f"seat taken (${nPeople+1} customer(s) sitting")
+ seats.write(r)
+ }
+ in
+ }
+
+ override def run(): Unit = {
+ logDebug("started")
+ assert(nSeats > 0)
+
+ logDebug("starting barber")
+ val (barberIn, barberOut) = bfactory()
+ barber = new Barber(barberOut)
+
+ logDebug("entering main loop")
+
+ loop(barberIn)
+
+ logDebug("terminating barber")
+ barber.quit()
+
+ logInfo("quitting")
+ }
+
+ private def loop(barber: In[Available]): Unit = try {
+ logDebug("waiting for barber availability...")
+ barber ? { avail =>
+ logDebug("waiting for customer")
+ val cust = seats.read
+ peopleWaiting.getAndDecrement()
+ logDebug("notifying the customer that the barber is ready")
+ val custd = cust !! Ready()_
+ logDebug("forwarding customer to barber, and looping")
+ loop(avail.cont !! Serve(custd)_)
+ }
+ } catch {
+ case _: InterruptedException => logInfo("interrupted, leaving main loop")
+ }
+}
+
+object Shop {
+ def apply(maxSeats: Int)(implicit d: Duration) = {
+ new Shop(maxSeats, LocalChannel.factory, LocalChannel.factory)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Alice.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Alice.scala
new file mode 100644
index 0000000..d0c8d6e
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Alice.scala
@@ -0,0 +1,126 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.threebuyer.alice
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import lchannels.examples.threebuyer.protocol.alice._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Alice(s: In[binary.PlayAlice])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(msg)
+ private def logDebug(msg: String) = logger.debug(msg)
+ private def logInfo(msg: String) = logger.info(msg)
+ private def logWarn(msg: String) = logger.warn(msg)
+ private def logError(msg: String) = logger.error(msg)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ val c = MPPlayAlice(s) // Wrap the channel in a multiparty session obj
+
+ logInfo("Started. Waiting for multiparty session...")
+ val c2 = c.receive.p
+
+ val choices = Map(
+ 1 -> ("Alice in Wonderland", "Lewis Carroll"),
+ 2 -> ("War and Peace", "Lev Nikolajevič Tolstoj"),
+ 3 -> ("The Art of Computer Programming", "Donald Knuth")
+ )
+ val title = chooseTitle(choices)
+ logInfo(f"Sending title: '${title}' --- and waiting for quote...")
+ val quote = c2.send(Title(title)).receive
+ logInfo(f"Got quote: ${quote.p}")
+ val halfQuote = quote.p / 2
+
+ logInfo(f"Telling Bob that we contribute ${halfQuote}; waiting answer...")
+ quote.cont.send(ShareA(halfQuote)).receive match {
+ case OkA(()) => logInfo("Shared purchase accepted")
+ case QuitA(()) => logInfo("Shared purchase rejected")
+ }
+
+ logInfo("Terminating.")
+ }
+
+ @scala.annotation.tailrec
+ private def chooseTitle(choices: Map[Int, (String, String)]): String = {
+
+ println("Which book should Alice choose? Please select:")
+ for (k <- choices.keys) {
+ val c = choices.get(k).get
+ println(f" ${k} - ${c._1} (${c._2})")
+ }
+ print("> ")
+ val choice = scala.io.StdIn.readInt
+ if (choice >= 1 && choice <= 3) {
+ choices.get(choice).get._1
+ } else {
+ println("Invalid choice, please retry")
+ chooseTitle(choices)
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ import binary.actor.ConnectAlice
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("ThreeBuyerAliceSys",
+ config = Some(config.getConfig("ThreeBuyerAliceSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 60.seconds
+
+ val sellerPath = "akka.tcp://ThreeBuyerSellerSys@127.0.0.1:31350/user/alice"
+ println(f"[*] Connecting to ${sellerPath}...")
+ val c: Out[ConnectAlice] = ActorOut[ConnectAlice](sellerPath)
+ val c2 = c !! ConnectAlice()_
+
+ val alice = new Alice(c2)(30.seconds)
+
+ alice.join()
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Bob.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Bob.scala
new file mode 100644
index 0000000..9d859ce
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Bob.scala
@@ -0,0 +1,138 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.threebuyer.bob
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import lchannels.examples.threebuyer.protocol.bob._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Bob(s: In[binary.PlayBob],
+ carolConnector: (String => Unit) => Out[binary.Contrib])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(msg)
+ private def logDebug(msg: String) = logger.debug(msg)
+ private def logInfo(msg: String) = logger.info(msg)
+ private def logWarn(msg: String) = logger.warn(msg)
+ private def logError(msg: String) = logger.error(msg)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ val c = MPPlayBob(s) // Wrap the channel in a multiparty session obj
+
+ logInfo("Started. Waiting for multiparty session...")
+ val c2 = c.receive.p
+
+ logInfo("Waiting for book quote...")
+ val quote = c2.receive
+ logInfo(f"Got quote: ${quote.p}. Waiting to know Alice's share...")
+ val contrib = quote.cont.receive
+ logInfo(f"Got Alice's share: ${contrib.p}")
+
+ val myShare = quote.p - contrib.p
+ assert(myShare >= 0)
+
+ val budget = 25
+
+ logInfo(f"My share is: ${myShare}; my maximum budget is: ${budget}")
+ if (myShare > budget) {
+ val needed = myShare - budget
+ logInfo(f"I need ${needed} more. Involving Carol...")
+ delegateCarol(contrib.cont, needed)
+ } else {
+ logInfo("Accepting proposal, sending address, waiting delivery date")
+ val delivery = contrib.cont.send(OkA(()))
+ .send(OkS(()))
+ .send(Address("Bob Smith, 221B Baker Street, London, UK"))
+ .receive
+ logInfo(f"Got delivery date: ${delivery.p}")
+ }
+
+ logInfo("Terminating.")
+ }
+
+ private def delegateCarol(s: MPOkAOrQuitA, needed: Int) = {
+ // Wrap Carol's channel in a session object
+ val carol = MPContrib(carolConnector(logInfo))
+
+ logInfo(f"Telling Carol to contribute ${needed}; delegating session with Alice and Seller")
+ val ccont = carol.send(Contrib(needed)).send(Delegate(s))
+ logInfo(f"Waiting for Carol's decision...")
+ ccont.receive match {
+ case OkC(()) => logInfo("Carol accepted")
+ case QuitC(()) => logInfo("Carol declined")
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ import binary.actor.{ConnectBob, ConnectCarol}
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("ThreeBuyerBobSys",
+ config = Some(config.getConfig("ThreeBuyerBobSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 60.seconds
+
+ val sellerPath = "akka.tcp://ThreeBuyerSellerSys@127.0.0.1:31350/user/bob"
+ println(f"[*] Connecting to ${sellerPath}...")
+ val c: Out[ConnectBob] = ActorOut[ConnectBob](sellerPath)
+ val c2 = c !! ConnectBob()_
+
+ def connector(logger: String => Unit) = {
+ // Path where Carol is waiting for Bob's connection
+ val carolPath = "akka.tcp://ThreeBuyerCarolSys@127.0.0.1:31353/user/bob"
+ logger(f"Connecting to ${carolPath}...")
+ val c: Out[ConnectCarol] = ActorOut[ConnectCarol](carolPath)
+ c !! ConnectCarol()_
+ }
+
+ val bob = new Bob(c2, connector)(30.seconds)
+
+ bob.join()
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Carol.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Carol.scala
new file mode 100644
index 0000000..d5c6eb3
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Carol.scala
@@ -0,0 +1,126 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.threebuyer.carol
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import lchannels.examples.threebuyer.protocol.bob
+import lchannels.examples.threebuyer.protocol.carol._
+
+import scala.concurrent.duration._
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Carol(bobConnector: () => In[binary.Contrib])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(msg)
+ private def logDebug(msg: String) = logger.debug(msg)
+ private def logInfo(msg: String) = logger.info(msg)
+ private def logWarn(msg: String) = logger.warn(msg)
+ private def logError(msg: String) = logger.error(msg)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+ def interrupt() = thread.interrupt()
+
+ override def run(): Unit = {
+ logInfo("Started. Ready to connect with Bob...")
+ val bobc = try {
+ // Wrap the binary channel with bob in a session object
+ MPContrib(bobConnector())
+ } catch {
+ case e: InterruptedException => {
+ // We were interrupted without being involved in the interaction
+ logInfo("Interrupted. Terminating.")
+ return
+ }
+ }
+
+ logInfo("Bob connected. Waiting for his contribution request...")
+ val contrib = bobc.receive
+ logInfo(f"Bob is asking for: ${contrib.p}")
+
+ logInfo("Receiving delegated session with Seller and Alice")
+ val deleg = contrib.cont.receive
+
+ val budget = 100
+ logInfo(f"My budget is: ${budget}")
+ if (contrib.p > budget) {
+ logInfo(f"Budget is not sufficient: declining")
+ deleg.cont.send(QuitC(()))
+ deleg.p.send(bob.QuitA(())).send(bob.QuitS(()))
+ } else {
+ logInfo(f"Budget OK: accepting, sending address, waiting delivery date")
+ deleg.cont.send(OkC(()))
+ val delivery = deleg.p.send(bob.OkA(()))
+ .send(bob.OkS(()))
+ .send(bob.Address("Carol Jones, 17 Cherry Tree Lane, London, UK"))
+ .receive
+ logInfo(f"Got delivery date: ${delivery.p}")
+ }
+
+ logInfo("Terminating.")
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("ThreeBuyerCarolSys",
+ config = Some(config.getConfig("ThreeBuyerCarolSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 120.seconds
+
+ // We give a human-readable name to the connection endpoints
+ val (bi, bo) = ActorChannel.factory[binary.actor.ConnectCarol]("bob");
+ println(f"[*] Waiting Bob's connections on: ${bo.path}")
+
+ def connector() = {
+ val conn = bi.receive
+ println(f"[*] Bob connected")
+ conn.cont
+ }
+
+ val alice = new Carol(connector)(30.seconds)
+
+ alice.join()
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Demo.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Demo.scala
new file mode 100644
index 0000000..3164d0e
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Demo.scala
@@ -0,0 +1,86 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.threebuyer.demo
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary.{PlayAlice, PlayBob, Contrib}
+import lchannels.examples.threebuyer.alice.Alice
+import lchannels.examples.threebuyer.bob.Bob
+import lchannels.examples.threebuyer.carol.Carol
+import lchannels.examples.threebuyer.seller.Seller
+
+import scala.concurrent.{Await, duration, Promise}
+
+object Local extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ Demo.start(LocalChannel.factory[PlayAlice],
+ LocalChannel.factory[PlayBob],
+ LocalChannel.factory[Contrib])
+}
+
+object Demo {
+ def start(afactory: () => (In[PlayAlice], Out[PlayAlice]),
+ bfactory: () => (In[PlayBob], Out[PlayBob]),
+ cfactory: () => (In[Contrib], Out[Contrib])) = {
+ import scala.concurrent.duration._
+ implicit val timeout = 60.seconds
+
+ // Client/server channels towards Alice and Bob
+ val (ca, sa) = afactory()
+ val (cb, sb) = bfactory()
+
+ // Promise/future pair used to "connect" Bob and Carol
+ val bcp = Promise[In[Contrib]]
+ val bcf = bcp.future
+
+ // Here, the connector to Bob (used by Carol) will not return
+ // until the connector to Carol (used by Bob) is invoked
+ // (but this will only happen if Bob actually decides to involve Carol)
+ def carolConnector(_logger: String => Unit) = {
+ val (bci, bco) = cfactory() // Channel between Bob and Carol
+
+ bcp.success(bci) // Complete the Promise with one channel endpoint...
+ bco // ...and return the other endpoint
+ }
+ def bobConnector() = Await.result(bcf, duration.Duration.Inf)
+
+ val seller = new Seller(sa, sb)
+ val alice = new Alice(ca)
+ val bob = new Bob(cb, carolConnector)
+ val carol = new Carol(bobConnector)
+
+ // Wait for Alice, Bob, Seller to quit (NOTE: Carol might not be involved)
+ alice.join()
+ bob.join()
+ seller.join()
+
+ Thread.sleep(1000) // Just to let Carol terminate, if involved
+ carol.interrupt()
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Seller.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Seller.scala
new file mode 100644
index 0000000..3f5b7b8
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/Seller.scala
@@ -0,0 +1,163 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.examples.threebuyer.seller
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import lchannels.examples.threebuyer.protocol.alice
+import lchannels.examples.threebuyer.protocol.bob
+import lchannels.examples.threebuyer.protocol.seller._
+
+import scala.concurrent.duration._
+import java.time.ZonedDateTime
+
+import com.typesafe.scalalogging.StrictLogging
+
+class Seller(ca: Out[binary.PlayAlice],
+ cb: Out[binary.PlayBob])
+ (implicit timeout: Duration)
+ extends Runnable with StrictLogging {
+ private def logTrace(msg: String) = logger.trace(msg)
+ private def logDebug(msg: String) = logger.debug(msg)
+ private def logInfo(msg: String) = logger.info(msg)
+ private def logWarn(msg: String) = logger.warn(msg)
+ private def logError(msg: String) = logger.error(msg)
+
+ // Own thread
+ private val thread = { val t = new Thread(this); t.start(); t }
+ def join() = thread.join()
+
+ override def run() = {
+ logInfo("Starting. Creating binary channels for multiparty session...")
+
+ // We need to create a pair of channel endpoints for each pair of
+ // connected roles in the multiparty session. We use the create()
+ // method because:
+ //
+ // (1) ca.create() ensures that the returned channel endpoints use the
+ // same message transport of ca (and similary for cb);
+ //
+ // (2) we need *both* input/output endpoints, so that later we can
+ // instantiate alice.MPTitle and bob.MPQuoteB, and send
+ // them to the session participants;
+ //
+ // (3) we do not *want* to use e.g. ca.!!(...), because it would only
+ // return *one* endpoint, and later we would not be able to
+ // instantiate alice.MPTitle and bob.MPQuoteB;
+ //
+ // (3) anyway, we *cannot* meaningfully use e.g. ca.!!(...): since
+ // the carried type has no continuation, it is quite difficult to
+ // come up with a "reasonable" value for "..." that is accepted by
+ // the Scala compiler. E.g., the following is accepted, but
+ // is visibly bogus: ca !! ((_:In[Int]) => null)
+ val (abi, abo) = ca.create[binary.ShareA] // Used between Alice and Bob
+ val (sai, sao) = ca.create[binary.Title] // Used between Seller and Alice
+ val (sbi, sbo) = cb.create[binary.QuoteB] // Used between Seller and Bob
+
+
+ // We now instantiate multiparty session objects (i.e., n-uples of
+ // binary linear channels), and send them to our clients via channels
+ // ca, cb, cc.
+ //
+ // Note that the types of the arguments of a.MPInfoCA, bMPInfoBC and
+ // c.MPInfoCA ensure that the channel endpoints above are used in the
+ // correct way: if the wrong channel is used somewhere, the resulting
+ // code does not compile
+ logInfo("...and sending multiparty objects to clients.")
+ ca ! binary.PlayAlice(alice.MPTitle(abo, sao))
+ cb ! binary.PlayBob(bob.MPQuoteB(abi, sbi))
+
+ // Wrap binary channels into a multiparty session object
+ val s = MPTitle(sai, sbo)
+ sell(s)
+
+ logInfo("Terminating.")
+ }
+
+ private def sell(s: MPTitle) = {
+ logInfo("Waiting for order...")
+ val order = s.receive
+ logInfo(f"Received order: '${order.p}'")
+ val quote = order.p match {
+ case "Alice in Wonderland" => 10
+ case "War and Peace" => 100
+ case _ => 1000 // We can find any book, but it will be expensive...
+ }
+ logInfo(f"Sending quote: ${quote} --- then waiting for answer...")
+ order.cont.send(QuoteA(quote)).send(QuoteB(quote)).receive match {
+ case OkS((), cont) => {
+ logInfo("Quote accepted, waiting for address")
+ val address = cont.receive
+ logInfo(f"Got delivery address: '${address.p}'")
+
+ val deliveryDate = ZonedDateTime.now().plusDays(7)
+ logInfo(f"Sending delivery date (7 days from now): ${deliveryDate}")
+ address.cont.send(Deliver(deliveryDate))
+ }
+ case QuitS(()) => {
+ logInfo("Quote rejected")
+ }
+ }
+ }
+}
+
+object Actor extends App {
+ // Helper method to ease external invocation
+ def run() = main(Array())
+
+ import scala.concurrent.duration._
+ import scala.concurrent.ExecutionContext.Implicits.global
+ import com.typesafe.config.ConfigFactory
+ import akka.actor.ActorSystem
+
+ val config = ConfigFactory.load() // Loads resources/application.conf
+ implicit val as = ActorSystem("ThreeBuyerSellerSys",
+ config = Some(config.getConfig("ThreeBuyerSellerSys")),
+ defaultExecutionContext = Some(global))
+
+ ActorChannel.setDefaultEC(global)
+ ActorChannel.setDefaultAS(as)
+
+ implicit val timeout = 120.seconds
+
+ // We give a human-readable name to the connection endpoints
+ val (ai, ao) = ActorChannel.factory[binary.actor.ConnectAlice]("alice");
+ val (bi, bo) = ActorChannel.factory[binary.actor.ConnectBob]("bob");
+ println(f"[*] Waiting connections on: ${ao.path}, ${bo.path}")
+
+ val ac = ai.receive
+ println(f"[*] Alice connected")
+ val bc = bi.receive
+ println(f"[*] Bob connected")
+
+ val seller = new Seller(ac.cont, bc.cont)(30.seconds)
+
+ seller.join()
+ Thread.sleep(2000) // Just to deliver pending actor messages
+ println(f"[*] Quitting")
+ as.terminate()
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Alice.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Alice.scala
new file mode 100644
index 0000000..fed8944
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Alice.scala
@@ -0,0 +1,99 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for role Alice in three-buyer example.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.threebuyer.protocol.alice
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+
+// Local type for role alice:
+// seller⊕{Title(String).seller&{QuoteA(Int).bob⊕{ShareA(Int).bob&{OkA(Unit).end, QuitA(Unit).end}}}}
+
+// Input message types for multiparty sessions (autogenerated by Scribble)
+case class PlayAlice(p: MPTitle)
+case class QuoteA(p: Int, cont: MPShareA)
+sealed abstract class MsgMPOkAOrQuitA
+case class OkA(p: Unit) extends MsgMPOkAOrQuitA
+case class QuitA(p: Unit) extends MsgMPOkAOrQuitA
+
+// Output message types for multiparty sessions (autogenerated by Scribble)
+case class Title(p: String)
+case class ShareA(p: Int)
+
+// Multiparty session classes (autogenerated by Scribble)
+case class MPPlayAlice(c: In[binary.PlayAlice]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.PlayAlice(p) => {
+ PlayAlice(p)
+ }
+ }
+ }
+}
+
+case class MPTitle(bob: Out[binary.ShareA], seller: Out[binary.Title]) {
+ def send(v: Title) = {
+ val cnt = seller !! binary.Title(v.p)_
+ MPQuoteA(bob, cnt)
+ }
+}
+
+case class MPQuoteA(bob: Out[binary.ShareA], seller: In[binary.QuoteA]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ seller.receive(timeout) match {
+ case m @ binary.QuoteA(p) => {
+ QuoteA(p, MPShareA(bob, ()))
+ }
+ }
+ }
+}
+
+case class MPShareA(bob: Out[binary.ShareA], seller: Unit) {
+ def send(v: ShareA) = {
+ val cnt = bob !! binary.ShareA(v.p)_
+ MPOkAOrQuitA(cnt, seller)
+ }
+}
+
+case class MPOkAOrQuitA(bob: In[binary.OkAOrQuitA], seller: Unit) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ bob.receive(timeout) match {
+ case m @ binary.OkA(p) => {
+ OkA(p)
+ }
+ case m @ binary.QuitA(p) => {
+ QuitA(p)
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Binary.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Binary.scala
new file mode 100644
index 0000000..29ee049
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Binary.scala
@@ -0,0 +1,82 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Binary protocol classes for the three-buyer example.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.threebuyer.protocol.binary
+
+import lchannels._
+import lchannels.examples.threebuyer.protocol.alice
+import lchannels.examples.threebuyer.protocol.bob
+
+import java.time.ZonedDateTime
+
+// Classes representing messages (with continuations) in binary sessions,
+// for the main protocol (autogenerated by Scribble)
+case class PlayAlice(p: alice.MPTitle)
+case class PlayBob(p: bob.MPQuoteB)
+
+case class ShareA(p: Int)(val cont: Out[OkAOrQuitA])
+
+sealed abstract class OkAOrQuitA
+case class OkA(p: Unit) extends OkAOrQuitA
+case class QuitA(p: Unit) extends OkAOrQuitA
+
+case class Title(p: String)(val cont: Out[QuoteA])
+case class QuoteA(p: Int)
+case class QuoteB(p: Int)(val cont: Out[OkSOrQuitS])
+
+sealed abstract class OkSOrQuitS
+case class OkS(p: Unit)(val cont: In[Address]) extends OkSOrQuitS
+case class QuitS(p: Unit) extends OkSOrQuitS
+
+case class Address(p: String)(val cont: Out[Deliver])
+case class Deliver(p: ZonedDateTime)
+
+// Classes representing messages (with continuations) in binary sessions,
+// for the delegation protocol (autogenerated by Scribble)
+//
+// TODO: these could be moved to a package only used by Bob and Carol
+case class Contrib(p: Int)(val cont: In[Delegate])
+case class Delegate(p: bob.MPOkAOrQuitA)(val cont: Out[OkCOrQuitC])
+sealed abstract class OkCOrQuitC
+case class OkC(p: Unit) extends OkCOrQuitC
+case class QuitC(p: Unit) extends OkCOrQuitC
+
+/** Binary protocol classes for establishing binary connections
+ * (used e.g. in the actor-based demo)
+ */
+package object actor {
+ case class ConnectAlice()(val cont: Out[PlayAlice])
+ case class ConnectBob()(val cont: Out[PlayBob])
+
+ // TODO: this could be moved to a package only used by Bob and Carol
+ case class ConnectCarol()(val cont: In[Contrib])
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Bob.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Bob.scala
new file mode 100644
index 0000000..1aa6a27
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Bob.scala
@@ -0,0 +1,167 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for role Bob in three-buyer example.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.threebuyer.protocol.bob
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import java.time.ZonedDateTime
+
+// Local type for role Bob (in main protocol):
+// Seller&{QuoteB(Int).Alice&{ShareA(Int).Alice⊕{OkA(Unit).Seller⊕{OkS(Unit).Seller⊕{Address(String).Seller&{Deliver(ZonedDateTime).end}}}, QuitA(Unit).Seller⊕{QuitS(Unit).end}}}}
+
+// Input message types for multiparty sessions (autogenerated by Scribble)
+case class PlayBob(p: MPQuoteB)
+case class QuoteB(p: Int, cont: MPShareA)
+case class ShareA(p: Int, cont: MPOkAOrQuitA)
+case class Deliver(p: ZonedDateTime)
+
+// Output message types for multiparty sessions (autogenerated by Scribble)
+case class OkA(p: Unit)
+case class QuitA(p: Unit)
+case class OkS(p: Unit)
+case class Address(p: String)
+case class QuitS(p: Unit)
+
+// Multiparty session classes (autogenerated by Scribble)
+case class MPPlayBob(c: In[binary.PlayBob]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.PlayBob(p) => {
+ PlayBob(p)
+ }
+ }
+ }
+}
+
+case class MPQuoteB(alice: In[binary.ShareA], seller: In[binary.QuoteB]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ seller.receive(timeout) match {
+ case m @ binary.QuoteB(p) => {
+ QuoteB(p, MPShareA(alice, m.cont))
+ }
+ }
+ }
+}
+
+case class MPShareA(alice: In[binary.ShareA], seller: Out[binary.OkSOrQuitS]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ alice.receive(timeout) match {
+ case m @ binary.ShareA(p) => {
+ ShareA(p, MPOkAOrQuitA(m.cont, seller))
+ }
+ }
+ }
+}
+
+case class MPOkAOrQuitA(alice: Out[binary.OkAOrQuitA], seller: Out[binary.OkSOrQuitS]) {
+ def send(v: OkA) = {
+ val cnt = alice ! binary.OkA(v.p)
+ MPOkS(cnt, seller)
+ }
+ def send(v: QuitA) = {
+ val cnt = alice ! binary.QuitA(v.p)
+ MPQuitS(cnt, seller)
+ }
+}
+
+case class MPOkS(alice: Unit, seller: Out[binary.OkSOrQuitS]) {
+ def send(v: OkS) = {
+ val cnt = seller !! binary.OkS(v.p)_
+ MPAddress(alice, cnt)
+ }
+}
+
+case class MPAddress(alice: Unit, seller: Out[binary.Address]) {
+ def send(v: Address) = {
+ val cnt = seller !! binary.Address(v.p)_
+ MPDeliver(alice, cnt)
+ }
+}
+
+case class MPDeliver(alice: Unit, seller: In[binary.Deliver]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ seller.receive(timeout) match {
+ case m @ binary.Deliver(p) => {
+ Deliver(p)
+ }
+ }
+ }
+}
+
+case class MPQuitS(alice: Unit, seller: Out[binary.OkSOrQuitS]) {
+ def send(v: QuitS) = {
+ val cnt = seller ! binary.QuitS(v.p)
+ ()
+ }
+}
+
+// Local type for role Bob (in Delegation protocol):
+// Carol⊕{Contrib(Int).Carol⊕{Delegate(alice⊕{OkA(Unit).seller⊕{OkS(Unit).seller⊕{Address(String).seller&{Deliver(ZonedDateTime).end}}}, QuitA(Unit).seller⊕{QuitS(Unit).end}}).Carol&{OkC(Unit).end, QuitC(Unit).end}}}
+
+// Input message types for multiparty sessions (autogenerated by Scribble)
+sealed abstract class MsgMPOkCOrQuitC
+case class OkC(p: Unit) extends MsgMPOkCOrQuitC
+case class QuitC(p: Unit) extends MsgMPOkCOrQuitC
+
+// Output message types for multiparty sessions (autogenerated by Scribble)
+case class Contrib(p: Int)
+case class Delegate(p: MPOkAOrQuitA)
+
+// Multiparty session classes
+case class MPContrib(carol: Out[binary.Contrib]) {
+ def send(v: Contrib) = {
+ val cnt = carol !! binary.Contrib(v.p)_
+ MPDelegate(cnt)
+ }
+}
+
+case class MPDelegate(carol: Out[binary.Delegate]) {
+ def send(v: Delegate) = {
+ val cnt = carol !! binary.Delegate(v.p)_
+ MPOkCOrQuitC(cnt)
+ }
+}
+
+case class MPOkCOrQuitC(carol: In[binary.OkCOrQuitC]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ carol.receive(timeout) match {
+ case m @ binary.OkC(p) => {
+ OkC(p)
+ }
+ case m @ binary.QuitC(p) => {
+ QuitC(p)
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Carol.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Carol.scala
new file mode 100644
index 0000000..ebeeb85
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Carol.scala
@@ -0,0 +1,119 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for role Carol in three-buyer example.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.threebuyer.protocol.carol
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import lchannels.examples.threebuyer.protocol.bob.MPOkAOrQuitA
+import java.time.ZonedDateTime
+
+// Local type for role Carol:
+// bob&{Contrib(Int).bob&{Delegate(alice⊕{OkA(Unit).seller⊕{OkS(Unit).seller⊕{Address(String).seller&{Deliver(ZonedDateTime).end}}}, QuitA(Unit).seller⊕{QuitS(Unit).end}}).bob⊕{OkC(Unit).end, QuitC(Unit).end}}}
+
+// Input message types for multiparty sessions (autogenerated by Scribble)
+case class Contrib(p: Int, cont: MPDelegate)
+case class Delegate(p: MPOkAOrQuitA, cont: MPOkCOrQuitC)
+case class Deliver(p: ZonedDateTime)
+
+// Output message types for multiparty sessions (autogenerated by Scribble)
+case class OkC(p: Unit)
+case class QuitC(p: Unit)
+case class OkA(p: Unit)
+case class QuitA(p: Unit)
+case class OkS(p: Unit)
+case class Address(p: String)
+case class QuitS(p: Unit)
+
+// Multiparty session classes (autogenerated by Scribble)
+case class MPContrib(bob: In[binary.Contrib]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ bob.receive(timeout) match {
+ case m @ binary.Contrib(p) => {
+ Contrib(p, MPDelegate(m.cont))
+ }
+ }
+ }
+}
+
+case class MPDelegate(bob: In[binary.Delegate]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ bob.receive(timeout) match {
+ case m @ binary.Delegate(p) => {
+ Delegate(p, MPOkCOrQuitC(m.cont))
+ }
+ }
+ }
+}
+
+case class MPOkS(alice: Unit, seller: Out[binary.OkSOrQuitS]) {
+ def send(v: OkS) = {
+ val cnt = seller !! binary.OkS(v.p)_
+ MPAddress(alice, cnt)
+ }
+}
+
+case class MPAddress(alice: Unit, seller: Out[binary.Address]) {
+ def send(v: Address) = {
+ val cnt = seller !! binary.Address(v.p)_
+ MPDeliver(alice, cnt)
+ }
+}
+
+case class MPDeliver(alice: Unit, seller: In[binary.Deliver]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ seller.receive(timeout) match {
+ case m @ binary.Deliver(p) => {
+ Deliver(p)
+ }
+ }
+ }
+}
+
+case class MPQuitS(alice: Unit, seller: Out[binary.OkSOrQuitS]) {
+ def send(v: QuitS) = {
+ val cnt = seller ! binary.QuitS(v.p)
+ ()
+ }
+}
+
+case class MPOkCOrQuitC(bob: Out[binary.OkCOrQuitC]) {
+ def send(v: OkC) = {
+ val cnt = bob ! binary.OkC(v.p)
+ ()
+ }
+ def send(v: QuitC) = {
+ val cnt = bob ! binary.QuitC(v.p)
+ ()
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Seller.scala b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Seller.scala
new file mode 100644
index 0000000..e9e0432
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/examples/src/main/scala/lchannels/examples/threebuyer/protocol/Seller.scala
@@ -0,0 +1,108 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2017, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+/** Multiparty protocol classes for role Seller in three-buyer example.
+ * The classes in this package have been automatically generated from the
+ * multiparty game protocol:
+ * https://github.com/alcestes/scribble-java/blob/linear-channels/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ *
+ * @author Alceste Scalas */
+package lchannels.examples.threebuyer.protocol.seller
+
+import scala.concurrent.duration.Duration
+import lchannels._
+import lchannels.examples.threebuyer.protocol.binary
+import java.time.ZonedDateTime
+
+// Local type for role Seller:
+// Alice&{Title(String).Alice⊕{QuoteA(Int).Bob⊕{QuoteB(Int).Bob&{OkS(Unit).Bob&{Address(String).Bob⊕{Deliver(ZonedDateTime).end}}, QuitS(Unit).end}}}}
+
+// Input message types for multiparty sessions (autogenerated by Scribble)
+case class Title(p: String, cont: MPQuoteA)
+sealed abstract class MsgMPOkSOrQuitS
+case class OkS(p: Unit, cont: MPAddress) extends MsgMPOkSOrQuitS
+case class QuitS(p: Unit) extends MsgMPOkSOrQuitS
+case class Address(p: String, cont: MPDeliver)
+
+// Output message types for multiparty sessions (autogenerated by Scribble)
+case class QuoteA(p: Int)
+case class QuoteB(p: Int)
+case class Deliver(p: ZonedDateTime)
+
+// Multiparty session classes (autogenerated by Scribble)
+case class MPTitle(alice: In[binary.Title], bob: Out[binary.QuoteB]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ alice.receive(timeout) match {
+ case m @ binary.Title(p) => {
+ Title(p, MPQuoteA(m.cont, bob))
+ }
+ }
+ }
+}
+
+case class MPQuoteA(alice: Out[binary.QuoteA], bob: Out[binary.QuoteB]) {
+ def send(v: QuoteA) = {
+ val cnt = alice ! binary.QuoteA(v.p)
+ MPQuoteB(cnt, bob)
+ }
+}
+
+case class MPQuoteB(alice: Unit, bob: Out[binary.QuoteB]) {
+ def send(v: QuoteB) = {
+ val cnt = bob !! binary.QuoteB(v.p)_
+ MPOkSOrQuitS(alice, cnt)
+ }
+}
+
+case class MPOkSOrQuitS(alice: Unit, bob: In[binary.OkSOrQuitS]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ bob.receive(timeout) match {
+ case m @ binary.OkS(p) => {
+ OkS(p, MPAddress(alice, m.cont))
+ }
+ case m @ binary.QuitS(p) => {
+ QuitS(p)
+ }
+ }
+ }
+}
+
+case class MPAddress(alice: Unit, bob: In[binary.Address]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ bob.receive(timeout) match {
+ case m @ binary.Address(p) => {
+ Address(p, MPDeliver(alice, m.cont))
+ }
+ }
+ }
+}
+
+case class MPDeliver(alice: Unit, bob: Out[binary.Deliver]) {
+ def send(v: Deliver) = {
+ val cnt = bob ! binary.Deliver(v.p)
+ ()
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Actor.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Actor.scala
new file mode 100644
index 0000000..fca6337
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Actor.scala
@@ -0,0 +1,409 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.annotation.meta.field
+import scala.concurrent.{blocking, ExecutionContext, Future}
+import scala.concurrent.duration.{Duration, FiniteDuration}
+import scala.util.{Try, Success}
+
+import java.util.concurrent.{LinkedTransferQueue => Fifo}
+
+import akka.typed.{ActorRef, Props}
+import akka.typed.ScalaDSL.{Stopped, Partial, Total}
+
+/** The medium of actor-based channels. */
+case class Actor()
+
+protected[lchannels] object Defaults {
+ import java.util.concurrent.atomic.AtomicReference
+ import akka.actor.ActorSystem
+
+ private val exCtx = new AtomicReference[ExecutionContext]()
+ private val actorSys = new AtomicReference[akka.actor.ActorSystem]()
+
+ def setExCtx(ec: ExecutionContext): Unit = exCtx.set(ec)
+
+ def setExCtxIfUnset(ec: ExecutionContext): Boolean = {
+ exCtx.compareAndSet(null, ec)
+ }
+
+ def getExCtx(ec: ExecutionContext): ExecutionContext = {
+ if (ec == null) {
+ // Try to use the default
+ exCtx.get() match {
+ case null => {
+ // FIXME: more descriptive exception here
+ throw new IllegalStateException("Cannot determine ExecutionContext")
+ }
+ case ctx => ctx
+ }
+ } else {
+ ec
+ }
+ }
+
+ def setActorSys(as: ActorSystem): Unit = actorSys.set(as)
+
+ def setActorSysIfUnset(as: ActorSystem): Boolean = {
+ actorSys.compareAndSet(null, as)
+ }
+
+ def getActorSys(as: ActorSystem): ActorSystem = {
+ if (as == null) {
+ // Try to use the default
+ actorSys.get() match {
+ case null => {
+ // FIXME: more descriptive exception here
+ throw new IllegalStateException("Cannot determine ActorSystem")
+ }
+ case sys => sys
+ }
+ } else {
+ as
+ }
+ }
+}
+
+/** Channels that implement message delivery by automatically spawning
+ * Akka Typed actors. */
+object ActorChannel {
+ /** Set the default execution context for actor-based channels.
+ *
+ * This default must be provided before using `ActorChannel`s over a network.
+ */
+ def setDefaultEC(ec: ExecutionContext) = Defaults.setExCtx(ec)
+
+ /** Set the default execution context for actor-based channels,
+ * if it was not already set.
+ *
+ * This default must be provided before using `ActorChannel`s over a network.
+ *
+ * @return `true` if the default is updated, `false` if it was already set
+ */
+ def setDefaultECIfUnset(ec: ExecutionContext): Boolean = {
+ Defaults.setExCtxIfUnset(ec)
+ }
+
+ /** Set the default actor system for actor-based channels.
+ *
+ * This default must be provided before using `ActorChannel`s over a network.
+ */
+ def setDefaultAS(as: akka.actor.ActorSystem) = Defaults.setActorSys(as)
+
+ /** Set the default execution context for actor-based channels,
+ * if it was not already set.
+ *
+ * This default must be provided before using `ActorChannel`s over a network.
+ *
+ * @return `true` if the default is updated, `false` if it was already set
+ */
+ def setDefaultASIfUnset(as: akka.actor.ActorSystem): Boolean = {
+ Defaults.setActorSysIfUnset(as)
+ }
+
+ /** Create a pair of actor-based I/O channel endpoints.
+ *
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor context for internal actor management
+ */
+ def factory[T]()(implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): (ActorIn[T],
+ ActorOut[T]) = {
+ import akka.typed.Ops
+ val bref = Ops.ActorSystemOps(as).spawn(
+ Props(behaviors.bridgeBeh[T]))
+
+ (new ActorIn[T](bref), new ActorOut[T](bref)(Defaults.getExCtx(ec),
+ Defaults.getActorSys(as)))
+ }
+
+ /** Create a pair of actor-based I/O channel endpoints, with a specific name.
+ *
+ * Unlike [[factory[T]()*]], this method allows to assign a meaningful name
+ * to the actor giving access to returned I/O channel endpoints: this is
+ * reflected in their Actor Paths.
+ *
+ * @see [[ActorIn.path]] and [[ActorOut.path]]
+ *
+ * @param name Name of the Akka actor giving access to the returned
+ * actor endpoints.
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor context for internal actor management
+ */
+ def factory[T](name: String)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): (ActorIn[T],
+ ActorOut[T]) = {
+ import akka.typed.Ops
+ val bref = Ops.ActorSystemOps(as).spawn(
+ Props(behaviors.bridgeBeh[T]), name)
+
+ (new ActorIn[T](bref), new ActorOut[T](bref)(Defaults.getExCtx(ec),
+ Defaults.getActorSys(as)))
+ }
+
+ /** Spawn two functions as threads communicating via a pair of actor-based
+ * channel endpoints.
+ *
+ * This method invokes [[factory[T]()*]] to create a pair of channel endpoints
+ * `(in,out)`, and then spawns `p1(in)` and `p2(out)`.
+ *
+ * @return A pair of `Future`s `(f1, f2)`, completed respectively when
+ * `p1(in)` and `p2(out)` terminate.
+ *
+ * @param p1 Function using the input channel endpoint
+ * @param p2 Function using the output channel endpoint
+ * @param ec Execution context where the `p1` and `p2` will run
+ * @param as Actor context for internal actor management
+ */
+ def parallel[T, R1, R2](p1: ActorIn[T] => R1,
+ p2: ActorOut[T] => R2)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): (Future[R1],
+ Future[R2]) ={
+ val (in, out) = factory[T]()
+ ( Future { blocking { p1(in) } }, Future { blocking { p2(out) } } )
+ }
+}
+
+package object behaviors {
+ protected[lchannels] def bridgeBeh[T] = Total[ValueOrDest[T]] {
+ case Value(v) => Partial {
+ case Dest(ref) => {
+ ref ! v
+ Stopped
+ }
+ }
+ case Dest(ref) => Partial {
+ case Value(v) => {
+ ref ! v
+ Stopped
+ }
+ }
+ }
+}
+
+private sealed abstract class ValueOrDest[T]
+private case class Value[T](v: Try[T]) extends ValueOrDest[T]
+private case class Dest[T](ref: ActorRef[Try[T]]) extends ValueOrDest[T]
+
+/** Actor-based input channel endpoint,
+ * usually created through the [[[ActorIn$.apply* companion object]]]
+ * or via [[ActorChannel.factory]].
+ */
+// FIXME: with some fiddling, we should make ActorIn covariant on T
+@SerialVersionUID(1L)
+protected[lchannels] class ActorIn[T](bref: ActorRef[Dest[T]])
+ (@(transient @field) implicit val as: akka.actor.ActorSystem)
+ extends medium.In[Actor, T] with Serializable {
+ // We have to reimplement usage flags in a serializable way
+ private var used = false
+ protected final def _markAsUsed() : Unit = synchronized {
+ if (used) {
+ throw new lchannels.AlreadyUsed()
+ }
+ used = true
+ }
+
+ /** Return the path of the Akka actor giving access to the channel endpoint
+ *
+ * The path allows to (remotely) proxy the channel endpoint,
+ * via [[ActorIn $.apply]].
+ */
+ def path: akka.actor.ActorPath = bref.path
+
+ @transient
+ private var _ref: ActorRef[Try[T]] = null
+
+ override def receive(implicit atMost: Duration) = {
+ _markAsUsed()
+ // FIXME: can be definitely optimised
+ import akka.typed.Ops
+ val (fifo, hT) = buildPFBeh
+ _ref = Ops.ActorSystemOps(Defaults.getActorSys(as)).spawn(Props(hT))
+ bref ! Dest(_ref)
+ if (atMost.isFinite) {
+ // recvd value has type Try[T]
+ val v = fifo.poll(atMost.length, atMost.unit)
+ if (v == null) {
+ throw new java.util.concurrent.TimeoutException(f"Input timed out after ${atMost}")
+ }
+ v.get.asInstanceOf[T]
+ } else {
+ fifo.take().get.asInstanceOf[T] // recvd value has type T
+ }
+ }
+
+ private def buildPFBeh: (Fifo[Try[T]], Total[Try[T]]) = {
+ val fifo = new Fifo[Try[T]]()
+ val hT = Total[Try[T]]( v => { fifo.put(v); Stopped } )
+ (fifo, hT)
+ }
+
+ override def toString() = {
+ f"ActorIn@${hashCode} ← ${if (_ref != null) _ref.path else null} (bridge: ${bref.path})"
+ }
+}
+
+/** Actor-based input channel endpoint. */
+object ActorIn {
+ private[lchannels] def apply[T](ref: ActorRef[Dest[T]])
+ (implicit as: akka.actor.ActorSystem): ActorIn[T] = {
+ new ActorIn(ref)
+ }
+ private[lchannels] def apply[T](ref: akka.actor.ActorRef)
+ (implicit as: akka.actor.ActorSystem): ActorIn[T] = {
+ apply(ActorRef[Dest[T]](ref))
+ }
+ /** Proxy an [[ActorIn]] instance reachable through the given Akka actor path
+ *
+ * @param path Actor path, matching the value of some [[ActorIn.path]]
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor system for internal actor handling
+ * @param timeout Max wait time for path resolution
+ */
+ def apply[T](path: akka.actor.ActorPath)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem,
+ timeout: FiniteDuration): ActorIn[T] = {
+ apply(resolvePath(path, timeout))
+ }
+
+ /** Proxy an [[ActorIn]] instance reachable through the given Akka actor path
+ * (given as a string).
+ *
+ * @param path Actor path, matching the value of some [[ActorIn.path]]
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor system for internal actor handling
+ * @param timeout Max wait time for path resolution
+ */
+ def apply[T](path: String)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem,
+ timeout: FiniteDuration): ActorIn[T] = {
+ apply(akka.actor.ActorPaths.fromString(path))
+ }
+
+ private[lchannels] def resolvePath(path: akka.actor.ActorPath,
+ timeout: FiniteDuration)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): akka.actor.ActorRef = {
+ import akka.actor.{ActorIdentity, Identify}
+ import akka.pattern.ask
+ import scala.concurrent.Await
+
+ val sel = Await.result(
+ as.actorSelection(path).resolveOne(timeout), timeout)
+ val ref = for {
+ ans <- ask(sel, Identify(1))(timeout).mapTo[ActorIdentity]
+ } yield ans.getRef
+
+ Await.result(ref, timeout)
+ }
+}
+
+/** Actor-based output channel endpoint,
+ * usually created through the [[[ActorOut$.apply* companion object]]]
+ * or via [[ActorChannel.factory]].
+ */
+@SerialVersionUID(1L)
+protected[lchannels] class ActorOut[-T](bref: ActorRef[Value[T]])
+ (implicit @(transient @field) val ec: ExecutionContext,
+ @(transient @field) val as: akka.actor.ActorSystem)
+ extends medium.Out[Actor, T] with Serializable {
+ // We have to reimplement usage flags in a serializable way
+ private var used = false
+ protected final def _markAsUsed() : Unit = synchronized {
+ if (used) {
+ throw new lchannels.AlreadyUsed()
+ }
+ used = true
+ }
+
+ /** Return the path of the Akka actor giving access to the channel endpoint
+ *
+ * The path allows to (remotely) proxy the channel endpoint,
+ * via [[ActorOut$.apply]].
+ */
+ def path: akka.actor.ActorPath = bref.path
+
+ override def send(v: T) = synchronized {
+ _markAsUsed()
+ bref ! Value(Success(v))
+ }
+
+ override def create[U](): (ActorIn[U], ActorOut[U]) = {
+ ActorChannel.factory[U]()(Defaults.getExCtx(ec), Defaults.getActorSys(as))
+ }
+
+ override def toString() = {
+ f"ActorOut@${hashCode} → ${bref.path}"
+ }
+}
+
+/** Actor-based output channel endpoint. */
+object ActorOut {
+ private[lchannels] def apply[T](ref: ActorRef[Value[T]])
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): ActorOut[T] = {
+ new ActorOut(ref)
+ }
+ private[lchannels] def apply[T](ref: akka.actor.ActorRef)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem): ActorOut[T] = {
+ apply(ActorRef[Value[T]](ref))
+ }
+
+ /** Proxy an [[ActorOut]] instance reachable through the given Akka actor path
+ *
+ * @param path Actor path, matching the value of some [[ActorIn.path]]
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor system for internal actor handling
+ * @param timeout Max wait time for path resolution
+ */
+ def apply[T](path: akka.actor.ActorPath)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem,
+ timeout: FiniteDuration): ActorOut[T] = {
+ apply(ActorIn.resolvePath(path, timeout))
+ }
+ /** Proxy an [[ActorOut]] instance reachable through the given Akka actor path
+ * (given as a string).
+ *
+ * @param path Actor path, matching the value of some [[ActorIn.path]]
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ * @param as Actor system for internal actor handling
+ * @param timeout Max wait time for path resolution
+ */
+ def apply[T](path: String)
+ (implicit ec: ExecutionContext,
+ as: akka.actor.ActorSystem,
+ timeout: FiniteDuration): ActorOut[T] = {
+ apply(akka.actor.ActorPaths.fromString(path))
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Channel.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Channel.scala
new file mode 100644
index 0000000..718d53e
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Channel.scala
@@ -0,0 +1,249 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.concurrent.{ExecutionContext, Future, Promise}
+import scala.concurrent.duration.Duration
+import scala.util.{Try, Success, Failure}
+
+/** Base abstract class for all channel endpoints. */
+abstract class Channel[D <: Direction] {
+ @transient // Serializable sub-classes might not be able to use this field
+ private val used = new java.util.concurrent.atomic.AtomicBoolean(false)
+
+ protected final def markAsUsed() : Unit = {
+ if (!used.compareAndSet(false, true)) {
+ throw new lchannels.AlreadyUsed()
+ }
+ }
+}
+
+/** Signals a double usage of an input or output channel endpoint. */
+class AlreadyUsed(message: String = "I/O endpoint already used")
+ extends IllegalStateException(message)
+
+/** Base abstract class for linear input channel endpoints. */
+abstract class In[+T] extends Channel[Receive] {
+ private var _future: Future[_] = null // Will actually be Future[T]
+ /** Return a future that will be completed when the channel endpoint
+ * receives a value, or incurs in an input error.
+ */
+ def future(implicit ec: ExecutionContext) = synchronized {
+ if (_future == null) {
+ // FIXME: maybe the duration below should be a parameter (implicit?)
+ _future = Future { receive(Duration.Inf) }
+ }
+ // This cast is safe: the returned future only retrieves a T-typed value
+ _future.asInstanceOf[Future[T]]
+ }
+
+ /** Receive and return a message, blocking until its arrival.
+ *
+ * The default implementation of this method corresponds to
+ * [[receive(implicit atMost:*]] with an infinite timeout value.
+ *
+ * @throws AlreadyUsed if the channel endpoint was already used for input
+ * @throws java.lang.IllegalArgumentException if `atMost` is Duration.Undefined
+ * @throws java.lang.InterruptedException if the current thread is interrupted while waiting
+ * @throws java.util.concurrent.TimeoutException if after waiting for `atMost`, no message arrives
+ * @throws scala.util.control.NonFatal in case of other errors (e.g. deserialization or network issues)
+ */
+ def receive(): T = {
+ receive(Duration.Inf)
+ }
+
+ /** Receive and return a message, blocking until its arrival.
+ *
+ * @param atMost Maximum wait time
+ *
+ * @throws AlreadyUsed if the channel endpoint was already used for input
+ * @throws java.lang.IllegalArgumentException if `atMost` is Duration.Undefined
+ * @throws java.lang.InterruptedException if the current thread is interrupted while waiting
+ * @throws java.util.concurrent.TimeoutException if after waiting for `atMost`, no message arrives
+ * @throws scala.util.control.NonFatal in case of other errors (e.g. deserialization or network issues)
+ */
+ def receive(implicit atMost: Duration): T
+
+ /** Receive and return message, blocking until its arrival, or until a failure.
+ *
+ * The default implementation of this method corresponds to
+ * [[tryReceive(implicit atMost:*]] with an infinite timeout value.
+ *
+ * Once a message `msg` is received, this method returns`Success(msg)`.
+ * In case of errors, the method returns `Failure(exception)`.
+ *
+ * @param atMost Maximum wait time
+ */
+ def tryReceive(): Try[T] = {
+ try {
+ Success(receive())
+ } catch {
+ case scala.util.control.NonFatal(e) => Failure(e)
+ }
+ }
+
+ /** Receive and return message, blocking until its arrival, or until a failure.
+ *
+ * Once a message `msg` is received, this method returns`Success(msg)`.
+ * In case of errors, the method returns `Failure(exception)`.
+ *
+ * @param atMost Maximum wait time
+ */
+ def tryReceive(implicit atMost: Duration): Try[T] = {
+ try {
+ Success(receive)
+ } catch {
+ case scala.util.control.NonFatal(e) => Failure(e)
+ }
+ }
+
+ /** Wait for a message, pass it as argument to `f`, get the return value.
+ *
+ * Once a message `msg` is received, this method returns `f(msg)`.
+ *
+ * @param atMost Maximum wait time
+ *
+ * @throws java.lang.IllegalArgumentException if `atMost` is Duration.Undefined
+ * @throws java.lang.InterruptedException if the current thread is interrupted while waiting
+ * @throws java.util.concurrent.TimeoutException if after waiting for `atMost`, no message arrives
+ * @throws scala.util.control.NonFatal in case of other errors (e.g. deserialization or network issues)
+ */
+ def ?[R](f: T => R)(implicit atMost: Duration): R = {
+ f(receive)
+ }
+
+ /** Wait for a message or error, pass it as argument to `f`, get the return value.
+ *
+ * Once a message `msg` is received, this method returns
+ * `f(Success(msg))`. In case of errors, the method returns
+ * `f(Failure(exception))`.
+ *
+ * @param atMost Maximum wait time
+ */
+ def ??[R](f: Try[T] => R)(implicit atMost: Duration): R = {
+ f(tryReceive)
+ }
+}
+
+/** Base abstract class for output channel endpoints. */
+abstract class Out[-T] extends Channel[Send] {
+ private var _promise: Promise[_] = null // Will actually be Promise[T]
+ /** Return a promise that, once completed with a value `v`,
+ * causes `v` to be sent along this channel endpoint. */
+ def promise[U <: T] = synchronized {
+ if (_promise == null) {
+ _promise = Promise[Any] // Will be actually used as a Promise[T]
+ }
+ // The following cast is safe: the returned promise can only
+ // be completed with U-typed values, which are also T-typed
+ _promise.asInstanceOf[Promise[U]]
+ }
+
+ /** Return a pair of input/output linear channel endpoints */
+ def create[U](): (In[U], Out[U])
+
+/** Return a pair of input/output linear channel endpoints,
+ * with the ''input'' one meant to be ''used'' to receive a value,
+ * and the ''output'' meant to be ''sent'' as continuation
+ * within a session.
+ *
+ * This method is mainly used internally,
+ * e.g. by `!!`.
+ * The main difference wrt. [[create]] is that
+ * the returned channel endpoints might be optimized
+ * for the use described above; as a consequence,
+ * their behavior is undefined for other uses
+ * (e.g., if the input endpoint is sent as continuation in a session).
+ *
+ * The default implementation is an alias for [[create[U]()*]].
+ * Only use and/or override this method if you really know what you are doing.
+ */
+ def createContIn[U](): (In[U], Out[U]) = create[U]()
+
+ /** Return a pair of input/output linear channel endpoints,
+ * with the ''input'' one meant to be ''sent'' as continuation
+ * within session,
+ * and the ''output'' meant to be ''used'' to send a value.
+ *
+ * This method is the "dual" of [[createContIn]], and similar remarks hold.
+ */
+ def createContOut[U](): (In[U], Out[U]) = create[U]()
+
+ /** Send a message.
+ *
+ * @param msg Message to be sent
+ *
+ * @throws AlreadyUsed if the channel endpoint was already used for output
+ */
+ def send(msg: T): Unit
+
+ /** Alias for [[send]]. */
+ def !(msg: T) = send(msg)
+
+ /** Create a pair of `U`-typed channel endpoints `(i,o)`,
+ * send the return value of `f(i)`, and return `o`.
+ *
+ * This method automates channel creation in continuation-passing-style
+ * protocols,
+ * when the ''input'' endpoint must be sent as part of a `T`-typed message
+ * (as returned by `f(i)`),
+ * and the ``output`` endpoint is later used to continue the interaction.
+ *
+ * @param f Function that, when applied to an input channel endpoint,
+ * returns the message to be sent.
+ */
+ def !: Out[U] = {
+ val (cin, cout) = createContOut[U]()
+ this ! f(cin)
+ cout
+ }
+
+ /** Create a pair of `U`-typed channel endpoints `(i,o)`,
+ * send the return value of `f(o)`, and return `i`.
+ *
+ * This method automates channel creation in continuation-passing-style
+ * protocols,
+ * when the ''output'' endpoint must be sent as part of a `T`-typed message
+ * (as returned by `f(i)`),
+ * and the ``input`` endpoint is later used to continue the interaction.
+ *
+ * @param f Function that, when applied to an output channel endpoint,
+ * returns the message to be sent.
+ */
+ def !: In[U] = {
+ val (cin, cout) = createContIn[U]()
+ this ! f(cout)
+ cin
+ }
+}
+
+/** Linear channel endpoint without input/output capabilities.
+ *
+ * For most practical purposes, instances of this object can be often replaced
+ * with instances of `Unit`.
+ * */
+case class End() extends Channel[None]
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Local.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Local.scala
new file mode 100644
index 0000000..dfc05cf
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Local.scala
@@ -0,0 +1,86 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.concurrent.{Await, blocking, ExecutionContext, Future, Promise}
+import scala.concurrent.duration.Duration
+
+/** The medium of local channel endpoints. */
+case class Local()
+
+/** Simple implementation of local channel endpoints,
+ * based on Scala `Promise`s/`Future`s. */
+object LocalChannel {
+ /** Create a pair of linear I/O channel endpoints, for local use. */
+ def factory[T](): (LocalIn[T], LocalOut[T]) = {
+ val promise = scala.concurrent.Promise[T]()
+ val future = promise.future
+ (new LocalIn[T](future), new LocalOut[T](promise))
+ }
+
+ /** Spawn two functions as threads communicating via a pair of local
+ * channel endpoints.
+ *
+ * This method invokes [[factory]] to create a pair of channel endpoints
+ * `(in,out)`, and then spawns `p1(in)` and `p2(out)`.
+ *
+ * @return A pair of `Future`s `(f1, f2)`, completed respectively when
+ * `p1(in)` and `p2(out)` terminate.
+ *
+ * @param p1 Function using the input channel endpoint
+ * @param p2 Function using the output channel endpoint
+ * @param ec Execution context where the `p1` and `p2` will run
+ */
+ def parallel[T, R1, R2](p1: LocalIn[T] => R1,
+ p2: LocalOut[T] => R2)
+ (implicit ec: ExecutionContext): (Future[R1], Future[R2]) = {
+ val (in, out) = factory[T]()
+ ( Future { blocking { p1(in) } }, Future { blocking { p2(out) } } )
+ }
+}
+
+/** Local input channel endpoint, usually created via [[LocalChannel.factory]]. */
+class LocalIn[+T](val future: Future[T]) extends medium.In[Local, T] {
+ override def receive(implicit atMost: Duration): T = {
+ Await.result[T](future, atMost)
+ }
+}
+
+/** Local output channel endpoint, usually created via [[LocalChannel.factory]]. */
+class LocalOut[-T](p: Promise[T]) extends medium.Out[Local, T] {
+ override def promise[U <: T] = {
+ // The following cast is safe: the returned promise can only
+ // be completed with U-typed values, which are also T-typed
+ p.asInstanceOf[Promise[U]]
+ }
+
+ override def create[U]() = LocalChannel.factory[U]()
+
+ override def send(msg: T): Unit = {
+ promise success msg
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Medium.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Medium.scala
new file mode 100644
index 0000000..fa43b46
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Medium.scala
@@ -0,0 +1,57 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.medium
+
+abstract class In[M, +T] extends lchannels.In[T]
+
+abstract class Out[M, -T] extends lchannels.Out[T] {
+ override def create[U](): (In[M, U], Out[M, U])
+
+ /** Medium-constrained version of [[lchannels.Out.createContIn]]. */
+ override def createContIn[U](): (In[M, U], Out[M, U]) = create[U]()
+
+ /** Medium-constrained version of [[lchannels.Out.createContOut]]. */
+ override def createContOut[U](): (In[M, U], Out[M, U]) = create[U]()
+
+ // FIXME: we would ideally merge ! and !!, but Scala gets confused
+ // NOTE: !! works better with messages defined as curried case classes
+ // See: http://pchiusano.blogspot.co.uk/2011/05/making-most-of-scalas-extremely-limited.html
+ /** Medium-constrained version of [[lchannels.Out]].`!!`. */
+ def !: Out[M, U] = {
+ val (cin, cout) = createContOut[U]()
+ this ! f(cin)
+ cout
+ }
+ /** Medium-constrained version of [[lchannels.Out]].`!!`. */
+ def !: In[M, U] = {
+ val (cin, cout) = createContIn[U]()
+ this ! f(cout)
+ cin
+ }
+}
+
+case class End[M]() extends lchannels.Channel[lchannels.None]
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Protocol.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Protocol.scala
new file mode 100644
index 0000000..e9d8977
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Protocol.scala
@@ -0,0 +1,110 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.protocol
+
+import lchannels.{Channel, Direction, Send, Receive}
+import lchannels.medium.{In, Out, End}
+
+abstract class Message[M, D <: Direction] {
+ val cont: Channel[_]
+}
+
+protected[lchannels] trait MessageSeq[M, D <: Direction, C <: Channel[_]] {
+ self: Message[M, D] =>
+ val cont: C
+}
+
+trait SeqSendSend[M, T1 <: Message[M, Send]]
+ extends MessageSeq[M, Send, In[M, T1]] {
+ self: Message[M, Send] =>
+ override val cont: In[M, T1]
+}
+trait SeqSendReceive[M, T1 <: Message[M, Receive]]
+ extends MessageSeq[M, Send, Out[M, T1]] {
+ self: Message[M, Send] =>
+ override val cont: Out[M, T1]
+}
+trait SeqSendEnd[M]
+ extends MessageSeq[M, Send, End[M]] {
+ self: Message[M, Send] =>
+ override val cont = End[M]()
+}
+trait SeqReceiveSend[M, T1 <: Message[M, Send]]
+ extends MessageSeq[M, Receive, Out[M, T1]] {
+ self: Message[M, Receive] =>
+ override val cont: Out[M, T1]
+}
+trait SeqReceiveReceive[M, T1 <: Message[M, Receive]]
+ extends MessageSeq[M, Receive, In[M, T1]] {
+ self: Message[M, Receive] =>
+ override val cont: In[M, T1]
+}
+
+trait SeqReceiveEnd[M]
+ extends MessageSeq[M, Receive, End[M]] {
+ self: Message[M, Receive] =>
+ override val cont = End[M]()
+}
+
+object Protocol {
+ import lchannels.{Local, LocalChannel}
+ import scala.concurrent.{blocking, ExecutionContext, Future}
+ import scala.concurrent.duration.Duration
+
+ def serve[Msg <: Message[Local, Send]](
+ handler: (In[Local, Msg], Duration) => Unit
+ )(implicit ctx: ExecutionContext,
+ timeout: Duration): Out[Local, Msg] = {
+ serve(LocalChannel.factory[Msg], handler)
+ }
+
+ def serve[M, Msg <: Message[M, Send]](
+ factory: () => (In[M, Msg], Out[M, Msg]),
+ handler: (In[M, Msg], Duration) => Unit
+ )(implicit ctx: ExecutionContext,
+ timeout: Duration): Out[M, Msg] = {
+ val (cin, cout) = factory()
+ Future { blocking { handler(cin, timeout) } }
+ cout
+ }
+
+ def serve[Msg <: Message[Local, Receive]](
+ handler: (Out[Local, Msg], Duration) => Unit
+ )(implicit ctx: ExecutionContext,
+ timeout: Duration): In[Local, Msg] = {
+ serve(LocalChannel.factory[Msg], handler)
+ }
+ def serve[M, Msg <: Message[M, Receive]](
+ factory: () => (In[M, Msg], Out[M, Msg]),
+ handler: (Out[M, Msg], Duration) => Unit
+ )(implicit ctx: ExecutionContext,
+ timeout: Duration): In[M, Msg] = {
+ val (cin, cout) = factory()
+ Future { blocking { handler(cout, timeout) } }
+ cin
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Queue.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Queue.scala
new file mode 100644
index 0000000..9857359
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Queue.scala
@@ -0,0 +1,129 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.concurrent.{blocking, ExecutionContext, Future}
+import scala.concurrent.duration.Duration
+
+import java.util.concurrent.{LinkedTransferQueue => Fifo}
+
+/** Channel endpoints for local use, based on Java `LinkedTransferQueue`s.
+ *
+ * Queue-based channels are (almost) a drop-in replacement for
+ * [[LocalChannel]]s. They are optimized for bypassing [[Out.promise]] and
+ * [[In.future]] whenever possible, e.g. when a program mostly calls
+ * [[Out.send]] and [[In.receive]]; as a consequence, if [[QueueOut.promise]]
+ * or [[QueueIn.future]] are used, the performance will be impacted.
+ *
+ * NOTE : due to limitations of Java `LinkedTransferQueue`s,
+ * invoking [[QueueIn.receive]] on a `QueueIn[Null]` instance with a
+ * finite wait time will cause a spurious timeout error.
+ * If you really need channel endpoints that carry `Null` values, you should
+ * use [[LocalChannel]]s.
+ */
+object QueueChannel {
+ /** Create a pair of queue-based I/O channel endpoints.
+ *
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ */
+ def factory[T]()(implicit ec: ExecutionContext): (QueueIn[T], QueueOut[T]) = {
+ val fifo1 = new Fifo[Any]()
+ val fifo2 = new Fifo[Any]()
+ (new QueueIn[T](fifo1),
+ new QueueOut[T](fifo1, fifo2))
+ }
+
+ /** Spawn two functions as threads communicating via a pair of queue-based
+ * channel endpoints.
+ *
+ * This method invokes [[factory]] to create a pair of channel endpoints
+ * `(in,out)`, and then spawns `p1(in)` and `p2(out)`.
+ *
+ * @return A pair of `Future`s `(f1, f2)`, completed respectively when
+ * `p1(in)` and `p2(out)` terminate.
+ *
+ * @param p1 Function using the input channel endpoint
+ * @param p2 Function using the output channel endpoint
+ * @param ec Execution context where the `p1` and `p2` will run
+ */
+ def parallel[T, R1, R2](p1: QueueIn[T] => R1,
+ p2: QueueOut[T] => R2)
+ (implicit ec: ExecutionContext): (Future[R1], Future[R2]) = {
+ val (in, out) = factory[T]()
+ ( Future { blocking { p1(in) } }, Future { blocking { p2(out) } } )
+ }
+}
+
+/** Queue-based input endpoint, usually created via [[QueueChannel.factory]]. */
+class QueueIn[+T](fifo: Fifo[Any])
+ (implicit ec: ExecutionContext) extends medium.In[Local, T] {
+ override def receive(): T = {
+ markAsUsed()
+ fifo.take().asInstanceOf[T]
+ }
+
+ override def receive(implicit atMost: Duration): T = {
+ markAsUsed()
+ if (atMost.isFinite) {
+ val v = fifo.poll(atMost.length, atMost.unit)
+ if (v == null) {
+ // NOTE: if a null value is received, we treat it as a timeout
+ throw new java.util.concurrent.TimeoutException(f"Input timed out after ${atMost}")
+ }
+ v.asInstanceOf[T]
+ } else {
+ fifo.take().asInstanceOf[T] // recvd value has type T
+ }
+ }
+}
+
+/** Queue-based output endpoint, usually created via [[QueueChannel.factory]]. */
+class QueueOut[-T](fifoW: Fifo[Any], fifoR: Fifo[Any])
+ (implicit ec: ExecutionContext) extends medium.Out[Local, T] {
+
+
+ override def send(msg: T): Unit = {
+ markAsUsed()
+ fifoW.put(msg)
+ }
+
+ override def create[U](): (QueueIn[U], QueueOut[U]) = {
+ QueueChannel.factory[U]()
+ }
+
+ override def createContIn[U](): (QueueIn[U], QueueOut[U]) = {
+ // Keep the same queues for reading and writing
+ (new QueueIn[U](fifoR),
+ new QueueOut[U](fifoR, fifoW))
+ }
+
+ override def createContOut[U](): (QueueIn[U], QueueOut[U]) = {
+ // Swap the read/write queues, so that we will not read the data we sent
+ (new QueueIn[U](fifoW),
+ new QueueOut[U](fifoW, fifoR))
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Socket.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Socket.scala
new file mode 100644
index 0000000..32b5760
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Socket.scala
@@ -0,0 +1,155 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.concurrent.duration.Duration
+
+import java.net.{Socket => JSocket}
+import java.io.{InputStream, OutputStream}
+
+/** The medium of socket-based channel endpoints. */
+case class Socket()
+
+/** Base class for socket management and (de)serialization of messages.
+ *
+ * This class assumes to have exclusive control over the given socket.
+ *
+ * @param socket Socket for sending/receiving data
+ */
+abstract class SocketManager(socket: JSocket) {
+ /** The `InputStream` of `socket`. */
+ protected val in: InputStream = {
+ socket.setSoTimeout(0) // Make the socket blocking by default
+ socket.getInputStream()
+ }
+
+ /** The `OutputStream` of `socket`. */
+ protected val out: OutputStream = socket.getOutputStream()
+
+ /** Read data from [[in]], deserialize an object and return it.
+ *
+ * @param atMost Maximum wait time
+ *
+ * @throws java.util.concurrent.TimeoutException if after waiting for `atMost`, no message arrives
+ * @throws Exception if a deserialization error occurs.
+ */
+ protected[lchannels] final def destreamer(atMost: Duration): Any = {
+ if (atMost.isFinite) {
+ socket.setSoTimeout(java.lang.Math.toIntExact(atMost.toMillis))
+ }
+ val recvd = destreamer()
+ if (atMost.isFinite) {
+ socket.setSoTimeout(0)
+ }
+ recvd
+ }
+
+ /** Read data from [[in]], deserialize an object and return it.
+ *
+ * @throws Exception if a deserialization error occurs.
+ */
+ def destreamer(): Any
+
+ /** Serialize an object and write it into [[out]].
+ *
+ * @param x Object to serialize.
+ */
+ def streamer(x: Any): Unit
+
+ /** Close the socket.
+ *
+ * You could derive this method to perform additional cleanup
+ * when closing the `StreamManager`.
+ */
+ def close(): Unit = { socket.close() }
+
+ /** Alias for [[close]]. */
+ final override def finalize() = close()
+
+ /** Create a pair of I/O socket-based channel endpoints,
+ * reading from `in` and writing to `out`.
+ *
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ */
+ def factory[T](): (SocketIn[T], SocketOut[T]) = {
+ (SocketIn[T](this), SocketOut[T](this))
+ }
+}
+
+/** Stream-based input channel endpoint, usually created
+ * through the [[[StreamIn$.apply* companion object]]]
+ * or via [[StreamManager.factory]].
+ */
+protected[lchannels] class SocketIn[T](sktm: SocketManager)
+ extends medium.In[Socket, T] {
+ override def receive() = {
+ sktm.destreamer().asInstanceOf[T]
+ }
+
+ override def receive(implicit atMost: Duration) = {
+ try {
+ sktm.destreamer(atMost).asInstanceOf[T]
+ } catch {
+ case e: java.net.SocketTimeoutException => {
+ throw new java.util.concurrent.TimeoutException(e.getMessage())
+ }
+ }
+ }
+}
+
+/** Stream-based input channel endpoint. */
+object SocketIn {
+ /** Return a socket-based input channel endpoint.
+ *
+ * @param strm Socket manager owning the input/output data streams
+ */
+ def apply[T](sktm: SocketManager) = {
+ new SocketIn[T](sktm)
+ }
+}
+
+/** Stream-based input channel endpoint, usually created
+ * through the [[[StreamOut$.apply* companion object]]]
+ * or via [[StreamManager.factory]].
+ */
+class SocketOut[-T](sktm: SocketManager)
+ extends medium.Out[Socket, T] {
+ override def send(x: T) = sktm.streamer(x)
+
+ override def create[U]() = sktm.factory()
+}
+
+/** Stream-based output channel endpoint. */
+object SocketOut {
+ /** Return a socket-based output channel endpoint.
+ *
+ * @param strm Socket manager owning the input/output data streams
+ */
+ def apply[T](sktm: SocketManager) = {
+ new SocketOut[T](sktm)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Stream.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Stream.scala
new file mode 100644
index 0000000..70b5527
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/Stream.scala
@@ -0,0 +1,140 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels
+
+import scala.concurrent.{Await, ExecutionContext, Future}
+import scala.concurrent.duration.Duration
+
+import java.io.{InputStream, OutputStream}
+
+/** The medium of stream-based channel endpoints. */
+case class Stream()
+
+/** Base class for stream management and (de)serialization of messages.
+ *
+ * @param in Stream of input data
+ * @param out Stream of output data
+ */
+abstract class StreamManager(in: InputStream, out: OutputStream) {
+ /** Read data from `in`, deserialize an object and return it.
+ *
+ * @param atMost Maximum wait time
+ * @param ec Execution context where an internal Future will run
+ *
+ * @throws java.util.concurrent.TimeoutException if after waiting for `atMost`, no message arrives
+ * @throws Exception if a deserialization error occurs.
+ */
+ protected[lchannels] final def destreamer(atMost: Duration)
+ (implicit ec: ExecutionContext): Any = {
+ Await.result(Future { destreamer() }, atMost)
+ }
+
+ /** Read data from `in`, deserialize an object and return it.
+ *
+ * @throws Exception if a deserialization error occurs.
+ */
+ def destreamer(): Any
+
+ /** Serialize an object and write it into `out`.
+ *
+ * @param x Object to serialize.
+ */
+ def streamer(x: Any): Unit
+
+ /** Close `in` and `out` streams.
+ *
+ * You could derive this method to perform additional cleanup
+ * when closing the `StreamManager`.
+ */
+ def close(): Unit = { in.close(); out.close() }
+
+ /** Alias for [[close]]. */
+ final override def finalize() = close()
+
+ /** Create a pair of I/O stream-based channel endpoints,
+ * reading from `in` and writing to `out`.
+ *
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ */
+ def factory[T]()(implicit ec: ExecutionContext): (StreamIn[T], StreamOut[T]) = {
+ (StreamIn[T](this), StreamOut[T](this))
+ }
+}
+
+/** Stream-based input channel endpoint, usually created
+ * through the [[[StreamIn$.apply* companion object]]]
+ * or via [[StreamManager.factory]].
+ */
+protected[lchannels] class StreamIn[T](strm: StreamManager)
+ (implicit ec: ExecutionContext)
+ extends medium.In[Stream, T] {
+ override def receive() = {
+ strm.destreamer().asInstanceOf[T]
+ }
+
+ override def receive(implicit atMost: Duration) = {
+ strm.destreamer(atMost).asInstanceOf[T]
+ }
+}
+
+/** Stream-based input channel endpoint. */
+object StreamIn {
+ /** Return a stream-based input channel endpoint.
+ *
+ * @param strm Stream manager owning the input/output data streams
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ */
+ def apply[T](strm: StreamManager)
+ (implicit ec: ExecutionContext) = {
+ new StreamIn[T](strm)
+ }
+}
+
+/** Stream-based input channel endpoint, usually created
+ * through the [[[StreamOut$.apply* companion object]]]
+ * or via [[StreamManager.factory]].
+ */
+class StreamOut[-T](strm: StreamManager)
+ (implicit ec: ExecutionContext)
+ extends medium.Out[Stream, T] {
+ override def send(x: T) = strm.streamer(x)
+
+ override def create[U]() = strm.factory()
+}
+
+/** Stream-based output channel endpoint. */
+object StreamOut {
+ /** Return a stream-based output channel endpoint.
+ *
+ * @param strm Stream manager owning the input/output data streams
+ * @param ec Execution context for internal `Promise`/`Future` handling
+ */
+ def apply[T](strm: StreamManager)
+ (implicit ec: ExecutionContext) = {
+ new StreamOut[T](strm)
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/package.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/package.scala
new file mode 100644
index 0000000..68decaa
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/package.scala
@@ -0,0 +1,46 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+
+package object lchannels {
+ /** Convenience trait allowing to distinguish the intended direction
+ * of a channel or a message type.
+ */
+ sealed trait Direction
+
+ /** Convenience trait for output channels,
+ * and message types whose instances should be sent.
+ */
+ trait Send extends Direction
+
+ /** Convenience trait for input channels,
+ * and message types whose instances should be received.
+ */
+ trait Receive extends Direction
+
+ /** Convenience trait for channels without I/O capabilities. */
+ trait None extends Direction
+}
diff --git a/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/util/Fifo.scala b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/util/Fifo.scala
new file mode 100644
index 0000000..5488fc9
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/lchannels/src/main/scala/lchannels/util/Fifo.scala
@@ -0,0 +1,97 @@
+// lchannels - session programming in Scala
+// Copyright (c) 2016, Alceste Scalas and Imperial College London
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+/** @author Alceste Scalas */
+package lchannels.util
+
+import scala.concurrent.ExecutionContext
+import scala.concurrent.duration.Duration
+
+import lchannels._
+
+///////////////////////////////////////////////////////////////////////////////
+// Session type (from output endpoint):
+// rec X . !Datum(T). X
+///////////////////////////////////////////////////////////////////////////////
+private case class Datum[T](value: T)(val cont: In[Datum[T]])
+
+/** Interface for FIFO queues. */
+trait Fifo[T] {
+ /** Return and remove the value at the head of the queue.
+ *
+ * This method blocks if the queue is empty,
+ * until a value can be retrieved, or the given timeout expires.
+ *
+ * @param timeout Maximum wait time when the queue is empty.
+ *
+ * @throws java.util.concurrent.TimeoutException if `timeout` expires
+ * @throws java.util.concurrent.InterruptedException if thread is interrupted
+ */
+ def read(implicit timeout: Duration): T
+
+ /** Append a value to the queue.
+ *
+ * @param value Value to be appended.
+ */
+ def write(value: T): Unit
+}
+
+/** Simple implementation of FIFO queue.
+ *
+ * @param factory Used internally to create [[In]]/[[Out]] instances.
+ */
+protected class FifoImpl[T](factory: () => (In[Datum[T]], Out[Datum[T]]))
+ extends Fifo[T] {
+ private[this] var (in, out) = factory()
+
+ private[this] val inLock = new Object() // Guards "in" above
+ private[this] val outLock = new Object() // Guards "out" above
+
+ def read(implicit timeout: Duration): T = inLock.synchronized {
+ val res = in.receive
+ in = res.cont
+ res.value
+ }
+
+ def write(value: T): Unit = outLock.synchronized {
+ out = out !! Datum[T](value)_
+ }
+}
+
+/** Simple FIFO queue. */
+object Fifo {
+ /** Return an empty FIFO, internally based on [[LocalChannel]]s. */
+ def apply[T](): Fifo[T] = {
+ new FifoImpl[T](LocalChannel.factory[Datum[T]])
+ }
+
+ /** Return an empty FIFO, internally based on [[QueueChannel]]s
+ *
+ * @param ec Execution context for [[QueueChannel]] creation.
+ */
+ def apply[T](ec: ExecutionContext): Fifo[T] = {
+ new FifoImpl[T](() => QueueChannel.factory[Datum[T]]()(ec))
+ }
+}
diff --git a/src/main/jbake/assets/docs/lchannels/scripts/benchmark.sh b/src/main/jbake/assets/docs/lchannels/scripts/benchmark.sh
new file mode 100644
index 0000000..fe872ca
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/scripts/benchmark.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+default_repetitions=1
+default_msgcnt=20000
+
+if [ -z "$1" ]; then
+ repetitions=$default_repetitions
+else
+ repetitions="$1"
+fi
+
+if [ -z "$2" ]; then
+ msgcnt=$default_msgcnt
+else
+ msgcnt="$2"
+fi
+
+# Increase the heap size, if a "serious" benchmark is being launched
+if [ $msgcnt -gt $default_msgcnt ]; then
+ heap="4g"
+else
+ heap="512m"
+fi
+
+for rep in `seq 1 $repetitions`; do
+ echo "*** Benchmarking: JVM instantiation $rep/$repetitions"
+ mkdir -p bench/$rep; # Create benchmarking directories
+ sbt -J-Xms$heap -J-Xmx$heap "project benchmarks" "run bench/$rep $msgcnt"
+ echo
+done
+
+# Concatenate benchmark files (skipping the first 2 lines with title/headers)
+echo "*** Concatenating benchmark results..."
+cp bench/1/* .
+
+for rep in `seq 2 $repetitions`; do
+ for f in `ls bench/$rep/*`; do
+ tail -n +3 $f >> `basename $f`
+ done
+done
+
+echo "*** Generating plots (in PDF format)..."
+for f in *.csv; do
+ pdfplot="`basename $f .csv`.pdf"
+ echo " $f -> $pdfplot"
+ ./scripts/plot-benchmark.py $f $pdfplot
+done
diff --git a/src/main/jbake/assets/docs/lchannels/scripts/greeting-server.py b/src/main/jbake/assets/docs/lchannels/scripts/greeting-server.py
new file mode 100644
index 0000000..06f4b5a
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/scripts/greeting-server.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+SERVER_HOST = '127.0.0.1'
+SERVER_PORT = 1337
+
+import re, socket
+
+MSG_GREET_RE = re.compile('''^GREET +(.+)''')
+MSG_QUIT_RE = re.compile('''^QUIT$''')
+
+def serve(srv):
+ while 1:
+ print '[S] Waiting for new connections...'
+ (s, address) = srv.accept()
+ sf = s.makefile('rw')
+ print '[S] New connection from {0}'.format(address)
+ handle_connection(sf)
+ print '[S] Closing connection'
+ sf.close(); s.close()
+
+def handle_connection(sf):
+ while 1:
+ print '[S] Waiting for request...'
+ req = sf.readline().strip()
+ print '[S] Received: "{0}"'.format(req)
+
+ m = MSG_GREET_RE.match(req)
+ if (m is not None):
+ ans = 'HELLO {0}'.format(m.group(1))
+ print '[S] Answering: "{0}"'.format(ans)
+ sf.write(ans); sf.write("\n"); sf.flush()
+ continue
+
+ m = MSG_QUIT_RE.match(req)
+ if (m is not None):
+ print '[S] End of session'
+ break
+
+ # No message regex was matched
+ print '[S] Invalid message'
+ break
+
+if (__name__ == '__main__'):
+ print '[S] Greeting server starting. Press Ctrl+C to quit'
+ srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Avoid TIME_WAIT
+ srv.bind((SERVER_HOST, SERVER_PORT))
+ print '[S] Listening on {0}:{1}'.format(SERVER_HOST, SERVER_PORT)
+ srv.listen(8)
+ serve(srv)
+ srv.close()
diff --git a/src/main/jbake/assets/docs/lchannels/scripts/plot-benchmark.py b/src/main/jbake/assets/docs/lchannels/scripts/plot-benchmark.py
new file mode 100644
index 0000000..a4c984f
--- /dev/null
+++ b/src/main/jbake/assets/docs/lchannels/scripts/plot-benchmark.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import matplotlib.pyplot as plotlib
+import numpy
+import sys
+
+DELIMITER = ','
+
+def makePlot(infile, outfile):
+ import matplotlib
+ matplotlib.rcParams.update({'font.size': 12})
+
+ (title, headers) = readTitleAndHeaders(infile)
+
+ data = numpy.genfromtxt(infile,
+ delimiter=DELIMITER,
+ skip_header=1, # Skip benchmark title
+ dtype=numpy.long) / 1000000.0
+
+ box_colours = ['ForestGreen', 'SkyBlue', 'Tan', 'Plum', 'ForestGreen', 'Maroon', 'ForestGreen']
+
+ locations = range(1, len(headers) + 1)
+
+ fig = plotlib.figure()
+ plot = plotlib.boxplot(data, widths=0.7, notch=True, positions=locations,
+ patch_artist=True,
+ sym='') # Do not print outliers
+
+ for box, colour in zip(plot['boxes'], box_colours):
+ plotlib.setp(box, #color='DarkMagenta',
+ linewidth=1,
+ facecolor=colour)
+
+ # plotlib.setp(plot['whiskers'], color='DarkMagenta', linewidth=1)
+ # plotlib.setp(plot['caps'], color='DarkMagenta', linewidth=1)
+ # plotlib.setp(plot['fliers'], color='OrangeRed', marker='o', markersize=3)
+ # plotlib.setp(plot['medians'], color='OrangeRed', linewidth=1)
+
+ plotlib.grid(axis='y', # set y-axis grid lines
+ linestyle='--', # use dashed lines
+ which='major', # only major ticks
+ color='lightgrey', # line colour
+ alpha=0.8) # make lines semi-translucent
+
+ plotlib.xticks(locations, # tick marks
+ headers, # labels
+ rotation=25) # rotate the labels
+
+ plotlib.ylabel('milliseconds') # y-axis label
+ plotlib.title(title, fontsize=12, fontweight='bold') # plot title
+
+ # plotlib.show() # render the plot
+ fig.savefig(outfile, bbox_inches='tight')
+
+def readTitleAndHeaders(infile):
+ f = open(infile)
+ title = f.readline()
+ headers = map(lambda x: x.replace(' ', "\n"),
+ f.readline().strip().split(DELIMITER))
+ f.close()
+ return (title, headers)
+
+if (__name__ == '__main__'):
+ infile = sys.argv[1]
+ outfile = sys.argv[2]
+ makePlot(infile, outfile)
diff --git a/src/main/jbake/assets/docs/scribble-java.html b/src/main/jbake/assets/docs/scribble-java.html
new file mode 100644
index 0000000..1eba5c5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble-java.html
@@ -0,0 +1,3816 @@
+
+
+
+
+
+
+ Scribble-Java tutorial
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Scribble-Java tutorial
+
+
+
+
+
+ This is a tutorial for using the Scribble-Java toolchain (for Scribble version 0.4).
+
+
+
+
+
+
+
+
+
+ Contents:
+
+
+
+
+
+ About this tutorial
+
+
+
+
+
+ Quick Start: the Adder application protocol
+
+
+ Protocol overview
+
+
+ Scribble global protocol
+
+
+
+ An Adder client
+
+
+
+ An Adder server
+
+
+
+
+
+
+
+
+ Scribble modules and global protocols
+
+
+ Scribble modules
+
+
+ Message signatures
+
+
+ Global protocol declarations
+
+
+ The message interaction statement
+
+
+ The choice statement
+
+
+
+ The do statement
+
+
+
+
+
+
+
+
+ Further global protocol features
+
+
+ Message signature name declarations
+
+
+ The connection interaction statement
+
+
+
+
+
+
+
+
+ Scribble protocol validation
+
+
+ Valid global protocols and well-formed modules
+
+
+ Characteristics of Endpoint FSMs
+
+
+ Safety and liveness of Scribble global protocols
+
+
+
+
+
+
+
+ Endpoint API generation
+
+
+ Endpoint API overview
+
+
+ Names as singleton types
+
+
+ State Channel API
+
+
+ Output state API
+
+
+ Receive state API
+
+
+ Accept state API
+
+
+ Disconnect state API
+
+
+ Linear usage of state channel instances
+
+
+
+
+
+
+
+ Endpoint implementation using Scribble-generated APIs
+
+
+ Connection establishment for non-explicit global protocols
+
+
+ Usage contract of Endpoint APIs and endpoint implementation safety
+
+
+
+
+
+
+
+ Building/installing Scribble-Java
+
+
+
+
+
+ Running the Scribble-Java command line tool
+
+
+
+
+
+
+
+
+
+
+
+ Scribble-Java is a toolchain for programming distributed applications in Java based on the theory of multiparty session types .
+
+
+
+ The main input and output of the toolchain are:
+
+
+
+
+
+
+
+ Useful links and resources
+
+
+
+
+
+ Scribble home page
+
+
+
+ N.B. some of the tools currently available from
+the home page are for an older version of Scribble than presented in
+this tutorial. Those include:
+
+
+
+ Eclipse integration
+
+
+ Message trace simulation
+
+
+ Browser based tooling
+
+
+
+
+
+
+ Scribble-Java open source repository: GitHub
+
+
+
+ Some Scribble protocol and Java endpoint program examples: GitHub
+
+
+
+
+
+
+
+
+
+ Tutorial version history
+
+
+
+
+
+
+ 0.4b
+
+
+ (Draft) revision for Scribble 0.4.
+
+
+
+
+
+
+
+
+ Upcoming in future tutorial versions
+
+
+
+
+
+ Scribble language:
+
+
+
+ Payload type and message signature name parameters for global protocol declarations
+
+
+ Messages in connection interaction statements
+
+
+
+
+ Endpoint API generation:
+
+
+
+ Branch callback API
+
+
+ Accept-branch API
+
+
+ Receive state futures
+
+
+ Abstract I/O state interfaces
+
+
+
+ Choice subtype interfaces
+
+
+
+
+
+
+ Host languages:
+
+
+
+ Scribble-Scala
+
+
+
+ Multiparty session delegation
+
+
+
+
+
+
+
+
+
+
+ Upcoming in future Scribble versions
+
+
+
+
+
+ Scribble language:
+
+
+
+ Protocol annotations
+
+
+
+ Message value assertions
+
+
+ Dynamic port actions (binary shared channel passing)
+
+
+
+
+
+
+ Endpoint API generation:
+
+
+
+ State name annotations
+
+
+ Event-driven API
+
+
+
+
+ Scribble Runtime API:
+
+
+ Additional transports: shared memory, HTTP
+
+
+
+
+ Host languages:
+
+
+
+ Scribble-Go
+
+
+
+ Role parameters for global protocol declarations
+
+
+
+
+
+
+
+
+
+
+ <
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ This quick start will run through the specification of a simple
+application protocol in Scribble, to Java implementations of client and
+server endpoints using the Scribble-generated APIs.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ We illustrate a client-server protocol for a network service that adds two numbers.
+
+
+
+ An outline of the protocol:
+
+
+
+ The client C may choose to send to the server S one of the following two messages.
+
+
+ An Add message with a payload of two Int s.
+
+
+
+ After receiving the Add , S sends to C a Res message with a payload of one Int (the sum of the received Int s).
+
+
+ Then C and S continue by "looping" back to the start of the protocol.
+
+
+
+
+ A Bye message with an empty payload.
+
+
+
+ The protocol ends for C after sending.
+
+
+ The protocol ends for S after receiving.
+
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Below we give a Scribble specification of the Adder application protocol.
+
+
+
+ This code is available from the Scribble GitHub repository:
+
+
+
+
+
+
+ Adder.scr
+
+ Lst. 2.1: Scribble specification of the Adder application protocol.
+
+
+
+ module tutorial . adder . Adder ; type <java> "java.lang.Integer" from "rt.jar" as Int ; global protocol Adder ( role C , role S ) { choice at C { Add ( Int , Int ) from C to S ; Res ( Int ) from S to C ; do Adder ( C , S ); } or { Bye () from C to S ; } }
+
+
+
+
+ We use the above example to give a brief overiew of Scribble syntax. The links give further details of each element.
+
+
+
+ The payload type declaration
+
+ type <java> "java.lang.Integer" from "rt.jar" as Int;
+
+
+ declares java.lang.Integer as a Scribble payload type, Int , for use in this Scribble module.
+
+
+
+ In the global protocol declaration :
+
+ global protocol Adder(role C, role S) { ... }
+
+
+
+
+ Adder is the name of the protocol;
+
+
+ C and S are the two participants in this protocol.
+
+
+
+
+
+ The choice statement
+
+ choice at C { ... } or { ... }
+
+ states that the protocol proceeds according to one of the or -separated blocks.
+
+
+
+ In this example, C is the master role that decides which block to follow.
+
+
+ C communicates its decision to the slave S by the message passing in each case.
+
+
+
+
+
+ The message signature
+
+ Add(Int, Int)
+
+
+ specifies:
+
+
+
+ Add is the operator (i.e. , a label or header that identifies the message);
+
+
+ (Int, Int) is a payload of two Int s.
+
+
+
+
+ In the message passing statement:
+
+ Add(Int, Int) from C to S;
+
+
+
+
+ C is the sender , which asynchronously dispatches the specified message (i.e. , without blocking);
+
+
+ C is the receiver , which blocks until the message is received.
+
+
+
+
+
+ The do statement
+
+ do Adder(C, S);
+
+
+ allows a protocol to be defined in terms of other (sub)protocols . It is also used for recursive protocol definitions, as for Adder in this example.
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ The command given below assumes:
+
+
+
+
+
+ In a Linux, Cygwin/Windows, or Mac OS terminal, the command is:
+
+
./scribblec.sh scribble-demos/scrib/tutorial/src/tutorial/adder/Adder.scr -api Adder C -d scribble-demos/scrib/tutorial/src/
+
+
+
+
+ The above command performs the following tasks.
+
+
+
+ Checks that the Scribble module in Adder.scr is a valid Scribble module -- this means that every global protocol (i.e. , Adder ) in the module must be a valid Scribble protocol (which it is).
+
+
+ Generates the Java Endpoint API for implementing the C endpoint of the Adder protocol. The output is written to the directory specified by the -d argument.
+
+
+
+
+
+ See here for more information on using the command line tool.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ We use the Endpoint API generated in the previous step to summarise the key elements behind Scribble Endpoint APIs.
+
+
+
+ The Endpoint API is generated from a finite state machine (FSM) representation of the I/O actions to be performed in the protocol by the target role.
+
+
+ An Endpoint API comprises a family of Java classes
+ that represent the states of the protocol relevant to the target role,
+with methods for the I/O actions permitted in each state.
+
+
+
+
+
+ We then give an example implementation using the above Endpoint API.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Below is the endpoint finite state machine (FSM) for the C role in Adder .
+
+
+
+
+ Fig. 2.3.1: The FSM for C in Adder .
+
+
+
+
+
+
+
+
+
+
+
+ State 1 is the initial state .
+
+
+ State 3 is the terminal state .
+
+
+ The notation S!Add(Int, Int) denotes the action by C to send S
+ a message with the specified signature .
+
+
+
+ The directed edge labelled by the above action means that performing this action in state 1 transitions C from to state 2 .
+
+
+ The notation S?Res(Int) means C receives from S
+ a message of the specified signature .
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ So that they may be used as types in Java, Scribble generates the various role and operator names in the source protocol as singleton types . For the Adder protocol, Scribble generates the following singleton types as API constants:
+
+
+
+
+
+ Roles
+
+
+ C ,
+ S
+
+
+
+
+ Operators
+
+
+ Add ,
+ Res ,
+ Bye
+
+
+
+
+
+
+
+ The API generation renders each state in the Endpoint FSM as a Java class for a state-specific communication channel (which we call a state channel for short).
+
+
+
+ By default, Scribble names the state channel classes by a state
+enumeration. For example, the class for the initial state is named Adder_C_1 . The terminal state (if any) is generated as a specially named EndSocket class.
+
+
+ Each class is generated with methods to perform the I/O actions permitted at the corresponding state.
+
+
+ The return type of each method is the class generated for the successor state of the corresponding I/O action.
+
+
+
+
+
+ See here for an example Javadoc generated (by the standard javadoc tool) from the Scribble-generated API for C in Adder .
+
+
+
+ The following summarises the key state channel classes and their I/O methods.
+
+
+
+
Adder_C_1 (state
1 in the
FSM ).
+
+
+
+
+
+
+ Type
+
+
+ Method
+
+
+
+
+ Adder_C_2
+
+
+ send(S role, Add op, Integer arg0, Integer arg1)
+
+
+
+
+ EndSocket
+
+
+ send(S role, Bye op)
+
+
+
+
+
+
+ The S , Add and Bye parameter types are the generated singleton types.
+
+
+ The send methods are non -blocking, i.e. they return immediately after asynchronously dispatching the specified message.
+
+
+ Calling a generated I/O method returns a new instance of the successor channel class.
+
+
+
+
+
+
Adder_C_2 (state
2 in the
FSM ).
+
+
+
+
+
+
+ Type
+
+
+ Method
+
+
+
+
+ Adder_C_1
+
+
+ receive(S role, Res op, Buf<? super Integer> arg0)
+
+
+
+
+
+
+ Buf<T> is a utility class in the Scribble API.
+
+
+
+ It has a mutable field val of type T .
+
+ It has a public constructor Buf(T t) , which initialises val to t .
+
+
+
+
+ The receive method blocks until the message is received. It then writes the received Integer value to the val field of supplied Buf before returning a new instance of the successor channel class.
+
+
+
+
+
+
+
+ We explain the Endpoint API for S later. It features the API generation for non-unary receive (i.e. , branch ) states, which is not demonstrated by the API for C .
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Usage contract of Scribble-generated Endpoint APIs
+
+
+
+ Starting from an instance of the initial state channel, an endpoint implemention should proceed by calling one method on the current channel to obtain the next, up to the end of the protocol (if any).
+
+
+
+ Below we give an example implementation of a C endpoint in an application of Adder . We use the Scribble-generated API following the above usage contract. This client uses the Adder service to calculate the n -th Fibonacci number.
+
+
+
+ The full code is available from the Scribble GitHub repository:
+
+
+
+
+
+
+ AdderC.java
+
+ Lst. 2.3.1: Java implementation of a C endpoint in Adder using the Scribble-generated API.
+
+
+
+
+package tutorial . adder ; /* Imports omitted */ public class AdderC { public static void main ( String [] args ) throws Exception { Adder adder = new Adder (); try ( MPSTEndpoint < Adder , C > client = new MPSTEndpoint <>( adder , C , new ObjectStreamFormatter ())) { client . connect ( S , SocketChannelEndpoint :: new , "localhost" , 8888 ); int n = 10 ; System . out . println ( n + "th Fibonacci number: " + fibo ( new Adder_C_1 ( client ), n )); } } private static int fibo ( Adder_C_1 c1 , int n ) throws Exception { Buf < Integer > x = new Buf <>( 0 ); Buf < Integer > y = new Buf <>( 1 ); Buf < Integer > i = new Buf <>( 1 ); while ( i . val < n ) { Adder_C_2 c2 = c1 . send ( S , Add , x . val , y . val ); x . val = y . val ; c1 = c2 . receive ( S , Res , y ); c1 = add1 ( c1 , i ); } c1 . send ( S , Bye ); // EndSocket return x . val ; } private static Adder_C_1 add1 ( Adder_C_1 c1 , Buf < Integer > i ) throws Exception { return c1 . send ( S , Add , i . val , 1 ). receive ( S , Res , i ); } }
+
+
+
+
+
+ We summarise the key elements in the above code.
+
+
+
+ The main method contains a typical preamble for an implemention of a client endpoint using an Endpoint API.
+
+
+
+ A new session is created as an instance of the front-end Session Class :
+
+ Adder adder = new Adder();
+
+
+ The Session Class in this example, Adder , is part of the generated Endpoint API.
+
+
+ We create a session endpoint object for the role being implemented:
+
+ try (MPSTEndpoint<Adder, C> client =
+ new MPSTEndpoint<>(adder, C, new ObjectStreamFormatter())) { ... }
+
+
+
+
+ MPSTEndpoint<P, R> is a class in the base API of Scribble. It is parameterised on the generated API types designating the intended protocol, Adder , and the name of the target role, C .
+
+
+ The second constructor argument is an API constant of the generated type.
+
+
+
+
+ ObjectStreamFormatter ,
+ provided by the Scribble API, performs the serialization and
+deserialization of messages using the default Java object serialization
+protocol.
+
+
+
+
+ The MPSTEndpoint is first used to establish a connection to the server endpoint S .
+
+ client.connect(S, SocketChannelEndpoint::new, "localhost", 8888);
+
+
+ connect is the client-side operation for requesting a connection.
+
+
+
+ S is an API constant of the type generated for role name S .
+
+
+ SocketChannelEndpoint::new is a constructor reference to a TCP channel class provided by the Scribble API.
+
+
+ "localhost" and 8888 give the address of the S server.
+
+
+
+
+
+ After establishing the required connection(s), the MPSTEndpoint is finally used to create the initial state channel:
+
+ new Adder_C_1(client)
+
+
+
+
+
+
+ The fibo method contains the main body of the protocol implementation using the Endpoint API explained earlier. It takes the initial state channel, Adder_C_1 , and follows the protocol through to the terminal state channel, EndSocket , returned by c1.send(S, Bye) .
+
+
+
+ The fibo method uses the Add option of the Adder service to add the the values of x and y for the next Fibonacci number.
+
+
+ It also uses add1 method to increment the the i counter.
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ We give an example implementation of an S server for Adder . We highlight the features that are different or not shown in the example client implementation.
+
+
+
+ The full code is available from the Scribble GitHub repository:
+
+
+
+
+
+
+ AdderS.java
+
+ Lst. 2.3.2: Java implementation of a S endpoint in Adder using the Scribble-generated API.
+
+
+
+
+package tutorial . adder ; /* Imports omitted */ public class AdderS { public static void main ( String [] args ) throws Exception { try ( ScribServerSocket ss = new SocketChannelServer ( 8888 )) { while ( true ) { Adder adder = new Adder (); try ( MPSTEndpoint < Adder , S > server = new MPSTEndpoint <>( adder , S , new ObjectStreamFormatter ())) { server . accept ( ss , C ); new AdderS (). run ( new Adder_S_1 ( server )); } catch ( ScribbleRuntimeException | IOException | ClassNotFoundException e ) { e . printStackTrace (); } } } } private void run ( Adder_S_1 s1 ) throws Exception { Buf < Integer > x = new Buf <>(); Buf < Integer > y = new Buf <>(); while ( true ) { Adder_S_1_Cases cases = s1 . branch ( C ); switch ( cases . op ) { case Add : s1 = cases . receive ( Add , x , y ) . send ( C , Res , x . val + y . val ); break ; case Bye : cases . receive ( Bye ); return ; } } } }
+
+
+
+
+
+
+ SocketChannelServer is used to accept TCP connections. It is a concrete implementation of ScribServerSocket , provided by the base API of Scribble.
+
+
+
+ The MPSTEndpoint and SocketChannelServer are used to establish a connection with the client C .
+
+ server.accept(ss, C);
+
+
+
+
+
+ Adder_S_1 is the initial state channel of the Endpoint API generated for S . This state is a non-unary receive state, or branch state.
+
+
+
+
+ The Adder_S_1 class has an instance method:
+
+
+ Adder_S_1_Cases branch(C role)
+
+
+
+ This method blocks until a message is received. It returns a new instance of Adder_S_1_Cases that has a field op of the generated type:
+
+
+ enum Branch_S_C_Add_Int_Int__C_Bye_Enum implements OpEnum {
+ Add, Bye
+ }
+
+
+
+ The name of the enum is a mechanically
+derived name that does not need to be exposed in endpoint implementation
+ code in typical usage. The API sets the value of the op field of the returned Adder_S_1_Cases according to the operator of the received message.
+
+
+
+ Adder_S_1_Cases has the instance methods:
+
+
+ Adder_S_2 receive(C role, Add op, Buf<? super Integer> arg1, Buf<? super Integer> arg2)
+
+ EndSocket receive(C role, Bye op)
+
+
+
+ In each case of the switch(cases.op) statement, the corresponding method is used as a "cast" to obtain the appropriate state channel from the Cases object. The above methods are generated to throw a ScribbleRuntimeException if the wrong method is used.
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+ This section describes the syntax of Scribble modules and global protocols in more detail. It describes the syntactic constraints imposed by Scribble on global protocol definitions.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ A Scribble module is a file with the following elements, in order.
+
+
+
+
+ A module declaration :
+
+ module mypackage.MyModule;
+
+
+
+
+ The package prefix is optional.
+
+
+ The name of the file containing a Scribble module must be the same as the last element of the module name (MyModule ) appended by the Scribble file extension (.scr ); i.e. , the module declared above should be contained in a file named MyModule.scr .
+
+
+
+
+
+ Zero or more import declarations :
+
+ import somepackage.subpackage.SomeModule;
+
+
+
+
+ Scribble-Java looks for a declared import along the import path , mapping the package prefix to a directory offset. E.g. , for the above import declaration, Scribble-Java looks along the import path for a file somepackage/subpackage/SomeModule.scr .
+
+
+
+
+
+ Zero or more payload type declarations and message signature name declarations . The following explains payload type declarations.
+
+ type <java> "java.lang.Integer" from "rt.jar" as Int;
+
+
+
+
+ A payload type name (Int ) may be used in the payload of a message signature .
+
+
+ java is the host language of the declared data type -- N.B. the current version of Scribble-Java only accepts Java (reference) types;
+
+
+ java.lang.Int is of name of the type in the host language;
+
+
+ rt.jar is the resource where the type definition is located -- N.B. the current version of Scribble-Java does not actually use this field (any string value may be specified, and will be ignored);
+
+
+ Int is a payload type name by which the type may be referred to.
+
+
+
+
+
+
+ Message signature name declarations are explained later in this tutorial.
+
+
+
+
+ Zero or more global protocol declarations .
+
+
+
+
+ The notion of well-formed module is discussed here .
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ A message signature specifies the key elements of a message.
+
+
+ op ( pay1 , ..., payn )
+
+
+
+
+
+ op is the message operator . Scribble uses the operator to identify the message. In the concrete message format , the operator may be conveyed as, e.g. , a header field.
+
+
+
+ The operator may be omitted, denoting the empty operator .
+
+
+
+
+ ( pay1 , ..., payn ) is the message payload .
+
+
+ Each payi must be a payload type name .
+
+
+ The list of payload types may be the empty list, denoting the empty payload ; i.e. , () .
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+ A Scribble global protocol describes the interactions between the participants of a communication session from the global (i.e. , neutral) perspective. A Scribble protocol abstracts session participants as named roles . Sessions are sometimes referred to as conversations .
+
+
+
+ Scribble protocols are strictly explicit about session
+interactions -- the only application-level messages to be communicated
+in a Scribble session are those that are explicitly specified in the
+protocol.
+
+
+ global protocol MyProto(role r1 , ..., role rn ) {
+ ...
+ }
+
+
+
+
+
+ MyProto is the name of the protocol.
+
+
+ role r1 , ..., role rn is the role declaration list .
+
+
+ The list of roles must contain at least two roles. The role names must be distinct.
+
+
+
+
+ { ... } is the protocol body .
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ A message interaction is a role-to-role pairing of a send action and a receive action of a message.
+
+
+
+ The message may be specified as a message signature , i.e. , an operator and a payload:
+
+
+ op(pay) from r1 to r2 ;
+
+
+
+ or as a message signature name :
+
+
+ Msg from r1 to r2 ;
+
+
+
+ In both cases, r1 is the sender role and r2 is the receiver role.
+
+
+
+ The sender and receiver roles must be distinct.
+
+
+
+
+
+ The communication model of Scribble is that message delivery is output-asynchronous , reliable , and ordered in each direction between each pair of roles. Output-asynchronous means the send action is non -blocking for the sender, but the receive action is blocking for the receiver (i.e. , until the message arrives). This model caters for applications using, e.g. , TCP channels, or (unbounded) FIFOs in shared memory.
+
+
+
+ Send actions are considered output actions, and receive actions are considered input actions.
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ The choice statement designates a branch point in the protocol. The protocol proceeds according to one of the or -separated cases.
+
+
+ choice at r { ... } or ... or { ... }
+
+
+
+
+
+ The at -role, r , is called the choice subject .
+
+
+ The choice subject selects which block to follow as an internal choice . A Scribble protocol leaves the actual decision procedure abstract (it is an implementation -level concern).
+
+
+ The decision is to be communicated as an external choice to the other roles by appropriate message passing in each case.
+
+
+
+
+
+ A choice statement is subject to the following syntactic constraints.
+
+
+
+ Role enabling :
+
+
+
+ A role is either enabled or disabled .
+
+
+ On entering a choice , the only enabled role is the choice subject; all other roles that occur in the choice are disabled.
+
+
+ A role in a choice context may only send a message or request a connection if it is enabled.
+
+
+ A disabled role is enabled by receiving a message or accepting a connection (from an enabled role).
+
+
+
+
+
+ Consistent external choices :
+
+
+
+ For each role that occurs in the choice that is not the choice subject, the action kind (i.e. , receive or accept ) and interaction peer (i.e. , the sender or client role) of the enabling action must be the same in all choice cases.
+
+
+
+
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The do statement instructs the specified roles to perform the interactions of the specified protocol inline with the current session.
+
+
+
+
+do MyProto(r1 , ..., rn );
+
+
+
+
+
+ MyProto is the name of a global protocol , referred to as the target protocol .
+
+
+ r1 , ..., rn is the role argument list .
+
+
+ The length of role argument list must be the same as the length of the role declaration list of the target protocol declaration.
+
+
+ The role argument names must be distinct.
+
+
+
+
+ The meaning of the do statement is given by substituting every occurrence in the target protocol body
+ of a role in the role declaration list of the target protocol
+declaration with the corresponding role in the role argument list.
+
+
+
+
+
+ In other words, each role in the role argument list plays the corresponding role in the role declaration list of the target protocol declaration.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+ A do statement may be used to define (mutually) recursive protocols.
+
+
+
+ In addition to the previously stated constraints on do statements, Scribble global protocol definitions must be tail recursive per role . This means:
+
+
+
+ for any declared role r of a global protocol , it is not permitted for the protocol definition to sequence any action by r after a (mutually) recursive do statement that involves r .
+
+
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Message signature name declarations may occur alongside payload type declarations .
+
+
sig <java> "javapackage.JavaClass" from "myjar.jar" as MyMessage;
+
+
+
+
+ A message signature name (MyMessage ) may be used in a message interaction . It represents a data type in the host language with custom message formatting , i.e. , serialization and deserialization, routines.
+
+
+
+ java is the host language for which the message formatting routines for this message are implemented -- N.B. the current version of Scribble-Java only accepts concrete Java subclasses of the ScribMessage interface of the Scribble API;
+
+
+ javapackage.JavaClass is the host language name of the code unit (i.e. Java class) that contains the message formatting routines;
+
+
+ myjar.jar is the resource where the code message formatting code is located -- N.B. the current version of Scribble-Java does not actually use this field (any string value may be specified, and will be ignored);
+
+
+ MyMessage is the message signature name for this message format.
+
+
+
+
+
+
+ In message interactions that specify message signature names, the message signature name itself is used to identify the message (cf. the operator in a message signature ).
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ A global protocol declaration may have the modifier explicit .
+
+
+ explicit global protocol MyProto(...) { ... }
+
+
+
+ The communication model of a non -explicit global protocol starts with all roles pairwise connected by binary channels.
+
+
+
+ By contrast, the communication model of an explicit global protocol starts with all roles unconnected . A pair of roles are connected by:
+
+
+ connect r1 to r2 ;
+
+
+
+ A connection interaction is a role-to-role pairing of a connection request action and a connection accept action. Role r1 is the client role, and r2 is the server role.
+
+
+
+
+ The client and server roles must be distinct.
+
+
+
+
+ A connection interaction is a synchronous interaction between the client and server, i.e. ,
+ the first role to perform either connection action is blocked until the
+ other role performs the counterpart action. The connection interaction
+ establishes a bidirectional binary (i.e. , two-party) channel with the characteristics of the Scribble communication model , i.e. message delivery is reliable and ordered in each direction.
+
+
+ A pair of roles are disconnected by:
+
+
disconnect r1 and r2 ;
+
+
+
+ Each of the two disconnect actions in the above pairing is a local action, i.e.
+ it does not directly pertain to any interaction between the two roles.
+ A disconnect action closes the input queue at the local endpoint of the
+ binary channel between these two roles.
+
+
+
+ In the context of Scribble, request actions are considered output actions, and accept actions are considered input actions.
+
+
+
+ An explicit global protocol is subject to the following syntactic constraints.
+
+
+
+
+ A connection action is not permitted if the two roles are potentially connected.
+
+
+ A message action or a disconnect action is not permitted if the two roles are potentially unconnected.
+
+
+
+
+ This means there is at most one connection between any two roles.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This section describes the notion of whether a global protocol is valid in Scribble.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Scribble validates that a global protocol satisfies a set of desirable properties . These include safety properties, such as no reception errors (a role never receives an unexpected message), and liveness properties, such as eventual reception (a sent message can always eventually be received).
+
+
+
+ A Scribble global protocol is validated by two steps.
+
+
+
+
+
+ Specifically, the second step is conducted on a 1-bounded
+model of the protocol. That is, the model given by the exhaustive
+execution of the system of Endpoint FSMs where each communication
+channel is limited to a maximum capacity of one message in each
+direction.
+
+
+
+
+
+ A global protocol is syntactically well-formed if it satisfies the syntactic constraints of the first of the above steps.
+
+
+ A global protocol is valid if it satisfies both the above steps.
+
+
+
+
+
+ The Scribble validation of global protocols is sound . This means a valid protocol satisfies the safety and liveness properties in the general setting, i.e. , the system of Endpoint FSMs with unbounded channels. Soundness is due to the characteristics of the protocol (i.e. , the restrictions on endpoint behaviours) imposed by syntactic well-formedness.
+
+
+
Well-formed modules
+
+
+ A module is well-formed if every global protocol in the module is valid.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ The Endpoint FSM for any role of a syntactically well-formed Scribble global protocol has the following characteristics.
+
+
+
+
+
+ There is one initial state and at most one terminal state .
+
+
+ A state is one of the following kinds.
+
+
+ An output state . It has at least one transition. The action of every transition is either a send or a connect action.
+
+
+ An input state . It has at least one transition and is either:
+
+
+ a receive state , where all actions of its transitions are receive actions from the same role;
+
+
+ an accept state , where all actions of its transitions are accept actions from the same role;
+
+
+
+
+ A disconnect state. It has one transition. The action of the transition is a disconnect action.
+
+
+ The terminal state. It has no transitions.
+
+
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ We first introduce some terminology.
+
+
+
+ A role is inactive in a session if it is at the initial state and is not connected to any role, or it is at the terminal state . Otherwise the role is active .
+
+
+ A session is in a final state if no role can perform any action.
+
+
+
+
+
+ A valid Scribble global protocol satisfies the following safety properties.
+
+
+
+ No reception errors . A message received by a role at a receive state is always one of the expected messages.
+
+
+ No orphan messages . A role that is inactive in a session has no outstanding messages to receive.
+
+
+ No unfinished roles . No role is left in an active state in a session that is in a final state.
+
+
+
+
+
+ A valid Scribble global protocol satisfies the following liveness properties.
+
+
+
+ Role progress . An active role can always eventually perform an action.
+
+
+ Eventual reception . A sent message can always eventually be received.
+
+
+
+
+
+Fairness of output choice implementations
+
+
+ Scribble supports an option to consider unfair endpoint implementations of the choice between actions at output states . Here, "unfair" means that the implementation of the choice may never perform a particular action, even if the choice may be repeated infinitely often (i.e. , in a recursive protocol).
+
+
+
+ For this option, Scribble adopts a "worst case" view where endpoint implementations exercise a minimal number of output choice options during session execution. This is affected by a model transformation that commits a role to perform again the same action whenever an output state is revisited .
+
+
+
+ See here for the instructions on enabling or disabling this option in the command line tool.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ This section describes the generation of protocol-specific Java Endpoint APIs by Scribble in more detail.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ An Endpoint API for a role in a global protocol has two parts.
+
+
+
+
+ The Session Class is the front-end class for creating a new session following the source protocol.
+
+
+ The State Channel API is a family of state-specific channel classes used to implement an endpoint playing the target role.
+
+
+
+
+ For a global protocol, e.g. Adder , in the Scribble module tutorial.adder.Adder , Scribble generates the Session Class as follows.
+
+
+
+
+ The Session Class is public class with the same name as the protocol, i.e. , Adder .
+
+
+ The package of the Session Class is given by the module name and the protocol name, i.e. , tutorial.adder.Adder.Adder ;
+
+
+ The Session class has a no-args constructor:
+
+ public Adder()
+
+
+
+
+
+ An Endpoint API uses classes from the base Scribble API, which will be referred to in the following sections.
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ A global protocol may feature several kinds of names : roles, message operators and message signature names.
+
+
+
+ Scribble generates a Java singleton type for each of these names. The following illustrates the code generated for the C role in Adder .
+
+
+ package tutorial.adder.Adder.Adder.roles;
+
+ public final class C extends org.scribble.sesstype.name.Role {
+ private static final long serialVersionUID = -6429099329627603991L;
+ public static final C C = new C();
+ private C() {
+ super("C");
+ }
+ }
+
+
+
+
+
+ C is the name of the class (i.e. , of the singleton type ).
+
+
+ C is also the name of the public static final field of type C in the class that refers to the singleton instance of the class.
+
+
+ The leading tutorial.adder.Adder in the package name derives from the source module name. The second Adder in the package name derives from the global protocol name.
+
+
+
+
+
+ The generation for message operators and message signature names is similar.
+
+
+
+ For convenience, the Session Class is also generated with field constants referring to the singleton type instances. E.g. , the Adder Session Class has the following fields:
+
+
+ public static final C C = tutorial.adder.Adder.Adder.roles.C.C;
+ public static final S S = tutorial.adder.Adder.Adder.roles.S.S;
+ public static final Add Add = tutorial.adder.Adder.Adder.ops.Add.Add;
+ public static final Bye Bye = tutorial.adder.Adder.Adder.ops.Bye.Bye;
+ public static final Res Res = tutorial.adder.Adder.Adder.ops.Res.Res;
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ The generation of a State Channel API for a role in a global protocol is based on the Endpoint FSM of that role.
+
+
+
+ Assume a valid global protocol G in a well-formed module mypack.MyMod . Let s be a state in the Endpoint FSM of G for role r .
+
+
+
+
+ If s is a terminal state in the Endpoint FSM, the Scribble generates:
+
+
+
+
+ a class named EndSocket with no I/O methods in package mypack.MyMod.G.channels.r .
+
+
+
+
+
+ N.B. Henceforth, references to a class G_r_s where s is a terminal state should be considered as EndSocket .
+
+
+
+
+ Otherwise (i.e. , if s is not a terminal state):
+
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Let:
+
+
+
+
+ soutput be an output state in the Endpoint FSM of G for role r ;
+
+
+ a1..n be the actions of the transitions from s , leading to states s1..n , respectively.
+
+
+
+ For each send action ai to receiver role r_i for message m :
+
+
+
+
+ If m is a message signature Op(Pay1 , ..., Payn ) , Scribble generates an instance method in class G_r_s :
+
+ public G_r_si send(ri role, Op op, Pay1 arg1, ..., Payn argn) throws ScribbleRuntimeException, IOException
+
+
+
+
+ If m is a message signature name M , Scribble generates an instance method in class G_r_s :
+
+
+ public G_r_si send(ri role, M msg) throws ScribbleRuntimeException, IOException
+
+
+
+
+
+ The send methods are generated to return without blocking.
+
+
+
+ For each connect action ai to receiver role r_i , Scribble generates an instance method in class G_r_s :
+
+
+ public G_r_si connect(ri role, Callable<? extends BinaryChannelEndpoint> cons, String host, int port)
+ throws ScribbleRuntimeException, IOException
+
+
+
+
+ org.scribble.net.session.BinaryChannelEndpoint is an abstract class that represents a bidirectional binary communication channel. Implementations of this interface must provide a channel that is reliable and order-preserving in each direction .
+
+
+
+ org.scribble.net.session.SocketChannelEndpoint is a concrete implementation using TCP provided by the Scribble API.
+
+
+
+
+ host and port identify the address of the server for the connection request.
+
+
+ E.g. , for SocketChannelEndpoint , host is an Internet host name and port a TCP port.
+
+
+
+
+
+
+ The connect method is generated to block until the connection is established or an exception is thrown.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Let:
+
+
+
+
+ sreceive be a receive state in the Endpoint FSM of G for role r ;
+
+
+ a1..n be the receive actions, from sender role r' , of the transitions from s , leading to states s1..n , respectively.
+
+
+
+
+ If s is a unary receive state, i.e. , n = 1 , and a1 specifies message m , then:
+
+
+
+
+ If m is a message signature Op(Pay1 , ..., Payn ) , Scribble generates an instance method in class G_r_s :
+
+ public G_r_s1 receive(r' role, Op op, Buf<? super Pay1 > arg1, ..., Buf<? super Payn > argn)
+ throws ScribbleRuntimeException, IOException, ClassNotFoundException
+
+
+
+
+ If m is a message signature name M , Scribble generates an instance method in class G_r_s :
+
+
+ public G_r_s1 receive(r' role, Buf<? super M arg1)
+ throws ScribbleRuntimeException, IOException, ClassNotFoundException
+
+
+
+
+
+ The receive methods are generated to block until a message is received.
+
+
+
+
+
+
+
+ Otherwise, i.e. , n > 1 , Scribble generates:
+
+
+
+
+
+ The branch method is generated to block until a message is received.
+
+
+
+ For each (receive) action ai from sender r' for message mi :
+
+
+
+
+ If mi is a message signature Op(Pay1 , ..., Payn ) , Scribble generates an instance method in class G_r_s_Cases :
+
+ public G_r_si receive(r' role, Op op, Buf<? super Pay1 > arg1, ..., Buf<? super Payn > argn)
+ throws ScribbleRuntimeException, IOException, ClassNotFoundException
+
+
+
+
+ If mi is a message signature name M , Scribble generates an instance method in class G_r_s_Cases :
+
+
+ public G_r_si receive(r' role, Buf<? super M arg1)
+ throws ScribbleRuntimeException, IOException, ClassNotFoundException
+
+
+
+
+
+
+ org.scribble.net.Buf<T> is a utility class in the Scribble API.
+
+
+
+
+ It has a mutable field val of type T .
+
+ It has a public constructor Buf(T t) , which initialises val to t .
+
+
+
+
+ The receive methods in both of the above cases are generated to write the each of the received payload values or message object to the val field of the corresponding Buf argument before returning.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Let:
+
+
+
+
+ saccept be an accept state in the Endpoint FSM of G for role r ;
+
+
+ a1..n be the actions of the transitions from s , leading to states s1..n , respectively.
+
+
+
+ Scribble generates an instance method in class G_r_s :
+
+
+ public G_r_s1 accept(ri role, ScribServerSocket ss) throws ScribbleRuntimeException, IOException
+
+
+
+
+ org.scribble.net.scribsock.ScribServerSocket is an abstract class that represents a server port for accepting BinaryChannelEndpoint connection requests.
+
+ org.scribble.net.scribsock.SocketChannelServer is a concrete implementation using TCP provided by the Scribble API.
+
+
+
+
+
+ The accept method is generated to block until the connection is established or an exception is thrown.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ If s is a disconnect state in the Endpoint FSM of G for role r :
+
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Scribble enforces a linear usage discipline on state channel
+instances, by generating run-time checks into the Endpoint API. This
+means an endpoint implementation should invoke one I/O method on every instance of a state channel, apart from EndSocket . The following situations throw a ScribbleRuntimeException .
+
+
+
+
+ An I/O method is invoked on a state channel instance on which an I/O method has been previously invoked.
+
+
+
+ A try -with-resources statement closes an MPSTEndpoint declared in a resource specification and there exists an associated state channel instance on which no I/O method has been called.
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ This section describes additional details of endpoint implementation
+using Scribble-generated APIs and the associated safety properties.
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+Endpoint implementation using an Endpoint API generated from a non -explicit global protocol relies on a connection establishment phase between creating the MPSTEndpoint and the initial state channel .
+
+
+Client-side connection requests
+
+
+ The following is from the example implementation of a C client in Adder .
+
+
+ Adder adder = new Adder();
+ try (MPSTEndpoint<Adder, C> client =
+ new MPSTEndpoint<>(adder, C, new ObjectStreamFormatter())) {
+ client.connect(S, SocketChannelEndpoint::new, "localhost", 8888);
+ /* Body of endpoint implementation, starting from new Adder_C_1(client)*/
+ }
+
+
+
+ Client -side connection requests are peformed by the connect method of MPSTEndpoint .
+
+
+ client.connect(S, SocketChannelEndpoint::new, "localhost", 8888);
+
+
+
+ The above method has the same signature as the State Channel API connect method generated for explicit connect actions.
+
+
+
+
+ S is the singleton type constant generated for the role being connected to.
+
+
+ SocketChannelEndpoint::new is a constructor reference, passed as parameter of expected type Callable<? extends BinaryChannelEndpoint > .
+
+
+ "localhost" and 8888 are the hostname and port arguments for the SocketChannelEndpoint connection request.
+
+
+
+
+Server-side connection accepts
+
+
+ The following is from the example implementation of a S server in Adder .
+
+
+ try (ScribServerSocket ss = new SocketChannelServer(8888)) {
+ while (true) {
+ Adder adder = new Adder();
+ try (MPSTEndpoint<Adder, S> server
+ = new MPSTEndpoint<>(adder, S, new ObjectStreamFormatter())) {
+ server.accept(ss, C);
+ new AdderS().run(new Adder_S_1(server));
+ } catch (ScribbleRuntimeException | IOException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+
+ Server -side connections are accepted via an instance of org.scrible.net.scribsock.ScribServerSocket .
+
+
+ try (ScribServerSocket ss = new SocketChannelServer(8888)) { ... }
+
+
+
+
+ org.scribble.net.scribsock.SocketChannelServer is a concrete implementation of ScribServerSocket for accepting the server-side of TCP connections.
+
+
+
+
+ A connection is accepted by the accept method of MPSTEndpoint .
+
+
+ server.accept(ss, C);
+
+
+
+
+ ss is the SocketChannelServer , passed as a parameter of expected type ScribServerSocket .
+
+
+ C is the singleton type constant generated for the role to accept the connection from.
+
+
+
+
+Safety warning for non-explicit connection actions
+
+
+ The actions performed in a non-explicit connection phase are not verified in any way. This means it is up to the programmer to avoid errors such as the following.
+
+
+
+
+ Incorrect connection request or accept actions, i.e. , performing a connect instead of an accept or vice versa, or attempting a connect or accept with the wrong role.
+
+
+ Deadlock, e.g. , due to a wait-for cycle between two or more roles mutally blocked on connect and accept actions.
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ The following is the API usage contract for endpoint implementations using Scribble-generated Endpoint APIs .
+
+
+
+ Starting from an instance of the initial state channel, an endpoint implemention should proceed by calling one method on the current channel to obtain the next, up to the end of the protocol (if any).
+
+
+
+ An Endpoint API is generated to throw a ScribbleRuntimeException at run-time if the above rule, i.e. linear usage of state channels, is violated. The I/O action of the offending method invocation will not be performed.
+
+
+
+ Note that the only way that an endpoint implementation using an Endpoint API may attempt
+ to violate the protocol is by violating state channel linearity. This
+confers the following form of safety for Scribble endpoint
+implementations.
+
+
+
+
+ If linear channel usage is respected, then Java type checking statically guarantees that the endpoint implementation complies to the specified protocol.
+
+
+ Regardless of linear channel usage, Java type checking
+guarantees protocol compliance up to premature termination of the
+endpoint in the session.
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ The source code of the master version of Scribble-Java is available from GitHub .
+
+
+
+
+
+ We give basic instructions for two ways of building the Scribble-Java tool from source. Both ways will create the file
+
+
+ dist/target/scribble-dist-0.4-SNAPSHOT.zip
+
+
+
+ under the root directory of the scribble-java project.
+
+
+
+
+
+ Building Scribble-Java using command line Maven
+
+
+
+ These instructions have been tested on Ubuntu 16.04.2 using:
+
+
+
+
+ OpenJDK 1.8.0_221
+
+
+ Apache Maven 3.3.9
+
+
+
+
+ From the root directory of the scribble-java project:
+
+
+
+
+ mvn [clean] install
+
+
+
+
+
+
+
+
+ Building Scribble-Java using Eclipse
+
+
+
+ These instructions have been tested for:
+
+
+
+
+ Ubuntu 16.04.2, using Eclipse Luna 2 (4.4.2).
+
+
+ Windows 10, using Eclipse Neon.2 (4.6.2).
+
+
+ Mac OS X Yosemite, using Eclipse Luna 2 (4.4.2).
+
+
+
+
+ N.B. Eclipse must be configured to use a JDK 8 (Preferences → Java → Installed JREs).
+
+
+
+ Steps:
+
+
+
+
+ Import the Scribble-Java projects as Existing Maven Projects.
+
+
+ Eclipse should compile Scribble automatically.
+
+
+
+ You may be prompted to install a Maven-Eclipse plugin (M2E connector) for ANTLR.
+
+
+
+
+ Right click, e.g. , scribble-java in the Package Explorer → Run As → Maven install
+
+
+
+
+
+Installing the Scribble-Java command line tool
+
+
+ The scribble-dist-0.4-SNAPSHOT.zip built by the above instructions contains:
+
+
+
+
+ scribblec.sh -- the command line script to run the tool;
+
+
+ lib -- a directory of jars for running the tool.
+
+
+
+
+ The script is immediately executable after extracting the zip contents.
+
+
+
+ Alternatively, the script is executable from the scribble-java root directory, without lib , by:
+
+
+
+
+ building the project (as above);
+
+
+ and setting the ANTLR variable in the script to provide the Java classpath for an ANTLR 3 runtime.
+
+
+
+
+ <
+ prev ;
+ top ;
+ next
+ >
+
+
+
+
+
+
+
+
+
+ Assuming the scribblec.sh script is installed at the current directory, various command line options are shown by:
+
+
+ ./scribblec.sh --help
+
+
+
+ The following summarises a few options mentioned in this tutorial:
+
+
+
+
+
+ -ip import_path
+
+
+ Sets the import path for module imports.
+
+
+
+
+ -fair
+
+
+ Disables the model transformation and checking for "unfair " endpoint implementations.
+
+
+
+
+
+
+ A command for validating the example Adder protocol and generating the API for the C role was given here .
+
+
+
+ Further examples of using the scribblec.sh script can be found in this README .
+
+
+
+ <
+ prev ;
+ top ;
+ >
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble-scala.html b/src/main/jbake/assets/docs/scribble-scala.html
new file mode 100644
index 0000000..3fd8828
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble-scala.html
@@ -0,0 +1,1708 @@
+
+
+
+
+
+ Scribble-Scala: Tutorial and Demo Artifacts
+
+
+
+
+
+
+
+
+
+ Scribble-Scala: Tutorial and Demo Artifact
+
+
+
+ Alceste Scalas ,
+ Ornela Dardha,
+ Raymond Hu,
+ Nobuko Yoshida
+
+
+ Table of Contents
+
+
+
+
+ Overview
+
+
+
+ This is a tutorial and demo of the Scribble-Scala tool for specifying and validating multiparty protocols, and Scala API generation for endpoint implementation.
+
+
+
+ This tutorial is accompanied by a demo artifact, published along with a paper on this work to appear in the proceedings of ECOOP '17. These are available online at the following:
+
+
+
+
+
+
+
+ The artifact contains a VirtualBox image for a ready-to-use, streamlined Ubuntu 16.04.2
+ operating system
+ (thanks to OSBoxes.org ),
+ plus an installation of Scribble
+ and lchannels (see above) with their
+ dependencies.
+
+
+
+ See here for instructions on how to build Scribble-Scala from source.
+
+
+
+ In a nutshell, the interplay between Scribble
+ and lchannels is the following:
+
+
+
+
+ the Scribble tool can read a global protocol
+ specification, describing the interactions between
+ several roles (see Figure 9 in the paper), and
+ validate it against various communication errors;
+
+
+ then, for each role r in the protocol, our Scala
+ extension to Scribble can generate a set of Scala class definitions,
+ dubbed multiparty session classes : their API
+ consists of typed send() /receive()
+ methods, and guides the type-safe implementation of programs
+ playing role r ;
+
+
+ internally, the multiparty session classes perform their
+ communications through typed binary channels, using
+ the lchannels library. The theory that allows
+ Scribble-Scala to decompose the source multiparty
+ protocols into typed binary channels is developed in the paper
+ (from §2 to §6).
+
+
+
+
+ This tutorial presents several Scribble global protocols,
+ and their Scala implementations. The Scala endpoint programs
+ use the multiparty session classes generated by Scribble.
+
+
+ Quick Start (Kick-the-Tires)
+
+
+ In this section we provide basic instructions
+ to setup the VirtualBox image , and try out the
+ demos .
+
+
+ Setting Up and Running the VirtualBox Image
+
+
+ The following instructions have been tested
+ using VirtualBox
+ version 4.3.36 (on Ubuntu 14.04) and version 5.1.18 (on Windows
+ 10 and Mac OS X).
+ Start VirtualBox, and:
+
+
+
+
+ click "New"
+
+
+ type: Linux
+
+
+ version: Ubuntu (32-bit)
+
+
+ memory size: 1024 MB (if possible, 2048 MB or more)
+
+
+ hard disk: "Use an existing virtual hard disk file":
+ mpst-linear-artifact.vdi
+
+
+
+
+
+ then, right-click on the newly-added virtual machine →
+ "Settings...", and:
+
+
+ "System" → "Processor" → Check "Enable PAE/NX"
+
+
+
+
+
+
+ You can now start the newly-added VM, by selecting it and
+ clicking "Start". To log in , use the
+ following credentials:
+
+
+
+
+ username: osboxes
+
+
+ password: osboxes
+
+
+
+ Trying Out the Demos
+
+
+ After logging in, double-click on the "ECOOP'17 artifact
+ demo" icon on the desktop, and follow the instructions.
+
+
+
+ Each demo will:
+
+
+
+ show its Scribble global protocol specification;
+
+
+ run Scribble, and show its output: i.e. , the Scala multiparty
+ session classes corresponding to the global protocol;
+
+
+ launch an example implementation based on the multiparty session
+ classes above (at this point, one or more terminals will pop up).
+
+
+
+
+ All demos should run without troubles. You might
+ occasionally see a log message similar to the following, which
+ is harmless:
+
+
+
+[ERROR] [04/11/2017 17:09:16.721] [GreetingServerSys-akka.remote.default-remote-dispatcher-2] [akka.tcp://GreetingServerSys@127.0.0.1:31337/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FGreetingClientSys%40127.0.0.1%3A31338-0/endpointWriter] AssociationError [akka.tcp://GreetingServerSys@127.0.0.1:31337] <- [akka.tcp://GreetingClientSys@127.0.0.1:31338]: Error [Shut down address: akka.tcp://GreetingClientSys@127.0.0.1:31338] [
+akka.remote.ShutDownAssociation: Shut down address: akka.tcp://GreetingClientSys@127.0.0.1:31338
+Caused by: akka.remote.transport.Transport$InvalidAssociationException: The remote system terminated the association because it is shutting down.
+
+
+ If you do not encounter other issues, then the artifact should
+ be working, and ready for further evaluation.
+
+
+ Contents of the VirtualBox Image
+
+ These are the main contents of the VirtualBox image, and their locations:
+
+
+
+ Scribble, installed in:
+ /home/osboxes/scribble/
+
+
+ lchannels , installed
+ in: /home/osboxes/lchannels/
+
+
+ the artifact demo script, available
+ in: /home/osboxes/bin/demo
+
+
+
+ How to run Scribble (command line)
+
+
+ The Scribble tool can be used to check the well-formedness of
+ MPST protocols. In this artifact, we present an extended
+ version that also allows to generate the Scala APIs for
+ implementing the protocol endpoints.
+
+
+
+ Once logged in the VM, you can:
+
+
+
+
+
+ A concrete example:
+
+
+
+./scribblec-linmp.sh modules/linmp-scala/src/test/scrib/Greeting.scr Proto
+
+
+
+ For an explanation of Scribble syntax and the tool output, see
+ the Greeting protocol tutorial .
+
+
+ Scribble Tutorial: Greeting Protocol
+
+
+ We now give an overview of Scribble
+ syntax and the Scribble-Scala tool
+ output , using the "Greeting protocol" example originally
+ described
+ in this
+ ECOOP'16 paper .
+
+
+ Specifying an MPST Protocol in Scribble
+
+
+ Scribble provides a user-friendly, Java-inspired notation for
+ defining structured "global protocols" among multiple
+ participants. Such global protocols correspond to global types
+ in MPST theory (cf. section "Global Types" in the paper, page 10).
+
+
+
+ The Scribble specification of the Greeting protocol is available
+ in: /home/osboxes/scribble/modules/linmp-scala/src/test/scrib/Greeting.scr
+ (and also
+ here ):
+
+
+
+module Greeting;
+
+type <java> "java.lang.String" from "rt.jar" as String;
+
+global protocol Proto(role c, role s) {
+ choice at c {
+ Greet(String) from c to s;
+ choice at s {
+ Hello(String) from s to c;
+ do Proto(c, s);
+ } or {
+ Bye(String) from s to c;
+ }
+ } or {
+ Quit() from c to s;
+ }
+}
+
+
+ The above defines a simple protocol between two
+ participants, or roles : c (client)
+ and s (server). The protocol can be
+ read as follows:
+
+
+
+
+ c starts with a choice (line 6): it
+ could send to s either
+ a Greet message carrying
+ a String payload (line 7), or
+ a Quit message carrying an empty
+ payload (line 15);
+
+
+ if c chooses to
+ send Greet ,
+ then s can choose (line 8) to send
+ back to c either
+ a Hello message (line 9), or
+ a Bye message (line 12);
+
+
+ if s
+ chooses Hello , the protocol
+ recursively repeats from the start (line 10);
+
+
+ if s
+ chooses Bye , the
+ protocol terminates;
+
+
+
+
+ otherwise, if c chooses to send
+ Quit (line 15), the protocol terminates.
+
+
+
+
+ For reference, the formal notation for the corresponding global
+ type (see p.10, section "Global Types" in the paper) is:
+
+
+μX.c⟶s{
+ Greet(String).s⟶c{ Hello(String).X, Bye(String).end },
+ Quit(Unit).end
+}
+
+
+ We now briefly explain the various elements appearing in the
+ above Scribble specification:
+
+
+
+
+
+ NOTE: just like formal global types, this
+ version of Scribble supports action
+ prefixing : i.e. , sequential composition of
+ role-to-role message passing actions followed by other message
+ actions, do statements
+ or choice statements. However,
+ sequential composition following a
+ do or
+ a choice is not permitted.
+
+
+
+
+
+ Using the command line script:
+
+
+
+cd ~/scribble
+./scribblec-linmp.sh modules/linmp-scala/src/test/scrib/Greeting.scr Proto
+
+
+ The output will be a set of Scala class definitions, similar to
+ the following:
+
+
+
+// Global type (from modules/linmp-scala/src/test/scrib/Greeting.scr)
+// μ(Greeting_Proto___c__s_).c->s{Greet(String).s->c{Hello(String).Greeting_Proto___c__s_, Bye(String).end}, Quit(Unit).end}
+
+// -----------------------------------------------------
+// Local type for role s:
+// μ(Greeting_Proto___c__s_).c&{Greet(String).c⊕{Hello(String).Greeting_Proto___c__s_, Bye(String).end}, Quit(Unit).end}
+package test.proto.s
+
+import scala.concurrent.duration.Duration
+import lchannels._
+
+// Input message types for multiparty sessions
+sealed abstract class MsgMPGreetOrQuit
+case class Greet(p: String, cont: MPByeOrHello) extends MsgMPGreetOrQuit
+case class Quit(p: Unit) extends MsgMPGreetOrQuit
+
+// Output message types for multiparty sessions
+case class Hello(p: String)
+case class Bye(p: String)
+
+// Multiparty session classes
+case class MPGreetOrQuit(c: In[binary.GreetOrQuit]) {
+ def receive(implicit timeout: Duration = Duration.Inf) = {
+ c.receive(timeout) match {
+ case m @ binary.Greet(p) => {
+ Greet(p, MPByeOrHello(m.cont))
+ }
+ case m @ binary.Quit(p) => {
+ Quit(p)
+ }
+ }
+ }
+}
+case class MPByeOrHello(c: Out[binary.ByeOrHello]) {
+ def send(v: Bye) = {
+ val cnt = c ! binary.Bye(v.p)
+ ()
+ }
+ def send(v: Hello) = {
+ val cnt = c !! binary.Hello(v.p)_
+ MPGreetOrQuit(cnt)
+ }
+}
+
+// Classes representing messages (with continuations) in binary sessions
+package object binary {
+ sealed abstract class GreetOrQuit
+ case class Greet(p: String)(val cont: Out[ByeOrHello]) extends GreetOrQuit
+ case class Quit(p: Unit) extends GreetOrQuit
+
+ sealed abstract class ByeOrHello
+ case class Bye(p: String) extends ByeOrHello
+ case class Hello(p: String)(val cont: Out[GreetOrQuit]) extends ByeOrHello
+}
+// -----------------------------------------------------
+
+// -----------------------------------------------------
+// Local type for role c:
+// μ(Greeting_Proto___c__s_).s⊕{Greet(String).s&{Hello(String).Greeting_Proto___c__s_, Bye(String).end}, Quit(Unit).end}
+package test.proto.c
+
+// ... [OMITTED]
+
+// -----------------------------------------------------
+
+
+ We now explain the elements of the above tool output,
+ and how the generated Scala APIs may be used to implement the
+ session endpoints.
+
+
+
+
+ The "Global type" on line 2 is the translation of the Scribble
+ greeting protocol into the formal global types
+ notation of the paper (cf. p.10). Note
+ that Greeting_Proto___c__s_ is an
+ autogenerated recursion variable.
+
+
+ Then, for each role in the protocol (in this
+ case, s
+ and c ), the code between
+ the markers "// ----... "
+ contains:
+
+
+ the local type for the role (lines 5-6 and
+ 57-58), with the formal notation in the paper (§2).
+ The local type is obtained by projecting the
+ global type above onto the role (cf. p.10 and §A.2 in
+ the paper);
+
+
+ a Scala package declaration and some
+ minimal import s (lines 7/10);
+
+
+ a set of case class es
+ representing messages that could be received by
+ the role (lines 12-15). In our greeting protocol example,
+ in the case of role s , such
+ messages/classes are Greet
+ and Quit ; and since they could
+ be received in the same (external) choice, they are
+ grouped by deriving a common
+ autogenerated sealed abstract class
+ MsgMPGreetOrQuit (line 13). In all input message
+ classes, the field p contains
+ the payload of the received message, while
+ the cont field (if present)
+ tells what is the next state of the multiparty
+ session following the message (more details soon);
+
+
+ a set of case class es
+ representing messages that could be sent by the
+ role (lines 17-19). In our example, such messages/classes
+ are Hello
+ and Bye . As above, the
+ field p contains the payload of
+ the message;
+
+
+ a set of multiparty session classes representing
+ the possible states of the multiparty session
+ (lines 21-43). Such classes offer either
+ a send() or
+ a receive() method. In our
+ example, MPGreetOrQuit (lines
+ 22-33) represents the initial state of the greeting
+ protocol, and therefore offers
+ a receive() method (line 23)
+ that could return one of the input messages
+ Greet
+ or Quit (seen above).
+ Instead, MPByeOrHello represents
+ the state where role s must
+ send() one of the output messages
+ Bye
+ or Hello (seen above). The
+ send() methods return
+ the next state of the multiparty session : e.g. ,
+ if Bye is sent (line 35),
+ then () is returned, because the
+ session is terminated; instead,
+ if Hello is sent (line 39), then
+ the method returns an instance
+ of MPGreetOrQuit to continue the
+ session;
+
+
+ finally, a package object binary
+ containing the Scala representation of
+ the linearly-encoded partial projections of the local
+ type . Roughly:
+
+
+ the partial projections isolate
+ the binary , point-to-point interactions between
+ role s and each
+ other role in the multiparty session (in this example,
+ the only other role is c ).
+ See Definition 2.9 in the paper;
+
+
+ its linear encoding , using
+ continuation-passing style, is defined in the paper,
+ section "Encoding of Types" (p.16);
+
+
+ the Scala representation of such a linear
+ encoding is based
+ on this
+ ECOOP'16 paper (§7).
+
+
+
+
+
+
+
+
+ Note that an analogous set of classes (for input/output
+ messages, multiparty sessions, binary channels) is also
+ generated for the other roles of the global protocol. In this
+ example, the only other role is s
+ (for which we have omitted the resulting classes, in lines 59-63).
+
+
+ Using the Scribble-Generated Multiparty Session Classes
+
+
+ Now, the classes in the binary
+ package could be used directly
+ with lchannels (as
+ described here )
+ to implement type-safe point-to-point interactions between each
+ pair of roles involved in a multiparty session. However, doing
+ so has two drawbacks:
+
+
+
+
+ a programmer would be (partly) exposed to the
+ continuation-passing-style
+ of lchannels , and
+
+
+ if a multiparty session has n participants, a
+ programmer implementing one of its participants would need to
+ handle up to n-1 point-to-point channels, without any
+ guidance on their usage order: this can introduce deadlocks
+ (see §7 of the paper). This is not a problem for the
+ greeting protocol (that only has two participants), but will
+ be relevant, e.g. , for the Game
+ and ThreeBuyer examples later on.
+
+
+
+
+ These issues are addressed by the multiparty session
+ classes MPGreetOrQuit
+ and MPByeOrHello (seen above): they offer a
+ simplified interface where a programmer only uses the
+ input/output message classes Greet ,
+ Quit , Bye
+ and Hello (seen above), without
+ visible continuations. The binary classes (and their
+ continuations) are handled
+ internally by MPGreetOrQuit
+ and MPByeOrHello , following the
+ formalisation in §5 of the paper. Moreover, if a session
+ involves more than two participants, the interface ensures that
+ the interactions will not result in a deadlock.
+
+
+
+ Here is an implementation of the greeting protocol server, based
+ on the multiparty session classes above.
+
+
+
+def server(c: MPGreetOrQuit)
+ (implicit timeout: Duration): Unit = {
+ c.receive match {
+ case Greet(whom, cont) => {
+ val c2 = cont.send(Hello(whom)) // Use "cont" to continue the session
+ server(c2) // The return value of .send above allows to keep interacting
+ }
+ case Quit(()) => {
+ () // The session is terminated
+ }
+ }
+}
+
+
+ The code should be pretty self-explanatory. Note that
+ all send calls are type-safe, and the
+ Scala compiler will warn if a .receive match {
+ ... } does not cover all possible input messages
+ (depending on the state of the session). Also note that the
+ multiparty session objects have the same runtime linearity
+ usage rules of the underlying linear channels: i.e. , the
+ programmer is supposed to use each instance exactly
+ once to send/receive a message. If
+ .send() /.receive()
+ is called twice on the same
+ object, lchannels will throw a runtime
+ exception
+ (see the
+ ECOOP'16 paper for more details).
+
+
+
+ One remaining question is: how does one produce an instance
+ of MPGreetOrQuit , to invoke
+ the server function above? Here is an
+ example:
+
+
+
+// Create channel for local client/server interaction...
+val (in, out) = LocalChannel.factory[binary.GreetOrQuit]
+// ...and spawn server and client (note: we wrap channels in sesison objs)
+val sf = Future { server(MPGreetOrQuit(in)) }
+val cf = Future { client(...(out)) }
+
+
+ In other words, the caller creates a pair of linear channel
+ endpoints connecting the client to the server, uses one of them
+ to instantiate MPGreetOrQuit(in) , and
+ passes it to the server function.
+ Note that the type of the parameter
+ of MPGreetOrQuit constrains the
+ carried type of the binary channel, that on line 2 must
+ be
+ binary.GreetOrQuit (or a subtype).
+
+
+
+ The example above uses LocalChannel s
+ for inter-thread communication, but a similar approach can be
+ used with socket-based
+ or Akka actor-based channels (both
+ provided by lchannels ), thus
+ allowing server to interact with a
+ remote client (indeed, the server code
+ is independent from the message transport).
+
+
+ Implementation
+
+
+ An implementation of the greeting protocol, based on the
+ Scribble-generated multiparty session classes, is available in
+ ScribbleGreeting.scala .
+ The file contains the autogenerated classes for both
+ roles s
+ and c , enclosed in different
+ namespaces to avoid clashes.
+
+
+
+ The artifact demo script launches a
+ greeting server and client interacting remotely, using Akka
+ actor-based channels. The implementation, however, includes more
+ examples, with the the greeting client/server launched as
+ interacting threads, or the client talking through a socket with a
+ simple server written in Python .
+ If you want to try them, you can go to
+ the lchannels
+ installation directory and follow
+ the lchannels
+ documentation .
+
+
+
+
+ NOTE:
+ the ScribbleGreeting.scala
+ example is functionally equivalent to the
+ pre-existing Greeting.scala
+ example; the difference is that the latter interacts directly
+ via lchannels without using
+ Scribble-generated multiparty session classes.
+
+
+
+ HTTP (GET fragment)
+
+
+ This example implements fragment of the HTTP/1.1 protocol,
+ focused on the GET method. The purpose is to show that our
+ artifact is flexible enough to handle non-trivial "real-world"
+ protocols, and produce endpoint code that is interoperable with
+ existing (compliant) implementations of such protocols (in this
+ example, existing Web browsers).
+
+
+
+ Similarly to the greeting protocol in
+ the tutorial above , HTTP is a
+ binary protocol --- i.e. , a multiparty protocol with two roles
+ (client and server). Its Scribble specification is
+ available in /home/osboxes/scribble/modules/linmp-scala/src/test/scrib/Http.scr
+ (and also
+ here ).
+ Its only new feature is a recursive protocol block ,
+ that behaves as expected:
+
+
+
+rec X {
+ choice at c {
+ Host(String) from c to s;
+ continue X;
+ } or {
+ UserAgent(String) from c to s;
+ continue X;
+ } or {
+ ...
+ } or {
+ RequestBody(Body) from c to s;
+ do Response(c, s);
+ }
+}
+
+
+ To generate the session classes from the Scribble HTTP specification,
+ you can run:
+
+
+
+cd ~/scribble
+./scribblec-linmp.sh modules/linmp-scala/src/test/scrib/Http.scr Http
+
+ Implementation Details
+
+
+ The Scribble-generated classes have been integrated in the
+ HTTP server implementation. More in detail:
+
+
+
+
+ Running the HTTP Server
+
+
+ To run the HTTP server, you can launch
+ the artifact demo script or follow
+ the lchannels
+ documentation . While the server is running, you can connect
+ to http://127.0.0.1:8080/
+ with your browser. You can also "stress test" the implementation by
+ recursively downloading all the files being served:
+
+
+
+cd /tmp
+wget -l 100 -r http://127.0.0.1:8080/
+
+
+ NOTE: wget might
+ report some transient read errors ("Connection reset by peer"),
+ because our HTTP server does not currently observe the client's
+ keep-alive requests . Still, the download will
+ proceed through a new connection.
+
+
+ Example: Peer-to-Peer Game
+
+
+ This is the main running example in
+ the paper (first introduced in §1),
+ that concisely leverages all MPST features (session with more
+ than two roles, branching, selection, recursion, delegation,
+ inter-role dependencies, projection, merging). Its Scribble
+ specification (corresponding to Figure 9 in the paper) is
+ available
+ in /home/osboxes/scribble/modules/linmp-scala/src/test/scrib/Game.scr
+ (and also
+ here ).
+
+
+
+ Unlike the greeting protocol and
+ HTTP examples, the game protocol involves three
+ roles. Moreover, it features session delegation :
+
+
+
+global protocol ClientA(role p, role q) {
+ PlayA(Game@a) from q to p;
+}
+
+global protocol ClientB(role p, role q) {
+ PlayB(Game@b) from q to p;
+}
+
+global protocol ClientC(role p, role q) {
+ PlayC(Game@c) from q to p;
+}
+
+global protocol Game(role a, role b, role c) {
+ ...
+}
+
+
+ Above, the message PlayA(Game@a)
+ carries as payload a multiparty session channel : it
+ allows the recipient to join an instance of the
+ the Game protocol, by playing the
+ role a . Here,
+ "@ " is the projection
+ operator , and is one of our main additions to Scribble,
+ based on the theory of our paper. It means that the type of the
+ payload of PlayA(Game@a) is determined
+ by projecting Game onto
+ role a (cf. p.10 and §A.2 in the
+ paper; note that before being
+ projected, Game is first converted
+ into the formal syntax for global types). The same intuition
+ applies for messages PlayB(Game@b)
+ and PlayC(Game@c) . As one might
+ expect, the projection of a Scribble global
+ protocol G onto a
+ role r is only allowed
+ if r appears
+ in G 's declaration; e.g. , in this
+ example, Game can only be projected
+ onto a , b
+ and c .
+
+
+ Generating and Using the Session Classes
+
+
+ The Scribble specification above contains multiple global
+ protocols, and each one can be used to generate multiparty
+ protocol classes. Assume that we want to implement a Scala
+ program that talks to the game server, and then plays
+ role a : to do so, we can generate the
+ session classes for the global
+ protocol ClientA , and write our
+ program using those pertaining to role p
+ (recall that, from §1 of the
+ paper, p is the client that receives
+ the game delegation, while q is the
+ game server).
+
+
+
+cd ~/scribble
+./scribblec-linmp.sh modules/linmp-scala/src/test/scrib/Game.scr ClientA
+
+
+ Similarly, to implement roles b
+ and c , we can specify respectively the
+ global protocls ClientB
+ and ClientC , and in both cases,
+ use the Scribble-generated classes for the client
+ role p to write our code.
+
+
+
+ Instead, if we want to implement the server, we will
+ need all the Scribble-generated session classes
+ obtained
+ from ClientA , ClientB
+ and ClientC --- but this time, we will
+ need to write our code using those pertaining to the server
+ role q . The reason is intuitive: the
+ game server will need to interact with clients willing to play
+ any role in the game; therefore, the server must implement the
+ server-side of all the necessary protocols.
+
+
+ Implementation Details
+
+
+
+ The Scribble-generated multiparty session classes for the
+ clients playing
+ roles a , b
+ and c are included, respectively,
+ in examples/game/protocol/ClientA.scala , .../ClientB.scala
+ and .../ClientC.scala .
+
+
+ The Scribble-generated binary channel classes are included
+ in examples/game/protocol/Binary.scala .
+ Note that the file also defines some additional message
+ classes, inside the package actor :
+ they are used by the clients to initiate a connection to the
+ server, when using lchannels with
+ the Akka actor-based transport. After establishing the
+ connection, the continuation of such messages allows the
+ server to answer with
+ a PlayA /PlayB /PlayC
+ message, as expected from the Scribble protocol.
+
+
+ the game server and the three clients are available
+ in examples/game/Server.scala , .../ClientA.scala , .../ClientB.scala
+ and .../ClientC.scala .
+ As explained above, the game server uses the
+ Scribble-generated multiparty session protocol classes
+ necessary to interact with all
+ clients a , b
+ and c . Each game client
+ uses its own classes (e.g. , the implementation of
+ role a does not
+ use b
+ or c 's protocol classes).
+
+
+ After creating and delegating a multiparty session to the
+ clients, the game server terminates: it emphasises that after
+ delegation, the clients actually interact directly, and the
+ server does not route their messages.
+
+
+
+ Running the Game Example
+
+
+ To run the game example, you can launch
+ the artifact demo script or follow
+ the lchannels
+ documentation . Note that the demo script will launch the
+ example using distributed Akka actor-based channels;
+ the lchannels documentation also
+ includes a version where the server and clients run as
+ local threads, using a non-distributed message transport.
+
+
+ Example: Three-Buyer Protocol
+
+
+ This is a widely-used example in MPST literature, inspired by
+ a W3C Web Services
+ Choreography use case (C-UC-001 Travel Agent). We use the
+ version presented in the following paper:
+
+ M. Coppo, M. Dezani-Ciancaglini, N. Yoshida, L. Padovani.
+ "Global Progress for Dynamically Interleaved Multiparty Sessions".
+ Mathematical Structures in Computer Science, 2016
+ (DOI ) .
+
+
+
+ Quoting from the paper:
+
+
+
+
+ The overall scenario, involving a Seller
+ (s ), Alice
+ (a ), Bob
+ (b ) and Carol
+ (c ), proceeds as follows.
+
+
+
+ Alice sends a book title to Seller, then Seller sends back a
+ quote to Alice and Bob. Then Alice tells Bob how much she
+ can contribute.
+
+
+ If the price is within Bob’s budget, Bob notifies both
+ Seller and Alice he accepts, then sends his address, and
+ Seller sends back the delivery date.
+
+
+ If the price exceeds the budget, Bob asks Carol to
+ collaborate together by establishing a new session. Then Bob
+ sends how much Carol must pay, then delegates the remaining
+ interactions with Alice and Seller to Carol.
+
+
+ If the rest of the price is within Carol’s budget, Carol
+ accepts the quote and notifies Alice, Bob and Seller, and
+ continues the rest of the protocol with Seller and Alice
+ transparently, as if she were Bob. Otherwise she notifies
+ Alice, Bob and Seller to quit the protocol.
+
+
+
+
+
+ Similarly to the multiparty game , this example
+ includes a three-party session --- here, involving the Seller
+ and the two buyers Alice and Bob. Depending on Alice's choice,
+ a third buyer (Carol) could be transparently involved by Bob,
+ using session delegation.
+
+
+
+ The Scribble specification of this example is available
+ in /home/osboxes/scribble/modules/linmp-scala/src/test/scrib/ThreeBuyer.scr
+ (and also
+ here ):
+
+
+
+global protocol PlayAlice(role alice, role seller) {
+ PlayAlice(TwoBuyer@alice) from seller to alice;
+}
+
+global protocol PlayBob(role bob, role seller) {
+ PlayBob(TwoBuyer@bob) from seller to bob;
+}
+
+global protocol TwoBuyer(role alice, role bob, role seller) {
+ Title(String) from alice to seller;
+ QuoteA(Int) from seller to alice;
+ QuoteB(Int) from seller to bob;
+ ShareA(Int) from alice to bob;
+ do TwoBuyerChoice(alice, bob, seller);
+}
+
+aux global protocol TwoBuyerChoice(role alice, role bob, role seller) {
+ choice at bob {
+ OkA() from bob to alice;
+ OkS() from bob to seller;
+ Address(String) from bob to seller;
+ Deliver(ZonedDateTime) from seller to bob;
+ } or {
+ QuitA() from bob to alice;
+ QuitS() from bob to seller;
+ }
+}
+
+
+global protocol Delegation(role carol, role bob) {
+ Contrib(Int) from bob to carol; // Should be the Int on line 19
+ Delegate(TwoBuyerChoice@bob) from bob to carol;
+ choice at carol {
+ OkC() from carol to bob;
+ } or {
+ QuitC() from carol to bob;
+ }
+}
+
+
+ We model this example by letting the Seller behave similarly to
+ the server in the multiparty game . The Seller
+ will wait for Alice's and Bob's connections, and then it will
+ create a three-party session (based on
+ the TwoBuyer protocol, on line 9)
+ involving the Seller itself, Alice and Bob. For this purpose,
+ the Seller first establishes binary sessions with Alice and Bob
+ (based on the PlayAlice
+ and PlayBob protocols, on lines 1 and
+ 5); after they are connected, the Seller sends them
+ the PlayAlice
+ and PlayBob messages (lines 2 and 6),
+ using delegation to let them join the three-party session.
+
+
+
+ The global protocol Delegation only
+ involves Carol and Bob, and is used by the former to let Carol
+ know how much she should contribute, and then pass her what is
+ left of Bob's duties in the TwoBuyer
+ session (specifically, Bob delegates
+ the TwoBuyerChoice part of the session).
+
+
+
+ Generating and Using the Session Classes
+
+
+ The Scribble specification above contains multiple global
+ protocols, and each one can be used to generate multiparty
+ protocol classes. Assume that we want to implement a Scala
+ program that talks to the Seller, and then plays
+ role Alice : to do so, we can generate the
+ session classes for the global
+ protocol PlayAlice , and write our
+ program using those pertaining to role alice .
+
+
+
+cd ~/scribble
+./scribblec-linmp.sh modules/linmp-scala/src/test/scrib/ThreeBuyer.scr PlayAlice
+
+
+ Similarly, to implement role bob , we
+ can specify the global
+ protocls PlayBob , and use the
+ Scribble-generated classes for role
+ bob to write our code. Note, however,
+ that to fully implement Bob and his interaction with Carol, we
+ will also need bob 's session classes
+ generated from the Delegation global
+ protocol.
+
+
+
+ Instead, if we want to implement the Seller, we will
+ need all the Scribble-generated session classes
+ obtained from PlayAlice
+ and PlayBob , using those pertaining to
+ role seller . The reason is that
+ Seller will need to interact with both Alice and Bob, and must
+ therefore implement the "other side" of both their protocols.
+
+
+
+ Finally, if we want to implement Carol, we need to specify the
+ global protocol Delegation , and then
+ use carol 's session classes.
+
+
+ Implementation Details
+
+
+ This implementation adopts several solutions already seen in
+ the game example .
+
+
+
+
+ The Scribble-generated multiparty session classes for Seller, Alice,
+ Bob and Carol are included, respectively,
+ in examples/threebuyer/protocol/Seller.scala ,
+ .../Alice.scala , .../Bob.scala
+ and .../Carol.scala .
+
+
+ The Scribble-generated binary channel classes are included
+ in examples/threebuyer/protocol/Binary.scala .
+ Note that the file also defines some additional message
+ classes, inside the package actor :
+ they are used with the lchannels
+ Akka-based transport, to initiate connections between
+ Alice/Bob and the Seller, and also (when necessary) between
+ Bob and Carol. After establishing such connections, the
+ continuation of such messages allow to send/receive
+ PlayAlice /PlayBob
+ and Contrib messages, as expected
+ from the Scribble protocol.
+
+
+ the Seller, Alice, Bob and Carol source code is available
+ in examples/threebuyer/Seller.scala , .../Alice.scala , .../Bob.scala
+ and .../Carol.scala .
+ As explained above, the Seller uses the
+ Scribble-generated multiparty session protocol classes needed
+ to interact with both alice
+ and bob ; notably, it
+ does not have any knowledge about
+ carol , because she might
+ transparently implement part of Bob's session. Alice and
+ Bob use their own classes (i.e. , the implementation
+ of role bob does not
+ use alice 's protocol classes). Bob
+ and Carol, instead, share several session class definitions,
+ to realise the session delegation.
+
+
+ Alice's implementation lets the user choose which book to buy:
+ this allows to observe the various scenarios of the protocol,
+ that might involve Carol, or not.
+
+
+
+ Running the ThreeBuyer Example
+
+
+ To run the game example, you can launch
+ the artifact demo script or follow
+ the lchannels
+ documentation . Note that the demo script will launch the
+ example using distributed Akka actor-based channels;
+ the lchannels documentation also
+ includes a version where the server and clients run as
+ local threads, using a non-distributed message transport.
+
+
+ Notes on Scribble with Scala API Extension
+
+ Implementation
+
+
+
+ Known Limitations
+
+
+
+ The error messages generated by Scribble can be a bit obscure,
+ and require the reader to be familiar with the theory of our
+ ECOOP'17 paper,
+ and other
+ Scribble papers .
+
+
+ Session and π-calculus typing systems
+ are structural , whereas the Scala type system
+ is nominal . Encoding the types of the former into
+ those of the latter requires introducing naming conventions
+ and namespaces. This can be done in many ways; our current
+ approach tries to be as direct as possible, but imposes some
+ restrictions on message labels: see
+ e.g. this
+ example , that terminates with an error about incompatible
+ Scala class definitions.
+
+
+ Some autogenerated class names can be quite long, as mentioned
+ in the HTTP example : e.g. , a choice among
+ messages A , B , C ,... will
+ be called AOrBOrC... . Ideally,
+ Scribble should provide a way to annotate choices, giving them
+ a human-readable name that will then be used for autogenerated
+ classes. This would also allow to address the message label
+ restrictions mentioned above.
+
+
+
+
+ Building Scribble-Scala from Source
+
+ Prerequisites
+
+
+
+ Git
+
+
+ A Java 8 compiler
+
+
+ Apache Maven
+
+
+
+ Command Line Instructions
+
+
+
+
+ If all goes well, you will finally see a report like this:
+
+
+
+ [INFO] ------------------------------------------------------------------------
+ [INFO] Reactor Summary:
+ [INFO]
+ [INFO] Scribble .......................................... SUCCESS [7.924s]
+ [INFO] Scribble::Modules::Core ........................... SUCCESS [3:29.963s]
+ [INFO] Scribble::Modules::Parser ......................... SUCCESS [7.706s]
+ [INFO] Scribble::Modules::CLI ............................ SUCCESS [55.630s]
+ [INFO] linmp-scala ....................................... SUCCESS [1.763s]
+ [INFO] Scribble::Distribution ............................ SUCCESS [2.364s]
+ [INFO] Scribble::Modules::Demos .......................... SUCCESS [0.145s]
+ [INFO] ------------------------------------------------------------------------
+ [INFO] BUILD SUCCESS
+ [INFO] ------------------------------------------------------------------------
+ [INFO] Total time: 4:45.961s
+ [INFO] Finished at: Thu Jun 22 22:32:21 BST 2017
+ [INFO] Final Memory: 30M/203M
+ [INFO] ------------------------------------------------------------------------
+
+
+
+ Scribble-Scala is now ready to use via the scribblec-linmp.sh script in the project root directory, e.g. , following the instructions here .
+
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/.gitignore b/src/main/jbake/assets/docs/scribble/.gitignore
new file mode 100644
index 0000000..7d5e6d7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/.gitignore
@@ -0,0 +1,57 @@
+target/
+classes/
+tools/eclipse/plugins/org.scribble.*/bin/
+*.jar
+.classpath
+.project
+.checkstyle
+.settings/
+.DS_Store
+*~
+*.swp
+bin/scribblec.sh
+*.bak
+
+javadoc
+
+#modules/demos/scrib
+
+modules/demos/scrib/betty16/src/betty16/lec1/httplong/HttpLong/
+modules/demos/scrib/betty16/src/betty16/lec1/httpshort/HttpShort/
+modules/demos/scrib/betty16/src/betty16/lec2/adder/Adder/
+modules/demos/scrib/betty16/src/betty16/lec2/smtp/Smtp/
+modules/demos/scrib/bettybook/src/bettybook/http/longv/HttpLong/
+modules/demos/scrib/bettybook/src/bettybook/http/shortv/HttpShort/
+modules/demos/scrib/bettybook/src/bettybook/math/scrib/Math/
+modules/demos/scrib/coco/src/coco/fibo/Fibo/
+modules/demos/scrib/coco/src/coco/smtp/Smtp/
+modules/demos/scrib/fase16/src/fase16/adder/Adder/
+modules/demos/scrib/fase16/src/fase16/smtp/Smtp/
+modules/demos/scrib/fib/src/fib/Fib/
+modules/demos/scrib/http/src/http/longvers/HttpLong/
+modules/demos/scrib/http/src/http/shortvers/HttpShort/
+modules/demos/scrib/loan/src/loan/LoanApplication/
+modules/demos/scrib/smtp/src/smtp/Smtp/
+modules/demos/scrib/travel/src/travel/Travel/
+
+#modules/core/src/test/scrib
+
+modules/core/src/test/scrib/exercise/calculator/EProtocol/
+modules/core/src/test/scrib/exercise/voting/EProtocol/
+
+modules/core/src/test/scrib/demo/fase17/travel/TravelAgent/
+modules/core/src/test/scrib/demo/fase17/travel2/TravelAgent2/
+
+modules/core/src/test/scrib/scratch/scratch1/
+modules/core/src/test/scrib/scratch/scratch2/
+modules/core/src/test/scrib/test/foo/Foo/
+modules/core/src/test/scrib/test/test1/
+modules/core/src/test/scrib/test/test2/
+modules/core/src/test/scrib/test/test3/
+modules/core/src/test/scrib/test/test4/
+modules/core/src/test/scrib/test/test5/
+modules/core/src/test/scrib/test/test6/
+modules/core/src/test/scrib/test/test7/
+modules/core/src/test/scrib/test/test8/
+modules/core/src/test/scrib/test/test9/
+
diff --git a/src/main/jbake/assets/docs/scribble/CHANGES b/src/main/jbake/assets/docs/scribble/CHANGES
new file mode 100644
index 0000000..a51d35f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/CHANGES
@@ -0,0 +1,27 @@
+Scribble 0.4
+---
+
+- Updated scribblec script interface: check
+
+ ./scribblec --help
+
+ for new usages.
+
+- The core tooling has been refactored to support an alternative validation
+ method. To obtain more details on safety/progress errors, use the -V
+ command line flag.
+
+ N.B. the -oldwf command line flag allows to use only the well-formedness
+ from the previous version of the tool. Warning: no guarantees are offered
+ when validating newer Scribble features using -oldwf.
+
+- Most Scribble demos that were previously in
+
+ modules/core/src/test/scrib/demo
+
+ have been relocated to
+
+ modules/demos/scrib/
+
+ Some demo files have also been renamed or repackaged.
+
\ No newline at end of file
diff --git a/src/main/jbake/assets/docs/scribble/README b/src/main/jbake/assets/docs/scribble/README
new file mode 100644
index 0000000..e9283ec
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/README
@@ -0,0 +1,103 @@
+README for
+
+ scribble-dist-0.4-SNAPSHOT.zip
+
+This distribution is built from the master branch
+
+ https://github.com/scribble/scribble-java/tree/master
+
+by
+
+ mvn [clean] install
+
+(Output written to dist/target)
+
+
+Issue reporting
+---
+
+Bugs and issues can be reported via the github Issues facility.
+
+Or email rhu1234 [at] doc.ic.ac.uk excluding the 1234.
+
+
+Scribble distribution zip contents:
+---
+
+- lib jars needed to run the scribble-java tool
+- scribblec.sh script for running the command line tool
+
+
+Command line usage:
+---
+
+> List command line options.
+
+ ./scribblec.sh --help
+
+
+> Check well-formedness of global protocols in, e.g., Test.scr.
+
+ ./scribblec.sh Test.scr
+
+Notes:
+- Use the -oldwf command line flag to use the simpler syntactic protocol
+ well-formedness from the previous version of Scribble.
+- Use the -V command line flag to obtain full traces for errors according
+ to the new protocol validation (and other details).
+
+
+> Project local protocol for role "C" of protocol "Proto" in Test.scr
+
+ ./scribblec.sh Test.scr -project Proto C
+
+
+> Print dot representation of Endpoint FSM for role "C" of protocol "Proto"
+ in Test.scr
+
+ ./scribblec.sh Test.scr -fsm Proto C
+
+
+> Generate Java Endpoint API for role "C" of protocol "Proto"
+ in Test.scr
+
+ ./scribblec.sh -d . Test.scr -api Proto C
+
+
+Examples:
+---
+
+> To make an HelloWorld test:
+
+ echo 'module Test; global protocol Proto(role C, role S) {
+ Hello() from C to S; }' > Test.scr
+
+
+Further examples can be found in:
+
+ https://github.com/scribble/scribble-java/tree/master/modules/demos/scrib
+
+The distribution zip does not include these examples. They can be obtained
+as part of the source repository, or separately via the above link.
+
+> E.g. To generate the Java Endpoint API for role "C" of the "Adder"
+ protocol from http://www.doc.ic.ac.uk/~rhu/scribble/fase16.pdf
+
+ ./scribblec.sh -d modules/demos/scrib/fase16/src
+ modules/demos/scrib/fase16/src/fase16/adder/Adder.scr -api Adder C
+
+
+Alternative command line usage:
+---
+
+To run the Scribble tool directly via java:
+
+ try scribblec.sh with the --verbose flag
+
+to see the underlying java command with main class, classpath and other args.
+
+Or try (from Nick Ng):
+
+ $ mvn dependency:build-classpath -Dmdep.outputFile=classpath
+ $ java -cp $(cat dist/classpath)
+ org.scribble.cli.CommandLine [args] MyModule.scr
diff --git a/src/main/jbake/assets/docs/scribble/dist/pom.xml b/src/main/jbake/assets/docs/scribble/dist/pom.xml
new file mode 100644
index 0000000..befda79
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/dist/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+ 4.0.0
+
+
+ org.scribble
+ parent
+ 0.4-SNAPSHOT
+
+
+ scribble-dist
+ pom
+ Scribble::Distribution
+ Scribble distribution.
+
+
+
+ org.scribble
+ scribble-core
+
+
+ org.scribble
+ scribble-parser
+
+
+ org.scribble
+ scribble-cli
+
+
+ org.scribble
+ linmp-scala
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ src/main/scripts/distro-assembly.xml
+
+ false
+
+
+
+ distro-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/dist/src/main/resources/scribblec.sh b/src/main/jbake/assets/docs/scribble/dist/src/main/resources/scribblec.sh
new file mode 100644
index 0000000..78a33ab
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/dist/src/main/resources/scribblec.sh
@@ -0,0 +1,182 @@
+#!/bin/sh
+
+# Directory containing Scribble jars
+LIB=lib
+
+# antlr 3.2 location (if no lib jar)
+ANTLR=
+ # e.g. '/cygdrive/c/Users/[User]/.m2/repository/org/antlr/antlr-runtime/3.2/antlr-runtime-3.2.jar'
+
+PRG=`basename "$0"`
+DIR=`dirname "$0"` # Non Cygwin..
+#DIR=`dirname "$0"`/.. # Cygwin
+#BASEDIR=$(dirname $0)
+
+usage() {
+ echo Usage: 'scribblec.sh [option]... [option]...'
+ cat < Source Scribble module (.scr file)
+
+Options:
+ -h, --help Show this info and exit$
+ -V Scribble debug info$
+ --verbose Echo the java command$
+
+ -ip Scribble module import path$
+
+ -oldwf Use the simpler syntactic protocol well-formedness
+
+
+ -project Project protocol
+
+ -fsm Generate default Endpoint FSM
+ -aut Output as aut (instead of dot)
+ -fsmpng
+ Draw default Endpoint FSM as png (via dot)
+ -vfsm, -ufsm
+ -vfsmpng, -ufsmpng
+ Output the EFSM used in validation (or the "unfair" variant)
+ -minlts
+ Minimise EFSMs for dot output and API generation (but not validation)
+ (Requires ltsconvert)
+
+
+ -model Generate global model
+ -modelpng
+ Draw global model as png (requires dot)
+ -fair Assume fair output choices
+ -umodel, -umodelpng (with appropriate args) "Unfair" variant
+
+
+ -api Generate Java Endpoint API
+ -d API output directory
+ -sessapi Generate Session API only
+ -chanapi Generate State Channel API only
+ -subtypes Enable subtypes for -chanapi
+EOF
+}
+
+fixpath() {
+ windows=0
+
+ if [ `uname | grep -c CYGWIN` -ne 0 ]; then
+ windows=1
+ fi
+
+ cp="$1"
+ if [ "$windows" = 1 ]; then
+ cygpath -pw "$cp"
+ else
+ echo "$cp"
+ fi
+}
+
+ARGS=
+
+CLASSPATH=$DIR'/modules/cli/target/classes/'
+CLASSPATH=$CLASSPATH':'$DIR'/modules/core/target/classes'
+CLASSPATH=$CLASSPATH':'$DIR'/modules/parser/target/classes'
+CLASSPATH=$CLASSPATH':'$DIR'/modules/f17/target/classes'
+CLASSPATH=$CLASSPATH':'$ANTLR
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/antlr.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/antlr-runtime.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/commons-io.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/scribble-cli.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/scribble-core.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/scribble-parser.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/scribble-f17.jar'
+CLASSPATH=$CLASSPATH':'$DIR'/'$LIB'/stringtemplate.jar'
+CLASSPATH="'"`fixpath "$CLASSPATH"`"'"
+
+usage=0
+verbose=0
+dot=0
+nondot=0
+
+while true; do
+ case "$1" in
+ "")
+ break
+ ;;
+ #-dot)
+ # # Should not be used in conjunction with other flags..
+ # # ..that output to stdout
+ # ARGS="$ARGS '-fsm'"
+ # shift
+ # ARGS="$ARGS '$1'"
+ # shift
+ # ARGS="$ARGS '$1'"
+ # shift
+ # dot=$1
+ # if [ "$dot" == '' ]; then
+ # echo '-dot missing output file name argument'
+ # exit 1
+ # fi
+ # shift
+ # ;;
+ -h)
+ usage=1
+ break
+ ;;
+ --help)
+ usage=1
+ break
+ ;;
+ --verbose)
+ verbose=1
+ shift
+ ;;
+ #-ip)
+ # ARGS="$ARGS '$1'"
+ # shift
+ # ;;
+ #-d)
+ # ARGS="$ARGS '$1'"
+ # shift
+ # ;;
+ #-subtypes)
+ # ARGS="$ARGS '$1'"
+ # shift
+ # ;;
+ #-*)
+ # nondot=1
+ # ARGS="$ARGS '$1'"
+ # shift
+ # ;;
+ *)
+ ARGS="$ARGS '$1'"
+ shift
+ ;;
+ esac
+done
+
+#if [ "$dot" != 0 ]; then
+# if [ $nondot == 1 ]; then
+# echo '-dot cannot be used in conjunction with other flags that output to stdout: ' $ARGS
+# exit 1
+# fi
+# ARGS="$ARGS |"
+# ARGS="$ARGS dot"
+# ARGS="$ARGS '-Tpng'"
+# ARGS="$ARGS '-o'"
+# ARGS="$ARGS '$dot'"
+#fi
+
+if [ "$usage" = 1 ]; then
+ usage
+ exit 0
+fi
+
+CMD='java -cp '$CLASSPATH' org.scribble.cli.CommandLine'
+
+scribblec() {
+ eval $CMD "$@"
+}
+
+if [ "$verbose" = 1 ]; then
+ echo $CMD "$ARGS"
+fi
+
+scribblec "$ARGS"
+
diff --git a/src/main/jbake/assets/docs/scribble/dist/src/main/scripts/distro-assembly.xml b/src/main/jbake/assets/docs/scribble/dist/src/main/scripts/distro-assembly.xml
new file mode 100644
index 0000000..f23279a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/dist/src/main/scripts/distro-assembly.xml
@@ -0,0 +1,49 @@
+
+
+
+
+ distribution
+
+ zip
+
+
+ false
+ ${project.build.finalName}
+
+
+
+ src/main/resources
+
+
+
+
+
+
+ lib
+ false
+ ${artifact.artifactId}.${artifact.extension}
+ false
+ 0644
+ 0755
+
+
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/LICENSE.txt b/src/main/jbake/assets/docs/scribble/modules/cli/LICENSE.txt
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/LICENSE.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/build.properties b/src/main/jbake/assets/docs/scribble/modules/cli/build.properties
new file mode 100644
index 0000000..b844f8d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/build.properties
@@ -0,0 +1,19 @@
+source.. = src/main/java/,\
+ target/generated-sources/antlr3/
+output.. = bin/
+bin.includes = META-INF/,\
+ .,\
+ src/main/resources/lib/antlr-3.2.jar,\
+ OSGI-INF/,\
+ LICENSE.txt
+src.includes = target/generated-sources/,\
+ src/,\
+ pom.xml,\
+ build.properties,\
+ META-INF/,\
+ .project,\
+ .classpath,\
+ OSGI-INF/,\
+ LICENSE.txt
+jars.compile.order = .,\
+ lib/antlr-3.2.jar
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/pom.xml b/src/main/jbake/assets/docs/scribble/modules/cli/pom.xml
new file mode 100644
index 0000000..3c19966
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/pom.xml
@@ -0,0 +1,37 @@
+
+ 4.0.0
+ scribble-cli
+ jar
+ Scribble::Modules::CLI
+
+
+ org.scribble
+ parent
+ 0.4-SNAPSHOT
+ ../../pom.xml
+
+
+
+
+ org.scribble
+ scribble-core
+
+
+ org.scribble
+ scribble-parser
+
+
+
+ commons-io
+ commons-io
+
+
+
+ junit
+ junit
+ test
+
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLine.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLine.java
new file mode 100644
index 0000000..e60b8fb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLine.java
@@ -0,0 +1,496 @@
+package org.scribble.cli;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import org.scribble.ast.Module;
+import org.scribble.ast.ProtocolDecl;
+import org.scribble.ast.global.GProtocolDecl;
+import org.scribble.main.Job;
+import org.scribble.main.JobContext;
+import org.scribble.main.MainContext;
+import org.scribble.main.RuntimeScribbleException;
+import org.scribble.main.ScribbleException;
+import org.scribble.main.resource.DirectoryResourceLocator;
+import org.scribble.main.resource.ResourceLocator;
+import org.scribble.model.endpoint.EGraph;
+import org.scribble.model.global.SGraph;
+import org.scribble.sesstype.name.GProtocolName;
+import org.scribble.sesstype.name.LProtocolName;
+import org.scribble.sesstype.name.Role;
+import org.scribble.util.ScribParserException;
+import org.scribble.util.ScribUtil;
+
+public class CommandLine
+{
+ protected enum ArgFlag
+ {
+ // Unique flags
+ JUNIT, // For internal use (JUnit test harness)
+ MAIN_MOD,
+ IMPORT_PATH,
+ VERBOSE,
+ SCHAN_API_SUBTYPES,
+ OLD_WF,
+ NO_LIVENESS,
+ LTSCONVERT_MIN, // Currently only affects EFSM output (i.e. -fsm..) and API gen -- doesn't affect validation
+ FAIR,
+ NO_LOCAL_CHOICE_SUBJECT_CHECK,
+ NO_ACCEPT_CORRELATION_CHECK,
+ DOT,
+ AUT,
+ NO_VALIDATION,
+ NO_MODULE_NAME_CHECK,
+ INLINE_MAIN_MOD,
+ F17,
+
+ // Non-unique flags
+ PROJECT,
+ API_OUTPUT,
+ EFSM,
+ VALIDATION_EFSM,
+ UNFAIR_EFSM,
+ UNFAIR_EFSM_PNG,
+ EFSM_PNG,
+ VALIDATION_EFSM_PNG,
+ SGRAPH,
+ UNFAIR_SGRAPH,
+ SGRAPH_PNG,
+ UNFAIR_SGRAPH_PNG,
+ API_GEN,
+ SESS_API_GEN,
+ SCHAN_API_GEN,
+ }
+
+ private final Map args; // Maps each flag to list of associated argument values
+
+ public CommandLine(String... args) throws CommandLineException
+ {
+ this.args = new CommandLineArgParser(args).getArgs();
+ if (!this.args.containsKey(ArgFlag.MAIN_MOD) && !this.args.containsKey(ArgFlag.INLINE_MAIN_MOD))
+ {
+ throw new CommandLineException("No main module has been specified\r\n");
+ }
+ }
+
+ public static void main(String[] args) throws CommandLineException, ScribbleException
+ {
+ try
+ {
+ new CommandLine(args).run();
+ }
+ catch (ScribParserException | CommandLineException e)
+ {
+ System.err.println(e.getMessage()); // No need to give full stack trace, even for debug, for command line errors
+ System.exit(1);
+ }
+ catch (RuntimeScribbleException e)
+ {
+ System.err.println(e.getMessage());
+ System.exit(1);
+ }
+ }
+
+ public void run() throws ScribbleException, CommandLineException, ScribParserException
+ {
+ try
+ {
+ Job job = newJob(newMainContext());
+ ScribbleException fail = null;
+ try
+ {
+ /*// Scribble extensions (custom Job passes)
+ if (this.args.containsKey(ArgFlag.F17))
+ {
+ GProtocolName simpname = new GProtocolName(this.args.get(ArgFlag.F17)[0]);
+ F17Main.parseAndCheckWF(job, simpname); // Includes base passes
+ }
+
+ // Base Scribble
+ else*/
+ {
+ job.checkWellFormedness();
+ }
+ }
+ catch (ScribbleException x)
+ {
+ fail = x;
+ }
+ try
+ {
+ // Following must be ordered appropriately -- ?
+ if (this.args.containsKey(ArgFlag.PROJECT))
+ {
+ outputProjections(job);
+ }
+ if (this.args.containsKey(ArgFlag.EFSM))
+ {
+ outputEGraph(job, true, true);
+ }
+ if (this.args.containsKey(ArgFlag.VALIDATION_EFSM))
+ {
+ outputEGraph(job, false, true);
+ }
+ if (this.args.containsKey(ArgFlag.UNFAIR_EFSM))
+ {
+ outputEGraph(job, false, false);
+ }
+ if (this.args.containsKey(ArgFlag.EFSM_PNG))
+ {
+ drawEGraph(job, true, true);
+ }
+ if (this.args.containsKey(ArgFlag.VALIDATION_EFSM_PNG))
+ {
+ drawEGraph(job, false, true);
+ }
+ if (this.args.containsKey(ArgFlag.UNFAIR_EFSM_PNG))
+ {
+ drawEGraph(job, false, false);
+ }
+ if (this.args.containsKey(ArgFlag.SGRAPH) || this.args.containsKey(ArgFlag.SGRAPH_PNG)
+ || this.args.containsKey(ArgFlag.UNFAIR_SGRAPH) || this.args.containsKey(ArgFlag.UNFAIR_SGRAPH_PNG))
+ {
+ if (job.useOldWf)
+ {
+ throw new CommandLineException("Global model flag(s) incompatible with: " + CommandLineArgParser.OLD_WF_FLAG);
+ }
+ if (this.args.containsKey(ArgFlag.SGRAPH))
+ {
+ outputSGraph(job, true);
+ }
+ if (this.args.containsKey(ArgFlag.UNFAIR_SGRAPH))
+ {
+ outputSGraph(job, false);
+ }
+ if (this.args.containsKey(ArgFlag.SGRAPH_PNG))
+ {
+ drawSGraph(job, true);
+ }
+ if (this.args.containsKey(ArgFlag.UNFAIR_SGRAPH_PNG))
+ {
+ drawSGraph(job, false);
+ }
+ }
+ }
+ catch (ScribbleException x)
+ {
+ if (fail == null)
+ {
+ fail = x;
+ }
+ }
+ if (fail != null)
+ {
+ throw fail;
+ }
+
+ if (this.args.containsKey(ArgFlag.SESS_API_GEN))
+ {
+ outputSessionApi(job);
+ }
+ if (this.args.containsKey(ArgFlag.SCHAN_API_GEN))
+ {
+ outputStateChannelApi(job);
+ }
+ if (this.args.containsKey(ArgFlag.API_GEN))
+ {
+ outputEndpointApi(job);
+ }
+ }
+ catch (ScribbleException e) // Wouldn't need to do this if not Runnable (so maybe change)
+ {
+ if (this.args.containsKey(ArgFlag.JUNIT) || this.args.containsKey(ArgFlag.VERBOSE))
+ {
+ /*RuntimeScribbleException ee = new RuntimeScribbleException(e.getMessage());
+ ee.setStackTrace(e.getStackTrace());
+ throw ee;*/
+ throw e;
+ }
+ else
+ {
+ System.err.println(e.getMessage()); // JUnit harness looks for an exception
+ System.exit(1);
+ }
+ }
+ }
+
+ // FIXME: option to write to file, like classes
+ private void outputProjections(Job job) throws CommandLineException, ScribbleException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = this.args.get(ArgFlag.PROJECT);
+ for (int i = 0; i < args.length; i += 2)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ Role role = checkRoleArg(jcontext, fullname, args[i+1]);
+ Map projections = job.getProjections(fullname, role);
+ System.out.println("\n" + projections.values().stream().map((p) -> p.toString()).collect(Collectors.joining("\n\n")));
+ }
+ }
+
+ // dot/aut text output
+ // forUser: true means for API gen and general user info (may be minimised), false means for validation (non-minimised, fair or unfair)
+ // (forUser && !fair) should not hold, i.e. unfair doesn't make sense if forUser
+ private void outputEGraph(Job job, boolean forUser, boolean fair) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = forUser ? this.args.get(ArgFlag.EFSM) : (fair ? this.args.get(ArgFlag.VALIDATION_EFSM) : this.args.get(ArgFlag.UNFAIR_EFSM));
+ for (int i = 0; i < args.length; i += 2)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ Role role = checkRoleArg(jcontext, fullname, args[i+1]);
+ EGraph fsm = getEGraph(job, fullname, role, forUser, fair);
+ String out = this.args.containsKey(ArgFlag.AUT) ? fsm.toAut() : fsm.toDot();
+ System.out.println("\n" + out); // Endpoint graphs are "inlined" (a single graph is built)
+ }
+ }
+
+ private void drawEGraph(Job job, boolean forUser, boolean fair) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = forUser ? this.args.get(ArgFlag.EFSM_PNG) : (fair ? this.args.get(ArgFlag.VALIDATION_EFSM_PNG) : this.args.get(ArgFlag.UNFAIR_EFSM_PNG));
+ for (int i = 0; i < args.length; i += 3)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ Role role = checkRoleArg(jcontext, fullname, args[i+1]);
+ String png = args[i+2];
+ EGraph fsm = getEGraph(job, fullname, role, forUser, fair);
+ runDot(fsm.toDot(), png);
+ }
+ }
+
+ // Endpoint graphs are "inlined", so only a single graph is built (cf. projection output)
+ private EGraph getEGraph(Job job, GProtocolName fullname, Role role, boolean forUser, boolean fair)
+ throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ GProtocolDecl gpd = (GProtocolDecl) jcontext.getMainModule().getProtocolDecl(fullname.getSimpleName());
+ if (gpd == null || !gpd.header.roledecls.getRoles().contains(role))
+ {
+ throw new CommandLineException("Bad FSM construction args: " + Arrays.toString(this.args.get(ArgFlag.DOT)));
+ }
+ EGraph graph;
+ if (forUser) // The (possibly minimised) user-output EFSM for API gen
+ {
+ graph = this.args.containsKey(ArgFlag.LTSCONVERT_MIN)
+ ? jcontext.getMinimisedEGraph(fullname, role) : jcontext.getEGraph(fullname, role);
+ }
+ else // The (possibly unfair-transformed) internal EFSM for validation
+ {
+ graph = //(!this.args.containsKey(ArgFlag.FAIR) && !this.args.containsKey(ArgFlag.NO_LIVENESS)) // Cf. GlobalModelChecker.getEndpointFSMs
+ !fair
+ ? jcontext.getUnfairEGraph(fullname, role) : jcontext.getEGraph(fullname, role);
+ }
+ if (graph == null)
+ {
+ throw new RuntimeScribbleException("Shouldn't see this: " + fullname); // Should be suppressed by an earlier failure
+ }
+ return graph;
+ }
+
+ private void outputSGraph(Job job, boolean fair) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = fair ? this.args.get(ArgFlag.SGRAPH) : this.args.get(ArgFlag.UNFAIR_SGRAPH);
+ for (int i = 0; i < args.length; i += 1)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ SGraph model = getSGraph(job, fullname, fair);
+ String out = this.args.containsKey(ArgFlag.AUT) ? model.toAut() : model.toDot();
+ System.out.println("\n" + out);
+ }
+ }
+
+ private void drawSGraph(Job job, boolean fair) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = fair ? this.args.get(ArgFlag.SGRAPH_PNG) : this.args.get(ArgFlag.UNFAIR_SGRAPH_PNG);
+ for (int i = 0; i < args.length; i += 2)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ String png = args[i+1];
+ SGraph model = getSGraph(job, fullname, fair);
+ runDot(model.toDot(), png);
+ }
+ }
+
+ private static SGraph getSGraph(Job job, GProtocolName fullname, boolean fair) throws ScribbleException
+ {
+ JobContext jcontext = job.getContext();
+ SGraph model = fair ? jcontext.getSGraph(fullname) : jcontext.getUnfairSGraph(fullname);
+ if (model == null)
+ {
+ throw new RuntimeScribbleException("Shouldn't see this: " + fullname); // Should be suppressed by an earlier failure
+ }
+ return model;
+ }
+
+ private void outputEndpointApi(Job job) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = this.args.get(ArgFlag.API_GEN);
+ for (int i = 0; i < args.length; i += 2)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ Map sessClasses = job.generateSessionApi(fullname);
+ outputClasses(sessClasses);
+ Role role = checkRoleArg(jcontext, fullname, args[i+1]);
+ Map scClasses = job.generateStateChannelApi(fullname, role, this.args.containsKey(ArgFlag.SCHAN_API_SUBTYPES));
+ outputClasses(scClasses);
+ }
+ }
+
+ private void outputSessionApi(Job job) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = this.args.get(ArgFlag.SESS_API_GEN);
+ for (String fullname : args)
+ {
+ GProtocolName gpn = checkGlobalProtocolArg(jcontext, fullname);
+ Map classes = job.generateSessionApi(gpn);
+ outputClasses(classes);
+ }
+ }
+
+ private void outputStateChannelApi(Job job) throws ScribbleException, CommandLineException
+ {
+ JobContext jcontext = job.getContext();
+ String[] args = this.args.get(ArgFlag.SCHAN_API_GEN);
+ for (int i = 0; i < args.length; i += 2)
+ {
+ GProtocolName fullname = checkGlobalProtocolArg(jcontext, args[i]);
+ Role role = checkRoleArg(jcontext, fullname, args[i+1]);
+ Map classes = job.generateStateChannelApi(fullname, role, this.args.containsKey(ArgFlag.SCHAN_API_SUBTYPES));
+ outputClasses(classes);
+ }
+ }
+
+ // filepath -> class source
+ private void outputClasses(Map classes) throws ScribbleException
+ {
+ Consumer f;
+ if (this.args.containsKey(ArgFlag.API_OUTPUT))
+ {
+ String dir = this.args.get(ArgFlag.API_OUTPUT)[0];
+ f = (path) -> { ScribUtil.handleLambdaScribbleException(() ->
+ {
+ String tmp = dir + "/" + path;
+ if (this.args.containsKey(ArgFlag.VERBOSE))
+ {
+ System.out.println("\n[DEBUG] Writing to: " + tmp);
+ }
+ ScribUtil.writeToFile(tmp, classes.get(path)); return null;
+ }); };
+ }
+ else
+ {
+ f = (path) -> { System.out.println(path + ":\n" + classes.get(path)); };
+ }
+ classes.keySet().stream().forEach(f);
+ }
+
+ private static void runDot(String dot, String png) throws ScribbleException, CommandLineException
+ {
+ String tmpName = png + ".tmp";
+ File tmp = new File(tmpName);
+ if (tmp.exists())
+ {
+ throw new CommandLineException("Cannot overwrite: " + tmpName);
+ }
+ try
+ {
+ ScribUtil.writeToFile(tmpName, dot);
+ String[] res = ScribUtil.runProcess("dot", "-Tpng", "-o" + png, tmpName);
+ System.out.print(!res[1].isEmpty() ? res[1] : res[0]); // already "\n" terminated
+ }
+ finally
+ {
+ tmp.delete();
+ }
+ }
+
+ private Job newJob(MainContext mc)
+ {
+ //Job job = new Job(cjob); // Doesn't work due to (recursive) maven dependencies
+ return mc.newJob();
+ }
+
+ private MainContext newMainContext() throws ScribParserException, ScribbleException
+ {
+ //boolean jUnit = this.args.containsKey(ArgFlag.JUNIT);
+ boolean debug = this.args.containsKey(ArgFlag.VERBOSE);
+ boolean useOldWF = this.args.containsKey(ArgFlag.OLD_WF);
+ boolean noLiveness = this.args.containsKey(ArgFlag.NO_LIVENESS);
+ boolean minEfsm = this.args.containsKey(ArgFlag.LTSCONVERT_MIN);
+ boolean fair = this.args.containsKey(ArgFlag.FAIR);
+ boolean noLocalChoiceSubjectCheck = this.args.containsKey(ArgFlag.NO_LOCAL_CHOICE_SUBJECT_CHECK);
+ boolean noAcceptCorrelationCheck = this.args.containsKey(ArgFlag.NO_ACCEPT_CORRELATION_CHECK);
+ boolean noValidation = this.args.containsKey(ArgFlag.NO_VALIDATION);
+ boolean noModuleNameCheck = this.args.containsKey(ArgFlag.NO_MODULE_NAME_CHECK);
+
+ List impaths = this.args.containsKey(ArgFlag.IMPORT_PATH)
+ ? CommandLine.parseImportPaths(this.args.get(ArgFlag.IMPORT_PATH)[0])
+ : Collections.emptyList();
+ ResourceLocator locator = new DirectoryResourceLocator(impaths);
+ if (this.args.containsKey(ArgFlag.INLINE_MAIN_MOD))
+ {
+ return new MainContext(debug, locator, this.args.get(ArgFlag.INLINE_MAIN_MOD)[0], useOldWF, noLiveness, minEfsm, fair,
+ noLocalChoiceSubjectCheck, noAcceptCorrelationCheck, noValidation, noModuleNameCheck);
+ }
+ else
+ {
+ Path mainpath = CommandLine.parseMainPath(this.args.get(ArgFlag.MAIN_MOD)[0]);
+ //return new MainContext(jUnit, debug, locator, mainpath, useOldWF, noLiveness);
+ return new MainContext(debug, locator, mainpath, useOldWF, noLiveness, minEfsm, fair,
+ noLocalChoiceSubjectCheck, noAcceptCorrelationCheck, noValidation, noModuleNameCheck);
+ }
+ }
+
+ private static Path parseMainPath(String path)
+ {
+ return Paths.get(path);
+ }
+
+ private static List parseImportPaths(String paths)
+ {
+ return Arrays.stream(paths.split(File.pathSeparator)).map((s) -> Paths.get(s)).collect(Collectors.toList());
+ }
+
+ private static GProtocolName checkGlobalProtocolArg(JobContext jcontext, String simpname) throws CommandLineException
+ {
+ GProtocolName simpgpn = new GProtocolName(simpname);
+ Module main = jcontext.getMainModule();
+ if (!main.hasProtocolDecl(simpgpn))
+ {
+ throw new CommandLineException("Global protocol not found: " + simpname);
+ }
+ ProtocolDecl> pd = main.getProtocolDecl(simpgpn);
+ if (pd == null || !pd.isGlobal())
+ {
+ throw new CommandLineException("Global protocol not found: " + simpname);
+ }
+ if (pd.isAuxModifier()) // CHECKME: maybe don't check for all, e.g. -project
+ {
+ throw new CommandLineException("Invalid aux protocol specified as root: " + simpname);
+ }
+ return new GProtocolName(jcontext.main, simpgpn);
+ }
+
+ private static Role checkRoleArg(JobContext jcontext, GProtocolName fullname, String rolename) throws CommandLineException
+ {
+ ProtocolDecl> pd = jcontext.getMainModule().getProtocolDecl(fullname.getSimpleName());
+ Role role = new Role(rolename);
+ if (!pd.header.roledecls.getRoles().contains(role))
+ {
+ throw new CommandLineException("Role not declared for " + fullname + ": " + role);
+ }
+ return role;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineArgParser.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineArgParser.java
new file mode 100644
index 0000000..a584963
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineArgParser.java
@@ -0,0 +1,410 @@
+package org.scribble.cli;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.scribble.cli.CommandLine.ArgFlag;
+
+// String[] -> Map -- Map array values are the arguments associated to each CommandLine.Arg
+public class CommandLineArgParser
+{
+ // Unique flags
+ public static final String JUNIT_FLAG = "-junit"; // For internal use (JUnit test harness)
+ public static final String VERBOSE_FLAG = "-V";
+ public static final String IMPORT_PATH_FLAG = "-ip";
+ public static final String API_OUTPUT_DIR_FLAG = "-d";
+ public static final String STATECHAN_SUBTYPES_FLAG = "-subtypes";
+ public static final String OLD_WF_FLAG = "-oldwf";
+ public static final String NO_LIVENESS_FLAG = "-nolive";
+ public static final String LTSCONVERT_MIN_FLAG = "-minlts";
+ public static final String FAIR_FLAG = "-fair";
+ public static final String NO_LOCAL_CHOICE_SUBJECT_CHECK = "-nolocalchoicecheck";
+ public static final String NO_ACCEPT_CORRELATION_CHECK = "-nocorrelation";
+ public static final String DOT_FLAG = "-dot";
+ public static final String AUT_FLAG = "-aut";
+ public static final String NO_VALIDATION_FLAG = "-novalid";
+ public static final String NO_MODULE_NAME_CHECK_FLAG = "-nomodnamecheck";
+ public static final String INLINE_MAIN_MOD_FLAG = "-inline";
+ public static final String F17_FLAG = "-f17";
+
+ // Non-unique flags
+ public static final String PROJECT_FLAG = "-project";
+ public static final String EFSM_FLAG = "-fsm";
+ public static final String VALIDATION_EFSM_FLAG = "-vfsm";
+ public static final String UNFAIR_EFSM_FLAG = "-ufsm";
+ public static final String EFSM_PNG_FLAG = "-fsmpng";
+ public static final String VALIDATION_EFSM_PNG_FLAG = "-vfsmpng";
+ public static final String UNFAIR_EFSM_PNG_FLAG = "-ufsmpng";
+ public static final String SGRAPH_FLAG = "-model";
+ public static final String UNFAIR_SGRAPH_FLAG = "-umodel";
+ public static final String SGRAPH_PNG_FLAG = "-modelpng";
+ public static final String UNFAIR_SGRAPH_PNG_FLAG = "-umodelpng";
+ public static final String API_GEN_FLAG = "-api";
+ public static final String SESSION_API_GEN_FLAG = "-sessapi";
+ public static final String STATECHAN_API_GEN_FLAG = "-chanapi";
+
+ private static final Map UNIQUE_FLAGS = new HashMap<>();
+ {
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.JUNIT_FLAG, CommandLine.ArgFlag.JUNIT);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.VERBOSE_FLAG, CommandLine.ArgFlag.VERBOSE);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.IMPORT_PATH_FLAG, CommandLine.ArgFlag.IMPORT_PATH);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.API_OUTPUT_DIR_FLAG, CommandLine.ArgFlag.API_OUTPUT);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.STATECHAN_SUBTYPES_FLAG, CommandLine.ArgFlag.SCHAN_API_SUBTYPES);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.OLD_WF_FLAG, CommandLine.ArgFlag.OLD_WF);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.LTSCONVERT_MIN_FLAG, CommandLine.ArgFlag.LTSCONVERT_MIN);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.FAIR_FLAG, CommandLine.ArgFlag.FAIR);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.NO_LOCAL_CHOICE_SUBJECT_CHECK, CommandLine.ArgFlag.NO_LOCAL_CHOICE_SUBJECT_CHECK);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.NO_ACCEPT_CORRELATION_CHECK, CommandLine.ArgFlag.NO_ACCEPT_CORRELATION_CHECK);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.DOT_FLAG, CommandLine.ArgFlag.DOT);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.AUT_FLAG, CommandLine.ArgFlag.AUT);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.NO_VALIDATION_FLAG, CommandLine.ArgFlag.NO_VALIDATION);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.NO_MODULE_NAME_CHECK_FLAG, CommandLine.ArgFlag.NO_MODULE_NAME_CHECK);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.INLINE_MAIN_MOD_FLAG, CommandLine.ArgFlag.INLINE_MAIN_MOD);
+ CommandLineArgParser.UNIQUE_FLAGS.put(CommandLineArgParser.F17_FLAG, CommandLine.ArgFlag.F17);
+ }
+
+ private static final Map NON_UNIQUE_FLAGS = new HashMap<>();
+ {
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.PROJECT_FLAG, CommandLine.ArgFlag.PROJECT);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.EFSM_FLAG, CommandLine.ArgFlag.EFSM);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.VALIDATION_EFSM_FLAG, CommandLine.ArgFlag.VALIDATION_EFSM);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.UNFAIR_EFSM_FLAG, CommandLine.ArgFlag.UNFAIR_EFSM);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.EFSM_PNG_FLAG, CommandLine.ArgFlag.EFSM_PNG);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.VALIDATION_EFSM_PNG_FLAG, CommandLine.ArgFlag.VALIDATION_EFSM_PNG);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.UNFAIR_EFSM_PNG_FLAG, CommandLine.ArgFlag.UNFAIR_EFSM_PNG);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.SGRAPH_FLAG, CommandLine.ArgFlag.SGRAPH);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.UNFAIR_SGRAPH_FLAG, CommandLine.ArgFlag.UNFAIR_SGRAPH);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.SGRAPH_PNG_FLAG, CommandLine.ArgFlag.SGRAPH_PNG);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.UNFAIR_SGRAPH_PNG_FLAG, CommandLine.ArgFlag.UNFAIR_SGRAPH_PNG);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.API_GEN_FLAG, CommandLine.ArgFlag.API_GEN);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.SESSION_API_GEN_FLAG, CommandLine.ArgFlag.SESS_API_GEN);
+ CommandLineArgParser.NON_UNIQUE_FLAGS.put(CommandLineArgParser.STATECHAN_API_GEN_FLAG, CommandLine.ArgFlag.SCHAN_API_GEN);
+ }
+
+ private static final Map FLAGS = new HashMap<>();
+ {
+ CommandLineArgParser.FLAGS.putAll(CommandLineArgParser.UNIQUE_FLAGS);
+ CommandLineArgParser.FLAGS.putAll(CommandLineArgParser.NON_UNIQUE_FLAGS);
+ }
+
+ private final String[] args;
+ private final Map parsed = new HashMap<>();
+
+ public CommandLineArgParser(String[] args) throws CommandLineException
+ {
+ this.args = args;
+ parseArgs();
+ }
+
+ public Map getArgs()
+ {
+ return this.parsed;
+ }
+
+ private void parseArgs() throws CommandLineException
+ {
+ for (int i = 0; i < this.args.length; i++)
+ {
+ String arg = this.args[i];
+ if (CommandLineArgParser.FLAGS.containsKey(arg))
+ {
+ i = this.parseFlag(i);
+ }
+ else
+ {
+ if (isMainModuleParsed())
+ {
+ if (arg.startsWith("-"))
+ {
+ throw new CommandLineException("Unknown flag or bad main module arg: " + arg);
+ }
+ // May actually be the second bad argument -- we didn't validate the value of the main arg
+ throw new CommandLineException("Bad/multiple main module arg: " + arg);
+ }
+ parseMain(i);
+ }
+ }
+ }
+
+ private boolean isMainModuleParsed()
+ {
+ return this.parsed.containsKey(CommandLine.ArgFlag.MAIN_MOD) || this.parsed.containsKey(CommandLine.ArgFlag.INLINE_MAIN_MOD);
+ }
+
+ // Pre: i is the index of the current flag to parse
+ // Post: i is the index of the last argument parsed -- parseArgs does the index increment to the next current flag
+ // Currently allows repeat flag decls: next overrides previous
+ private int parseFlag(int i) throws CommandLineException
+ {
+ String flag = this.args[i];
+ switch (flag)
+ {
+ // Unique flags
+ case CommandLineArgParser.IMPORT_PATH_FLAG:
+ {
+ return parseImportPath(i);
+ }
+ case CommandLineArgParser.INLINE_MAIN_MOD_FLAG:
+ {
+ if (isMainModuleParsed())
+ {
+ throw new CommandLineException("Multiple main modules given.");
+ }
+ return parseInlineMainModule(i);
+ }
+ case CommandLineArgParser.F17_FLAG:
+ {
+ return parseF17(i);
+ }
+ case CommandLineArgParser.JUNIT_FLAG:
+ case CommandLineArgParser.VERBOSE_FLAG:
+ case CommandLineArgParser.STATECHAN_SUBTYPES_FLAG:
+ case CommandLineArgParser.OLD_WF_FLAG:
+ case CommandLineArgParser.NO_LIVENESS_FLAG:
+ case CommandLineArgParser.LTSCONVERT_MIN_FLAG:
+ case CommandLineArgParser.FAIR_FLAG:
+ case CommandLineArgParser.NO_LOCAL_CHOICE_SUBJECT_CHECK:
+ case CommandLineArgParser.NO_ACCEPT_CORRELATION_CHECK:
+ case CommandLineArgParser.NO_VALIDATION_FLAG:
+ case CommandLineArgParser.NO_MODULE_NAME_CHECK_FLAG:
+ {
+ checkAndAddNoArgUniqueFlag(flag, new String[0]);
+ return i;
+ }
+ case CommandLineArgParser.API_OUTPUT_DIR_FLAG:
+ {
+ return parseOutput(i);
+ }
+ case CommandLineArgParser.DOT_FLAG:
+ {
+ if (this.parsed.containsKey(CommandLineArgParser.UNIQUE_FLAGS.get(AUT_FLAG)))
+ {
+ throw new CommandLineException("Incompatible flags: " + DOT_FLAG + " and " + AUT_FLAG);
+ }
+ checkAndAddNoArgUniqueFlag(flag, new String[0]);
+ return i;
+ }
+ case CommandLineArgParser.AUT_FLAG:
+ {
+ if (this.parsed.containsKey(CommandLineArgParser.UNIQUE_FLAGS.get(DOT_FLAG)))
+ {
+ throw new CommandLineException("Incompatible flags: " + DOT_FLAG + " and " + AUT_FLAG);
+ }
+ checkAndAddNoArgUniqueFlag(flag, new String[0]);
+ return i;
+ }
+
+ // Non-unique flags
+ case CommandLineArgParser.PROJECT_FLAG:
+ {
+ return parseProject(i);
+ }
+ case CommandLineArgParser.EFSM_FLAG:
+ case CommandLineArgParser.VALIDATION_EFSM_FLAG:
+ case CommandLineArgParser.UNFAIR_EFSM_FLAG:
+ case CommandLineArgParser.API_GEN_FLAG:
+ case CommandLineArgParser.STATECHAN_API_GEN_FLAG:
+ {
+ return parseProtoAndRoleArgs(flag, i);
+ }
+ case CommandLineArgParser.EFSM_PNG_FLAG:
+ case CommandLineArgParser.VALIDATION_EFSM_PNG_FLAG:
+ case CommandLineArgParser.UNFAIR_EFSM_PNG_FLAG:
+ {
+ return parseProtoRoleAndFileArgs(flag, i);
+ }
+ case CommandLineArgParser.SGRAPH_FLAG:
+ case CommandLineArgParser.UNFAIR_SGRAPH_FLAG:
+ case CommandLineArgParser.SESSION_API_GEN_FLAG:
+ {
+ return parseProtoArg(flag, i);
+ }
+ case CommandLineArgParser.SGRAPH_PNG_FLAG:
+ case CommandLineArgParser.UNFAIR_SGRAPH_PNG_FLAG:
+ {
+ return parseProtoAndFileArgs(flag, i);
+ }
+
+ default:
+ {
+ throw new RuntimeException("[TODO] Unknown flag: " + flag);
+ }
+ }
+ }
+
+ private void checkAndAddNoArgUniqueFlag(String flag, String[] args) throws CommandLineException
+ {
+ ArgFlag argFlag = CommandLineArgParser.UNIQUE_FLAGS.get(flag);
+ if (this.parsed.containsKey(argFlag))
+ {
+ throw new CommandLineException("Duplicate flag: " + flag);
+ }
+ this.parsed.put(argFlag, args);
+ }
+
+ private int parseOutput(int i) throws CommandLineException
+ {
+ if ((i + 1) >= this.args.length)
+ {
+ throw new CommandLineException("Missing directory argument");
+ }
+ String dir = this.args[++i];
+ this.parsed.put(CommandLineArgParser.UNIQUE_FLAGS.get(CommandLineArgParser.API_OUTPUT_DIR_FLAG), new String[] { dir } );
+ return i;
+ }
+
+ private void parseMain(int i) throws CommandLineException
+ {
+ String main = args[i];
+ if (!CommandLineArgParser.validateModuleArg(main))
+ {
+ throw new CommandLineException("Bad module arg: " + main);
+ }
+ this.parsed.put(CommandLine.ArgFlag.MAIN_MOD, new String[] { main } );
+ }
+
+ private int parseImportPath(int i) throws CommandLineException
+ {
+ if ((i + 1) >= this.args.length)
+ {
+ throw new CommandLineException("Missing path argument");
+ }
+ String path = this.args[++i];
+ if (!validatePaths(path))
+ {
+ throw new CommandLineException("Scribble module import path '"+ path +"' is not valid\r\n");
+ }
+ //this.parsed.put(CommandLineArgParser.FLAGS.get(CommandLineArgParser.PATH_FLAG), new String[] { path });
+ checkAndAddNoArgUniqueFlag(CommandLineArgParser.IMPORT_PATH_FLAG, new String[] { path });
+ return i;
+ }
+
+ private int parseInlineMainModule(int i) throws CommandLineException
+ {
+ if ((i + 1) >= this.args.length)
+ {
+ throw new CommandLineException("Missing module definition");
+ }
+ String inline = this.args[++i];
+ checkAndAddNoArgUniqueFlag(CommandLineArgParser.INLINE_MAIN_MOD_FLAG, new String[] { inline });
+ return i;
+ }
+
+ private int parseF17(int i) throws CommandLineException
+ {
+ if ((i + 1) >= this.args.length)
+ {
+ throw new CommandLineException("Missing simple global protocol name argument");
+ }
+ String proto = this.args[++i];
+ checkAndAddNoArgUniqueFlag(CommandLineArgParser.F17_FLAG, new String[] { proto });
+ return i;
+ }
+
+ private int parseProject(int i) throws CommandLineException // Similar to parseProtoAndRoleArgs
+ {
+ if ((i + 2) >= this.args.length)
+ {
+ throw new CommandLineException("Missing protocol/role arguments");
+ }
+ String proto = this.args[++i];
+ String role = this.args[++i];
+ /*if (!validateProtocolName(proto)) // TODO
+ {
+ throw new RuntimeException("Protocol name '"+ proto +"' is not valid\r\n");
+ }*/
+ concatArgs(CommandLineArgParser.NON_UNIQUE_FLAGS.get(CommandLineArgParser.PROJECT_FLAG), proto, role);
+ return i;
+ }
+
+ private int parseProtoAndRoleArgs(String f, int i) throws CommandLineException
+ {
+ ArgFlag flag = CommandLineArgParser.NON_UNIQUE_FLAGS.get(f);
+ if ((i + 2) >= this.args.length)
+ {
+ throw new CommandLineException("Missing protocol/role arguments");
+ }
+ String proto = this.args[++i];
+ String role = this.args[++i];
+ concatArgs(flag, proto, role);
+ return i;
+ }
+
+ private int parseProtoRoleAndFileArgs(String f, int i) throws CommandLineException
+ {
+ ArgFlag flag = CommandLineArgParser.NON_UNIQUE_FLAGS.get(f);
+ if ((i + 3) >= this.args.length)
+ {
+ throw new CommandLineException("Missing protocol/role/file arguments");
+ }
+ String proto = this.args[++i];
+ String role = this.args[++i];
+ String png = this.args[++i];
+ concatArgs(flag, proto, role, png);
+ return i;
+ }
+
+ private int parseProtoArg(String f, int i) throws CommandLineException
+ {
+ ArgFlag flag = CommandLineArgParser.NON_UNIQUE_FLAGS.get(f);
+ if ((i + 1) >= this.args.length)
+ {
+ throw new CommandLineException("Missing protocol argument");
+ }
+ String proto = this.args[++i];
+ concatArgs(flag, proto);
+ return i;
+ }
+
+ private int parseProtoAndFileArgs(String f, int i) throws CommandLineException
+ {
+ ArgFlag flag = CommandLineArgParser.NON_UNIQUE_FLAGS.get(f);
+ if ((i + 2) >= this.args.length)
+ {
+ throw new CommandLineException("Missing protocol/file arguments");
+ }
+ String proto = this.args[++i];
+ String png = this.args[++i];
+ concatArgs(flag, proto, png);
+ return i;
+ }
+
+ private void concatArgs(ArgFlag flag, String... toAdd)
+ {
+ String[] args = this.parsed.get(flag);
+ if (args == null)
+ {
+ args = Arrays.copyOf(toAdd, toAdd.length);
+ }
+ else
+ {
+ String[] tmp = new String[args.length + toAdd.length];
+ System.arraycopy(args, 0, tmp, 0, args.length);
+ System.arraycopy(toAdd, 0, tmp, args.length, toAdd.length);
+ args = tmp;
+ }
+ this.parsed.put(flag, args);
+ }
+
+ // Used to guard subsequent file open attempt?
+ private static boolean validateModuleArg(String arg)
+ {
+ return arg.chars().noneMatch((i) ->
+ !Character.isLetterOrDigit(i) && i != '.' && i != File.separatorChar && i != ':' && i != '-' && i != '_'
+ && i != '/'); // Hack? (cygwin)
+ }
+
+ private static boolean validatePaths(String paths)
+ {
+ for (String path : paths.split(File.pathSeparator))
+ {
+ if (!new File(path).isDirectory())
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineException.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineException.java
new file mode 100644
index 0000000..da26038
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/cli/CommandLineException.java
@@ -0,0 +1,35 @@
+package org.scribble.cli;
+
+public class CommandLineException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+
+ public CommandLineException()
+ {
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommandLineException(String arg0)
+ {
+ super(arg0);
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommandLineException(Throwable arg0)
+ {
+ super(arg0);
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommandLineException(String arg0, Throwable arg1)
+ {
+ super(arg0, arg1);
+ // TODO Auto-generated constructor stub
+ }
+
+ public CommandLineException(String arg0, Throwable arg1, boolean arg2, boolean arg3)
+ {
+ super(arg0, arg1, arg2, arg3);
+ // TODO Auto-generated constructor stub
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/main/MainContext.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/main/MainContext.java
new file mode 100644
index 0000000..689740a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/main/java/org/scribble/main/MainContext.java
@@ -0,0 +1,153 @@
+package org.scribble.main;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.scribble.ast.ImportDecl;
+import org.scribble.ast.ImportModule;
+import org.scribble.ast.Module;
+import org.scribble.main.resource.DirectoryResourceLocator;
+import org.scribble.main.resource.InlineResource;
+import org.scribble.main.resource.Resource;
+import org.scribble.main.resource.ResourceLocator;
+import org.scribble.parser.AntlrParser;
+import org.scribble.parser.ScribModuleLoader;
+import org.scribble.parser.ScribParser;
+import org.scribble.sesstype.name.ModuleName;
+import org.scribble.util.Pair;
+import org.scribble.util.ScribParserException;
+
+
+// N.B. the same package is declared in core
+
+// Scribble tool context for main module
+// FIXME: should be in core org.scribble.main, but currently here due to Maven dependency restrictions
+// MainContext takes ResourceLocator abstractly (e.g. DirectoryResourceLocator), but because abstract Resource itself works off paths, it takes mainpath (rather than something more abstract, e.g. URI, to identify the "main" resource)
+// Resource and ResourceLocator should be made abstract from (file)paths (cf. use of toPath in ScribbleModuleLoader)
+public class MainContext
+{
+ //public final boolean jUnit;
+ public final boolean debug;
+ public final boolean useOldWF;
+ public final boolean noLiveness;
+ public final boolean minEfsm;
+ public final boolean fair;
+ public final boolean noLocalChoiceSubjectCheck;
+ public final boolean noAcceptCorrelationCheck;
+ public final boolean noValidation; // FIXME: deprecate
+ public final boolean noModuleNameCheck;
+
+ // Only "manually" used here for loading main module (which should be factored out to front end) -- otherwise, only used within loader
+ private final AntlrParser antlrParser = new AntlrParser(); // Not encapsulated inside ScribbleParser, because ScribbleParser's main function is to "parse" ANTLR CommonTrees into ModelNodes
+ private final ScribParser scribParser = new ScribParser();
+
+ private final ResourceLocator locator; // Path -> Resource
+ private final ScribModuleLoader loader; // sesstype.ModuleName -> Pair
+
+ private ModuleName main;
+
+ // ModuleName keys are full module names -- parsed are the modules read from file, distinguished from the generated projection modules
+ // Resource recorded for source path
+ private final Map> parsed = new HashMap<>();
+
+ // FIXME: make Path abstract as e.g. URI -- locator is abstract but Path is coupled to concrete DirectoryResourceLocator
+ //public MainContext(boolean jUnit, boolean debug, ResourceLocator locator, Path mainpath, boolean useOldWF, boolean noLiveness)
+ public MainContext(boolean debug, ResourceLocator locator, boolean useOldWF, boolean noLiveness, boolean minEfsm,
+ boolean fair, boolean noLocalChoiceSubjectCheck, boolean noAcceptCorrelationCheck, boolean noValidation, boolean noModuleNameCheck)
+ throws ScribParserException, ScribbleException
+ {
+ //this.jUnit = jUnit;
+ this.debug = debug;
+ this.useOldWF = useOldWF;
+ this.noLiveness = noLiveness;
+ this.minEfsm = minEfsm;
+ this.fair = fair;
+ this.noLocalChoiceSubjectCheck = noLocalChoiceSubjectCheck;
+ this.noAcceptCorrelationCheck = noAcceptCorrelationCheck;
+ this.noValidation = noValidation;
+ this.noModuleNameCheck = noModuleNameCheck;
+
+ this.locator = locator;
+ this.loader = new ScribModuleLoader(this.locator, this.antlrParser, this.scribParser);
+ }
+
+ public MainContext(boolean debug, ResourceLocator locator, String inline, boolean useOldWF, boolean noLiveness, boolean minEfsm,
+ boolean fair, boolean noLocalChoiceSubjectCheck, boolean noAcceptCorrelationCheck, boolean noValidation, boolean noModuleNameCheck)
+ throws ScribParserException, ScribbleException
+ {
+ this(debug, locator, useOldWF, noLiveness, minEfsm, fair, noLocalChoiceSubjectCheck, noAcceptCorrelationCheck, noValidation, noModuleNameCheck);
+
+ Resource res = new InlineResource(inline);
+ Module mod = (Module) this.scribParser.parse(this.antlrParser.parseAntlrTree(res));
+
+ init(res, mod);
+ }
+
+ // Load main module from file system
+ public MainContext(boolean debug, ResourceLocator locator, Path mainpath, boolean useOldWF, boolean noLiveness, boolean minEfsm,
+ boolean fair, boolean noLocalChoiceSubjectCheck, boolean noAcceptCorrelationCheck, boolean noValidation, boolean noModuleNameCheck)
+ throws ScribParserException, ScribbleException
+ {
+ this(debug, locator, useOldWF, noLiveness, minEfsm, fair, noLocalChoiceSubjectCheck, noAcceptCorrelationCheck, noValidation, noModuleNameCheck);
+
+ // FIXME: checking main module resource exists at specific location should be factored out to front-end (e.g. CommandLine) -- main module resource is specified at local front end level of abstraction, while MainContext uses abstract resource loading
+ //Pair p = this.loader.loadMainModule(mainpath);
+ Resource res = DirectoryResourceLocator.getResourceByFullPath(mainpath); // FIXME: hardcoded to DirectoryResourceLocator -- main module loading should be factored out to front end (e.g. CommandLine)
+ Module mod = (Module) this.scribParser.parse(this.antlrParser.parseAntlrTree(res)); // FIXME: rename exceptions
+ checkMainModuleName(mainpath, mod);
+
+ init(res, mod);
+ }
+
+ private void init(Resource res, Module mainmod) throws ScribParserException, ScribbleException
+ {
+ Pair p = new Pair<>(res, mainmod);
+ this.main = p.right.getFullModuleName(); // FIXME: main modname comes from the inlined moddecl -- check for issues if this clashes with an existing file system resource
+ loadAllModules(p);
+ }
+
+ private void loadAllModules(Pair module) throws ScribParserException, ScribbleException
+ {
+ this.parsed.put(module.right.getFullModuleName(), module);
+ for (ImportDecl> id : module.right.getImportDecls())
+ {
+ if (id.isImportModule())
+ {
+ ModuleName modname = ((ImportModule) id).modname.toName();
+ if (!this.parsed.containsKey(modname))
+ {
+ loadAllModules(this.loader.loadModule(modname));
+ }
+ }
+ }
+ }
+
+ public Map getParsedModules()
+ {
+ return this.parsed.entrySet().stream().collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue().right));
+ }
+
+ public Job newJob()
+ {
+ return new Job(this.debug, this.getParsedModules(), this.main, this.useOldWF, this.noLiveness, this.minEfsm, this.fair,
+ this.noLocalChoiceSubjectCheck, this.noAcceptCorrelationCheck, this.noValidation, this.noModuleNameCheck);
+ }
+
+ // Hacky? But not Scribble tool's job to check nested directory location of module fully corresponds to the fullname of module? Cf. Java classes
+ private void checkMainModuleName(Path mainpath, Module main) throws ScribbleException
+ {
+ String path = mainpath.toString(); // FIXME: hack
+ // FileSystems.getDefault().getSeparator() ?
+ String tmp = path.substring((path.lastIndexOf(File.separator) == -1) ? 0 : path.lastIndexOf(File.separator) + 1, path.lastIndexOf('.'));
+ if (!this.noValidation && !this.noModuleNameCheck) // For webapp convenience -- relies on ModuleContext building using the declared module name, not file name
+ {
+ if (!tmp.equals(main.getFullModuleName().getSimpleName().toString())) // ModuleName.toString hack?
+ {
+ throw new ScribbleException(main.moddecl.name.getSource(), "Simple module name at path " + path + " mismatch: " + main.getFullModuleName());
+ }
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/AllTest.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/AllTest.java
new file mode 100644
index 0000000..4c1514e
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/AllTest.java
@@ -0,0 +1,87 @@
+package scribtest;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+import org.scribble.cli.CommandLine;
+import org.scribble.cli.CommandLineArgParser;
+import org.scribble.cli.CommandLineException;
+import org.scribble.main.ScribbleException;
+import org.scribble.util.ScribParserException;
+
+/**
+ * Runs all tests under good and bad root directories in Scribble.
+ */
+//@RunWith(value = Parameterized.class)
+@RunWith(Parameterized.class)
+public class AllTest
+{
+ protected static final boolean GOOD_TEST = false;
+ protected static final boolean BAD_TEST = !GOOD_TEST;
+
+ protected final String example;
+ protected final boolean isBadTest;
+
+ // relative to cli/src/test/resources (or target/test-classes/)
+ protected static final String ALL_ROOT = ".";
+ protected static final String GOOD_ROOT = "good";
+ protected static final String BAD_ROOT = "bad";
+
+ public AllTest(String example, boolean isBadTest)
+ {
+ this.example = example;
+ this.isBadTest = isBadTest;
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection data()
+ {
+ // Test all tests under good and bad root directories
+ String dir_good = ClassLoader.getSystemResource(GoodTest.GOOD_ROOT).getFile();
+ String dir_bad = ClassLoader.getSystemResource(BadTest.BAD_ROOT).getFile();
+ List result = new LinkedList<>();
+ result.addAll(Harness.makeTests(GOOD_TEST, dir_good));
+ result.addAll(Harness.makeTests(BAD_TEST, dir_bad));
+ return result;
+ }
+
+ @Test
+ public void tests() throws IOException, InterruptedException, ExecutionException
+ {
+ try
+ {
+ // TODO: For now just locate classpath for resources -- later maybe directly execute job
+ /*URL url = ClassLoader.getSystemResource(AllTest.GOOD_ROOT); // Assume good/bad have same parent
+ String dir = url.getFile().substring(0, url.getFile().length() - ("/" + AllTest.GOOD_ROOT).length());*/
+ String dir = ClassLoader.getSystemResource(AllTest.ALL_ROOT).getFile();
+
+ if (File.separator.equals("\\")) // HACK: Windows
+ {
+ dir = dir.substring(1).replace("/", "\\");
+ }
+
+ // FIXME: read runtime arguments from a config file, e.g. -oldwf, -fair, etc
+ // Also need a way to specify expected tool output (e.g. projections/EFSMs for good, errors for bad)
+ new CommandLine(this.example, CommandLineArgParser.JUNIT_FLAG, CommandLineArgParser.IMPORT_PATH_FLAG, dir).run();
+ // Added JUNIT flag -- but for some reason only bad DoArgList01.scr was breaking without it...
+ Assert.assertFalse("Expecting exception", this.isBadTest);
+ }
+ catch (ScribbleException e)
+ {
+ Assert.assertTrue("Unexpected exception '" + e.getMessage() + "'", this.isBadTest);
+ }
+ catch (ScribParserException | CommandLineException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/BadTest.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/BadTest.java
new file mode 100644
index 0000000..c7370c9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/BadTest.java
@@ -0,0 +1,35 @@
+package scribtest;
+
+import java.util.Collection;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// For running all tests under the specified directory as bad tests
+// Needs -Dtest.dir=[test root dir] system property -- Eclipse VM arg: -Dtest.dir=${selected_resource_loc}
+//@RunWith(value = Parameterized.class)
+@RunWith(Parameterized.class)
+public class BadTest extends AllTest
+{
+ public BadTest(String example, boolean isBadTest)
+ {
+ super(example, isBadTest);
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection data()
+ {
+ return Harness.checkTestDirProperty(AllTest.BAD_TEST, BAD_ROOT);
+ /*String dir_good = ClassLoader.getSystemResource(GoodTest.GOOD_ROOT).getFile();
+ String dir_bad = ClassLoader.getSystemResource(BadTest.BAD_ROOT).getFile();
+ List result = new LinkedList<>();
+ //result.add(new Object[] { "C:\\cygwin\\home\\Raymond\\code\\scribble\\github-rhu1\\scribble-java\\modules\\cli\\target\\test-classes\\good\\efsm\\grecursion\\Test03.scr", false });
+ result.add(new Object[] { "C:\\cygwin\\home\\Raymond\\code\\scribble\\github-rhu1\\scribble-java\\modules\\cli\\target\\test-classes\\bad\\efsm\\grecursion\\unfair\\Test01.scr", true });
+ result.add(new Object[] { "C:\\cygwin\\home\\Raymond\\code\\scribble\\github-rhu1\\scribble-java\\modules\\cli\\target\\test-classes\\bad\\efsm\\grecursion\\unfair\\Test06.scr", true });
+ //result.addAll(Harness.makeTests(GOOD_TEST, dir_good));
+ //result.addAll(Harness.makeTests(BAD_TEST, dir_bad));
+ return result;
+ //*/
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/ExecUtil.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/ExecUtil.java
new file mode 100644
index 0000000..b06cc80
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/ExecUtil.java
@@ -0,0 +1,161 @@
+package scribtest;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.apache.commons.io.IOUtils;
+
+
+public class ExecUtil {
+ /**
+ * The summary of a process.
+ * @author Tiago Cogumbreiro (cogumbreiro@users.sf.net)
+ *
+ */
+ public static class ProcessSummary {
+ /**
+ * The standard output.
+ */
+ public final String stdout;
+ /**
+ * The standard error output.
+ */
+ public final String stderr;
+ /**
+ * The exit value of the process.
+ */
+ public final int exitValue;
+ public ProcessSummary(String stdout, String stderr, int exitValue) {
+ this.stdout = stdout;
+ this.stderr = stderr;
+ this.exitValue = exitValue;
+ }
+ }
+ public static String joinPath(String ...parts) {
+ return join(File.separator, parts);
+ }
+ public static String joinPath(Iterable parts) {
+ return join(File.separator, parts);
+ }
+ public static String join(String separator, String ...parts) {
+ int count = 0;
+ StringBuilder builder = new StringBuilder();
+ for (String part : parts) {
+ if (count > 0) {
+ builder.append(separator);
+ }
+ builder.append(part);
+ count++;
+ }
+ return builder.toString();
+ }
+ public static String join(String separator, Iterable parts) {
+ int count = 0;
+ StringBuilder builder = new StringBuilder();
+ for (String part : parts) {
+ if (count > 0) {
+ builder.append(separator);
+ }
+ builder.append(part);
+ count++;
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Runs a process, up to a certain timeout,
+ *
+ * @throws IOException
+ * @throws TimeoutException
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ public static ProcessSummary execUntil(long timeout, String... command)
+ throws IOException, InterruptedException, ExecutionException {
+ return execUntil(new TreeMap(), timeout, command);
+ }
+
+ /**
+ * Runs a process, up to a certain timeout,
+ *
+ * @throws IOException
+ * @throws TimeoutException
+ * @throws ExecutionException
+ * @throws InterruptedException
+ */
+ public static ProcessSummary execUntil(Map env, long timeout, String... command)
+ throws IOException, InterruptedException, ExecutionException {
+ ProcessBuilder builder = new ProcessBuilder(command);
+ builder.environment().putAll(env);
+ Process process = builder.start();
+ ExecutorService executorService = Executors.newFixedThreadPool(3);
+ CyclicBarrier onStart = new CyclicBarrier(3);
+ Future stderr = executorService.submit(toString(process.getErrorStream(),
+ onStart));
+ Future stdout = executorService.submit(toString(process.getInputStream(),
+ onStart));
+ Future waitFor = executorService.submit(waitFor(process,
+ onStart));
+ ProcessSummary result = null;
+ try {
+ waitFor.get(timeout, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ // timeouts are ok
+ } finally {
+ process.destroy();
+ waitFor.get();
+ result = new ProcessSummary(stdout.get(), stderr.get(), process.exitValue());
+ executorService.shutdown();
+ }
+ return result;
+ }
+
+ /**
+ * Waits for a process to terminate.
+ *
+ * @param process The process it should wait for.
+ * @param onStart Notifies others upon starting.
+ * @return The return code.
+ */
+ public static Callable waitFor(final Process process,
+ final CyclicBarrier onStart) {
+ return new Callable() {
+ public Integer call() throws InterruptedException, BrokenBarrierException {
+ onStart.await();
+ return process.waitFor();
+ }
+ };
+ }
+
+ /**
+ * Converts an input stream to a string as much as possible. It stops
+ * at IOExceptions, but returns what it read thus far.
+ * @param in The input stream.
+ * @param onStart Notify others that it started.
+ */
+ private static Callable toString(final InputStream input, final CyclicBarrier onStart) {
+ return new Callable() {
+ public String call() throws InterruptedException, BrokenBarrierException, IOException {
+ onStart.await();
+ try {
+ return IOUtils.toString(input);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+ };
+ }
+
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/GoodTest.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/GoodTest.java
new file mode 100644
index 0000000..eca4fb1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/GoodTest.java
@@ -0,0 +1,26 @@
+package scribtest;
+
+import java.util.Collection;
+
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+// For running all tests under the specified directory as good tests
+// Needs -Dtest.dir=[test root dir] system property -- Eclipse VM arg: -Dtest.dir=${selected_resource_loc}
+@RunWith(value = Parameterized.class)
+public class GoodTest extends AllTest
+{
+ public GoodTest(String example, boolean isBadTest)
+ {
+ super(example, isBadTest);
+ }
+
+ @Parameters(name = "{0}")
+ public static Collection data()
+ {
+ //String dir = ClassLoader.getSystemResource(AllTest.GOOD_ROOT).getFile(); // No: checkTestDirProperty does this
+ String dir = AllTest.GOOD_ROOT;
+ return Harness.checkTestDirProperty(AllTest.GOOD_TEST, dir);
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/Harness.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/Harness.java
new file mode 100644
index 0000000..19ccb0f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/Harness.java
@@ -0,0 +1,124 @@
+package scribtest;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+public class Harness
+{
+ //private static final SuffixFileFilter SCRIBBLE_EXT = new SuffixFileFilter(".scr");
+ private static final String SCRIBBLE_EXT = ".scr";
+
+ protected static final String DIR_IGNORE_FILE = "IGNORE";
+ protected static final String TEST_DIR_FLAG = "test.dir";
+
+ /*/**
+ * Returns the java builder with the default settings to use JArmus.
+ *
+ * @param className
+ * @return
+ * /
+ // Currently unused
+ @Deprecated
+ public static JavaProcessBuilder java(String className)
+ {
+ JavaProcessBuilder java = new JavaProcessBuilder(className);
+ // java.appendClasspath("lib/antlr-3.5.2-complete.jar");
+ // java.appendClasspath("bin/");
+ java.appendClasspath("target/classes/");
+ java.appendClasspath("c:/Users/Raymond/.m2/repository/org/antlr/antlr-runtime/3.2/antlr-runtime-3.2.jar/");
+ java.appendClasspath("../core/target/classes/");
+ java.appendClasspath("../parser/target/classes/");
+ /*java.appendClasspath("../validation/target/classes/");
+ java.appendClasspath("../projection/target/classes/");
+ java.appendClasspath("../trace/target/classes/");*/
+ /*java.appendClasspath("C:/Users/Raymond/.m2/repository/org/codehaus/jackson/jackson-mapper-asl/1.9.9/jackson-mapper-asl-1.9.9.jar/");
+ java.appendClasspath("C:/Users/Raymond/.m2/repository/org/codehaus/jackson/jackson-core-asl/1.9.9/jackson-core-asl-1.9.9.jar/");* /
+ // java.appendClasspath("org.scribble2.cli.CommandLine -path modules/validation/src/test/scrib/src modules/validation/src/test/scrib/src/Test.scr/");
+ return java;
+ }
+ //*/
+
+ /**
+ * Returns all Scribble files in a given directory.
+ *
+ * @param dir
+ * @return
+ */
+ private static Collection getScribbleFilePaths(String dir)
+ {
+ /* // Broken: somehow interfering with JUnit results (e.g., bad.efsm.grecursion.unfair.Test01)
+ NameFileFilter ignore = new NameFileFilter("IGNORE");
+ TreeSet result = new TreeSet<>();
+ FileUtils.listFiles(new File(directory), SCRIBBLE_EXT, new IgnoredDirectoryWhenFilter(ignore)).stream().forEach((file) -> result.add(file.getPath()));
+ */
+ /*return getAllScrFilesUnderDirectory(dir).stream() // Debugging AllTest/BadTest bad.efsm.grecursion.unfair.Test01; problem
+ .map((f) -> f.getAbsolutePath().toString())
+ .collect(Collectors.toList());*/
+ List res = new LinkedList<>();
+ for (File f : getAllScrFilesUnderDirectory(dir))
+ {
+ res.add(f.getAbsolutePath().toString());
+ }
+ return res;
+ }
+
+ private static List getAllScrFilesUnderDirectory(String path)
+ {
+ List res = new LinkedList<>();
+ for (File f : new File(path).listFiles())
+ {
+ if (f.isFile())
+ {
+ if (f.getName().equals(Harness.DIR_IGNORE_FILE))
+ {
+ return Collections.emptyList();
+ }
+ else if (f.getName().endsWith(Harness.SCRIBBLE_EXT))
+ {
+ res.add(f);
+ }
+ }
+ else if (f.isDirectory())
+ {
+ res.addAll(getAllScrFilesUnderDirectory(f.getAbsolutePath()));
+ }
+ }
+ return res;
+ }
+
+ public static Collection findTests(String path)
+ {
+ return getScribbleFilePaths(path);
+ //return Collections.emptyList();
+ }
+
+ // dir should be full path
+ public static Collection makeTests(boolean isBadTest, String dir)
+ {
+ /*return findTests(dir).stream() // Debugging AllTest/BadTest bad.efsm.grecursion.unfair.Test01; problem
+ .map((e) -> new Object[] { e, isBadTest })
+ .collect(Collectors.toList());*/
+ List res = new LinkedList<>();
+ for (String t : findTests(dir))
+ {
+ res.add(new Object[] { t, isBadTest });
+ }
+ return res;
+ }
+
+ // root is relative path from cli/src/test/resources
+ // This is for "Good/BarTest (test.dir)", i.e., running single test from inside Eclipse
+ // root is used as a default if the test.dir property is not present
+ public static Collection checkTestDirProperty(boolean isBadTest, String root)
+ {
+ String dir = System.getProperty(Harness.TEST_DIR_FLAG);
+ if (dir == null)
+ {
+ dir = ClassLoader.getSystemResource(root).getFile();
+ }
+ return makeTests(isBadTest, dir);
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/IgnoredDirectoryWhenFilter.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/IgnoredDirectoryWhenFilter.java
new file mode 100644
index 0000000..5d54b11
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/IgnoredDirectoryWhenFilter.java
@@ -0,0 +1,22 @@
+package scribtest;
+
+import java.io.File;
+import java.io.FileFilter;
+
+import org.apache.commons.io.filefilter.AbstractFileFilter;
+
+public class IgnoredDirectoryWhenFilter extends AbstractFileFilter {
+ private final FileFilter filter;
+
+ public IgnoredDirectoryWhenFilter(FileFilter filter) {
+ this.filter = filter;
+ }
+
+ @Override
+ public boolean accept(File file) {
+ if (!file.isDirectory()) {
+ return false;
+ }
+ return file.listFiles(filter).length == 0;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/JavaProcessBuilder.java b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/JavaProcessBuilder.java
new file mode 100644
index 0000000..0b79e00
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/java/scribtest/JavaProcessBuilder.java
@@ -0,0 +1,121 @@
+package scribtest;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Map.Entry;
+
+@Deprecated
+public class JavaProcessBuilder {
+ private String jvm;
+ private Collection classpath = new ArrayList<>();
+ private String javaAgent;
+ private String javaAgentArgs;
+ private String program;
+ private Collection programArgs = new ArrayList<>();
+ private Map systemProps = new HashMap<>();
+ public JavaProcessBuilder(String jvm, String program) {
+ this.jvm = jvm;
+ this.program = program;
+ }
+ public JavaProcessBuilder(String program) {
+ this("java", program);
+ }
+ public String getJvm() {
+ return jvm;
+ }
+ public void setJvm(String jvm) {
+ this.jvm = jvm;
+ }
+ public void setProgram(String program) {
+ this.program = program;
+ }
+ public String getProgram() {
+ return program;
+ }
+ public void appendClasspath(String param) {
+ classpath.add(param);
+ }
+ public void appendProgramArgument(String arg) {
+ programArgs.add(arg);
+ }
+ public void setJavaAgent(String param) {
+ this.javaAgent = param;
+ }
+ public String getJavaAgent() {
+ return javaAgent;
+ }
+ public void setJavaAgentArgs(String javaAgentArgs) {
+ this.javaAgentArgs = javaAgentArgs;
+ }
+ public String getJavaAgentArgs() {
+ return javaAgentArgs;
+ }
+
+ public void setSystemProperty(String key) {
+ setSystemProperty(key, null);
+ }
+
+ public void setSystemProperty(String key, String value) {
+ systemProps.put(key, value);
+ }
+
+ public void unsetSystemProperty(String key) {
+ systemProps.remove(key);
+ }
+
+ private String buildClasspath() {
+ return ExecUtil.join(File.pathSeparator, classpath);
+ }
+
+ private String buildJavaAgent() {
+ StringBuilder builder = new StringBuilder("-javaagent:");
+ builder.append(javaAgent);
+ if (javaAgentArgs != null) {
+ builder.append('=');
+ builder.append(javaAgentArgs);
+ }
+ return builder.toString();
+ }
+
+ public String[] build() {
+ Collection result = new ArrayList<>();
+ result.add(jvm);
+ if (classpath.size() > 0) {
+ result.add("-cp");
+ result.add(buildClasspath());
+ }
+ if (javaAgent != null) {
+ result.add(buildJavaAgent());
+ }
+ buildSystemProperties(result);
+ result.add(program);
+ result.addAll(programArgs);
+ return result.toArray(new String[0]);
+ }
+ private void buildSystemProperties(Collection result) {
+ for (Entry entry : systemProps.entrySet()) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("-D");
+ builder.append(entry.getKey());
+ if (entry.getValue() != null) {
+ builder.append("=");
+ builder.append(entry.getValue());
+ }
+ result.add(builder.toString());
+ }
+ }
+ public void setSystemProperties(Properties props) {
+ systemProps.clear();
+ for (String key : props.stringPropertyNames()) {
+ systemProps.put(key, props.getProperty(key));
+ }
+ }
+ @Override
+ public String toString() {
+ return ExecUtil.join(" ", build());
+ }
+}
\ No newline at end of file
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test01.scr
new file mode 100644
index 0000000..69428ba
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test01.scr
@@ -0,0 +1,38 @@
+module bad.efsm.grecursion.unfair.Test01;
+
+
+// OK if -fair
+// Also tests GraphBuilderUtil addContinueEdge (addEdge vs. addEdgeAux) AllTest/BadTest problem
+global protocol Proto1(role A, role B, role C)
+{
+ rec Y
+ {
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 2() from A to C;
+ continue Y;
+ }
+ or
+ {
+ 3() from A to B;
+ 3() from A to C;
+ }
+ }
+ }
+ or
+ {
+ 4() from A to B;
+ continue Y;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test06.scr
new file mode 100644
index 0000000..bcd9a23
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test06.scr
@@ -0,0 +1,28 @@
+module bad.efsm.grecursion.unfair.Test06;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A // OK if -fair
+ {
+ 1() from A to B;
+ do Proto1Aux1(A, B, C);
+ }
+ or
+ {
+ do Proto1Aux2(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux1(role A, role B, role C)
+{
+ do Proto1(A, B, C);
+}
+
+aux global protocol Proto1Aux2(role A, role B, role C)
+{
+ 2() from A to B;
+ 3() from B to C;
+ do Proto1(A, B, C);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11b.scr
new file mode 100644
index 0000000..f5ee7a5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11b.scr
@@ -0,0 +1,27 @@
+module bad.efsm.grecursion.unfair.Test11b;
+
+
+// OK if -fair
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to A;
+ //4() from B to C; // Testing EFSM building
+ continue X;
+ }
+ or
+ {
+ 3() from B to A;
+ 4() from B to C;
+ continue X;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11c.scr
new file mode 100644
index 0000000..77b7317
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/efsm/grecursion/unfair/Test11c.scr
@@ -0,0 +1,24 @@
+module bad.efsm.grecursion.unfair.Test11c;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to A;
+ }
+ or
+ {
+ 3() from B to A;
+ 4() from B to C;
+ }
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test01.scr
new file mode 100644
index 0000000..533c9b6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test01.scr
@@ -0,0 +1,25 @@
+module bad.liveness.messagelive.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B; // Bad: testing deadlock detection (input blocked -> terminated)
+ 2() from A to B; // Message liveness -- also counterpart wait-for
+ 2() from B to C;
+ }
+ or
+ {
+ 1() from A to B;
+ rec X
+ {
+ // NB: not a role progress problem: A is already done by here
+ 4() from B to C;
+ 5() from C to B;
+ continue X;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test02.scr
new file mode 100644
index 0000000..575283d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test02.scr
@@ -0,0 +1,20 @@
+module bad.liveness.messagelive.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 1() from A to B;
+ choice at B
+ {
+ rec X
+ {
+ 2() from B to C;
+ continue X;
+ }
+ }
+ or
+ {
+ 3() from B to C;
+ }
+ 4() from A to C;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test03a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test03a.scr
new file mode 100644
index 0000000..81381a0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/Test03a.scr
@@ -0,0 +1,22 @@
+module bad.liveness.messagelive.Test03a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ 2() from C to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/unfair/Test03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/unfair/Test03b.scr
new file mode 100644
index 0000000..3632f54
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/messagelive/unfair/Test03b.scr
@@ -0,0 +1,27 @@
+module bad.liveness.messagelive.unfair.Test03b;
+
+
+// Cf. bad.liveness.messagelive.Test03a
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ () from A to B;
+ }
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ 2() from C to B; // OK if -fair
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test01.scr
new file mode 100644
index 0000000..c7ae972
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test01.scr
@@ -0,0 +1,21 @@
+module bad.liveness.roleprog.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ rec X // Fairness irrelevant
+ {
+ 1() from A to B;
+ //1() from A to C;
+ continue X;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from A to C; // "role liveness"
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test02.scr
new file mode 100644
index 0000000..9b1b63e
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test02.scr
@@ -0,0 +1,20 @@
+module bad.liveness.roleprog.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 1() from A to B;
+ choice at B
+ {
+ rec X
+ {
+ 2() from B to C;
+ continue X;
+ }
+ }
+ or
+ {
+ 3() from B to C;
+ }
+ 4() from C to A;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03a.scr
new file mode 100644
index 0000000..7d25a75
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03a.scr
@@ -0,0 +1,22 @@
+module bad.liveness.roleprog.Test03a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C; // Role progress violation for C
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03b.scr
new file mode 100644
index 0000000..80505f4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/Test03b.scr
@@ -0,0 +1,26 @@
+module bad.liveness.roleprog.Test03b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ () from A to B; // Also wait-for error at C -- tests projected choice case pruning (for C)
+ }
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C; // Role progress violation for C
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test01.scr
new file mode 100644
index 0000000..511a7df
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test01.scr
@@ -0,0 +1,22 @@
+module bad.liveness.roleprog.unfair.Test01;
+
+
+// OK if -fair
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to C;
+ }
+ or
+ {
+ 2() from A to B;
+ //4() from B to C;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test02.scr
new file mode 100644
index 0000000..643ba5c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test02.scr
@@ -0,0 +1,24 @@
+module bad.liveness.roleprog.unfair.Test02;
+
+
+// OK if -fair
+// Cf. good.wfchoice.gchoice.Choice11;
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from A to C; // bad liveness
+ choice at A
+ {
+ 1() from A to C;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from A to C;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03a.scr
new file mode 100644
index 0000000..d76b303
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03a.scr
@@ -0,0 +1,23 @@
+module bad.liveness.roleprog.unfair.Test03a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ //continue X;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03b.scr
new file mode 100644
index 0000000..3587efe
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test03b.scr
@@ -0,0 +1,23 @@
+module bad.liveness.roleprog.unfair.Test03b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ //continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ //continue X;
+ }
+ continue X;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04a.scr
new file mode 100644
index 0000000..786f171
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04a.scr
@@ -0,0 +1,38 @@
+module bad.liveness.roleprog.unfair.Test04a;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ // OK if -fair
+ choice at A
+ {
+ 1() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to C;
+ choice at C
+ {
+ 2() from C to A;
+ continue X;
+ }
+ or
+ {
+ 3() from C to A;
+ 3() from C to D;
+ continue X;
+ }
+ }
+ }
+ }
+ or
+ {
+ 0() from A to B;
+ 0() from B to C;
+ 0() from C to D;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04b.scr
new file mode 100644
index 0000000..972a825
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test04b.scr
@@ -0,0 +1,37 @@
+module bad.liveness.roleprog.unfair.Test04b;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ // OK if -fair
+ choice at A
+ {
+ 1() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to C;
+ choice at C
+ {
+ 2() from C to A;
+ }
+ or
+ {
+ 3() from C to A;
+ 3() from C to D;
+ }
+ continue X;
+ }
+ }
+ }
+ or
+ {
+ 0() from A to B;
+ 0() from B to C;
+ 0() from C to D;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test05.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test05.scr
new file mode 100644
index 0000000..85271df
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test05.scr
@@ -0,0 +1,26 @@
+module bad.liveness.roleprog.unfair.Test05;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ choice at A
+ {
+ 1() from A to B;
+ rec X
+ {
+ 2() from B to C;
+ choice at C
+ {
+ 2() from C to A;
+ }
+ or
+ {
+ () from C to A;
+ () from C to D;
+ }
+ continue X; // OK if -fair
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test06.scr
new file mode 100644
index 0000000..d40b82d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/liveness/roleprog/unfair/Test06.scr
@@ -0,0 +1,29 @@
+module bad.liveness.roleprog.unfair.Test06;
+
+
+// Duplicate of bad.efsm.unfair.Test06
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A // OK if -fair
+ {
+ 1() from A to B;
+ do Proto1Aux1(A, B, C);
+ }
+ or
+ {
+ do Proto1Aux2(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux1(role A, role B, role C)
+{
+ do Proto1(A, B, C);
+}
+
+aux global protocol Proto1Aux2(role A, role B, role C)
+{
+ 2() from A to B;
+ 3() from B to C;
+ do Proto1(A, B, C);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gchoice/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gchoice/Test01.scr
new file mode 100644
index 0000000..fb9ff22
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gchoice/Test01.scr
@@ -0,0 +1,21 @@
+module bad.reach.globals.gchoice.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to A;
+ continue X;
+ }
+ or
+ {
+ // Bad: (empty) case pruned, so reachability fails (no "optional")
+ }
+ }
+ 2() from A to B;
+ 3() from B to C;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01a.scr
new file mode 100644
index 0000000..0bfd3e9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01a.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test01a;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1(A, B);
+ 3() from A to B;
+}
+
+aux global protocol Proto2(role C, role D)
+{
+ 2() from C to D;
+ do Proto2(C, D);
+ //2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01b.scr
new file mode 100644
index 0000000..81b31a8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test01b.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test01b;
+
+
+aux global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1(A, B);
+ 3() from A to B;
+}
+
+global protocol Proto2(role C, role D)
+{
+ 2() from C to D;
+ do Proto2(C, D);
+ //2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02a.scr
new file mode 100644
index 0000000..0bc0a6f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02a.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test02a;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1(A, B);
+ //3() from A to B;
+}
+
+aux global protocol Proto2(role C, role D)
+{
+ 2() from C to D;
+ do Proto2(C, D);
+ 2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02b.scr
new file mode 100644
index 0000000..37a5691
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test02b.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test02b;
+
+
+aux global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1(A, B);
+ //3() from A to B;
+}
+
+global protocol Proto2(role C, role D)
+{
+ 2() from C to D;
+ do Proto2(C, D);
+ 2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test03.scr
new file mode 100644
index 0000000..938e929
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test03.scr
@@ -0,0 +1,13 @@
+module bad.reach.globals.gdo.Test03;
+
+
+import bad.reach.globals.gdo.pack1.Test03b;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do bad.reach.globals.gdo.pack1.Test03b.Bar(A, B);
+ 2() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test04.scr
new file mode 100644
index 0000000..82926a1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test04.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test04;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ do Foo(A, B); // Unguarded gets unfolded..
+ 2() from A to B; // ..becomes unreachable after continue once unfolded
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05a.scr
new file mode 100644
index 0000000..94889b9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05a.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test05a;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B);
+ 3() from A to B;
+ } or {
+ 2() from A to B;
+ 3() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05b.scr
new file mode 100644
index 0000000..5cff175
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test05b.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.gdo.Test05b;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B);
+ } or {
+ 2() from A to B;
+ 3() from A to B;
+ }
+ 3() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06a.scr
new file mode 100644
index 0000000..8b95fb7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06a.scr
@@ -0,0 +1,23 @@
+module bad.reach.globals.gdo.Test06a;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B);
+ do Bar(A, B);
+ }
+ or
+ {
+ do Bar(A, B);
+ }
+}
+
+global protocol Bar(role C, role D)
+{
+ 5() from C to D;
+ do Bar(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06b.scr
new file mode 100644
index 0000000..30375c4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06b.scr
@@ -0,0 +1,23 @@
+module bad.reach.globals.gdo.Test06b;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Bar(A, B);
+ do Foo(A, B);
+ }
+ or
+ {
+ do Bar(A, B);
+ }
+}
+
+global protocol Bar(role C, role D)
+{
+ 5() from C to D;
+ do Bar(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06c.scr
new file mode 100644
index 0000000..e65290b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06c.scr
@@ -0,0 +1,23 @@
+module bad.reach.globals.gdo.Test06c;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B);
+ do Bar(A, B);
+ }
+ or
+ {
+ do Bar(A, B);
+ }
+}
+
+global protocol Bar(role C, role D)
+{
+ 5() from C to D;
+ do Bar(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06d.scr
new file mode 100644
index 0000000..370ae7f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06d.scr
@@ -0,0 +1,23 @@
+module bad.reach.globals.gdo.Test06d;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B);
+ }
+ or
+ {
+ do Bar(A, B);
+ do Foo(A, B);
+ }
+}
+
+global protocol Bar(role C, role D)
+{
+ 5() from C to D;
+ do Bar(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06e.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06e.scr
new file mode 100644
index 0000000..ba36b78
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/Test06e.scr
@@ -0,0 +1,23 @@
+module bad.reach.globals.gdo.Test06e;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ do Foo(A, B); // No continuation after this choice permitted, will become non tail recursive once inlined
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ 3() from A to B;
+}
+
+global protocol Bar(role C, role D)
+{
+ 5() from C to D;
+ do Bar(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/pack1/IGNORE b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/pack1/IGNORE
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/pack1/Test03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/pack1/Test03b.scr
new file mode 100644
index 0000000..9cb890b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/gdo/pack1/Test03b.scr
@@ -0,0 +1,12 @@
+module bad.reach.globals.gdo.pack1.Test03b;
+
+
+import bad.reach.globals.gdo.Test03;
+
+
+global protocol Bar(role C, role D)
+{
+ 3() from C to D;
+ do bad.reach.globals.gdo.Test03.Foo(C, D);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01b.scr
new file mode 100644
index 0000000..7a36ff8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01b.scr
@@ -0,0 +1,16 @@
+module bad.reach.globals.grecursion.Recursion01b;
+
+
+global protocol Recursion1b(role A, role B)
+{
+ //rec A { } // Currently OK (role/recvar name clash). Disallow?
+
+ rec X
+ {
+ l1() from A to B;
+ continue X;
+ //continue Y; // Uncomment bad, even if above line is commented
+ l2() from A to B; // Uncomment bad
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01c.scr
new file mode 100644
index 0000000..385a551
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion01c.scr
@@ -0,0 +1,20 @@
+module bad.reach.globals.grecursion.Recursion01c;
+
+
+global protocol Recursion1c(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ continue X;
+ l2() from A to B;
+ }
+ or
+ {
+ l3() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03a.scr
new file mode 100644
index 0000000..d03aa5c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03a.scr
@@ -0,0 +1,19 @@
+module bad.reach.globals.grecursion.Recursion03a;
+
+
+global protocol Recursion3a(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ rec Y
+ {
+ l2() from B to A;
+ continue Y;
+ //continue X; // Uncomment OK if above line is commented
+ }
+ l3() from B to A; // Uncommemt bad
+ }
+ //l4() from B to A; // Uncomment bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03b.scr
new file mode 100644
index 0000000..fe80c05
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion03b.scr
@@ -0,0 +1,19 @@
+module bad.reach.globals.grecursion.Recursion03b;
+
+
+global protocol Recursion3b(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ rec Y
+ {
+ l2() from B to A;
+ continue Y;
+ //continue X; // Uncomment OK if above line is commented
+ }
+ //l3() from B to A; // Uncommemt bad
+ }
+ l4() from B to A; // Uncomment bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion04b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion04b.scr
new file mode 100644
index 0000000..98a856f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion04b.scr
@@ -0,0 +1,18 @@
+module bad.reach.globals.grecursion.Recursion04b;
+
+
+global protocol Recursion4b(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ rec Y
+ //rec X // Uncomment bad, even if above line is commented
+ {
+ l2() from B to A;
+ continue X;
+ continue Y; // Uncomment OK if the above line is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion05a.scr
new file mode 100644
index 0000000..e5b76dd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion05a.scr
@@ -0,0 +1,18 @@
+module bad.reach.globals.grecursion.Recursion05a;
+
+
+global protocol Recursion5a(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ continue X; // Uncomment makes the below rec bad
+ //continue Y; // Uncomment bad
+ }
+ rec Y
+ {
+ l2() from B to A;
+ continue Y; // A bad "continue X" here tested in Recursion11
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06a.scr
new file mode 100644
index 0000000..dfd631d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06a.scr
@@ -0,0 +1,22 @@
+module bad.reach.globals.grecursion.Recursion06a;
+
+
+global protocol Recursion6a(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ continue X;
+ l1() from A to B; // Uncomment bad
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ //l3() from A to B; // Uncomment bad. "General recursion" issue ("unfold stacking"). Fix is to move inside the second choice block
+ }
+ l4() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06b.scr
new file mode 100644
index 0000000..ee5e845
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion06b.scr
@@ -0,0 +1,22 @@
+module bad.reach.globals.grecursion.Recursion06b;
+
+
+global protocol Recursion6(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ continue X;
+ //l1() from A to B; // Uncomment bad
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ l3() from A to B; // Uncomment bad. "General recursion" issue ("unfold stacking"). Fix is to move inside the second choice block
+ }
+ l4() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07a.scr
new file mode 100644
index 0000000..525c561
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07a.scr
@@ -0,0 +1,21 @@
+module bad.reach.globals.grecursion.Recursion07a;
+
+
+global protocol Recursion7a(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ continue X;
+ l3() from A to B; // Uncomment bad
+ }
+ //l3() from A to B; // Uncomment bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07b.scr
new file mode 100644
index 0000000..fafa65a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion07b.scr
@@ -0,0 +1,21 @@
+module bad.reach.globals.grecursion.Recursion07b;
+
+
+global protocol Recursion7b(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ continue X;
+ //l3() from A to B; // Uncomment bad
+ }
+ l3() from A to B; // Uncomment bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08a.scr
new file mode 100644
index 0000000..d4ca587
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08a.scr
@@ -0,0 +1,21 @@
+module bad.reach.globals.grecursion.Recursion08a;
+
+
+global protocol Recursion8a(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ continue X;
+ }
+ or
+ {
+ l2() from A to B;
+ continue X;
+ }
+ }
+ l3() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08b.scr
new file mode 100644
index 0000000..6ee8fbf
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion08b.scr
@@ -0,0 +1,21 @@
+module bad.reach.globals.grecursion.Recursion08b;
+
+
+global protocol Recursion8b(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ l1() from A to B;
+ continue X;
+ }
+ or
+ {
+ l2() from A to B;
+ continue X;
+ }
+ l3() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion11.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion11.scr
new file mode 100644
index 0000000..4b5c7cc
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion11.scr
@@ -0,0 +1,17 @@
+module bad.reach.globals.grecursion.Recursion11;
+
+
+global protocol Recursion11(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ continue X;
+ }
+ rec Y
+ {
+ l2() from B to A;
+ continue X; // Not really a reachability case, caught by name disamb
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17a.scr
new file mode 100644
index 0000000..7c2ea98
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17a.scr
@@ -0,0 +1,26 @@
+module bad.reach.globals.grecursion.Recursion17a;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ continue X;
+ 4() from A to B;
+ }
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ }
+ }
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17b.scr
new file mode 100644
index 0000000..f378892
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17b.scr
@@ -0,0 +1,26 @@
+module good.reach.globals.grecursion.Recursion17b;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ continue X;
+ }
+ 4() from A to B;
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ }
+ }
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17c.scr
new file mode 100644
index 0000000..76e48cb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17c.scr
@@ -0,0 +1,26 @@
+module bad.reach.globals.grecursion.Recursion17c;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ continue Y;
+ }
+ }
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17d.scr
new file mode 100644
index 0000000..ef13445
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17d.scr
@@ -0,0 +1,26 @@
+module bad.reach.globals.grecursion.Recursion17d;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ continue Z;
+ }
+ }
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17e.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17e.scr
new file mode 100644
index 0000000..7375025
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17e.scr
@@ -0,0 +1,26 @@
+module bad.reach.globals.grecursion.Recursion17e;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ }
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ continue Z;
+ }
+ }
+ 4() from A to B;
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17f.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17f.scr
new file mode 100644
index 0000000..5a55465
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion17f.scr
@@ -0,0 +1,26 @@
+module bad.reach.globals.grecursion.Recursion17f;
+
+
+global protocol Foo(role A, role B)
+{
+ rec Z
+ {
+ choice at A
+ {
+ rec X
+ {
+ 1() from A to B;
+ }
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ continue Z;
+ }
+ 4() from A to B;
+ }
+ }
+ 3() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion18.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion18.scr
new file mode 100644
index 0000000..cd90c46
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion18.scr
@@ -0,0 +1,20 @@
+module bad.reach.globals.grecursion.Recursion18;
+
+
+global protocol Foo(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ continue X; // Bad reach // Reachability error comes out as a graph building error if not checked first
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19a.scr
new file mode 100644
index 0000000..e397203
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19a.scr
@@ -0,0 +1,12 @@
+module bad.reach.globals.grecursion.Recursion19a;
+
+
+global protocol Foo(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from A to B;
+ continue X;
+ }
+ 2() from A to C;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19b.scr
new file mode 100644
index 0000000..8dbdb10
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/reach/globals/grecursion/Recursion19b.scr
@@ -0,0 +1,19 @@
+module bad.reach.globals.grecursion.Recursion19b;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ 1() from A to B; // Reachability error -- comes out as a global model error if that pass is done first (tail recursion needed as syntactic check prior to model checking) // Reachability now done before model check
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01a.scr
new file mode 100644
index 0000000..8306ff6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01a.scr
@@ -0,0 +1,19 @@
+module bad.safety.orphan.threeparty.Test01a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to C;
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from B to C;
+ 3() from B to C; // Also wait-for
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01b.scr
new file mode 100644
index 0000000..1336c35
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test01b.scr
@@ -0,0 +1,19 @@
+module bad.safety.orphan.threeparty.Test01b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to C;
+ }
+ or
+ {
+ 1() from A to B;
+ 4() from A to B; // Also wait-for
+ 2() from B to C; // (Mergeable B-C, no stuck)
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test02.scr
new file mode 100644
index 0000000..33e0d75
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test02.scr
@@ -0,0 +1,22 @@
+module bad.safety.orphan.threeparty.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 3() from A to C;
+ continue X; // Also wait-for
+ }
+ or
+ {
+ 1() from A to B;
+ 4() from A to C;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test03.scr
new file mode 100644
index 0000000..ec7832a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/threeparty/Test03.scr
@@ -0,0 +1,20 @@
+module bad.safety.orphan.threeparty.Test03;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ // Wait-for + orphan error counterparts (arise together)
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 2() from B to C;
+ }
+ or
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 2() from C to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test01.scr
new file mode 100644
index 0000000..6a3bc12
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test01.scr
@@ -0,0 +1,21 @@
+module bad.safety.orphan.twoparty.Test01;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B; // (..and counterpart wait-for)
+ continue X;
+ }
+ or
+ {
+ 1() from A to B; // Bad: orphans
+ }
+ }
+}
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test02.scr
new file mode 100644
index 0000000..31ed714
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/orphan/twoparty/Test02.scr
@@ -0,0 +1,19 @@
+module bad.safety.orphan.twoparty.Test02;
+
+
+global protocol Proto1(role A, role B)
+{
+ // Wait-for + orphan error counterparts (arise together)
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to A;
+ 2() from A to B; // Orphan (and deadlock)
+ }
+ or
+ {
+ 1() from A to B;
+ 1() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test01.scr
new file mode 100644
index 0000000..f9a4350
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test01.scr
@@ -0,0 +1,38 @@
+module bad.safety.stuckmsg.fourparty.Test01;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ choice at A
+ {
+ 1() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to C;
+ 2() from C to D;
+ choice at C
+ {
+ 2() from C to A;
+ }
+ or
+ {
+ () from C to A;
+ 2() from C to D; // Reception error (but OK if following is commented)
+ () from C to D;
+ }
+ continue X;
+ }
+ }
+ }
+ or
+ {
+ 0() from A to B;
+ 0() from B to C;
+ 0() from C to D;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02a.scr
new file mode 100644
index 0000000..80732dd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02a.scr
@@ -0,0 +1,23 @@
+module bad.safety.stuckmsg.fourparty.Test02a;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ rec X
+ {
+ 1() from A to B;
+ continue X; // Checking safety in non-terminating global model
+ }
+ choice at C
+ {
+ 2() from C to D;
+ 3() from D to C; // Stuck messages also manifest as message liveness errors (subsumed? so unnecessary to check safety except for actual terminations? -- but morally, they are local safety errors for the roles, as opposed to global liveness errors for the system)
+ }
+ or
+ {
+ 2() from C to D;
+ 4() from D to C;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02b.scr
new file mode 100644
index 0000000..a2cbaf5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/fourparty/Test02b.scr
@@ -0,0 +1,22 @@
+module bad.safety.stuckmsg.fourparty.Test02b;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ 1() from A to B;
+ 3() from B to A; // A not safe (but not a terminal global state)
+ }
+ rec X
+ {
+ 4() from C to D;
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01a.scr
new file mode 100644
index 0000000..5e48c76
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01a.scr
@@ -0,0 +1,24 @@
+module bad.safety.stuckmsg.payloads.Test01a;
+
+
+type "java.lang.Integer" from "rt.jar" as Int1;
+type "java.lang.Integer" from "rt.jar" as Int2;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ //1() from A to B; // Uncomment still bad
+ 1(Int1) from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ 1(Int2) from A to B; // here, payloads aren't considered as distinguishing for labels
+ // error is the special case of actions that are non-deterministic labels up to payloads
+ // (Syntactic MPST merge not defined for repeat labels, Cf. -oldwf)
+ 3() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01b.scr
new file mode 100644
index 0000000..d255271
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01b.scr
@@ -0,0 +1,24 @@
+module bad.safety.stuckmsg.payloads.Test01b;
+
+
+type "java.lang.Integer" from "rt.jar" as Int1;
+type "java.lang.Integer" from "rt.jar" as Int2;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1(Int1) from A to B;
+ 2() from B to A; // Mergeable continuations, but still bad..
+ }
+ or
+ {
+ 1(Int2) from A to B; // B doesn't know to treat as Int1 or Int2
+ // here, payloads aren't considered as distinguishing for labels
+ // error is the special case of actions that are non-deterministic labels up to payloads
+ // (Syntactic MPST merge not defined for repeat labels)
+ 2() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01c.scr
new file mode 100644
index 0000000..72b4c02
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01c.scr
@@ -0,0 +1,25 @@
+module bad.safety.stuckmsg.payloads.Test01c;
+
+
+type "java.lang.Integer" from "rt.jar" as Int1;
+type "java.lang.Integer" from "rt.jar" as Int2;
+
+
+// Cf. bad.safety.stuckmsg.payloads.Test01b
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2(Int1) from B to A;
+ }
+ or
+ {
+ 1() from A to B;
+ 2(Int2) from B to A; // (Syntactic MPST merge not defined for repeat labels)
+ // here, payloads are considered as distinguishing (as default) -- whole I/O action used as label
+ // Cf.\ Test01b, non-deterministic actions of the same state
+ // here, actions on different states -- error manifests as regular stuck
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01d.scr
new file mode 100644
index 0000000..9a9116b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01d.scr
@@ -0,0 +1,24 @@
+module bad.safety.stuckmsg.payloads.Test01d;
+
+
+type "java.lang.Integer" from "rt.jar" as Int1;
+type "java.lang.Integer" from "rt.jar" as Int2;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B; // Uncomment still bad
+ 1(Int1) from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ 1(Int2) from A to B; // here, payloads aren't considered as distinguishing for labels
+ // error is the special case of actions that are non-deterministic labels up to payloads
+ // (Syntactic MPST merge not defined for repeat labels, Cf. -oldwf)
+ 3() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01e.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01e.scr
new file mode 100644
index 0000000..3ad6e6a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/payloads/Test01e.scr
@@ -0,0 +1,22 @@
+module bad.safety.stuckmsg.payloads.Test01e;
+
+
+type "java.lang.Integer" from "rt.jar" as Int1;
+type "java.lang.Integer" from "rt.jar" as Int2;
+
+
+// Three-party
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2(Int1) from B to C;
+ }
+ or
+ {
+ 1() from A to B;
+ 2(Int2) from B to C; // Payload actions deterministic at B (following initial non-det by A), but non-det at C
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test01.scr
new file mode 100644
index 0000000..75acac5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test01.scr
@@ -0,0 +1,23 @@
+module bad.safety.stuckmsg.threeparty.Test01;
+
+
+// Cf. good.wfchoice.gchoice.Choice11;
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 2() from A to B; // bad safety
+ choice at A
+ {
+ 1() from A to C;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from A to C;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test02.scr
new file mode 100644
index 0000000..03a326b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test02.scr
@@ -0,0 +1,23 @@
+module bad.safety.stuckmsg.threeparty.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 3() from A to C;
+ continue X;
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from A to B;
+ 4() from A to C;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test03.scr
new file mode 100644
index 0000000..f9bd459
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/threeparty/Test03.scr
@@ -0,0 +1,24 @@
+module bad.safety.stuckmsg.threeparty.Test03;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from A to B;
+ 1() from B to C;
+ choice at B
+ {
+ 2() from B to A;
+ continue X;
+ }
+ or
+ {
+ 3() from B to A;
+ 1() from B to C;
+ 3() from B to C;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01a.scr
new file mode 100644
index 0000000..307f476
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01a.scr
@@ -0,0 +1,16 @@
+module bad.safety.stuckmsg.twoparty.Test01a;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01b.scr
new file mode 100644
index 0000000..dd584bd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test01b.scr
@@ -0,0 +1,20 @@
+module bad.safety.stuckmsg.twoparty.Test01b;
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from A to B;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test03.scr
new file mode 100644
index 0000000..de77ac4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test03.scr
@@ -0,0 +1,22 @@
+module bad.safety.stuckmsg.twoparty.Test03;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ do Bar(A, B);
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from A to B;
+ }
+}
+
+global protocol Bar(role A, role B)
+{
+ do Foo(A, B);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test04.scr
new file mode 100644
index 0000000..6ce3cf8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/stuckmsg/twoparty/Test04.scr
@@ -0,0 +1,18 @@
+module bad.safety.stuckmsg.twoparty.Test04;
+
+
+// Cf. good.wfchoice.enabling.twoparty.Test02
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ 1() from A to B;
+ 3() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test01.scr
new file mode 100644
index 0000000..5faa8e4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test01.scr
@@ -0,0 +1,19 @@
+module bad.safety.waitfor.threeparty.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B; // C starved without -fair, but irrelevant to deadlock error
+ continue X;
+ }
+ or
+ {
+ 2() from A to C; // No reception error, but B can be deadlocked
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test02.scr
new file mode 100644
index 0000000..5c07e6d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test02.scr
@@ -0,0 +1,30 @@
+module bad.safety.waitfor.threeparty.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at A
+ {
+ 1() from A to C;
+ continue X; // ** CHECKME: badly formed (for B) -- empty block bvelow is pruned leaving only the continue case, which leads to deadlock in global model, is this OK as WF algorithm? (any way to be unsound?) or should explicitly detect inconsistent choice block projections?
+ }
+ or
+ {
+ 3() from A to C;
+ }
+ }
+ or
+ {
+ 2() from A to C;
+ 2() from A to B;
+ }
+ }
+}
+//*/
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test03.scr
new file mode 100644
index 0000000..03787c3
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test03.scr
@@ -0,0 +1,19 @@
+module bad.safety.waitfor.threeparty.Test03;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to C;
+ }
+ or
+ {
+ 2() from A to B;
+ //1() from B to C; // Comment is bad
+ }
+ 2() from C to B;
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test04.scr
new file mode 100644
index 0000000..e023382
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test04.scr
@@ -0,0 +1,20 @@
+module bad.safety.waitfor.threeparty.Test04;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 1() from C to B;
+ }
+ or
+ {
+ 1() from A to B; // Uncomment is bad
+ 2() from A to B;
+ 2() from A to C;
+ 2() from C to B; // Also potential stuck
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test05.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test05.scr
new file mode 100644
index 0000000..58bd3f8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/threeparty/Test05.scr
@@ -0,0 +1,16 @@
+module bad.safety.waitfor.threeparty.Test05;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ 4() from A to C;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/twoparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/twoparty/Test01.scr
new file mode 100644
index 0000000..77c1d96
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/safety/waitfor/twoparty/Test01.scr
@@ -0,0 +1,26 @@
+module bad.safety.waitfor.twoparty.Test01;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to A;
+ 2() from A to B; // Async. permut. needs to be an asymmetric local feature; done "globally" allows potential deadlock
+ 2() from A to B;
+ 3() from B to A;
+ 3() from B to A;
+ }
+ or
+ {
+ 1() from A to B;
+ 1() from B to A;
+ 3() from B to A; // Potential deadlock
+ 3() from B to A;
+ 2() from A to B;
+ 2() from A to B;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test01.scr
new file mode 100644
index 0000000..25c7921
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test01.scr
@@ -0,0 +1,18 @@
+module bad.syntax.consistentchoicesubj.Test01;
+
+
+// Maybe refactor as projection
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test02.scr
new file mode 100644
index 0000000..5a99b0b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test02.scr
@@ -0,0 +1,15 @@
+module bad.syntax.consistentchoicesubj.Test02;
+
+
+// Maybe refactor as projection
+global protocol Proto1(role C, role S)
+{
+ choice at C
+ {
+ choice at S // Fixed peekParentEnv index bug for enabled choice subjs
+ {
+ 1() from S to C;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04a.scr
new file mode 100644
index 0000000..6ca52a1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04a.scr
@@ -0,0 +1,25 @@
+module bad.syntax.consistentchoicesubj.Test04a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ // Safe (without recursion), but (intentionally) not supported by local choice syntax -- so global model checking incomplete in general (i.e. morally, outside of Scribble syntax restrictions)
+ // Cf. -nolocalchoicecheck
+ /*rec X
+ {*/
+ choice at A
+ {
+ 1() from A to B;
+ 3() from B to C;
+ //continue X; // With recursion, -nolocalchoicecheck under WF_1 is unsound
+ }
+ or
+ {
+ 2() from A to B;
+ 4() from A to C;
+ //continue X;
+ }
+ //}
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04b.scr
new file mode 100644
index 0000000..03c94d0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04b.scr
@@ -0,0 +1,23 @@
+module bad.syntax.consistentchoicesubj.Test04b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 3() from B to C;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 4() from A to C;
+ continue X;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04c.scr
new file mode 100644
index 0000000..5429fa5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test04c.scr
@@ -0,0 +1,22 @@
+module bad.syntax.consistentchoicesubj.Test04c;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 3() from B to C;
+ }
+ or
+ {
+ 2() from A to B;
+ 4() from A to C;
+ }
+ continue X; // Would be OK, even ignoring inconsistent choice subjects, because C just needs to receive 3s or 4s with no further consequences
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05a.scr
new file mode 100644
index 0000000..801e79a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05a.scr
@@ -0,0 +1,20 @@
+module bad.syntax.consistentchoicesubj.Test05a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C; // Bad, even..
+ 2() from B to C;
+ 3() from C to B;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C; // ..without local choice subject check
+ 4() from C to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05b.scr
new file mode 100644
index 0000000..e73fdbd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test05b.scr
@@ -0,0 +1,25 @@
+module bad.syntax.consistentchoicesubj.Test05b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C; // Bad, even..
+ 2() from B to C;
+ 3() from C to B;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C; // ..without local choice subject check
+ 4() from C to B;
+ }
+ continue X;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06a.scr
new file mode 100644
index 0000000..f6e8189
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06a.scr
@@ -0,0 +1,18 @@
+module bad.syntax.consistentchoicesubj.Test06a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A // Would be OK without local choice subject checking (and a multi-queue input branch primitive)
+ {
+ 1() from A to B;
+ 2() from B to C;
+ 3() from C to B;
+ }
+ or
+ {
+ 4() from A to C;
+ 5() from C to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06b.scr
new file mode 100644
index 0000000..845c59d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test06b.scr
@@ -0,0 +1,22 @@
+module bad.syntax.consistentchoicesubj.Test06b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A // Morally bad, even without local choice subject checking (and a multi-queue input branch primitive, etc.)
+ {
+ 1() from A to B;
+ 2() from B to C;
+ 3() from C to B;
+ }
+ or
+ {
+ 4() from A to C;
+ 5() from C to B;
+ }
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07a.scr
new file mode 100644
index 0000000..e06b406
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07a.scr
@@ -0,0 +1,23 @@
+module bad.syntax.consistentchoicesubj.Test07a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 4() from C to B; // Bad.. (apart from inconsistent choice subjects)
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 3() from B to C;
+ 5() from C to B;
+ //continue X; // ..but OK if this continue commented -- is that correct?
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07b.scr
new file mode 100644
index 0000000..e6cf842
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07b.scr
@@ -0,0 +1,23 @@
+module bad.syntax.consistentchoicesubj.Test07b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 4() from C to B;
+ // OK if -nolocalchoicecheck, and morally OK (no recursion, cf. bad.syntax.consistentchoicesubj.Test07a)
+ }
+ or
+ {
+ 2() from A to B;
+ 3() from B to C;
+ 5() from C to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07c.scr
new file mode 100644
index 0000000..256c33c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test07c.scr
@@ -0,0 +1,24 @@
+module bad.syntax.consistentchoicesubj.Test07b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 4() from C to A; // OK by -nolocalchoicecheck, and also morally OK because A receives from C (no asynchronous "iteration ambiguity" -- cf. 07a/07b)
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 3() from B to C;
+ 5() from C to A;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test08.scr
new file mode 100644
index 0000000..e7c0fa9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/consistentchoicesubj/Test08.scr
@@ -0,0 +1,22 @@
+module bad.syntax.consistentchoicesubj.Test08;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ 1() from C to B;
+ }
+ or
+ {
+ 2() from A to C;
+ 2() from C to B; // B enabled by A/C
+ }
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/Test01.scr
new file mode 100644
index 0000000..70fdc6e
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/Test01.scr
@@ -0,0 +1,14 @@
+module bad.syntax.disamb.Test01;
+
+
+sig "foo" from "bar" as M1;
+
+
+global protocol Foo(role A, role B)
+{
+ () from A to B;
+ M from A to B;
+ M1 from A to B;
+ M2 from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList01.scr
new file mode 100644
index 0000000..311f987
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList01.scr
@@ -0,0 +1,14 @@
+module bad.syntax.disamb.doarglist.DoArgList01;
+
+
+global protocol Foo(role A, role B, role C)
+{
+ 1() from A to B;
+ 1() from A to C;
+ do Bar(A, B, C);
+}
+
+global protocol Bar(role D, role E)
+{
+ 2() from D to E;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList02.scr
new file mode 100644
index 0000000..22bdafc
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList02.scr
@@ -0,0 +1,14 @@
+module bad.syntax.disamb.doarglist.DoArgList02;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar(A, B);
+}
+
+global protocol Bar(role C, role D, role E)
+{
+ 1() from C to D;
+ 2() from D to E;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList03.scr
new file mode 100644
index 0000000..9c5e43f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList03.scr
@@ -0,0 +1,14 @@
+module bad.syntax.disamb.doarglist.DoArgList03;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar<2()>(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ M1 from C to D;
+ M2 from D to C;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList04.scr
new file mode 100644
index 0000000..dfc6093
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList04.scr
@@ -0,0 +1,13 @@
+module bad.syntax.disamb.doarglist.DoArgList04;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar<2(), 3()>(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ M1 from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList05.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList05.scr
new file mode 100644
index 0000000..68255b7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList05.scr
@@ -0,0 +1,13 @@
+module bad.syntax.disamb.doarglist.DoArgList05;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar<2()>(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ 2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList06.scr
new file mode 100644
index 0000000..41098fc
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/doarglistdel/DoArgList06.scr
@@ -0,0 +1,13 @@
+module bad.syntax.disamb.doarglist.DoArgList06;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ M1 from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gmessagetransfer/GMessageTransfer01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gmessagetransfer/GMessageTransfer01.scr
new file mode 100644
index 0000000..ada699b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gmessagetransfer/GMessageTransfer01.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.globals.gmessagetransfer.GMessageTransfer01;
+
+
+global protocol Foo(role A)
+{
+ 1() from A to A; // TODO: should be good
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl01.scr
new file mode 100644
index 0000000..e7899ed
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl01.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.globals.gprotocoldecl.GProtocolDecl01;
+
+
+global protocol Foo(role A, role B, role C)
+{
+
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl02.scr
new file mode 100644
index 0000000..f8e600a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl02.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.globals.gprotocoldecl.GProtocolDecl02;
+
+
+global protocol GProtocolDecl2(role A, role B, role C)
+{
+ 1() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl03.scr
new file mode 100644
index 0000000..8b2bad3
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl03.scr
@@ -0,0 +1,12 @@
+module bad.syntax.disamb.globals.protocoldecl.GProtocolDecl03;
+
+
+global protocol GProtocolDecl3a(role D, role E)
+{
+ do GProtocolDecl3b(D, E);
+}
+
+global protocol GProtocolDecl3b(role D, role E)
+{
+ do GProtocolDecl3a(D, E);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl04.scr
new file mode 100644
index 0000000..0fabd2a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/globals/gprotocoldecl/GProtocolDecl04.scr
@@ -0,0 +1,18 @@
+module bad.syntax.disamb.globals.protocoldecl.GProtocolDecl04;
+
+
+global protocol GProtocolDecl4(role A, role B, role C)
+{
+ do GProtocolDecl4a(A, B);
+}
+
+global protocol GProtocolDecl4a(role B, role C)
+{
+ do GProtocolDecl4b(B, C);
+}
+
+global protocol GProtocolDecl4b(role D, role E)
+{
+ 1() from D to E;
+ do GProtocolDecl4a(D, E);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/headerparamdecllist/HeaderParamDeclList03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/headerparamdecllist/HeaderParamDeclList03.scr
new file mode 100644
index 0000000..a84d6b5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/headerparamdecllist/HeaderParamDeclList03.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.headerparamdecllist.HeaderParamDeclList03;
+
+
+global protocol Foo(role A, role A)
+{
+ 1() from A to A;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import02.scr
new file mode 100644
index 0000000..0c2d171
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import02.scr
@@ -0,0 +1,4 @@
+module bad.syntax.disamb.imports.Import02;
+
+
+import bad.syntax.disamb.imports.Import02;
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import03.scr
new file mode 100644
index 0000000..05c54e1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import03.scr
@@ -0,0 +1,4 @@
+module bad.syntax.disamb.imports.Import03;
+
+
+import bad.syntax.disamb.imports.Import03 as Import03; // The simple name Import03 is implicitly visible from moduledecl
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import06.scr
new file mode 100644
index 0000000..e72d3fe
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import06.scr
@@ -0,0 +1,9 @@
+//Raymond@HZHL2 ~/code/python/scribble-tools
+//$ PYTHONPATH='lib/:bin/' python src/scribble/Main.py -ip test/ test/base/impourt/Import6.scr
+
+// Possiblty good for testing for potential loops in recursive imports when a module is bad? -- could allow this as a special case? (import self)
+
+module bad.parsing.imports.Import06;
+
+import bad.parsing.imports.Import06;
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import17.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import17.scr
new file mode 100644
index 0000000..2119030
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import17.scr
@@ -0,0 +1,7 @@
+//Raymond@HZHL2 ~/code/python/scribble-tools
+//$ PYTHONPATH='lib/:bin/' python src/scribble/Main.py -ip test/ test/base/impourt/Import17.scr
+
+module bad.parsing.imports.Import17;
+
+
+import bad.parsing.imports.Import17; // Old: OK because full name is a "compound" name -- old: if simple and full names coincide for a module, self import is not OK
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import18.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import18.scr
new file mode 100644
index 0000000..5e83c0d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import18.scr
@@ -0,0 +1,12 @@
+module good.syntax.disamb.imports.Import18;
+
+
+import bad.syntax.disamb.imports.pack1.Import18b as Bar;
+
+
+global protocol Foo(role A, role B)
+{
+ do good.syntax.disamb.imports.pack1.Import18b.Proto2(A, B);
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import19.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import19.scr
new file mode 100644
index 0000000..92c1cf1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/Import19.scr
@@ -0,0 +1,7 @@
+module good.syntax.disamb.imports.Import19;
+
+
+import bad.syntax.disamb.imports.pack1.Import19b;
+
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/pack1/IGNORE b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/pack1/IGNORE
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/pack1/Import19b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/pack1/Import19b.scr
new file mode 100644
index 0000000..1ecfdc4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/imports/pack1/Import19b.scr
@@ -0,0 +1,8 @@
+module bad.syntax.disamb.imports.pack1.Import19b;
+
+
+global protocol Proto1(role C, role D)
+{
+ () from C to A;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules01.scr
new file mode 100644
index 0000000..d83bb4d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules01.scr
@@ -0,0 +1,13 @@
+module bad.syntax.disamb.modules.Modules01;
+
+
+// FIXME: inconsistent classification of diamb tests, e.g. modules vs protocoldecl, imports, etc
+global protocol Foo(role A, role B)
+{
+
+}
+
+global protocol Foo(role A, role B)
+{
+
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules02.scr
new file mode 100644
index 0000000..30eac00
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules02.scr
@@ -0,0 +1,5 @@
+module bad.syntax.disamb.modules.Modules02;
+
+
+type "java.lang.String" from "rt.jar" as String;
+type "java.lang.String" from "rt.jar" as String;
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules03.scr
new file mode 100644
index 0000000..0f02a88
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules03.scr
@@ -0,0 +1,5 @@
+module bad.syntax.disamb.modules.Modules03;
+
+
+type "java.lang.String" from "rt.jar" as String;
+sig "java.lang.String" from "rt.jar" as String;
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04a.scr
new file mode 100644
index 0000000..565880e
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04a.scr
@@ -0,0 +1,4 @@
+module bad.syntax.disamb.modules.Modules04a;
+
+import bad.syntax.disamb.modules.Modules04a;
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04b.scr
new file mode 100644
index 0000000..b3c2af7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04b.scr
@@ -0,0 +1,4 @@
+module bad.syntax.disamb.modules.Modules04b;
+
+import bad.syntax.disamb.modules.Modules04b as Modules04b; // Simple name coincides with full name
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04c.scr
new file mode 100644
index 0000000..9bd3447
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04c.scr
@@ -0,0 +1,11 @@
+module bad.syntax.disamb.modules.Modules04c;
+
+
+import bad.syntax.disamb.modules.pack1.Foo1 as Modules04c;
+
+
+global protocol Proto(role A, role B)
+{
+ 1() from A to B;
+ do Modules04c.Proto(A, B);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04d.scr
new file mode 100644
index 0000000..e2c63fb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04d.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.modules.Modules04d;
+
+
+import bad.syntax.disamb.modules.pack1.Foo1 as Foo;
+import bad.syntax.disamb.modules.pack1.Foo2 as Foo;
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04e.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04e.scr
new file mode 100644
index 0000000..73444b8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/Modules04e.scr
@@ -0,0 +1,4 @@
+module bad.syntax.disamb.modules.Modules04e;
+
+import bad.syntax.disamb.modules.pack1.Foo1 as Modules04e;
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo1.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo1.scr
new file mode 100644
index 0000000..533686d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo1.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.modules.pack1.Foo1;
+
+
+global protocol Proto(role A, role B)
+{
+ 2() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo2.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo2.scr
new file mode 100644
index 0000000..2d1ac71
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/Foo2.scr
@@ -0,0 +1,7 @@
+module bad.syntax.disamb.modules.pack1.Foo2;
+
+
+global protocol Proto(role A, role B)
+{
+ 2() from A to B;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/IGNORE b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/modules/pack1/IGNORE
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList01.scr
new file mode 100644
index 0000000..018a05b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList01.scr
@@ -0,0 +1,15 @@
+module bad.syntax.disamb.rolearglist.NonRoleArgList01;
+
+
+type "..." from "..." as TTT;
+
+
+global protocol Foo(role A, role B)
+{
+ do Bar(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ M1 from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList02.scr
new file mode 100644
index 0000000..8a31491
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/nonrolearglist/NonRoleArgList02.scr
@@ -0,0 +1,12 @@
+module bad.syntax.disamb.rolearglist.NonRoleArgList02;
+
+
+global protocol Foo(role A, role B)
+{
+ do Bar<1()>(A, B);
+}
+
+global protocol Bar(role C, role D)
+{
+ 2(T) from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test01.scr
new file mode 100644
index 0000000..14074a6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test01.scr
@@ -0,0 +1,15 @@
+module bad.syntax.disamb.protocoldecl.Test01;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1(A, B);
+}
+
+
+global protocol Proto1(role A, role B) // Testing disamb
+{
+ 2() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test02.scr
new file mode 100644
index 0000000..6bf106d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/protocoldecl/Test02.scr
@@ -0,0 +1,9 @@
+module bad.syntax.disamb.protocoldecl.Test02;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto2(A, B); // Testing disamb
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion01a.scr
new file mode 100644
index 0000000..3a7bbeb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion01a.scr
@@ -0,0 +1,16 @@
+module bad.syntax.disamb.recvarnode.Recursion01a;
+
+
+global protocol Recursion1a(role A, role B)
+{
+ //rec A { } // Currently OK. Disallow?
+
+ rec X
+ {
+ l1() from A to B;
+ //continue X;
+ continue Y; // Uncomment bad, even if above line is commented
+ //l2() from A to B; // Uncomment bad
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05b.scr
new file mode 100644
index 0000000..0f7e4ad
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05b.scr
@@ -0,0 +1,18 @@
+module bad.syntax.disamb.recvarnode.Recursion05b;
+
+
+global protocol Recursion5b(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ //continue X; // Uncomment makes the below rec bad
+ continue Y; // Uncomment bad
+ }
+ rec Y
+ {
+ l2() from B to A;
+ continue Y; // A bad "continue X" here tested in Recursion11
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05c.scr
new file mode 100644
index 0000000..feecdca
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion05c.scr
@@ -0,0 +1,19 @@
+module bad.syntax.disamb.recvarnode.Recursion05c;
+
+
+global protocol Recursion5c(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ //continue X; // Uncomment makes the below rec bad
+ //continue Y; // Uncomment bad
+ }
+ rec Y
+ {
+ l2() from B to A;
+ //continue Y; // A bad "continue X" here tested in Recursion11
+ continue X; // A bad "continue X" here tested in Recursion11
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion11.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion11.scr
new file mode 100644
index 0000000..549cbc4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/recvarnode/Recursion11.scr
@@ -0,0 +1,17 @@
+module bad.syntax.disamb.recvarnode.Recursion11;
+
+
+global protocol Recursion11(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ }
+ rec Y
+ {
+ l2() from B to A;
+ //continue Y;
+ continue X; // Uncomment bad, even if above line is commented
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolearglist/RoleArgList02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolearglist/RoleArgList02.scr
new file mode 100644
index 0000000..dfbc07b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolearglist/RoleArgList02.scr
@@ -0,0 +1,12 @@
+module bad.syntax.disamb.rolearglist.RoleArgList02;
+
+
+global protocol Foo(role A, role B)
+{
+ do Bar(A, A);
+}
+
+global protocol Bar(role C, role D)
+{
+ 1() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do13a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do13a.scr
new file mode 100644
index 0000000..5304dfe
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do13a.scr
@@ -0,0 +1,9 @@
+module bad.syntax.disamb.roledecl.Do13a;
+
+
+global protocol Do13a(role A, role B)
+{
+ //l1() from A to B; // Uncomment is OK
+ do Do13a(A, B);
+ //l1() from A to B; // Uncomment is bad
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do23a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do23a.scr
new file mode 100644
index 0000000..c648acf
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/roledecl/Do23a.scr
@@ -0,0 +1,22 @@
+module bad.syntax.disamb.roledecl.Do23a;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+// Overlaps with Do25?
+global protocol Do23a(role A, role B)
+{
+ do Do23aux(A, B);
+}
+
+global protocol Do23aux(role C, role D)
+{
+ do Do23a2(C, D);
+}
+
+global protocol Do23a2(role E, role F)
+//global protocol Do23a2(role E, role F)
+{
+ //X from E to F; // Uncomment OK
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolenode/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolenode/Test02.scr
new file mode 100644
index 0000000..13b0ce4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/disamb/rolenode/Test02.scr
@@ -0,0 +1,17 @@
+module bad.syntax.disamb.rolenode.Test02;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from A to C;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test01.scr
new file mode 100644
index 0000000..3b87de4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test01.scr
@@ -0,0 +1,21 @@
+module bad.syntax.enabling.Test02;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to C;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ or
+ {
+ 1() from B to C;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test02.scr
new file mode 100644
index 0000000..3dbd21f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/enabling/Test02.scr
@@ -0,0 +1,20 @@
+module bad.syntax.enabling.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ //0() from B to C;
+ choice at A
+ {
+ 0() from B to C;
+ 1() from A to B;
+ 1() from B to C;
+ }
+ or
+ {
+ 0() from B to C;
+ 2() from A to B;
+ 2() from B to C;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/rolecollection/gprotocoldecl/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/rolecollection/gprotocoldecl/Test01.scr
new file mode 100644
index 0000000..585d1cd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/syntax/rolecollection/gprotocoldecl/Test01.scr
@@ -0,0 +1,15 @@
+module bad.syntax.rolecollection.gprotocoldecl.Test01;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ do Bar(A, B);
+ 2() from A to B;
+}
+
+aux global protocol Bar(role A, role B) // Testing bad unused role decls (wrt. subprotocol collected role occurrences) -- actually, allow due to aux?
+{
+
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07a.scr
new file mode 100644
index 0000000..3487c42
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07a.scr
@@ -0,0 +1,25 @@
+module bad.wfchoice.gchoice.Choice07a;
+
+
+global protocol Choice7a(role A, role B)
+{
+ choice at A
+ {
+ /*// TODO
+ par
+ {
+ l1() from A to B;
+ }
+ and
+ {
+ l2() from B to A; // Uncomment is bad
+ l2() from A to B;
+ }*/
+ }
+ or
+ {
+ //l2() from A to B; // Uncomment is bad
+ l3() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07b.scr
new file mode 100644
index 0000000..23d613d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Choice07b.scr
@@ -0,0 +1,25 @@
+module bad.wfchoice.gchoice.Choice07b;
+
+
+global protocol Choice7b(role A, role B)
+{
+ choice at A
+ {
+ /*//TODO
+ par
+ {
+ l1() from A to B;
+ }
+ and
+ {
+ //l2() from B to A; // Uncomment is bad
+ l2() from A to B;
+ }*/
+ }
+ or
+ {
+ l2() from A to B; // Uncomment is bad
+ l3() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/IGNORE b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/IGNORE
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Modules02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Modules02.scr
new file mode 100644
index 0000000..e6414d6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Modules02.scr
@@ -0,0 +1,12 @@
+module bad.syntax.disamb.modules.Modules02;
+
+
+global protocol Foo(role A, role B)
+{
+
+}
+
+local protocol Foo(role A, role B) // TODO: local not parsed
+{
+
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion12.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion12.scr
new file mode 100644
index 0000000..6a04254
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion12.scr
@@ -0,0 +1,21 @@
+module base.recursion.Recursion12;
+
+
+global protocol Recursion12(role A, role B)
+{
+ /*// TODO
+ par
+ {
+ rec X
+ {
+ l1() from A to B;
+ continue X;
+ }
+ }
+ and
+ {
+ l2() from B to A;
+ }*/
+ l3() from B to A; // Uncommented bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion14a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion14a.scr
new file mode 100644
index 0000000..744ae9c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/todo/Recursion14a.scr
@@ -0,0 +1,23 @@
+module base.recursion.Recursion14a;
+
+
+global protocol Recursion14a(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ do Y: base.recursion.Recursion14.Recursion14a(A, B); // TODO
+ do X: base.recursion.Recursion14.Recursion14a(A, B); // Uncomment OK if above line is commented // TODO
+ //do base.recursion.Recursion14.Recursion14a(A, B); // Uncomment bad, even if above two lines are commented
+ }
+}
+
+global protocol Recursion14a(role A, role B)
+{
+ rec X
+ {
+ l1() from A to B;
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/fourparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/fourparty/Test01.scr
new file mode 100644
index 0000000..9410bbd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/fourparty/Test01.scr
@@ -0,0 +1,27 @@
+module bad.wfchoice.enabling.fourparty.Test01;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ choice at A
+ {
+ 1() from A to B;
+ rec X
+ {
+ 2() from B to C;
+ choice at C
+ {
+ 2() from C to A;
+ continue X; // Enabling error, with the continue inside this choice scope (continue factored out after choice is not enabling error)
+ }
+ or
+ {
+ () from C to A;
+ () from C to D;
+ continue X;
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test01.scr
new file mode 100644
index 0000000..12e5ab7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test01.scr
@@ -0,0 +1,17 @@
+module bad.wfchoice.enabling.threeparty.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 1() from A to B;
+ 2() from A to C;
+ choice at A
+ {
+ do Proto1(A, B, C);
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from A to C;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test02.scr
new file mode 100644
index 0000000..e8c6a74
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test02.scr
@@ -0,0 +1,23 @@
+module bad.wfchoice.enabling.threeparty.Test02;
+
+
+// Cf. good.wfchoice.gchoice.Choice11;
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from B to C; // bad enabling
+ choice at A
+ {
+ 1() from A to C;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from A to C;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03a.scr
new file mode 100644
index 0000000..54c1869
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03a.scr
@@ -0,0 +1,28 @@
+module bad.wfchoice.enabling.threeparty.Test03a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 1() from B to C;
+ continue X; // choice at A, but A not enabled
+ }
+ or
+ {
+ 3() from B to C;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03b.scr
new file mode 100644
index 0000000..5354865
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/threeparty/Test03b.scr
@@ -0,0 +1,31 @@
+module bad.wfchoice.enabling.threeparty.Test03b;
+
+
+// Cf. module good.wfchoice.enabling.threeparty.Test03b;
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 1() from B to C;
+ continue X;
+ }
+ or
+ {
+ 3() from B to C;
+ continue X;
+ }
+ //continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01a.scr
new file mode 100644
index 0000000..940aec2
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01a.scr
@@ -0,0 +1,16 @@
+module bad.wfchoice.enabling.twoparty.Test01a;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ 1() from C to D; // Enabled violation once unfolded
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01b.scr
new file mode 100644
index 0000000..08f8613
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01b.scr
@@ -0,0 +1,16 @@
+module bad.wfchoice.enabling.twoparty.Test01b;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ rec X
+ {
+ 1() from C to D; // Enabled violation once unfolded
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01c.scr
new file mode 100644
index 0000000..77cfc41
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/enabling/twoparty/Test01c.scr
@@ -0,0 +1,21 @@
+module bad.wfchoice.enabling.twoparty.Test01c;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ 3() from C to D; // Enabled violation once unfolded
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice01.scr
new file mode 100644
index 0000000..aff45c6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice01.scr
@@ -0,0 +1,14 @@
+module bad.wfchoice.gchoice.Choice01;
+
+global protocol Choice1(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l1() from A to B; // Uncomment is bad
+ l2() from A to B;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice02.scr
new file mode 100644
index 0000000..a05c1ef
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice02.scr
@@ -0,0 +1,10 @@
+module bad.wfchoice.gchoice.Choice02;
+
+global protocol Choice2(role A, role B)
+{
+ choice at A
+ {
+ l1() from B to A; // Uncomment is bad
+ l1() from A to B;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05a.scr
new file mode 100644
index 0000000..1ee8b34
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05a.scr
@@ -0,0 +1,32 @@
+module bad.wfchoice.gchoice.Choice05a;
+
+
+global protocol Choice5a(role A, role B)
+{
+ choice at B
+ {
+ //l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ }
+ or
+ {
+ l3() from B to A;
+ //l2() from B to A; // Uncomment OK unless l3() is commented
+ //l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05b.scr
new file mode 100644
index 0000000..daf3d59
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05b.scr
@@ -0,0 +1,34 @@
+module bad.wfchoice.gchoice.Choice05b;
+
+
+global protocol Choice5b(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ l3() from B to A; // Comment is still bad
+ }
+ or
+ {
+ //l3() from B to A;
+ l2() from B to A; // Uncomment OK unless l3() is commented
+ l4() from B to A;
+ //l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05c.scr
new file mode 100644
index 0000000..1557e3f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice05c.scr
@@ -0,0 +1,32 @@
+module bad.wfchoice.gchoice.Choice05c;
+
+
+global protocol Choice5c(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ }
+ or
+ {
+ //l3() from B to A;
+ //l2() from B to A; // Uncomment OK unless l3() is commented
+ l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice06.scr
new file mode 100644
index 0000000..a8d23ff
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice06.scr
@@ -0,0 +1,28 @@
+module bad.wfchoice.gchoice.Choice06;
+
+
+global protocol Choice6(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ () from A to B;
+ }
+ l1() from A to B;
+ () from A to B;
+ choice at A
+ {
+ //l1() from A to B; // Comment is bad
+ () from A to B;
+ 2() from A to B;
+ }
+ or
+ {
+ () from A to B;
+ 3() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice08.scr
new file mode 100644
index 0000000..a1261bb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice08.scr
@@ -0,0 +1,21 @@
+module bad.wfchoice.gchoice.Choice08;
+
+
+global protocol Choice8(role A, role B, role C)
+{
+ choice at A
+ {
+ l1() from A to B;
+ l2() from B to C;
+ //l4() from C to A; // Uncomment makes it OK
+ }
+ or
+ {
+ l2() from A to B;
+ //l1() from B to C; // Comment is bad
+ l2() from B to C;
+ l4() from C to A;
+ }
+ l4() from A to C;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09a.scr
new file mode 100644
index 0000000..7f71948
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09a.scr
@@ -0,0 +1,30 @@
+module bad.wfchoice.gchoice.Choice09a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 3() from A to C;
+ rec Y
+ {
+ 2() from A to B; // Uncomment is bad (non-enabled B / non-disjoint labels)
+ 2a() from B to A;
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ 2() from A to B;
+ continue X; // Testing project choice subject "inference", and testing continue-only choice block
+ }
+ or
+ {
+ continue Y; // Different continue-only-blocks, tests FSM building
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09b.scr
new file mode 100644
index 0000000..4174eb0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09b.scr
@@ -0,0 +1,34 @@
+module bad.wfchoice.gchoice.Choice09b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 3() from A to C;
+ rec Y
+ {
+ 2() from A to B; // Uncomment is bad (non-enabled B / non-disjoint labels)
+ 2a() from B to A;
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ 2() from A to B;
+ continue X; // Testing project choice subject "inference", and testing continue-only choice block
+ }
+ or
+ {
+ continue X;
+ }
+ or
+ {
+ continue Y; // Different continue-only-blocks, tests FSM building
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09c.scr
new file mode 100644
index 0000000..c93ea8b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice09c.scr
@@ -0,0 +1,34 @@
+module bad.wfchoice.gchoice.Choice09c;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 3() from A to C;
+ rec Y
+ {
+ 2() from A to B; // Uncomment is bad (non-enabled B / non-disjoint labels)
+ 2a() from B to A;
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue Y; // Different continue-only-blocks, tests FSM building
+ }
+ or
+ {
+ 2() from A to B;
+ continue X; // Testing project choice subject "inference", and testing continue-only choice block
+ }
+ or
+ {
+ continue X;
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10a.scr
new file mode 100644
index 0000000..af2cab6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10a.scr
@@ -0,0 +1,32 @@
+module bad.wfchoice.gchoice.Choice10a;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ choice at A
+ {
+ //continue X;
+ continue Y;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10b.scr
new file mode 100644
index 0000000..714fe69
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gchoice/Choice10b.scr
@@ -0,0 +1,31 @@
+module bad.wfchoice.gchoice.Choice10b;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ //1() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ rec Y
+ {
+ 2() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test01.scr
new file mode 100644
index 0000000..d870c4e
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test01.scr
@@ -0,0 +1,23 @@
+module bad.wfchoice.gdo.params.Test01;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ M1 from A to B;
+ 1() from A to B;
+ }
+ or
+ {
+ do Bar(A, B);
+ 2() from A to B;
+ }
+}
+
+
+aux global protocol Bar(role C, role D)
+{
+ M2 from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test02.scr
new file mode 100644
index 0000000..0d95bbf
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test02.scr
@@ -0,0 +1,23 @@
+module bad.wfchoice.gdo.params.Test02;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ do Bar<1()>(A, B);
+ 3() from B to A;
+ }
+}
+
+
+aux global protocol Bar(role C, role D)
+{
+ M from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test03.scr
new file mode 100644
index 0000000..0ab414b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/gdo/params/Test03.scr
@@ -0,0 +1,25 @@
+module bad.wfchoice.gdo.params.Test03;
+
+
+sig "foo" from "bar" as M1;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ M1 from A to B;
+ }
+ or
+ {
+ do Bar(A, B);
+ do Bar<1()>(A, B);
+ }
+}
+
+
+aux global protocol Bar(role A, role B)
+{
+ M from A to B;
+}
+//*/
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/merge/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/merge/Test01.scr
new file mode 100644
index 0000000..33640a0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/merge/Test01.scr
@@ -0,0 +1,21 @@
+module bad.wfchoice.merge.Test01;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ // Would be OK with "syntactic merge", but currently bad wrt. basic non-det interpretation
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to C;
+ 4() from A to C;
+ }
+ or
+ {
+ 3() from A to B;
+ 2() from B to C;
+ 5() from A to C;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/IGNORE b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/IGNORE
new file mode 100644
index 0000000..e69de29
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice05b.scr
new file mode 100644
index 0000000..0777ac4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice05b.scr
@@ -0,0 +1,35 @@
+module bad.oldwfchoice.gchoice.Choice05b;
+
+
+// Bad only for -oldwf; good by default
+global protocol Choice5b(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ //l3() from B to A; // Comment is still bad
+ }
+ or
+ {
+ ////l3() from B to A;
+ l2() from B to A; // Uncomment OK unless l3() is commented
+ //l4() from B to A;
+ ////l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice06.scr
new file mode 100644
index 0000000..1361094
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice06.scr
@@ -0,0 +1,28 @@
+module bad.wfchoice.oldwfchoice.gchoice.Choice06;
+
+
+global protocol Choice6(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ () from A to B;
+ }
+ l1() from A to B;
+ () from A to B;
+ choice at A
+ {
+ ////l1() from A to B; // Comment is bad
+ () from A to B;
+ //2() from A to B;
+ }
+ or
+ {
+ () from A to B;
+ //3() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice08.scr
new file mode 100644
index 0000000..e6b1084
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice08.scr
@@ -0,0 +1,21 @@
+module bad.wfchoice.oldwfchoice.gchoice.Choice08;
+
+
+global protocol Choice8(role A, role B, role C)
+{
+ choice at A
+ {
+ l1() from A to B;
+ l2() from B to C;
+ ////l4() from C to A; // Uncomment makes it OK
+ }
+ or
+ {
+ l2() from A to B;
+ ////l1() from B to C; // Comment is bad
+ l2() from B to C;
+ //l4() from C to A;
+ }
+ l2() from C to A;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice09.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice09.scr
new file mode 100644
index 0000000..20c262d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice09.scr
@@ -0,0 +1,19 @@
+module bad.wfchoice.oldwfchoice.gchoice.Choice09;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice10.scr
new file mode 100644
index 0000000..be27df3
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/bad/wfchoice/oldwfchoice/globals/gchoice/Choice10.scr
@@ -0,0 +1,34 @@
+module bad.wfchoice.oldwfchoice.gchoice.Choice10;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 3() from A to C;
+ rec Y
+ {
+ () from A to B;
+ 2a() from B to A;
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ //2() from A to B;
+ continue X; // Testing project choice subject "inference", and testing continue-only choice block
+ }
+ or
+ {
+ continue X;
+ }
+ or
+ {
+ continue Y; // Different continue-only-blocks, tests FSM building
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test02.scr
new file mode 100644
index 0000000..e9b379c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test02.scr
@@ -0,0 +1,18 @@
+module good.efsm.gchoice.Test02;
+
+
+global protocol Foo(role A, role B)
+{
+ 0() from A to B;
+ choice at A
+ {
+ 1a() from A to B;
+ }
+ or
+ {
+ 2a() from A to B;
+ 2b() from B to A;
+ }
+ 3() from B to A;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test03.scr
new file mode 100644
index 0000000..ebd95ff
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test03.scr
@@ -0,0 +1,22 @@
+module good.efsm.gchoice.Test03;
+
+
+global protocol Foo(role A, role B)
+{
+ choice at A // unary choice with continuation
+ {
+ 1() from A to B;
+ }
+ rec X
+ {
+ choice at A
+ {
+ 2() from A to B;
+ continue X;
+ }
+ or
+ {
+ 3() from A to B;
+ }
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test09.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test09.scr
new file mode 100644
index 0000000..2b5b868
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test09.scr
@@ -0,0 +1,20 @@
+module good.efsm.gchoice.Test09;
+
+
+global protocol Foo(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 1() from A to B;
+ }
+ continue X; // Testing recursion edge replacement cornercase (length 1 non-det choice paths are merged as a single case)
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test10.scr
new file mode 100644
index 0000000..478ad2a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test10.scr
@@ -0,0 +1,39 @@
+module good.efsm.gchoice.Test10;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ choice at B
+ {
+ 2() from B to A;
+ continue X;
+ }
+ or
+ {
+ 4a() from B to A;
+ }
+ or
+ {
+ 4b() from B to A;
+ }
+ }
+ }
+ choice at A
+ {
+ 3a() from A to B;
+ }
+ or {
+ 3b() from A to B;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test11.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test11.scr
new file mode 100644
index 0000000..e7ca4cc
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test11.scr
@@ -0,0 +1,16 @@
+module good.efsm.gchoice.Test11;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to C; // Graph building (popping choice block into a null parent choice block context)
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12a.scr
new file mode 100644
index 0000000..d7a3d13
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12a.scr
@@ -0,0 +1,20 @@
+module good.efsm.gchoice.Test12a;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ continue X; // EFSM building -- multiple predecessors for continue-edge replacement
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12b.scr
new file mode 100644
index 0000000..37ef3ac
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test12b.scr
@@ -0,0 +1,23 @@
+module good.efsm.gchoice.Test12b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 0() from A to B;
+ 0() from B to C;
+ choice at A
+ {
+ 1() from A to B;
+ 1() from B to C;
+ }
+ or
+ {
+ 2() from A to B;
+ 2() from B to C;
+ }
+ continue X; // EFSM building -- multiple predecessors for continue-edge replacement
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test13.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test13.scr
new file mode 100644
index 0000000..46be3d2
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gchoice/Test13.scr
@@ -0,0 +1,30 @@
+module good.efsm.gchoice.Test13;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 1() from B to A;
+ 1() from A to C;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ choice at B
+ {
+ 2() from B to A;
+ 2() from A to C;
+ }
+ }
+ continue X; // EFSM building -- multiple predecessors for continue-edge replacement
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01a.scr
new file mode 100644
index 0000000..6454e8f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01a.scr
@@ -0,0 +1,21 @@
+module good.efsm.gcontinue.choiceunguarded.Test01a;
+
+
+// Cf. -minfsm
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X; // Not "isomorphic" to a single continue X, because non-det not bisim equiv (cf. language equiv) -- so no "syntatic collapse" to single case
+ }
+ or
+ {
+ continue X;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01b.scr
new file mode 100644
index 0000000..e004a28
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01b.scr
@@ -0,0 +1,22 @@
+module good.efsm.gcontinue.choiceunguarded.Test01b;
+
+
+// Cf. -minfsm
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01c.scr
new file mode 100644
index 0000000..ddbbe13
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test01c.scr
@@ -0,0 +1,15 @@
+module good.efsm.gcontinue.choiceunguarded.Test01c;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02a.scr
new file mode 100644
index 0000000..fdd63ed
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02a.scr
@@ -0,0 +1,23 @@
+module good.efsm.gcontinue.choiceunguarded.Test02a;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec Y
+ {
+ 0() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at A // Testing multi-rec enacting states in intermediate continue-edge fixing
+ {
+ continue X;
+ }
+ or
+ {
+ continue Y;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02b.scr
new file mode 100644
index 0000000..22ca333
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02b.scr
@@ -0,0 +1,23 @@
+module good.efsm.gcontinue.choiceunguarded.Test02b;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec Y
+ {
+ 0() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at A // Testing multi-rec enacting states in intermediate continue-edge fixing
+ {
+ continue Y;
+ }
+ or
+ {
+ continue X;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02c.scr
new file mode 100644
index 0000000..a8baec5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02c.scr
@@ -0,0 +1,27 @@
+module good.efsm.gcontinue.choiceunguarded.Test02c;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec Y
+ {
+ 0() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at A // Testing multi-rec enacting states in intermediate continue-edge fixing
+ {
+ continue X;
+ }
+ or
+ {
+ continue X;
+ }
+ or
+ {
+ continue Y;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02d.scr
new file mode 100644
index 0000000..2bc56c1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test02d.scr
@@ -0,0 +1,27 @@
+module good.efsm.gcontinue.choiceunguarded.Test02d;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec Y
+ {
+ 0() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at A // Testing multi-rec enacting states in intermediate continue-edge fixing
+ {
+ continue X;
+ }
+ or
+ {
+ continue Y;
+ }
+ or
+ {
+ continue X;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test03.scr
new file mode 100644
index 0000000..393a754
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test03.scr
@@ -0,0 +1,27 @@
+module good.efsm.gcontinue.choiceunguarded.Test03;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec Y
+ {
+ 0() from A to B;
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X; // Check FSM building
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ or
+ {
+ continue Y;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test04.scr
new file mode 100644
index 0000000..178dfa1
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test04.scr
@@ -0,0 +1,23 @@
+module good.efsm.gcontinue.choiceunguarded.Test04;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 0() from A to B;
+ choice at A
+ {
+ //1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ //continue X;
+ }
+ }
+ 3() from A to B;
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05a.scr
new file mode 100644
index 0000000..5096650
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05a.scr
@@ -0,0 +1,23 @@
+module good.efsm.gcontinue.choiceunguarded.Test05a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from A to B;
+ 2() from B to C;
+ choice at C
+ {
+ 3() from C to A;
+ continue X; // Projected as recursive non-det choice for B
+ }
+ or
+ {
+ 4() from C to A;
+ continue X;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05b.scr
new file mode 100644
index 0000000..d1985ba
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test05b.scr
@@ -0,0 +1,26 @@
+module good.efsm.gcontinue.choiceunguarded.Test05b;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ rec Y
+ {
+ 1() from A to B;
+ 2() from B to C;
+ choice at C
+ {
+ 3() from C to A;
+ continue X;
+ }
+ or
+ {
+ 4() from C to A;
+ continue Y;
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test06.scr
new file mode 100644
index 0000000..9120b36
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test06.scr
@@ -0,0 +1,21 @@
+module good.efsm.gcontinue.choiceungaurded.Test06;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ rec X
+ {
+ 2() from A to B;
+ 3() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07a.scr
new file mode 100644
index 0000000..6a6edba
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07a.scr
@@ -0,0 +1,27 @@
+module good.efsm.gcontinue.choiceungaurded.Test07a;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A // Testing EFSM building rec with multiple "enactings"
+ {
+ 3() from A to B;
+ choice at A
+ {
+ //1() from A to B;
+ continue X; // (and choice-unguarded continue)
+ }
+ or
+ {
+ 6() from A to B;
+ }
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07b.scr
new file mode 100644
index 0000000..09f123b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07b.scr
@@ -0,0 +1,35 @@
+module good.efsm.gcontinue.choiceungaurded.Test07b;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A // Testing EFSM building rec with multiple "enactings"
+ {
+ 3() from A to B;
+ choice at A
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ //2() from A to B;
+ continue X; // (and choice-unguarded continue)
+ }
+ }
+ or
+ {
+ 6() from A to B;
+ }
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07c.scr
new file mode 100644
index 0000000..e07d204
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test07c.scr
@@ -0,0 +1,35 @@
+module good.efsm.gcontinue.choiceungaurded.Test07c;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A // Testing EFSM building rec with multiple "enactings"
+ {
+ 3() from A to B;
+ choice at A
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ 6() from A to B;
+ }
+ }
+ or
+ {
+ 4() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test08.scr
new file mode 100644
index 0000000..dc22bfe
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gcontinue/choiceunguarded/Test08.scr
@@ -0,0 +1,26 @@
+module good.efsm.gcontinue.choiceungaurded.Test08;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at A
+ {
+ continue X;
+ }
+ or
+ {
+ 3() from A to B;
+ }
+ }
+ or
+ {
+ 2() from A to B; // Testing EFSM building: multiple "enacting"
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test01.scr
new file mode 100644
index 0000000..1ffe8e8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test01.scr
@@ -0,0 +1,34 @@
+//$ bin/scribblec.sh -ip modules/core/src/test/scrib/ modules/cli/src/test/resources/good/efsm/gdo/Test01.scr -fsm Bar1 C
+
+module good.efsm.gdo.Test01;
+
+
+// Test inlined protocol unfolding -- unguarded rec and continue under choice
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ do Bar1(A, B);
+ }
+ or
+ {
+ do Bar2(A, B);
+ }
+ or
+ {
+ 4() from A to B;
+ }
+}
+
+global protocol Bar1(role C, role D) // <- As root, check choice inside Foo is unfolded correctly wrt. unguarded continue for Bar1 case
+{
+ 2() from C to D;
+ do Foo(C, D);
+}
+
+global protocol Bar2(role C, role D)
+{
+ 5() from C to D;
+ do Bar2(C, D);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test04.scr
new file mode 100644
index 0000000..5c1c004
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test04.scr
@@ -0,0 +1,24 @@
+module good.efsm.gdo.Test04;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ 2() from A to B;
+ do Bar(A, B);
+ }
+ or
+ {
+ 4() from A to B;
+ }
+}
+
+
+aux global protocol Bar(role A, role B)
+{
+ 3() from B to A;
+ do Foo(B, A); // Roles reversed
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test06.scr
new file mode 100644
index 0000000..68db9cd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test06.scr
@@ -0,0 +1,26 @@
+module good.efsm.gdo.Test06;
+
+
+global protocol Foo(role A, role B)
+{
+ 2() from A to B;
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ do Foo2(A, B); // Direct result is non-minimal EFSM
+ }
+}
+
+global protocol Foo2(role A, role B)
+{
+ do Foo3(A, B);
+}
+
+global protocol Foo3(role A, role B)
+{
+ do Foo(A, B);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test07.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test07.scr
new file mode 100644
index 0000000..331f29f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test07.scr
@@ -0,0 +1,36 @@
+module good.efsm.gdo.Test07;
+
+
+// Similar to good.efsm.gdo.Test01
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ do Bar1(A, B);
+ }
+ or
+ {
+ do Bar2(A, B); // Graph building done on unfolded-inlined (the static unfolding -- here: Bar2 rec is unguarded) // Cf. not the "dynamic" unfolding visiting
+ }
+ or
+ {
+ 5() from A to B;
+ }
+}
+
+// The Bar1 inlined rec for root Foo gets "dropped" by unguarded unfolding because no continue for this rec
+aux global protocol Bar1(role A, role B)
+{
+ 2() from A to B;
+ 3() from B to A;
+ do Foo(A, B);
+}
+
+// For root Foo, Bar2 inlined as unguarded rec, then gets unfolded
+aux global protocol Bar2(role A, role B)
+{
+ 4() from A to B;
+ do Bar2(A, B);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08a.scr
new file mode 100644
index 0000000..555096f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08a.scr
@@ -0,0 +1,24 @@
+module good.efsm.gdo.Test08a;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ 2() from A to B;
+ do Bar(A, B);
+ }
+ or
+ {
+ 1() from A to B;
+ 2() from A to B;
+ }
+}
+
+
+aux global protocol Bar(role A, role B)
+{
+ //3() from A to B;
+ do Foo(A, B);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08b.scr
new file mode 100644
index 0000000..b59bf42
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test08b.scr
@@ -0,0 +1,21 @@
+module good.efsm.gdo.Test08b;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ choice at A
+ {
+ do Bar(A, B);
+ }
+ or
+ {
+ 2() from A to B;
+ }
+}
+
+
+aux global protocol Bar(role A, role B)
+{
+ do Foo(A, B);
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test10.scr
new file mode 100644
index 0000000..7955f57
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test10.scr
@@ -0,0 +1,34 @@
+module good.efsm.gdo.Test10;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ do Proto1Aux1(A, B, C);
+ }
+ or
+ {
+ do Proto1Aux2(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux1(role A, role B, role C)
+{
+ choice at A
+ {
+ 3() from A to B;
+ 3() from A to C;
+ do Proto1Aux1(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux2(role A, role B, role C)
+{
+ choice at A
+ {
+ 2() from A to B;
+ do Proto1Aux1(A, B, C);
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test11.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test11.scr
new file mode 100644
index 0000000..482f910
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test11.scr
@@ -0,0 +1,38 @@
+module good.efsm.gdo.Test11;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 1() from A to B;
+ do Proto1Aux1(A, B, C);
+}
+
+aux global protocol Proto1Aux1(role A, role B, role C)
+{
+ choice at A
+ {
+ 3() from A to B;
+ 3() from B to C;
+ do Proto1Aux2(A, B, C);
+ }
+ or
+ {
+ 4() from A to B;
+ 4() from B to C;
+ }
+}
+
+aux global protocol Proto1Aux2(role A, role B, role C)
+{
+ choice at A
+ {
+ 5() from A to B;
+ do Proto1Aux1(A, B, C); // Testing graph building (C vs. A/B)
+ }
+ or
+ {
+ 6() from A to B;
+ 6() from B to C;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test12.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test12.scr
new file mode 100644
index 0000000..fdb90c6
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test12.scr
@@ -0,0 +1,21 @@
+module good.efsm.gdo.Test12;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ do Proto1Aux1(A, B);
+}
+
+
+aux global protocol Proto1Aux1(role A, role B)
+{
+ choice at A
+ {
+ do Proto1(A, B);
+ }
+ or
+ {
+ 2() from A to B;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13a.scr
new file mode 100644
index 0000000..625d60a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13a.scr
@@ -0,0 +1,33 @@
+module good.efsm.gdo.Test13a;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ choice at B
+ {
+ do Proto1Aux1(A, B);
+ }
+ or
+ {
+ do Proto1Aux2(A, B);
+ }
+ or
+ {
+ 4() from B to A;
+ }
+}
+
+global protocol Proto1Aux1(role A, role B)
+{
+ 2() from B to A;
+ do Proto1(A, B);
+}
+
+aux global protocol Proto1Aux2(role A, role B)
+{
+ 3() from B to A;
+ //do Proto1(A, B);
+ do Proto1Aux2(A, B);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13b.scr
new file mode 100644
index 0000000..5228ddf
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/gdo/Test13b.scr
@@ -0,0 +1,32 @@
+module good.efsm.gdo.Test13b;
+
+
+global protocol Proto1(role A, role B)
+{
+ 1() from A to B;
+ choice at B
+ {
+ do Proto1Aux1(A, B);
+ }
+ or
+ {
+ do Proto1Aux2(A, B);
+ }
+ or
+ {
+ 4() from B to A;
+ }
+}
+
+global protocol Proto1Aux1(role A, role B)
+{
+ 2() from B to A;
+ do Proto1(A, B);
+}
+
+aux global protocol Proto1Aux2(role A, role B)
+{
+ 3() from B to A;
+ do Proto1(A, B);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test03.scr
new file mode 100644
index 0000000..e2b4a23
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test03.scr
@@ -0,0 +1,22 @@
+module good.efsm.grecursion.Test03;
+
+
+global protocol Foo(role A, role B)
+{
+ 0() from A to B;
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to A;
+ continue X;
+ }
+ or
+ {
+ 3() from A to B;
+ }
+ }
+ 4() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test05.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test05.scr
new file mode 100644
index 0000000..be48944
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test05.scr
@@ -0,0 +1,30 @@
+module good.efsm.grecursion.Test05;
+
+
+global protocol Foo(role A, role B)
+{
+ 1() from A to B;
+ 2() from B to A;
+ choice at B
+ {
+ 3() from B to A;
+ }
+ or
+ {
+ 4() from B to A;
+ }
+ rec X
+ {
+ choice at A
+ {
+ 5() from A to B;
+ 6() from B to A;
+ continue X;
+ }
+ or
+ {
+ 6() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test06b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test06b.scr
new file mode 100644
index 0000000..f5584df
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test06b.scr
@@ -0,0 +1,24 @@
+module good.efsm.grecursion.Test06b;
+
+
+global protocol Foo(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ 4() from A to B;
+ rec Y
+ {
+ 3() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test07.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test07.scr
new file mode 100644
index 0000000..70df9b0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test07.scr
@@ -0,0 +1,40 @@
+module good.efsm.grecursion.Test07;
+
+
+global protocol Foo(role C, role S)
+{
+ rec XXX
+ {
+ choice at C
+ {
+ 1() from C to S;
+ }
+ or
+ {
+ LIST() from C to S;
+ choice at S
+ {
+ OK() from S to C;
+ rec summary_choice_list
+ {
+ choice at S
+ {
+ DOT() from S to C;
+ }
+ or
+ {
+ SUM() from S to C;
+ continue summary_choice_list;
+ }
+ }
+ }
+ or
+ {
+ ERR() from S to C;
+ }
+ continue XXX; // Testing graph building (updating predecessor state, when edges removed to be replaced by actual recursion edge) -- continue sequenced after a nested choice-continue: removing prev edge needs to remove pred state, and adding recursion edge should not add pred state
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test08.scr
new file mode 100644
index 0000000..ad96db7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test08.scr
@@ -0,0 +1,23 @@
+module good.efsm.grecursion.Test08;
+
+
+global protocol Foo(role A, role B)
+{
+ rec X
+ {
+ rec Y
+ {
+ choice at A
+ {
+ 1() from A to B;
+ }
+ or
+ {
+ 2() from A to B;
+ continue Y; // Testing graph building (updating predecessor state, when edges removed to be replaced by actual recursion edge) -- continue sequenced after a nested choice-continue: removing prev edge needs to remove pred state, and adding recursion edge should not add pred state
+ }
+ }
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test09.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test09.scr
new file mode 100644
index 0000000..2a51a48
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test09.scr
@@ -0,0 +1,36 @@
+module good.efsm.grecursion.Test09;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ 1() from A to B;
+ 2() from A to C;
+ rec X
+ {
+ choice at A
+ {
+ 3() from A to B;
+ }
+ choice at A
+ {
+ rec Y
+ {
+ choice at A
+ {
+ 3() from A to B;
+ continue X;
+ }
+ or
+ {
+ 4() from A to B;
+ continue Y;
+ }
+ or
+ {
+ 5() from A to B;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test10.scr
new file mode 100644
index 0000000..9cbcfbf
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test10.scr
@@ -0,0 +1,16 @@
+module good.efsm.grecursion.Test10;
+
+
+global protocol Proto1(role A, role B)
+{
+ choice at A
+ {
+ 1() from A to B;
+ rec X // Testing EFSM building, "enabling" stack push/pop for rec inside choice
+ {
+ 2() from B to A;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test11a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test11a.scr
new file mode 100644
index 0000000..f72d61d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test11a.scr
@@ -0,0 +1,26 @@
+module good.efsm.grecursion.Test11a;
+
+
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ choice at B
+ {
+ 2() from B to A;
+ 4() from B to C; // Testing EFSM building
+ continue X;
+ }
+ or
+ {
+ 3() from B to A;
+ 4() from B to C;
+ continue X;
+ }
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test12.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test12.scr
new file mode 100644
index 0000000..55463b8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/Test12.scr
@@ -0,0 +1,12 @@
+module good.efsm.grecursion.Test12;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ continue X; // Test graph building: EndpointGraph null exit (no term)
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test04.scr
new file mode 100644
index 0000000..86671dc
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test04.scr
@@ -0,0 +1,25 @@
+module good.efsm.grecursion.choiceunguarded.Test04;
+
+
+// Same as good.syntax.inlinedunfolding.grecursion.Test04;
+global protocol Proto4(role A, role B)
+{
+ choice at A
+ {
+ rec X
+ {
+ choice at A
+ {
+ 1() from A to B;
+ continue X;
+ }
+ or
+ {
+ 2() from A to B;
+ continue X;
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test06a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test06a.scr
new file mode 100644
index 0000000..fdcafb0
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test06a.scr
@@ -0,0 +1,23 @@
+module good.efsm.grecursion.choiceunguarded.Test06a;
+
+
+global protocol Foo(role A, role B)
+{
+ rec X
+ {
+ 1() from A to B;
+ choice at A
+ {
+ rec Y
+ {
+ 3() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test07.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test07.scr
new file mode 100644
index 0000000..4f5773f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/efsm/grecursion/choiceunguarded/Test07.scr
@@ -0,0 +1,24 @@
+module good.efsm.grecursion.choiceunguarded.Test07;
+
+
+global protocol Proto1(role A, role B)
+{
+ rec X
+ {
+ 0() from A to B;
+ choice at A
+ {
+ rec X // Checking FSM generation -- unfolding of unguarded shadowed choice-rec
+ {
+ 1() from A to B;
+ continue X;
+ }
+ }
+ or
+ {
+ 2() from A to B;
+ }
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test01.scr
new file mode 100644
index 0000000..f590489
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test01.scr
@@ -0,0 +1,23 @@
+module good.liveness.Test01;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ choice at A
+ {
+ 1() from A to B;
+ 2() from B to A;
+ }
+ or
+ {
+ 2() from A to B;
+ 3() from B to A;
+ }
+ rec X // Testing terminal set building
+ {
+ 4() from C to D;
+ continue X;
+ }
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test02.scr
new file mode 100644
index 0000000..46e9add
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/Test02.scr
@@ -0,0 +1,14 @@
+module good.liveness.Test02;
+
+
+global protocol Proto1(role A, role B, role C, role D)
+{
+ rec X // Testing terminal set building
+ {
+ 1() from A to B;
+ 2() from B to A;
+ continue X;
+ }
+ 3() from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test03.scr
new file mode 100644
index 0000000..16e2418
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test03.scr
@@ -0,0 +1,24 @@
+module good.liveness.roleprog.Test03;
+
+
+// Same as good.safety.stuckmsg.threeparty.Test03
+global protocol Proto1(role A, role B, role C)
+{
+ rec X
+ {
+ 1() from A to B;
+ 1() from B to C;
+ choice at B
+ {
+ 2() from B to A;
+ continue X; // No C in this choice case, but fine
+ }
+ or
+ {
+ 3() from B to A;
+ 1() from B to C;
+ continue X;
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test10.scr
new file mode 100644
index 0000000..d31f71b
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/liveness/roleprog/Test10.scr
@@ -0,0 +1,35 @@
+module good.efsm.gdo.Test10;
+
+
+// Duplicate of good.efsm.gdo.Test10
+global protocol Proto1(role A, role B, role C)
+{
+ choice at A
+ {
+ do Proto1Aux1(A, B, C);
+ }
+ or
+ {
+ do Proto1Aux2(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux1(role A, role B, role C)
+{
+ choice at A
+ {
+ 3() from A to B;
+ 3() from A to C;
+ do Proto1Aux1(A, B, C);
+ }
+}
+
+aux global protocol Proto1Aux2(role A, role B, role C)
+{
+ choice at A
+ {
+ 2() from A to B;
+ do Proto1Aux1(A, B, C);
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice01.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice01.scr
new file mode 100644
index 0000000..96c3fa9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice01.scr
@@ -0,0 +1,15 @@
+module good.misc.globals.gchoice.Choice01;
+
+// FIXME: not really about parsing, refactor
+global protocol Choice1(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ //l1() from A to B; // Uncomment is bad
+ l2() from A to B;
+ }
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice02.scr
new file mode 100644
index 0000000..ff486bd
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice02.scr
@@ -0,0 +1,11 @@
+module good.misc.globals.gchoice.Choice02;
+
+global protocol Choice2(role A, role B)
+{
+ choice at A
+ {
+ //l1() from B to A; // Uncomment is bad
+ l1() from A to B;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice03.scr
new file mode 100644
index 0000000..2cdac27
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice03.scr
@@ -0,0 +1,15 @@
+module good.misc.globals.gchoice.Choice03;
+
+global protocol Choice3(role A, role B)
+{
+ l1() from A to B;
+ choice at B
+ {
+ l1() from B to A;
+ }
+ or
+ {
+ l2() from B to A;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice04.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice04.scr
new file mode 100644
index 0000000..c8881e5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice04.scr
@@ -0,0 +1,17 @@
+module good.misc.globals.gchoice.Choice04;
+
+global protocol Choice4(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A;
+ l2() from A to B;
+ }
+ or
+ {
+ l2() from B to A;
+ l1() from A to B;
+ l2() from A to B; // same label OK because B is the subject (TODO: should be generalised for all roles already enabled)
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05a.scr
new file mode 100644
index 0000000..ff42e36
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05a.scr
@@ -0,0 +1,32 @@
+module good.misc.globals.gchoice.Choice05a;
+
+
+global protocol Choice5a(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ }
+ or
+ {
+ l3() from B to A;
+ //l2() from B to A; // Uncomment OK unless l3() is commented
+ //l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05b.scr
new file mode 100644
index 0000000..1a683ff
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05b.scr
@@ -0,0 +1,32 @@
+module good.misc.globals.gchoice.Choice05b;
+
+
+global protocol Choice5b(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ }
+ or
+ {
+ l3() from B to A;
+ //l2() from B to A; // Uncomment OK unless l3() is commented
+ //l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05c.scr
new file mode 100644
index 0000000..5b11c1d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice05c.scr
@@ -0,0 +1,32 @@
+module good.misc.globals.gchoice.Choice05c;
+
+
+global protocol Choice5c(role A, role B)
+{
+ choice at B
+ {
+ l1() from B to A; // Comment is bad
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ }
+ or
+ {
+ choice at B
+ {
+ l2() from B to A;
+ }
+ or
+ {
+ l3() from B to A;
+ //l2() from B to A; // Uncomment OK unless l3() is commented
+ //l1() from B to A; // Uncomment OK unless l3() is commented
+ }
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice06.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice06.scr
new file mode 100644
index 0000000..604751a
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice06.scr
@@ -0,0 +1,27 @@
+module good.misc.globals.gchoice.Choice06;
+
+
+global protocol Choice6(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ () from A to B;
+ }
+ l1() from A to B;
+ () from A to B;
+ choice at A
+ {
+ l1() from A to B; // Comment is bad
+ () from A to B;
+ }
+ or
+ {
+ () from A to B;
+ }
+ 2() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice08.scr
new file mode 100644
index 0000000..d0c2ae8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gchoice/Choice08.scr
@@ -0,0 +1,19 @@
+module good.misc.globals.gchoice.Choice08;
+
+
+global protocol Choice8(role A, role B, role C)
+{
+ choice at A
+ {
+ l1() from A to B;
+ l2() from B to C;
+ }
+ or
+ {
+ l2() from A to B;
+ l1() from B to C; // Comment is bad
+ l2() from B to C;
+ }
+ l2() from A to C;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01a.scr
new file mode 100644
index 0000000..9f34e74
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01a.scr
@@ -0,0 +1,17 @@
+module good.misc.globals.gdo.Do01a;
+
+
+global protocol Do1a(role A, role B)
+{
+ do Do1aux(A, B);
+ //do good.misc.globals.gdo.Do01a.Do1aux(A, B); // Uncomment is OK
+ //do X: Do1aux(A, B); // Uncomment is OK // TODO
+ //do Do1aux(A, B); // Uncomment is bad
+ //do Do1aux(A, A); // Uncomment is bad
+}
+
+global protocol Do1aux(role C, role D)
+{
+ l1() from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01b.scr
new file mode 100644
index 0000000..c0a423d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do01b.scr
@@ -0,0 +1,17 @@
+module good.misc.globals.gdo.Do01b;
+
+
+global protocol Do1b(role A, role B)
+{
+ do Do1aux(A, B);
+ do good.misc.globals.gdo.Do01b.Do1aux(A, B); // Uncomment is OK
+ //do X: Do1aux(A, B); // Uncomment is OK // TODO
+ //do Do1aux(A, B); // Uncomment is bad
+ //do Do1aux(A, A); // Uncomment is bad
+}
+
+global protocol Do1aux(role C, role D)
+{
+ l1() from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do02.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do02.scr
new file mode 100644
index 0000000..8bdf16d
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do02.scr
@@ -0,0 +1,13 @@
+module good.misc.globals.gdo.Do02;
+
+
+global protocol Do2(role A, role B)
+{
+ do Do2a(B, A);
+}
+
+global protocol Do2a(role A, role B)
+{
+ l1() from A to B;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do03.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do03.scr
new file mode 100644
index 0000000..a64f104
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do03.scr
@@ -0,0 +1,20 @@
+module good.misc.globals.gdo.Do03;
+
+
+global protocol Do3(role A, role B)
+{
+ l1() from A to B;
+ do Do3a(A, B);
+}
+
+global protocol Do3a(role C, role D)
+{
+ l2() from C to D;
+ do Do3b(C, D);
+}
+
+global protocol Do3b(role E, role F)
+{
+ //l3() from A to B; // Uncomment is bad
+ l3() from E to F;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do04a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do04a.scr
new file mode 100644
index 0000000..812f03c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do04a.scr
@@ -0,0 +1,27 @@
+module good.misc.globals.gdo.Do04a;
+
+
+global protocol Do4a(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ do Do4_1(A, B);
+ //do X: Do4_1(A as C, B as D); // Uncomment is OK // TODO
+ }
+}
+
+global protocol Do4_1(role C, role D)
+{
+ do Do4_2(C, D);
+}
+
+global protocol Do4_2(role E, role F)
+{
+ //l1() from E to F; // Uncomment OK for the X-do only, not the unscoped one
+ l2() from E to F;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do05.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do05.scr
new file mode 100644
index 0000000..392efbb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do05.scr
@@ -0,0 +1,20 @@
+module good.misc.globals.gdo.Do05;
+
+
+global protocol Do5(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ }
+ or
+ {
+ do Do5a(A, B); // Comment is bad
+ l3() from B to A;
+ }
+}
+
+global protocol Do5a(role C, role D)
+{
+ l2() from C to D;
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06a.scr
new file mode 100644
index 0000000..95623ca
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06a.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do06a;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+global protocol Do6a(role A, role B)
+{
+ do Do6_1(A, B);
+ //do Do6_1(A, B); // Uncomment is OK
+ //do Do6_1<(String)>(A, B); // Uncomment is OK
+ //do Do6_1<()>(A, B); // Uncomment is OK
+ //do Do6_1(A, B); // Uncomment is bad
+ //do Do6_1(A, B); // Uncomment is bad
+}
+
+global protocol Do6_1(role C, role D)
+//global protocol Do6_1(role C, role D) // Makes all above bad
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06b.scr
new file mode 100644
index 0000000..44366f7
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06b.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do06b;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+global protocol Do6b(role A, role B)
+{
+ do Do6_1(A, B);
+ do Do6_1(A, B); // Uncomment is OK
+ //do Do6_1<(String)>(A, B); // Uncomment is OK
+ //do Do6_1<()>(A, B); // Uncomment is OK
+ //do Do6_1(A, B); // Uncomment is bad
+ //do Do6_1(A, B); // Uncomment is bad
+}
+
+global protocol Do6_1(role C, role D)
+//global protocol Do6_1(role C, role D) // Makes all above bad
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06c.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06c.scr
new file mode 100644
index 0000000..9ea22f5
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06c.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do06c;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+global protocol Do6c(role A, role B)
+{
+ do Do6_1(A, B);
+ do Do6_1(A, B); // Uncomment is OK
+ do Do6_1<(String)>(A, B); // Uncomment is OK
+ //do Do6_1<()>(A, B); // Uncomment is OK
+ //do Do6_1(A, B); // Uncomment is bad
+ //do Do6_1(A, B); // Uncomment is bad
+}
+
+global protocol Do6_1(role C, role D)
+//global protocol Do6_1(role C, role D) // Makes all above bad
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06d.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06d.scr
new file mode 100644
index 0000000..310e031
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do06d.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do06d;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+global protocol Do6d(role A, role B)
+{
+ do Do6_1(A, B);
+ do Do6_1(A, B); // Uncomment is OK
+ do Do6_1<(String)>(A, B); // Uncomment is OK
+ do Do6_1<()>(A, B); // Uncomment is OK
+ //do Do6_1(A, B); // Uncomment is bad
+ //do Do6_1(A, B); // Uncomment is bad
+}
+
+global protocol Do6_1(role C, role D)
+//global protocol Do6_1(role C, role D) // Makes all above bad
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do07.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do07.scr
new file mode 100644
index 0000000..13f7e07
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do07.scr
@@ -0,0 +1,16 @@
+module good.misc.globals.gdo.Do07;
+
+
+type "MyExternalType" from "MyExternalType.blah" as MyType;
+
+
+global protocol Do7(role A, role B)
+{
+ do Do7a(A, B);
+}
+
+global protocol Do7a(role C, role D)
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do08.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do08.scr
new file mode 100644
index 0000000..4fd171c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do08.scr
@@ -0,0 +1,14 @@
+module good.misc.globals.gdo.Do08;
+
+
+global protocol Do8(role A, role B)
+{
+ do Do8Aux(A, B);
+ do Do8Aux(B, A);
+}
+
+global protocol Do8Aux(role C, role D)
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do09.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do09.scr
new file mode 100644
index 0000000..a3c5cee
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do09.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do09;
+
+
+global protocol Do9(role A, role B)
+{
+ choice at A
+ {
+ do Do9Aux(A, B);
+ }
+ or
+ {
+ do Do9Aux(A, B);
+ //do Do9Aux(B, A); // Uncomment bad if the first line in this block is commented
+ //do Do9Aux(A, B); // Uncomment bad if the first line in this block is commented
+ }
+}
+
+global protocol Do9Aux(role C, role D)
+{
+ X from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do10.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do10.scr
new file mode 100644
index 0000000..08c294f
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do10.scr
@@ -0,0 +1,15 @@
+module good.misc.globals.gdo.Do10;
+
+
+global protocol Do10(role A, role B)
+{
+ do Do10a(A, B);
+ //do Do10a(A, B); // Uncomment is bad
+}
+
+global protocol Do10a(role C, role D)
+{
+ X from C to D;
+ //Y from C to D; // Uncomment is bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do11.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do11.scr
new file mode 100644
index 0000000..bd789a8
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do11.scr
@@ -0,0 +1,25 @@
+module good.misc.globals.gdo.Do11;
+
+
+global protocol Do11(role A, role B)
+{
+ /*choice at A // Uncomment (with below) is bad
+ {*/
+ do Do11Aux1(A, B);
+ /*}
+ or
+ {
+ l1() from A to B;
+ }*/
+}
+
+global protocol Do11Aux1(role C, role D)
+{
+ do Do11Aux2(C, D);
+}
+
+global protocol Do11Aux2(role E, role F)
+{
+ X from E to F;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do12a.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do12a.scr
new file mode 100644
index 0000000..6a00ed9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do12a.scr
@@ -0,0 +1,23 @@
+module good.misc.globals.gdo.Do12a;
+
+
+global protocol Do12a(role A, role B)
+{
+ choice at A
+ {
+ do Do12_1(A, B);
+ }
+ or
+ {
+ //do Do12_1(A, B); // Uncomment is bad
+ //do X: Do12_1(A, B); // Uncomment is OK
+ do Do12_1(A, B);
+ }
+}
+
+global protocol Do12_1(role C, role D)
+{
+ X from C to D;
+}
+
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do13b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do13b.scr
new file mode 100644
index 0000000..9316afb
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do13b.scr
@@ -0,0 +1,9 @@
+module good.misc.globals.gdo.Do13b;
+
+
+global protocol Do13b(role A, role B)
+{
+ l1() from A to B; // Uncomment is OK
+ do Do13b(A, B);
+ //l1() from A to B; // Uncomment is bad
+}
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do14.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do14.scr
new file mode 100644
index 0000000..bbf7a9c
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do14.scr
@@ -0,0 +1,22 @@
+module good.misc.globals.gdo.Do14;
+
+
+global protocol Do14(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ do Do14a(A, B);
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ //l1() from A to B; // Uncomment is bad
+}
+
+global protocol Do14a(role C, role D)
+{
+ do Do14(D, C);
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do15.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do15.scr
new file mode 100644
index 0000000..12b3ea4
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do15.scr
@@ -0,0 +1,17 @@
+module good.misc.globals.gdo.Do15;
+
+
+global protocol Do15(role A, role B)
+{
+ choice at A
+ {
+ l1() from A to B;
+ do Do15(A, B);
+ }
+ or
+ {
+ l2() from A to B;
+ }
+ //l3() from A to B; // Uncomment is bad
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do17.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do17.scr
new file mode 100644
index 0000000..bbfec24
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do17.scr
@@ -0,0 +1,21 @@
+module good.misc.globals.gdo.Do17;
+
+
+global protocol Do17(role A, role B)
+{
+ do Do17a(A, B);
+ //do Do17a(A, B); // Uncomment is bad
+}
+
+global protocol Do17a(role C, role D)
+{
+ choice at C
+ {
+ X from C to D;
+ }
+ or
+ {
+ Y from C to D;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do19.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do19.scr
new file mode 100644
index 0000000..8885481
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do19.scr
@@ -0,0 +1,18 @@
+module good.misc.globals.gdo.Do19;
+
+
+global protocol Do19(role A, role B)
+{
+ do Do19a(A, B);
+ //l1() from A to B; // Uncomment is bad
+}
+
+global protocol Do19a(role C, role D)
+{
+ rec X
+ {
+ l1() from C to D;
+ continue X;
+ }
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do21.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do21.scr
new file mode 100644
index 0000000..37e9b42
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do21.scr
@@ -0,0 +1,18 @@
+module good.misc.globals.gdo.Do21;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+global protocol Do6(role A, role B)
+{
+ do Do6a(A, B);
+ //do Do6a(A, B); // Uncomment is bad
+}
+
+global protocol Do6a(role C, role D)
+//global protocol Do6a(role C, role D)
+{
+ (X) from C to D;
+}
+
diff --git a/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do23b.scr b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do23b.scr
new file mode 100644
index 0000000..edbf5f9
--- /dev/null
+++ b/src/main/jbake/assets/docs/scribble/modules/cli/src/test/resources/good/misc/globals/gdo/Do23b.scr
@@ -0,0 +1,23 @@
+module good.misc.globals.gdo.Do23b;
+
+
+type "java.lang.String" from "rt.jar" as String;
+
+
+// Overlaps with Do25
+global protocol Do23b(role A, role B)
+{
+ do Do23aux