"!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>$2>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("")).appendTo(b.documentElement),b=(Cb[0].contentWindow||Cb[0].contentDocument).document,b.write(),b.close(),c=Eb(a,b),Cb.detach()),Db[a]=c),c}!function(){var a;k.shrinkWrapBlocks=function(){if(null!=a)return a;a=!1;var b,c,d;return c=y.getElementsByTagName("body")[0],c&&c.style?(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:1px;width:1px;zoom:1",b.appendChild(y.createElement("div")).style.width="5px",a=3!==b.offsetWidth),c.removeChild(d),a):void 0}}();var Gb=/^margin/,Hb=new RegExp("^("+S+")(?!px)[a-z%]+$","i"),Ib,Jb,Kb=/^(top|right|bottom|left)$/;a.getComputedStyle?(Ib=function(a){return a.ownerDocument.defaultView.getComputedStyle(a,null)},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c.getPropertyValue(b)||c[b]:void 0,c&&(""!==g||m.contains(a.ownerDocument,a)||(g=m.style(a,b)),Hb.test(g)&&Gb.test(b)&&(d=h.width,e=h.minWidth,f=h.maxWidth,h.minWidth=h.maxWidth=h.width=g,g=c.width,h.width=d,h.minWidth=e,h.maxWidth=f)),void 0===g?g:g+""}):y.documentElement.currentStyle&&(Ib=function(a){return a.currentStyle},Jb=function(a,b,c){var d,e,f,g,h=a.style;return c=c||Ib(a),g=c?c[b]:void 0,null==g&&h&&h[b]&&(g=h[b]),Hb.test(g)&&!Kb.test(b)&&(d=h.left,e=a.runtimeStyle,f=e&&e.left,f&&(e.left=a.currentStyle.left),h.left="fontSize"===b?"1em":g,g=h.pixelLeft+"px",h.left=d,f&&(e.left=f)),void 0===g?g:g+""||"auto"});function Lb(a,b){return{get:function(){var c=a();if(null!=c)return c?void delete this.get:(this.get=b).apply(this,arguments)}}}!function(){var b,c,d,e,f,g,h;if(b=y.createElement("div"),b.innerHTML=" a ",d=b.getElementsByTagName("a")[0],c=d&&d.style){c.cssText="float:left;opacity:.5",k.opacity="0.5"===c.opacity,k.cssFloat=!!c.cssFloat,b.style.backgroundClip="content-box",b.cloneNode(!0).style.backgroundClip="",k.clearCloneStyle="content-box"===b.style.backgroundClip,k.boxSizing=""===c.boxSizing||""===c.MozBoxSizing||""===c.WebkitBoxSizing,m.extend(k,{reliableHiddenOffsets:function(){return null==g&&i(),g},boxSizingReliable:function(){return null==f&&i(),f},pixelPosition:function(){return null==e&&i(),e},reliableMarginRight:function(){return null==h&&i(),h}});function i(){var b,c,d,i;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),b.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",e=f=!1,h=!0,a.getComputedStyle&&(e="1%"!==(a.getComputedStyle(b,null)||{}).top,f="4px"===(a.getComputedStyle(b,null)||{width:"4px"}).width,i=b.appendChild(y.createElement("div")),i.style.cssText=b.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",i.style.marginRight=i.style.width="0",b.style.width="1px",h=!parseFloat((a.getComputedStyle(i,null)||{}).marginRight)),b.innerHTML="",i=b.getElementsByTagName("td"),i[0].style.cssText="margin:0;border:0;padding:0;display:none",g=0===i[0].offsetHeight,g&&(i[0].style.display="",i[1].style.display="none",g=0===i[0].offsetHeight),c.removeChild(d))}}}(),m.swap=function(a,b,c,d){var e,f,g={};for(f in b)g[f]=a.style[f],a.style[f]=b[f];e=c.apply(a,d||[]);for(f in b)a.style[f]=g[f];return e};var Mb=/alpha\([^)]*\)/i,Nb=/opacity\s*=\s*([^)]*)/,Ob=/^(none|table(?!-c[ea]).+)/,Pb=new RegExp("^("+S+")(.*)$","i"),Qb=new RegExp("^([+-])=("+S+")","i"),Rb={position:"absolute",visibility:"hidden",display:"block"},Sb={letterSpacing:"0",fontWeight:"400"},Tb=["Webkit","O","Moz","ms"];function Ub(a,b){if(b in a)return b;var c=b.charAt(0).toUpperCase()+b.slice(1),d=b,e=Tb.length;while(e--)if(b=Tb[e]+c,b in a)return b;return d}function Vb(a,b){for(var c,d,e,f=[],g=0,h=a.length;h>g;g++)d=a[g],d.style&&(f[g]=m._data(d,"olddisplay"),c=d.style.display,b?(f[g]||"none"!==c||(d.style.display=""),""===d.style.display&&U(d)&&(f[g]=m._data(d,"olddisplay",Fb(d.nodeName)))):(e=U(d),(c&&"none"!==c||!e)&&m._data(d,"olddisplay",e?c:m.css(d,"display"))));for(g=0;h>g;g++)d=a[g],d.style&&(b&&"none"!==d.style.display&&""!==d.style.display||(d.style.display=b?f[g]||"":"none"));return a}function Wb(a,b,c){var d=Pb.exec(b);return d?Math.max(0,d[1]-(c||0))+(d[2]||"px"):b}function Xb(a,b,c,d,e){for(var f=c===(d?"border":"content")?4:"width"===b?1:0,g=0;4>f;f+=2)"margin"===c&&(g+=m.css(a,c+T[f],!0,e)),d?("content"===c&&(g-=m.css(a,"padding"+T[f],!0,e)),"margin"!==c&&(g-=m.css(a,"border"+T[f]+"Width",!0,e))):(g+=m.css(a,"padding"+T[f],!0,e),"padding"!==c&&(g+=m.css(a,"border"+T[f]+"Width",!0,e)));return g}function Yb(a,b,c){var d=!0,e="width"===b?a.offsetWidth:a.offsetHeight,f=Ib(a),g=k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,f);if(0>=e||null==e){if(e=Jb(a,b,f),(0>e||null==e)&&(e=a.style[b]),Hb.test(e))return e;d=g&&(k.boxSizingReliable()||e===a.style[b]),e=parseFloat(e)||0}return e+Xb(a,b,c||(g?"border":"content"),d,f)+"px"}m.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=Jb(a,"opacity");return""===c?"1":c}}}},cssNumber:{columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":k.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,c,d){if(a&&3!==a.nodeType&&8!==a.nodeType&&a.style){var e,f,g,h=m.camelCase(b),i=a.style;if(b=m.cssProps[h]||(m.cssProps[h]=Ub(i,h)),g=m.cssHooks[b]||m.cssHooks[h],void 0===c)return g&&"get"in g&&void 0!==(e=g.get(a,!1,d))?e:i[b];if(f=typeof c,"string"===f&&(e=Qb.exec(c))&&(c=(e[1]+1)*e[2]+parseFloat(m.css(a,b)),f="number"),null!=c&&c===c&&("number"!==f||m.cssNumber[h]||(c+="px"),k.clearCloneStyle||""!==c||0!==b.indexOf("background")||(i[b]="inherit"),!(g&&"set"in g&&void 0===(c=g.set(a,c,d)))))try{i[b]=c}catch(j){}}},css:function(a,b,c,d){var e,f,g,h=m.camelCase(b);return b=m.cssProps[h]||(m.cssProps[h]=Ub(a.style,h)),g=m.cssHooks[b]||m.cssHooks[h],g&&"get"in g&&(f=g.get(a,!0,c)),void 0===f&&(f=Jb(a,b,d)),"normal"===f&&b in Sb&&(f=Sb[b]),""===c||c?(e=parseFloat(f),c===!0||m.isNumeric(e)?e||0:f):f}}),m.each(["height","width"],function(a,b){m.cssHooks[b]={get:function(a,c,d){return c?Ob.test(m.css(a,"display"))&&0===a.offsetWidth?m.swap(a,Rb,function(){return Yb(a,b,d)}):Yb(a,b,d):void 0},set:function(a,c,d){var e=d&&Ib(a);return Wb(a,c,d?Xb(a,b,d,k.boxSizing&&"border-box"===m.css(a,"boxSizing",!1,e),e):0)}}}),k.opacity||(m.cssHooks.opacity={get:function(a,b){return Nb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=m.isNumeric(b)?"alpha(opacity="+100*b+")":"",f=d&&d.filter||c.filter||"";c.zoom=1,(b>=1||""===b)&&""===m.trim(f.replace(Mb,""))&&c.removeAttribute&&(c.removeAttribute("filter"),""===b||d&&!d.filter)||(c.filter=Mb.test(f)?f.replace(Mb,e):f+" "+e)}}),m.cssHooks.marginRight=Lb(k.reliableMarginRight,function(a,b){return b?m.swap(a,{display:"inline-block"},Jb,[a,"marginRight"]):void 0}),m.each({margin:"",padding:"",border:"Width"},function(a,b){m.cssHooks[a+b]={expand:function(c){for(var d=0,e={},f="string"==typeof c?c.split(" "):[c];4>d;d++)e[a+T[d]+b]=f[d]||f[d-2]||f[0];return e}},Gb.test(a)||(m.cssHooks[a+b].set=Wb)}),m.fn.extend({css:function(a,b){return V(this,function(a,b,c){var d,e,f={},g=0;if(m.isArray(b)){for(d=Ib(a),e=b.length;e>g;g++)f[b[g]]=m.css(a,b[g],!1,d);return f}return void 0!==c?m.style(a,b,c):m.css(a,b)},a,b,arguments.length>1)},show:function(){return Vb(this,!0)},hide:function(){return Vb(this)},toggle:function(a){return"boolean"==typeof a?a?this.show():this.hide():this.each(function(){U(this)?m(this).show():m(this).hide()})}});function Zb(a,b,c,d,e){return new Zb.prototype.init(a,b,c,d,e)}m.Tween=Zb,Zb.prototype={constructor:Zb,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")
-},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fx.off?0:"number"==typeof d.duration?d.duration:d.duration in m.fx.speeds?m.fx.speeds[d.duration]:m.fx.speeds._default,(null==d.queue||d.queue===!0)&&(d.queue="fx"),d.old=d.complete,d.complete=function(){m.isFunction(d.old)&&d.old.call(this),d.queue&&m.dequeue(this,d.queue)},d},m.fn.extend({fadeTo:function(a,b,c,d){return this.filter(U).css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,d){var e=m.isEmptyObject(a),f=m.speed(b,c,d),g=function(){var b=kc(this,m.extend({},a),f);(e||m._data(this,"finish"))&&b.stop(!0)};return g.finish=g,e||f.queue===!1?this.each(g):this.queue(f.queue,g)},stop:function(a,b,c){var d=function(a){var b=a.stop;delete a.stop,b(c)};return"string"!=typeof a&&(c=b,b=a,a=void 0),b&&a!==!1&&this.queue(a||"fx",[]),this.each(function(){var b=!0,e=null!=a&&a+"queueHooks",f=m.timers,g=m._data(this);if(e)g[e]&&g[e].stop&&d(g[e]);else for(e in g)g[e]&&g[e].stop&&cc.test(e)&&d(g[e]);for(e=f.length;e--;)f[e].elem!==this||null!=a&&f[e].queue!==a||(f[e].anim.stop(c),b=!1,f.splice(e,1));(b||!c)&&m.dequeue(this,a)})},finish:function(a){return a!==!1&&(a=a||"fx"),this.each(function(){var b,c=m._data(this),d=c[a+"queue"],e=c[a+"queueHooks"],f=m.timers,g=d?d.length:0;for(c.finish=!0,m.queue(this,a,[]),e&&e.stop&&e.stop.call(this,!0),b=f.length;b--;)f[b].elem===this&&f[b].queue===a&&(f[b].anim.stop(!0),f.splice(b,1));for(b=0;g>b;b++)d[b]&&d[b].finish&&d[b].finish.call(this);delete c.finish})}}),m.each(["toggle","show","hide"],function(a,b){var c=m.fn[b];m.fn[b]=function(a,d,e){return null==a||"boolean"==typeof a?c.apply(this,arguments):this.animate(gc(b,!0),a,d,e)}}),m.each({slideDown:gc("show"),slideUp:gc("hide"),slideToggle:gc("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){m.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),m.timers=[],m.fx.tick=function(){var a,b=m.timers,c=0;for($b=m.now();ca ",d=b.getElementsByTagName("a")[0],c=y.createElement("select"),e=c.appendChild(y.createElement("option")),a=b.getElementsByTagName("input")[0],d.style.cssText="top:1px",k.getSetAttribute="t"!==b.className,k.style=/top/.test(d.getAttribute("style")),k.hrefNormalized="/a"===d.getAttribute("href"),k.checkOn=!!a.value,k.optSelected=e.selected,k.enctype=!!y.createElement("form").enctype,c.disabled=!0,k.optDisabled=!e.disabled,a=y.createElement("input"),a.setAttribute("value",""),k.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),k.radioValue="t"===a.value}();var lc=/\r/g;m.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=m.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,m(this).val()):a,null==e?e="":"number"==typeof e?e+="":m.isArray(e)&&(e=m.map(e,function(a){return null==a?"":a+""})),b=m.valHooks[this.type]||m.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=m.valHooks[e.type]||m.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(lc,""):null==c?"":c)}}}),m.extend({valHooks:{option:{get:function(a){var b=m.find.attr(a,"value");return null!=b?b:m.trim(m.text(a))}},select:{get:function(a){for(var b,c,d=a.options,e=a.selectedIndex,f="select-one"===a.type||0>e,g=f?null:[],h=f?e+1:d.length,i=0>e?h:f?e:0;h>i;i++)if(c=d[i],!(!c.selected&&i!==e||(k.optDisabled?c.disabled:null!==c.getAttribute("disabled"))||c.parentNode.disabled&&m.nodeName(c.parentNode,"optgroup"))){if(b=m(c).val(),f)return b;g.push(b)}return g},set:function(a,b){var c,d,e=a.options,f=m.makeArray(b),g=e.length;while(g--)if(d=e[g],m.inArray(m.valHooks.option.get(d),f)>=0)try{d.selected=c=!0}catch(h){d.scrollHeight}else d.selected=!1;return c||(a.selectedIndex=-1),e}}}}),m.each(["radio","checkbox"],function(){m.valHooks[this]={set:function(a,b){return m.isArray(b)?a.checked=m.inArray(m(a).val(),b)>=0:void 0}},k.checkOn||(m.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var mc,nc,oc=m.expr.attrHandle,pc=/^(?:checked|selected)$/i,qc=k.getSetAttribute,rc=k.input;m.fn.extend({attr:function(a,b){return V(this,m.attr,a,b,arguments.length>1)},removeAttr:function(a){return this.each(function(){m.removeAttr(this,a)})}}),m.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(a&&3!==f&&8!==f&&2!==f)return typeof a.getAttribute===K?m.prop(a,b,c):(1===f&&m.isXMLDoc(a)||(b=b.toLowerCase(),d=m.attrHooks[b]||(m.expr.match.bool.test(b)?nc:mc)),void 0===c?d&&"get"in d&&null!==(e=d.get(a,b))?e:(e=m.find.attr(a,b),null==e?void 0:e):null!==c?d&&"set"in d&&void 0!==(e=d.set(a,c,b))?e:(a.setAttribute(b,c+""),c):void m.removeAttr(a,b))},removeAttr:function(a,b){var c,d,e=0,f=b&&b.match(E);if(f&&1===a.nodeType)while(c=f[e++])d=m.propFix[c]||c,m.expr.match.bool.test(c)?rc&&qc||!pc.test(c)?a[d]=!1:a[m.camelCase("default-"+c)]=a[d]=!1:m.attr(a,c,""),a.removeAttribute(qc?c:d)},attrHooks:{type:{set:function(a,b){if(!k.radioValue&&"radio"===b&&m.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}}}),nc={set:function(a,b,c){return b===!1?m.removeAttr(a,c):rc&&qc||!pc.test(c)?a.setAttribute(!qc&&m.propFix[c]||c,c):a[m.camelCase("default-"+c)]=a[c]=!0,c}},m.each(m.expr.match.bool.source.match(/\w+/g),function(a,b){var c=oc[b]||m.find.attr;oc[b]=rc&&qc||!pc.test(b)?function(a,b,d){var e,f;return d||(f=oc[b],oc[b]=e,e=null!=c(a,b,d)?b.toLowerCase():null,oc[b]=f),e}:function(a,b,c){return c?void 0:a[m.camelCase("default-"+b)]?b.toLowerCase():null}}),rc&&qc||(m.attrHooks.value={set:function(a,b,c){return m.nodeName(a,"input")?void(a.defaultValue=b):mc&&mc.set(a,b,c)}}),qc||(mc={set:function(a,b,c){var d=a.getAttributeNode(c);return d||a.setAttributeNode(d=a.ownerDocument.createAttribute(c)),d.value=b+="","value"===c||b===a.getAttribute(c)?b:void 0}},oc.id=oc.name=oc.coords=function(a,b,c){var d;return c?void 0:(d=a.getAttributeNode(b))&&""!==d.value?d.value:null},m.valHooks.button={get:function(a,b){var c=a.getAttributeNode(b);return c&&c.specified?c.value:void 0},set:mc.set},m.attrHooks.contenteditable={set:function(a,b,c){mc.set(a,""===b?!1:b,c)}},m.each(["width","height"],function(a,b){m.attrHooks[b]={set:function(a,c){return""===c?(a.setAttribute(b,"auto"),c):void 0}}})),k.style||(m.attrHooks.style={get:function(a){return a.style.cssText||void 0},set:function(a,b){return a.style.cssText=b+""}});var sc=/^(?:input|select|textarea|button|object)$/i,tc=/^(?:a|area)$/i;m.fn.extend({prop:function(a,b){return V(this,m.prop,a,b,arguments.length>1)},removeProp:function(a){return a=m.propFix[a]||a,this.each(function(){try{this[a]=void 0,delete this[a]}catch(b){}})}}),m.extend({propFix:{"for":"htmlFor","class":"className"},prop:function(a,b,c){var d,e,f,g=a.nodeType;if(a&&3!==g&&8!==g&&2!==g)return f=1!==g||!m.isXMLDoc(a),f&&(b=m.propFix[b]||b,e=m.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=m.find.attr(a,"tabindex");return b?parseInt(b,10):sc.test(a.nodeName)||tc.test(a.nodeName)&&a.href?0:-1}}}}),k.hrefNormalized||m.each(["href","src"],function(a,b){m.propHooks[b]={get:function(a){return a.getAttribute(b,4)}}}),k.optSelected||(m.propHooks.selected={get:function(a){var b=a.parentNode;return b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex),null}}),m.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){m.propFix[this.toLowerCase()]=this}),k.enctype||(m.propFix.enctype="encoding");var uc=/[\t\r\n\f]/g;m.fn.extend({addClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j="string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).addClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):" ")){f=0;while(e=b[f++])d.indexOf(" "+e+" ")<0&&(d+=e+" ");g=m.trim(d),c.className!==g&&(c.className=g)}return this},removeClass:function(a){var b,c,d,e,f,g,h=0,i=this.length,j=0===arguments.length||"string"==typeof a&&a;if(m.isFunction(a))return this.each(function(b){m(this).removeClass(a.call(this,b,this.className))});if(j)for(b=(a||"").match(E)||[];i>h;h++)if(c=this[h],d=1===c.nodeType&&(c.className?(" "+c.className+" ").replace(uc," "):"")){f=0;while(e=b[f++])while(d.indexOf(" "+e+" ")>=0)d=d.replace(" "+e+" "," ");g=a?m.trim(d):"",c.className!==g&&(c.className=g)}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):this.each(m.isFunction(a)?function(c){m(this).toggleClass(a.call(this,c,this.className,b),b)}:function(){if("string"===c){var b,d=0,e=m(this),f=a.match(E)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else(c===K||"boolean"===c)&&(this.className&&m._data(this,"__className__",this.className),this.className=this.className||a===!1?"":m._data(this,"__className__")||"")})},hasClass:function(a){for(var b=" "+a+" ",c=0,d=this.length;d>c;c++)if(1===this[c].nodeType&&(" "+this[c].className+" ").replace(uc," ").indexOf(b)>=0)return!0;return!1}}),m.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(a,b){m.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),m.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)},bind:function(a,b,c){return this.on(a,null,b,c)},unbind:function(a,b){return this.off(a,null,b)},delegate:function(a,b,c,d){return this.on(b,a,c,d)},undelegate:function(a,b,c){return 1===arguments.length?this.off(a,"**"):this.off(b,a||"**",c)}});var vc=m.now(),wc=/\?/,xc=/(,)|(\[|{)|(}|])|"(?:[^"\\\r\n]|\\["\\\/bfnrt]|\\u[\da-fA-F]{4})*"\s*:?|true|false|null|-?(?!0\d)\d+(?:\.\d+|)(?:[eE][+-]?\d+|)/g;m.parseJSON=function(b){if(a.JSON&&a.JSON.parse)return a.JSON.parse(b+"");var c,d=null,e=m.trim(b+"");return e&&!m.trim(e.replace(xc,function(a,b,e,f){return c&&b&&(d=0),0===d?a:(c=e||b,d+=!f-!e,"")}))?Function("return "+e)():m.error("Invalid JSON: "+b)},m.parseXML=function(b){var c,d;if(!b||"string"!=typeof b)return null;try{a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b))}catch(e){c=void 0}return c&&c.documentElement&&!c.getElementsByTagName("parsererror").length||m.error("Invalid XML: "+b),c};var yc,zc,Ac=/#.*$/,Bc=/([?&])_=[^&]*/,Cc=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Dc=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ec=/^(?:GET|HEAD)$/,Fc=/^\/\//,Gc=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,Hc={},Ic={},Jc="*/".concat("*");try{zc=location.href}catch(Kc){zc=y.createElement("a"),zc.href="",zc=zc.href}yc=Gc.exec(zc.toLowerCase())||[];function Lc(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(E)||[];if(m.isFunction(c))while(d=f[e++])"+"===d.charAt(0)?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Mc(a,b,c,d){var e={},f=a===Ic;function g(h){var i;return e[h]=!0,m.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Nc(a,b){var c,d,e=m.ajaxSettings.flatOptions||{};for(d in b)void 0!==b[d]&&((e[d]?a:c||(c={}))[d]=b[d]);return c&&m.extend(!0,a,c),a}function Oc(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===e&&(e=a.mimeType||b.getResponseHeader("Content-Type"));if(e)for(g in h)if(h[g]&&h[g].test(e)){i.unshift(g);break}if(i[0]in c)f=i[0];else{for(g in c){if(!i[0]||a.converters[g+" "+i[0]]){f=g;break}d||(d=g)}f=f||d}return f?(f!==i[0]&&i.unshift(f),c[f]):void 0}function Pc(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}m.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:zc,type:"GET",isLocal:Dc.test(yc[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Jc,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":m.parseJSON,"text xml":m.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Nc(Nc(a,m.ajaxSettings),b):Nc(m.ajaxSettings,a)},ajaxPrefilter:Lc(Hc),ajaxTransport:Lc(Ic),ajax:function(a,b){"object"==typeof a&&(b=a,a=void 0),b=b||{};var c,d,e,f,g,h,i,j,k=m.ajaxSetup({},b),l=k.context||k,n=k.context&&(l.nodeType||l.jquery)?m(l):m.event,o=m.Deferred(),p=m.Callbacks("once memory"),q=k.statusCode||{},r={},s={},t=0,u="canceled",v={readyState:0,getResponseHeader:function(a){var b;if(2===t){if(!j){j={};while(b=Cc.exec(f))j[b[1].toLowerCase()]=b[2]}b=j[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return 2===t?f:null},setRequestHeader:function(a,b){var c=a.toLowerCase();return t||(a=s[c]=s[c]||a,r[a]=b),this},overrideMimeType:function(a){return t||(k.mimeType=a),this},statusCode:function(a){var b;if(a)if(2>t)for(b in a)q[b]=[q[b],a[b]];else v.always(a[v.status]);return this},abort:function(a){var b=a||u;return i&&i.abort(b),x(0,b),this}};if(o.promise(v).complete=p.add,v.success=v.done,v.error=v.fail,k.url=((a||k.url||zc)+"").replace(Ac,"").replace(Fc,yc[1]+"//"),k.type=b.method||b.type||k.method||k.type,k.dataTypes=m.trim(k.dataType||"*").toLowerCase().match(E)||[""],null==k.crossDomain&&(c=Gc.exec(k.url.toLowerCase()),k.crossDomain=!(!c||c[1]===yc[1]&&c[2]===yc[2]&&(c[3]||("http:"===c[1]?"80":"443"))===(yc[3]||("http:"===yc[1]?"80":"443")))),k.data&&k.processData&&"string"!=typeof k.data&&(k.data=m.param(k.data,k.traditional)),Mc(Hc,k,b,v),2===t)return v;h=k.global,h&&0===m.active++&&m.event.trigger("ajaxStart"),k.type=k.type.toUpperCase(),k.hasContent=!Ec.test(k.type),e=k.url,k.hasContent||(k.data&&(e=k.url+=(wc.test(e)?"&":"?")+k.data,delete k.data),k.cache===!1&&(k.url=Bc.test(e)?e.replace(Bc,"$1_="+vc++):e+(wc.test(e)?"&":"?")+"_="+vc++)),k.ifModified&&(m.lastModified[e]&&v.setRequestHeader("If-Modified-Since",m.lastModified[e]),m.etag[e]&&v.setRequestHeader("If-None-Match",m.etag[e])),(k.data&&k.hasContent&&k.contentType!==!1||b.contentType)&&v.setRequestHeader("Content-Type",k.contentType),v.setRequestHeader("Accept",k.dataTypes[0]&&k.accepts[k.dataTypes[0]]?k.accepts[k.dataTypes[0]]+("*"!==k.dataTypes[0]?", "+Jc+"; q=0.01":""):k.accepts["*"]);for(d in k.headers)v.setRequestHeader(d,k.headers[d]);if(k.beforeSend&&(k.beforeSend.call(l,v,k)===!1||2===t))return v.abort();u="abort";for(d in{success:1,error:1,complete:1})v[d](k[d]);if(i=Mc(Ic,k,b,v)){v.readyState=1,h&&n.trigger("ajaxSend",[v,k]),k.async&&k.timeout>0&&(g=setTimeout(function(){v.abort("timeout")},k.timeout));try{t=1,i.send(r,x)}catch(w){if(!(2>t))throw w;x(-1,w)}}else x(-1,"No Transport");function x(a,b,c,d){var j,r,s,u,w,x=b;2!==t&&(t=2,g&&clearTimeout(g),i=void 0,f=d||"",v.readyState=a>0?4:0,j=a>=200&&300>a||304===a,c&&(u=Oc(k,v,c)),u=Pc(k,u,v,j),j?(k.ifModified&&(w=v.getResponseHeader("Last-Modified"),w&&(m.lastModified[e]=w),w=v.getResponseHeader("etag"),w&&(m.etag[e]=w)),204===a||"HEAD"===k.type?x="nocontent":304===a?x="notmodified":(x=u.state,r=u.data,s=u.error,j=!s)):(s=x,(a||!x)&&(x="error",0>a&&(a=0))),v.status=a,v.statusText=(b||x)+"",j?o.resolveWith(l,[r,x,v]):o.rejectWith(l,[v,x,s]),v.statusCode(q),q=void 0,h&&n.trigger(j?"ajaxSuccess":"ajaxError",[v,k,j?r:s]),p.fireWith(l,[v,x]),h&&(n.trigger("ajaxComplete",[v,k]),--m.active||m.event.trigger("ajaxStop")))}return v},getJSON:function(a,b,c){return m.get(a,b,c,"json")},getScript:function(a,b){return m.get(a,void 0,b,"script")}}),m.each(["get","post"],function(a,b){m[b]=function(a,c,d,e){return m.isFunction(c)&&(e=e||d,d=c,c=void 0),m.ajax({url:a,type:b,dataType:e,data:c,success:d})}}),m.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(a,b){m.fn[b]=function(a){return this.on(b,a)}}),m._evalUrl=function(a){return m.ajax({url:a,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})},m.fn.extend({wrapAll:function(a){if(m.isFunction(a))return this.each(function(b){m(this).wrapAll(a.call(this,b))});if(this[0]){var b=m(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&1===a.firstChild.nodeType)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return this.each(m.isFunction(a)?function(b){m(this).wrapInner(a.call(this,b))}:function(){var b=m(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=m.isFunction(a);return this.each(function(c){m(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){m.nodeName(this,"body")||m(this).replaceWith(this.childNodes)}).end()}}),m.expr.filters.hidden=function(a){return a.offsetWidth<=0&&a.offsetHeight<=0||!k.reliableHiddenOffsets()&&"none"===(a.style&&a.style.display||m.css(a,"display"))},m.expr.filters.visible=function(a){return!m.expr.filters.hidden(a)};var Qc=/%20/g,Rc=/\[\]$/,Sc=/\r?\n/g,Tc=/^(?:submit|button|image|reset|file)$/i,Uc=/^(?:input|select|textarea|keygen)/i;function Vc(a,b,c,d){var e;if(m.isArray(b))m.each(b,function(b,e){c||Rc.test(a)?d(a,e):Vc(a+"["+("object"==typeof e?b:"")+"]",e,c,d)});else if(c||"object"!==m.type(b))d(a,b);else for(e in b)Vc(a+"["+e+"]",b[e],c,d)}m.param=function(a,b){var c,d=[],e=function(a,b){b=m.isFunction(b)?b():null==b?"":b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};if(void 0===b&&(b=m.ajaxSettings&&m.ajaxSettings.traditional),m.isArray(a)||a.jquery&&!m.isPlainObject(a))m.each(a,function(){e(this.name,this.value)});else for(c in a)Vc(c,a[c],b,e);return d.join("&").replace(Qc,"+")},m.fn.extend({serialize:function(){return m.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=m.prop(this,"elements");return a?m.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!m(this).is(":disabled")&&Uc.test(this.nodeName)&&!Tc.test(a)&&(this.checked||!W.test(a))}).map(function(a,b){var c=m(this).val();return null==c?null:m.isArray(c)?m.map(c,function(a){return{name:b.name,value:a.replace(Sc,"\r\n")}}):{name:b.name,value:c.replace(Sc,"\r\n")}}).get()}}),m.ajaxSettings.xhr=void 0!==a.ActiveXObject?function(){return!this.isLocal&&/^(get|post|head|put|delete|options)$/i.test(this.type)&&Zc()||$c()}:Zc;var Wc=0,Xc={},Yc=m.ajaxSettings.xhr();a.ActiveXObject&&m(a).on("unload",function(){for(var a in Xc)Xc[a](void 0,!0)}),k.cors=!!Yc&&"withCredentials"in Yc,Yc=k.ajax=!!Yc,Yc&&m.ajaxTransport(function(a){if(!a.crossDomain||k.cors){var b;return{send:function(c,d){var e,f=a.xhr(),g=++Wc;if(f.open(a.type,a.url,a.async,a.username,a.password),a.xhrFields)for(e in a.xhrFields)f[e]=a.xhrFields[e];a.mimeType&&f.overrideMimeType&&f.overrideMimeType(a.mimeType),a.crossDomain||c["X-Requested-With"]||(c["X-Requested-With"]="XMLHttpRequest");for(e in c)void 0!==c[e]&&f.setRequestHeader(e,c[e]+"");f.send(a.hasContent&&a.data||null),b=function(c,e){var h,i,j;if(b&&(e||4===f.readyState))if(delete Xc[g],b=void 0,f.onreadystatechange=m.noop,e)4!==f.readyState&&f.abort();else{j={},h=f.status,"string"==typeof f.responseText&&(j.text=f.responseText);try{i=f.statusText}catch(k){i=""}h||!a.isLocal||a.crossDomain?1223===h&&(h=204):h=j.text?200:404}j&&d(h,i,j,f.getAllResponseHeaders())},a.async?4===f.readyState?setTimeout(b):f.onreadystatechange=Xc[g]=b:b()},abort:function(){b&&b(void 0,!0)}}}});function Zc(){try{return new a.XMLHttpRequest}catch(b){}}function $c(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}m.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(a){return m.globalEval(a),a}}}),m.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),m.ajaxTransport("script",function(a){if(a.crossDomain){var b,c=y.head||m("head")[0]||y.documentElement;return{send:function(d,e){b=y.createElement("script"),b.async=!0,a.scriptCharset&&(b.charset=a.scriptCharset),b.src=a.url,b.onload=b.onreadystatechange=function(a,c){(c||!b.readyState||/loaded|complete/.test(b.readyState))&&(b.onload=b.onreadystatechange=null,b.parentNode&&b.parentNode.removeChild(b),b=null,c||e(200,"success"))},c.insertBefore(b,c.firstChild)},abort:function(){b&&b.onload(void 0,!0)}}}});var _c=[],ad=/(=)\?(?=&|$)|\?\?/;m.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=_c.pop()||m.expando+"_"+vc++;return this[a]=!0,a}}),m.ajaxPrefilter("json jsonp",function(b,c,d){var e,f,g,h=b.jsonp!==!1&&(ad.test(b.url)?"url":"string"==typeof b.data&&!(b.contentType||"").indexOf("application/x-www-form-urlencoded")&&ad.test(b.data)&&"data");return h||"jsonp"===b.dataTypes[0]?(e=b.jsonpCallback=m.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h?b[h]=b[h].replace(ad,"$1"+e):b.jsonp!==!1&&(b.url+=(wc.test(b.url)?"&":"?")+b.jsonp+"="+e),b.converters["script json"]=function(){return g||m.error(e+" was not called"),g[0]},b.dataTypes[0]="json",f=a[e],a[e]=function(){g=arguments},d.always(function(){a[e]=f,b[e]&&(b.jsonpCallback=c.jsonpCallback,_c.push(e)),g&&m.isFunction(f)&&f(g[0]),g=f=void 0}),"script"):void 0}),m.parseHTML=function(a,b,c){if(!a||"string"!=typeof a)return null;"boolean"==typeof b&&(c=b,b=!1),b=b||y;var d=u.exec(a),e=!c&&[];return d?[b.createElement(d[1])]:(d=m.buildFragment([a],b,e),e&&e.length&&m(e).remove(),m.merge([],d.childNodes))};var bd=m.fn.load;m.fn.load=function(a,b,c){if("string"!=typeof a&&bd)return bd.apply(this,arguments);var d,e,f,g=this,h=a.indexOf(" ");return h>=0&&(d=m.trim(a.slice(h,a.length)),a=a.slice(0,h)),m.isFunction(b)?(c=b,b=void 0):b&&"object"==typeof b&&(f="POST"),g.length>0&&m.ajax({url:a,type:f,dataType:"html",data:b}).done(function(a){e=arguments,g.html(d?m("").append(m.parseHTML(a)).find(d):a)}).complete(c&&function(a,b){g.each(c,e||[a.responseText,b,a])}),this},m.expr.filters.animated=function(a){return m.grep(m.timers,function(b){return a===b.elem}).length};var cd=a.document.documentElement;function dd(a){return m.isWindow(a)?a:9===a.nodeType?a.defaultView||a.parentWindow:!1}m.offset={setOffset:function(a,b,c){var d,e,f,g,h,i,j,k=m.css(a,"position"),l=m(a),n={};"static"===k&&(a.style.position="relative"),h=l.offset(),f=m.css(a,"top"),i=m.css(a,"left"),j=("absolute"===k||"fixed"===k)&&m.inArray("auto",[f,i])>-1,j?(d=l.position(),g=d.top,e=d.left):(g=parseFloat(f)||0,e=parseFloat(i)||0),m.isFunction(b)&&(b=b.call(a,c,h)),null!=b.top&&(n.top=b.top-h.top+g),null!=b.left&&(n.left=b.left-h.left+e),"using"in b?b.using.call(a,n):l.css(n)}},m.fn.extend({offset:function(a){if(arguments.length)return void 0===a?this:this.each(function(b){m.offset.setOffset(this,a,b)});var b,c,d={top:0,left:0},e=this[0],f=e&&e.ownerDocument;if(f)return b=f.documentElement,m.contains(b,e)?(typeof e.getBoundingClientRect!==K&&(d=e.getBoundingClientRect()),c=dd(f),{top:d.top+(c.pageYOffset||b.scrollTop)-(b.clientTop||0),left:d.left+(c.pageXOffset||b.scrollLeft)-(b.clientLeft||0)}):d},position:function(){if(this[0]){var a,b,c={top:0,left:0},d=this[0];return"fixed"===m.css(d,"position")?b=d.getBoundingClientRect():(a=this.offsetParent(),b=this.offset(),m.nodeName(a[0],"html")||(c=a.offset()),c.top+=m.css(a[0],"borderTopWidth",!0),c.left+=m.css(a[0],"borderLeftWidth",!0)),{top:b.top-c.top-m.css(d,"marginTop",!0),left:b.left-c.left-m.css(d,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||cd;while(a&&!m.nodeName(a,"html")&&"static"===m.css(a,"position"))a=a.offsetParent;return a||cd})}}),m.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,b){var c=/Y/.test(b);m.fn[a]=function(d){return V(this,function(a,d,e){var f=dd(a);return void 0===e?f?b in f?f[b]:f.document.documentElement[d]:a[d]:void(f?f.scrollTo(c?m(f).scrollLeft():e,c?e:m(f).scrollTop()):a[d]=e)},a,d,arguments.length,null)}}),m.each(["top","left"],function(a,b){m.cssHooks[b]=Lb(k.pixelPosition,function(a,c){return c?(c=Jb(a,b),Hb.test(c)?m(a).position()[b]+"px":c):void 0})}),m.each({Height:"height",Width:"width"},function(a,b){m.each({padding:"inner"+a,content:b,"":"outer"+a},function(c,d){m.fn[d]=function(d,e){var f=arguments.length&&(c||"boolean"!=typeof d),g=c||(d===!0||e===!0?"margin":"border");return V(this,function(b,c,d){var e;return m.isWindow(b)?b.document.documentElement["client"+a]:9===b.nodeType?(e=b.documentElement,Math.max(b.body["scroll"+a],e["scroll"+a],b.body["offset"+a],e["offset"+a],e["client"+a])):void 0===d?m.css(b,c,g):m.style(b,c,d,g)},b,f?d:void 0,f,null)}})}),m.fn.size=function(){return this.length},m.fn.andSelf=m.fn.addBack,"function"==typeof define&&define.amd&&define("jquery",[],function(){return m});var ed=a.jQuery,fd=a.$;return m.noConflict=function(b){return a.$===m&&(a.$=fd),b&&a.jQuery===m&&(a.jQuery=ed),m},typeof b===K&&(a.jQuery=a.$=m),m});
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index a7aa5c4..f2f0a4e 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -230,4 +230,15 @@
});
};
initNode();
+ document.addEventListener('DOMContentLoaded', function() {
+ var errors = document.getElementsByClassName('has-error');
+ if (errors.length <= 0) {
+ errors = document.getElementsByClassName('info-message');
+ }
+
+ if (errors.length > 0) {
+ errors[0].scrollIntoView();
+ }
+ });
})();
+
diff --git a/app/assets/javascripts/map.js b/app/assets/javascripts/map.js
new file mode 100644
index 0000000..588f543
--- /dev/null
+++ b/app/assets/javascripts/map.js
@@ -0,0 +1,117 @@
+(function() {
+ function loadMap() {
+ var projection = d3.geo.mercator();
+ var path = d3.geo.path().projection(projection);
+ var tooltip = document.getElementById('tooltip');
+
+ var initialScale = 3.5;
+ var initialPosition = [-100, -175];
+
+ var zoom = d3.behavior.zoom()
+ .scaleExtent([1, 20])
+ .scale(initialScale).translate(initialPosition)
+ .on("zoom", function() {
+ setZoom(d3.event.translate, d3.event.scale);
+ });
+
+ function setZoom(translate, scale) {
+ container.attr("transform", "translate(" + translate + ") scale(" + scale + ")").attr('data-scale', scale);
+ }
+
+ var container = d3.select('#map').call(zoom).insert('g', ':first-child').attr('class', 'map');
+
+ setZoom(initialPosition, initialScale);
+
+ container.append("path")
+ .datum(d3.geo.graticule())
+ .attr("class", "graticule")
+ .attr("d", path);
+
+ d3.selection.prototype.moveToFront = function() {
+ return this.each(function(){
+ this.parentNode.appendChild(this);
+ });
+ };
+
+ d3.json('/assets/world-110m.json', function(error, world) {
+ if (error) {
+ throw error;
+ }
+
+ container.selectAll('path')
+ .data(topojson.feature(world, world.objects.countries).features)
+ .enter()
+ .append('path')
+ .attr('d', path)
+ .attr('class', 'country');
+
+ var year = (new Date()).getFullYear();
+ var conferences = document.querySelectorAll('#conferences .conference');
+ var conference_path = []
+
+ for (var i = 0; i < conferences.length; i++) {
+ var d = conferences[i];
+ if (d.getAttribute('data-t') === 'annual') {
+ var coords = projection([d.getAttribute('data-o'), d.getAttribute('data-a')]);
+ if (conference_path.length) {
+ conference_path[conference_path.length - 1].x2 = coords[0];
+ conference_path[conference_path.length - 1].y2 = coords[1];
+ }
+ conference_path.push({
+ x1: coords[0],
+ y1: coords[1]
+ });
+ }
+ }
+
+ container.append('defs').html('
')
+
+ var regionalConferences = container.append('g').attr('filter', 'url(#svg-gooey-filter)').attr('class', 'cities regional-conferences');
+ var annualConferences = container.append('g').attr('filter', 'url(#svg-gooey-filter)').attr('class', 'cities annual-conferences');
+
+ function mouseover(e) {
+ c = document.getElementById('conference-' + event.target.getAttribute('data-c'));
+ tooltip.innerHTML = '
' + c.querySelector('.title').innerHTML + ' ' +
+ '
' + c.querySelector('.conference-details').innerHTML + '
';
+ tooltip.className = 'open';
+ }
+
+ function mouseout(e) {
+ tooltip.className = '';
+ }
+
+ function click(e) {
+ l = document.querySelector('#conference-' + event.target.getAttribute('data-c') + ' .conference-link');
+ window.location.href = l.getAttribute('href');
+ }
+
+ for (var i = conferences.length - 1; i >= 0; i--) {
+ var c = conferences[i];
+ var type = c.getAttribute('data-t');
+ var coords = projection([c.getAttribute('data-o'), c.getAttribute('data-a')]);
+
+ (type === 'annual' ? annualConferences : regionalConferences)
+ .append('circle')
+ .attr('class', 'city type-' + type)
+ .attr('data-c', c.id.replace(/^conference\-/, ''))
+ .attr('cx', function(d) { return coords[0]; })
+ .attr('cy', function(d) { return coords[1]; })
+ .attr('r', Math.max(3,
+ (15 - (
+ (year - parseInt(c.getAttribute('data-y'))) * 2.5)) * 1.125)
+ )
+ .on('mouseover', mouseover)
+ .on('mouseout', mouseout)
+ .on('click', click);
+ }
+ });
+
+ d3.select("#map").attr('class', 'loaded');
+ }
+
+ document.onreadystatechange = function () {
+ if (document.readyState == 'complete') {
+ loadMap();
+ }
+ };
+})();
diff --git a/app/assets/javascripts/userfield.js b/app/assets/javascripts/userfield.js
new file mode 100644
index 0000000..3ee54d8
--- /dev/null
+++ b/app/assets/javascripts/userfield.js
@@ -0,0 +1,26 @@
+(function() {
+ function find_user(email, f) {
+ var request = new XMLHttpRequest();
+ request.open('POST', '/user/find', true);
+ request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
+ request.setRequestHeader('X-CSRF-Token', encodeURI(document.querySelector('meta[name="csrf-token"]').getAttribute('content')));
+ request.send('e=' + encodeURI(email));
+ request.onreadystatechange = function() {
+ if (request.readyState == 4) {
+ if (request.status == 200) {
+ f(JSON.parse(request.responseText));
+ } else {
+ f({error: request.status});
+ }
+ }
+ }
+ }
+ document.addEventListener('DOMContentLoaded', function() {
+ var fields = document.getElementsByClassName('user-field');
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ var input = field.getElementsByTagName('input')[0];
+ var name = field.getElementsByClassName('user-name')[0];
+ }
+ }, false);
+})();
diff --git a/app/assets/stylesheets/_application.scss b/app/assets/stylesheets/_application.scss
index e907535..4cf5e58 100644
--- a/app/assets/stylesheets/_application.scss
+++ b/app/assets/stylesheets/_application.scss
@@ -6,638 +6,674 @@ $bug-list: ("search-element-appearance": ());
$zindex-base: 0;
html, body {
- color: $black;
- position: relative;
- z-index: $zindex-base;
+ color: $black;
+ position: relative;
+ z-index: $zindex-base;
}
body {
- padding-bottom: 20vw;
+ padding-bottom: 20vw;
}
-h1, h2, h3, h4, h5, h6, label, button, .button, dt, th, .table-th, nav.sub-menu {
- @include font-family(secondary);
- font-weight: normal;
+h1, h2, h3, h4, h5, h6, label, button, .button, dt, th, .table-th, nav.sub-menu, legend {
+ @include font-family(secondary);
+ font-weight: normal;
}
h2 {
- margin: 0;
- font-size: 6vw;
+ margin: 0;
+ font-size: 6vw;
}
-h3 {
- font-size: 4.5vw;
+h3, legend {
+ font-size: 4.5vw;
+ margin: 0.75em 0;
}
h3.subtitle {
- font-size: 1.5em;
+ font-size: 1.5em;
}
h4 {
- font-size: 1.25em;
+ font-size: 1.25em;
}
h5 {
- font-size: 1.125em;
+ font-size: 1.125em;
}
h6 {
- font-size: 1em;
+ font-size: 1em;
+}
+
+legend {
+ font-size: 1.25em;
}
p {
- font-size: 4vw;
+ font-size: 4vw;
}
a {
- text-decoration: none;
- color: $link-colour;
- border-bottom: 0 solid;
- outline: 0;
- position: relative;
- cursor: pointer;
-
- @include after {
- content: '';
- position: absolute;
- border-bottom: 0 solid;
- right: 0;
- bottom: 0;
- left: 0;
- opacity: 0;
- @include _(transition, all 150ms ease-in-out);
- @include _(transform-origin, 25% 50%);
- @include _(transform, scaleX(0));
- }
-
- &:hover,
- &:active,
- &:focus {
- @include after {
- border-bottom: 0.1em solid;
- opacity: 1;
- @include _(transform, scaleX(1));
- }
- }
+ text-decoration: none;
+ color: $link-colour;
+ border-bottom: 0 solid;
+ outline: 0;
+ position: relative;
+ cursor: pointer;
+
+ @include after {
+ content: '';
+ position: absolute;
+ border-bottom: 0 solid;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ opacity: 0;
+ @include _(transition, all 150ms ease-in-out);
+ @include _(transform-origin, 25% 50%);
+ @include _(transform, scaleX(0));
+ }
+
+ &:hover,
+ &:active,
+ &:focus {
+ @include after {
+ border-bottom: 0.1em solid;
+ opacity: 1;
+ @include _(transform, scaleX(1));
+ }
+ }
+}
+
+nav.sub-nav {
+ text-align: center;
+
+ ul {
+ margin: 0.85em 0;
+ list-style: none;
+ padding: 0;
+ }
+
+ li {
+ display: inline-block;
+
+ @include after {
+ content: '|';
+ margin: 0 0.5em;
+ }
+
+ &:last-child {
+ @include after {
+ display: none;
+ }
+ }
+ }
}
.screen-reader-text {
- clip: rect(1px, 1px, 1px, 1px);
- height: 1px;
- width: 1px;
- overflow: hidden;
- position: absolute !important;
+ clip: rect(1px, 1px, 1px, 1px);
+ height: 1px;
+ width: 1px;
+ overflow: hidden;
+ position: absolute !important;
}
table, .table {
margin-bottom: 2em;
- margin-left: 1em;
-
- th, td, .table-th, .table-td {
- text-align: left;
- padding: 0.25em 0.5em;
- border: 0.1rem solid #EEE;
-
- &.center {
- text-align: center;
- }
-
- &.big {
- font-size: 1.5em;
- }
-
- &.status {
- width: 0.1rem;
- background-color: transparent;
- border: 0;
- }
-
- &.state {
- background-size: 1.333em;
- background-repeat: no-repeat;
- background-position: center;
- width: 1.75em;
-
- &.happy {
- background-image: url("data:image/svg+xml;utf8,
");
- }
-
- &.unhappy {
- background-image: url('data:image/svg+xml;utf8,
');
- }
- }
- }
-
- th, .table-th {
- background-color: #F8F8F8;
-
- &.corner {
- background-color: transparent;
- border: 0;
- }
- }
-
- td, .table-td {
- &.inner-table {
- padding: 0;
-
- table {
- margin: 0;
- width: 100%;
- }
-
- tr:first-child {
- td, th {
- border-top: 0;
- }
- }
-
- tr:last-child {
- td, th {
- border-bottom: 0;
- }
- }
-
- td, th {
- &:first-child {
- border-left: 0
- }
-
- &:last-child {
- border-right: 0
- }
- }
- }
-
- &.bold {
- @include font-family(secondary);
- }
- }
-
- tbody th {
- width: 0.1rem;
- }
-
- &.admin-edit {
- width: 100%;
- }
-
- tr.hidden {
- display: none;
- }
-
- tr[data-key] {
- cursor: cell;
-
- &.editable:hover {
- background-color: lighten($colour-2, 33%);
- }
-
- + .editor {
- display: none;
- background-color: lighten($colour-1, 50%);
-
- td {
- opacity: 0.5;
-
- &.has-editor {
- opacity: 1;
-
- @include after {
- content: '';
- position: absolute;
- top: 100%;
- right: 0;
- left: 0;
- height: 0.25em;
- background-color: rgba($black, 0.125);
- }
- }
-
- .cell-editor {
- &[type=number]::-webkit-inner-spin-button,
- &[type=number]::-webkit-outer-spin-button {
- -webkit-appearance: none;
- }
- }
-
- select.cell-editor {
- -webkit-appearance: none;
- -moz-appearance: none;
- -ms-appearance: none;
- appearance: none;
- cursor: pointer;
- }
-
- &.date .cell-editor {
- text-align-last: right;
- }
- }
- }
-
- + .editor, &.always-edit {
- td {
- position: relative;
- vertical-align: top;
- background: inherit;
- cursor: default;
-
- .cell-editor {
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- padding: inherit;
- font: inherit;
- margin: inherit;
- background: inherit;
- border: 0;
- min-height: 0;
- width: 100% !important;
- border-radius: 0;
- line-height: inherit;
- overflow: hidden;
- box-shadow: none;
- text-align: inherit;
- }
- }
- }
-
- &.always-edit td .cell-editor {
- position: absolute;
- }
-
- &.editing {
- display: none;
-
- + .editor {
- display: table-row;
-
- .cell-editor {
- position: absolute;
- }
- }
- }
- }
-
- &.always-editing {
- tr {
- cursor: default;
-
- &:hover {
- background-color: transparent;
- }
- }
-
- .cell-editor {
- position: absolute;
- }
-
- td.text {
- height: 5em;
- }
- }
-
- tr.editable, tr.editor {
- td {
- white-space: nowrap;
-
- &.date, &.datetime, &.money, &.number {
- font-family: monospace;
- font-size: 1.25em;
- text-align: right;
- }
-
- &.text {
- max-width: 20em;
- }
- }
- }
-
- tr.editable td.text,
- tr.editor td.text .value {
- overflow: hidden;
- text-overflow: ellipsis;
- }
-
- tr.editor {
- td.text .cell-editor {
- white-space: normal;
- bottom: auto;
- height: 10em;
- z-index: 1;
- background: inherit;
- overflow: auto !important;
- }
- }
+ margin-left: 1em;
+
+ th, td, .table-th, .table-td {
+ text-align: left;
+ padding: 0.25em 0.5em;
+ border: 0.1rem solid $light-gray;
+
+ &.center {
+ text-align: center;
+ }
+
+ &.big {
+ font-size: 1.5em;
+ }
+
+ &.status {
+ width: 0.1rem;
+ background-color: transparent;
+ border: 0;
+ }
+
+ &.state {
+ background-size: 1.333em;
+ background-repeat: no-repeat;
+ background-position: center;
+ width: 1.75em;
+
+ &.happy {
+ background-image: url("data:image/svg+xml;utf8,
");
+ }
+
+ &.unhappy {
+ background-image: url('data:image/svg+xml;utf8,
');
+ }
+ }
+ }
+
+ th, .table-th {
+ background-color: #F8F8F8;
+
+ &.corner {
+ background-color: transparent;
+ border: 0;
+ }
+ }
+
+ td, .table-td {
+ &.inner-table {
+ padding: 0;
+
+ table {
+ margin: 0;
+ width: 100%;
+ }
+
+ tr:first-child {
+ td, th {
+ border-top: 0;
+ }
+ }
+
+ tr:last-child {
+ td, th {
+ border-bottom: 0;
+ }
+ }
+
+ td, th {
+ &:first-child {
+ border-left: 0
+ }
+
+ &:last-child {
+ border-right: 0
+ }
+ }
+ }
+
+ &.bold {
+ @include font-family(secondary);
+ }
+ }
+
+ tbody th {
+ width: 0.1rem;
+ }
+
+ &.admin-edit {
+ width: 100%;
+ }
+
+ tr.hidden {
+ display: none;
+ }
+
+ tr[data-key] {
+ cursor: cell;
+
+ &.editable:hover {
+ background-color: lighten($colour-2, 33%);
+ }
+
+ + .editor {
+ display: none;
+ background-color: lighten($colour-1, 50%);
+
+ td {
+ opacity: 0.5;
+
+ &.has-editor {
+ opacity: 1;
+
+ @include after {
+ content: '';
+ position: absolute;
+ top: 100%;
+ right: 0;
+ left: 0;
+ height: 0.25em;
+ background-color: rgba($black, 0.125);
+ }
+ }
+
+ .cell-editor {
+ &[type=number]::-webkit-inner-spin-button,
+ &[type=number]::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ }
+ }
+
+ select.cell-editor {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ -ms-appearance: none;
+ appearance: none;
+ cursor: pointer;
+ }
+
+ &.date .cell-editor {
+ text-align-last: right;
+ }
+ }
+ }
+
+ + .editor, &.always-edit {
+ td {
+ position: relative;
+ vertical-align: top;
+ background: inherit;
+ cursor: default;
+
+ .cell-editor {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: inherit;
+ font: inherit;
+ margin: inherit;
+ background: inherit;
+ border: 0;
+ min-height: 0;
+ width: 100% !important;
+ border-radius: 0;
+ line-height: inherit;
+ overflow: hidden;
+ box-shadow: none;
+ text-align: inherit;
+ }
+ }
+ }
+
+ &.always-edit td .cell-editor {
+ position: absolute;
+ }
+
+ &.editing {
+ display: none;
+
+ + .editor {
+ display: table-row;
+
+ .cell-editor {
+ position: absolute;
+ }
+ }
+ }
+ }
+
+ &.always-editing {
+ tr {
+ cursor: default;
+
+ &:hover {
+ background-color: transparent;
+ }
+ }
+
+ .cell-editor {
+ position: absolute;
+ }
+
+ td.text {
+ height: 5em;
+ }
+ }
+
+ tr.editable, tr.editor {
+ td {
+ white-space: nowrap;
+
+ &.date, &.datetime, &.money, &.number {
+ font-family: monospace;
+ font-size: 1.25em;
+ text-align: right;
+ }
+
+ &.text {
+ max-width: 20em;
+ }
+ }
+ }
+
+ tr.editable td.text,
+ tr.editor td.text .value {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ tr.editor {
+ td.text .cell-editor {
+ white-space: normal;
+ bottom: auto;
+ height: 10em;
+ z-index: 1;
+ background: inherit;
+ overflow: auto !important;
+ }
+ }
}
.table-scroller {
- overflow: auto;
- background-color: #F8F8F8;
- @include _(box-shadow, inset 0 0 10em 0 rgba(0,0,0,0.125));
+ overflow: auto;
+ background-color: #F8F8F8;
+ @include _(box-shadow, inset 0 0 10em 0 rgba(0,0,0,0.125));
- table {
- background-color: $white;
- margin: 0 0 8.5em;
- }
+ table {
+ background-color: $white;
+ margin: 0 0 8.5em;
+ }
- body.expanded-element .expanded & {
- overflow: visible;
- }
+ body.expanded-element .expanded & {
+ overflow: visible;
+ }
}
.goes-fullscreen {
- [data-contracts] {
- display: none;
- }
+ [data-contracts] {
+ display: none;
+ }
}
body.modal-open {
- overflow: hidden;
+ overflow: hidden;
}
body.expanded-element {
- overflow: hidden;
+ overflow: hidden;
- .goes-fullscreen.expanded {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1000;
- background-color: $white;
- overflow: auto;
- padding: 0 1em;
+ .goes-fullscreen.expanded {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1000;
+ background-color: $white;
+ overflow: auto;
+ padding: 0 1em;
- [data-expands] {
- display: none;
- }
+ [data-expands] {
+ display: none;
+ }
+
+ [data-contracts] {
+ display: block;
+ }
+ }
+}
- [data-contracts] {
- display: block;
- }
- }
+.centered {
+ text-align: center;
}
#main .columns .modal-edit {
- display: none;
- position: fixed;
- top: 0;
- right: 0;
- left: 0;
- bottom: 0;
- z-index: 1001;
- margin: 0;
- background-color: rgba($black, 0.5);
-
- &.open {
- display: flex;
- }
-
- .modal-edit-overlay {
- position: absolute;
- top: 0;
- right: 0;
- left: 0;
- bottom: 0;
- cursor: pointer;
- }
-
- table {
- margin: 0;
- background-color: $white;
-
- td.empty {
- background-color: #F8F8F8;
- border-bottom-color: #F8F8F8;
- }
- }
-
- thead th {
- text-align: center;
- font-size: 1.125em;
- color: $white;
- border-color: lighten($colour-1, 12.5%);
- background-color: $colour-1;
- }
-
- tbody th {
- white-space: nowrap;
- }
-
- .modal-edit-content {
- position: relative;
- min-width: 50em;
- max-width: 75em;
- margin: auto;
- overflow: auto;
- z-index: 1002;
- background-color: #F8F8F8;
- flex: 1;
- }
-
- .actions {
- margin: 1em 1em 0;
- }
+ display: none;
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ z-index: 1001;
+ margin: 0;
+ background-color: rgba($black, 0.5);
+
+ &.open {
+ display: flex;
+ }
+
+ .modal-edit-overlay {
+ position: absolute;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ cursor: pointer;
+ }
+
+ table {
+ margin: 0;
+ background-color: $white;
+
+ td.empty {
+ background-color: #F8F8F8;
+ border-bottom-color: #F8F8F8;
+ }
+ }
+
+ thead th {
+ text-align: center;
+ font-size: 1.125em;
+ color: $white;
+ border-color: lighten($colour-1, 12.5%);
+ background-color: $colour-1;
+ }
+
+ tbody th {
+ white-space: nowrap;
+ }
+
+ .modal-edit-content {
+ position: relative;
+ min-width: 50em;
+ max-width: 75em;
+ margin: auto;
+ overflow: auto;
+ z-index: 1002;
+ background-color: #F8F8F8;
+ flex: 1;
+ }
+
+ .actions {
+ margin: 1em 1em 0;
+ }
}
.table {
- display: table;
- border-collapse: collapse;
+ display: table;
+ border-collapse: collapse;
}
.table-tr {
- display: table-row;
+ display: table-row;
}
.table-th, .table-td {
- display: table-cell;
+ display: table-cell;
}
button,
.button {
- @include button;
+ @include button;
vertical-align: top;
- #main &[type="submit"] {
- background-color: $colour-5;
+ #main &[type="submit"] {
+ background-color: $colour-5;
- &[value="send"] {
- background-color: $colour-4;
- }
- }
+ &[value="send"] {
+ background-color: $colour-4;
+ }
+ }
- &.register {
- background-color: $colour-4;
- }
+ &.register {
+ background-color: $colour-2;
+ }
- form.logout & {
- background-color: #666;
- }
+ form.logout & {
+ background-color: #666;
+ }
- #main &.modify {
- background-color: $colour-5;
- }
+ #main &.modify {
+ background-color: $colour-5;
+ }
- &.delete,
- #main &.delete {
- background-color: $colour-4;
- }
+ &.delete,
+ #main &.delete {
+ background-color: $red;
+ }
- #main &.secondary {
- background-color: $colour-1;
- }
+ #main &.secondary {
+ background-color: $colour-1;
+ }
- &.subdued, #main &.subdued {
- background-color: #888;
- }
+ &.subdued, #main &.subdued {
+ background-color: #888;
+ }
- #main &.accented {
- background-color: $colour-2;
- }
+ #main &.accented {
+ background-color: $colour-2;
+ }
- &.facebook {
- background-color: #3A5795;
- }
+ &.facebook {
+ background-color: #3A5795;
+ }
- &.small {
- font-size: 0.9em;
- }
+ &.small {
+ font-size: 0.9em;
+ }
}
a.button {
- @include after {
- border-bottom: 1em solid transparent;
- left: auto;
- bottom: auto;
- @include _(transform, none);
- }
+ text-align: center;
+
+ @include after {
+ border-bottom: 1em solid transparent;
+ left: auto;
+ bottom: auto;
+ @include _(transform, none);
+ }
}
nav.sub-menu {
- display: inline-block;
- margin: 2rem 0 0;
- text-align: center;
-
- a {
- margin: 0 1em 0.5em 0;
- background-color: #E8E8E8;
- color: #888;
- display: inline-block;
- padding: 0.25em 0.75em 0.333em;
- font-size: 0.9em;
- border: 1px solid #AAA;
- border-bottom-width: 0.2em;
- @include _(border-radius, 0.15em);
- @include default-box-shadow(top, 2);
- @include _(text-shadow, 0 0.05em 0.05em #FFF);
-
- &:hover, &:active, &:focus {
- background-color: #D8D8D8;
- }
-
- &.current {
- background-color: #CCC;
- color: #666;
- padding: 0.333em 0.75em 0.25em;
- }
-
- @include after {
- display: none;
- }
- }
-
- @include breakpoint(medium) {
- border: 1px solid #AAA;
- background-color: #AAA;
- border-bottom-width: 0.125em;
- @include _(border-radius, 0.15em);
- @include default-box-shadow(top, 2);
-
- a {
- border: 0;
- margin: 0 1px 0 0;
- float: left;
- @include _(border-radius, 0);
- @include _(box-shadow, none);
-
- &:last-child {
- margin: 0;
- }
- }
- }
+ display: inline-block;
+ margin: 2rem 0 0;
+ text-align: center;
+
+ a {
+ margin: 0 1em 0.5em 0;
+ background-color: $gray;
+ color: #888;
+ display: inline-block;
+ padding: 0.25em 0.75em 0.333em;
+ font-size: 0.9em;
+ border: 1px solid #AAA;
+ border-bottom-width: 0.2em;
+ @include _(border-radius, 0.15em);
+ @include default-box-shadow(top, 2);
+ @include _(text-shadow, 0 0.05em 0.05em #FFF);
+
+ &:hover, &:active, &:focus {
+ background-color: #D8D8D8;
+ }
+
+ &.current {
+ background-color: #CCC;
+ color: #666;
+ padding: 0.333em 0.75em 0.25em;
+ }
+
+ @include after {
+ display: none;
+ }
+ }
+
+ @include breakpoint(medium) {
+ border: 1px solid #AAA;
+ background-color: #AAA;
+ border-bottom-width: 0.125em;
+ @include _(border-radius, 0.15em);
+ @include default-box-shadow(top, 2);
+
+ a {
+ border: 0;
+ margin: 0 1px 0 0;
+ float: left;
+ @include _(border-radius, 0);
+ @include _(box-shadow, none);
+
+ &:last-child {
+ margin: 0;
+ }
+ }
+ }
}
textarea, .textarea {
- display: block;
- width: 100%;
- min-height: 15em;
- font-size: 1.25em;
- line-height: 1.5em;
- margin: 1em 0;
- padding: 0.75em;
- border: 0.1rem solid #E8E8E8;
- outline: 0;
- @include _(border-radius, 0.25rem);
- color: inherit;
- background-image: repeating-linear-gradient(135deg, rgba(#000, 0.025), rgba(#000, 0.025) 0.1em, transparent 0.1em, transparent 0.4em);
- @include _(box-shadow, 0 0 0 0 rgba(0,0,0,0.05));
- @include _(transition, box-shadow 200ms ease-in-out);
- will-change: box-shadow;
-
- &[data-edit-on="click"],
- &[data-edit-on="focus"] {
- cursor: text;
- }
-
- &:hover, &:focus, &:active {
- @include _(box-shadow, 0 0 0 0.3em rgba(0,0,0,0.05));
- }
-
- &.short {
- min-height: 10em;
- }
+ display: block;
+ width: 100%;
+ min-height: 15em;
+ font-size: 1.25em;
+ line-height: 1.5em;
+ margin: 1em 0;
+ padding: 0.75em;
+ border: 0.1rem solid $gray;
+ outline: 0;
+ @include _(border-radius, 0.25rem);
+ color: inherit;
+ background-image: repeating-linear-gradient(135deg, rgba(#000, 0.025), rgba(#000, 0.025) 0.1em, transparent 0.1em, transparent 0.4em);
+ @include _(box-shadow, 0 0 0 0 rgba(0,0,0,0.05));
+ @include _(transition, box-shadow 200ms ease-in-out);
+ will-change: box-shadow;
+
+ &[data-edit-on="click"],
+ &[data-edit-on="focus"] {
+ cursor: text;
+ }
+
+ &:hover, &:focus, &:active {
+ @include _(box-shadow, 0 0 0 0.3em rgba(0,0,0,0.05));
+ }
+
+ &.short {
+ min-height: 10em;
+ }
}
.textarea, .workshop-description {
- > :first-child {
- margin-top: 0;
- }
+ > :first-child {
+ margin-top: 0;
+ }
- > :last-child {
- margin-bottom: 0;
- }
+ > :last-child {
+ margin-bottom: 0;
+ }
- p {
- font-size: 1.125em;
- }
+ p {
+ font-size: 1.125em;
+ }
- h1 {
- font-size: 1.667em;
- }
+ h1 {
+ font-size: 1.667em;
+ }
- h2 {
- font-size: 1.25em;
- }
+ h2 {
+ font-size: 1.25em;
+ }
}
input {
- &[type="text"], &[type="password"], &[type="tel"], &[type="search"], &[type="email"], &[type="url"], &[type="number"] {
- display: block;
- font-size: 1.25em;
- outline: 0;
- border: 0;
- margin: 1em 0;
- width: 100%;
- padding: 0.25em 0.5em;
- border-bottom: 0.15em solid transparent;
- }
-
- &[type="number"], &[type="tel"] {
- @include font-family(secondary);
- }
+ &[type="text"], &[type="password"], &[type="tel"], &[type="search"], &[type="email"], &[type="url"], &[type="number"] {
+ display: block;
+ font-size: 1.25em;
+ outline: 0;
+ border: 0;
+ margin: 1em 0;
+ width: 100%;
+ padding: 0.25em 0.5em;
+ border-bottom: 0.15em solid transparent;
+ }
+
+ &[type="number"], &[type="tel"] {
+ @include font-family(secondary);
+ }
}
.number-field,
@@ -646,71 +682,71 @@ input {
.telephone-field,
.password-field,
.text-field {
- position: relative;
- margin-bottom: 2em;
- color: inherit;
-
- &.empty {
- label {
- z-index: $zindex-base + 3;
- @include _(transform, translateY(-100%) scale(1));
- background-color: transparent;
- color: #666;
- }
- }
-
- &.big input {
- @include font-family(secondary);
-
- .composition & {
- margin-top: 1em;
- }
- }
-
- label,
- &.focused label {
- position: absolute;
- z-index: $zindex-base + 3;
- font-size: 1em;
- padding: 0.25em 0.667em;
- width: auto;
- @include _(transition, 'transform 250ms ease-in-out, color 250ms ease-in-out, background-color 250ms ease-in-out');
- top: 100%;
- left: 0;
- @include _(transform, translateY(0) scale(0.75));
- @include _(transform-origin, 0 0);
- line-height: 1.5em;
- background-color: transparent;
- color: $black;
- cursor: text;
- will-change: transform, color, background-color;
- }
-
- input {
- margin: 0;
- position: relative;
- z-index: $zindex-base + 2;
- padding: 0.15em 0.5em;
- background-color: #F8F8F8;
- @include _(border-radius, 0.25rem);
- @include _(box-shadow, 0 0 0 0 rgba(0,0,0,0.05));
- @include _(transition, box-shadow 200ms ease-in-out);
- background-image: repeating-linear-gradient(135deg, rgba(0, 0, 0, 0.025), rgba(0, 0, 0, 0.025) 0.1rem, transparent 0.1rem, transparent 0.4rem);
- border: 0.1rem solid #E8E8E8;
- will-change: box-shadow;
- }
-
- &:focus, &:active, &:focus {
-
- label {
- color: $black;
- }
- }
- input {
- &:focus, &:active, &:hover {
- @include _(box-shadow, 0 0 0 0.3em rgba(0,0,0,0.05));
- }
- }
+ position: relative;
+ margin-bottom: 2em;
+ color: inherit;
+
+ &.empty {
+ label {
+ z-index: $zindex-base + 3;
+ @include _(transform, translateY(-100%) scale(1));
+ background-color: transparent;
+ color: #666;
+ }
+ }
+
+ &.big input {
+ @include font-family(secondary);
+
+ .composition & {
+ margin-top: 1em;
+ }
+ }
+
+ label,
+ &.focused label {
+ position: absolute;
+ z-index: $zindex-base + 3;
+ font-size: 1em;
+ padding: 0.25em 0.667em;
+ width: auto;
+ @include _(transition, 'transform 250ms ease-in-out, color 250ms ease-in-out, background-color 250ms ease-in-out');
+ top: 100%;
+ left: 0;
+ @include _(transform, translateY(0) scale(0.75));
+ @include _(transform-origin, 0 0);
+ line-height: 1.5em;
+ background-color: transparent;
+ color: $black;
+ cursor: text;
+ will-change: transform, color, background-color;
+ }
+
+ input {
+ margin: 0;
+ position: relative;
+ z-index: $zindex-base + 2;
+ padding: 0.15em 0.5em;
+ background-color: #F8F8F8;
+ @include _(border-radius, 0.25rem);
+ @include _(box-shadow, 0 0 0 0 rgba(0,0,0,0.05));
+ @include _(transition, box-shadow 200ms ease-in-out);
+ background-image: repeating-linear-gradient(135deg, rgba(0, 0, 0, 0.025), rgba(0, 0, 0, 0.025) 0.1rem, transparent 0.1rem, transparent 0.4rem);
+ border: 0.1rem solid $gray;
+ will-change: box-shadow;
+ }
+
+ &:focus, &:active, &:focus {
+
+ label {
+ color: $black;
+ }
+ }
+ input {
+ &:focus, &:active, &:hover {
+ @include _(box-shadow, 0 0 0 0.3em rgba(0,0,0,0.05));
+ }
+ }
}
.number-field,
.email-field,
@@ -719,667 +755,931 @@ input {
.search-field,
.text-field,
.text-area-field {
- text-align: left;
+ text-align: left;
}
.telephone-field {
- max-width: 15em;
+ max-width: 15em;
+}
+
+.file-field {
+ label {
+ display: inline-block;
+ cursor: pointer;
+ }
+
+ .file-field-selector {
+ display: block;
+ text-align: right;
+ padding: 0.5em;
+ background-color: #F8F8F8;
+ @include _(border-radius, 0.25rem);
+ @include _(box-shadow, 0 0 0 0 rgba(0,0,0,0.05));
+ @include _(transition, box-shadow 200ms ease-in-out);
+ background-image: repeating-linear-gradient(135deg, rgba(0, 0, 0, 0.025), rgba(0, 0, 0, 0.025) 0.1rem, transparent 0.1rem, transparent 0.4rem);
+ border: 0.1rem solid $gray;
+ will-change: box-shadow;
+
+ .file-field-name {
+ display: inline-block;
+ padding: 0.25em 0.5em;
+ }
+
+ .unselected {
+ color: $mid-gray;
+ }
+
+ .button {
+ vertical-align: middle;
+ margin-left: 1em;
+ }
+
+ img {
+ display: none;
+ max-width: 100%;
+ margin-bottom: 1em;
+
+ &[src] {
+ display: block;
+ }
+
+ &.changed {
+ opacity: 0.5;
+ }
+ }
+ }
+
+ label:hover, input:focus + label, input:active + label, label:focus, label:active {
+ .file-field-selector {
+ @include _(box-shadow, 0 0 0 0.3em rgba(0,0,0,0.05));
+ }
+ }
}
@include keyframes(bend) {
- to {
- @include _(transform, skewX(-5deg));
- }
+ to {
+ @include _(transform, skewX(-5deg));
+ }
}
.hidden {
- display: none !important;
+ display: none !important;
}
.field-error {
- display: block;
- background-color: rgba($colour-2, 0.333);
- @include font-family(secondary);
- padding: 0.5em 1em;
- margin: 0 0.2em;
- text-align: center;
- @include default-box-shadow(top, 2);
+ display: block;
+ background-color: rgba($colour-2, 0.333);
+ @include font-family(secondary);
+ padding: 0.5em 1em;
+ margin: 0 0.2em;
+ text-align: center;
+ @include default-box-shadow(top, 2);
}
.input-field {
- .field-error {
- float: right;
- @include _(transform, skewX(-15deg));
- @include _(transform-origin, 0 100%);
- @include _(animation, bend ease-in-out 500ms infinite alternate both);
- }
-
- &.check-box-field {
- &.has-error {
- margin-top: 3em;
- }
-
- .field-error {
- position: absolute;
- bottom: 100%;
- right: 0;
- margin-right: 0;
- }
- }
-
- &.small select,
- &.small input {
- font-size: 1em;
- @include font-family(secondary)
- }
+ .field-error {
+ float: right;
+ @include _(transform, skewX(-15deg));
+ @include _(transform-origin, 0 100%);
+ @include _(animation, bend ease-in-out 500ms infinite alternate both);
+ }
+
+ &.check-box-field {
+ &.has-error {
+ margin-top: 3em;
+ }
+
+ .field-error {
+ position: absolute;
+ bottom: 100%;
+ right: 0;
+ margin-right: 0;
+ }
+ }
+
+ &.small select,
+ &.small input {
+ font-size: 1em;
+ @include font-family(secondary)
+ }
}
.input-field.other-field label {
- input {
- float: left;
- margin: 0.33em 0;
-
- &:checked {
- + div input {
- z-index: $zindex-base + 2;
- @include _(opacity, 1);
- }
- }
- }
-
- div {
- float: left;
- position: relative;
- margin: 0 0.25em;
- font-size: 1em;
-
- input {
- //display: none;
- @include _(opacity, 0);
- @include _(transition, opacity 250ms ease-in-out);
- z-index: $zindex-base;
- position: absolute;
- width: auto;
- margin: 0;
- border: 1px solid #CCC;
- height: 1.5em;
- left: 0;
- top: 0;
- }
- }
+ input {
+ float: left;
+ margin: 0.33em 0;
+
+ &:checked {
+ + div input {
+ z-index: $zindex-base + 2;
+ @include _(opacity, 1);
+ }
+ }
+ }
+
+ div {
+ float: left;
+ position: relative;
+ margin: 0 0.25em;
+ font-size: 1em;
+
+ input {
+ //display: none;
+ @include _(opacity, 0);
+ @include _(transition, opacity 250ms ease-in-out);
+ z-index: $zindex-base;
+ position: absolute;
+ width: auto;
+ margin: 0;
+ border: 1px solid #CCC;
+ height: 1.5em;
+ left: 0;
+ top: 0;
+ }
+ }
}
.number-field {
- display: inline-block;
- font-size: 1.5em;
+ display: inline-block;
+ font-size: 1.5em;
- input {
- text-align: right;
- }
+ input {
+ text-align: right;
+ }
}
.input-field-help {
- margin: 0.5em 1em;
- line-height: 1.3333em;
- font-size: 1.125em;
+ margin: 0.5em 1em;
+ line-height: 1.3333em;
+ font-size: 1.125em;
}
.check-box-field,
.check-box-field.vertical,
.radio-button-field {
- @include clearfix;
- margin-bottom: 3em;
- position: relative;
- @include default-box-shadow(top);
-
- td &,
- .table-td & {
- @include _(box-shadow, none);
- margin: 0;
- }
-
- label {
- display: block;
- background-color: #FFF;
- height: 2.333em;
- font-weight: normal;
- font-size: 1.25em;
- @include font-family(secondary);
- text-align: center;
- position: relative;
- padding: 0.5em 0.5em 0.5em 3em;
- cursor: pointer;
- border: 0.1em solid;
- border-top: 0;
- overflow: hidden;
- white-space: nowrap;
- text-overflow: ellipsis;
- @include _(transition, #{'transform, background-color 100ms, 100ms ease-in-out, ease-in-out'});
-
- @include before-and-after {
- content: '';
- position: absolute;
- @include _(transition, transform 200ms ease-in-out);
- }
-
- @include before {
- content: '';
- position: absolute;
- top: 0;
- left: 0;
- width: 2.333em;
- height: 100%;
- border-right: inherit;
- }
-
- @include after {
- content: '';
- position: absolute;
- visibility: hidden;
- left: 0.75em;
- top: 0.25em;
- border: 0.2em solid #FFF;
- width: 0.75em;
- height: 1.5em;
- border-width: 0 .2em 0.2em 0;
- @include _(transform, rotate(45deg) scale(0));
- @include _(transition, transform 200ms cubic-bezier(0, 0.38, 0.9, 2));
- }
- }
-
- input:first-child + label {
- border: 0.1em solid;
- }
-
- input {
- position: fixed;
- opacity: 0 !important;
- left: -100000px;
- z-index: -10;
-
- &:checked + label {
- @include before {
- background-color: $colour-5;
- }
- @include after {
- visibility: visible;
- @include _(transform, rotate(45deg) scale(1));
- }
- }
-
- &:hover + label {
- @include before {
- background-color: $colour-3;
- }
- }
-
- &[type="radio"] + label {
- @include after {
- content: '+';
- border: 0;
- font-size: 2.5em;
- top: -0.025em;
- left: 0.175em;
- line-height: 1em;
- color: #FFF;
- height: 1em;
- width: 1em;
- }
- }
- }
-
- &.inline input[type="radio"] + label {
- @include after {
- top: -0.15em;
- left: 0.05em;
- }
- }
-
- .other {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 2.333em;
-
- input {
- z-index: 1;
- opacity: 1!important;
- position: static;
- margin: 0;
- font: inherit;
- height: 100%;
- text-align: inherit;
- cursor: inherit;
- }
- }
+ @include clearfix;
+ margin-bottom: 3em;
+ position: relative;
+ @include default-box-shadow(top);
+
+ td &,
+ .table-td & {
+ @include _(box-shadow, none);
+ margin: 0;
+ }
+
+ label {
+ display: block;
+ background-color: #FFF;
+ height: 2.333em;
+ font-weight: normal;
+ font-size: 1.25em;
+ @include font-family(secondary);
+ text-align: center;
+ position: relative;
+ padding: 0.5em 0.5em 0.5em 3em;
+ cursor: pointer;
+ border: 0.1em solid;
+ border-top: 0;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ @include _(transition, #{'transform, background-color 100ms, 100ms ease-in-out, ease-in-out'});
+
+ @include before-and-after {
+ content: '';
+ position: absolute;
+ @include _(transition, transform 200ms ease-in-out);
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 2.333em;
+ height: 100%;
+ border-right: inherit;
+ }
+
+ @include after {
+ content: '';
+ position: absolute;
+ visibility: hidden;
+ left: 0.75em;
+ top: 0.25em;
+ border: 0.2em solid #FFF;
+ width: 0.75em;
+ height: 1.5em;
+ border-width: 0 .2em 0.2em 0;
+ @include _(transform, rotate(45deg) scale(0));
+ @include _(transition, transform 200ms cubic-bezier(0, 0.38, 0.9, 2));
+ }
+ }
+
+ input:first-child + label {
+ border: 0.1em solid;
+ }
+
+ input {
+ position: fixed;
+ opacity: 0 !important;
+ left: -100000px;
+ z-index: -10;
+
+ &:checked + label {
+ @include before {
+ background-color: $colour-5;
+ }
+ @include after {
+ visibility: visible;
+ @include _(transform, rotate(45deg) scale(1));
+ }
+ }
+
+ &:hover + label {
+ @include before {
+ background-color: $colour-3;
+ }
+ }
+
+ &[type="radio"] + label {
+ @include after {
+ content: '+';
+ border: 0;
+ font-size: 2.5em;
+ top: -0.025em;
+ left: 0.175em;
+ line-height: 1em;
+ color: #FFF;
+ height: 1em;
+ width: 1em;
+ }
+ }
+ }
+
+ &.inline input[type="radio"] + label {
+ @include after {
+ top: -0.15em;
+ left: 0.05em;
+ }
+ }
+
+ .other {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 2.333em;
+
+ input {
+ z-index: 1;
+ opacity: 1!important;
+ position: static;
+ margin: 0;
+ font: inherit;
+ height: 100%;
+ text-align: inherit;
+ cursor: inherit;
+ }
+ }
}
.check-box-field.vertical {
- display: block;
- margin: 0 2em 5em;
- font-size: 0.75em;
+ display: block;
+ margin: 0 2em 5em;
+ font-size: 0.75em;
- input[type="radio"] + label {
- @include after {
- top: -0.15em;
- left: 0.05em;
- }
- }
+ input[type="radio"] + label {
+ @include after {
+ top: -0.15em;
+ left: 0.05em;
+ }
+ }
}
.check-box-field.inline {
- display: inline-block;
+ display: inline-block;
- label {
- min-width: 7.5em;
- }
+ label {
+ min-width: 7.5em;
+ }
}
.check-box-field.small {
- font-size: 0.75em;
+ font-size: 0.75em;
- &.small label {
- font-size: 1.125em;
- }
+ &.small label {
+ font-size: 1.125em;
+ }
}
.check-box-field.big {
- font-size: 1em;
+ font-size: 1em;
}
.radio-button-field {
- label {
- width: 7em;
- height: 5em;
- }
+ label {
+ width: 7em;
+ height: 5em;
+ }
}
.single-check-box-field,
.single-radio-button-field {
- margin-left: 1em;
+ margin-left: 1em;
}
.date-span {
- margin-left: 1em;
-
- label, input {
- display: block;
- height: 1.5em;
- margin: 0 0.5em 1em 0;
- }
-
- label {
- height: 1.5em;
- }
-
- input {
- @include default-box-shadow(top);
- background-color: $colour-1;
- color: $white;
- border: 0;
- padding: 0 0.25em;
- font-weight: normal;
- @include font-family(secondary);
- @include _(text-stroke, 0.5px #000);
- }
-
- .date-labels {
- float: left;
- }
-
- .date-field {
- overflow: auto;
- }
+ margin-left: 1em;
+
+ label, input {
+ display: block;
+ height: 1.5em;
+ margin: 0 0.5em 1em 0;
+ }
+
+ label {
+ height: 1.5em;
+ }
+
+ input {
+ @include default-box-shadow(top);
+ background-color: $colour-1;
+ color: $white;
+ border: 0;
+ padding: 0 0.25em;
+ font-weight: normal;
+ @include font-family(secondary);
+ @include _(text-stroke, 0.5px #000);
+ }
+
+ .date-labels {
+ float: left;
+ }
+
+ .date-field {
+ overflow: auto;
+ }
}
.requesting {
- opacity: 0.5;
- -webkit-filter: blur(5px);
+ opacity: 0.5;
+ -webkit-filter: blur(5px);
}
form {
- @include _(transition, 'opacity 250ms ease-in-out, filter 250ms ease-in-out, -webkit-filter 250ms ease-in-out');
-
- &.composition {
- textarea {
- height: 16em;
- }
- }
-
- &.mini-flex-form {
- @include _-(display, flex);
- @include _(align-items, flex-start);
-
- .input-field {
- @include _(flex, 1);
- }
-
- button, .button {
- margin-left: 1em;
- height: 2.6em;
- }
- }
-
- &.centered {
- text-align: center;
- }
+ @include _(transition, 'opacity 250ms ease-in-out, filter 250ms ease-in-out, -webkit-filter 250ms ease-in-out');
+
+ &.composition {
+ textarea {
+ height: 16em;
+ }
+ }
+
+ &.mini-flex-form {
+ @include _-(display, flex);
+ @include _(align-items, flex-start);
+
+ .input-field {
+ @include _(flex, 1);
+ }
+
+ button, .button {
+ margin-left: 1em;
+ height: 2.6em;
+ }
+ }
+
+ button[value="upload"] {
+ display: none;
+
+ &[data-enabled="1"] {
+ display: inline-block;
+ }
+ }
+
+ #main .columns &.inline {
+ display: inline-block;
+ margin: 0;
+ }
+
+ &.right {
+ float: right;
+ }
}
#main .columns th form {
- display: inline;
- vertical-align: super;
- margin: 0 0 0 0.5em;
+ display: inline;
+ vertical-align: super;
+ margin: 0 0 0 0.5em;
- button {
- float: right;
- }
+ button {
+ float: right;
+ }
}
#main .columns th.form,
#main .columns .table-th.form {
- display: none;
+ display: none;
}
#main .columns td.form,
#main .columns .table-td.form {
- border: 0;
- width: 1px;
-
- form {
- margin: 0;
- }
+ border: 0;
+ width: 1px;
+
+ form {
+ margin: 0;
+ }
- button {
- display: block;
- width: 100%;
- white-space: nowrap;
+ button, .button {
+ display: block;
+ width: 100%;
+ white-space: nowrap;
- + button {
- margin-top: 0.5em;
- }
- }
+ + button {
+ margin-top: 0.5em;
+ }
+ }
}
fieldset {
- margin: 0;
- padding: 0;
- border: 0;
+ margin: 0;
+ padding: 0;
+ border: 0;
+
+ &.centered {
+ margin-top: 3em;
+ }
+
+ &.right-help {
+ text-align: center;
+
+ .input-field {
+ margin: 0;
+ }
- &.centered {
- margin-top: 3em;
- text-align: center;
- }
+ + .input-field-help {
+ overflow: auto;
+ margin-bottom: 2em;
+ }
+ }
- &.right-help {
- text-align: center;
+ &.inline {
+ display: inline-block;
+ }
- .input-field {
- margin: 0;
- }
+ &.inline-label {
+ legend {
+ display: inline-block;
+ }
+ }
- + .input-field-help {
- overflow: auto;
- margin-bottom: 2em;
- }
- }
+ .field-error {
+ color: $red;
+ background-color: transparent;
+ @include _(box-shadow, none);
+ }
}
.fieldgroup {
- @include _(align-items, flex-end);
- @include _(flex-wrap, wrap);
- margin: 0 0 3em 1em;
-
- > .input-field {
- margin-right: 1em;
- @include _(flex, 1);
- }
+ @include _(align-items, flex-end);
+ @include _(flex-wrap, wrap);
+ margin: 0 0 3em 1em;
+
+ > .input-field {
+ margin-right: 1em;
+ @include _(flex, 1);
+ }
+}
+
+#main article fieldset.translator {
+ .fieldgroup {
+ display: block;
+
+ > .input-field {
+ flex: none;
+ }
+ }
+
+ ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ clear: left;
+ }
+
+ .locale-select {
+ @include _-(display, flex);
+ @include _(flex-wrap, wrap);
+ margin: 0 0.5em;
+ text-align: center;
+ border: 0.1em solid $gray;
+ border-bottom: 0;
+ @include default-box-shadow(top, 2);
+
+ li {
+ @include _(flex, 1);
+ position: relative;
+ margin: 0;
+
+ &:hover {
+ background-color: $light-gray;
+ color: $black;
+ }
+
+ &.selected {
+ background-color: $gray;
+ z-index: 1;
+ }
+ }
+
+ a {
+ display: block;
+ @include font-family(secondary);
+ color: inherit;
+ padding: 0.25em 0.75em;
+
+ @include after {
+ display: none;
+ }
+ }
+ }
+
+ .text-editors {
+ position: relative;
+ background-color: $white;
+
+ li {
+ display: none;
+
+ &.selected {
+ display: block;
+ }
+ }
+ }
+
+ &.multi-field-translator {
+ .text-editors {
+ border: 0.1em solid $gray;
+ padding: 1em 1em 0;
+ @include default-box-shadow(top, 2);
+ }
+ }
+
+ .textarea {
+ margin-top: 0;
+ }
}
.view-object {
- margin-bottom: 3em;
+ margin-bottom: 3em;
}
.comments {
- list-style: none;
-
- .comment {
- position: relative;
- border: 0.1rem solid #EEE;
- border-radius: 0.25rem;
- padding: 1em;
- margin-top: 2em;
- }
-
- .comment-body {
- font-size: 0.9em;
- }
-
- .comment-title {
- position: absolute;
- top: -0.75em;
- left: 0.5em;
- margin: 0;
- padding: 0 0.5em;
- background-color: #FFF;
- }
-
- time {
- position: absolute;
- top: -0.75em;
- right: 0.75em;
- background-color: #FFF;
- padding: 0 0.5em;
- }
-
- .sub-comment {
- font-size: 0.8em;
-
- .comment-title, time {
- top: -1em;
- }
- }
-
- .text-area-field {
- overflow: hidden;
- max-height: 0;
- padding: 0.3em;
- @include _(transition, max-height 150ms ease-in-out);
- }
-
- #main .columns & form {
- margin-top: 0;
- button {
- @include _(transition, background-color 150ms ease-in-out);
- background-color: #888;
- }
-
- &.open {
- @include _(opacity, 1);
-
- .text-area-field {
- max-height: 12em;
- }
-
- button {
- background-color: $colour-5;
- }
- }
- }
-
- button {
- margin: 0;
- }
+ list-style: none;
+
+ .comment {
+ position: relative;
+ border: 0.1rem solid $light-gray;
+ border-radius: 0.25rem;
+ padding: 1em;
+ margin-top: 2em;
+ }
+
+ .comment-body {
+ font-size: 0.9em;
+ }
+
+ .comment-title {
+ position: absolute;
+ top: -0.75em;
+ left: 0.5em;
+ margin: 0;
+ padding: 0 0.5em;
+ background-color: #FFF;
+ }
+
+ time {
+ position: absolute;
+ top: -0.75em;
+ right: 0.75em;
+ background-color: #FFF;
+ padding: 0 0.5em;
+ }
+
+ .sub-comment {
+ font-size: 0.8em;
+
+ .comment-title, time {
+ top: -1em;
+ }
+ }
+
+ .text-area-field {
+ overflow: hidden;
+ max-height: 0;
+ padding: 0.3em;
+ @include _(transition, max-height 150ms ease-in-out);
+ }
+
+ #main .columns & form {
+ margin-top: 0;
+ button {
+ @include _(transition, background-color 150ms ease-in-out);
+ background-color: #888;
+ }
+
+ &.open {
+ @include _(opacity, 1);
+
+ .text-area-field {
+ max-height: 12em;
+ }
+
+ button {
+ background-color: $colour-5;
+ }
+ }
+ }
+
+ button {
+ margin: 0;
+ }
}
#comments {
- textarea {
- font-size: 1.25em;
- min-height: 7.5em;
- }
+ textarea {
+ font-size: 1.25em;
+ min-height: 7.5em;
+ }
- .actions {
- margin: 0;
+ .actions {
+ margin: 0;
- button {
- margin: 0;
- }
- }
+ button {
+ margin: 0;
+ }
+ }
}
.flex-form, .flex-column {
- button, .button {
- width: 100%;
- text-align: center;
-
- + button, + .button {
- margin-left: 0.75em;
- }
- }
+ button, .button {
+ width: 100%;
+ text-align: center;
+
+ + button, + .button {
+ margin-left: 0.75em;
+ }
+ }
+
+ &.address-form {
+ .city {
+ @include _(align-self, flex-end);
+ @include font-family(secondary);
+ text-align: right;
+ margin-bottom: 2.5em;
+ }
+ }
}
.flex-inputs {
- @include _-(display, flex);
- @include _(align-items, flex-end);
- @include _(flex-wrap, wrap);
- @include _(justify-content, flex-end);
- margin-bottom: 2em;
+ @include _-(display, flex);
+ @include _(align-items, flex-end);
+ @include _(flex-wrap, wrap);
+ @include _(justify-content, flex-end);
+ margin-bottom: 2em;
- .input-field {
- margin: 0 0.5em;
- }
+ .input-field {
+ margin: 0 0.5em;
+ }
- .stretch-item {
- @include _(flex, 1);
- @include _(flex-basis, 100%);
+ .stretch-item {
+ @include _(flex, 1);
+ @include _(flex-basis, 100%);
- select {
- width: 100%;
- }
- }
+ select {
+ width: 100%;
+ }
+ }
}
.actions {
- text-align: center;
- margin: 0 1em;
-
- button, .button {
- margin: 0 0.25em 1em;
- vertical-align: middle;
- }
-
- &.left {
- text-align: left;
- }
-
- &.right {
- text-align: right;
-
- .note {
- float: left;
- color: $colour-1;
- font-weight: bold;
- font-size: 1.25em;
- }
-
- .left {
- float: left;
- }
- }
-
- &.fill {
- @include _-(display, flex);
- @include _(flex-wrap, wrap);
-
- > button, > .button {
- flex: 1;
- margin-bottom: 0;
- }
- }
-
- .conferences-view_workshop & {
- margin-bottom: 5em;
- }
-
- &.next-prev {
- @include _-(display, flex);
- @include _(flex-wrap, wrap);
- @include _(flex-direction, row-reverse);
- margin: 0;
- clear: both;
-
- button, .button {
- margin-bottom: 1em;
- }
- }
+ text-align: center;
+ margin: 0 1em;
+
+ button, .button {
+ margin: 0 0.25em 1em;
+ vertical-align: middle;
+ }
+
+ &.left {
+ text-align: left;
+ }
+
+ &.right {
+ text-align: right;
+
+ .note {
+ float: left;
+ color: $colour-1;
+ font-weight: bold;
+ font-size: 1.25em;
+ }
+
+ .left {
+ float: left;
+ }
+ }
+
+ &.small {
+ margin-top: 0;
+ }
+
+ &.fill {
+ @include _-(display, flex);
+ @include _(flex-wrap, wrap);
+
+ > button, > .button {
+ flex: 1;
+ margin-bottom: 0;
+ }
+ }
+
+ .conferences-view_workshop & {
+ margin-bottom: 5em;
+ }
+
+ &.next-prev {
+ @include _-(display, flex);
+ @include _(flex-wrap, wrap);
+ @include _(flex-direction, row-reverse);
+ margin: 0;
+ clear: both;
+
+ button, .button {
+ margin-bottom: 1em;
+ }
+ }
+
+ &.figures {
+ .figure {
+ display: block;
+ margin: 0;
+ text-align: left;
+ }
+
+ a {
+ @include _-(display, flex);
+ @include _(flex-direction, column);
+ @include _(align-items, stretch);
+ color: inherit;
+ margin: 0.5em;
+ border: 0.1em solid $light-gray;
+ border-bottom: 0;
+ @include default-box-shadow(top, 2);
+ @include _(transition, box-shadow 150ms ease-in-out);
+
+ @include after {
+ display: none;
+ }
+
+ &:hover {
+ @include default-box-shadow(top, 0.75);
+ }
+ }
+
+ svg, img {
+ width: 5em;
+ height: 5em;
+ margin: 0.5em;
+ float: left;
+ }
+
+ svg {
+ fill: $black;
+ stroke: none;
+
+ &.stroked {
+ fill: none;
+ stroke: $black;
+ }
+ }
+
+ header {
+ @include font-family(secondary);
+ background-color: $black;
+ color: $white;
+ padding: 0.5em;
+ font-size: 1.125em;
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
+ @include _(transition, background-color 150ms ease-in-out);
+ }
+
+ .description {
+ overflow: auto;
+ padding: 1em 0.5em;
+ }
+ }
}
.test-preview {
- padding: 0 1em;
- border: 0.1em solid #CCC;
- background-color: #F8F8F8;
+ padding: 0 1em;
+ border: 0.1em solid #CCC;
+ background-color: #F8F8F8;
- h4 {
- border-bottom: 0.1rem solid #CCC;
- padding: 1em 0 0.75em;
- font-size: 1.5em;
- margin: 0 0 1em 0;
- }
+ h4 {
+ border-bottom: 0.1rem solid #CCC;
+ padding: 1em 0 0.75em;
+ font-size: 1.5em;
+ margin: 0 0 1em 0;
+ }
- h5 {
- font-size: 1.25em;
- }
+ h5 {
+ font-size: 1.25em;
+ }
- h6, p {
- font-size: 1.125em;
- }
+ h6, p {
+ font-size: 1.125em;
+ }
}
ul.warnings {
- list-style: none;
- padding: 0;
+ list-style: none;
+ padding: 0;
}
-ul.warnings li,
-.warning-info {
- position: relative;
- min-height: 4em;
- border: 0.2em solid rgba(0, 0, 0, 0.1);
- background-color: rgba($colour-3, 0.333);
- padding: 1em 1em 1em 4em;
- @include font-family(secondary);
- text-align: left;
- margin: 1em;
- width: auto;
- @include default-box-shadow(top, 2);
-
- @include before {
- content: '!';
- display: block;
- position: absolute;
- top: 0.5em;
- left: 0.5em;
- width: 1.5em;
- height: 1.5em;
- background-color: $black;
- color: $white;
- text-align: center;
- line-height: 1.5em;
- font-size: 1.5em;
- background-color: $colour-3;
- @include _(border-radius, 50%);
- @include default-box-shadow(top, 2);
- }
-
- &.make-room {
- margin-bottom: 2em;
- }
+.warning-info,
+.info-message {
+ position: relative;
+ min-height: 4em;
+ border: 0.2em solid rgba(0, 0, 0, 0.1);
+ background-color: rgba($colour-3, 0.333);
+ padding: 1em 1em 1em 4em;
+ @include font-family(secondary);
+ text-align: left;
+ margin: 1em;
+ width: auto;
+ @include default-box-shadow(top, 2);
+
+ @include before {
+ content: '!';
+ display: block;
+ position: absolute;
+ top: 0.5em;
+ left: 0.5em;
+ width: 1.5em;
+ height: 1.5em;
+ background-color: $black;
+ color: $white;
+ text-align: center;
+ line-height: 1.5em;
+ font-size: 1.5em;
+ background-color: $colour-3;
+ @include _(border-radius, 50%);
+ @include default-box-shadow(top, 2);
+ }
+
+ &.make-room {
+ margin-bottom: 2em;
+ }
+}
+
+.success-info {
+ background-color: rgba($colour-5, 0.333);
+
+ @include before {
+ background-color: $colour-5;
+ }
+}
+
+.error-info {
+ background-color: rgba($red, 0.333);
+
+ @include before {
+ background-color: $red;
+ }
+}
+
+.info-messages {
+ text-align: center;
+
+ .info-message {
+ display: inline-block;
+ }
+}
+
+.current-poster {
+ img {
+ border: 0.25em solid $colour-1;
+ }
}
.warning-info {
- display: inline-block;
- margin: 0;
- vertical-align: bottom;
+ display: inline-block;
+ margin: 0;
+ vertical-align: bottom;
}
::-webkit-resizer {
@@ -1388,3075 +1688,3323 @@ ul.warnings li,
}
@include selection {
- background-color: rgba($colour-2, 0.75);
- color: rgba($white, 0.75);
+ background-color: rgba($colour-2, 0.75);
+ color: rgba($white, 0.75);
}
#main-nav {
- @include font-family(secondary);
- overflow: hidden;
-
- .nav {
- position: fixed;
- z-index: 100;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 0;
- font-size: 6vw;
- background-color: #FFF;
- @include _-(display, flex);
- @include _(box-shadow, 0 0 2em -0.5em rgba(0, 0, 0, 0.5));
-
- a.policy {
- background-color: $colour-5;
- @include _(order, 1);
- }
-
- a.about {
- background-color: $colour-3;
- @include _(order, 1);
- }
-
- form, .register {
- background-color: $colour-2;
- }
-
- a, form {
- width: 33%;
- @include _(flex, 1);
- }
-
- a, form, button {
- text-align: center;
- padding: 0;
- margin: 0;
- }
-
- a, button {
- display: inline-block;
- font-weight: normal;
- padding: 0.5em 0;
- color: #FFF;
- @include _(border-radius, 0);
- @include _(box-shadow, inset 0 0 1.5em 0.25em rgba(0, 0, 0, 0.125));
- @include _(text-shadow, 0 0 0.25em rgba(0,0,0,0.5));
-
- @include after {
- display: none;
- }
-
- .title {
- font-size: 0.8em;
- }
- }
-
- button {
- background-color: transparent;
- border: 0;
- font-size: 1em;
- width: 100%;
- @include _(text-stroke, 0);
-
- @include before {
- display: none;
- }
- }
-
- form {
- display: inline;
- }
-
- .strlen-12, .strlen-13 {
- .title {
- font-size: 0.7em;
- }
- }
-
- .strlen-14, .strlen-15 {
- .title {
- font-size: 0.6333em;
- }
- }
-
- body.error-locale-not-available & {
- display: none;
- }
- }
-
- .logo {
- font-size: 5em;
- }
+ overflow: hidden;
+ @include font-family(secondary);
+
+ .nav {
+ position: fixed;
+ z-index: 100;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ padding: 0;
+ font-size: 6vw;
+ background-color: #FFF;
+ @include _-(display, flex);
+ @include _(box-shadow, 0 0 2em -0.5em rgba(0, 0, 0, 0.5));
+
+ a.policy {
+ background-color: $colour-5;
+ @include _(order, 1);
+ }
+
+ a.about {
+ background-color: $colour-3;
+ @include _(order, 1);
+ }
+
+ a.conferences {
+ background-color: $colour-2;
+ @include _(order, 1);
+ }
+
+ form, .register {
+ background-color: $colour-2;
+ }
+
+ a, form {
+ width: 33%;
+ @include _(flex, 1);
+ }
+
+ a, form, button {
+ text-align: center;
+ padding: 0;
+ margin: 0;
+ }
+
+ a, button {
+ display: inline-block;
+ font-weight: normal;
+ padding: 0.5em 0;
+ color: #FFF;
+ @include _(border-radius, 0);
+ @include _(box-shadow, inset 0 0 1.5em 0.25em rgba(0, 0, 0, 0.125));
+ @include _(text-shadow, 0 0 0.25em rgba(0,0,0,0.5));
+
+ @include after {
+ display: none;
+ }
+
+ .title {
+ font-size: 0.8em;
+ }
+ }
+
+ button {
+ background-color: transparent;
+ border: 0;
+ font-size: 1em;
+ width: 100%;
+ @include _(text-stroke, 0);
+
+ @include before {
+ display: none;
+ }
+ }
+
+ form {
+ display: inline;
+ }
+
+ .strlen-12, .strlen-13 {
+ .title {
+ font-size: 0.7em;
+ }
+ }
+
+ .strlen-14, .strlen-15 {
+ .title {
+ font-size: 0.6333em;
+ }
+ }
+
+ body.error-locale-not-available & {
+ display: none;
+ }
+ }
+
+ .logo {
+ font-size: 5em;
+ }
+
+ .locale-nav {
+ text-align: center;
+ }
+
+ .locales {
+ list-style: none;
+ padding: 0;
+ text-transform: uppercase;
+ line-height: 1.5em;
+ font-size: 1em;
+
+ a {
+ color: inherit;
+ padding: 0 0.333em;
+
+ &:hover {
+ @include after {
+ display: none;
+ }
+ }
+ }
+
+ li {
+ border: 0.1em solid transparent;
+ margin-bottom: 0.25em;
+ display: inline-block;
+
+ &.current {
+ border-color: #CCC;
+ background-color: #F8F8F8;
+ }
+
+ &:hover {
+ border-color: $light-gray;
+ }
+ }
+ }
}
.clearfix {
- @include clearfix;
+ @include clearfix;
}
.flow-steps {
- width: 100%;
- list-style: none;
- text-align: center;
- font-size: 0.85em;
- @include font-family(secondary);
-
- ul {
- @include _-(display, flex);
- list-style: none;
- margin: 2em 2em 5em 0;
- padding: 0;
- }
-
- li {
- position: relative;
- @include _(flex, 1);
- padding-top: 1.5em;
-
- @include after {
- content: '';
- position: absolute;
- top: 0;
- left: -50%;
- width: 100%;
- @include _(opacity, 0.5);
- border-top: 0.25em dashed #CCC;
- }
-
- &:first-child {
- @include after {
- left: 0;
- width: 50%;
- }
- }
-
- &.enabled {
- @include after {
- border-color: $colour-1;
- border-color: rgba($colour-1, 0.5);
- }
-
- &.post {
- @include after {
- border-color: $colour-1;
- border-color: rgba($colour-5, 0.5);
- }
- }
- }
-
- @include before {
- content: '';
- position: absolute;
- background-color: #CCC;
- @include _(box-shadow, 0 0 0.25rem #CCC);
- font-size: 1.3em;
- top: -0.2em;
- width: 0.55em;
- height: 0.55em;
- left: 0;
- right: 0;
- margin: auto;
- z-index: 1;
- @include _(border-radius, 50%);
- }
-
- &.enabled {
- @include after {
- border-top-style: solid;
- }
-
- @include before {
- background-color: $colour-1;
- @include _(box-shadow, 0 0 0.25rem $colour-1);
- }
-
- &.post {
- @include before {
- background-color: $colour-5;
- @include _(box-shadow, 0 0 0.25rem $colour-5);
- }
- }
-
- &.current {
-
- @include before {
- top: -0.25em;
- font-size: 3em;
- background-color: $white;
- border: 0.075em solid $colour-1;
- }
-
- &.post {
- @include before {
- border-color: $colour-5;
- }
- }
- }
- }
- }
-
- .step {
- color: #CCC;
- position: absolute;
- @include _(transform-origin, 10% 60%);
- @include _(transform, rotate(45deg));
- text-align: left;
- top: 0.5em;
- left: 50%;
- line-height: 1.2em;
- }
-
- a {
- position: static;
- color: inherit;
- @include _(color, 0.5);
- @include _(transition, color 150ms ease-in-out);
-
- @include before {
- content: '';
- position: absolute;
- top: -1em;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 1;
- }
-
- @include after {
- display: none;
- }
- }
-
- .current .step {
- //top: 1.5em;
- @include _(transform-origin, 10% 100%);
- }
-
- .current .step,
- a:hover,
- a:focus {
- color: $black;
- }
+ width: 100%;
+ list-style: none;
+ text-align: center;
+ font-size: 0.85em;
+ @include font-family(secondary);
+
+ ul {
+ @include _-(display, flex);
+ list-style: none;
+ margin: 2em 2em 5em 0;
+ padding: 0;
+ }
+
+ li {
+ position: relative;
+ @include _(flex, 1);
+ padding-top: 1.5em;
+
+ @include after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -50%;
+ width: 100%;
+ @include _(opacity, 0.5);
+ border-top: 0.25em dashed #CCC;
+ }
+
+ &:first-child {
+ @include after {
+ left: 0;
+ width: 50%;
+ }
+ }
+
+ &.enabled {
+ @include after {
+ border-color: $colour-1;
+ border-color: rgba($colour-1, 0.5);
+ }
+
+ &.post {
+ @include after {
+ border-color: $colour-1;
+ border-color: rgba($colour-5, 0.5);
+ }
+ }
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ background-color: #CCC;
+ @include _(box-shadow, 0 0 0.25rem #CCC);
+ font-size: 1.3em;
+ top: -0.2em;
+ width: 0.55em;
+ height: 0.55em;
+ left: 0;
+ right: 0;
+ margin: auto;
+ z-index: 1;
+ @include _(border-radius, 50%);
+ }
+
+ &.enabled {
+ @include after {
+ border-top-style: solid;
+ }
+
+ @include before {
+ background-color: $colour-1;
+ @include _(box-shadow, 0 0 0.25rem $colour-1);
+ }
+
+ &.post {
+ @include before {
+ background-color: $colour-5;
+ @include _(box-shadow, 0 0 0.25rem $colour-5);
+ }
+ }
+
+ &.current {
+
+ @include before {
+ top: -0.25em;
+ font-size: 3em;
+ background-color: $white;
+ border: 0.075em solid $colour-1;
+ }
+
+ &.post {
+ @include before {
+ border-color: $colour-5;
+ }
+ }
+ }
+ }
+ }
+
+ .step {
+ color: #CCC;
+ position: absolute;
+ @include _(transform-origin, 10% 60%);
+ @include _(transform, rotate(45deg));
+ text-align: left;
+ top: 0.5em;
+ left: 50%;
+ line-height: 1.2em;
+ }
+
+ a {
+ position: static;
+ color: inherit;
+ @include _(color, 0.5);
+ @include _(transition, color 150ms ease-in-out);
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: -1em;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1;
+ }
+
+ @include after {
+ display: none;
+ }
+ }
+
+ .current .step {
+ //top: 1.5em;
+ @include _(transform-origin, 10% 100%);
+ }
+
+ .current .step,
+ a:hover,
+ a:focus {
+ color: $black;
+ }
}
.link-dump {
- a {
- margin: 0.25em;
- }
+ a {
+ margin: 0.25em;
+ }
}
#main article #registration-admin-menu {
- margin: 1em 0 0;
- padding: 0;
- list-style: none;
-
- a {
- display: block;
- padding: 0.5em 0.75em;
- border: 0.1rem solid #EEE;
- border-top: 0;
- border-right: 0;
-
- @include after {
- display: none;
- }
-
- &:hover {
- background-color: #EEE;
- }
- }
-
- li {
- margin: 0;
-
- &.current {
- a {
- color: $white;
- background-color: $colour-5;
- }
- }
- }
+ margin: 1em 0 0;
+ padding: 0;
+ list-style: none;
+
+ a {
+ display: block;
+ padding: 0.5em 0.75em;
+ border: 0.1rem solid $light-gray;
+ border-top: 0;
+ border-right: 0;
+
+ @include after {
+ display: none;
+ }
+
+ &:hover {
+ background-color: $light-gray;
+ }
+ }
+
+ li {
+ margin: 0;
+
+ &.current {
+ a {
+ color: $white;
+ background-color: $colour-5;
+ }
+ }
+ }
}
.data-set {
- display: table-row;
+ display: table-row;
}
.data-set-key, .data-set-value {
- display: table-cell;
- padding: 0.25em 0.5em;
- vertical-align: top;
- border-bottom: 0.1rem solid #EEE;
+ display: table-cell;
+ padding: 0.25em 0.5em;
+ vertical-align: top;
+ border-bottom: 0.1rem solid $light-gray;
}
.data-set-key {
- font-size: 1em;
- width: 1rem;
- white-space: nowrap;
+ font-size: 1em;
+ width: 1rem;
+ white-space: nowrap;
}
.data-set:last-child {
- .data-set-key, .data-set-value {
- border: 0;
- }
+ .data-set-key, .data-set-value {
+ border: 0;
+ }
}
.details {
- display: table;
- width: 100%;
+ display: table;
+ width: 100%;
- &.inline {
- width: auto;
- }
+ &.inline {
+ width: auto;
+ }
}
.space, .address {
- .data-set-key, .data-set-value {
- white-space: nowrap;
- }
+ .data-set-key, .data-set-value {
+ white-space: nowrap;
+ }
}
.admin-blocks {
- @include _-(display, inline-flex);
- @include _(flex-wrap, wrap);
- @include _(align-items, flex-start);
- @include _(justify-content, flex-start);
- list-style: none;
- padding: 0;
-
- > li {
- max-width: 25em;
- border: 0.1rem solid #EEE;
- padding: 1em;
- margin: 0.25em;
- @include _(flex, 1);
- background-color: #F8F8F8;
- @include default-box-shadow(top, 2);
- }
-
- .actions {
- margin: 0 auto;
- }
-
- .title {
- margin: 0 0 1em;
- padding: 0 0 0.5em;
- border-bottom: 0.1rem solid #EEE;
- }
-
- .amenities {
- list-style: none;
- padding: 0;
-
- > li {
- font-weight: bold;
- white-space: nowrap;
- display: inline-block;
- padding: 0 0.5em;
- line-height: 1.5em;
- font-size: 0.8em;
- border: 0.1rem solid #CCC;
- background-color: #EEE;
- @include _(border-radius, 0.125rem);
- }
- }
+ @include _-(display, inline-flex);
+ @include _(flex-wrap, wrap);
+ @include _(align-items, flex-start);
+ @include _(justify-content, flex-start);
+ list-style: none;
+ padding: 0;
+
+ > li {
+ max-width: 25em;
+ border: 0.1rem solid $light-gray;
+ padding: 1em;
+ margin: 0.25em;
+ @include _(flex, 1);
+ background-color: #F8F8F8;
+ @include default-box-shadow(top, 2);
+ }
+
+ .actions {
+ margin: 0 auto;
+ }
+
+ .title {
+ margin: 0 0 1em;
+ padding: 0 0 0.5em;
+ border-bottom: 0.1rem solid $light-gray;
+ }
+
+ .amenities {
+ list-style: none;
+ padding: 0;
+
+ > li {
+ font-weight: bold;
+ white-space: nowrap;
+ display: inline-block;
+ padding: 0 0.5em;
+ line-height: 1.5em;
+ font-size: 0.8em;
+ border: 0.1rem solid #CCC;
+ background-color: $light-gray;
+ @include _(border-radius, 0.125rem);
+ }
+ }
}
#admin-housing, #admin-schedule {
- .guests-housed {
- margin-bottom: 1em;
- text-align: right;
-
- h5 {
- display: inline-block;
- margin: 0;
- padding-right: 0.5em;
- }
-
- .data {
- display: inline-block;
- font-size: 1.125em;
- }
- }
-
- #housing-table {
- @include _(transition, opacity 1s ease-in-out);
-
- &.loading {
- @include _(opacity, 0.5);
- pointer-events: none;
- }
-
- tr.host {
- th {
- vertical-align: top;
- }
-
- .address {
- margin-top: 1em;
- text-align: right;
- @include font-family(primary);
- }
- }
- }
-
- .host-table {
- tr.place-guest {
- td {
- background-color: lighten($colour-1, 40%);
-
- &:hover {
- background-color: $colour-1;
- }
-
- &.full {
- background-color: #E8E8E8;
-
- &:hover {
- background-color: #CCC;
- }
- }
- }
-
- a {
- display: block;
- color: $white;
- text-align: center;
- @include font-family(secondary);
-
- @include after {
- display: none;
- }
- }
- }
-
- .remove-guest,
- button, .button {
- float: right;
- }
-
- td {
- vertical-align: top;
- }
-
- .state {
- position: relative;
- font-family: inherit;
-
- &.unhappy {
- cursor: pointer;
- }
-
- ul {
- display: none;
- position: absolute;
- right: 100%;
- top: 0;
- background-color: $white;
- border: 0.1em solid #CCC;
- padding: 0.25em 0.75em 0.25em 1.5em;
- margin: 0;
- list-style-type: square;
- @include default-box-shadow(top, 2);
- z-index: 10;
- }
-
- li {
- white-space: nowrap;
- margin: 0;
- }
-
- &:hover {
- ul {
- display: block;
- }
- }
- }
- }
-
- #workshop-selector {
- td, th {
- white-space: nowrap;
- }
-
- td .text {
- max-width: 50em;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- }
-
- #guest-selector, #workshop-selector {
- display: none;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: rgba($black, 0.5);
- cursor: pointer;
- z-index: 1000;
-
- &.open {
- display: block;
- }
-
- .guest-dlg, .workshop-dlg {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: $white;
- width: 80%;
- margin: auto;
- padding: 1em;
- height: 80%;
- cursor: default;
- @include default-box-shadow(top, 2);
-
- h3 {
- margin: 0 0 1em;
- }
- }
- }
+ .guests-housed {
+ margin-bottom: 1em;
+ text-align: right;
+
+ h5 {
+ display: inline-block;
+ margin: 0;
+ padding-right: 0.5em;
+ }
+
+ .data {
+ display: inline-block;
+ font-size: 1.125em;
+ }
+ }
+
+ #housing-table {
+ @include _(transition, opacity 1s ease-in-out);
+
+ &.loading {
+ @include _(opacity, 0.5);
+ pointer-events: none;
+ }
+
+ tr.host {
+ th {
+ vertical-align: top;
+ }
+
+ .address {
+ margin-top: 1em;
+ text-align: right;
+ @include font-family(primary);
+ }
+ }
+ }
+
+ .host-table {
+ tr.place-guest {
+ td {
+ background-color: lighten($colour-1, 40%);
+
+ &:hover {
+ background-color: $colour-1;
+ }
+
+ &.full {
+ background-color: $gray;
+
+ &:hover {
+ background-color: #CCC;
+ }
+ }
+ }
+
+ a {
+ display: block;
+ color: $white;
+ text-align: center;
+ @include font-family(secondary);
+
+ @include after {
+ display: none;
+ }
+ }
+ }
+
+ .remove-guest,
+ button, .button {
+ float: right;
+ }
+
+ td {
+ vertical-align: top;
+ }
+
+ .state {
+ position: relative;
+ font-family: inherit;
+
+ &.unhappy {
+ cursor: pointer;
+ }
+
+ ul {
+ display: none;
+ position: absolute;
+ right: 100%;
+ top: 0;
+ background-color: $white;
+ border: 0.1em solid #CCC;
+ padding: 0.25em 0.75em 0.25em 1.5em;
+ margin: 0;
+ list-style-type: square;
+ @include default-box-shadow(top, 2);
+ z-index: 10;
+ }
+
+ li {
+ white-space: nowrap;
+ margin: 0;
+ }
+
+ &:hover {
+ ul {
+ display: block;
+ }
+ }
+ }
+ }
+
+ #workshop-selector {
+ td, th {
+ white-space: nowrap;
+ }
+
+ td .text {
+ max-width: 50em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ #guest-selector, #workshop-selector {
+ display: none;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: rgba($black, 0.5);
+ cursor: pointer;
+ z-index: 1000;
+
+ &.open {
+ display: block;
+ }
+
+ .guest-dlg, .workshop-dlg {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: $white;
+ width: 80%;
+ margin: auto;
+ padding: 1em;
+ height: 80%;
+ cursor: default;
+ @include default-box-shadow(top, 2);
+
+ h3 {
+ margin: 0 0 1em;
+ }
+ }
+ }
}
@include keyframes(whiten) {
- to { background-color: $white; }
+ to { background-color: $white; }
}
#table {
- position: relative;
- overflow: auto;
- height: 80%;
- height: calc(100% - 4em);
- background-color: $white;
- @include _(transition, background-color 250ms ease-in-out);
-
- &.loading {
- background-color: #CCC;
- @include _(animation, whiten ease-in-out 1s infinite alternate both);
-
- .host-field, table, .legend {
- display: none;
- }
- }
-
- table {
- margin: 0 0 2em;
- }
-
- h4 {
- margin: 0;
-
- &.inline {
- display: inline-block;
- padding-right: 0.5em;
- }
- }
-
- .plain-value {
- font-size: 1.2em;
- }
-
- blockquote {
- margin-top: 0;
- font-size: 0.85em;
-
- > :first-child {
- margin-top: 0;
- }
- }
-
- tr.selectable {
- cursor: pointer;
-
- &:hover {
- th {
- background-color: $colour-2;
- }
- }
-
- &.other-host, &.other-space, &.bad-match {
- td, th {
- opacity: 0.5;
- }
- }
-
- &.selected-space, &.other-space {
- td {
- background-color: lighten($colour-5, 35);
- }
-
- th {
- background-color: $colour-5;
- }
- }
-
- &.other-host {
- td {
- background-color: lighten($colour-1, 40%);
- }
-
- th {
- background-color: $colour-1;
- }
- }
-
- &:hover {
- td {
- opacity: 1;
- background-color: lighten($colour-2, 25%);
- }
- }
- }
-
- .legend ul {
- @include _-(display, flex);
- list-style: none;
- padding: 0;
-
- li {
- @include _(flex, 1);
- text-align: center;
- margin-bottom: 0.5em;
- padding: 0.125em 0.5em;
- margin: 0.1em;
- border: 0.1em solid #EEE;
- background-color: #F8F8F8;
- @include font-family(secondary);
-
- &.other-host, &.other-space, &.bad-match {
- opacity: 0.5;
- }
-
- &.selected-space, &.other-space {
- background-color: $colour-5;
- }
-
- &.other-host {
- background-color: $colour-1;
- }
- }
- }
-
- .p {
- max-height: 4em;
- overflow: auto;
- }
+ position: relative;
+ overflow: auto;
+ height: 80%;
+ height: calc(100% - 4em);
+ background-color: $white;
+ @include _(transition, background-color 250ms ease-in-out);
+
+ &.loading {
+ background-color: #CCC;
+ @include _(animation, whiten ease-in-out 1s infinite alternate both);
+
+ .host-field, table, .legend {
+ display: none;
+ }
+ }
+
+ table {
+ margin: 0 0 2em;
+ }
+
+ h4 {
+ margin: 0;
+
+ &.inline {
+ display: inline-block;
+ padding-right: 0.5em;
+ }
+ }
+
+ .plain-value {
+ font-size: 1.2em;
+ }
+
+ blockquote {
+ margin-top: 0;
+ font-size: 0.85em;
+
+ > :first-child {
+ margin-top: 0;
+ }
+ }
+
+ tr.selectable {
+ cursor: pointer;
+
+ &:hover {
+ th {
+ background-color: $colour-2;
+ }
+ }
+
+ &.other-host, &.other-space, &.bad-match {
+ td, th {
+ opacity: 0.5;
+ }
+ }
+
+ &.selected-space, &.other-space {
+ td {
+ background-color: lighten($colour-5, 35);
+ }
+
+ th {
+ background-color: $colour-5;
+ }
+ }
+
+ &.other-host {
+ td {
+ background-color: lighten($colour-1, 40%);
+ }
+
+ th {
+ background-color: $colour-1;
+ }
+ }
+
+ &:hover {
+ td {
+ opacity: 1;
+ background-color: lighten($colour-2, 25%);
+ }
+ }
+ }
+
+ .legend ul {
+ @include _-(display, flex);
+ list-style: none;
+ padding: 0;
+
+ li {
+ @include _(flex, 1);
+ text-align: center;
+ margin-bottom: 0.5em;
+ padding: 0.125em 0.5em;
+ margin: 0.1em;
+ border: 0.1em solid $light-gray;
+ background-color: #F8F8F8;
+ @include font-family(secondary);
+
+ &.other-host, &.other-space, &.bad-match {
+ opacity: 0.5;
+ }
+
+ &.selected-space, &.other-space {
+ background-color: $colour-5;
+ }
+
+ &.other-host {
+ background-color: $colour-1;
+ }
+ }
+ }
+
+ .p {
+ max-height: 4em;
+ overflow: auto;
+ }
}
#admin-housing {
- #table table {
- min-width: 100em;
- }
-
- #hosts {
- background-color: $white;
-
- ul {
- list-style: none;
- padding: 0;
- }
-
- .host {
- padding: 0.5em;
- border: 0.1em solid #EEE;
- background-color: #F8F8F8;
-
- &.status-good {
- background-color: rgba($colour-5, 0.1);
- }
-
- &.status-warning {
- background-color: rgba($colour-3, 0.1);
- }
-
- &.status-error {
- background-color: rgba($colour-2, 0.1);
- }
- }
-
- h4 {
- margin: 0 0.5em 0 0;
- float: left;
- }
-
- h5 {
- margin: 1em 0 0;
- }
-
- .email {
- color: #888;
-
- @include before {
- content: '(';
- }
-
- @include after {
- content: ')';
- }
- }
-
- .guests {
- clear: left;
- @include _-(display, flex);
- }
-
- .space {
- @include _(flex, 1);
- }
-
- .place-guest {
- background-color: $colour-5;
-
- &.booked {
- background-color: $colour-4;
- }
-
- &.unwanted {
- background-color: #888;
- }
-
- &.overbooked {
- background-color: $colour-2;
- }
- }
-
- .warning, .error {
- display: inline-block;
- font-style: italic;
- padding: 0 0.5em;
- margin-left: 0.5em;
- border: 0.1em solid rgba($black, 0.25);
- @include _(border-radius, 0.125em);
- @include _(transform, skewX(-15deg));
- @include _(transform-origin, 0 100%);
- @include _(animation, bend ease-in-out 500ms infinite alternate both);
- }
-
- .warning {
- background-color: lighten($colour-3, 25%);
- }
-
- .error {
- background-color: lighten($colour-2, 25%);
- }
- }
-
- #guests {
- .guests {
- @include _-(display, flex);
- //@include _(align-items, flex-start);
- @include _(flex-wrap, wrap);
- list-style: none;
- padding: 0;
- }
-
- .guest {
- flex: 1;
- margin: 0.5em;
- border: 0.1rem solid #EEE;
- background-color: #F8F8F8;
- padding: 0.5em;
- }
-
- h4 {
- margin: 0 0 0.5em;
- }
-
- .set-host {
- width: 100%;
- }
- }
+ #table table {
+ min-width: 100em;
+ }
+
+ #hosts {
+ background-color: $white;
+
+ ul {
+ list-style: none;
+ padding: 0;
+ }
+
+ .host {
+ padding: 0.5em;
+ border: 0.1em solid $light-gray;
+ background-color: #F8F8F8;
+
+ &.status-good {
+ background-color: rgba($colour-5, 0.1);
+ }
+
+ &.status-warning {
+ background-color: rgba($colour-3, 0.1);
+ }
+
+ &.status-error {
+ background-color: rgba($colour-2, 0.1);
+ }
+ }
+
+ h4 {
+ margin: 0 0.5em 0 0;
+ float: left;
+ }
+
+ h5 {
+ margin: 1em 0 0;
+ }
+
+ .email {
+ color: #888;
+
+ @include before {
+ content: '(';
+ }
+
+ @include after {
+ content: ')';
+ }
+ }
+
+ .guests {
+ clear: left;
+ @include _-(display, flex);
+ }
+
+ .space {
+ @include _(flex, 1);
+ }
+
+ .place-guest {
+ background-color: $colour-5;
+
+ &.booked {
+ background-color: $colour-4;
+ }
+
+ &.unwanted {
+ background-color: #888;
+ }
+
+ &.overbooked {
+ background-color: $colour-2;
+ }
+ }
+
+ .warning, .error {
+ display: inline-block;
+ font-style: italic;
+ padding: 0 0.5em;
+ margin-left: 0.5em;
+ border: 0.1em solid rgba($black, 0.25);
+ @include _(border-radius, 0.125em);
+ @include _(transform, skewX(-15deg));
+ @include _(transform-origin, 0 100%);
+ @include _(animation, bend ease-in-out 500ms infinite alternate both);
+ }
+
+ .warning {
+ background-color: lighten($colour-3, 25%);
+ }
+
+ .error {
+ background-color: lighten($colour-2, 25%);
+ }
+ }
+
+ #guests {
+ .guests {
+ @include _-(display, flex);
+ //@include _(align-items, flex-start);
+ @include _(flex-wrap, wrap);
+ list-style: none;
+ padding: 0;
+ }
+
+ .guest {
+ flex: 1;
+ margin: 0.5em;
+ border: 0.1rem solid $light-gray;
+ background-color: #F8F8F8;
+ padding: 0.5em;
+ }
+
+ h4 {
+ margin: 0 0 0.5em;
+ }
+
+ .set-host {
+ width: 100%;
+ }
+ }
}
#admin-workshop_times {
- .workshop-blocks {
- td, th {
- vertical-align: top;
- }
- }
+ .workshop-blocks {
+ margin: 0 auto;
- .table-tr.new {
- .table-th {
- background-color: transparent;
- border: 0;
- }
+ td, th {
+ vertical-align: top;
+ }
+ }
- .table-td {
- background-color: transparent;
- border: 0;
- }
- }
+ .table-tr.new {
+ .table-th {
+ background-color: transparent;
+ border: 0;
+ }
+
+ .table-td {
+ background-color: transparent;
+ border: 0;
+ }
+ }
}
.details.org-members {
- padding: 1em;
- border: 0.1rem solid #EEE;
- border-bottom: 0;
+ padding: 1em;
+ border: 0.1rem solid $light-gray;
+ border-bottom: 0;
}
#schedule-preview {
- overflow: auto;
+ overflow: auto;
}
table.schedule {
- width: 100%;
- margin: 0 0 1em;
-
- tbody {
- border: 0.1rem solid #EEE;
- }
-
- &.locations-1 td.workshop.filled {
- width: 100%;
- }
-
- &.locations-2 td.workshop.filled {
- width: 50%;
- }
-
- &.locations-3 td.workshop.filled {
- width: 33.333%;
- }
-
- &.locations-4 td.workshop.filled {
- width: 25%;
- }
-
- &.locations-5 td.workshop.filled {
- width: 20%;
- }
-
- &.locations-6 td.workshop.filled {
- width: 16.66667%;
- }
- td {
- text-align: center;
- border: 0;
- background-color: #F8F8F8;
-
- &.workshop {
-
- &.filled {
- background-color: lighten($colour-1, 25%);
- border: 0.1rem solid #EEE;
- }
-
- &.open {
- #admin-schedule & {
- background-color: lighten($colour-1, 40%);
- cursor: pointer;
- white-space: nowrap;
- border: 0.1rem solid #EEE;
-
- &:hover {
- background-color: lighten($colour-1, 25%);
- }
- }
- }
- }
-
- &.event {
- background-color: lighten($colour-2, 25%);
- border: 0.1rem solid #EEE;
- }
-
- &.meal {
- background-color: lighten($colour-3, 25%);
- border: 0.1rem solid #EEE;
- }
-
- .title {
- @include font-family(secondary);
- }
-
- .event-detail-link {
- display: inline-block;
- display: inline-flex;
- position: relative;
- height: 100%;
- width: 100%;
- color: inherit;
- align-items: center;
-
- @include after {
- display: none;
- }
-
- .details {
- flex: 1;
- }
- }
-
- .status {
- display: inline-block;
- text-align: left;
- float: right;
- font-size: 0.9em;
- margin-top: 0.5em;
- }
-
- .conflict-score {
- text-align: right;
-
- .title {
- @include font-family(secondary);
- }
- }
-
- .errors {
- position: relative;
- border-top: 0.1rem solid #666;
- margin-top: 0.5em;
- padding-top: 0.25em;
-
- @include before {
- content: '!';
- display: inline-block;
- position: absolute;
- z-index: 1;
- top: 0;
- left: -1.2em;
- width: 1em;
- color: $white;
- @include font-family(secondary);
- }
-
- @include after {
- content: '';
- position: absolute;
- top: 0.1em;
- left: -1.333em;
- width: 0;
- height: 0;
- font-size: 1.25em;
- border: 0.5em solid;
- border-left-color: transparent;
- border-right-color: transparent;
- border-width: 0 0.5em 0.75em;
- color: darken($colour-2, 25%);
- }
- }
-
- #main .columns & form {
- margin-top: 0;
-
- button {
- margin-top: 0.5em;
- float: left;
- z-index: 1;
- }
- }
- }
+ width: 100%;
+ margin: 0 0 1em;
+
+ tbody {
+ border: 0.1rem solid $light-gray;
+ }
+
+ &.locations-1 td.workshop.filled {
+ width: 100%;
+ }
+
+ &.locations-2 td.workshop.filled {
+ width: 50%;
+ }
+
+ &.locations-3 td.workshop.filled {
+ width: 33.333%;
+ }
+
+ &.locations-4 td.workshop.filled {
+ width: 25%;
+ }
+
+ &.locations-5 td.workshop.filled {
+ width: 20%;
+ }
+
+ &.locations-6 td.workshop.filled {
+ width: 16.66667%;
+ }
+ td {
+ text-align: center;
+ border: 0;
+ background-color: #F8F8F8;
+
+ &.workshop {
+
+ &.filled {
+ background-color: lighten($colour-1, 25%);
+ border: 0.1rem solid $light-gray;
+ }
+
+ &.open {
+ #admin-schedule & {
+ background-color: lighten($colour-1, 40%);
+ cursor: pointer;
+ white-space: nowrap;
+ border: 0.1rem solid $light-gray;
+
+ &:hover {
+ background-color: lighten($colour-1, 25%);
+ }
+ }
+ }
+ }
+
+ &.event {
+ background-color: lighten($colour-2, 25%);
+ border: 0.1rem solid $light-gray;
+ }
+
+ &.meal {
+ background-color: lighten($colour-3, 25%);
+ border: 0.1rem solid $light-gray;
+ }
+
+ .title {
+ @include font-family(secondary);
+ }
+
+ .event-detail-link {
+ display: inline-block;
+ display: inline-flex;
+ position: relative;
+ height: 100%;
+ width: 100%;
+ color: inherit;
+ align-items: center;
+
+ @include after {
+ display: none;
+ }
+
+ .details {
+ flex: 1;
+ }
+ }
+
+ .status {
+ display: inline-block;
+ text-align: left;
+ float: right;
+ font-size: 0.9em;
+ margin-top: 0.5em;
+ }
+
+ .conflict-score {
+ text-align: right;
+
+ .title {
+ @include font-family(secondary);
+ }
+ }
+
+ .errors {
+ position: relative;
+ border-top: 0.1rem solid #666;
+ margin-top: 0.5em;
+ padding-top: 0.25em;
+
+ @include before {
+ content: '!';
+ display: inline-block;
+ position: absolute;
+ z-index: 1;
+ top: 0;
+ left: -1.2em;
+ width: 1em;
+ color: $white;
+ @include font-family(secondary);
+ }
+
+ @include after {
+ content: '';
+ position: absolute;
+ top: 0.1em;
+ left: -1.333em;
+ width: 0;
+ height: 0;
+ font-size: 1.25em;
+ border: 0.5em solid;
+ border-left-color: transparent;
+ border-right-color: transparent;
+ border-width: 0 0.5em 0.75em;
+ color: darken($colour-2, 25%);
+ }
+ }
+
+ #main .columns & form {
+ margin-top: 0;
+
+ button {
+ margin-top: 0.5em;
+ float: left;
+ z-index: 1;
+ }
+ }
+ }
}
body.home table.schedule td .event-detail-link:hover {
- outline: 0.33rem solid #02CA9E;
- outline-offset: 0.25rem;
- z-index: 1;
+ outline: 0.33rem solid #02CA9E;
+ outline-offset: 0.25rem;
+ z-index: 1;
}
.event-dlg {
- @include _-(flex, 0);
- position: relative;
- z-index: 1000;
- background-color: $white;
- text-align: left;
- padding: 2em 2em 0.5em;
- min-width: 100%;
- max-height: 100%;
- overflow: auto;
- margin: auto;
- @include _(opacity, 0);
- @include _(transition, opacity 150ms ease-in-out);
- @include default-box-shadow(top, 2);
-
- .actions {
- margin: 2em 0 0;
- }
-
- &.open {
- @include _(opacity, 1);
- }
+ @include _-(flex, 0);
+ position: relative;
+ z-index: 1000;
+ background-color: $white;
+ text-align: left;
+ padding: 2em 2em 0.5em;
+ min-width: 100%;
+ max-height: 100%;
+ overflow: auto;
+ margin: auto;
+ @include _(opacity, 0);
+ @include _(transition, opacity 150ms ease-in-out);
+ @include default-box-shadow(top, 2);
+
+ .actions {
+ margin: 2em 0 0;
+ }
+
+ &.open {
+ @include _(opacity, 1);
+ }
}
.event-details {
- .title {
- margin: 0 0 0.5em;
- }
+ .title {
+ margin: 0 0 0.5em;
+ }
- .address {
- text-align: center;
- }
+ .address {
+ text-align: center;
+ }
}
#admin-schedule {
- .workshops-to-schedule {
- @include _-(display, flex);
- @include _(flex-wrap, wrap);
- list-style: none;
- padding: 0;
-
- li {
- @include _(flex, 1);
- @include _(flex-basis, 48%);
- margin: 1%;
- @include _(transition, background-color 250ms ease-in-out);
- background-color: #EEE;
-
- .title {
- @include _(transition, background-color 250ms ease-in-out);
- }
-
- &.booked {
- background-color: lighten($colour-1, 40%);
-
- .not-booked-only {
- display: none;
- }
-
- .data-set-key, .data-set-value {
- border-bottom-color: #888;
- }
- }
-
- &.not-booked {
-
- .booked-only {
- display: none;
- }
- }
-
- .field-error {
- background-color: lighten($colour-2, 22.5%);
- margin: 0;
- }
-
- .already-booked {
- overflow: hidden;
- max-height: 0;
-
- &.is-true {
- max-height: 3em;
- @include _(transition, max-height 150ms ease-in-out);
- }
- }
-
- .workshop-description {
- max-height: none;
- }
-
- .details {
- margin: 0 5% 1em;
- width: 90%;
- }
-
- .title {
- margin: 0 0 0.5em;
- padding: 0.5em;
- background-color: $black;
- color: $white;
- }
-
- .drop-downs {
- padding: 0 1em 1em;
-
- select {
- width: 100%;
- }
- }
-
- .actions {
- margin: 0;
- padding: 0 0.5em;
- }
- }
-
- #main .columns & form {
- margin: 0;
- }
- }
+ .workshops-to-schedule {
+ @include _-(display, flex);
+ @include _(flex-wrap, wrap);
+ list-style: none;
+ padding: 0;
+
+ li {
+ @include _(flex, 1);
+ @include _(flex-basis, 48%);
+ margin: 1%;
+ @include _(transition, background-color 250ms ease-in-out);
+ background-color: $light-gray;
+
+ .title {
+ @include _(transition, background-color 250ms ease-in-out);
+ }
+
+ &.booked {
+ background-color: lighten($colour-1, 40%);
+
+ .not-booked-only {
+ display: none;
+ }
+
+ .data-set-key, .data-set-value {
+ border-bottom-color: #888;
+ }
+ }
+
+ &.not-booked {
+
+ .booked-only {
+ display: none;
+ }
+ }
+
+ .field-error {
+ background-color: lighten($colour-2, 22.5%);
+ margin: 0;
+ }
+
+ .already-booked {
+ overflow: hidden;
+ max-height: 0;
+
+ &.is-true {
+ max-height: 3em;
+ @include _(transition, max-height 150ms ease-in-out);
+ }
+ }
+
+ .workshop-description {
+ max-height: none;
+ }
+
+ .details {
+ margin: 0 5% 1em;
+ width: 90%;
+ }
+
+ .title {
+ margin: 0 0 0.5em;
+ padding: 0.5em;
+ background-color: $black;
+ color: $white;
+ }
+
+ .drop-downs {
+ padding: 0 1em 1em;
+
+ select {
+ width: 100%;
+ }
+ }
+
+ .actions {
+ margin: 0;
+ padding: 0 0.5em;
+ }
+ }
+
+ #main .columns & form {
+ margin: 0;
+ }
+ }
}
#main {
- position: relative;
- background-color: $white;
- padding-bottom: rems(2);
- @include _(flex, 1);
-
- article {
- padding: rems(2.5) 0;
- margin: 0 1em;
-
- &.supplementary {
- margin: rems(1) 7.5%;
- overflow: hidden;
- @include _(border-radius, 0.33em);
- border: 0.1rem solid #DDD;
- background-color: #F7F7F7;
- @include default-box-shadow(top, 2);
- }
-
- ul {
- font-size: 1.125em;
- line-height: 1.5em;
- }
-
- li {
- margin-bottom: 0.5em;
- }
- }
-
- .featured-image-container {
- @include _-(display, flex);
- @include _(flex-wrap, wrap);
-
- figure {
- @include _(order, 1);
- width: 100%;
- height: 12em;
- margin: 0;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
- }
-
- ul {
- clear: left;
- }
- }
+ position: relative;
+ background-color: $white;
+ padding-bottom: rems(2);
+ @include _(flex, 1);
+
+ article {
+ padding: rems(2.5) 0;
+ margin: 0 1em;
+
+ &.supplementary {
+ margin: rems(1) 7.5%;
+ overflow: hidden;
+ @include _(border-radius, 0.33em);
+ border: 0.1rem solid #DDD;
+ background-color: #F7F7F7;
+ @include default-box-shadow(top, 2);
+ }
+
+ ul {
+ font-size: 1.125em;
+ line-height: 1.5em;
+ }
+
+ li {
+ margin-bottom: 0.5em;
+ }
+ }
+
+ .featured-image-container {
+ @include _-(display, flex);
+ @include _(flex-wrap, wrap);
+
+ figure {
+ @include _(order, 1);
+ width: 100%;
+ height: 12em;
+ margin: 0;
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+ }
+
+ ul {
+ clear: left;
+ }
+ }
}
a.logo {
- display: inline-block;
- font-size: rems(5);
- text-decoration: none;
- padding: 0.1em;
- border: 0;
- float: left;
- overflow: auto;
- @include clearfix;
-
- &:hover,
- &:active,
- &:focus {
- @include after {
- display: none;
- }
- }
+ display: inline-block;
+ font-size: rems(5);
+ text-decoration: none;
+ padding: 0.1em;
+ border: 0;
+ float: left;
+ overflow: auto;
+ @include clearfix;
+
+ &:hover,
+ &:active,
+ &:focus {
+ @include after {
+ display: none;
+ }
+ }
}
@if capable_of(svg) {
- @include keyframes(active-logo) {
- 0% { fill: $colour-1; }
- 15% { fill: $colour-5; }
- 30% { fill: $colour-3; }
- 45% { fill: $colour-4; }
- 60% { fill: $colour-2; }
- }
+ @include keyframes(active-logo) {
+ 0% { fill: $colour-1; }
+ 15% { fill: $colour-5; }
+ 30% { fill: $colour-3; }
+ 45% { fill: $colour-4; }
+ 60% { fill: $colour-2; }
+ }
- @include keyframes(banner-load) {
- 0% { fill: #888; }
- }
+ @include keyframes(banner-load) {
+ 0% { fill: #888; }
+ }
- svg.bb-icon-logo {
- fill: $colour-1;
- }
- svg.bb-icon-logo-text {
- fill: $black;
- }
+ svg.bb-icon-logo {
+ fill: $colour-1;
+ }
+ svg.bb-icon-logo-text {
+ fill: $black;
+ }
- .logo:hover svg.bb-icon-logo {
- @include _(animation, active-logo 4s infinite);
- }
+ .logo:hover svg.bb-icon-logo {
+ @include _(animation, active-logo 4s infinite);
+ }
}
.logo .icons {
- display: inline-block;
- height: 1em;
- width: 1em;
- float: left;
+ display: inline-block;
+ height: 1em;
+ width: 1em;
+ float: left;
}
#banner {
- clear: left;
- float: none;
- margin: 1em auto 0;
- text-align: center;
+ clear: left;
+ float: none;
+ margin: 1em auto 0;
+ text-align: center;
- body.home & {
- max-width: 40em;
- }
+ figure {
+ position: relative;
+ width: 100%;
+ max-width: rems(60);
+ margin: 0;
+ }
- figure {
- position: relative;
- width: 100%;
- max-width: rems(60);
- margin: 0;
- }
+ img {
+ width: 100%;
+ }
+}
- img {
- width: 100%;
- }
+.conference-banner {
+ text-align: center;
- .title {
- font-size: 5vw;
- margin: 1em auto 2em;
+ .title {
+ font-size: 5vw;
+ margin: 1em auto 2em;
- h1, h2 {
- margin: 0;
- }
+ h1, h2 {
+ margin: 0;
+ }
- h2 {
- font-size: 1.15em;
- }
- }
+ h2 {
+ font-size: 1.15em;
+ }
+ }
- .secondary {
- font-size: 0.85em;
- line-height: 2em;
- }
+ .secondary {
+ font-size: 0.85em;
+ line-height: 2em;
+ }
+}
+
+.conference-details {
+ .links {
+ text-align: center;
+ font-size: 1.25em;
+ margin-bottom: 2em;
+
+ .button {
+ margin: 0 0.25em;
+ }
+ }
}
$header-tilt: 8deg;
#header-title {
- font-size: 2.25vw;
- background-size: cover;
- background-position: 50% 20%;
- background-repeat: no-repeat;
- background-color: $colour-1;
- color: #EEE;
- overflow: hidden;
- position: relative;
- z-index: $zindex-base;
-
- .row, .columns {
- position: static;
- }
-
- rect {
- fill: $colour-1;
- }
-
- svg {
- .colour-1 { fill: $colour-1; }
- .colour-2 { fill: $colour-2; }
- .colour-3 { fill: $colour-3; }
- .colour-4 { fill: $colour-4; }
- .colour-5 { fill: $colour-5; }
- }
-
- .cover {
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: $zindex-base;
- background-size: inherit;
- background-position: inherit;
- }
-
- @if capable_of(css-mixblendmode) {
- position: relative;
-
- .cover {
- -webkit-filter: saturate(25%);
- filter: saturate(25%);
- @include _(mix-blend-mode, multiply);
- }
- }
-
- &.no-image,
- &.short {
- h1 {
- text-align: center;
- font-size: 3.5em;
- margin: 1.75em 0 .75em;
- position: relative;
- z-index: $zindex-base + 2;
- @include _(text-shadow, 0 0 0.15em #000);
- }
- }
-
- svg {
- display: none;
- position: absolute;
- width: 100%;
- height: 100%;
- }
-
- .title {
- position: relative;
- padding: 1%;
- overflow: hidden;
- text-align: center;
- @include _(text-shadow, 0 0.2em 0.5em rgba(32, 32, 32, 0.5))
- }
-
- .background {
- position: absolute;
- @include _(transform, rotate($header-tilt));
- color: $colour-1;
- @include _(opacity, 0.65);
- margin: -4% -2%;
- @if capable_of(calc) {
- font-size: calc(5vw + #{rems(7)});
- } @else {
- font-size: rems(11);
- }
- display: none;
- }
-
- .details {
- position: relative;
- display: inline-block;
- color: $white;
- @include _(text-shadow, 0 0.2em 0.5em rgba(32, 32, 32, 0.75));
- }
-
- .primary {
- font-size: 2.5em;
- margin: 0;
- }
-
- .secondary {
- font-size: 1.5em;
- }
-
- body.error-locale-not-available & {
- background-color: $colour-5;
- }
-
- body.error-500 & {
- background-position: 50% 50%;
- background-color: $colour-2;
- }
-
- body.error-403 & {
- background-position: 50% 33%;
- }
+ font-size: 2.25vw;
+ background-size: cover;
+ background-position: 50% 20%;
+ background-repeat: no-repeat;
+ background-color: $colour-1;
+ color: $light-gray;
+ overflow: hidden;
+ position: relative;
+ z-index: $zindex-base;
+
+ .row, .columns {
+ position: static;
+ }
+
+ rect {
+ fill: $colour-1;
+ }
+
+ .cover {
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $zindex-base;
+ background-size: inherit;
+ background-position: inherit;
+ }
+
+ @if capable_of(css-mixblendmode) {
+ position: relative;
+
+ .cover {
+ -webkit-filter: saturate(25%);
+ filter: saturate(25%);
+ @include _(mix-blend-mode, multiply);
+ }
+ }
+
+ &.no-image,
+ &.short {
+ h1 {
+ text-align: center;
+ font-size: 3.5em;
+ margin: 1.75em 0 .75em;
+ position: relative;
+ z-index: $zindex-base + 2;
+ @include _(text-shadow, 0 0 0.15em #000);
+ }
+ }
+
+ &.map {
+ background-position: 0 0;
+ background-repeat: repeat;
+ background-size: 30px 30px;
+ @include _(animation, barberpole 500ms linear infinite);
+ background-image: linear-gradient(
+ 45deg,
+ rgba($white, 0.1) 25%,
+ transparent 25%,
+ transparent 50%,
+ rgba($white, 0.1) 50%,
+ rgba($white, 0.1) 75%,
+ transparent 75%,
+ transparent
+ );
+
+ svg {
+ vertical-align: middle;
+ background-color: transparent;
+ @include _(transition, background-color 1s ease-in-out);
+
+ &.loaded {
+ background-color: $colour-1;
+ }
+ }
+
+ .country {
+ fill: $colour-5;
+ stroke: rgba($black, 0.02);
+ stroke-width: 1px;
+ }
+
+ .cities {
+ fill: $colour-4;
+ opacity: 0.75;
+
+ .city {
+ opacity: 0.9;
+ cursor: pointer;
+ @include _(animation, fade-out ease-in-out 3s infinite alternate both);
+
+ &:hover {
+ fill: $colour-3;
+ stroke: rgba($white, 0.125);
+ stroke-width: 6px;
+ }
+ }
+
+ &.annual-conferences {
+ fill: $colour-2;
+ }
+ }
+
+ #tooltip {
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ opacity: 0;
+ background-color: rgba($black, 0.25);
+ padding: 0.5em 0;
+ pointer-events: none;
+ text-shadow: 0 0 0.5em $black;
+ @include _(transition, opacity 250ms ease-in-out);
+
+ &.open {
+ opacity: 1;
+ }
+
+ h3 {
+ margin: 0;
+ padding-right: 0.5em;
+ }
+
+ .conference-details {
+ margin: 0.25em 0;
+
+ div {
+ display: inline-block;
+
+ @include after {
+ content: '|';
+ margin: 0 0.75em;
+ }
+
+ &:last-child {
+ @include after {
+ display: none;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ .title {
+ position: relative;
+ padding: 1%;
+ overflow: hidden;
+ text-align: center;
+ @include _(text-shadow, 0 0.2em 0.5em rgba(32, 32, 32, 0.5))
+ }
+
+ .background {
+ position: absolute;
+ @include _(transform, rotate($header-tilt));
+ color: $colour-1;
+ @include _(opacity, 0.65);
+ margin: -4% -2%;
+ @if capable_of(calc) {
+ font-size: calc(5vw + #{rems(7)});
+ } @else {
+ font-size: rems(11);
+ }
+ display: none;
+ }
+
+ .details {
+ position: relative;
+ display: inline-block;
+ color: $white;
+ @include _(text-shadow, 0 0.2em 0.5em rgba(32, 32, 32, 0.75));
+ }
+
+ .primary {
+ font-size: 2.5em;
+ margin: 0;
+ }
+
+ .secondary {
+ font-size: 1.5em;
+ }
+
+ body.error-locale-not-available & {
+ background-color: $colour-5;
+ }
+
+ body.error-500 & {
+ background-position: 50% 50%;
+ background-color: $colour-2;
+ }
+
+ body.error-403 & {
+ background-position: 50% 33%;
+ }
+
+ body.admin & {
+ background-color: $colour-4;
+ }
+
+ body.banner-bottom & {
+ background-position: center bottom;
+ }
}
#footer {
- padding: 1em;
-
- footer {
- @include _-(display, flex);
- @include _(flex-flow, row wrap);
- @include _(align-items, flex-end);
- font-size: 4.1vw;
- }
-
- .site-info, .user-controls, .external {
- width: 100%;
- text-align: center;
-
- a {
- text-align: right;
- white-space: nowrap;
- color: #666;
- }
- }
-
- .external {
- margin-bottom: 1em;
- }
-
- .my-account {
- margin-right: 0.5em;
- }
-
- .logout {
- margin-left: 0.5em;
- }
-
- .contact-us {
- line-height: 2.5em;
- }
-
- .facebook {
- a {
- &:hover, &:active, &:focus {
- svg {
- fill: #3B579D;
- }
- }
- }
- }
-
- .github, .facebook {
- display: inline-block;
- margin: 0.5em;
-
- a {
- @include after {
- display: none;
- }
- svg {
- @include _(transition, fill 250ms ease-in-out);
- }
- }
-
- .icons {
- width: 1.5em;
- height: 1.5em;
- fill: $black;
- vertical-align: middle;
- }
- }
-
- .github {
- a {
- @include after {
- display: none;
- }
- }
-
- &:hover .icons {
- fill: #4183C4;
- }
- }
-
- ul.locales {
- @include _(flex, 1);
- @include _(flex-basis, 100%);
- padding: 0;
- margin: 1em 0;
- list-style: none;
- text-align: center;
- }
-
- body.error-locale-not-available & {
- .github, .facebook, .locales {
- visibility: hidden;
- }
- }
+ padding: 1em;
+
+ footer {
+ @include _-(display, flex);
+ @include _(flex-flow, row wrap);
+ @include _(align-items, flex-end);
+ font-size: 4.1vw;
+ }
+
+ .site-info, .user-controls, .external {
+ width: 100%;
+ text-align: center;
+
+ a {
+ text-align: right;
+ white-space: nowrap;
+ color: #666;
+ }
+ }
+
+ .external {
+ margin-bottom: 1em;
+ }
+
+ .my-account {
+ margin-right: 0.5em;
+ }
+
+ .logout {
+ margin-left: 0.5em;
+ }
+
+ .contact-us {
+ line-height: 2.5em;
+ }
+
+ .facebook {
+ a {
+ &:hover, &:active, &:focus {
+ svg {
+ fill: #3B579D;
+ }
+ }
+ }
+ }
+
+ .github, .facebook {
+ display: inline-block;
+ margin: 0.5em;
+
+ a {
+ @include after {
+ display: none;
+ }
+ svg {
+ @include _(transition, fill 250ms ease-in-out);
+ }
+ }
+
+ .icons {
+ width: 1.5em;
+ height: 1.5em;
+ fill: $black;
+ vertical-align: middle;
+ }
+ }
+
+ .github {
+ a {
+ @include after {
+ display: none;
+ }
+ }
+
+ &:hover .icons {
+ fill: #4183C4;
+ }
+ }
+
+ ul.locales {
+ @include _(flex, 1);
+ @include _(flex-basis, 100%);
+ padding: 0;
+ margin: 1em 0;
+ list-style: none;
+ text-align: center;
+ }
+
+ body.error-locale-not-available & {
+ .github, .facebook, .locales {
+ visibility: hidden;
+ }
+ }
}
body.error-locale-not-available {
- .locales {
- list-style: none;
- padding: 0;
- text-align: center;
+ .locales {
+ list-style: none;
+ padding: 0;
+ text-align: center;
- li {
- margin-bottom: 1em;
- }
- }
+ li {
+ margin-bottom: 1em;
+ }
+ }
}
body {
- #primary-content {
- @include _(transition, 'filter 250ms ease-in-out, -webkit-filter 250ms ease-in-out');
- display: -webkit-flex;
- flex-direction: column;
- min-height: 100vh;
- overflow: hidden;
- }
-
- &.has-overlay {
- overflow: hidden;
-
- #primary-content {
- -webkit-filter: blur(5px);
- @include _(filter, blur(5px));
- }
-
- #overlay {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- cursor: pointer;
- z-index: 1000;
- }
- }
+ #primary-content {
+ @include _(transition, 'filter 250ms ease-in-out, -webkit-filter 250ms ease-in-out');
+ display: -webkit-flex;
+ flex-direction: column;
+ min-height: 100vh;
+ overflow: hidden;
+ }
+
+ &.has-overlay {
+ overflow: hidden;
+
+ #primary-content {
+ -webkit-filter: blur(5px);
+ @include _(filter, blur(5px));
+ }
+
+ #overlay {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ cursor: pointer;
+ z-index: 1000;
+ }
+ }
}
#content-overlay {
- display: none;
-
- body.has-overlay & {
- display: block;
- }
-
- body.has-overlay.is-event-dlg & {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- @include _-(display, flex);
- }
-
- .dlg {
- @include before {
- content: '';
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: rgba($black, 0);
- @include _(transition, background-color 250ms ease-in-out);
- }
-
- &.open {
- @include before {
- background-color: rgba($black, 0.5);
- }
-
- .dlg-content {
- @include _(transform, rotateZ(0deg));
- }
- }
- }
-
- .dlg-content {
- position: fixed;
- right: 0;
- bottom: 0;
- left: 0;
- max-width: 50rem;
- max-height: 100%;
- font-size: 0.9em;
- overflow-y: auto;
- margin: auto;
- z-index: 1001;
- background-color: $white;
- text-align: center;
- @include _(transition, transform 500ms ease-in-out);
- @include _(transform, rotateX(-90deg));
- @include _(transform-origin, center 250%);
- }
-
- .dlg {
- .title {
- padding: 0.5em;
- background-color: $colour-5;
- color: $white;
- @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
- }
- }
-
- button, .button {
- margin: 0 0.25em 0.5em;
- }
-
- .dlg-inner {
- padding: 0 2em 2em;
- }
-
- .message {
- margin-bottom: 2em;
- }
-
- #info-dlg .title {
- background-color: $colour-1;
- font-size: 1.5em;
- text-align: left;
- }
-
- #info-dlg .message {
- text-align: left;
-
- p, h4 {
- font-size: 0.8em;
- }
-
- h3 {
- font-size: 1em;
- }
-
- h5 {
- font-size: 0.667em;
- }
-
- h6 {
- font-size: 0.8em;
- }
- }
-
- #login-dlg .title {
- font-size: 2em;
- margin-bottom: 1em;
- }
-
- #contact-dlg {
- .title {
- background-color: $colour-4;
- }
-
- .email-field {
- margin-top: 2em;
- }
-
- textarea {
- min-height: 7.5em;
- }
- }
+ display: none;
+
+ body.has-overlay & {
+ display: block;
+ }
+
+ body.has-overlay.is-event-dlg & {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ @include _-(display, flex);
+ }
+
+ .dlg {
+ @include before {
+ content: '';
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: rgba($black, 0);
+ @include _(transition, background-color 250ms ease-in-out);
+ }
+
+ &.open {
+ @include before {
+ background-color: rgba($black, 0.5);
+ }
+
+ .dlg-content {
+ @include _(transform, rotateZ(0deg));
+ }
+ }
+ }
+
+ .dlg-content {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ max-width: 50rem;
+ max-height: 100%;
+ font-size: 0.9em;
+ overflow-y: auto;
+ margin: auto;
+ z-index: 1001;
+ background-color: $white;
+ text-align: center;
+ @include _(transition, transform 500ms ease-in-out);
+ @include _(transform, rotateX(-90deg));
+ @include _(transform-origin, center 250%);
+ }
+
+ .dlg {
+ .title {
+ padding: 0.5em;
+ background-color: $colour-5;
+ color: $white;
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
+ }
+ }
+
+ button, .button {
+ margin: 0 0.25em 0.5em;
+ }
+
+ .dlg-inner {
+ padding: 0 2em 2em;
+ }
+
+ .message {
+ margin-bottom: 2em;
+ }
+
+ #info-dlg .title {
+ background-color: $colour-1;
+ font-size: 1.5em;
+ text-align: left;
+ }
+
+ #info-dlg .message {
+ text-align: left;
+
+ p, h4 {
+ font-size: 0.8em;
+ }
+
+ h3 {
+ font-size: 1em;
+ }
+
+ h5 {
+ font-size: 0.667em;
+ }
+
+ h6 {
+ font-size: 0.8em;
+ }
+ }
+
+ #login-dlg .title {
+ font-size: 2em;
+ margin-bottom: 1em;
+ }
+
+ #contact-dlg {
+ .title {
+ background-color: $colour-4;
+ }
+
+ .email-field {
+ margin-top: 2em;
+ }
+
+ textarea {
+ min-height: 7.5em;
+ }
+ }
}
@include keyframes(fade-out) {
- to {
- @include _(opacity, 0.25);
- }
+ to {
+ @include _(opacity, 0.6667);
+ }
}
@include keyframes(colour-out) {
- to {
- background-color: transparent;
- }
+ to {
+ background-color: transparent;
+ }
+}
+
+@include keyframes(barberpole) {
+ to { background-position: 60px 30px; }
}
html :focus {
- outline: 0;
+ outline: 0;
}
@mixin header-colour($page, $colour) {
- body.#{$page} {
- #header-title {
- background-color: $colour;
-
- rect {
- fill: $colour;
- }
- }
- }
+ body.#{$page} {
+ #header-title {
+ background-color: $colour;
+ }
+ }
}
@include header-colour(about, $colour-3);
@include header-colour(policy, $colour-5);
+// @include header-colour(conferences, $colour-2);
#bike_small + label {
- padding: 0.5em 2.5em;
+ padding: 0.5em 2.5em;
}
#bike_medium + label {
- padding: 0.5em 1.5em;
+ padding: 0.5em 1.5em;
}
#main .graded-options {
- text-align: center;
-
- button {
- background-color: $colour-4;
- width: 6em;
- font-size: 1.5em;
- margin: 0.25em;
-
- &.option-1 {
- background-color: $colour-3;
- }
-
- &:last-child {
- background-color: $colour-5;
- }
- }
-
- &.option-count-4 button.option-3,
- &.option-count-5 button.option-4 {
- background-color: mix($colour-3, $colour-5);
- }
-
- &.option-count-5 {
- button.option-2 {
- background-color: mix($colour-3, $colour-4);
- }
- }
+ text-align: center;
+
+ button {
+ background-color: $colour-4;
+ width: 6em;
+ font-size: 1.5em;
+ margin: 0.25em;
+
+ &.option-1 {
+ background-color: $colour-3;
+ }
+
+ &:last-child {
+ background-color: $colour-5;
+ }
+ }
+
+ &.option-count-4 button.option-3,
+ &.option-count-5 button.option-4 {
+ background-color: mix($colour-3, $colour-5);
+ }
+
+ &.option-count-5 {
+ button.option-2 {
+ background-color: mix($colour-3, $colour-4);
+ }
+ }
}
#main .skip {
- button {
- background-color: #888;
- }
+ button {
+ background-color: #888;
+ }
}
#main form.custom-payment {
- input[type="number"] {
- width: 4em;
- margin-right: 0.5em;
- }
+ input[type="number"] {
+ width: 4em;
+ margin-right: 0.5em;
+ }
- button {
- background-color: $colour-1;
- }
+ button {
+ background-color: $colour-1;
+ }
- .currency {
- color: #888;
- font-size: 1.5em;
- }
+ .currency {
+ color: #888;
+ font-size: 1.5em;
+ }
- .number-field {
- margin-bottom: 0;
- }
+ .number-field {
+ margin-bottom: 0;
+ }
}
#main ul.workshops {
- list-style: none;
- padding: 0;// 1em;
- text-align: center;
- @include default-box-shadow(top, 2, true);
- background-color: #F8F8F8;
-
- li {
- display: inline-block;
- margin: 0.5em;
- vertical-align: middle;
-
- @include before {
- content: '';
- display: none;
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: #000;
- z-index: 99;
- @include _(opacity, 0.5);
- }
-
- &.view {
- @include before {
- display: block;
- }
-
- .info {
- left: 0;
- right: 0;
- top: 50%;
- @include _(transform, translateY(-50%) scale(1));
-
- @include breakpoint(large) {
- left: 19rem;
- }
- }
- }
- }
-
- h4, h5 {
- color: $white;
- background-color: $colour-1;
- @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
- }
-
- h4 {
- max-width: 10em;
- cursor: pointer;
- margin: 0;
- padding: 1em;
- position: relative;
- @include default-box-shadow(top);
- @include _(transition, transform 100ms ease-in-out);
-
- &:hover {
- z-index: $zindex-base + 3;
- @include _(transform, scale(1.25));
- }
- }
-
- h5 {
- font-size: 1.5em;
- margin: 0;
- padding: 0.5em 1em;
-
- + p {
- margin-top: 0;
- }
- }
-
- p {
- padding: 1em;
- max-height: 50vh;
- overflow-y: auto;
- }
-
- form {
- text-align: center;
- }
-
- button {
- margin: 1em 0.5em;
- }
-
- [contenteditable] {
- outline: none;
- }
-
- .info {
- position: fixed;
- background-color: $white;
- color: $black;
- left: auto;
- right: auto;
- top: auto;
- z-index: 100;
- max-width: 40em;
- max-height: 40em;
- margin: auto;
- text-align: left;
- @include _(transition, all 250ms ease-in-out);
- @include _(transform, translateY(0) scale(0));
- @include default-box-shadow(top);
- }
-
- [value="delete"] {
- background-color: $colour-2;
- }
-
- [value="cancel"] {
- background-color: $colour-4;
- }
+ list-style: none;
+ padding: 0;// 1em;
+ text-align: center;
+ @include default-box-shadow(top, 2, true);
+ background-color: #F8F8F8;
+
+ li {
+ display: inline-block;
+ margin: 0.5em;
+ vertical-align: middle;
+
+ @include before {
+ content: '';
+ display: none;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: #000;
+ z-index: 99;
+ @include _(opacity, 0.5);
+ }
+
+ &.view {
+ @include before {
+ display: block;
+ }
+
+ .info {
+ left: 0;
+ right: 0;
+ top: 50%;
+ @include _(transform, translateY(-50%) scale(1));
+
+ @include breakpoint(large) {
+ left: 19rem;
+ }
+ }
+ }
+ }
+
+ h4, h5 {
+ color: $white;
+ background-color: $colour-1;
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
+ }
+
+ h4 {
+ max-width: 10em;
+ cursor: pointer;
+ margin: 0;
+ padding: 1em;
+ position: relative;
+ @include default-box-shadow(top);
+ @include _(transition, transform 100ms ease-in-out);
+
+ &:hover {
+ z-index: $zindex-base + 3;
+ @include _(transform, scale(1.25));
+ }
+ }
+
+ h5 {
+ font-size: 1.5em;
+ margin: 0;
+ padding: 0.5em 1em;
+
+ + p {
+ margin-top: 0;
+ }
+ }
+
+ p {
+ padding: 1em;
+ max-height: 50vh;
+ overflow-y: auto;
+ }
+
+ form {
+ text-align: center;
+ }
+
+ button {
+ margin: 1em 0.5em;
+ }
+
+ [contenteditable] {
+ outline: none;
+ }
+
+ .info {
+ position: fixed;
+ background-color: $white;
+ color: $black;
+ left: auto;
+ right: auto;
+ top: auto;
+ z-index: 100;
+ max-width: 40em;
+ max-height: 40em;
+ margin: auto;
+ text-align: left;
+ @include _(transition, all 250ms ease-in-out);
+ @include _(transform, translateY(0) scale(0));
+ @include default-box-shadow(top);
+ }
+
+ [value="delete"] {
+ background-color: $colour-2;
+ }
+
+ [value="cancel"] {
+ background-color: $colour-4;
+ }
}
.conferences-register .policy-agreement {
- padding: 0.25em 1em;
- margin: 0 0 3em;
- border: 0.1em solid #DDD;
- @include _(border-radius, 0.25em);
- @include default-box-shadow(top, 2);
+ padding: 0.25em 1em;
+ margin: 0 0 3em;
+ border: 0.1em solid #DDD;
+ @include _(border-radius, 0.25em);
+ @include default-box-shadow(top, 2);
}
body.policy .policy-agreement ul {
- padding: 0 1.5em 0;
+ padding: 0 1.5em 0;
}
.stats {
- list-style: none;
-
- h3 {
- display: inline-block;
- }
-
- .stat {
- position: relative;
- padding: 0 0.25em;
- margin: 0.5em;
- font-size: 2em;
- display: inline-block;
- color: $white;
- background-color: $colour-1;
- @include default-box-shadow(top, 2);
- @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
-
- &.important {
- width: 2em;
- height: 2em;
- line-height: 2em;
- text-align: center;
- @include _(border-radius, 50%);
- }
- }
-
- .money .stat {
- background-color: $colour-5;
- }
-
- .percent {
- font-size: 1.5em;
- }
-
- .value {
- position: relative;
- z-index: $zindex-base + 2;
- }
-
- .stat-with-label {
- display: inline-block;
- text-align: center;
- vertical-align: middle;
- background-color: $colour-3;
- margin: 0.5em;
- @include default-box-shadow(top, 2);
-
- .stat {
- margin-top: 0.25em;
- }
- }
-
- .label {
- padding: 0.5em 0.5em 0;
- font-weight: bold;
- }
-
- .breakdown {
- text-align: center;
- }
+ list-style: none;
+
+ h3 {
+ display: inline-block;
+ }
+
+ .stat {
+ position: relative;
+ padding: 0 0.25em;
+ margin: 0.5em;
+ font-size: 2em;
+ display: inline-block;
+ color: $white;
+ background-color: $colour-1;
+ @include default-box-shadow(top, 2);
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
+
+ &.important {
+ width: 2em;
+ height: 2em;
+ line-height: 2em;
+ text-align: center;
+ @include _(border-radius, 50%);
+ }
+ }
+
+ .money .stat {
+ background-color: $colour-5;
+ }
+
+ .percent {
+ font-size: 1.5em;
+ }
+
+ .value {
+ position: relative;
+ z-index: $zindex-base + 2;
+ }
+
+ .stat-with-label {
+ display: inline-block;
+ text-align: center;
+ vertical-align: middle;
+ background-color: $colour-3;
+ margin: 0.5em;
+ @include default-box-shadow(top, 2);
+
+ .stat {
+ margin-top: 0.25em;
+ }
+ }
+
+ .label {
+ padding: 0.5em 0.5em 0;
+ font-weight: bold;
+ }
+
+ .breakdown {
+ text-align: center;
+ }
}
.email-preview {
- max-width: 40rem;
- margin: 2em auto 0;
- padding: 0.25em 1em;
- background-color: #F8F8F8;
- @include default-box-shadow(top, 2, true);
+ max-width: 40rem;
+ margin: 2em auto 0;
+ padding: 0.25em 1em;
+ background-color: #F8F8F8;
+ @include default-box-shadow(top, 2, true);
}
.facilitators {
- display: inline-block;
+ display: inline-block;
- .facilitator {
- margin: 0 0 0.5em 0.5em;
- padding: 1.5em 0.5em 0.5em;
- border-top: 0.1em solid #CCC;
+ .facilitator {
+ margin: 0 0 0.5em 0.5em;
+ padding: 1.5em 0.5em 0.5em;
+ border-top: 0.1em solid #CCC;
- &:first-child {
- border: 0;
- padding-top: 0;
- }
- }
+ &:first-child {
+ border: 0;
+ padding-top: 0;
+ }
+ }
- .name {
- position: relative;
- font-weight: bold;
- }
+ .name {
+ position: relative;
+ font-weight: bold;
+ }
- .details {
- margin-top: 0.5em;
- }
+ .details {
+ margin-top: 0.5em;
+ }
- .email {
- margin-bottom: 0.5em;
- }
+ .email {
+ margin-bottom: 0.5em;
+ }
- .name, .role {
- display: inline;
- }
+ .name, .role {
+ display: inline;
+ }
- .role {
- color: #666;
- white-space: nowrap;
+ .role {
+ color: #666;
+ white-space: nowrap;
- @include before {
- content: '(';
- }
+ @include before {
+ content: '(';
+ }
- @include after {
- content: ')';
- }
+ @include after {
+ content: ')';
+ }
- &:last-child {
- @include after {
- content: ')';
- }
- }
- }
+ &:last-child {
+ @include after {
+ content: ')';
+ }
+ }
+ }
}
.workshop-previews {
- position: relative;
- list-style: none;
- padding: 0;
-
- p {
- padding: 0 1rem 0.5rem;
- font-size: 1em;
- color: $black;
- }
-
- h4 {
- color: $white;
- background-color: $colour-1;
- padding: 0.5em 1em;
- margin: 0;
- @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
- }
-
- a {
- display: block;
- max-height: 10em;
- overflow: hidden;
- margin: 0.5em 0;
- @include default-box-shadow(top, 2);
- @include _(transition, transform 150ms ease-in-out);
-
- &:hover, &:focus, &:active {
- z-index: $zindex-base + 2;
- @include _(transform, scale(1.1));
- }
-
- @include before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: $zindex-base + 2;
- cursor: pointer;
- @include _(box-shadow, inset 0em -2em 2em -1em $white);
- }
-
- @include after {
- display: none;
- }
- }
+ position: relative;
+ list-style: none;
+ padding: 0;
+
+ p {
+ padding: 0 1rem 0.5rem;
+ font-size: 1em;
+ color: $black;
+ }
+
+ h4 {
+ color: $white;
+ background-color: $colour-1;
+ padding: 0.5em 1em;
+ margin: 0;
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
+ }
+
+ a {
+ display: block;
+ max-height: 10em;
+ overflow: hidden;
+ margin: 0.5em 0;
+ @include default-box-shadow(top, 2);
+ @include _(transition, transform 150ms ease-in-out);
+
+ &:hover, &:focus, &:active {
+ z-index: $zindex-base + 2;
+ @include _(transform, scale(1.1));
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $zindex-base + 2;
+ cursor: pointer;
+ @include _(box-shadow, inset 0em -2em 2em -1em $white);
+ }
+
+ @include after {
+ display: none;
+ }
+ }
}
.translated-content.block {
- display: inherit;
+ display: inherit;
+}
+
+.list-view {
+ ul {
+ list-style: none;
+
+ &.break {
+ border-top: 0.25em solid $light-gray;
+ margin-top: 2em;
+ padding-top: 1em;
+ }
+ }
+
+ li {
+ box-shadow: 0 0.15em 0 -0.1em $light-gray;
+
+ &:last-child {
+ box-shadow: 0 0.333em 0 -0.1em $light-gray;
+ }
+
+ > div {
+ vertical-align: top;
+ padding: 2em 0;
+
+ h3, p:first-child {
+ margin-top: 0;
+ }
+
+ &:first-child {
+ padding-right: 1em;
+ }
+ }
+ }
+
+ .info {
+
+ .title {
+ margin: 0 0 0.5em;
+ }
+
+ .conference-details {
+ margin-bottom: 1em;
+ }
+ }
+
+ .img {
+ text-align: center;
+
+ img {
+ max-width: 100%;
+ }
+ }
+
+ .actions {
+ margin-top: 1em;
+ }
}
html[data-lingua-franca-example="html"] {
- $caption-height: 4rem;
- @include _(transform, translateY($caption-height));
-
- #lingua-franca-window-caption {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: $caption-height;
- background-color: #CCC;
- z-index: 1000;
- font-size: 1rem;
- @include _(transform, translateY($caption-height * -1));
-
- .window-tab {
- height: 1.75rem;
- position: relative;
- background-color: #EEE;
- float: left;
- padding: 0.25em 2em 0 1em;
- margin: 0.25em 1em;
- @include _(border-radius, 0.5em 0.5em 0 0);
- color: #444;
- @include font-family(secondary);
-
- @include after {
- content: '×';
- position: absolute;
- right: 0.5em;
- top: 0.25em;
- }
- }
-
- i {
- font-style: normal;
- }
-
- .window-minimize,
- .window-maximize {
- @include after {
- content: '';
- position: absolute;
- top: 0.75em;
- right: 2em;
- border-style: solid;
- border-width: 0 0.5em 0.5em 0.5em;
- border-left-color: transparent;
- border-right-color: transparent;
- }
- }
-
- .window-minimize {
- @include after {
- right: 3.5em;
- border-top-width: 0.5em;
- border-bottom-width: 0;
- }
- }
-
- .window-close {
- @include after {
- content: '×';
- position: absolute;
- right: 0.25em;
- top: 0.1em;
- font-size: 1.75em;
- line-height: 1em;
- }
- }
-
- .window-url-bar {
- position: absolute;
- top: $caption-height / 2;
- left: 0;
- width: 100%;
- height: $caption-height / 2;
- background-color: #EEE;
-
- .url-bar {
- position: absolute;
- top: 0.25rem;
- left: 3.5rem;
- bottom: 0.25rem;
- right: 0.25rem;
- background-color: #FFF;
- border: 1px solid #CCC;
- padding: 0 0.25rem;
- font-size: 0.8em;
- line-height: 1.75em;
- }
- }
-
- .window-back,
- .window-forward {
- @include after {
- content: '';
- position: absolute;
- left: 0.5em;
- top: 0.5em;
- border-style: solid;
- border-width: 0.5em 0.75em 0.5em 0;
- border-top-color: transparent;
- border-bottom-color: transparent;
- color: #666;
- }
- }
-
- .window-forward {
- @include after {
- left: 2em;
- border-right-width: 0;
- border-left-width: 0.75em;
- }
- }
- }
-
- @include translation-pointer;
+ $caption-height: 4rem;
+ @include _(transform, translateY($caption-height));
+
+ #lingua-franca-window-caption {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: $caption-height;
+ background-color: #CCC;
+ z-index: 1000;
+ font-size: 1rem;
+ @include _(transform, translateY($caption-height * -1));
+
+ .window-tab {
+ height: 1.75rem;
+ position: relative;
+ background-color: $light-gray;
+ float: left;
+ padding: 0.25em 2em 0 1em;
+ margin: 0.25em 1em;
+ @include _(border-radius, 0.5em 0.5em 0 0);
+ color: #444;
+ @include font-family(secondary);
+
+ @include after {
+ content: '×';
+ position: absolute;
+ right: 0.5em;
+ top: 0.25em;
+ }
+ }
+
+ i {
+ font-style: normal;
+ }
+
+ .window-minimize,
+ .window-maximize {
+ @include after {
+ content: '';
+ position: absolute;
+ top: 0.75em;
+ right: 2em;
+ border-style: solid;
+ border-width: 0 0.5em 0.5em 0.5em;
+ border-left-color: transparent;
+ border-right-color: transparent;
+ }
+ }
+
+ .window-minimize {
+ @include after {
+ right: 3.5em;
+ border-top-width: 0.5em;
+ border-bottom-width: 0;
+ }
+ }
+
+ .window-close {
+ @include after {
+ content: '×';
+ position: absolute;
+ right: 0.25em;
+ top: 0.1em;
+ font-size: 1.75em;
+ line-height: 1em;
+ }
+ }
+
+ .window-url-bar {
+ position: absolute;
+ top: $caption-height / 2;
+ left: 0;
+ width: 100%;
+ height: $caption-height / 2;
+ background-color: $light-gray;
+
+ .url-bar {
+ position: absolute;
+ top: 0.25rem;
+ left: 3.5rem;
+ bottom: 0.25rem;
+ right: 0.25rem;
+ background-color: #FFF;
+ border: 1px solid #CCC;
+ padding: 0 0.25rem;
+ font-size: 0.8em;
+ line-height: 1.75em;
+ }
+ }
+
+ .window-back,
+ .window-forward {
+ @include after {
+ content: '';
+ position: absolute;
+ left: 0.5em;
+ top: 0.5em;
+ border-style: solid;
+ border-width: 0.5em 0.75em 0.5em 0;
+ border-top-color: transparent;
+ border-bottom-color: transparent;
+ color: #666;
+ }
+ }
+
+ .window-forward {
+ @include after {
+ left: 2em;
+ border-right-width: 0;
+ border-left-width: 0.75em;
+ }
+ }
+ }
+
+ @include translation-pointer;
}
.workshop-notes {
- p {
- font-size: 1em;
- }
+ p {
+ font-size: 1em;
+ }
}
.major-group {
- border-bottom: 0.333em solid #EEE;
- padding-bottom: 2em;
- margin-bottom: 1em;
+ border-bottom: 0.333em solid $light-gray;
+ padding-bottom: 2em;
+ margin-bottom: 1em;
- &:last-child {
- border-bottom: 0;
- }
+ &:last-child {
+ border-bottom: 0;
+ }
}
#main .workshop-list {
- list-style: none;
- padding: 0;
-
- .workshop-description {
- font-size: 0.9em;
- max-height: 20em;
- padding: 1em;
- overflow: hidden;
- }
-
- > li {
- position: relative;
- margin-bottom: 1em;
- border-bottom: 0.1rem solid #EEE;
-
- ul {
- padding-left: 2.5em;
- margin-bottom: 1.5em;
- }
-
- li {
- border: 0;
- }
-
- &:last-child {
- border: 0;
- }
- }
-
- .actions {
- position: relative;
- margin: 0;
- @include _(box-shadow, 0 -1em 1em $white);
- }
-
- .title {
- margin: 0;
- }
-
- p {
- font-size: 1em;
- }
-
- h5, h6 {
- margin: 1em 0 0;
- }
+ list-style: none;
+ padding: 0;
+
+ .workshop-description {
+ font-size: 0.9em;
+ max-height: 20em;
+ padding: 1em;
+ overflow: hidden;
+ }
+
+ > li {
+ position: relative;
+ margin-bottom: 1em;
+ border-bottom: 0.1rem solid $light-gray;
+
+ ul {
+ padding-left: 2.5em;
+ margin-bottom: 1.5em;
+ }
+
+ li {
+ border: 0;
+ }
+
+ &:last-child {
+ border: 0;
+ }
+ }
+
+ .actions {
+ position: relative;
+ margin: 0;
+ @include _(box-shadow, 0 -1em 1em $white);
+ }
+
+ .title {
+ margin: 0;
+ }
+
+ p {
+ font-size: 1em;
+ }
+
+ h5, h6 {
+ margin: 1em 0 0;
+ }
}
.interest-button {
- button {
- margin: 0 0 0 1em;
- font-size: 0.8em;
- padding-top: 0.333em;
- padding-bottom: 0.333em;
- background-color: $colour-5;
- }
+ button {
+ margin: 0 0 0 1em;
+ font-size: 0.8em;
+ padding-top: 0.333em;
+ padding-bottom: 0.333em;
+ background-color: $colour-5;
+ }
}
#main .workshop-interest, .workshop-interest {
- text-align: right;
+ text-align: right;
- form {
- display: inline;
- }
+ form {
+ display: inline;
+ }
- button {
- font-size: 0.9em;
- margin: 1em;
- background-color: $colour-5;
- vertical-align: middle;
+ button {
+ font-size: 0.9em;
+ margin: 1em;
+ background-color: $colour-5;
+ vertical-align: middle;
- &.delete {
- background-color: $colour-4;
- }
- }
+ &.delete {
+ background-color: $colour-4;
+ }
+ }
}
.original-text {
- margin: 0 0 1em 1em;
-
- .value {
- border: 0.1em solid #DDD;
- padding: 1em;
- margin: 1em 0 3em 0;
- background-color: #F8F8F8;
- @include _(border-radius, 0.25em);
- }
-
- h4, h5, h6 {
- margin: 0;
- }
-
- h5 {
- font-size: 1em;
- }
-
- h6 {
- font-size: 0.9em;
- }
-
- p {
- margin: 0.5em 0;
- font-size: 0.9em;
- }
+ margin: 0 0 1em 1em;
+
+ .value {
+ border: 0.1em solid #DDD;
+ padding: 1em;
+ margin: 1em 0 3em 0;
+ background-color: #F8F8F8;
+ @include _(border-radius, 0.25em);
+ }
+
+ h4, h5, h6 {
+ margin: 0;
+ }
+
+ h5 {
+ font-size: 1em;
+ }
+
+ h6 {
+ font-size: 0.9em;
+ }
+
+ p {
+ margin: 0.5em 0;
+ font-size: 0.9em;
+ }
}
.facilitator .button {
- font-size: 0.9em;
- vertical-align: -0.75em;
+ font-size: 0.9em;
+ vertical-align: -0.75em;
}
#main .facilitators .actions {
- margin: 0;
+ margin: 0;
}
#main form.add-facilitator {
- button {
- height: 2.4em;
- font-size: 0.9em;
- }
+ button {
+ height: 2.4em;
+ font-size: 0.9em;
+ }
}
.conferences-edit_schedule {
- #main {
- .location {
- padding-top: 0.25em;
- }
- .errors {
- padding: 0.25em;
- background-color: $colour-2;
- color: $white;
- @include _(text-stroke, 0.5px #000);
- }
- .conflict-score {
- padding: 0.25em;
- background-color: $colour-3;
- color: $black;
- }
- .all-workshops, .all-events {
- list-style: none;
- padding: 0;
-
- li {
- margin: 0.5em;
- padding: 0.5em;
- border: 1px solid #CCC;
- background-color: lighten($colour-1, 35);
- @include default-box-shadow(top, 2);
-
- &.error {
- outline: 0.2em solid $colour-2;
- outline-offset: -0.2em;
- }
- }
-
- h3 {
- margin: 0;
- }
-
- .workshop-interest {
- color: #888;
- text-align: left;
- margin: 0.25em 0;
- }
-
- .error-description {
- background-color: $colour-2;
- color: $white;
- padding: 0.5em;
- margin-top: 0.5em;
- }
-
- .warnings {
- background-color: $colour-3;
- color: $black;
- padding: 0.5em;
- margin-top: 0.5em;
- list-style: none;
-
- li {
- padding: 0;
- margin: 0;
- background-color: transparent;
- @include _(box-shadow, none);
- }
- }
- }
- .day_parts {
- list-style: none;
-
- h4 {
- display: inline-block;
- min-width: 12.5em;
- margin: 0.25em 0;
- }
-
- select, .select {
- float: right;
- min-width: 5em;
- }
-
- .select {
- padding-left: 0.25em;
- }
- }
- .actions {
- margin: 2em;
- }
- .unsaved {
- @include font-family(secondary);
- background-color: lighten($colour-3, 25);
- margin: 0 5em 2em;
- padding: 0.5em;
- }
- .all-events {
- li {
- background-color: lighten($colour-5, 35);
-
- &.meal {
- background-color: lighten($colour-3, 25);
- }
- }
-
- h3 {
- margin-bottom: 0.5em;
- }
- }
- }
+ #main {
+ .location {
+ padding-top: 0.25em;
+ }
+ .errors {
+ padding: 0.25em;
+ background-color: $colour-2;
+ color: $white;
+ @include _(text-stroke, 0.5px #000);
+ }
+ .conflict-score {
+ padding: 0.25em;
+ background-color: $colour-3;
+ color: $black;
+ }
+ .all-workshops, .all-events {
+ list-style: none;
+ padding: 0;
+
+ li {
+ margin: 0.5em;
+ padding: 0.5em;
+ border: 1px solid #CCC;
+ background-color: lighten($colour-1, 35);
+ @include default-box-shadow(top, 2);
+
+ &.error {
+ outline: 0.2em solid $colour-2;
+ outline-offset: -0.2em;
+ }
+ }
+
+ h3 {
+ margin: 0;
+ }
+
+ .workshop-interest {
+ color: #888;
+ text-align: left;
+ margin: 0.25em 0;
+ }
+
+ .error-description {
+ background-color: $colour-2;
+ color: $white;
+ padding: 0.5em;
+ margin-top: 0.5em;
+ }
+
+ .warnings {
+ background-color: $colour-3;
+ color: $black;
+ padding: 0.5em;
+ margin-top: 0.5em;
+ list-style: none;
+
+ li {
+ padding: 0;
+ margin: 0;
+ background-color: transparent;
+ @include _(box-shadow, none);
+ }
+ }
+ }
+ .day_parts {
+ list-style: none;
+
+ h4 {
+ display: inline-block;
+ min-width: 12.5em;
+ margin: 0.25em 0;
+ }
+
+ select, .select {
+ float: right;
+ min-width: 5em;
+ }
+
+ .select {
+ padding-left: 0.25em;
+ }
+ }
+ .actions {
+ margin: 2em;
+ }
+ .unsaved {
+ @include font-family(secondary);
+ background-color: lighten($colour-3, 25);
+ margin: 0 5em 2em;
+ padding: 0.5em;
+ }
+ .all-events {
+ li {
+ background-color: lighten($colour-5, 35);
+
+ &.meal {
+ background-color: lighten($colour-3, 25);
+ }
+ }
+
+ h3 {
+ margin-bottom: 0.5em;
+ }
+ }
+ }
}
.conferences-schedule {
- .actions {
- margin: 2em;
- }
-
- ul.events, ul.locations {
- list-style: none;
-
- .button {
- float: left;
- font-size: 1em;
- margin-bottom: 0.5em;
- margin-right: 0.5em;
- }
-
- h3 {
- display: inline-block;
- margin: 0.25em 0 0;
- }
-
- li {
- clear: both;
- }
- }
+ .actions {
+ margin: 2em;
+ }
+
+ ul.events, ul.locations {
+ list-style: none;
+
+ .button {
+ float: left;
+ font-size: 1em;
+ margin-bottom: 0.5em;
+ margin-right: 0.5em;
+ }
+
+ h3 {
+ display: inline-block;
+ margin: 0.25em 0 0;
+ }
+
+ li {
+ clear: both;
+ }
+ }
}
.select-field {
- line-height: 1.75em;
- position: relative;
-
- select {
- display: block;
- width: 100%;
- height: 1.75em;
- font-family: inherit;
- font-size: 1.5em;
- padding: 0 0.5em;
- border: 0.1em solid;
- cursor: pointer;
- @include default-box-shadow(top, 1.5, false);
-
- td &,
- .table-td & {
- @include _(box-shadow, none);
- }
- }
-
- @if capable_of(pointer-events) {
- label {
- @include after {
- content: '';
- position: absolute;
- display: block;
- border: 0.1em solid;
- width: 1.75em;
- height: 1.75em;
- background-color: $colour-5;
- font-size: 1.5em;
- z-index: 1;
- right: 0em;
- bottom: 0em;
- pointer-events: none;
- }
-
- @include before {
- content: '';
- position: absolute;
- display: block;
- @include _(transform, rotate(-45deg));
- border: 0 solid $white;
- border-width: 0 0 0.25em 0.25em;
- width: 1em;
- height: 1em;
- right: 0.75em;
- bottom: 1em;
- z-index: 2;
- pointer-events: none;
- }
- }
-
- &:hover label {
- @include after {
- background-color: $colour-3;
- }
- }
-
- select {
- padding-right: 2.25em;
- -webkit-appearance: none;
- @include _(appearance, none);
- }
-
- &.small label {
- @include after {
- font-size: 1em;
- }
-
- @include before {
- font-size: 0.725em;
- }
- }
- }
-
- &.full select {
- width: 100%;
- margin-bottom: 2em;
- }
-
- &.inline-label {
- display: inline-block;
-
- label {
- float: left;
- margin-right: 0.5em;
- }
- }
+ line-height: 1.75em;
+ position: relative;
+
+ select {
+ display: block;
+ width: 100%;
+ height: 1.75em;
+ font-family: inherit;
+ font-size: 1.5em;
+ padding: 0 0.5em;
+ border: 0.1em solid;
+ cursor: pointer;
+ @include default-box-shadow(top, 1.5, false);
+
+ td &,
+ .table-td & {
+ @include _(box-shadow, none);
+ }
+ }
+
+ @if capable_of(pointer-events) {
+ label {
+ @include after {
+ content: '';
+ position: absolute;
+ display: block;
+ border: 0.1em solid;
+ width: 1.75em;
+ height: 1.75em;
+ background-color: $colour-5;
+ font-size: 1.5em;
+ z-index: 1;
+ right: 0em;
+ bottom: 0em;
+ pointer-events: none;
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ display: block;
+ @include _(transform, rotate(-45deg));
+ border: 0 solid $white;
+ border-width: 0 0 0.25em 0.25em;
+ width: 1em;
+ height: 1em;
+ right: 0.75em;
+ bottom: 1em;
+ z-index: 2;
+ pointer-events: none;
+ }
+ }
+
+ &:hover label {
+ @include after {
+ background-color: $colour-3;
+ }
+ }
+
+ select {
+ padding-right: 2.25em;
+ -webkit-appearance: none;
+ @include _(appearance, none);
+ }
+
+ &.small label {
+ @include after {
+ font-size: 1em;
+ }
+
+ @include before {
+ font-size: 0.725em;
+ }
+ }
+ }
+
+ &.full select {
+ width: 100%;
+ margin-bottom: 2em;
+ }
+
+ &.inline-label {
+ display: inline-block;
+
+ label {
+ float: left;
+ margin-right: 0.5em;
+ }
+ }
}
.toggleable {
- @include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out, visibility 0s linear 250ms');
- @include _(transform, scaleY(0));
- @include _(transform-origin, 0 0);
- max-height: 0;
- visibility: hidden;
+ @include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out, visibility 0s linear 250ms');
+ @include _(transform, scaleY(0));
+ @include _(transform-origin, 0 0);
+ max-height: 0;
+ visibility: hidden;
- &.open {
- @include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out');
- max-height: 100em;
- visibility: visible;
- @include _(transform, scaleX(1));
- }
+ &.open {
+ @include _(transition, 'transform 250ms ease-in-out, max-height 250ms ease-in-out');
+ max-height: 100em;
+ visibility: visible;
+ @include _(transform, scaleX(1));
+ }
}
.on-top-only, .on-top-controls {
- display: none !important;
+ display: none !important;
}
html[data-ontop] {
- body {
- overflow: hidden;
- }
-
- .on-top-target {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- padding: 1em;
- margin: 0 !important;
- z-index: 100;
- overflow: auto;
- }
-
- .on-top-control {
- position: fixed;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 101;
- margin: 0 !important;
- padding: 1em;
- background-color: #F8F8F8;
- @include default-box-shadow(bottom, 2);
- }
-
- .on-top-controls {
- overflow: auto;
-
- .on-top-close {
- float: right;
- background-color: $colour-4;
- }
- }
-
- .on-top-only, .on-top-controls {
- display: inherit !important;
-
- &.space {
- display: block !important;
- }
- }
-
- .not-on-top {
- display: none !important;
- }
+ body {
+ overflow: hidden;
+ }
+
+ .on-top-target {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 1em;
+ margin: 0 !important;
+ z-index: 100;
+ overflow: auto;
+ }
+
+ .on-top-control {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 101;
+ margin: 0 !important;
+ padding: 1em;
+ background-color: #F8F8F8;
+ @include default-box-shadow(bottom, 2);
+ }
+
+ .on-top-controls {
+ overflow: auto;
+
+ .on-top-close {
+ float: right;
+ background-color: $colour-4;
+ }
+ }
+
+ .on-top-only, .on-top-controls {
+ display: inherit !important;
+
+ &.space {
+ display: block !important;
+ }
+ }
+
+ .not-on-top {
+ display: none !important;
+ }
}
@include breakpoint(medium) {
- :focus,
- input[type="submit"]:focus,
- .check-box-field input:focus + label,
- .radio-button-field input:focus + label,
- .select-field select:focus,
- .workshop-link:focus .title,
- #main-nav .nav a:focus .title {
- html[data-input="kb"] & {
- outline: 0.25rem solid rgba($colour-2, 0.5);
- outline-offset: 0.2rem;
- z-index: 1;
- }
- }
-
- html[data-input="kb"] {
- #main-nav .logo:focus {
- outline-offset: -0.2em;
- }
-
- .workshop-link:focus,
- #main-nav .nav a:focus {
- outline: none;
- }
- }
-
- body {
- padding-bottom: 0;
- }
-
- h2 {
- font-size: 2.25em;
- }
-
- h3 {
- font-size: 1.75em;
- }
-
- p {
- font-size: 1.25em;
- }
-
- fieldset {
- &.right-help {
- float: left;
- text-align: left;
- margin-right: 1.5em;
- }
- }
-
- form {
- &.flex-form {
- @include _-(display, flex);
- @include _(align-items, flex-start);
-
- .input-field {
- @include _(flex, 1);
- }
-
- button, .button {
- margin-left: 1em;
- height: 2.6em;
- }
- }
- }
-
- .input-field {
- &.big select,
- &.big input {
- font-size: 2em;
- }
- }
-
- .flex-column {
- @include _-(display, flex);
- @include _(align-items, flex-start);
- margin-top: 1em;
-
- p:first-child {
- margin-top: 0;
- }
-
- .stretch-item {
- @include _(flex, 1);
- margin-right: 1em;
- }
- }
-
- .flex-form, .flex-column {
- button, .button {
- width: auto;
- }
- }
-
- .flex-inputs {
- .stretch-item {
- @include _(flex, 1);
- @include _(flex-basis, auto);
- }
- }
-
- #main-nav {
- .logo {
- margin-top: 0.25em;
- font-size: 4.25em;
- }
-
- .nav {
- position: static;
- font-size: 1.8em;
- display: block;
- text-align: right;
- background-color: transparent;
- @include _(box-shadow, none);
-
- form {
- background-color: transparent;
- }
- }
-
- .nav a, .nav button {
- width: auto;
- min-width: 3.5em;
- overflow: visible;
- margin-left: 0.725em;
- line-height: 3em;
- padding: 0 0 1em;
- color: $black;
- @include _(box-shadow, none);
- @include _(text-shadow, none);
-
- @include before-and-after {
- content: '';
- display: block;
- position: absolute;
- background-color: rgba(0, 0, 0, 0.33);
- z-index: -1;
- margin: 0;
- border: 0;
- font-size: 1em;
- left: -0.51em;
- right: -0.51em;
- top: 0;
- height: 0;
- width: auto;
- @include _(opacity, 1);
- @include _(transition, height 150ms ease-in-out);
- @include _(transform, rotate(-20deg) scaleX(0.94) scaleY(1.5) translate(-0.5em, -0.5em));
- @include _(transform-origin, 0 0);
- }
-
- @include after {
- height: 3em;
- @include _(opacity, 0.1);
- }
- @include before {
- box-shadow: 0 0 2em -0.5em rgba(#000, 0.75);
- @include _(opacity, 0.33);
- }
-
- &.current {
- z-index: 2;
-
- @include before {
- height: 3em;
- @include _(opacity, 0.67);
- }
- }
-
- &:hover {
- @include before {
- height: 3em;
- }
- }
- }
-
- .nav a {
- &[class] {
- background-color: transparent;
- }
-
- &.policy {
- @include before-and-after {
- background-color: $colour-5;
- }
- }
-
- &.about {
- @include before-and-after {
- background-color: $colour-3;
- }
- }
- }
-
- .nav a.register, .nav button {
- @include before-and-after {
- background-color: $colour-2;
- }
- &:focus, &:active {
- @include _(transform, none);
- }
- }
- }
-
- #banner {
- margin: 3em auto;
-
- figure {
- min-height: 40em;
- margin: rems(-20) auto rems(2);
- }
-
- .title {
- font-size: 1.9em;
- margin: 3em 0 1em;
- }
-
- img {
- @include _(box-shadow, 0 0 2em -0.5em rgba(0, 0, 0, 0.5));
- }
- }
-
- #registration-admin-menu {
- a {
- margin: 0;
- }
- }
-
- ul.warnings {
- li {
- margin: 1em 4em;
- }
- }
-
- .warning-info {
- margin-left: 1em;
- }
-
- .workshop-list {
- padding: 0 2em;
- }
-
- .event-dlg {
- min-width: 75%;
- }
-
- #main {
- clear: right;
-
- article {
- padding: rems(2.5) 7.5%;
- }
-
- .columns {
- form {
- margin-top: 2em;
- }
- }
-
- .featured-image-container {
- display: block;
-
- figure {
- float: left;
- width: 33%;
- margin: 0 1.5em 1em -1.5em;
- }
- }
- }
-
- #header-title {
- min-height: rems(35);
- font-size: 1em;
-
- &.short {
- font-size: 2em;
- min-height: rems(15);
-
- h1 {
- position: absolute;
- bottom: 0;
- text-align: left;
- font-size: 2em;
- }
-
- @include before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: $zindex-base + 2;
- @include _(box-shadow, inset 0 -13rem 3em -4rem rgba(0, 0, 0, 0.8));
- @include _(transition, opacity 250ms ease-in-out);
- }
- }
-
- &.no-image {
- min-height: 0;
- font-size: 1em;
- }
-
- svg {
- display: block;
- }
-
- svg rect {
- @include _(animation, banner-load 1s ease-in-out);
- }
-
- .title {
- text-align: left;
- width: 110%;
- background-color: rgba($white, 0.5);
- @include _(transform, rotate(-$header-tilt) translate3d(0, 0, 0));
- @include _(transform-origin, 0 100%);
- }
-
- .details {
- text-align: right;
- color: #000;
- }
-
- .background {
- display: block;
- }
-
- .primary {
- padding: 12% 0 0 0.25em;
- }
- }
-
- #footer {
- footer {
- font-size: 1em;
- }
-
- .github {
- @include _(flex, none);
- @include _(flex-basis, auto);
- margin: 0 1em;
- }
- .facebook, .external {
- margin: 0;
- }
-
- .site-info {
- width: auto;
- text-align: right;
- }
-
- .site-info, .user-controls, .external {
- width: auto;
- }
-
- ul.locales {
- @include _(flex, none);
- @include _(flex-basis, auto);
- @include _(flex-grow, 1);
- margin: 0 0 0 2em;
- text-align: left;
- }
- }
-
- .check-box-field,
- .radio-button-field {
- @include _-(display, flex);
-
- label {
- @include _(flex, 1);
- border: 0.1em solid;
- border-left: 0;
- text-align: left;
- }
-
- input:first-child + label {
- border: 0.1em solid;
- }
-
- &.inline {
- label {
- float: left;
- }
-
- &.vertical {
- label {
- float: none;
- }
- }
-
- input[type="radio"] + label {
- @include after {
- top: -0.025em;
- left: 0.175em;
- }
- }
- }
- }
-
- .fieldgroup {
- @include _-(display, flex);
- @include _-(display, inline-flex);
- }
-
- .select-field {
- select {
- width: auto;
- }
- }
-
- .admin-blocks {
- > li {
- @include _(flex, none);
- min-width: 12em;
- }
- }
-
- .flow-steps {
- ul {
- margin: 2em 0 1em 0;
- }
-
- .step {
- position: static;
- text-align: center;
- white-space: normal;
- @include _(transform, none);
- }
- }
+ :focus,
+ input[type="submit"]:focus,
+ .check-box-field input:focus + label,
+ .radio-button-field input:focus + label,
+ .select-field select:focus,
+ .workshop-link:focus .title,
+ #main-nav .nav a:focus .title {
+ html[data-input="kb"] & {
+ outline: 0.25rem solid rgba($colour-2, 0.5);
+ outline-offset: 0.2rem;
+ z-index: 1;
+ }
+ }
+
+ html[data-input="kb"] {
+ #main-nav .logo:focus {
+ outline-offset: -0.2em;
+ }
+
+ .workshop-link:focus,
+ #main-nav .nav a:focus {
+ outline: none;
+ }
+ }
+
+ body {
+ padding-bottom: 0;
+ }
+
+ h2 {
+ font-size: 2.25em;
+
+ &.floating {
+ float: left;
+ }
+ }
+
+ h3, legend {
+ font-size: 1.75em;
+ }
+
+ p {
+ font-size: 1.25em;
+ }
+
+ fieldset {
+ &.right-help {
+ float: left;
+ text-align: left;
+ margin-right: 1.5em;
+ }
+ }
+
+ form {
+ &.flex-form {
+ @include _-(display, flex);
+ @include _(align-items, flex-start);
+
+ .input-field {
+ @include _(flex, 1);
+ }
+
+ button, .button {
+ margin-left: 1em;
+ height: 2.6em;
+ }
+ }
+ }
+
+ .input-field {
+ &.big select,
+ &.big input {
+ font-size: 2em;
+ }
+ }
+
+ .flex-column {
+ @include _-(display, flex);
+ @include _(align-items, flex-start);
+ margin-top: 1em;
+
+ p:first-child {
+ margin-top: 0;
+ }
+
+ .stretch-item {
+ @include _(flex, 1);
+ margin-right: 1em;
+ }
+
+ .select-field ~ .number-field,
+ .select-field ~ .email-field,
+ .select-field ~ .search-field,
+ .select-field ~ .telephone-field,
+ .select-field ~ .password-field,
+ .select-field ~ .text-field {
+ margin-top: 1em;
+ }
+ }
+
+ .flex-form, .flex-column {
+ button, .button {
+ width: auto;
+ }
+ }
+
+ .flex-inputs {
+ .stretch-item {
+ @include _(flex, 1);
+ @include _(flex-basis, auto);
+ }
+ }
+
+ nav.sub-nav {
+ float: right;
+ }
+
+ #main-nav {
+ .logo {
+ margin-top: 0;
+ padding-top: 0.1em;
+ font-size: 4.25em;
+ }
+
+ .nav {
+ position: static;
+ font-size: 1.75em;
+ display: block;
+ text-align: center;
+ background-color: transparent;
+ @include _(box-shadow, none);
+
+ form {
+ background-color: transparent;
+ }
+ }
+
+ .nav a {
+ &[class] {
+ width: auto;
+ overflow: visible;
+ margin-left: 0.725em;
+ padding: 0.25em 0.5em;
+ @include _(box-shadow, none);
+ @include _(text-shadow, none);
+ background-color: transparent;
+ @include _(transform, 'translateY(0)');
+ @include _(transition, transform 150ms ease-in-out);
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: -0.375em;
+ left: 0;
+ width: 100%;
+ height: 0.5em;
+ background-color: currentColor;
+ }
+
+ .title {
+ position: relative;
+ color: $black;
+ }
+
+ &.policy {
+ color: $colour-5;
+ }
+
+ &.about {
+ color: $colour-3;
+ }
+
+ &.conferences {
+ color: $colour-2;
+ }
+
+ &:hover {
+ @include _(transform, 'translateY(0.25em)');
+ }
+
+ &.current {
+ @include _(transform, 'translateY(0.333em)');
+
+ @include before {
+ height: 0.5em;
+ border-bottom: 0.2em solid rgba($white, 0.25);
+ }
+ }
+ }
+
+ // .nav a.register, .nav button {
+ // @include before-and-after {
+ // background-color: $colour-2;
+ // }
+ // &:focus, &:active {
+ // @include _(transform, none);
+ // }
+ // }
+ }
+
+ #banner {
+ margin: 3em auto;
+
+ figure {
+ min-height: 40em;
+ margin: rems(-20) auto rems(2);
+ }
+ }
+
+ .conference-banner {
+ .title {
+ font-size: 1.9em;
+ margin: 0 0 1em;
+ }
+
+ img {
+ max-height: 62em;
+ max-width: 100%;
+ margin-bottom: 5.5em;
+ @include _(box-shadow, 0 0 2em -0.5em rgba(0, 0, 0, 0.5));
+ }
+ }
+
+ #registration-admin-menu {
+ a {
+ margin: 0;
+ }
+ }
+
+ ul.warnings {
+ li {
+ margin: 1em 4em;
+ }
+ }
+
+ .warning-info {
+ margin-left: 1em;
+ }
+
+ .list-view {
+ ul {
+ display: table;
+ width: 100%;
+ }
+
+ li {
+ display: table-row;
+ }
+
+ .info, .img {
+ display: table-cell;
+ }
+ }
+
+ .workshop-list {
+ padding: 0 2em;
+ }
+
+ .event-dlg {
+ min-width: 75%;
+ }
+
+ #main {
+ clear: right;
+
+ article {
+ padding: rems(2.5) 7.5%;
+ }
+
+ .columns {
+ form {
+ margin-top: 2em;
+ }
+ }
+
+ .featured-image-container {
+ display: block;
+
+ figure {
+ float: left;
+ width: 33%;
+ margin: 0 1.5em 1em -1.5em;
+ }
+ }
+ }
+
+ #header-title {
+ min-height: rems(35);
+ font-size: 1em;
+
+ h1 {
+ margin: 0.67em 0 0.5em;
+ }
+
+ .row {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ }
+
+ &.short {
+ font-size: 2em;
+ min-height: rems(15);
+
+ h1 {
+ position: absolute;
+ bottom: 0;
+ text-align: left;
+ font-size: 2em;
+ }
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: $zindex-base + 2;
+ @include _(box-shadow, inset 0 -13rem 3em -4rem rgba(0, 0, 0, 0.8));
+ @include _(transition, opacity 250ms ease-in-out);
+ }
+ }
+
+ &.map {
+ min-height: 0;
+ }
+
+ &.no-image {
+ min-height: 0;
+ font-size: 1em;
+ }
+
+ svg {
+ display: block;
+ }
+
+ svg rect {
+ @include _(animation, banner-load 1s ease-in-out);
+ }
+
+ .title {
+ text-align: left;
+ width: 110%;
+ background-color: rgba($white, 0.5);
+ @include _(transform, rotate(-$header-tilt) translate3d(0, 0, 0));
+ @include _(transform-origin, 0 100%);
+ }
+
+ .details {
+ text-align: right;
+ color: #000;
+ }
+
+ .background {
+ display: block;
+ }
+
+ .primary {
+ padding: 12% 0 0 0.25em;
+ }
+ }
+
+ #footer {
+ footer {
+ font-size: 1em;
+ }
+
+ .github {
+ @include _(flex, none);
+ @include _(flex-basis, auto);
+ margin: 0 1em;
+ }
+ .facebook, .external {
+ margin: 0;
+ }
+
+ .site-info {
+ width: auto;
+ text-align: right;
+ }
+
+ .site-info, .user-controls, .external {
+ width: auto;
+ }
+
+ ul.locales {
+ @include _(flex, none);
+ @include _(flex-basis, auto);
+ @include _(flex-grow, 1);
+ margin: 0 0 0 2em;
+ text-align: left;
+ }
+ }
+
+ .check-box-field,
+ .radio-button-field {
+ @include _-(display, flex);
+
+ label {
+ @include _(flex, 1);
+ border: 0.1em solid;
+ border-left: 0;
+ text-align: left;
+ }
+
+ input:first-child + label {
+ border: 0.1em solid;
+ }
+
+ &.inline {
+ label {
+ float: left;
+ }
+
+ &.vertical {
+ label {
+ float: none;
+ }
+ }
+
+ input[type="radio"] + label {
+ @include after {
+ top: -0.025em;
+ left: 0.175em;
+ }
+ }
+ }
+ }
+
+ .fieldgroup {
+ @include _-(display, flex);
+ @include _-(display, inline-flex);
+ }
+
+ .select-field {
+ select {
+ width: auto;
+ min-width: 100%;
+ }
+ }
+
+ .admin-blocks {
+ > li {
+ @include _(flex, none);
+ min-width: 12em;
+ }
+ }
+
+ .flow-steps {
+ ul {
+ margin: 2em 0 1em 0;
+ }
+
+ .step {
+ position: static;
+ text-align: center;
+ white-space: normal;
+ @include _(transform, none);
+ }
+ }
}
@include breakpoint(large) {
- #main-nav {
- .nav {
- font-size: 1.9em;
- margin-top: 0;
- }
- }
-
- #banner {
- body.fixed-banner & {
- position: fixed;
- top: 0;
- right: 0;
- left: $sidebar-width;
- z-index: $zindex-base + 2;
- }
-
- figure {
- width: 90%;
- }
- }
-
- #main {
- padding-left: $sidebar-width;
- }
-
- #header-title {
- &.no-image {
- min-height: 0;
- }
- &.short {
- min-height: rems(25);
- }
- &.no-image,
- &.short {
- h1 {
- text-align: left;
- margin: 0.67em 0;
- }
- }
- }
-
- #main-nav .logo {
- font-size: 5em;
- }
-
- a.logo {
- display: block;
- float: none;
- padding: 0.2em;
- }
-
- .actions {
- margin: 4em 2em 0 0;
- }
-
- #footer {
- overflow: visible;
- padding: 0;
- width: 100%;
- margin-bottom: 2.5em;
-
- footer {
- max-width: $row-width;
- margin: 0 auto;
- }
- }
+ // #main-nav {
+ // .nav {
+ // font-size: 1.9em;
+ // margin-top: 0;
+ // }
+ // }
+
+ #banner {
+ body.fixed-banner & {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: $sidebar-width;
+ z-index: $zindex-base + 2;
+ }
+
+ figure {
+ width: 90%;
+ }
+ }
+
+ #main {
+ padding-left: $sidebar-width;
+ }
+
+ #header-title {
+ &.no-image {
+ min-height: 0;
+ }
+ &.short {
+ min-height: rems(25);
+ }
+ &.no-image,
+ &.short {
+ h1 {
+ text-align: left;
+ margin: 0.67em 0;
+ }
+ }
+
+ &.map {
+ svg {
+ height: 35em;
+ }
+ }
+ }
+
+ #main-nav .logo {
+ font-size: 5em;
+ }
+
+ a.logo {
+ display: block;
+ float: none;
+ padding: 0.2em;
+ }
+
+ .actions {
+ margin: 4em 2em 0 0;
+ }
+
+ #footer {
+ overflow: visible;
+ padding: 0;
+ width: 100%;
+ margin-bottom: 2.5em;
+
+ footer {
+ max-width: $row-width;
+ margin: 0 auto;
+ }
+ }
+}
+
+@include breakpoint(small, medium) {
+ #main-nav .columns.medium-3 {
+ width: auto;
+ }
}
diff --git a/app/assets/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss
index 8228909..23e736f 100644
--- a/app/assets/stylesheets/_settings.scss
+++ b/app/assets/stylesheets/_settings.scss
@@ -14,156 +14,165 @@ $colour-5: #02CA9E; // green
$white: #FFFEFE;
$black: #333;
+$gray: #E8E8E8;
+$light-gray: #EEE;
+$mid-gray: #888;
+// $red: #B24C63;
+$red: #FF5A5F;
$link-colour: darken($colour-1, 13%);
@mixin default-box-shadow($direction: top, $distance: 1, $inset: false, $additional-shadow: false) {
- @if capable_of(box-shadow) {
- $offset: 0.2em;
- @if $direction == right or $direction == bottom {
- $offset: -$offset;
- }
- @if $direction == left or $direction == right {
- $offset: '#{$offset} 0';
- } @else {
- $offset: '0 #{$offset}';
- }
- @if $inset {
- $offset: 'inset #{$offset}';
- }
- @if $additional-shadow {
- $additional-shadow: ', #{$additional-shadow}';
- } @else {
- $additional-shadow: '';
- }
- @include _(box-shadow, #{$offset} 0.8em #{-0.2em * $distance} #000#{$additional-shadow});
- }
+ @if capable_of(box-shadow) {
+ $offset: 0.2em;
+ @if $direction == right or $direction == bottom {
+ $offset: -$offset;
+ }
+ @if $direction == left or $direction == right {
+ $offset: '#{$offset} 0';
+ } @else {
+ $offset: '0 #{$offset}';
+ }
+ @if $inset {
+ $offset: 'inset #{$offset}';
+ }
+ @if $additional-shadow {
+ $additional-shadow: ', #{$additional-shadow}';
+ } @else {
+ $additional-shadow: '';
+ }
+ @include _(box-shadow, #{$offset} 0.8em #{-0.2em * $distance} #000#{$additional-shadow});
+ }
}
@mixin monospace-font {
- @include font(monospace);
- @include font(monospace-bold);
+ @include font(monospace);
+ @include font(monospace-bold);
}
@mixin translation-pointer {
- @include keyframes(bouncy) {
- from { transform: translateY(-0.25em); }
- to { transform: translateY(0.25em); }
- }
-
- #lingua-franca-pointer {
- $colour: $colour-5;
- width: 0.6667em;
- height: 1.25em;
- background-color: $colour;
- z-index: 1000;
- margin-left: -0.5em;
- margin-top: -2em;
- @include _(mix-blend-mode, exclusion);
- @include default-box-shadow(top, 2);
- @include _(animation, bouncy 1s infinite alternate);
-
- $twidth: 0.8em;
- @include after {
- content: '';
- width: 0;
- height: 0;
- position: absolute;
- border-style: solid;
- border-color: $colour transparent transparent;
- border-width: $twidth $twidth 0;
- top: 100%;
- left: -.4em;
- }
-
- &.up {
- margin-top: 2em;
-
- @include after {
- top: auto;
- bottom: 100%;
- border-color: transparent transparent $colour;
- border-width: 0 $twidth $twidth;
- }
- }
- }
+ @include keyframes(bouncy) {
+ from { transform: translateY(-0.25em); }
+ to { transform: translateY(0.25em); }
+ }
+
+ #lingua-franca-pointer {
+ $colour: $colour-5;
+ width: 0.6667em;
+ height: 1.25em;
+ background-color: $colour;
+ z-index: 1000;
+ margin-left: -0.5em;
+ margin-top: -2em;
+ @include _(mix-blend-mode, exclusion);
+ @include default-box-shadow(top, 2);
+ @include _(animation, bouncy 1s infinite alternate);
+
+ $twidth: 0.8em;
+ @include after {
+ content: '';
+ width: 0;
+ height: 0;
+ position: absolute;
+ border-style: solid;
+ border-color: $colour transparent transparent;
+ border-width: $twidth $twidth 0;
+ top: 100%;
+ left: -.4em;
+ }
+
+ &.up {
+ margin-top: 2em;
+
+ @include after {
+ top: auto;
+ bottom: 100%;
+ border-color: transparent transparent $colour;
+ border-width: 0 $twidth $twidth;
+ }
+ }
+ }
+}
+
+@mixin text-stroke {
+ @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
}
@mixin button {
- position: relative;
- display: inline-block;
- color: $white;
- background-color: $colour-1;
- border: 0;
- padding: 0.5em 1em;
- font-size: 1.25em;
- outline: 0;
- border-bottom: 0.125em solid rgba(0, 0, 0, 0.15);
- @include _(border-radius, 0.15em);
- @include default-box-shadow(top, 2);
- overflow: hidden;
- cursor: pointer;
- @include _(text-stroke, 1px rgba(0, 0, 0, 0.25));
-
- @include before {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- background-color: rgba(0,0,0,0);
- @include _(transition, background-color 150ms ease-in-out);
- }
- /*@include before-and-after {
- content: '';
- position: absolute;
- display: block;
- top: 0;
- right: 100%;
- font-size: 1.15em;
- background-color: rgba(0, 0, 0, 0);
- border-color: transparent;
- @include _(transition, all 250ms ease-in-out);
- }
- @include before {
- height: 100%;
- width: 100%;
- margin-right: 1em;
- }
- @include after {
- border-style: solid;
- border-width: 1em 0 1em 1em;
- }*/
-
- /*&:hover,
- &:focus {
- @include before-and-after {
- right: -1em;
- border-left-color: rgba(0, 0, 0, 0.15);
- }
- }*/
- &:hover,
- &:focus {
- @include before {
- background-color: rgba(0, 0, 0, 0.15);
- }
- }
-
- &:active {
- @include _(transform, scale(0.95));
-
- @include after {
- left: 120%;
- }
- }
-
- &:disabled {
- @include _(opacity, 0.5);
- cursor: inherit;
-
- @include before-and-after {
- display: none;
- }
- }
+ position: relative;
+ display: inline-block;
+ color: $white;
+ background-color: $colour-1;
+ border: 0;
+ padding: 0.5em 1em;
+ font-size: 1.25em;
+ outline: 0;
+ border-bottom: 0.125em solid rgba(0, 0, 0, 0.15);
+ @include _(border-radius, 0.15em);
+ @include default-box-shadow(top, 2);
+ overflow: hidden;
+ cursor: pointer;
+ @include text-stroke;
+
+ @include before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ background-color: rgba(0,0,0,0);
+ @include _(transition, background-color 150ms ease-in-out);
+ }
+ /*@include before-and-after {
+ content: '';
+ position: absolute;
+ display: block;
+ top: 0;
+ right: 100%;
+ font-size: 1.15em;
+ background-color: rgba(0, 0, 0, 0);
+ border-color: transparent;
+ @include _(transition, all 250ms ease-in-out);
+ }
+ @include before {
+ height: 100%;
+ width: 100%;
+ margin-right: 1em;
+ }
+ @include after {
+ border-style: solid;
+ border-width: 1em 0 1em 1em;
+ }*/
+
+ /*&:hover,
+ &:focus {
+ @include before-and-after {
+ right: -1em;
+ border-left-color: rgba(0, 0, 0, 0.15);
+ }
+ }*/
+ &:hover,
+ &:focus {
+ @include before {
+ background-color: rgba(0, 0, 0, 0.15);
+ }
+ }
+
+ &:active {
+ @include _(transform, scale(0.95));
+
+ @include after {
+ left: 120%;
+ }
+ }
+
+ &:disabled {
+ @include _(opacity, 0.5);
+ cursor: inherit;
+
+ @include before-and-after {
+ display: none;
+ }
+ }
}
diff --git a/app/assets/stylesheets/bumbleberry-settings.json b/app/assets/stylesheets/bumbleberry-settings.json
index 0897d6f..b434909 100644
--- a/app/assets/stylesheets/bumbleberry-settings.json
+++ b/app/assets/stylesheets/bumbleberry-settings.json
@@ -5,8 +5,8 @@
"chrome": ["51"]
},
"development": {
- "and_chr": ["53"],
- "chrome": ["53"],
+ "and_chr": ["54"],
+ "chrome": ["54"],
"edge": ["13"],
"firefox": ["44"],
"ie": ["11"],
diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
new file mode 100644
index 0000000..616c58d
--- /dev/null
+++ b/app/controllers/admin_controller.rb
@@ -0,0 +1,36 @@
+require 'geocoder/calculations'
+
+class AdminController < ApplicationController
+ def new
+ return do_404 unless logged_in? && current_user.administrator?
+ @this_conference = Conference.new
+ @page_title = 'articles.conferences.headings.new'
+ end
+
+ def edit
+ return do_404 unless logged_in? && current_user.administrator?
+ @this_conference = Conference.find_by!(slug: params[:slug])
+ @page_title = 'articles.conferences.headings.edit'
+ render 'new'
+ end
+
+ def save
+ conference = params[:id].present? ? Conference.find_by!(id: params[:id]) : Conference.new
+
+ if params[:button] == 'save'
+ city = City.search(params[:city])
+ conference.city_id = city.id
+ conference.conferencetype = params[:type]
+ conference.year = params[:year].to_i
+ conference.is_public = params[:is_public].present?
+ conference.is_featured = params[:is_featured].present?
+ conference.make_slug(true)
+ conference.save!
+ elsif params[:button] == 'delete'
+ conference.destroy
+ return redirect_to conferences_url
+ end
+
+ redirect_to conference_url(conference.slug)
+ end
+end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 1d4f20d..0e358d0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,644 +1,741 @@
module ActiveRecord
- class PremissionDenied < RuntimeError
- end
+ class PremissionDenied < RuntimeError
+ end
end
class ApplicationController < LinguaFrancaApplicationController
- include ScheduleHelper
-
- # Prevent CSRF attacks by raising an exception.
- # For APIs, you may want to use :null_session instead.
- protect_from_forgery with: :exception, :except => [:do_confirm, :js_error, :admin_update]
-
- before_filter :capture_page_info
-
- @@test_host
- @@test_location
-
- def capture_page_info
- if request.method == "GET" && (params[:controller] != 'application' || params[:action] != 'contact')
- session[:last_request]
- request_info = {
- 'params' => params,
- 'request' => {
- 'remote_ip' => request.remote_ip,
- 'uuid' => request.uuid,
- 'original_url' => request.original_url,
- 'env' => Hash.new
- }
- }
- request.env.each do | key, value |
- request_info['request']['env'][key.to_s] = value.to_s
- end
- session['request_info'] = request_info
- end
- # set the translator to the current user if we're logged in
- I18n.config.translator = current_user
- I18n.config.callback = self
-
- # get the current conference and set it globally
- @conference = Conference.order("start_date DESC").first
-
- # add some style sheets
- @stylesheets ||= Array.new
- # add the translations stylesheet if translating
- @stylesheets << params[:controller] if params[:controller] == 'translations'
-
- @_inline_scripts ||= []
- @_inline_scripts << Rails.application.assets.find_asset('main.js').to_s
-
- ActionMailer::Base.default_url_options = {:host => "#{request.protocol}#{request.host_with_port}"}
-
- if request.post? && params[:action] == 'do_confirm'
- halt_redirection!
- end
-
- @alt_lang_urls = {}
- I18n.backend.enabled_locales.each do |locale|
- locale = locale.to_s
- @alt_lang_urls[locale] = view_context.url_for_locale(locale) # don't show the current locale
- end
-
- # give each environment a different icon and theme colour so that we can easily see where we are. See https://css-tricks.com/give-development-domain-different-favicon-production
- @favicon = Rails.env.development? || Rails.env.preview? ? "favicon-#{Rails.env.to_s}.ico" : 'favicon.ico'
- @theme_colour = Rails.env.preview? ? '#EF57B4' : (Rails.env.development? ? '#D89E59' : '#00ADEF')
-
- # call the base method to detect the language
- super
- end
-
- def home
- @workshops = Workshop.where(:conference_id => @conference.id)
-
- if @conference.workshop_schedule_published
- @event_dlg = true
- #view_context.add_inline_script :schedule
- get_scheule_data(false)
- # @events = Event.where(:conference_id => @conference.id)
- # schedule = get_schedule_data
- # @schedule = schedule[:schedule]
- # @locations = Hash.new
- # EventLocation.where(:conference_id => @conference.id).each do |l|
- # @locations[l.id.to_s] = l
- # end
- # @day_parts = @conference.day_parts ? JSON.parse(@conference.day_parts) : {:morning => 0, :afternoon => 13, :evening => 18}
- end
- end
-
- def policy
- @is_policy_page = true
- end
-
- def robots
- render :text => File.read("config/robots-#{Rails.env.production? ? 'live' : 'dev'}.txt"), :content_type => 'text/plain'
- end
-
- def humans
- render :text => File.read("config/humans.txt"), :content_type => 'text/plain'
- end
-
- def self.set_host(host)
- @@test_host = host
- end
-
- def self.set_location(location)
- @@test_location = location
- end
-
- def self.get_location()
- @@test_location
- end
-
- def do_404
- error_404(status: 404)
- end
-
- def error_404(args = {})
- params[:_original_action] = params[:action]
- params[:action] = 'error-404'
- @page_title = 'page_titles.404.Page_Not_Found'
- @main_title = 'error.404.title'
- render 'application/404', args
- end
-
- def do_403(template = nil)
- @template = template
- @page_title ||= 'page_titles.403.Access_Denied'
- @main_title ||= @page_title
- params[:_original_action] = params[:action]
- params[:action] = 'error-403'
- render 'application/permission_denied', status: 403
- end
-
- def error_500(exception = nil)
- @page_title = 'page_titles.500.An_Error_Occurred'
- @main_title = 'error.500.title'
- params[:_original_action] = params[:action]
- params[:action] = 'error-500'
- render 'application/500', status: 500
- end
-
- def js_error
- # send and email if this is production
- report = "A JavaScript error has occurred on
#{params[:location]}
"
- if params[:location] == params[:url]
- report += " on line
#{params[:lineNumber]}
"
- else
- report += " in
#{params[:url]}:#{params[:lineNumber]}
"
- end
-
- begin
- # log the error
- logger.info exception.to_s
- logger.info exception.backtrace.join("\n")
-
- UserMailer.send_mail(:error_report) do
- [
- "A JavaScript error has occurred",
- report,
- params[:message],
- nil,
- request,
- params,
- current_user,
- Time.now.strftime("%d/%m/%Y %H:%M")
- ]
- end if Rails.env.preview? || Rails.env.production?
- rescue exception2
- logger.info exception2.to_s
- logger.info exception2.backtrace.join("\n")
- end
- render json: {}
- end
-
- rescue_from ActiveRecord::RecordNotFound do |exception|
- do_404
- end
-
- rescue_from ActiveRecord::PremissionDenied do |exception|
- do_403
- end
-
- rescue_from AbstractController::ActionNotFound do |exception|
- @banner_image = 'grafitti.jpg'
-
- if current_user
- @page_title = nil#'page_titles.Please_Login'
- do_403 'not_a_translator'
- #return
- else
- @page_title = 'page_titles.403.Please_Login'
- do_403 'translator_login'
- end
- end
-
- def locale_not_enabled!(locale = nil)
- locale_not_available!(locale)
- end
-
- def locale_not_available!(locale = nil)
- set_default_locale
- params[:_original_action] = params[:action]
- params[:action] = 'error-locale-not-available'
- @page_title = 'page_titles.404.Locale_Not_Available'
- @main_title_vars = { vars: { language: view_context.language_name(locale) } }
- @main_title = 'error.locale_not_available.title'
- render 'application/locale_not_available', status: 404
- end
-
- rescue_from StandardError do |exception|
- # log the error
- logger.info exception.to_s
- logger.info exception.backtrace.join("\n")
-
- # send and email if this is production
- suppress(Exception) do
- UserMailer.send_mail(:error_report) do
- [
- "An error has occurred in #{Rails.env}",
- nil,
- exception.to_s,
- exception.backtrace.join("\n"),
- request,
- params,
- current_user,
- Time.now.strftime("%d/%m/%Y %H:%M")
- ]
- end if Rails.env.preview? || Rails.env.production?
- end
-
- # raise the error if we are in development so that we can debug it
- raise exception if Rails.env.development?
-
- # show the error page
- error_500 exception
- end
-
- def generate_confirmation(user, url, expiry = nil)
- if user.is_a? String
- user = User.find_by_email(user)
-
- # if the user doesn't exist, just show them a 403
- do_403 unless user.present?
- end
- expiry ||= (Time.now + 12.hours)
- session[:confirm_uid] = user.id
-
- unless user.locale.present?
- user.locale = I18n.locale
- user.save
- end
-
- # send the confirmation email and make sure it get sent as quickly as possible
- UserMailer.send_mail :email_confirmation do
- EmailConfirmation.create(user_id: user.id, expiry: expiry, url: url)
- end
- end
-
- def user_settings
- @conferences = Array.new
- if logged_in?
- Conference.all.each do | conference |
- @conferences << conference if conference.host? current_user
- end
- end
- @main_title = @page_title = 'page_titles.user_settings.Your_Account'
- end
-
- def contact
- @main_title = @page_title = 'page_titles.contact.Contact_Us'
- end
-
- def contact_send
- email_list = ['Godwin
']
-
- if params[:reason] == 'conference'
-
- @conference.organizations.each do | org |
- org.users.each do | user |
- email_list << user.named_email
- end
- end
- end
-
- UserMailer.send_mail(:contact) do
- [
- current_user || params[:email],
- params[:subject],
- params[:message],
- email_list
- ]
- end
-
- request_info = session['request_info'] || { 'request' => request, 'params' => params }
- UserMailer.send_mail(:contact_details) do
- [
- current_user || params[:email],
- params[:subject],
- params[:message],
- request_info['request'],
- request_info['params']
- ]
- end
-
- redirect_to contact_sent_path
- end
-
- def contact_sent
- @main_title = @page_title = 'page_titles.contact.Contact_Us'
- @sent = true
- render 'contact'
- end
-
- def update_user_settings
- return do_403 unless logged_in?
- current_user.firstname = params[:name]
- current_user.lastname = nil
- current_user.languages = params[:languages].keys
- current_user.is_subscribed = params[:email_subscribe].present?
- current_user.save
- redirect_to settings_path
- end
-
- def do_confirm(settings = nil)
- settings ||= {:template => 'login_confirmation_sent'}
- if params[:email]
- # see if we've already sent the confirmation email and are just confirming
- # the email address
- if params[:token]
- user = User.find_by_email(params[:email])
- confirm(user)
- return
- end
- user = User.find_by_email(params[:email])
-
- unless user.present?
- user = User.create(:email => params[:email], locale: I18n.locale)
- end
-
- # generate the confirmation, send the email and show the 403
- referrer = params[:dest] || (request.present? && request.referer.present? ? request.referer.gsub(/^.*?\/\/.*?\//, '/') : settings_path)
- generate_confirmation(params[:email], referrer)
- template = 'login_confirmation_sent'
- @page_title ||= 'page_titles.403.Please_Check_Email'
-
- if (request.present? && request.referrer.present? && conference = /^\/conferences\/(\w+)\/register\/?$/.match(request.referrer.gsub(/^https?:\/\/.*?\//, '/')))
- @this_conference = Conference.find_by!(slug: conference[1])
- @banner_image = @this_conference.cover_url
- template = 'conferences/email_confirm'
- end
- end
-
- if request.post?
- @banner_image ||= 'grafitti.jpg'
- @page_title ||= 'page_titles.403.Please_Login'
-
- do_403 (template || 'translator_login')
- else
- do_404
- end
- end
-
- def confirm(uid = nil)
- @confirmation = EmailConfirmation.find_by_token(params[:token])
-
- unless @confirmation.present?
- @token_not_found = true
- return do_404
- end
-
- confirm_user = nil
- if uid.is_a?(User)
- confirm_user = uid
- uid = confirm_user.id
- end
- # check to see if we were given a user id to confirm against
- # if we were, make sure it was the same one
- if (uid ||= (params[:uid] || session[:confirm_uid]))
- if uid == @confirmation.user_id
- session[:uid] = nil
- confirm_user ||= User.find uid
- auto_login(confirm_user)
- else
- @confirmation.delete
- end
-
- redirect_to (@confirmation.url || '/')
- return
- end
-
- @banner_image = 'grafitti.jpg'
- @page_title = 'page_titles.403.Please_Confirm_Email'
- do_403 'login_confirm'
- end
-
- def translator_request
- @banner_image = 'grafitti.jpg'
- @page_title = 'page_titles.403.Translator_Request_Sent'
- do_403 'translator_request_sent'
- end
-
- def user_logout
- logout()
- redirect_to (params[:url] || '/')
- end
-
- def login_user(u)
- auto_login(u)
- end
-
- def on_translation_change(object, data, locale, translator_id)
- translator = User.find(translator_id)
- mailer = "#{object.class.table_name.singularize}_translated"
-
- if object.respond_to?(:get_translators)
- object.get_translators(data, locale).each do |id, user|
- if user.id != current_user.id && user.id != translator_id
- UserMailer.send_mail mailer, user.locale do
- { :args => [object, data, locale, user, translator] }
- end
- end
- end
- end
- end
-
- def on_translatable_content_change(object, data)
- mailer = "#{object.class.table_name.singularize}_original_content_changed"
-
- if object.respond_to?(:get_translators)
- object.get_translators(data).each do |id, user|
- if user.id != current_user.id
- UserMailer.send_mail mailer, user.locale do
- { :args => [object, data, user, current_user] }
- end
- end
- end
- end
- end
-
- def i18n_exception(str, exception, locale, key)
- # log it
- logger.info "Missing translation found for: #{key}"
-
- # send and email if this is production
- begin
- UserMailer.send_mail(:error_report) do
- [
- "A missing translation found in #{Rails.env}",
- "A translation for #{key}
in #{locale.to_s}
was found. The text that was rendered to the user was:
#{str || 'nil'} ",
- exception.to_s,
- nil,
- request,
- params,
- current_user,
- Time.now.strftime("%d/%m/%Y %H:%M")
- ]
- end if Rails.env.preview? || Rails.env.production?
- rescue exception2
- logger.info exception2.to_s
- logger.info exception2.backtrace.join("\n")
- end
- end
-
-
- def get_block_data
- conference = @this_conference || @conference
- @workshop_blocks = conference.workshop_blocks || []
- @block_days = []
- day = conference.start_date
- while day <= conference.end_date
- @block_days << day.wday
- day += 1.day
- end
- end
-
- def get_scheule_data(do_analyze = true)
- conference = @this_conference || @conference
- @meals = Hash[(conference.meals || {}).map{ |k, v| [k.to_i, v] }].sort.to_h
- @events = Event.where(:conference_id => conference.id).order(start_time: :asc)
- @workshops = Workshop.where(:conference_id => conference.id).order(start_time: :asc)
- @locations = {}
-
- get_block_data
-
- @schedule = {}
- day_1 = conference.start_date.wday
-
- @workshop_blocks.each_with_index do | info, block |
- info['days'].each do | block_day |
- day_diff = block_day.to_i - day_1
- day_diff += 7 if day_diff < 0
- day = (conference.start_date + day_diff.days).to_date
- time = info['time'].to_f
- @schedule[day] ||= { times: {}, locations: {} }
- @schedule[day][:times][time] ||= {}
- @schedule[day][:times][time][:type] = :workshop
- @schedule[day][:times][time][:length] = info['length'].to_f
- @schedule[day][:times][time][:item] = { block: block, workshops: {} }
- end
- end
-
- @workshops.each do | workshop |
- if workshop.block.present?
- block = @workshop_blocks[workshop.block['block'].to_i]
- day_diff = workshop.block['day'].to_i - day_1
- day_diff += 7 if day_diff < 0
- day = (conference.start_date + day_diff.days).to_date
-
- if @schedule[day].present? && @schedule[day][:times].present? && @schedule[day][:times][block['time'].to_f].present?
- @schedule[day][:times][block['time'].to_f][:item][:workshops][workshop.event_location_id] = { workshop: workshop, status: { errors: [], warnings: [], conflict_score: nil } }
- @schedule[day][:locations][workshop.event_location_id] ||= workshop.event_location if workshop.event_location.present?
- end
- end
- end
-
- @meals.each do | time, meal |
- day = meal['day'].to_date
- time = meal['time'].to_f
- @schedule[day] ||= {}
- @schedule[day][:times] ||= {}
- @schedule[day][:times][time] ||= {}
- @schedule[day][:times][time][:type] = :meal
- @schedule[day][:times][time][:length] = (meal['length'] || 1.0).to_f
- @schedule[day][:times][time][:item] = meal
- end
-
- @events.each do | event |
- day = event.start_time.midnight.to_date
- time = event.start_time.hour.to_f + (event.start_time.min / 60.0)
- @schedule[day] ||= {}
- @schedule[day][:times] ||= {}
- @schedule[day][:times][time] ||= {}
- @schedule[day][:times][time][:type] = :event
- @schedule[day][:times][time][:length] = (event.end_time - event.start_time) / 3600.0
- @schedule[day][:times][time][:item] = event
- end
-
- @schedule = @schedule.sort.to_h
- @schedule.each do | day, data |
- @schedule[day][:times] = data[:times].sort.to_h
- end
-
- @schedule.each do | day, data |
- last_event = nil
- data[:times].each do | time, time_data |
- if last_event.present?
- @schedule[day][:times][last_event][:next_event] = time
- end
- last_event = time
- end
- @schedule[day][:num_locations] = (data[:locations] || []).size
- end
-
- @schedule.deep_dup.each do | day, data |
- data[:times].each do | time, time_data |
- if time_data[:next_event].present? || time_data[:length] > 0.5
- span = 0.5
- length = time_data[:next_event].present? ? time_data[:next_event] - time : time_data[:length]
- while span < length
- @schedule[day][:times][time + span] ||= {
- type: (span >= time_data[:length] ? :empty : :nil),
- length: 0.5
- }
- span += 0.5
- end
- end
- end
- end
-
- @schedule = @schedule.sort.to_h
-
- @schedule.each do | day, data |
- @schedule[day][:times] = data[:times].sort.to_h
- @schedule[day][:locations] ||= {}
-
- # add an empty block if no workshops are scheduled on this day yet
- @schedule[day][:locations][0] = :add if do_analyze || @schedule[day][:locations].empty?
-
- if do_analyze
- data[:times].each do | time, time_data |
- if time_data[:type] == :workshop && time_data[:item].present? && time_data[:item][:workshops].present?
- ids = time_data[:item][:workshops].keys
- (0..ids.length).each do | i |
- if time_data[:item][:workshops][ids[i]].present?
- workshop_i = time_data[:item][:workshops][ids[i]][:workshop]
- conflicts = {}
-
- (i+1..ids.length).each do | j |
- workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
- if workshop_i.present? && workshop_j.present?
- workshop_i.active_facilitators.each do | facilitator_i |
- workshop_j.active_facilitators.each do | facilitator_j |
- if facilitator_i.id == facilitator_j.id
- @schedule[day][:times][time][:status] ||= {}
- @schedule[day][:times][time][:item][:workshops][ids[j]][:status][:errors] << {
- name: :common_facilitator,
- facilitator: facilitator_i,
- workshop: workshop_i,
- i18nVars: {
- facilitator_name: facilitator_i.name,
- workshop_title: workshop_i.title
- }
- }
- end
- end
- end
- end
- end
-
- location = workshop_i.event_location || EventLocation.new
- needs = JSON.parse(workshop_i.needs || '[]').map &:to_sym
- amenities = JSON.parse(location.amenities || '[]').map &:to_sym
-
- needs.each do | need |
- @schedule[day][:times][time][:item][:workshops][ids[i]][:status][:errors] << {
- name: :need_not_available,
- need: need,
- location: location,
- workshop: workshop_i,
- i18nVars: {
- need: need.to_s,
- location: location.title,
- workshop_title: workshop_i.title
- }
- } unless amenities.include? need
- end
-
- # collect common interested users
- interests = []
- (0..ids.length).each do | j |
- workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
- if workshop_i.present? && workshop_j.present? && workshop_i.id != workshop_j.id
- interests = interests | workshop_j.interested.map { | u | u.user_id }
- end
- end
-
- @schedule[day][:times][time][:item][:workshops][ids[i]][:status][:conflict_score] = (interests & (workshop_i.interested.map { | u | u.user_id })).length
- end
- end
- end
- end
- end
- end
- end
+ # Prevent CSRF attacks by raising an exception.
+ # For APIs, you may want to use :null_session instead.
+ protect_from_forgery with: :exception, :except => [:do_confirm, :js_error, :admin_update]
+
+ before_filter :capture_page_info
+
+ helper_method :protect
+
+ @@test_host
+ @@test_location
+
+ def capture_page_info
+ if request.method == "GET" && (params[:controller] != 'application' || params[:action] != 'contact')
+ session[:last_request]
+ request_info = {
+ 'params' => params,
+ 'request' => {
+ 'remote_ip' => request.remote_ip,
+ 'uuid' => request.uuid,
+ 'original_url' => request.original_url,
+ 'env' => Hash.new
+ }
+ }
+ request.env.each do | key, value |
+ request_info['request']['env'][key.to_s] = value.to_s
+ end
+ session['request_info'] = request_info
+ end
+ # set the translator to the current user if we're logged in
+ I18n.config.translator = current_user
+ I18n.config.callback = self
+
+ # get the current conferences and set them globally
+ status_hierarchy = {
+ open: 1,
+ pre: 2,
+ closed: 3
+ }
+ @conferences = Conference.where(is_featured: true, is_public: true).order("start_date DESC").sort do |a, b|
+ status_hierarchy[(a.registration_status || :closed).to_sym] <=> status_hierarchy[(b.registration_status || :closed).to_sym]
+ end
+
+ # set the top conference
+ @conference = @conferences.first
+
+ # add some style sheets
+ @stylesheets ||= Array.new
+ # add the translations stylesheet if translating
+ @stylesheets << params[:controller] if params[:controller] == 'translations'
+
+ @_inline_scripts ||= []
+ @_inline_scripts << Rails.application.assets.find_asset('main.js').to_s
+
+ ActionMailer::Base.default_url_options = {:host => "#{request.protocol}#{request.host_with_port}"}
+
+ if request.post? && params[:action] == 'do_confirm'
+ halt_redirection!
+ end
+
+ @alt_lang_urls = {}
+ I18n.backend.enabled_locales.each do |locale|
+ locale = locale.to_s
+ @alt_lang_urls[locale] = view_context.url_for_locale(locale) # don't show the current locale
+ end
+
+ # give each environment a different icon and theme colour so that we can easily see where we are. See https://css-tricks.com/give-development-domain-different-favicon-production
+ @favicon = Rails.env.development? || Rails.env.preview? ? "favicon-#{Rails.env.to_s}.ico" : 'favicon.ico'
+ @theme_colour = Rails.env.preview? ? '#EF57B4' : (Rails.env.development? ? '#D89E59' : '#00ADEF')
+
+ # call the base method to detect the language
+ super
+ end
+
+ def home
+ @workshops = Workshop.where(:conference_id => @conference.id)
+
+ if @conference.workshop_schedule_published
+ @event_dlg = true
+ get_scheule_data(false)
+ end
+ end
+
+ def policy
+ @is_policy_page = true
+ end
+
+ def robots
+ render :text => File.read("config/robots-#{Rails.env.production? ? 'live' : 'dev'}.txt"), :content_type => 'text/plain'
+ end
+
+ def humans
+ render :text => File.read("config/humans.txt"), :content_type => 'text/plain'
+ end
+
+ def self.set_host(host)
+ @@test_host = host
+ end
+
+ def self.set_location(location)
+ @@test_location = location
+ end
+
+ def self.get_location()
+ @@test_location
+ end
+
+ def do_404
+ error_404(status: 404)
+ end
+
+ def error_404(args = {})
+ params[:_original_action] = params[:action]
+ params[:action] = 'error-404'
+ @page_title = 'page_titles.404.Page_Not_Found'
+ @main_title = 'error.404.title'
+ render 'application/404', args
+ end
+
+ def do_403(template = nil)
+ @template = template
+ @page_title ||= 'page_titles.403.Access_Denied'
+ @main_title ||= @page_title
+ params[:_original_action] = params[:action]
+ params[:action] = 'error-403'
+ render 'application/permission_denied', status: 403
+ end
+
+ def error_500(exception = nil)
+ @page_title = 'page_titles.500.An_Error_Occurred'
+ @main_title = 'error.500.title'
+ params[:_original_action] = params[:action]
+ params[:action] = 'error-500'
+ render 'application/500', status: 500
+ end
+
+ def js_error
+ # send and email if this is production
+ report = "A JavaScript error has occurred on #{params[:location]}
"
+ if params[:location] == params[:url]
+ report += " on line #{params[:lineNumber]}
"
+ else
+ report += " in #{params[:url]}:#{params[:lineNumber]}
"
+ end
+
+ begin
+ # log the error
+ logger.info exception.to_s
+ logger.info exception.backtrace.join("\n")
+
+ UserMailer.send_mail(:error_report) do
+ [
+ "A JavaScript error has occurred",
+ report,
+ params[:message],
+ nil,
+ request,
+ params,
+ current_user,
+ Time.now.strftime("%d/%m/%Y %H:%M")
+ ]
+ end if Rails.env.preview? || Rails.env.production?
+ rescue exception2
+ logger.info exception2.to_s
+ logger.info exception2.backtrace.join("\n")
+ end
+ render json: {}
+ end
+
+ rescue_from ActiveRecord::RecordNotFound do |exception|
+ do_404
+ end
+
+ rescue_from ActiveRecord::PremissionDenied do |exception|
+ do_403
+ end
+
+ rescue_from AbstractController::ActionNotFound do |exception|
+ @banner_image = 'grafitti.jpg'
+
+ if current_user
+ @page_title = nil#'page_titles.Please_Login'
+ do_403 'not_a_translator'
+ #return
+ else
+ @page_title = 'page_titles.403.Please_Login'
+ do_403 'translator_login'
+ end
+ end
+
+ def locale_not_enabled!(locale = nil)
+ locale_not_available!(locale)
+ end
+
+ def locale_not_available!(locale = nil)
+ set_default_locale
+ params[:_original_action] = params[:action]
+ params[:action] = 'error-locale-not-available'
+ @page_title = 'page_titles.404.Locale_Not_Available'
+ @main_title_vars = { vars: { language: view_context.language_name(locale) } }
+ @main_title = 'error.locale_not_available.title'
+ render 'application/locale_not_available', status: 404
+ end
+
+ rescue_from StandardError do |exception|
+ handle_exception exception
+
+ # show the error page
+ error_500 exception
+ end
+
+ def handle_exception(exception)
+ # log the error
+ logger.info exception.to_s
+ logger.info exception.backtrace.join("\n")
+
+ # send and email if this is production
+ suppress(Exception) do
+ UserMailer.send_mail(:error_report) do
+ [
+ "An error has occurred in #{Rails.env}",
+ nil,
+ exception.to_s,
+ exception.backtrace.join("\n"),
+ request,
+ params,
+ current_user,
+ Time.now.strftime("%d/%m/%Y %H:%M")
+ ]
+ end if Rails.env.preview? || Rails.env.production?
+ end
+
+ # raise the error if we are in development so that we can debug it
+ raise exception if Rails.env.development?
+ end
+
+ def protect(&block)
+ begin
+ yield
+ rescue Exception => exception
+ handle_exception exception
+ end
+ end
+
+ def generate_confirmation(user, url, expiry = nil)
+ if user.is_a? String
+ user = User.find_by_email(user)
+
+ # if the user doesn't exist, just show them a 403
+ do_403 unless user.present?
+ end
+ expiry ||= (Time.now + 12.hours)
+ session[:confirm_uid] = user.id
+
+ unless user.locale.present?
+ user.locale = I18n.locale
+ user.save
+ end
+
+ # send the confirmation email and make sure it get sent as quickly as possible
+ UserMailer.send_mail :email_confirmation do
+ EmailConfirmation.create(user_id: user.id, expiry: expiry, url: url)
+ end
+ end
+
+ def user_settings
+ @conferences = Array.new
+ if logged_in?
+ Conference.all.each do | conference |
+ @conferences << conference if conference.host? current_user
+ end
+ end
+ @main_title = @page_title = 'page_titles.user_settings.Your_Account'
+ end
+
+ def contact
+ @main_title = @page_title = 'page_titles.contact.Contact_Us'
+ end
+
+ def contact_send
+ email_list = ['Godwin ']
+
+ if params[:reason] == 'conference'
+
+ @conference.organizations.each do | org |
+ org.users.each do | user |
+ email_list << user.named_email
+ end
+ end
+ end
+
+ UserMailer.send_mail(:contact) do
+ [
+ current_user || params[:email],
+ params[:subject],
+ params[:message],
+ email_list
+ ]
+ end
+
+ request_info = session['request_info'] || { 'request' => request, 'params' => params }
+ UserMailer.send_mail(:contact_details) do
+ [
+ current_user || params[:email],
+ params[:subject],
+ params[:message],
+ request_info['request'],
+ request_info['params']
+ ]
+ end
+
+ redirect_to contact_sent_path
+ end
+
+ def contact_sent
+ @main_title = @page_title = 'page_titles.contact.Contact_Us'
+ @sent = true
+ render 'contact'
+ end
+
+ def update_user_settings
+ return do_403 unless logged_in?
+ current_user.firstname = params[:name]
+ current_user.lastname = nil
+ current_user.languages = params[:languages].keys
+ current_user.is_subscribed = params[:email_subscribe].present?
+ current_user.save
+ redirect_to settings_path
+ end
+
+ def do_confirm(settings = nil)
+ settings ||= {:template => 'login_confirmation_sent'}
+ if params[:email]
+ # see if we've already sent the confirmation email and are just confirming
+ # the email address
+ if params[:token]
+ user = User.find_by_email(params[:email])
+ confirm(user)
+ return
+ end
+ user = User.find_by_email(params[:email])
+
+ unless user.present?
+ user = User.create(:email => params[:email], locale: I18n.locale)
+ end
+
+ # generate the confirmation, send the email and show the 403
+ referrer = params[:dest] || (request.present? && request.referer.present? ? request.referer.gsub(/^.*?\/\/.*?\//, '/') : settings_path)
+ generate_confirmation(params[:email], referrer)
+ template = 'login_confirmation_sent'
+ @page_title ||= 'page_titles.403.Please_Check_Email'
+
+ if (request.present? && request.referrer.present? && conference = /^\/conferences\/(\w+)\/register\/?$/.match(request.referrer.gsub(/^https?:\/\/.*?\//, '/')))
+ @this_conference = Conference.find_by!(slug: conference[1])
+ @banner_image = @this_conference.cover_url
+ template = 'conferences/email_confirm'
+ end
+ end
+
+ if request.post?
+ @banner_image ||= 'grafitti.jpg'
+ @page_title ||= 'page_titles.403.Please_Login'
+
+ do_403 (template || 'translator_login')
+ else
+ do_404
+ end
+ end
+
+ def confirm(uid = nil)
+ @confirmation = EmailConfirmation.find_by_token(params[:token])
+
+ unless @confirmation.present?
+ @token_not_found = true
+ return do_404
+ end
+
+ confirm_user = nil
+ if uid.is_a?(User)
+ confirm_user = uid
+ uid = confirm_user.id
+ end
+ # check to see if we were given a user id to confirm against
+ # if we were, make sure it was the same one
+ if (uid ||= (params[:uid] || session[:confirm_uid]))
+ if uid == @confirmation.user_id
+ session[:uid] = nil
+ confirm_user ||= User.find uid
+ auto_login(confirm_user)
+ else
+ @confirmation.delete
+ end
+
+ redirect_to (@confirmation.url || '/')
+ return
+ end
+
+ @banner_image = 'grafitti.jpg'
+ @page_title = 'page_titles.403.Please_Confirm_Email'
+ do_403 'login_confirm'
+ end
+
+ def translator_request
+ @banner_image = 'grafitti.jpg'
+ @page_title = 'page_titles.403.Translator_Request_Sent'
+ do_403 'translator_request_sent'
+ end
+
+ def user_logout
+ logout()
+ redirect_to (params[:url] || '/')
+ end
+
+ def find_user
+ user = User.find_user(params[:e])
+
+ if user.present?
+ return render json: {
+ name: user.name,
+ email: user.email,
+ exists: true
+ }
+ end
+ render json: {
+ name: I18n.t('user.not_found'),
+ email: nil,
+ exists: false
+ }
+ end
+
+ def login_user(u)
+ auto_login(u)
+ end
+
+ def on_translation_change(object, data, locale, translator_id)
+ translator = User.find(translator_id)
+ mailer = "#{object.class.table_name.singularize}_translated"
+
+ if object.respond_to?(:get_translators)
+ object.get_translators(data, locale).each do |id, user|
+ if user.id != current_user.id && user.id != translator_id
+ UserMailer.send_mail mailer, user.locale do
+ { :args => [object, data, locale, user, translator] }
+ end
+ end
+ end
+ end
+ end
+
+ def on_translatable_content_change(object, data)
+ mailer = "#{object.class.table_name.singularize}_original_content_changed"
+
+ if object.respond_to?(:get_translators)
+ object.get_translators(data).each do |id, user|
+ if user.id != current_user.id
+ UserMailer.send_mail mailer, user.locale do
+ { :args => [object, data, user, current_user] }
+ end
+ end
+ end
+ end
+ end
+
+ def i18n_exception(str, exception, locale, key)
+ # log it
+ logger.info "Missing translation found for: #{key}"
+
+ # send and email if this is production
+ begin
+ UserMailer.send_mail(:error_report) do
+ [
+ "A missing translation found in #{Rails.env}",
+ "A translation for #{key}
in #{locale.to_s}
was found. The text that was rendered to the user was:
#{str || 'nil'} ",
+ exception.to_s,
+ nil,
+ request,
+ params,
+ current_user,
+ Time.now.strftime("%d/%m/%Y %H:%M")
+ ]
+ end if Rails.env.preview? || Rails.env.production?
+ rescue exception2
+ logger.info exception2.to_s
+ logger.info exception2.backtrace.join("\n")
+ end
+ end
+
+ def set_success_message(message, is_ajax = false)
+ if is_ajax
+ @success_message = message
+ else
+ flash[:success_message] = message
+ end
+ end
+
+ def set_error_message(message, is_ajax = false)
+ if is_ajax
+ @error_message = message
+ else
+ flash[:error_message] = message
+ end
+ end
+
+ def set_error(field, error, is_ajax = false)
+ if is_ajax
+ @errors ||= {}
+ @errors[field] = error
+ else
+ flash[:errors] ||= {}
+ flash[:errors][field] = error
+ end
+ end
+
+ def set_flash_messages
+ @errors = flash[:errors] || {}
+ @warnings = flash[:warning] || []
+ @success_message = flash[:success_message]
+ @error_message = flash[:error_message] || []
+ end
+
+ def get_block_data
+ conference = @this_conference || @conference
+ @workshop_blocks = conference.workshop_blocks || []
+ @block_days = []
+ day = conference.start_date
+ while day <= conference.end_date
+ @block_days << day.wday
+ day += 1.day
+ end
+ end
+
+ def get_scheule_data(do_analyze = true)
+ conference = @this_conference || @conference
+ @meals = Hash[(conference.meals || {}).map{ |k, v| [k.to_i, v] }].sort.to_h
+ @events = Event.where(:conference_id => conference.id).order(start_time: :asc)
+ @workshops = Workshop.where(:conference_id => conference.id).order(start_time: :asc)
+ @locations = {}
+
+ get_block_data
+
+ @schedule = {}
+ day_1 = conference.start_date.wday
+
+ @workshop_blocks.each_with_index do | info, block |
+ info['days'].each do | block_day |
+ day_diff = block_day.to_i - day_1
+ day_diff += 7 if day_diff < 0
+ day = (conference.start_date + day_diff.days).to_date
+ time = info['time'].to_f
+ @schedule[day] ||= { times: {}, locations: {} }
+ @schedule[day][:times][time] ||= {}
+ @schedule[day][:times][time][:type] = :workshop
+ @schedule[day][:times][time][:length] = info['length'].to_f
+ @schedule[day][:times][time][:item] = { block: block, workshops: {} }
+ end
+ end
+
+ @workshops.each do | workshop |
+ if workshop.block.present?
+ block = @workshop_blocks[workshop.block['block'].to_i]
+ day_diff = workshop.block['day'].to_i - day_1
+ day_diff += 7 if day_diff < 0
+ day = (conference.start_date + day_diff.days).to_date
+
+ if @schedule[day].present? && @schedule[day][:times].present? && @schedule[day][:times][block['time'].to_f].present?
+ @schedule[day][:times][block['time'].to_f][:item][:workshops][workshop.event_location_id] = { workshop: workshop, status: { errors: [], warnings: [], conflict_score: nil } }
+ @schedule[day][:locations][workshop.event_location_id] ||= workshop.event_location if workshop.event_location.present?
+ end
+ end
+ end
+
+ @meals.each do | time, meal |
+ day = meal['day'].to_date
+ time = meal['time'].to_f
+ @schedule[day] ||= {}
+ @schedule[day][:times] ||= {}
+ @schedule[day][:times][time] ||= {}
+ @schedule[day][:times][time][:type] = :meal
+ @schedule[day][:times][time][:length] = (meal['length'] || 1.0).to_f
+ @schedule[day][:times][time][:item] = meal
+ end
+
+ @events.each do | event |
+ day = event.start_time.midnight.to_date
+ time = event.start_time.hour.to_f + (event.start_time.min / 60.0)
+ @schedule[day] ||= {}
+ @schedule[day][:times] ||= {}
+ @schedule[day][:times][time] ||= {}
+ @schedule[day][:times][time][:type] = :event
+ @schedule[day][:times][time][:length] = (event.end_time - event.start_time) / 3600.0
+ @schedule[day][:times][time][:item] = event
+ end
+
+ @schedule = @schedule.sort.to_h
+ @schedule.each do | day, data |
+ @schedule[day][:times] = data[:times].sort.to_h
+ end
+
+ @schedule.each do | day, data |
+ last_event = nil
+ data[:times].each do | time, time_data |
+ if last_event.present?
+ @schedule[day][:times][last_event][:next_event] = time
+ end
+ last_event = time
+ end
+ @schedule[day][:num_locations] = (data[:locations] || []).size
+ end
+
+ @schedule.deep_dup.each do | day, data |
+ data[:times].each do | time, time_data |
+ if time_data[:next_event].present? || time_data[:length] > 0.5
+ span = 0.5
+ length = time_data[:next_event].present? ? time_data[:next_event] - time : time_data[:length]
+ while span < length
+ @schedule[day][:times][time + span] ||= {
+ type: (span >= time_data[:length] ? :empty : :nil),
+ length: 0.5
+ }
+ span += 0.5
+ end
+ end
+ end
+ end
+
+ @schedule = @schedule.sort.to_h
+
+ @schedule.each do | day, data |
+ @schedule[day][:times] = data[:times].sort.to_h
+ @schedule[day][:locations] ||= {}
+
+ # add an empty block if no workshops are scheduled on this day yet
+ @schedule[day][:locations][0] = :add if do_analyze || @schedule[day][:locations].empty?
+
+ if do_analyze
+ data[:times].each do | time, time_data |
+ if time_data[:type] == :workshop && time_data[:item].present? && time_data[:item][:workshops].present?
+ ids = time_data[:item][:workshops].keys
+ (0..ids.length).each do | i |
+ if time_data[:item][:workshops][ids[i]].present?
+ workshop_i = time_data[:item][:workshops][ids[i]][:workshop]
+ conflicts = {}
+
+ (i+1..ids.length).each do | j |
+ workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
+ if workshop_i.present? && workshop_j.present?
+ workshop_i.active_facilitators.each do | facilitator_i |
+ workshop_j.active_facilitators.each do | facilitator_j |
+ if facilitator_i.id == facilitator_j.id
+ @schedule[day][:times][time][:status] ||= {}
+ @schedule[day][:times][time][:item][:workshops][ids[j]][:status][:errors] << {
+ name: :common_facilitator,
+ facilitator: facilitator_i,
+ workshop: workshop_i,
+ i18nVars: {
+ facilitator_name: facilitator_i.name,
+ workshop_title: workshop_i.title
+ }
+ }
+ end
+ end
+ end
+ end
+ end
+
+ location = workshop_i.event_location || EventLocation.new
+ needs = JSON.parse(workshop_i.needs || '[]').map &:to_sym
+ amenities = JSON.parse(location.amenities || '[]').map &:to_sym
+
+ needs.each do | need |
+ @schedule[day][:times][time][:item][:workshops][ids[i]][:status][:errors] << {
+ name: :need_not_available,
+ need: need,
+ location: location,
+ workshop: workshop_i,
+ i18nVars: {
+ need: need.to_s,
+ location: location.title,
+ workshop_title: workshop_i.title
+ }
+ } unless amenities.include? need
+ end
+
+ # collect common interested users
+ interests = []
+ (0..ids.length).each do | j |
+ workshop_j = time_data[:item][:workshops][ids[j]].present? ? time_data[:item][:workshops][ids[j]][:workshop] : nil
+ if workshop_i.present? && workshop_j.present? && workshop_i.id != workshop_j.id
+ interests = interests | workshop_j.interested.map { | u | u.user_id }
+ end
+ end
+
+ @schedule[day][:times][time][:item][:workshops][ids[i]][:status][:conflict_score] = (interests & (workshop_i.interested.map { | u | u.user_id })).length
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ protected
+ def set_conference
+ @this_conference = Conference.find_by!(slug: params[:slug])
+ end
+
+ def set_conference_registration
+ @registration = logged_in? ? ConferenceRegistration.find_by(user_id: current_user.id, conference_id: @this_conference.id) : nil
+ end
+
+ def set_conference_registration!
+ @registration = set_conference_registration
+ raise ActiveRecord::PremissionDenied unless @registration.present?
+ end
+
+ def set_or_create_conference_registration
+ set_conference_registration
+ return @registration if @registration.present?
+
+ @registration ||= ConferenceRegistration.new(
+ conference: @this_conference,
+ user_id: current_user.id,
+ steps_completed: []
+ )
+ last_registration_data = ConferenceRegistration.where(user_id: current_user.id).order(created_at: :desc).limit(1).first
+
+ if last_registration_data.present?
+ if last_registration_data['languages'].present? && current_user.languages.blank?
+ current_user.languages = JSON.parse(last_registration_data['languages'])
+ current_user.save!
+ end
+
+ @registration.city = last_registration_data.city if last_registration_data.city.present?
+ end
+ end
end
diff --git a/app/controllers/conference_administration_controller.rb b/app/controllers/conference_administration_controller.rb
new file mode 100644
index 0000000..ee53e79
--- /dev/null
+++ b/app/controllers/conference_administration_controller.rb
@@ -0,0 +1,1208 @@
+require 'geocoder/calculations'
+require 'rest_client'
+
+class ConferenceAdministrationController < ApplicationController
+ def administration
+ set_conference
+ return do_403 unless @this_conference.host? current_user
+ @page_title_vars = { title: @this_conference.title }
+ end
+
+ def administration_step(step = nil)
+ # get the step name
+ method_step = step || params[:step]
+ sub_step = view_context.administration_sub_steps[method_step]
+ @admin_step = sub_step.present? ? sub_step : method_step
+
+ # determine which method we will try to call
+ method_name = "administrate_#{method_step}"
+
+ # make sure the step exists, the conference exists, and the user has access to the page
+ return do_404 unless self.respond_to? method_name, true
+ set_conference
+ return do_403 unless @this_conference.host? current_user
+
+ @page_title_vars = { title: @this_conference.title }
+
+ # set the page title
+ @main_title = @page_title = 'articles.conference_registration.headings.Conference_Administration'
+ @page_title_vars = { title: @this_conference.title }
+ @main_title_vars = { vars: @page_title_vars }
+
+ @admin_group = view_context.get_administration_group(@admin_step)
+
+ set_flash_messages
+
+ # call the step method
+ self.send(method_name)
+
+ # only render if we're coming from somewhere else
+ render 'administration_step' if step.present?
+ end
+
+ def edit_event
+ administration_step(:event_edit)
+ end
+
+ def edit_location
+ administration_step(:location_edit)
+ end
+
+ def admin_update
+ # get the step name
+ @admin_step = params[:step]
+ # determine which method we will try to call
+ method_name = "admin_update_#{@admin_step}"
+
+ # make sure the step exists, the conference exists, and the user has access to the page
+ return do_404 unless self.respond_to? method_name, true
+ set_conference
+ return do_403 unless @this_conference.host? current_user
+
+ set_flash_messages
+
+ # redirect to the step unless the method handled redirection itself
+ unless self.send(method_name)
+ redirect_to administration_step_path(@this_conference.slug, @admin_step)
+ end
+ end
+
+ rescue_from ActiveRecord::PremissionDenied do |exception|
+ if logged_in?
+ redirect_to :register
+ else
+ @register_template = :confirm_email
+ @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
+ render :register
+ end
+ end
+
+ private
+ # Administration form pages
+ def administrate_administrators
+ @organizations = Organization.find_by_city(@this_conference.city_id)
+ end
+
+ def administrate_dates
+ if @this_conference.start_date.present?
+ @start_month = @this_conference.start_date.month
+ @start_day = @this_conference.start_date.day
+ end
+
+ if @this_conference.end_date.present?
+ @end_month = @this_conference.end_date.month
+ @end_day = @this_conference.end_date.day
+ end
+ end
+
+ def administrate_description
+ end
+
+ def administrate_poster
+ end
+
+ def administrate_broadcast
+ end
+
+ def administrate_broadcast_sent
+ end
+
+ def administrate_providers
+ end
+
+ def administrate_payment_message
+ end
+
+ def administrate_suggested_amounts
+ end
+
+ def administrate_paypal
+ end
+
+ def administrate_registration_status
+ end
+
+ def administrate_organizations
+ @organizations = Organization.all
+
+ if request.format.xlsx?
+ logger.info "Generating organizations.xls"
+ @excel_data = {
+ columns: [:name, :street_address, :city, :subregion, :country, :postal_code, :email, :phone, :status],
+ keys: {
+ name: 'forms.labels.generic.name',
+ street_address: 'forms.labels.generic.street_address',
+ city: 'forms.labels.generic.city',
+ subregion: 'forms.labels.generic.subregion',
+ country: 'forms.labels.generic.country',
+ postal_code: 'forms.labels.generic.postal_code',
+ email: 'forms.labels.generic.email',
+ phone: 'forms.labels.generic.phone',
+ status: 'forms.labels.generic.status'
+ },
+ data: [],
+ }
+ @organizations.each do | org |
+ if org.present?
+ address = org.locations.first
+ @excel_data[:data] << {
+ name: org.name,
+ street_address: address.present? ? address.street : nil,
+ city: address.present? ? address.city : nil,
+ subregion: address.present? ? I18n.t("geography.subregions.#{address.country}.#{address.territory}") : nil,
+ country: address.present? ? I18n.t("geography.countries.#{address.country}") : nil,
+ postal_code: address.present? ? address.postal_code : nil,
+ email: org.email_address,
+ phone: org.phone,
+ status: org.status
+ }
+ end
+ end
+ return respond_to do | format |
+ format.xlsx { render xlsx: :stats, filename: "organizations" }
+ end
+ end
+ end
+
+ def administrate_registrations
+ get_stats(!request.format.xlsx?)
+
+ if request.format.xlsx?
+ logger.info "Generating stats.xls"
+ return respond_to do | format |
+ format.xlsx { render xlsx: :stats, filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
+ end
+ end
+
+ @registration_count = @registrations.size
+ @completed_registrations = 0
+ @bikes = 0
+ @donation_count = 0
+ @donations = 0
+ @food = { meat: 0, vegan: 0, vegetarian: 0, all: 0 }
+ @registrations.each do | r |
+ if view_context.registration_status(r) == :registered
+ @completed_registrations += 1
+
+ @bikes += 1 if r.bike == 'yes'
+
+ if r.food.present?
+ @food[r.food.to_sym] += 1
+ @food[:all] += 1
+ end
+
+ if r.registration_fees_paid.present? && r.registration_fees_paid > 0
+ @donation_count += 1
+ @donations += r.registration_fees_paid
+ end
+ end
+ end
+ end
+
+ def administrate_stats
+ get_stats(!request.format.xlsx?)
+
+ if request.format.xlsx?
+ logger.info "Generating stats.xls"
+ return respond_to do | format |
+ format.xlsx { render xlsx: :stats, filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
+ end
+ else
+ @registration_count = @registrations.size
+ @completed_registrations = 0
+ @bikes = 0
+ @donation_count = 0
+ @donations = 0
+ @food = { meat: 0, vegan: 0, vegetarian: 0, all: 0 }
+ @registrations.each do | r |
+ if view_context.registration_status(r) == :registered
+ @completed_registrations += 1
+
+ @bikes += 1 if r.bike == 'yes'
+
+ if r.food.present?
+ @food[r.food.to_sym] += 1
+ @food[:all] += 1
+ end
+
+ if r.registration_fees_paid.present? && r.registration_fees_paid > 0
+ @donation_count += 1
+ @donations += r.registration_fees_paid
+ end
+ end
+ end
+ end
+ end
+
+ def administrate_housing
+ # do a full analysis
+ analyze_housing
+
+ if request.format.xlsx?
+ logger.info "Generating housing.xls"
+ @excel_data = {
+ columns: [:name, :phone, :street_address, :email, :availability, :considerations, :empty, :empty, :empty, :guests],
+ keys: {
+ name: 'forms.labels.generic.name',
+ street_address: 'forms.labels.generic.street_address',
+ email: 'forms.labels.generic.email',
+ phone: 'forms.labels.generic.phone',
+ availability: 'articles.conference_registration.headings.host.availability',
+ considerations: 'articles.conference_registration.headings.host.considerations'
+ },
+ column_types: {
+ name: :bold,
+ guests: :table
+ },
+ data: [],
+ }
+ @hosts.each do | id, host |
+ data = (host.housing_data || {})
+ host_data = {
+ name: host.user.name,
+ street_address: data['address'],
+ email: host.user.email,
+ phone: data['phone'],
+ availability: data['availability'].present? && data['availability'][1].present? ? view_context.date_span(data['availability'][0].to_date, data['availability'][1].to_date) : '',
+ considerations: ((data['considerations'] || []).map { | consideration | view_context._"articles.conference_registration.host.considerations.#{consideration}" }).join(', '),
+ empty: '',
+ guests: {
+ columns: [:name, :area, :email, :arrival_departure, :allergies, :food, :companion, :city],
+ keys: {
+ name: 'forms.labels.generic.name',
+ area: 'articles.workshops.headings.space',
+ email: 'forms.labels.generic.email',
+ arrival_departure: 'articles.admin.housing.headings.arrival_departure',
+ companion: 'forms.labels.generic.companion',
+ city: 'forms.labels.generic.city',
+ food: 'forms.labels.generic.food',
+ allergies: 'forms.labels.generic.allergies'
+ },
+ column_types: {
+ name: :bold
+ },
+ data: []
+ }
+ }
+
+ @housing_data[id][:guests].each do | space, space_data |
+ space_data.each do | guest_id, guest_data |
+ guest = guest_data[:guest]
+ if guest.present?
+ companion = view_context.companion(guest)
+
+ host_data[:guests][:data] << {
+ name: guest.user.name,
+ area: (view_context._"forms.labels.generic.#{space}"),
+ email: guest.user.email,
+ arrival_departure: guest.arrival.present? && guest.departure.present? ? view_context.date_span(guest.arrival.to_date, guest.departure.to_date) : '',
+ companion: companion.present? ? (companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}")) : '',
+ city: guest.city,
+ food: guest.food.present? ? (view_context._"articles.conference_registration.questions.food.#{guest.food}") : '',
+ allergies: guest.allergies
+ }
+ end
+ end
+ end
+
+ @excel_data[:data] << host_data
+ end
+ return respond_to do | format |
+ format.xlsx { render xlsx: :stats, filename: "housing" }
+ end
+ end
+ end
+
+ def administrate_locations
+ @locations = EventLocation.where(:conference_id => @this_conference.id)
+ end
+
+ def administrate_events
+ @event = Event.new(locale: I18n.locale)
+ @events = Event.where(:conference_id => @this_conference.id)
+ @day = nil
+ @time = nil
+ @length = 1.5
+ end
+
+ def administrate_event_edit
+ @event = Event.find_by!(conference_id: @this_conference.id, id: params[:id])
+ @day = @event.start_time.midnight
+ @time = view_context.hour_span(@day, @event.start_time)
+ @length = view_context.hour_span(@event.start_time, @event.end_time)
+ end
+
+ def administrate_location_edit
+ @location = EventLocation.find_by!(conference_id: @this_conference.id, id: params[:id])
+ @space = @location.space.present? ? @location.space.to_sym : nil
+ @amenities = @location.amenities.present? ? JSON.parse(@location.amenities).map(&:to_sym) : nil
+ end
+
+ def administrate_meals
+ @meals = Hash[(@this_conference.meals || {}).map{ |k, v| [k.to_i, v] }].sort.to_h
+ end
+
+ def administrate_workshop_times
+ get_block_data
+ @workshop_blocks << {
+ 'time' => nil,
+ 'length' => 1.0,
+ 'days' => []
+ }
+ end
+
+ def administrate_schedule
+ @can_edit = true
+ @entire_page = true
+ get_scheule_data
+ end
+
+ def administrate_publish_schedule
+ end
+
+ def get_stats(html_format = false, id = nil)
+ @registrations = ConferenceRegistration.where(:conference_id => @this_conference.id).sort { |a,b| (a.user.present? ? (a.user.firstname || '') : '').downcase <=> (b.user.present? ? (b.user.firstname || '') : '').downcase }
+ @excel_data = {
+ columns: [
+ :name,
+ :email,
+ :status,
+ :is_attending,
+ :is_subscribed,
+ :registration_fees_paid,
+ :date,
+ :city,
+ :preferred_language
+ ] +
+ User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
+ [
+ :arrival,
+ :departure,
+ :housing,
+ :bike,
+ :food,
+ :companion,
+ :companion_email,
+ :allergies,
+ :other,
+ :can_provide_housing,
+ :first_day,
+ :last_day,
+ :address,
+ :phone
+ ] + ConferenceRegistration.all_spaces +
+ ConferenceRegistration.all_considerations + [
+ :notes
+ ],
+ column_types: {
+ name: :bold,
+ date: :datetime,
+ email: :email,
+ companion_email: :email,
+ arrival: [:date, :day],
+ departure: [:date, :day],
+ registration_fees_paid: :money,
+ allergies: :text,
+ other: :text,
+ first_day: [:date, :day],
+ last_day: [:date, :day],
+ notes: :text
+ },
+ keys: {
+ name: 'forms.labels.generic.name',
+ email: 'forms.labels.generic.email',
+ status: 'forms.labels.generic.registration_status',
+ is_attending: 'articles.conference_registration.terms.is_attending',
+ is_subscribed: 'articles.user_settings.headings.email_subscribe',
+ city: 'forms.labels.generic.event_location',
+ date: 'articles.conference_registration.terms.Date',
+ preferred_language: 'articles.conference_registration.terms.Preferred_Languages',
+ arrival: 'forms.labels.generic.arrival',
+ departure: 'forms.labels.generic.departure',
+ housing: 'forms.labels.generic.housing',
+ bike: 'forms.labels.generic.bike',
+ food: 'forms.labels.generic.food',
+ companion: 'articles.conference_registration.terms.companion',
+ companion_email: 'articles.conference_registration.terms.companion_email',
+ allergies: 'forms.labels.generic.allergies',
+ registration_fees_paid: 'articles.conference_registration.headings.fees_paid',
+ other: 'forms.labels.generic.other_notes',
+ can_provide_housing: 'articles.conference_registration.can_provide_housing',
+ first_day: 'forms.labels.generic.first_day',
+ last_day: 'forms.labels.generic.last_day',
+ notes: 'forms.labels.generic.notes',
+ phone: 'forms.labels.generic.phone',
+ address: 'forms.labels.generic.address',
+ contact_info: 'articles.conference_registration.headings.contact_info',
+ questions: 'articles.conference_registration.headings.questions',
+ hosting: 'articles.conference_registration.headings.hosting'
+ },
+ data: []
+ }
+ User.AVAILABLE_LANGUAGES.each do | l |
+ @excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l.to_s}"
+ end
+ ConferenceRegistration.all_spaces.each do |s|
+ @excel_data[:column_types][s] = :number
+ @excel_data[:keys][s] = "forms.labels.generic.#{s.to_s}"
+ end
+ ConferenceRegistration.all_considerations.each do |c|
+ @excel_data[:keys][c] = "articles.conference_registration.host.considerations.#{c.to_s}"
+ end
+ @registrations.each do | r |
+ user = r.user_id ? User.where(id: r.user_id).first : nil
+ if user.present?
+ companion = view_context.companion(r)
+ companion = companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}") if companion.present?
+ steps = r.steps_completed || []
+
+ if id.nil? || id == r.id
+ housing_data = r.housing_data || {}
+ availability = housing_data['availability'] || []
+ availability[0] = Date.parse(availability[0]) if availability[0].present?
+ availability[1] = Date.parse(availability[1]) if availability[1].present?
+
+ data = {
+ id: r.id,
+ name: user.firstname || '',
+ email: user.email || '',
+ status: (view_context._"articles.conference_registration.terms.registration_status.#{view_context.registration_status(r)}"),
+ is_attending: (view_context._"articles.conference_registration.questions.bike.#{r.is_attending == 'n' ? 'no' : 'yes'}"),
+ is_subscribed: user.is_subscribed == false ? (view_context._'articles.conference_registration.questions.bike.no') : '',
+ date: r.created_at ? r.created_at.strftime("%F %T") : '',
+ city: r.city || '',
+ preferred_language: user.locale.present? ? (view_context.language_name user.locale) : '',
+ arrival: r.arrival ? r.arrival.strftime("%F %T") : '',
+ departure: r.departure ? r.departure.strftime("%F %T") : '',
+ housing: r.housing.present? ? (view_context._"articles.conference_registration.questions.housing.#{r.housing}") : '',
+ bike: r.bike.present? ? (view_context._"articles.conference_registration.questions.bike.#{r.bike}") : '',
+ food: r.food.present? ? (view_context._"articles.conference_registration.questions.food.#{r.food}") : '',
+ companion: companion,
+ companion_email: (housing_data['companions'] || ['']).first,
+ allergies: r.allergies,
+ registration_fees_paid: r.registration_fees_paid,
+ other: r.other,
+ can_provide_housing: r.can_provide_housing ? (view_context._'articles.conference_registration.questions.bike.yes') : '',
+ first_day: availability[0].present? ? availability[0].strftime("%F %T") : '',
+ last_day: availability[1].present? ? availability[1].strftime("%F %T") : '',
+ notes: housing_data['notes'],
+ address: housing_data['address'],
+ phone: housing_data['phone'],
+ raw_values: {
+ housing: r.housing,
+ bike: r.bike,
+ food: r.food,
+ arrival: r.arrival.present? ? r.arrival.to_date : nil,
+ departure: r.departure.present? ? r.departure.to_date : nil,
+ preferred_language: user.locale,
+ is_attending: r.is_attending != 'n',
+ is_subscribed: user.is_subscribed,
+ can_provide_housing: r.can_provide_housing,
+ first_day: availability[0].present? ? availability[0].to_date : nil,
+ last_day: availability[1].present? ? availability[1].to_date : nil
+ },
+ html_values: {
+ date: r.created_at.present? ? r.created_at.strftime("%F %T") : '',
+ arrival: r.arrival.present? ? view_context.date(r.arrival.to_date, :span_same_year_date_1) : '',
+ departure: r.departure.present? ? view_context.date(r.departure.to_date, :span_same_year_date_1) : '',
+ first_day: availability[0].present? ? view_context.date(availability[0].to_date, :span_same_year_date_1) : '',
+ last_day: availability[1].present? ? view_context.date(availability[1].to_date, :span_same_year_date_1) : ''
+ }
+ }
+ User.AVAILABLE_LANGUAGES.each do | l |
+ can_speak = ((user.languages || []).include? l.to_s)
+ data["language_#{l}".to_sym] = (can_speak ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
+ data[:raw_values]["language_#{l}".to_sym] = can_speak
+ end
+ ConferenceRegistration.all_spaces.each do |s|
+ space = (housing_data['space'] || {})[s.to_s]
+ data[s] = space.present? ? space.to_i : nil
+ end
+ ConferenceRegistration.all_considerations.each do |c|
+ consideration = (housing_data['considerations'] || []).include?(c.to_s)
+ data[c] = (consideration ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
+ data[:raw_values][c] = consideration
+ end
+ @excel_data[:data] << data
+ end
+ end
+ end
+
+ if html_format
+ yes_no = [
+ [(view_context._"articles.conference_registration.questions.bike.yes"), true],
+ [(view_context._"articles.conference_registration.questions.bike.no"), false]
+ ]
+ @column_options = {
+ housing: ConferenceRegistration.all_housing_options.map { |h| [
+ (view_context._"articles.conference_registration.questions.housing.#{h}"),
+ h] },
+ bike: ConferenceRegistration.all_bike_options.map { |b| [
+ (view_context._"articles.conference_registration.questions.bike.#{b}"),
+ b] },
+ food: ConferenceRegistration.all_food_options.map { |f| [
+ (view_context._"articles.conference_registration.questions.food.#{f}"),
+ f] },
+ arrival: view_context.conference_days_options_list(:before_plus_one),
+ departure: view_context.conference_days_options_list(:after_minus_one),
+ preferred_language: I18n.backend.enabled_locales.map { |l| [
+ (view_context.language_name l), l
+ ] },
+ is_attending: [yes_no.first],
+ is_subscribed: [yes_no.last],
+ can_provide_housing: yes_no,
+ first_day: view_context.conference_days_options_list(:before),
+ last_day: view_context.conference_days_options_list(:after)
+ }
+ User.AVAILABLE_LANGUAGES.each do | l |
+ @column_options["language_#{l}".to_sym] = [
+ [(view_context._"articles.conference_registration.questions.bike.yes"), true]
+ ]
+ end
+ ConferenceRegistration.all_considerations.each do | c |
+ @column_options[c.to_sym] = [
+ [(view_context._"articles.conference_registration.questions.bike.yes"), true]
+ ]
+ end
+ end
+ end
+
+ def get_housing_data
+ @hosts = {}
+ @guests = {}
+ ConferenceRegistration.where(:conference_id => @this_conference.id).each do | registration |
+ if registration.can_provide_housing
+ @hosts[registration.id] = registration
+ elsif registration.housing.present? && registration.housing != 'none'
+ @guests[registration.id] = registration
+ end
+ end
+ end
+
+ def analyze_housing
+ get_housing_data unless @hosts.present? && @guests.present?
+
+ @housing_data = {}
+ @hosts_affected_by_guests = {}
+ @hosts.each do | id, host |
+ @hosts[id].housing_data ||= {}
+ @housing_data[id] = { guests: {}, space: {} }
+ @hosts[id].housing_data['space'] ||= {}
+ @hosts[id].housing_data['space'].each do | s, size |
+ size = (size || 0).to_i
+ @housing_data[id][:guests][s.to_sym] = {}
+ @housing_data[id][:space][s.to_sym] = size
+ end
+ end
+
+ @guests_housed = 0
+
+ @guests.each do | guest_id, guest |
+ data = guest.housing_data || {}
+ @hosts_affected_by_guests[guest_id] ||= []
+
+ if data['host']
+ @guests_housed += 1
+ host_id = (data['host'].present? ? data['host'].to_i : nil)
+ host = host_id.present? ? @hosts[host_id] : nil
+
+ # make sure the host was found and that they are still accepting guests
+ if host.present? && host.can_provide_housing
+ @hosts_affected_by_guests[guest_id] << host_id
+
+ space = (data['space'] || :bed).to_sym
+
+ @housing_data[host_id] ||= {}
+ host_data = host.housing_data
+ unless @housing_data[host_id][:guests][space].present?
+ @housing_data[host_id][:guests][space] ||= {}
+ @housing_data[host_id][:space][space] ||= 0
+ end
+
+ @housing_data[host_id][:guests][space][guest_id] = { guest: guest }
+
+ @housing_data[host_id][:guest_data] ||= {}
+ @housing_data[host_id][:guest_data][guest_id] = { warnings: {}, errors: {} }
+
+ @housing_data[host_id][:guest_data][guest_id][:warnings][:dates] = {} unless view_context.available_dates_match?(host, guest)
+
+ if (guest.housing == 'house' && space == :tent) ||
+ (guest.housing == 'tent' && (space == :bed_space || space == :floor_space))
+ @housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: (view_context._"forms.labels.generic.#{space.to_s}"), expected: (view_context._"articles.conference_registration.questions.housing.#{guest.housing}")}
+ end
+
+ companions = data['companions'] || []
+ companions.each do | companion |
+ user = User.find_by_email(companion)
+ if user.present?
+ reg = ConferenceRegistration.find_by(
+ :user_id => user.id,
+ :conference_id => @this_conference.id
+ )
+ if reg.present? && @guests[reg.id].present?
+ housing_data = reg.housing_data || {}
+ companion_host = housing_data['host'].present? ? housing_data['host'].to_i : nil
+ @hosts_affected_by_guests[guest_id] << companion_host
+ if companion_host != host_id && reg.housing.present? && reg.housing != 'none'
+ # set this as an error if the guest has selected only one other to stay with, but if they have requested to stay with more, make this only a warning
+ @housing_data[host_id][:guest_data][guest_id][:warnings][:companions] = { name: "#{reg.user.name} ".html_safe, id: reg.id }
+ end
+ end
+ end
+ end
+ else
+ # make sure the housing data is empty if the host wasn't found, just in case something happened to the host
+ @guests[guest_id].housing_data ||= {}
+ @guests[guest_id].housing_data['host'] = nil
+ @guests[guest_id].housing_data['space'] = nil
+ end
+ end
+ end
+
+ @hosts.each do | id, host |
+ host_data = host.housing_data
+
+ @hosts[id].housing_data['space'].each do | space, size |
+ # make sure the host isn't overbooked
+ space = space.to_sym
+ space_available = (size || 0).to_i
+ @housing_data[id][:warnings] ||= {}
+ @housing_data[id][:warnings][:space] ||= {}
+ @housing_data[id][:warnings][:space][space] ||= []
+
+ if @housing_data[id][:guests][space].size > space_available
+ @housing_data[id][:warnings][:space][space] << :overbooked
+ end
+ end
+ end
+
+ return @hosts_affected_by_guests
+ end
+
+ # Administration update endpoints
+
+ def admin_update_administrators
+ case params[:button]
+ when 'add_org_member'
+ # add this user to the organization
+ organization = Organization.find(params[:org_id].to_i)
+
+ # make sure the organization is a host of the conference before adding a member
+ if @this_conference.host_organization?(organization)
+ organization.users << (User.get params[:email])
+ organization.save
+ set_success_message :org_member_added
+ else
+ set_error_message :error_adding_org_member
+ end
+ when 'remove_org_member'
+ organization = Organization.find(params[:org_id].to_i)
+ user = User.find(params[:user_id].to_i)
+ if !user.present? || !@this_conference.host_organization?(organization) || (user.id == current_user.id && !current_user.administrator?)
+ set_error_message :error_removing_org_member
+ else
+ organization.users -= [user]
+ organization.save
+ set_success_message :org_member_removed
+ end
+ when 'add_administrator'
+ begin
+ @this_conference.administrators << (User.get params[:email])
+ @this_conference.save
+ set_success_message :administrator_added
+ rescue
+ set_error_message :error_adding_administrator
+ end
+ when 'remove_administrator'
+ begin
+ user = User.find(params[:user_id].to_i)
+ if !user.present? || (user.id == current_user.id && !current_user.administrator?)
+ set_error_message :error_removing_administrator
+ else
+ @this_conference.administrators -= [user]
+ @this_conference.save
+ set_success_message :administrator_removed
+ end
+ rescue
+ set_error_message :error_removing_administrator
+ end
+ when 'set_organizations'
+ begin
+ @this_conference.organizations = params[:organizations].keys.map { |id| Organization.find(id) }
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+ else
+ do_404
+ return true
+ end
+
+ return false
+ end
+
+ def admin_update_dates
+ begin
+ start_date = DateTime.new(@this_conference.conference_year, params[:start_month].to_i, params[:start_day].to_i)
+ rescue
+ set_error_message(@admin_step)
+ return false
+ end
+
+ begin
+ end_date = DateTime.new(@this_conference.conference_year, params[:end_month].to_i, params[:end_day].to_i)
+ rescue
+ set_error(:end_date, :error)
+ return false
+ end
+
+ if start_date > end_date
+ set_error(:start_date, :start_date_after_end_date)
+ return false
+ end
+
+ @this_conference.start_date = start_date
+ @this_conference.end_date = end_date
+
+ set_success_message @admin_step
+ @this_conference.save
+ return false
+ end
+
+ def admin_update_description
+ params[:info].each do | locale, value |
+ @this_conference.set_column_for_locale(:info, locale, value)
+ end
+ @this_conference.save
+ set_success_message @admin_step
+ return false
+ end
+
+ def admin_update_poster
+ begin
+ @this_conference.poster = params[:poster]
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+ return false
+ end
+
+ def admin_update_payment_message
+ begin
+ params[:payment_message].each do | locale, value |
+ @this_conference.set_column_for_locale(:payment_message, locale, value)
+ end
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+
+ return false
+ end
+
+ def admin_update_suggested_amounts
+ begin
+ @this_conference.payment_amounts = ((params[:payment_amounts] || {}).values.map &:to_i) - [0]
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+
+ return false
+ end
+
+ def admin_update_paypal
+ begin
+ @this_conference.paypal_email_address = params[:paypal_email_address]
+ @this_conference.paypal_username = params[:paypal_username]
+ @this_conference.paypal_password = params[:paypal_password]
+ @this_conference.paypal_signature = params[:paypal_signature]
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+
+ return false
+ end
+
+ def admin_update_registration_status
+ begin
+ @this_conference.registration_status = params[:registration_status]
+ @this_conference.save
+ set_success_message @admin_step
+ rescue
+ set_error_message(@admin_step)
+ end
+
+ return false
+ end
+
+ def admin_update_registrations
+ if params[:button] == 'save' || params[:button] == 'update'
+ if params[:button] == 'save'
+ return do_404 unless params[:email].present? && params[:name].present?
+
+ user = User.find_by_email(params[:email]) || User.create(email: params[:email])
+ user.firstname = params[:name]
+ user.save!
+ registration = ConferenceRegistration.new(
+ conference: @this_conference,
+ user_id: user.id,
+ steps_completed: []
+ )
+ else
+ registration = ConferenceRegistration.where(
+ id: params[:key].to_i,
+ conference_id: @this_conference.id
+ ).limit(1).first
+ end
+
+ user_changed = false
+ params.each do | key, value |
+ case key.to_sym
+ when :city
+ registration.city = value.present? ? view_context.location(Geocoder.search(value, language: @this_conference.locale).first, @this_conference.locale) : nil
+ when :housing, :bike, :food, :allergies, :other
+ registration.send("#{key.to_s}=", value)
+ when :registration_fees_paid
+ registration.registration_fees_paid = value.to_i
+ when :can_provide_housing
+ registration.send("#{key.to_s}=", value.present?)
+ when :arrival, :departure
+ registration.send("#{key.to_s}=", value.present? ? Date.parse(value) : nil)
+ when :companion_email
+ registration.housing_data ||= {}
+ registration.housing_data['companions'] = [value]
+ when :preferred_language
+ registration.user.locale = value
+ user_changed = true
+ when :is_subscribed
+ registration.user.is_subscribed = (value != "false")
+ user_changed = true
+ when :is_attending
+ registration.is_attending = value.present? ? 'y' : 'n'
+ when :first_day
+ registration.housing_data ||= {}
+ registration.housing_data['availability'] ||= []
+ registration.housing_data['availability'][0] = value
+ when :last_day
+ registration.housing_data ||= {}
+ registration.housing_data['availability'] ||= []
+ registration.housing_data['availability'][1] = value
+ when :address, :phone, :notes
+ registration.housing_data ||= {}
+ registration.housing_data[key.to_s] = value
+ else
+ if key.start_with?('language_')
+ l = key.split('_').last
+ if User.AVAILABLE_LANGUAGES.include? l.to_sym
+ registration.user.languages ||= []
+ if value.present?
+ registration.user.languages |= [l]
+ else
+ registration.user.languages -= [l]
+ end
+ user_changed = true
+ end
+ elsif ConferenceRegistration.all_considerations.include? key.to_sym
+ registration.housing_data ||= {}
+ registration.housing_data['considerations'] ||= []
+ if value.present?
+ registration.housing_data['considerations'] |= [key]
+ else
+ registration.housing_data['considerations'] -= [key]
+ end
+ elsif ConferenceRegistration.all_spaces.include? key.to_sym
+ registration.housing_data ||= {}
+ registration.housing_data['space'] ||= {}
+ registration.housing_data['space'][key.to_s] = value
+ end
+ end
+ end
+ registration.user.save! if user_changed
+ registration.save!
+
+ # do the normal thing if this wasn't an ajax request
+ return false if params[:button] == 'save'
+
+ get_stats(true, params[:key].to_i)
+ options = view_context.registrations_table_options
+ options[:html] = true
+
+ render html: view_context.excel_rows(@excel_data, {}, options)
+ else
+ do_404
+ end
+
+ return true
+ end
+
+ def admin_update_housing
+ # modify the guest data
+ if params[:button] == 'get-guest-list'
+ analyze_housing
+ render partial: 'select_guest_table', locals: { host: @hosts[params['host'].to_i], space: params['space'] }
+ elsif params[:button] == 'set-guest'
+ guest = ConferenceRegistration.where(
+ id: params[:guest].to_i,
+ conference_id: @this_conference.id
+ ).limit(1).first
+
+ guest.housing_data ||= {}
+ guest.housing_data['space'] = params[:space]
+ guest.housing_data['host'] = params[:host].to_i
+ guest.save!
+
+ analyze_housing
+
+ render partial: 'hosts_table'
+ elsif params[:button] == 'remove-guest'
+ guest = ConferenceRegistration.where(
+ id: params[:guest].to_i,
+ conference_id: @this_conference.id
+ ).limit(1).first
+
+ guest.housing_data ||= {}
+ guest.housing_data.delete('space')
+ guest.housing_data.delete('host')
+ guest.save!
+
+ analyze_housing
+
+ render partial: 'hosts_table'
+ else
+ do_404
+ end
+
+ return true
+ end
+
+ def admin_update_broadcast
+ @hide_description = true
+ @subject = params[:subject]
+ @body = params[:body]
+ @send_to = params[:send_to]
+ @register_template = :administration
+ if params[:button] == 'send'
+ view_context.broadcast_to(@send_to).each do | user |
+ UserMailer.send_mail :broadcast do
+ [
+ "#{request.protocol}#{request.host_with_port}",
+ @subject,
+ @body,
+ user,
+ @this_conference
+ ]
+ end
+ end
+ redirect_to administration_step_path(@this_conference.slug, :broadcast_sent)
+ return true
+ elsif params[:button] == 'preview'
+ @send_to_count = view_context.broadcast_to(@send_to).size
+ @broadcast_step = :preview
+ elsif params[:button] == 'test'
+ @broadcast_step = :test
+ UserMailer.send_mail :broadcast do
+ [
+ "#{request.protocol}#{request.host_with_port}",
+ @subject,
+ @body,
+ current_user,
+ @this_conference
+ ]
+ end
+ @send_to_count = view_context.broadcast_to(@send_to).size
+ end
+ return false
+ end
+
+ def admin_update_locations
+ case params[:button]
+ when 'save'
+ location = EventLocation.find_by! id: params[:id].to_i, conference_id: @this_conference.id
+ empty_param = get_empty(params, [:title, :address, :space])
+ if empty_param.present?
+ flash[:warning] = (view_context._"errors.messages.fields.#{empty_param.to_s}.empty")
+ else
+ location.title = params[:title]
+ location.address = params[:address]
+ location.amenities = (params[:needs] || {}).keys.to_json
+ location.space = params[:space]
+ location.save!
+ end
+ when 'cancel'
+ # just go back to where we were
+ when 'delete'
+ location = EventLocation.find_by! id: params[:id].to_i, conference_id: @this_conference.id
+ location.destroy
+ when 'create'
+ empty_param = get_empty(params, [:title, :address, :space])
+ if empty_param.present?
+ flash[:warning] = (view_context._"errors.messages.fields.#{empty_param.to_s}.empty")
+ else
+ EventLocation.create(
+ conference_id: @this_conference.id,
+ title: params[:title],
+ address: params[:address],
+ amenities: (params[:needs] || {}).keys.to_json,
+ space: params[:space]
+ )
+ end
+ else
+ do_404
+ end
+
+ return false
+ end
+
+ def admin_update_meals
+ case params[:button]
+ when 'add_meal'
+ @this_conference.meals ||= {}
+ @this_conference.meals[(Date.parse(params[:day]) + params[:time].to_f.hours).to_time.to_i] = {
+ title: params[:title],
+ info: params[:info],
+ location: params[:event_location],
+ day: params[:day],
+ time: params[:time]
+ }
+ @this_conference.save!
+ return false
+ when 'delete'
+ @this_conference.meals ||= {}
+ @this_conference.meals.delete params[:meal]
+ @this_conference.save!
+ return false
+ end
+
+ do_404
+ return true
+ end
+
+ def admin_update_events
+ case params[:button]
+ when 'edit'
+ redirect_to edit_event_path(@this_conference.slug, params[:id])
+ return true
+ when 'save'
+ if params[:id].present?
+ event = Event.find_by!(conference_id: @this_conference.id, id: params[:id])
+ else
+ event = Event.new(conference_id: @this_conference.id, locale: I18n.locale)
+ end
+
+ # save title and info
+ event.title = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:title]) unless event.title! == params[:title]
+ event.info = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:info]) unless event.info! == params[:info]
+
+ # save schedule data
+ event.event_location_id = params[:event_location]
+ event.start_time = Date.parse(params[:day]) + params[:time].to_f.hours
+ event.end_time = event.start_time + params[:time_span].to_f.hours
+
+ # save translations
+ (params[:info_translations] || {}).each do | locale, value |
+ event.set_column_for_locale(:title, locale, value, current_user.id) unless value = event._title(locale)
+ event.set_column_for_locale(:info, locale, value, current_user.id) unless value = event._info(locale)
+ end
+
+ event.save
+
+ return false
+ when 'cancel'
+ return false
+ end
+
+ do_404
+ return true
+ end
+
+ def admin_update_workshop_times
+ case params[:button]
+ when 'save_block'
+ @this_conference.workshop_blocks ||= []
+ @this_conference.workshop_blocks[params[:workshop_block].to_i] = {
+ 'time' => params[:time],
+ 'length' => params[:time_span],
+ 'days' => params[:days].keys
+ }
+ @this_conference.save
+ return false
+ end
+
+ do_404
+ return true
+ end
+
+ def admin_update_schedule
+ case params[:button]
+ when 'deschedule_workshop'
+ workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:id])
+ workshop.event_location_id = nil
+ workshop.block = nil
+ workshop.save!
+ @can_edit = true
+ @entire_page = false
+ get_scheule_data
+ render partial: 'schedule'
+ return true
+ when 'get-workshop-list'
+ get_scheule_data(true)
+
+ @ordered_workshops = {}
+ @block = params[:block].to_i
+ @time = @workshop_blocks[@block]['time'].to_f
+ @day = (Date.parse params[:day])
+ @location = params[:location]
+ @event_location = @location.present? && @location.to_i > 0 ? EventLocation.find(@location.to_i) : nil
+
+ @workshops.sort { |a, b| a.title.downcase <=> b.title.downcase }.each do | workshop |
+ @ordered_workshops[workshop.id] = workshop
+ end
+
+ render partial: 'select_workshop_table'
+ return true
+ when 'set-workshop'
+ workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:workshop].to_i)
+ workshop.event_location_id = params[:location]
+ workshop.block = { day: (Date.parse params[:day]).wday, block: params[:block] }
+ workshop.save!
+
+ @can_edit = true
+ @entire_page = false
+ get_scheule_data
+
+ render partial: 'schedule'
+ return true
+ end
+
+ do_404
+ return true
+ end
+
+ def admin_update_schedule
+ case params[:button]
+ when 'publish'
+ @this_conference.workshop_schedule_published = !@this_conference.workshop_schedule_published
+ @this_conference.save
+ return false
+ end
+
+ do_404
+ return false
+ end
+
+ def get_empty(hash, keys)
+ keys = [keys] unless keys.is_a?(Array)
+ keys.each do | key |
+ return key unless hash[key].present?
+ end
+ return nil
+ end
+end
diff --git a/app/controllers/conferences_controller.rb b/app/controllers/conferences_controller.rb
index 3f06dbb..d374763 100644
--- a/app/controllers/conferences_controller.rb
+++ b/app/controllers/conferences_controller.rb
@@ -2,1813 +2,424 @@ require 'geocoder/calculations'
require 'rest_client'
class ConferencesController < ApplicationController
- include ScheduleHelper
-
- before_action :set_conference, only: [:show, :edit, :update, :destroy, :registrations]
- before_filter :authenticate, only: [:registrations]
-
- def authenticate
- auth = get_secure_info(:registrations_access)
- authenticate_or_request_with_http_basic('Administration') do |username, password|
- username == auth[:username] && password == auth[:password]
- end
- end
-
- def register
- set_conference
-
- @register_template = nil
-
- if logged_in?
- set_or_create_conference_registration
-
- @name = current_user.firstname
- # we should phase out last names
- @name += " #{current_user.lastname}" if current_user.lastname
-
- @name ||= current_user.username
-
- @is_host = @this_conference.host? current_user
- else
- @register_template = :confirm_email
- end
-
- steps = nil
- return do_404 unless registration_steps.present?
-
- @register_template = :administration if params[:admin_step].present?
-
- @errors = {}
- @warnings = []
- form_step = params[:button] ? params[:button].to_sym : nil
-
- # process any data that was passed to us
- if form_step
- if form_step.to_s =~ /^prev_(.+)$/
- steps = registration_steps
- @register_template = steps[steps.find_index($1.to_sym) - 1]
- elsif form_step == :paypal_confirm
- if @registration.present? && @registration.payment_confirmation_token == params[:confirmation_token]
- @amount = PayPal!.details(params[:token]).amount.total
- @registration.payment_info = {:payer_id => params[:PayerID], :token => params[:token], :amount => @amount}.to_yaml
-
- @amount = (@amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')
-
- @registration.save!
- end
-
- @page_title = 'articles.conference_registration.headings.Payment'
- @register_template = :paypal_confirm
- elsif form_step == :paypal_confirmed
- info = YAML.load(@registration.payment_info)
- @amount = nil
- status = nil
- if ENV['RAILS_ENV'] == 'test'
- status = info[:status]
- @amount = info[:amount]
- else
- paypal = PayPal!.checkout!(info[:token], info[:payer_id], PayPalRequest(info[:amount]))
- status = paypal.payment_info.first.payment_status
- @amount = paypal.payment_info.first.amount.total
- end
- if status == 'Completed'
- @registration.registration_fees_paid ||= 0
- @registration.registration_fees_paid += @amount
-
- # don't complete the step unless fees have been paid
- if @registration.registration_fees_paid > 0
- @registration.steps_completed << :payment
- @registration.steps_completed.uniq!
- end
-
- @registration.save!
- else
- @errors[:payment] = :incomplete
- @register_template = :payment
- end
- @page_title = 'articles.conference_registration.headings.Payment'
- else
-
- case form_step
- when :confirm_email
- return do_confirm
- when :contact_info
- if params[:name].present? && params[:name].gsub(/[\s\W]/, '').present?
- current_user.firstname = params[:name].squish
- current_user.lastname = nil
- else
- @errors[:name] = :empty
- end
-
- if params[:location].present? && params[:location].gsub(/[\s\W]/, '').present? && (l = Geocoder.search(params[:location], language: 'en')).present?
- corrected = view_context.location(l.first, @this_conference.locale)
-
- if corrected.present?
- @registration.city = corrected
- if params[:location].gsub(/[\s,]/, '').downcase != @registration.city.gsub(/[\s,]/, '').downcase
- @warnings << view_context._('warnings.messages.location_corrected', vars: {original: params[:location], corrected: corrected})
- end
- else
- @errors[:location] = :unknown
- end
- else
- @errors[:location] = :empty
- end
-
- if params[:languages].present?
- current_user.languages = params[:languages].keys
- else
- @errors[:languages] = :empty
- end
-
- current_user.save! unless @errors.present?
- when :hosting
- @registration.can_provide_housing = params[:can_provide_housing].present?
- if params[:not_attending]
- @registration.is_attending = 'n'
-
- if current_user.is_subscribed.nil?
- current_user.is_subscribed = false
- current_user.save!
- end
- else
- @registration.is_attending = 'y'
- end
-
- @registration.housing_data = {
- address: params[:address],
- phone: params[:phone],
- space: {
- bed_space: params[:bed_space],
- floor_space: params[:floor_space],
- tent_space: params[:tent_space],
- },
- considerations: (params[:considerations] || {}).keys,
- availability: [ params[:first_day], params[:last_day] ],
- notes: params[:notes]
- }
- when :questions
- # create the companion's user account and send a registration link unless they have already registered
- generate_confirmation(User.create(email: params[:companion]), register_path(@this_conference.slug)) if params[:companion].present? && User.find_by_email(params[:companion]).nil?
-
- @registration.housing = params[:housing]
- @registration.arrival = params[:arrival]
- @registration.departure = params[:departure]
- @registration.housing_data = {
- companions: [ params[:companion] ]
- }
- @registration.bike = params[:bike]
- @registration.food = params[:food]
- @registration.allergies = params[:allergies]
- @registration.other = params[:other]
- when :payment
- amount = params[:amount].to_f
-
- if amount > 0
- @registration.payment_confirmation_token = ENV['RAILS_ENV'] == 'test' ? 'token' : Digest::SHA256.hexdigest(rand(Time.now.to_f * 1000000).to_i.to_s)
- @registration.save
-
- host = "#{request.protocol}#{request.host_with_port}"
- response = PayPal!.setup(
- PayPalRequest(amount),
- register_paypal_confirm_url(@this_conference.slug, :paypal_confirm, @registration.payment_confirmation_token),
- register_paypal_confirm_url(@this_conference.slug, :paypal_cancel, @registration.payment_confirmation_token),
- noshipping: true,
- version: 204
- )
- if ENV['RAILS_ENV'] != 'test'
- redirect_to response.redirect_uri
- end
- return
- end
- end
-
- if @errors.present?
- @register_template = form_step
- else
- unless @registration.nil?
- steps = registration_steps
- step_index = steps.find_index(form_step)
- @register_template = steps[step_index + 1] if step_index.present?
-
- # have we reached a new level?
- unless @registration.steps_completed.include? form_step.to_s
- # this step is only completed if a payment has been made
- if form_step != :payment || (@registration.registration_fees_paid || 0) > 0
- @registration.steps_completed ||= []
- @registration.steps_completed << form_step
- @registration.steps_completed.uniq!
- end
- end
-
- @registration.save!
- end
- end
- end
- end
-
- steps ||= registration_steps
-
- # make sure we're on a valid step
- @register_template ||= (params[:step] || current_step).to_sym
-
- if logged_in? && @register_template != :paypal_confirm
- # if we're logged in
- if !steps.include?(@register_template)
- # and we are not viewing a valid step
- return redirect_to register_path(@this_conference.slug)
- elsif @register_template != current_step && !registration_complete? && !@registration.steps_completed.include?(@register_template.to_s)
- # or the step hasn't been reached, registration is not yet complete, and we're not viewing the latest incomplete step
- return redirect_to register_path(@this_conference.slug)
- end
- # then we'll redirect to the current registration step
- end
-
- # prepare the form
- case @register_template
- when :questions
- # see if someone else has asked to be your companion
- if @registration.housing_data.blank?
- ConferenceRegistration.where(
- conference_id: @this_conference.id, can_provide_housing: [nil, false]
- ).where.not(housing_data: nil).each do | r |
- @registration.housing_data = {
- companions: [ r.user.email ]
- } if r.housing_data['companions'].present? && r.housing_data['companions'].include?(current_user.email)
- end
-
- @registration.housing_data ||= { }
- end
- @page_title = 'articles.conference_registration.headings.Registration_Info'
- when :payment
- @page_title = 'articles.conference_registration.headings.Payment'
- when :workshops
- @page_title = 'articles.conference_registration.headings.Workshops'
-
- # initalize our arrays
- @my_workshops = Array.new
- @requested_workshops = Array.new
- @workshops_in_need = Array.new
- @workshops = Array.new
-
- # put wach workshop into the correct array
- Workshop.where(conference_id: @this_conference.id).each do | workshop |
- if workshop.active_facilitator?(current_user)
- @my_workshops << workshop
- elsif workshop.requested_collaborator?(current_user)
- @requested_workshops << workshop
- elsif workshop.needs_facilitators
- @workshops_in_need << workshop
- else
- @workshops << workshop
- end
- end
-
- # sort the arrays by name
- @my_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
- @requested_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
- @workshops_in_need.sort! { |a, b| a.title.downcase <=> b.title.downcase }
- @workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
- when :contact_info
- @page_title = 'articles.conference_registration.headings.Contact_Info'
- when :hosting
- @page_title = 'articles.conference_registration.headings.Hosting'
- @hosting_data = @registration.housing_data || {}
- @hosting_data['space'] ||= Hash.new
- @hosting_data['availability'] ||= Array.new
- @hosting_data['considerations'] ||= Array.new
- when :policy
- @page_title = 'articles.conference_registration.headings.Policy_Agreement'
- when :administration
- @warnings << flash[:error] if flash[:error].present?
- @admin_step = params[:admin_step] || view_context.admin_steps.first.to_s
- return do_404 unless view_context.valid_admin_steps.include?(@admin_step.to_sym)
- @page_title = 'articles.conference_registration.headings.Administration'
-
- case @admin_step.to_sym
- when :organizations
- @organizations = Organization.all
-
- if request.format.xlsx?
- logger.info "Generating organizations.xls"
- @excel_data = {
- columns: [:name, :street_address, :city, :subregion, :country, :postal_code, :email, :phone, :status],
- keys: {
- name: 'forms.labels.generic.name',
- street_address: 'forms.labels.generic.street_address',
- city: 'forms.labels.generic.city',
- subregion: 'forms.labels.generic.subregion',
- country: 'forms.labels.generic.country',
- postal_code: 'forms.labels.generic.postal_code',
- email: 'forms.labels.generic.email',
- phone: 'forms.labels.generic.phone',
- status: 'forms.labels.generic.status'
- },
- data: [],
- }
- @organizations.each do | org |
- if org.present?
- address = org.locations.first
- @excel_data[:data] << {
- name: org.name,
- street_address: address.present? ? address.street : nil,
- city: address.present? ? address.city : nil,
- subregion: address.present? ? I18n.t("geography.subregions.#{address.country}.#{address.territory}") : nil,
- country: address.present? ? I18n.t("geography.countries.#{address.country}") : nil,
- postal_code: address.present? ? address.postal_code : nil,
- email: org.email_address,
- phone: org.phone,
- status: org.status
- }
- end
- end
- return respond_to do | format |
- format.xlsx { render xlsx: :stats, filename: "organizations" }
- end
- end
- when :stats
- get_stats(!request.format.xlsx?)
-
- if request.format.xlsx?
- logger.info "Generating stats.xls"
- return respond_to do | format |
- format.xlsx { render xlsx: :stats, filename: "stats-#{DateTime.now.strftime('%Y-%m-%d')}" }
- end
- else
- @registration_count = @registrations.size
- @completed_registrations = 0
- @bikes = 0
- @donation_count = 0
- @donations = 0
- @food = { meat: 0, vegan: 0, vegetarian: 0, all: 0 }
- @registrations.each do | r |
- if view_context.registration_status(r) == :registered
- @completed_registrations += 1
-
- @bikes += 1 if r.bike == 'yes'
-
- if r.food.present?
- @food[r.food.to_sym] += 1
- @food[:all] += 1
- end
-
- if r.registration_fees_paid.present? && r.registration_fees_paid > 0
- @donation_count += 1
- @donations += r.registration_fees_paid
- end
- end
- end
- end
- when :housing
- # do a full analysis
- analyze_housing
-
- if request.format.xlsx?
- logger.info "Generating housing.xls"
- @excel_data = {
- columns: [:name, :phone, :street_address, :email, :availability, :considerations, :empty, :empty, :empty, :guests],
- keys: {
- name: 'forms.labels.generic.name',
- street_address: 'forms.labels.generic.street_address',
- email: 'forms.labels.generic.email',
- phone: 'forms.labels.generic.phone',
- availability: 'articles.conference_registration.headings.host.availability',
- considerations: 'articles.conference_registration.headings.host.considerations'
- },
- column_types: {
- name: :bold,
- guests: :table
- },
- data: [],
- }
- @hosts.each do | id, host |
- data = (host.housing_data || {})
- host_data = {
- name: host.user.name,
- street_address: data['address'],
- email: host.user.email,
- phone: data['phone'],
- availability: data['availability'].present? && data['availability'][1].present? ? view_context.date_span(data['availability'][0].to_date, data['availability'][1].to_date) : '',
- considerations: ((data['considerations'] || []).map { | consideration | view_context._"articles.conference_registration.host.considerations.#{consideration}" }).join(', '),
- empty: '',
- guests: {
- columns: [:name, :area, :email, :arrival_departure, :allergies, :food, :companion, :city],
- keys: {
- name: 'forms.labels.generic.name',
- area: 'articles.workshops.headings.space',
- email: 'forms.labels.generic.email',
- arrival_departure: 'articles.admin.housing.headings.arrival_departure',
- companion: 'forms.labels.generic.companion',
- city: 'forms.labels.generic.city',
- food: 'forms.labels.generic.food',
- allergies: 'forms.labels.generic.allergies'
- },
- column_types: {
- name: :bold
- },
- data: []
- }
- }
-
- @housing_data[id][:guests].each do | space, space_data |
- space_data.each do | guest_id, guest_data |
- guest = guest_data[:guest]
- if guest.present?
- companion = view_context.companion(guest)
-
- host_data[:guests][:data] << {
- name: guest.user.name,
- area: (view_context._"forms.labels.generic.#{space}"),
- email: guest.user.email,
- arrival_departure: guest.arrival.present? && guest.departure.present? ? view_context.date_span(guest.arrival.to_date, guest.departure.to_date) : '',
- companion: companion.present? ? (companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}")) : '',
- city: guest.city,
- food: guest.food.present? ? (view_context._"articles.conference_registration.questions.food.#{guest.food}") : '',
- allergies: guest.allergies
- }
- end
- end
- end
-
- @excel_data[:data] << host_data
- end
- return respond_to do | format |
- format.xlsx { render xlsx: :stats, filename: "housing" }
- end
- end
- when :locations
- @locations = EventLocation.where(:conference_id => @this_conference.id)
- when :events
- @event = Event.new(locale: I18n.locale)
- @events = Event.where(:conference_id => @this_conference.id)
- @day = nil
- @time = nil
- @length = 1.5
- when :meals
- @meals = Hash[(@this_conference.meals || {}).map{ |k, v| [k.to_i, v] }].sort.to_h
- when :workshop_times
- get_block_data
- @workshop_blocks << {
- 'time' => nil,
- 'length' => 1.0,
- 'days' => []
- }
- when :schedule
- @can_edit = true
- @entire_page = true
- get_scheule_data
- end
- when :confirm_email
- @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
- end
-
- end
-
- def get_stats(html_format = false, id = nil)
- @registrations = ConferenceRegistration.where(:conference_id => @this_conference.id).sort { |a,b| (a.user.present? ? (a.user.firstname || '') : '').downcase <=> (b.user.present? ? (b.user.firstname || '') : '').downcase }
- @excel_data = {
- columns: [
- :name,
- :email,
- :status,
- :is_attending,
- :is_subscribed,
- :registration_fees_paid,
- :date,
- :city,
- :preferred_language
- ] +
- User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
- [
- :arrival,
- :departure,
- :housing,
- :bike,
- :food,
- :companion,
- :companion_email,
- :allergies,
- :other,
- :can_provide_housing,
- :first_day,
- :last_day,
- :address,
- :phone
- ] + ConferenceRegistration.all_spaces +
- ConferenceRegistration.all_considerations + [
- :notes
- ],
- column_types: {
- name: :bold,
- date: :datetime,
- email: :email,
- companion_email: :email,
- arrival: [:date, :day],
- departure: [:date, :day],
- registration_fees_paid: :money,
- allergies: :text,
- other: :text,
- first_day: [:date, :day],
- last_day: [:date, :day],
- notes: :text
- },
- keys: {
- name: 'forms.labels.generic.name',
- email: 'forms.labels.generic.email',
- status: 'forms.labels.generic.registration_status',
- is_attending: 'articles.conference_registration.terms.is_attending',
- is_subscribed: 'articles.user_settings.headings.email_subscribe',
- city: 'forms.labels.generic.event_location',
- date: 'articles.conference_registration.terms.Date',
- preferred_language: 'articles.conference_registration.terms.Preferred_Languages',
- arrival: 'forms.labels.generic.arrival',
- departure: 'forms.labels.generic.departure',
- housing: 'forms.labels.generic.housing',
- bike: 'forms.labels.generic.bike',
- food: 'forms.labels.generic.food',
- companion: 'articles.conference_registration.terms.companion',
- companion_email: 'articles.conference_registration.terms.companion_email',
- allergies: 'forms.labels.generic.allergies',
- registration_fees_paid: 'articles.conference_registration.headings.fees_paid',
- other: 'forms.labels.generic.other_notes',
- can_provide_housing: 'articles.conference_registration.can_provide_housing',
- first_day: 'forms.labels.generic.first_day',
- last_day: 'forms.labels.generic.last_day',
- notes: 'forms.labels.generic.notes',
- phone: 'forms.labels.generic.phone',
- address: 'forms.labels.generic.address',
- contact_info: 'articles.conference_registration.headings.contact_info',
- questions: 'articles.conference_registration.headings.questions',
- hosting: 'articles.conference_registration.headings.hosting'
- },
- data: []
- }
- User.AVAILABLE_LANGUAGES.each do | l |
- @excel_data[:keys]["language_#{l}".to_sym] = "languages.#{l.to_s}"
- end
- ConferenceRegistration.all_spaces.each do |s|
- @excel_data[:column_types][s] = :number
- @excel_data[:keys][s] = "forms.labels.generic.#{s.to_s}"
- end
- ConferenceRegistration.all_considerations.each do |c|
- @excel_data[:keys][c] = "articles.conference_registration.host.considerations.#{c.to_s}"
- end
- @registrations.each do | r |
- user = r.user_id ? User.where(id: r.user_id).first : nil
- if user.present?
- companion = view_context.companion(r)
- companion = companion.is_a?(User) ? companion.name : (view_context._"articles.conference_registration.terms.registration_status.#{companion}") if companion.present?
- steps = r.steps_completed || []
-
- if id.nil? || id == r.id
- housing_data = r.housing_data || {}
- availability = housing_data['availability'] || []
- availability[0] = Date.parse(availability[0]) if availability[0].present?
- availability[1] = Date.parse(availability[1]) if availability[1].present?
-
- data = {
- id: r.id,
- name: user.firstname || '',
- email: user.email || '',
- status: (view_context._"articles.conference_registration.terms.registration_status.#{view_context.registration_status(r)}"),
- is_attending: (view_context._"articles.conference_registration.questions.bike.#{r.is_attending == 'n' ? 'no' : 'yes'}"),
- is_subscribed: user.is_subscribed == false ? (view_context._'articles.conference_registration.questions.bike.no') : '',
- date: r.created_at ? r.created_at.strftime("%F %T") : '',
- city: r.city || '',
- preferred_language: user.locale.present? ? (view_context.language_name user.locale) : '',
- arrival: r.arrival ? r.arrival.strftime("%F %T") : '',
- departure: r.departure ? r.departure.strftime("%F %T") : '',
- housing: r.housing.present? ? (view_context._"articles.conference_registration.questions.housing.#{r.housing}") : '',
- bike: r.bike.present? ? (view_context._"articles.conference_registration.questions.bike.#{r.bike}") : '',
- food: r.food.present? ? (view_context._"articles.conference_registration.questions.food.#{r.food}") : '',
- companion: companion,
- companion_email: (housing_data['companions'] || ['']).first,
- allergies: r.allergies,
- registration_fees_paid: r.registration_fees_paid,
- other: r.other,
- can_provide_housing: r.can_provide_housing ? (view_context._'articles.conference_registration.questions.bike.yes') : '',
- first_day: availability[0].present? ? availability[0].strftime("%F %T") : '',
- last_day: availability[1].present? ? availability[1].strftime("%F %T") : '',
- notes: housing_data['notes'],
- address: housing_data['address'],
- phone: housing_data['phone'],
- raw_values: {
- housing: r.housing,
- bike: r.bike,
- food: r.food,
- arrival: r.arrival.present? ? r.arrival.to_date : nil,
- departure: r.departure.present? ? r.departure.to_date : nil,
- preferred_language: user.locale,
- is_attending: r.is_attending != 'n',
- is_subscribed: user.is_subscribed,
- can_provide_housing: r.can_provide_housing,
- first_day: availability[0].present? ? availability[0].to_date : nil,
- last_day: availability[1].present? ? availability[1].to_date : nil
- },
- html_values: {
- date: r.created_at.present? ? r.created_at.strftime("%F %T") : '',
- arrival: r.arrival.present? ? view_context.date(r.arrival.to_date, :span_same_year_date_1) : '',
- departure: r.departure.present? ? view_context.date(r.departure.to_date, :span_same_year_date_1) : '',
- first_day: availability[0].present? ? view_context.date(availability[0].to_date, :span_same_year_date_1) : '',
- last_day: availability[1].present? ? view_context.date(availability[1].to_date, :span_same_year_date_1) : ''
- }
- }
- User.AVAILABLE_LANGUAGES.each do | l |
- can_speak = ((user.languages || []).include? l.to_s)
- data["language_#{l}".to_sym] = (can_speak ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
- data[:raw_values]["language_#{l}".to_sym] = can_speak
- end
- ConferenceRegistration.all_spaces.each do |s|
- space = (housing_data['space'] || {})[s.to_s]
- data[s] = space.present? ? space.to_i : nil
- end
- ConferenceRegistration.all_considerations.each do |c|
- consideration = (housing_data['considerations'] || []).include?(c.to_s)
- data[c] = (consideration ? (view_context._'articles.conference_registration.questions.bike.yes') : '')
- data[:raw_values][c] = consideration
- end
- @excel_data[:data] << data
- end
- end
- end
-
- if html_format
- yes_no = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true],
- [(view_context._"articles.conference_registration.questions.bike.no"), false]
- ]
- @column_options = {
- housing: ConferenceRegistration.all_housing_options.map { |h| [
- (view_context._"articles.conference_registration.questions.housing.#{h}"),
- h] },
- bike: ConferenceRegistration.all_bike_options.map { |b| [
- (view_context._"articles.conference_registration.questions.bike.#{b}"),
- b] },
- food: ConferenceRegistration.all_food_options.map { |f| [
- (view_context._"articles.conference_registration.questions.food.#{f}"),
- f] },
- arrival: view_context.conference_days_options_list(:before_plus_one),
- departure: view_context.conference_days_options_list(:after_minus_one),
- preferred_language: I18n.backend.enabled_locales.map { |l| [
- (view_context.language_name l), l
- ] },
- is_attending: [yes_no.first],
- is_subscribed: [yes_no.last],
- can_provide_housing: yes_no,
- first_day: view_context.conference_days_options_list(:before),
- last_day: view_context.conference_days_options_list(:after)
- }
- User.AVAILABLE_LANGUAGES.each do | l |
- @column_options["language_#{l}".to_sym] = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true]
- ]
- end
- ConferenceRegistration.all_considerations.each do | c |
- @column_options[c.to_sym] = [
- [(view_context._"articles.conference_registration.questions.bike.yes"), true]
- ]
- end
- end
- end
-
- def get_housing_data
- @hosts = {}
- @guests = {}
- ConferenceRegistration.where(:conference_id => @this_conference.id).each do | registration |
- if registration.can_provide_housing
- @hosts[registration.id] = registration
- elsif registration.housing.present? && registration.housing != 'none'
- @guests[registration.id] = registration
- end
- end
- end
-
- def analyze_housing
- get_housing_data unless @hosts.present? && @guests.present?
-
- @housing_data = {}
- @hosts_affected_by_guests = {}
- @hosts.each do | id, host |
- @hosts[id].housing_data ||= {}
- @housing_data[id] = { guests: {}, space: {} }
- @hosts[id].housing_data['space'] ||= {}
- @hosts[id].housing_data['space'].each do | s, size |
- size = (size || 0).to_i
- @housing_data[id][:guests][s.to_sym] = {}
- @housing_data[id][:space][s.to_sym] = size
- end
- end
-
- @guests_housed = 0
-
- @guests.each do | guest_id, guest |
- data = guest.housing_data || {}
- @hosts_affected_by_guests[guest_id] ||= []
-
- if data['host']
- @guests_housed += 1
- host_id = (data['host'].present? ? data['host'].to_i : nil)
- host = host_id.present? ? @hosts[host_id] : nil
-
- # make sure the host was found and that they are still accepting guests
- if host.present? && host.can_provide_housing
- @hosts_affected_by_guests[guest_id] << host_id
-
- space = (data['space'] || :bed).to_sym
-
- @housing_data[host_id] ||= {}
- host_data = host.housing_data
- unless @housing_data[host_id][:guests][space].present?
- @housing_data[host_id][:guests][space] ||= {}
- @housing_data[host_id][:space][space] ||= 0
- end
-
- @housing_data[host_id][:guests][space][guest_id] = { guest: guest }
-
- @housing_data[host_id][:guest_data] ||= {}
- @housing_data[host_id][:guest_data][guest_id] = { warnings: {}, errors: {} }
-
- @housing_data[host_id][:guest_data][guest_id][:warnings][:dates] = {} unless view_context.available_dates_match?(host, guest)
-
- if (guest.housing == 'house' && space == :tent) ||
- (guest.housing == 'tent' && (space == :bed_space || space == :floor_space))
- @housing_data[host_id][:guest_data][guest_id][:warnings][:space] = { actual: (view_context._"forms.labels.generic.#{space.to_s}"), expected: (view_context._"articles.conference_registration.questions.housing.#{guest.housing}")}
- end
-
- companions = data['companions'] || []
- companions.each do | companion |
- user = User.find_by_email(companion)
- if user.present?
- reg = ConferenceRegistration.find_by(
- :user_id => user.id,
- :conference_id => @this_conference.id
- )
- if reg.present? && @guests[reg.id].present?
- housing_data = reg.housing_data || {}
- companion_host = housing_data['host'].present? ? housing_data['host'].to_i : nil
- @hosts_affected_by_guests[guest_id] << companion_host
- if companion_host != host_id && reg.housing.present? && reg.housing != 'none'
- # set this as an error if the guest has selected only one other to stay with, but if they have requested to stay with more, make this only a warning
- @housing_data[host_id][:guest_data][guest_id][:warnings][:companions] = { name: "#{reg.user.name} ".html_safe, id: reg.id }
- end
- end
- end
- end
- else
- # make sure the housing data is empty if the host wasn't found, just in case something happened to the host
- @guests[guest_id].housing_data ||= {}
- @guests[guest_id].housing_data['host'] = nil
- @guests[guest_id].housing_data['space'] = nil
- end
- end
- end
-
- @hosts.each do | id, host |
- host_data = host.housing_data
-
- @hosts[id].housing_data['space'].each do | space, size |
- # make sure the host isn't overbooked
- space = space.to_sym
- space_available = (size || 0).to_i
- @housing_data[id][:warnings] ||= {}
- @housing_data[id][:warnings][:space] ||= {}
- @housing_data[id][:warnings][:space][space] ||= []
-
- if @housing_data[id][:guests][space].size > space_available
- @housing_data[id][:warnings][:space][space] << :overbooked
- end
- end
- end
-
- return @hosts_affected_by_guests
- end
-
- def admin_update
- set_conference
- # set_conference_registration
- return do_403 unless @this_conference.host? current_user
-
- # set the page title in case we render instead of redirecting
- @page_title = 'articles.conference_registration.headings.Administration'
- @register_template = :administration
- @admin_step = params[:admin_step]
-
- case params[:admin_step]
- when 'stats'
- if params[:button] == 'save' || params[:button] == 'update'
- if params[:button] == 'save'
- return do_404 unless params[:email].present? && params[:name].present?
-
- user = User.find_by_email(params[:email]) || User.create(email: params[:email])
- user.firstname = params[:name]
- user.save!
- registration = ConferenceRegistration.new(
- conference: @this_conference,
- user_id: user.id,
- steps_completed: []
- )
- else
- registration = ConferenceRegistration.where(
- id: params[:key].to_i,
- conference_id: @this_conference.id
- ).limit(1).first
- end
-
- user_changed = false
- params.each do | key, value |
- case key.to_sym
- when :city
- registration.city = value.present? ? view_context.location(Geocoder.search(value, language: @this_conference.locale).first, @this_conference.locale) : nil
- when :housing, :bike, :food, :allergies, :other
- registration.send("#{key.to_s}=", value)
- when :registration_fees_paid
- registration.registration_fees_paid = value.to_i
- when :can_provide_housing
- registration.send("#{key.to_s}=", value.present?)
- when :arrival, :departure
- registration.send("#{key.to_s}=", value.present? ? Date.parse(value) : nil)
- when :companion_email
- registration.housing_data ||= {}
- registration.housing_data['companions'] = [value]
- when :preferred_language
- registration.user.locale = value
- user_changed = true
- when :is_subscribed
- registration.user.is_subscribed = (value != "false")
- user_changed = true
- when :is_attending
- registration.is_attending = value.present? ? 'y' : 'n'
- when :first_day
- registration.housing_data ||= {}
- registration.housing_data['availability'] ||= []
- registration.housing_data['availability'][0] = value
- when :last_day
- registration.housing_data ||= {}
- registration.housing_data['availability'] ||= []
- registration.housing_data['availability'][1] = value
- when :address, :phone, :notes
- registration.housing_data ||= {}
- registration.housing_data[key.to_s] = value
- else
- if key.start_with?('language_')
- l = key.split('_').last
- if User.AVAILABLE_LANGUAGES.include? l.to_sym
- registration.user.languages ||= []
- if value.present?
- registration.user.languages |= [l]
- else
- registration.user.languages -= [l]
- end
- user_changed = true
- end
- elsif ConferenceRegistration.all_considerations.include? key.to_sym
- registration.housing_data ||= {}
- registration.housing_data['considerations'] ||= []
- if value.present?
- registration.housing_data['considerations'] |= [key]
- else
- registration.housing_data['considerations'] -= [key]
- end
- elsif ConferenceRegistration.all_spaces.include? key.to_sym
- registration.housing_data ||= {}
- registration.housing_data['space'] ||= {}
- registration.housing_data['space'][key.to_s] = value
- end
- end
- end
- registration.user.save! if user_changed
- registration.save!
-
- if params[:button] == 'save'
- return redirect_to register_step_path(@this_conference.slug, :administration)
- end
-
- get_stats(true, params[:key].to_i)
- options = view_context.registrations_table_options
- options[:html] = true
- return render html: view_context.excel_rows(@excel_data, {}, options)
- end
- when 'edit'
- case params[:button]
- when 'save'
- @this_conference.registration_status = params[:registration_status]
- @this_conference.info = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:info]) unless @this_conference.info! == params[:info]
-
- params[:info_translations].each do | locale, value |
- @this_conference.set_column_for_locale(:info, locale, value, current_user.id) unless value == @this_conference._info(locale)
- end
- @this_conference.save
- return redirect_to administration_step_path(@this_conference.slug, :edit)
- when 'add_member'
- org = nil
- @this_conference.organizations.each do | organization |
- org = organization if organization.id == params[:org_id].to_i
- end
- org.users << (User.get params[:email])
- org.save
- return redirect_to administration_step_path(@this_conference.slug, :edit)
- end
- when 'payment'
- case params[:button]
- when 'save'
- @this_conference.payment_message = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:payment_message]) unless @this_conference.payment_message! == params[:payment_message]
-
- params[:payment_message_translations].each do | locale, value |
- @this_conference.set_column_for_locale(:payment_message, locale, value, current_user.id) unless value == @this_conference._payment_message(locale)
- end
-
- @this_conference.payment_amounts = ((params[:payment_amounts] || {}).values.map &:to_i) - [0]
-
- @this_conference.paypal_email_address = params[:paypal_email_address]
- @this_conference.paypal_username = params[:paypal_username]
- @this_conference.paypal_password = params[:paypal_password]
- @this_conference.paypal_signature = params[:paypal_signature]
- @this_conference.save
- return redirect_to administration_step_path(@this_conference.slug, :payment)
- end
- when 'housing'
- # modify the guest data
-
- if params[:button] == 'get-guest-list'
- analyze_housing
- return render partial: 'conferences/admin/select_guest_table', locals: { host: @hosts[params['host'].to_i], space: params['space'] }
- elsif params[:button] == 'set-guest'
- guest = ConferenceRegistration.where(
- id: params[:guest].to_i,
- conference_id: @this_conference.id
- ).limit(1).first
-
- guest.housing_data ||= {}
- guest.housing_data['space'] = params[:space]
- guest.housing_data['host'] = params[:host].to_i
- guest.save!
-
- analyze_housing
-
- return render partial: 'conferences/admin/hosts_table'
- elsif params[:button] == 'remove-guest'
- guest = ConferenceRegistration.where(
- id: params[:guest].to_i,
- conference_id: @this_conference.id
- ).limit(1).first
-
- guest.housing_data ||= {}
- guest.housing_data.delete('space')
- guest.housing_data.delete('host')
- guest.save!
-
- analyze_housing
-
- return render partial: 'conferences/admin/hosts_table'
- end
- when 'broadcast'
- @hide_description = true
- @subject = params[:subject]
- @body = params[:body]
- @send_to = params[:send_to]
- @register_template = :administration
- if params[:button] == 'send'
- view_context.broadcast_to(@send_to).each do | user |
- UserMailer.send_mail :broadcast do
- [
- "#{request.protocol}#{request.host_with_port}",
- @subject,
- @body,
- user,
- @this_conference
- ]
- end
- end
- return redirect_to administration_step_path(@this_conference.slug, :broadcast_sent)
- elsif params[:button] == 'preview'
- @send_to_count = view_context.broadcast_to(@send_to).size
- @broadcast_step = :preview
- elsif params[:button] == 'test'
- @broadcast_step = :test
- UserMailer.send_mail :broadcast do
- [
- "#{request.protocol}#{request.host_with_port}",
- @subject,
- @body,
- current_user,
- @this_conference
- ]
- end
- @send_to_count = view_context.broadcast_to(@send_to).size
- end
- return render 'conferences/register'
- when 'locations'
- case params[:button]
- when 'edit'
- @location = EventLocation.find_by! id: params[:id].to_i, conference_id: @this_conference.id
- return render 'conferences/register'
- when 'save'
- location = EventLocation.find_by! id: params[:id].to_i, conference_id: @this_conference.id
- empty_param = get_empty(params, [:title, :address, :space])
- if empty_param.present?
- flash[:error] = (view_context._"errors.messages.fields.#{empty_param.to_s}.empty")
- else
- location.title = params[:title]
- location.address = params[:address]
- location.amenities = (params[:needs] || {}).keys.to_json
- location.space = params[:space]
- location.save!
- end
- return redirect_to administration_step_path(@this_conference.slug, :locations)
- when 'cancel'
- return redirect_to administration_step_path(@this_conference.slug, :locations)
- when 'delete'
- location = EventLocation.find_by! id: params[:id].to_i, conference_id: @this_conference.id
- location.destroy
- return redirect_to administration_step_path(@this_conference.slug, :locations)
- when 'create'
- empty_param = get_empty(params, [:title, :address, :space])
- if empty_param.present?
- flash[:error] = (view_context._"errors.messages.fields.#{empty_param.to_s}.empty")
- else
- EventLocation.create(
- conference_id: @this_conference.id,
- title: params[:title],
- address: params[:address],
- amenities: (params[:needs] || {}).keys.to_json,
- space: params[:space]
- )
- end
- return redirect_to administration_step_path(@this_conference.slug, :locations)
- end
- when 'meals'
- case params[:button]
- when 'add_meal'
- @this_conference.meals ||= {}
- @this_conference.meals[(Date.parse(params[:day]) + params[:time].to_f.hours).to_time.to_i] = {
- title: params[:title],
- info: params[:info],
- location: params[:event_location],
- day: params[:day],
- time: params[:time]
- }
- @this_conference.save!
- return redirect_to administration_step_path(@this_conference.slug, :meals)
- when 'delete'
- @this_conference.meals ||= {}
- @this_conference.meals.delete params[:meal]
- @this_conference.save!
- return redirect_to administration_step_path(@this_conference.slug, :meals)
- end
- when 'events'
- case params[:button]
- when 'edit'
- @event = Event.find_by!(conference_id: @this_conference.id, id: params[:id])
- @day = @event.start_time.midnight
- @time = view_context.hour_span(@day, @event.start_time)
- @length = view_context.hour_span(@event.start_time, @event.end_time)
- return render 'conferences/register'
- when 'save'
- if params[:id].present?
- event = Event.find_by!(conference_id: @this_conference.id, id: params[:id])
- else
- event = Event.new(conference_id: @this_conference.id, locale: I18n.locale)
- end
-
- # save title and info
- event.title = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:title]) unless event.title! == params[:title]
- event.info = LinguaFranca::ActiveRecord::UntranslatedValue.new(params[:info]) unless event.info! == params[:info]
-
- # save schedule data
- event.event_location_id = params[:event_location]
- event.start_time = Date.parse(params[:day]) + params[:time].to_f.hours
- event.end_time = event.start_time + params[:time_span].to_f.hours
-
- # save translations
- (params[:info_translations] || {}).each do | locale, value |
- event.set_column_for_locale(:title, locale, value, current_user.id) unless value = event._title(locale)
- event.set_column_for_locale(:info, locale, value, current_user.id) unless value = event._info(locale)
- end
-
- event.save
-
- return redirect_to administration_step_path(@this_conference.slug, :events)
- when 'cancel'
- return redirect_to administration_step_path(@this_conference.slug, :events)
- end
- when 'workshop_times'
- case params[:button]
- when 'save_block'
- @this_conference.workshop_blocks ||= []
- @this_conference.workshop_blocks[params[:workshop_block].to_i] = {
- 'time' => params[:time],
- 'length' => params[:time_span],
- 'days' => params[:days].keys
- }
- @this_conference.save
- return redirect_to administration_step_path(@this_conference.slug, :workshop_times)
- end
- when 'schedule'
- case params[:button]
- when 'deschedule_workshop'
- workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:id])
- workshop.event_location_id = nil
- workshop.block = nil
- workshop.save!
- @can_edit = true
- @entire_page = false
- get_scheule_data
- return render partial: 'conferences/admin/schedule'
- when 'publish'
- @this_conference.workshop_schedule_published = !@this_conference.workshop_schedule_published
- @this_conference.save
- return redirect_to administration_step_path(@this_conference.slug, :schedule)
- when 'get-workshop-list'
- get_scheule_data(true)
-
- @ordered_workshops = {}
- @block = params[:block].to_i
- @time = @workshop_blocks[@block]['time'].to_f
- @day = (Date.parse params[:day])
- @location = params[:location]
- @event_location = @location.present? && @location.to_i > 0 ? EventLocation.find(@location.to_i) : nil
-
- @workshops.sort { |a, b| a.title.downcase <=> b.title.downcase }.each do | workshop |
- @ordered_workshops[workshop.id] = workshop
- end
-
- return render partial: 'conferences/admin/select_workshop_table'
- when 'set-workshop'
- workshop = Workshop.find_by!(conference_id: @this_conference.id, id: params[:workshop].to_i)
- workshop.event_location_id = params[:location]
- workshop.block = { day: (Date.parse params[:day]).wday, block: params[:block] }
- workshop.save!
-
- @can_edit = true
- @entire_page = false
- get_scheule_data
- return render partial: 'conferences/admin/schedule'
- end
- end
- do_404
- end
-
- def workshops
- set_conference
- set_conference_registration!
- @workshops = Workshop.where(:conference_id => @this_conference.id)
- @my_workshops = Workshop.joins(:workshop_facilitators).where(:workshop_facilitators => {:user_id => current_user.id}, :conference_id => @this_conference.id)
- render 'workshops/index'
- end
-
- def view_workshop
- set_conference
- set_conference_registration!
- @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless @workshop
-
- @translations_available_for_editing = []
- I18n.backend.enabled_locales.each do |locale|
- @translations_available_for_editing << locale if @workshop.can_translate?(current_user, locale)
- end
- @page_title = 'page_titles.conferences.View_Workshop'
- @register_template = :workshops
-
- render 'workshops/show'
- end
-
- def create_workshop
- set_conference
- set_conference_registration!
- @workshop = Workshop.new
- @languages = [I18n.locale.to_sym]
- @needs = []
- @page_title = 'page_titles.conferences.Create_Workshop'
- @register_template = :workshops
- render 'workshops/new'
- end
-
- def translate_workshop
- @is_translating = true
- @translation = params[:locale]
- @page_title = 'page_titles.conferences.Translate_Workshop'
- @page_title_vars = { language: view_context.language_name(@translation) }
- @register_template = :workshops
-
- edit_workshop
- end
-
- def edit_workshop
- set_conference
- set_conference_registration!
- @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
-
- return do_404 unless @workshop.present?
-
- @page_title ||= 'page_titles.conferences.Edit_Workshop'
-
- @can_edit = @workshop.can_edit?(current_user)
-
- @is_translating ||= false
- if @is_translating
- return do_404 if @translation.to_s == @workshop.locale.to_s || !I18n.backend.enabled_locales.include?(@translation.to_s)
- return do_403 unless @workshop.can_translate?(current_user, @translation)
-
- @title = @workshop._title(@translation)
- @info = @workshop._info(@translation)
- else
- return do_403 unless @can_edit
-
- @title = @workshop.title
- @info = @workshop.info
- end
-
- @needs = JSON.parse(@workshop.needs || '[]').map &:to_sym
- @languages = JSON.parse(@workshop.languages || '[]').map &:to_sym
- @space = @workshop.space.to_sym if @workshop.space
- @theme = @workshop.theme.to_sym if @workshop.theme
- @notes = @workshop.notes
- @register_template = :workshops
-
- render 'workshops/new'
- end
-
- def delete_workshop
- set_conference
- set_conference_registration!
- @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
-
- return do_404 unless @workshop.present?
- return do_403 unless @workshop.can_delete?(current_user)
-
- if request.post?
- if params[:button] == 'confirm'
- if @workshop
- @workshop.workshop_facilitators.destroy_all
- @workshop.destroy
- end
-
- return redirect_to register_step_path(@this_conference.slug, 'workshops')
- end
- return redirect_to view_workshop_url(@this_conference.slug, @workshop.id)
- end
- @register_template = :workshops
-
- render 'workshops/delete'
- end
-
- def save_workshop
- set_conference
- set_conference_registration!
-
- if params[:button].to_sym != :save
- if params[:workshop_id].present?
- return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id])
- end
- return redirect_to register_step_path(@this_conference.slug, 'workshops')
- end
-
- if params[:workshop_id].present?
- workshop = Workshop.find(params[:workshop_id])
- return do_404 unless workshop.present?
- can_edit = workshop.can_edit?(current_user)
- else
- workshop = Workshop.new(:conference_id => @this_conference.id)
- workshop.workshop_facilitators = [WorkshopFacilitator.new(:user_id => current_user.id, :role => :creator)]
- can_edit = true
- end
-
- title = params[:title]
- info = params[:info].gsub(/^\s*(.*?)\s*$/, '\1')
-
- if params[:translation].present? && workshop.can_translate?(current_user, params[:translation])
- old_title = workshop._title(params[:translation])
- old_info = workshop._info(params[:translation])
-
- do_save = false
-
- unless title == old_title
- workshop.set_column_for_locale(:title, params[:translation], title, current_user.id)
- do_save = true
- end
- unless info == old_info
- workshop.set_column_for_locale(:info, params[:translation], info, current_user.id)
- do_save = true
- end
-
- # only save if the text has changed, if we want to make sure only to update the translator id if necessary
- workshop.save_translations if do_save
- elsif can_edit
- workshop.title = title
- workshop.info = info
- workshop.languages = (params[:languages] || {}).keys.to_json
- workshop.needs = (params[:needs] || {}).keys.to_json
- workshop.theme = params[:theme] == 'other' ? params[:other_theme] : params[:theme]
- workshop.space = params[:space]
- workshop.notes = params[:notes]
- workshop.needs_facilitators = params[:needs_facilitators].present?
- workshop.save
-
- # Rouge nil facilitators have been know to be created, just destroy them here now
- WorkshopFacilitator.where(:user_id => nil).destroy_all
- else
- return do_403
- end
-
- redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
-
- def toggle_workshop_interest
- set_conference
- set_conference_registration!
- workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless workshop
-
- # save the current state
- interested = workshop.interested? current_user
- # remove all associated fields
- WorkshopInterest.delete_all(:workshop_id => workshop.id, :user_id => current_user.id)
-
- # creat the new interest row if we weren't interested before
- WorkshopInterest.create(:workshop_id => workshop.id, :user_id => current_user.id) unless interested
-
- if request.xhr?
- render json: [
- {
- selector: '.interest-button',
- html: view_context.interest_button(workshop)
- },
- {
- selector: '.interest-text',
- html: view_context.interest_text(workshop)
- }
- ]
- else
- # go back to the workshop
- redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
- end
-
- def facilitate_workshop
- set_conference
- set_conference_registration!
- @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless @workshop
- return do_403 if @workshop.facilitator?(current_user) || !current_user
-
- @register_template = :workshops
- render 'workshops/facilitate'
- end
-
- def facilitate_request
- set_conference
- set_conference_registration!
- workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless workshop
- return do_403 if workshop.facilitator?(current_user) || !current_user
-
- # create the request by making the user a facilitator but making their role 'requested'
- WorkshopFacilitator.create(user_id: current_user.id, workshop_id: workshop.id, role: :requested)
-
- UserMailer.send_mail :workshop_facilitator_request do
- {
- :args => [ workshop, current_user, params[:message] ]
- }
- end
-
- redirect_to sent_facilitate_workshop_url(@this_conference.slug, workshop.id)
- end
-
- def sent_facilitate_request
- set_conference
- set_conference_registration!
- @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless @workshop
- return do_403 unless @workshop.requested_collaborator?(current_user)
-
- @register_template = :workshops
- render 'workshops/facilitate_request_sent'
- end
-
- def approve_facilitate_request
- return do_403 unless logged_in?
- set_conference
- set_conference_registration!
- workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
- return do_404 unless workshop.present?
-
- user_id = params[:user_id].to_i
- action = params[:approve_or_deny].to_sym
- user = User.find(user_id)
- case action
- when :approve
- if workshop.active_facilitator?(current_user) && workshop.requested_collaborator?(User.find(user_id))
- f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
- workshop.id, user_id)
- f.role = :collaborator
- f.save
- UserMailer.send_mail :workshop_facilitator_request_approved, user.locale do
- [ workshop, user ]
- end
- return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
- when :deny
- if workshop.active_facilitator?(current_user) && workshop.requested_collaborator?(User.find(user_id))
- WorkshopFacilitator.delete_all(
- :workshop_id => workshop.id,
- :user_id => user_id)
- UserMailer.send_mail :workshop_facilitator_request_denied, user.locale do
- [ workshop, user ]
- end
- return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
- when :remove
- if workshop.can_remove?(current_user, user)
- WorkshopFacilitator.delete_all(
- :workshop_id => workshop.id,
- :user_id => user_id)
- return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
- when :switch_ownership
- if workshop.creator?(current_user)
- f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
- workshop.id, current_user.id)
- f.role = :collaborator
- f.save
- f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
- workshop.id, user_id)
- f.role = :creator
- f.save
- return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
- end
- end
-
- return do_403
- end
-
- def add_workshop_facilitator
- set_conference
- set_conference_registration!
-
- user = User.find_by_email(params[:email])
-
- # create the user if they don't exist and send them a link to register
- unless user
- user = User.create(email: params[:email])
- generate_confirmation(user, register_path(@this_conference.slug))
- end
-
- workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
-
- return do_404 unless workshop && current_user
-
- unless workshop.facilitator?(user)
- WorkshopFacilitator.create(user_id: user.id, workshop_id: workshop.id, role: :collaborator)
-
- UserMailer.send_mail :workshop_facilitator_request_approved, user.locale do
- [ workshop, user ]
- end
- end
-
- return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id])
- end
-
- def add_comment
- set_conference
- set_conference_registration!
- workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
-
- return do_404 unless workshop && current_user
-
- if params[:button] == 'reply'
- comment = Comment.find_by!(id: params[:comment_id].to_i, model_type: :workshops, model_id: workshop.id)
- new_comment = comment.add_comment(current_user, params[:reply])
-
- unless comment.user.id == current_user.id
- UserMailer.send_mail :workshop_comment, comment.user.locale do
- [ workshop, new_comment, comment.user ]
- end
- end
- elsif params[:button] = 'add_comment'
- new_comment = workshop.add_comment(current_user, params[:comment])
-
- workshop.active_facilitators.each do | u |
- unless u.id == current_user.id
- UserMailer.send_mail :workshop_comment, u.locale do
- [ workshop, new_comment, u ]
- end
- end
- end
- else
- return do_404
- end
-
- return redirect_to view_workshop_url(@this_conference.slug, workshop.id, anchor: "comment-#{new_comment.id}")
- end
-
- helper_method :registration_steps
- helper_method :current_registration_steps
- helper_method :registration_complete?
-
- def registration_steps(conference = nil)
- conference ||= @this_conference || @conference
- status = conference.registration_status
- # return [] unless status == :pre || status == :open
-
- steps = status == :pre || status == :open ? [
- :policy,
- :contact_info,
- :questions,
- :hosting,
- :payment,
- :workshops
- ] : []
-
- steps -= [:questions] unless status == :open
- steps -= [:payment] unless status == :open && conference.paypal_email_address.present? && conference.paypal_username.present? && conference.paypal_password.present? && conference.paypal_signature.present?
- if @registration.present?
- if view_context.same_city?(@registration.city, view_context.location(conference.location, conference.locale))
- steps -= [:questions]
-
- # if this is a housing provider that is not attending the conference, remove these steps
- if @registration.is_attending == 'n'
- steps -= [:payment, :workshops]
- end
- else
- steps -= [:hosting]
- end
- else
- steps -= [:hosting, :questions]
- end
-
- steps += [:administration] if conference.host?(current_user)
-
- return steps
- end
-
- def required_steps(conference = nil)
- # return the intersection of current steps and required steps
- registration_steps(conference || @this_conference || @conference) & # current steps
- [:policy, :contact_info, :hosting, :questions] # all required steps
- end
-
- def registration_complete?(registration = @registration)
- completed_steps = registration.steps_completed || []
- required_steps(registration.conference).each do | step |
- return true if step == :workshops
- return false unless completed_steps.include?(step.to_s)
- end
- return true
- end
-
- def current_registration_steps(registration = @registration)
- return nil unless registration.present?
-
- steps = registration_steps(registration.conference)
- current_steps = []
- disable_steps = false
- completed_steps = registration.steps_completed || []
- registration_complete = registration_complete?(registration)
- steps.each do | step |
- # disable the step if we've already found an incomplete step
- enabled = !disable_steps || registration_complete
- # record whether or not we've found an incomplete step
- disable_steps ||= !completed_steps.include?(step.to_s)
-
- current_steps << {
- name: step,
- enabled: enabled
- }
- end
- return current_steps
- end
-
- def current_step(registration = @registration)
- completed_steps = registration.steps_completed || []
- (registration_steps(registration.conference) || []).each do | step |
- return step unless completed_steps.include?(step.to_s)
- end
- return registration_steps(registration.conference).last
- end
-
- rescue_from ActiveRecord::RecordNotFound do |exception|
- do_404
- end
-
- rescue_from ActiveRecord::PremissionDenied do |exception|
- if logged_in?
- redirect_to :register
- else
- @register_template = :confirm_email
- @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
- render :register
- end
- end
-
- private
- # Use callbacks to share common setup or constraints between actions.
- def set_conference
- @this_conference = Conference.find_by!(slug: params[:conference_slug] || params[:slug])
- end
-
- def set_conference_registration
- @registration = logged_in? ? ConferenceRegistration.find_by(:user_id => current_user.id, :conference_id => @this_conference.id) : nil
- end
-
- def set_conference_registration!
- @registration = set_conference_registration
- raise ActiveRecord::PremissionDenied unless @registration.present?
- end
-
- def set_or_create_conference_registration
- set_conference_registration
- return @registration if @registration.present?
-
- @registration ||= ConferenceRegistration.new(
- conference: @this_conference,
- user_id: current_user.id,
- steps_completed: []
- )
- last_registration_data = ConferenceRegistration.where(user_id: current_user.id).order(created_at: :desc).limit(1).first
-
- if last_registration_data.present?
- if last_registration_data['languages'].present? && current_user.languages.blank?
- current_user.languages = JSON.parse(last_registration_data['languages'])
- current_user.save!
- end
-
- @registration.city = last_registration_data.city if last_registration_data.city.present?
- end
- end
-
- # Only allow a trusted parameter "white list" through.
- def conference_params
- params.require(:conference).permit(:title, :slug, :start_date, :end_date, :info, :poster, :cover, :workshop_schedule_published, :registration_status, :meals_provided, :meal_info, :travel_info, :conference_type_id, conference_types: [:id])
- end
-
- def update_field_position(field_id, position)
- data = []
- for i in 0..@conference.conference_registration_form_fields.length
- f = @conference.conference_registration_form_fields[i]
- if f.registration_form_field_id == field_id
- data << (f.registration_form_field_id.to_s + ' == ' + field_id.to_s + ' [position: ' + position.to_s + ' == ' + f.position.to_s + ']')
- f.update_attributes(:position => position)
- return
- end
- end
- end
-
- def update_registration_data
- if session[:registration][:registration_id]
- registration = ConferenceRegistration.find(session[:registration][:registration_id])
- registration.data = YAML.load(registration.data).merge(session[:registration]).to_yaml
- registration.save!
- end
- end
-
- def complete_registration
- if session[:registration][:registration_id]
- registration = ConferenceRegistration.find(session[:registration][:registration_id])
- session[:registration] = YAML.load(registration.data)
- registration.completed = true
- if registration.is_confirmed
- registration.complete = true
-
- user = User.find_by(:email => session[:registration][:email])
- if !user
- user = User.new(:email => session[:registration][:email], :username => session[:registration][:user][:username], :role => 'user')
- end
- user.firstname = session[:registration][:user][:firstname]
- user.lastname = session[:registration][:user][:lastname]
- user.save!
-
- if session[:registration][:is_participant]
- UserOrganizationRelationship.destroy_all(:user_id => user.id)
- session[:registration][:organizations].each { |org_id|
- found = false
- org = Organization.find(org_id.is_a?(Array) ? org_id.first : org_id)
- org.user_organization_relationships.each {|rel| found = found && rel.user_id == user.id}
- if !found
- org.user_organization_relationships << UserOrganizationRelationship.new(:user_id => user.id, :relationship => UserOrganizationRelationship::Administrator)
- end
- org.save!
- }
-
- if session[:registration][:new_organization]
- session[:registration][:new_organization].each { |new_org|
- found = false
- org = Organization.find_by(:email_address => new_org[:email])
- if org.nil?
- org = Organization.new(
- :name => new_org[:name],
- :email_address => new_org[:email],
- :info => new_org[:info]
- )
- org.locations << Location.new(:country => new_org[:country], :territory => new_org[:territory], :city => new_org[:city], :street => new_org[:street])
- end
- org.user_organization_relationships.each {|rel| found = found && rel.user_id == user.id}
- if !found
- org.user_organization_relationships << UserOrganizationRelationship.new(:user_id => user.id, :relationship => UserOrganizationRelationship::Administrator)
- end
- org.save!
- org.avatar = "#{request.protocol}#{request.host_with_port}/#{new_org[:logo]}"
- cover = get_panoramio_image(org.locations.first)
- org.cover = cover[:image]
- org.cover_attribution_id = cover[:attribution_id]
- org.cover_attribution_user_id = cover[:attribution_user_id]
- org.cover_attribution_name = cover[:attribution_user_name]
- org.cover_attribution_src = cover[:attribution_src]
- org.save!
- }
- end
-
- if session[:registration][:is_workshop_host] && session[:registration][:workshop]
- session[:registration][:workshop].each { |new_workshop|
- workshop = Workshop.new(
- :conference_id => @conference.id,
- :title => new_workshop[:title],
- :info => new_workshop[:info],
- :workshop_stream_id => WorkshopStream.find_by(:slug => new_workshop[:stream]).id,
- :workshop_presentation_style => WorkshopPresentationStyle.find_by(:slug => new_workshop[:presentation_style])
- )
- workshop.workshop_facilitators << WorkshopFacilitator.new(:user_id => user.id)
- workshop.save!
- }
- end
- end
-
- send_confirmation_confirmation(registration, session[:registration])
-
- session.delete(:registration)
- session[:registration] = Hash.new
- session[:registration][:registration_id] = registration.id
- end
- registration.save!
- end
- end
-
- def create_registration
- if session[:registration][:registration_id].blank? || !ConferenceRegistration.exists?(session[:registration][:registration_id])
- registration = ConferenceRegistration.new(
- :conference_id => @conference.id,
- :user_id => session[:registration][:user][:id],
- :email => session[:registration][:email],
- :is_attending => 'y',
- :is_participant => session[:registration][:is_participant],
- :is_volunteer => session[:registration][:is_volunteer],
- :is_confirmed => false,
- :complete => false,
- :completed => false,
- :confirmation_token => rand_hash(32, :conference_registration, :confirmation_token),
- :payment_confirmation_token => rand_hash(32, :conference_registration, :payment_confirmation_token),
- :data => session[:registration].to_yaml
- )
- registration.save!
- session[:registration][:registration_id] = registration.id
- send_confirmation(registration, session[:registration])
- end
- end
-
- def send_confirmation(registration = nil, data = nil)
- registration ||= ConferenceRegistration.find(session[:registration][:registration_id])
- data ||= YAML.load(registration.data)
- UserMailer.conference_registration_email(@conference, data, registration).deliver
- end
-
- def send_confirmation_confirmation(registration = nil, data = nil)
- registration ||= ConferenceRegistration.find(session[:registration][:registration_id])
- data ||= YAML.load(registration.data)
- UserMailer.conference_registration_confirmed_email(@conference, data, registration).deliver
- end
-
- def send_payment_received(registration = nil, data = nil)
- registration ||= ConferenceRegistration.find(session[:registration][:registration_id])
- data ||= YAML.load(registration.data)
- UserMailer.conference_registration_payment_received(@conference, data, registration).deliver
- end
-
- def get_empty(hash, keys)
- keys = [keys] unless keys.is_a?(Array)
- keys.each do | key |
- puts " ===== #{key} = #{hash[key]} ===== "
- return key unless hash[key].present?
- end
- return nil
- end
-
- def PayPal!
- Paypal::Express::Request.new(
- username: @this_conference.paypal_username,
- password: @this_conference.paypal_password,
- signature: @this_conference.paypal_signature
- )
- end
-
- def PayPalRequest(amount)
- Paypal::Payment::Request.new(
- :currency_code => 'USD', # if nil, PayPal use USD as default
- :description => 'Conference Registration', # item description
- :quantity => 1, # item quantity
- :amount => amount.to_f, # item value
- :custom_fields => {
- CARTBORDERCOLOR: "00ADEF",
- LOGOIMG: "https://en.bikebike.org/assets/bblogo-paypal.png"
- }
- )
- end
+ def list
+ @page_title = 'articles.conferences.headings.Conference_List'
+ @conference_list = { future: [], passed: [] }
+ Conference.all.order("start_date DESC").each do | conference |
+ if conference.is_public || conference.host?(current_user)
+ @conference_list[conference.over? ? :passed : :future] << conference
+ end
+ end
+ @conference_list[:future].reverse!
+ end
+
+ def view
+ set_conference
+ @workshops = Workshop.where(:conference_id => @conference.id)
+
+ if @this_conference.workshop_schedule_published
+ @event_dlg = true
+ get_scheule_data(false)
+ end
+
+ if logged_in?
+ if current_user.administrator?
+ @links ||= []
+ @links = [:edit]
+ end
+
+ if @this_conference.host? current_user
+ @links ||= []
+ @links = [:administrate]
+ end
+ end
+ end
+
+ def register
+ set_conference
+
+ @register_template = nil
+
+ if logged_in?
+ set_or_create_conference_registration
+
+ @name = current_user.firstname
+ # we should phase out last names
+ @name += " #{current_user.lastname}" if current_user.lastname
+
+ @name ||= current_user.username
+
+ @is_host = @this_conference.host? current_user
+ else
+ @register_template = :confirm_email
+ end
+
+ steps = nil
+ return do_404 unless registration_steps.present?
+
+ @register_template = :administration if params[:admin_step].present?
+
+ @errors = {}
+ @warnings = []
+ form_step = params[:button] ? params[:button].to_sym : nil
+
+ # process any data that was passed to us
+ if form_step
+ if form_step.to_s =~ /^prev_(.+)$/
+ steps = registration_steps
+ @register_template = steps[steps.find_index($1.to_sym) - 1]
+ elsif form_step == :paypal_confirm
+ if @registration.present? && @registration.payment_confirmation_token == params[:confirmation_token]
+ @amount = PayPal!.details(params[:token]).amount.total
+ @registration.payment_info = {:payer_id => params[:PayerID], :token => params[:token], :amount => @amount}.to_yaml
+
+ @amount = (@amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2')
+
+ @registration.save!
+ end
+
+ @page_title = 'articles.conference_registration.headings.Payment'
+ @register_template = :paypal_confirm
+ elsif form_step == :paypal_confirmed
+ info = YAML.load(@registration.payment_info)
+ @amount = nil
+ status = nil
+ if ENV['RAILS_ENV'] == 'test'
+ status = info[:status]
+ @amount = info[:amount]
+ else
+ paypal = PayPal!.checkout!(info[:token], info[:payer_id], PayPalRequest(info[:amount]))
+ status = paypal.payment_info.first.payment_status
+ @amount = paypal.payment_info.first.amount.total
+ end
+ if status == 'Completed'
+ @registration.registration_fees_paid ||= 0
+ @registration.registration_fees_paid += @amount
+
+ # don't complete the step unless fees have been paid
+ if @registration.registration_fees_paid > 0
+ @registration.steps_completed << :payment
+ @registration.steps_completed.uniq!
+ end
+
+ @registration.save!
+ else
+ @errors[:payment] = :incomplete
+ @register_template = :payment
+ end
+ @page_title = 'articles.conference_registration.headings.Payment'
+ else
+
+ case form_step
+ when :confirm_email
+ return do_confirm
+ when :contact_info
+ if params[:name].present? && params[:name].gsub(/[\s\W]/, '').present?
+ current_user.firstname = params[:name].squish
+ current_user.lastname = nil
+ else
+ @errors[:name] = :empty
+ end
+
+ if params[:location].present? && params[:location].gsub(/[\s\W]/, '').present? && (l = Geocoder.search(params[:location], language: 'en')).present?
+ corrected = view_context.location(l.first, @this_conference.locale)
+
+ if corrected.present?
+ @registration.city = corrected
+ if params[:location].gsub(/[\s,]/, '').downcase != @registration.city.gsub(/[\s,]/, '').downcase
+ @warnings << view_context._('warnings.messages.location_corrected', vars: {original: params[:location], corrected: corrected})
+ end
+ else
+ @errors[:location] = :unknown
+ end
+ else
+ @errors[:location] = :empty
+ end
+
+ if params[:languages].present?
+ current_user.languages = params[:languages].keys
+ else
+ @errors[:languages] = :empty
+ end
+
+ current_user.save! unless @errors.present?
+ when :hosting
+ @registration.can_provide_housing = params[:can_provide_housing].present?
+ if params[:not_attending]
+ @registration.is_attending = 'n'
+
+ if current_user.is_subscribed.nil?
+ current_user.is_subscribed = false
+ current_user.save!
+ end
+ else
+ @registration.is_attending = 'y'
+ end
+
+ @registration.housing_data = {
+ address: params[:address],
+ phone: params[:phone],
+ space: {
+ bed_space: params[:bed_space],
+ floor_space: params[:floor_space],
+ tent_space: params[:tent_space],
+ },
+ considerations: (params[:considerations] || {}).keys,
+ availability: [ params[:first_day], params[:last_day] ],
+ notes: params[:notes]
+ }
+ when :questions
+ # create the companion's user account and send a registration link unless they have already registered
+ generate_confirmation(User.create(email: params[:companion]), register_path(@this_conference.slug)) if params[:companion].present? && User.find_by_email(params[:companion]).nil?
+
+ @registration.housing = params[:housing]
+ @registration.arrival = params[:arrival]
+ @registration.departure = params[:departure]
+ @registration.housing_data = {
+ companions: [ params[:companion] ]
+ }
+ @registration.bike = params[:bike]
+ @registration.food = params[:food]
+ @registration.allergies = params[:allergies]
+ @registration.other = params[:other]
+ when :payment
+ amount = params[:amount].to_f
+
+ if amount > 0
+ @registration.payment_confirmation_token = ENV['RAILS_ENV'] == 'test' ? 'token' : Digest::SHA256.hexdigest(rand(Time.now.to_f * 1000000).to_i.to_s)
+ @registration.save
+
+ host = "#{request.protocol}#{request.host_with_port}"
+ response = PayPal!.setup(
+ PayPalRequest(amount),
+ register_paypal_confirm_url(@this_conference.slug, :paypal_confirm, @registration.payment_confirmation_token),
+ register_paypal_confirm_url(@this_conference.slug, :paypal_cancel, @registration.payment_confirmation_token),
+ noshipping: true,
+ version: 204
+ )
+ if ENV['RAILS_ENV'] != 'test'
+ redirect_to response.redirect_uri
+ end
+ return
+ end
+ end
+
+ if @errors.present?
+ @register_template = form_step
+ else
+ unless @registration.nil?
+ steps = registration_steps
+ step_index = steps.find_index(form_step)
+ @register_template = steps[step_index + 1] if step_index.present?
+
+ # have we reached a new level?
+ unless @registration.steps_completed.include? form_step.to_s
+ # this step is only completed if a payment has been made
+ if form_step != :payment || (@registration.registration_fees_paid || 0) > 0
+ @registration.steps_completed ||= []
+ @registration.steps_completed << form_step
+ @registration.steps_completed.uniq!
+ end
+ end
+
+ @registration.save!
+ end
+ end
+ end
+ end
+
+ steps ||= registration_steps
+
+ # make sure we're on a valid step
+ @register_template ||= (params[:step] || current_step).to_sym
+
+ if logged_in? && @register_template != :paypal_confirm
+ # if we're logged in
+ if !steps.include?(@register_template)
+ # and we are not viewing a valid step
+ return redirect_to register_path(@this_conference.slug)
+ elsif @register_template != current_step && !registration_complete? && !@registration.steps_completed.include?(@register_template.to_s)
+ # or the step hasn't been reached, registration is not yet complete, and we're not viewing the latest incomplete step
+ return redirect_to register_path(@this_conference.slug)
+ end
+ # then we'll redirect to the current registration step
+ end
+
+ # prepare the form
+ case @register_template
+ when :questions
+ # see if someone else has asked to be your companion
+ if @registration.housing_data.blank?
+ ConferenceRegistration.where(
+ conference_id: @this_conference.id, can_provide_housing: [nil, false]
+ ).where.not(housing_data: nil).each do | r |
+ @registration.housing_data = {
+ companions: [ r.user.email ]
+ } if r.housing_data['companions'].present? && r.housing_data['companions'].include?(current_user.email)
+ end
+
+ @registration.housing_data ||= { }
+ end
+ @page_title = 'articles.conference_registration.headings.Registration_Info'
+ when :payment
+ @page_title = 'articles.conference_registration.headings.Payment'
+ when :workshops
+ @page_title = 'articles.conference_registration.headings.Workshops'
+
+ # initialize our arrays
+ @my_workshops = Array.new
+ @requested_workshops = Array.new
+ @workshops_in_need = Array.new
+ @workshops = Array.new
+
+ # put wach workshop into the correct array
+ Workshop.where(conference_id: @this_conference.id).each do | workshop |
+ if workshop.active_facilitator?(current_user)
+ @my_workshops << workshop
+ elsif workshop.requested_collaborator?(current_user)
+ @requested_workshops << workshop
+ elsif workshop.needs_facilitators
+ @workshops_in_need << workshop
+ else
+ @workshops << workshop
+ end
+ end
+
+ # sort the arrays by name
+ @my_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
+ @requested_workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
+ @workshops_in_need.sort! { |a, b| a.title.downcase <=> b.title.downcase }
+ @workshops.sort! { |a, b| a.title.downcase <=> b.title.downcase }
+ when :contact_info
+ @page_title = 'articles.conference_registration.headings.Contact_Info'
+ when :hosting
+ @page_title = 'articles.conference_registration.headings.Hosting'
+ @hosting_data = @registration.housing_data || {}
+ @hosting_data['space'] ||= Hash.new
+ @hosting_data['availability'] ||= Array.new
+ @hosting_data['considerations'] ||= Array.new
+ when :policy
+ @page_title = 'articles.conference_registration.headings.Policy_Agreement'
+ when :confirm_email
+ @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
+ end
+
+ end
+
+ helper_method :registration_steps
+ helper_method :current_registration_steps
+ helper_method :registration_complete?
+
+ def registration_steps(conference = nil)
+ conference ||= @this_conference || @conference
+ status = conference.registration_status
+
+ steps = status == :pre || status == :open ? [
+ :policy,
+ :contact_info,
+ :questions,
+ :hosting,
+ :payment,
+ :workshops
+ ] : []
+
+ steps -= [:questions] unless status == :open
+ steps -= [:payment] unless status == :open && conference.paypal_email_address.present? && conference.paypal_username.present? && conference.paypal_password.present? && conference.paypal_signature.present?
+ if @registration.present?
+ if view_context.same_city?(@registration.city, view_context.location(conference.location, conference.locale))
+ steps -= [:questions]
+
+ # if this is a housing provider that is not attending the conference, remove these steps
+ if @registration.is_attending == 'n'
+ steps -= [:payment, :workshops]
+ end
+ else
+ steps -= [:hosting]
+ end
+ else
+ steps -= [:hosting, :questions]
+ end
+
+ steps += [:administration] if conference.host?(current_user)
+
+ return steps
+ end
+
+ def required_steps(conference = nil)
+ # return the intersection of current steps and required steps
+ registration_steps(conference || @this_conference || @conference) & # current steps
+ [:policy, :contact_info, :hosting, :questions] # all required steps
+ end
+
+ def registration_complete?(registration = @registration)
+ completed_steps = registration.steps_completed || []
+ required_steps(registration.conference).each do | step |
+ return true if step == :workshops
+ return false unless completed_steps.include?(step.to_s)
+ end
+ return true
+ end
+
+ def current_registration_steps(registration = @registration)
+ return nil unless registration.present?
+
+ steps = registration_steps(registration.conference)
+ current_steps = []
+ disable_steps = false
+ completed_steps = registration.steps_completed || []
+ registration_complete = registration_complete?(registration)
+ steps.each do | step |
+ # disable the step if we've already found an incomplete step
+ enabled = !disable_steps || registration_complete
+ # record whether or not we've found an incomplete step
+ disable_steps ||= !completed_steps.include?(step.to_s)
+
+ current_steps << {
+ name: step,
+ enabled: enabled
+ }
+ end
+ return current_steps
+ end
+
+ def current_step(registration = @registration)
+ completed_steps = registration.steps_completed || []
+ (registration_steps(registration.conference) || []).each do | step |
+ return step unless completed_steps.include?(step.to_s)
+ end
+ return registration_steps(registration.conference).last
+ end
+
+ rescue_from ActiveRecord::PremissionDenied do |exception|
+ if logged_in?
+ redirect_to :register
+ else
+ @register_template = :confirm_email
+ @page_title = "articles.conference_registration.headings.#{@this_conference.registration_status == :open ? '': 'Pre_'}Registration_Details"
+ render :register
+ end
+ end
+
+ private
+
+ def PayPal!
+ Paypal::Express::Request.new(
+ username: @this_conference.paypal_username,
+ password: @this_conference.paypal_password,
+ signature: @this_conference.paypal_signature
+ )
+ end
+
+ def PayPalRequest(amount)
+ Paypal::Payment::Request.new(
+ :currency_code => 'USD', # if nil, PayPal use USD as default
+ :description => 'Conference Registration', # item description
+ :quantity => 1, # item quantity
+ :amount => amount.to_f, # item value
+ :custom_fields => {
+ CARTBORDERCOLOR: "00ADEF",
+ LOGOIMG: "https://en.bikebike.org/assets/bblogo-paypal.png"
+ }
+ )
+ end
end
diff --git a/app/controllers/workshops_controller.rb b/app/controllers/workshops_controller.rb
new file mode 100644
index 0000000..f678c76
--- /dev/null
+++ b/app/controllers/workshops_controller.rb
@@ -0,0 +1,355 @@
+
+class WorkshopsController < ApplicationController
+ def workshops
+ set_conference
+ set_conference_registration!
+ @workshops = Workshop.where(:conference_id => @this_conference.id)
+ @my_workshops = Workshop.joins(:workshop_facilitators).where(:workshop_facilitators => {:user_id => current_user.id}, :conference_id => @this_conference.id)
+ render 'workshops/index'
+ end
+
+ def view_workshop
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless @workshop
+
+ @translations_available_for_editing = []
+ I18n.backend.enabled_locales.each do |locale|
+ @translations_available_for_editing << locale if @workshop.can_translate?(current_user, locale)
+ end
+ @page_title = 'page_titles.conferences.View_Workshop'
+ @register_template = :workshops
+
+ render 'workshops/show'
+ end
+
+ def create_workshop
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.new
+ @languages = [I18n.locale.to_sym]
+ @needs = []
+ @page_title = 'page_titles.conferences.Create_Workshop'
+ @register_template = :workshops
+ render 'workshops/new'
+ end
+
+ def translate_workshop
+ @is_translating = true
+ @translation = params[:locale]
+ @page_title = 'page_titles.conferences.Translate_Workshop'
+ @page_title_vars = { language: view_context.language_name(@translation) }
+ @register_template = :workshops
+
+ edit_workshop
+ end
+
+ def edit_workshop
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+
+ return do_404 unless @workshop.present?
+
+ @page_title ||= 'page_titles.conferences.Edit_Workshop'
+
+ @can_edit = @workshop.can_edit?(current_user)
+
+ @is_translating ||= false
+ if @is_translating
+ return do_404 if @translation.to_s == @workshop.locale.to_s || !I18n.backend.enabled_locales.include?(@translation.to_s)
+ return do_403 unless @workshop.can_translate?(current_user, @translation)
+
+ @title = @workshop._title(@translation)
+ @info = @workshop._info(@translation)
+ else
+ return do_403 unless @can_edit
+
+ @title = @workshop.title
+ @info = @workshop.info
+ end
+
+ @needs = JSON.parse(@workshop.needs || '[]').map &:to_sym
+ @languages = JSON.parse(@workshop.languages || '[]').map &:to_sym
+ @space = @workshop.space.to_sym if @workshop.space
+ @theme = @workshop.theme.to_sym if @workshop.theme
+ @notes = @workshop.notes
+ @register_template = :workshops
+
+ render 'workshops/new'
+ end
+
+ def delete_workshop
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+
+ return do_404 unless @workshop.present?
+ return do_403 unless @workshop.can_delete?(current_user)
+
+ if request.post?
+ if params[:button] == 'confirm'
+ if @workshop
+ @workshop.workshop_facilitators.destroy_all
+ @workshop.destroy
+ end
+
+ return redirect_to register_step_path(@this_conference.slug, 'workshops')
+ end
+ return redirect_to view_workshop_url(@this_conference.slug, @workshop.id)
+ end
+ @register_template = :workshops
+
+ render 'workshops/delete'
+ end
+
+ def save_workshop
+ set_conference
+ set_conference_registration!
+
+ if params[:button].to_sym != :save
+ if params[:workshop_id].present?
+ return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id])
+ end
+ return redirect_to register_step_path(@this_conference.slug, 'workshops')
+ end
+
+ if params[:workshop_id].present?
+ workshop = Workshop.find(params[:workshop_id])
+ return do_404 unless workshop.present?
+ can_edit = workshop.can_edit?(current_user)
+ else
+ workshop = Workshop.new(:conference_id => @this_conference.id)
+ workshop.workshop_facilitators = [WorkshopFacilitator.new(:user_id => current_user.id, :role => :creator)]
+ can_edit = true
+ end
+
+ title = params[:title]
+ info = params[:info].gsub(/^\s*(.*?)\s*$/, '\1')
+
+ if params[:translation].present? && workshop.can_translate?(current_user, params[:translation])
+ old_title = workshop._title(params[:translation])
+ old_info = workshop._info(params[:translation])
+
+ do_save = false
+
+ unless title == old_title
+ workshop.set_column_for_locale(:title, params[:translation], title, current_user.id)
+ do_save = true
+ end
+ unless info == old_info
+ workshop.set_column_for_locale(:info, params[:translation], info, current_user.id)
+ do_save = true
+ end
+
+ # only save if the text has changed, if we want to make sure only to update the translator id if necessary
+ workshop.save_translations if do_save
+ elsif can_edit
+ workshop.title = title
+ workshop.info = info
+ workshop.languages = (params[:languages] || {}).keys.to_json
+ workshop.needs = (params[:needs] || {}).keys.to_json
+ workshop.theme = params[:theme] == 'other' ? params[:other_theme] : params[:theme]
+ workshop.space = params[:space]
+ workshop.notes = params[:notes]
+ workshop.needs_facilitators = params[:needs_facilitators].present?
+ workshop.save
+
+ # Rouge nil facilitators have been know to be created, just destroy them here now
+ WorkshopFacilitator.where(:user_id => nil).destroy_all
+ else
+ return do_403
+ end
+
+ redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+
+ def toggle_workshop_interest
+ set_conference
+ set_conference_registration!
+ workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless workshop
+
+ # save the current state
+ interested = workshop.interested? current_user
+ # remove all associated fields
+ WorkshopInterest.delete_all(:workshop_id => workshop.id, :user_id => current_user.id)
+
+ # creat the new interest row if we weren't interested before
+ WorkshopInterest.create(:workshop_id => workshop.id, :user_id => current_user.id) unless interested
+
+ if request.xhr?
+ render json: [
+ {
+ selector: '.interest-button',
+ html: view_context.interest_button(workshop)
+ },
+ {
+ selector: '.interest-text',
+ html: view_context.interest_text(workshop)
+ }
+ ]
+ else
+ # go back to the workshop
+ redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+ end
+
+ def facilitate_workshop
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless @workshop
+ return do_403 if @workshop.facilitator?(current_user) || !current_user
+
+ @register_template = :workshops
+ render 'workshops/facilitate'
+ end
+
+ def facilitate_request
+ set_conference
+ set_conference_registration!
+ workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless workshop
+ return do_403 if workshop.facilitator?(current_user) || !current_user
+
+ # create the request by making the user a facilitator but making their role 'requested'
+ WorkshopFacilitator.create(user_id: current_user.id, workshop_id: workshop.id, role: :requested)
+
+ UserMailer.send_mail :workshop_facilitator_request do
+ {
+ :args => [ workshop, current_user, params[:message] ]
+ }
+ end
+
+ redirect_to sent_facilitate_workshop_url(@this_conference.slug, workshop.id)
+ end
+
+ def sent_facilitate_request
+ set_conference
+ set_conference_registration!
+ @workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless @workshop
+ return do_403 unless @workshop.requested_collaborator?(current_user)
+
+ @register_template = :workshops
+ render 'workshops/facilitate_request_sent'
+ end
+
+ def approve_facilitate_request
+ return do_403 unless logged_in?
+ set_conference
+ set_conference_registration!
+ workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+ return do_404 unless workshop.present?
+
+ user_id = params[:user_id].to_i
+ action = params[:approve_or_deny].to_sym
+ user = User.find(user_id)
+ case action
+ when :approve
+ if workshop.active_facilitator?(current_user) && workshop.requested_collaborator?(User.find(user_id))
+ f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
+ workshop.id, user_id)
+ f.role = :collaborator
+ f.save
+ UserMailer.send_mail :workshop_facilitator_request_approved, user.locale do
+ [ workshop, user ]
+ end
+ return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+ when :deny
+ if workshop.active_facilitator?(current_user) && workshop.requested_collaborator?(User.find(user_id))
+ WorkshopFacilitator.delete_all(
+ :workshop_id => workshop.id,
+ :user_id => user_id)
+ UserMailer.send_mail :workshop_facilitator_request_denied, user.locale do
+ [ workshop, user ]
+ end
+ return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+ when :remove
+ if workshop.can_remove?(current_user, user)
+ WorkshopFacilitator.delete_all(
+ :workshop_id => workshop.id,
+ :user_id => user_id)
+ return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+ when :switch_ownership
+ if workshop.creator?(current_user)
+ f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
+ workshop.id, current_user.id)
+ f.role = :collaborator
+ f.save
+ f = WorkshopFacilitator.find_by_workshop_id_and_user_id(
+ workshop.id, user_id)
+ f.role = :creator
+ f.save
+ return redirect_to view_workshop_url(@this_conference.slug, workshop.id)
+ end
+ end
+
+ return do_403
+ end
+
+ def add_workshop_facilitator
+ set_conference
+ set_conference_registration!
+
+ user = User.find_by_email(params[:email])
+
+ # create the user if they don't exist and send them a link to register
+ unless user
+ user = User.create(email: params[:email])
+ generate_confirmation(user, register_path(@this_conference.slug))
+ end
+
+ workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+
+ return do_404 unless workshop && current_user
+
+ unless workshop.facilitator?(user)
+ WorkshopFacilitator.create(user_id: user.id, workshop_id: workshop.id, role: :collaborator)
+
+ UserMailer.send_mail :workshop_facilitator_request_approved, user.locale do
+ [ workshop, user ]
+ end
+ end
+
+ return redirect_to view_workshop_url(@this_conference.slug, params[:workshop_id])
+ end
+
+ def add_comment
+ set_conference
+ set_conference_registration!
+ workshop = Workshop.find_by_id_and_conference_id(params[:workshop_id], @this_conference.id)
+
+ return do_404 unless workshop && current_user
+
+ if params[:button] == 'reply'
+ comment = Comment.find_by!(id: params[:comment_id].to_i, model_type: :workshops, model_id: workshop.id)
+ new_comment = comment.add_comment(current_user, params[:reply])
+
+ unless comment.user.id == current_user.id
+ UserMailer.send_mail :workshop_comment, comment.user.locale do
+ [ workshop, new_comment, comment.user ]
+ end
+ end
+ elsif params[:button] = 'add_comment'
+ new_comment = workshop.add_comment(current_user, params[:comment])
+
+ workshop.active_facilitators.each do | u |
+ unless u.id == current_user.id
+ UserMailer.send_mail :workshop_comment, u.locale do
+ [ workshop, new_comment, u ]
+ end
+ end
+ end
+ else
+ return do_404
+ end
+
+ return redirect_to view_workshop_url(@this_conference.slug, workshop.id, anchor: "comment-#{new_comment.id}")
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index c6fc597..a069980 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -1,1977 +1,2209 @@
require 'redcarpet'
module ApplicationHelper
- include ScheduleHelper
-
- @@keyQueue = nil
- @@translationsOnThisPage = nil
- @@lastTranslation = nil
- @@allTranslations = nil
- @@no_banner = true
- @@banner_attribution_details = nil
- @@banner_image = nil
- @@has_content = true
- @@front_page = false
- @@body_class = nil
- @@test_location = nil
-
- def init_vars
- @@keyQueue = nil
- @@no_banner = true
- @@banner_attribution_details = nil
- @@banner_image = nil
- @@has_content = true
- @@front_page = false
- @@body_class = nil
- end
-
- def this_is_the_front_page
- @@front_page = true
- end
-
- def header_is_fixed
- @fixed_header = true
- end
-
- def is_header_fixed?
- @fixed_header ||= false
- end
-
- def is_this_the_front_page?
- return @@front_page
- end
-
- def header_classes
- classes = Array.new
- classes << 'fixed' if is_header_fixed?
- return classes
- end
-
- def ThereAreTranslationsOnThisPage?
- @@translationsOnThisPage
- end
-
- def get_all_translations
- @@allTranslations
- end
-
- def title(page_title)
- content_for(:title) { page_title.to_s }
- end
-
- def description(page_description)
- content_for(:description) { page_description.to_s }
- end
-
- def banner_image(banner_image, name: nil, id: nil, user_id: nil, src: nil)
- @@no_banner = false
- @@banner_image = banner_image
- if (name || id || user_id || src)
- @@banner_attribution_details = {:name => name, :id => id, :user_id => user_id, :src => src}
- end
- content_for(:banner_image) { banner_image.to_s }
- end
-
- def banner_attrs(banner_image)
- @@no_banner = false
- if banner_image.length > 0
- @@banner_image = banner_image
- return {style: 'background-image: url(' + banner_image + ');', class: 'has-image' }
- end
- {class: 'no-image'}
- end
-
- def has_banner?
- !@@no_banner
- end
-
- def has_content?
- @@has_content
- end
-
- def has_no_content
- @@has_content = false
- end
-
- def banner_title(banner_title)
- @@no_banner = false
- content_for(:banner) { ('
' + banner_title.to_s + ' ').html_safe }
- end
-
- def add_stylesheet(sheet)
- @stylesheets ||= []
- @stylesheets << sheet unless @stylesheets.include?(sheet)
- end
-
- def stylesheets
- html = ''
- Rack::MiniProfiler.step('inject_css') do
- html += inject_css!
- end
- (@stylesheets || []).each do |css|
- Rack::MiniProfiler.step("inject_css #{css}") do
- html += inject_css! css.to_s
- end
- end
- html += stylesheet_link_tag 'i18n-debug' if request.params['i18nDebug']
- return html.html_safe
- end
-
- def add_inline_script(script)
- @_inline_scripts ||= []
- script = Rails.application.assets.find_asset("#{script.to_s}.js").to_s
- @_inline_scripts << script unless @_inline_scripts.include?(script)
- end
-
- def inline_scripts
- return '' unless @_inline_scripts.present?
- "".html_safe
- end
-
- def banner_attribution
- if @@banner_image && @@banner_attribution_details
- src = @@banner_attribution_details[:src]
- attribution = ''
- attribution.html_safe
- end
- end
-
- def dom_ready(&block)
- content_for(:dom_ready, &block)
- end
-
- def body_class(c)
- @@body_class ||= Array.new
- @@body_class << (c.is_a?(Array) ? c.join(' ') : c)
- end
-
- def page_style
- classes = Array.new
-
- classes << 'has-translations' if ThereAreTranslationsOnThisPage?
- classes << 'no-content' unless @@has_content
- classes << 'has-banner-image' if @@banner_image
- classes << @@body_class.join(' ') if @@body_class
- classes << 'fixed-banner' if is_header_fixed?
-
- if params[:controller]
- classes << params[:action]
- unless params[:controller] == 'application'
- classes << params[:controller]
-
- if params[:action]
- classes << "#{params[:controller]}-#{params[:action]}"
- end
- end
- end
- return classes
- end
-
- def yield_or_default(section, default = '')
- content_for?(section) ? content_for(section) : default
- end
-
- def _translate_me(translation)
- @@translationsOnThisPage = true
- datakeys = ''
- translation['vars'].each { |key, value| datakeys += ' data-var-' + key.to_s + '="' + value.to_s.gsub('"', '"') + '"' }
- ('' + (translation['html'] || translation['untranslated']) + ' ').to_s.html_safe
- end
-
- def _do_translate(key, vars, behavior, behavior_size, locale)
- translation = {'key' => key, 'lang' => '0', 'vars' => vars}
- v = vars.dup
- begin
- v[:raise] = true
- options = {:raise => true}
- if locale
- options[:locale] = locale.to_sym
- end
- translation['untranslated'] = I18n.translate(key, v, options)
- translation['lang'] = locale.to_s
- translation['is_translated'] = true
-
- hash = Hash.new
- translations = Translation.where(["locale = ? AND key LIKE ?", locale.to_s, key + '%']).take(6).each { |o| hash[o.key] = o.value }
- translation['translated'] = hash.to_json.gsub('"', '"')
- rescue I18n::MissingTranslationData
- default_translation = I18n::MissingTranslationExceptionHandler.note(key, behavior, behavior_size)
- translation['untranslated'] = default_translation
- end
- return translation
- end
-
- def _can_translate?()
- false
- end
-
- def off_screen(text, id = nil)
- content_tag(:span, text.html_safe, id: id, class: 'screen-reader-text')
- end
-
- def url_for_locale(locale, url = nil)
- return url unless locale.present?
-
- unless url.present?
- new_params = params.merge({action: (params[:_original_action] || params[:action])})
- new_params.delete(:_original_action)
-
- if Rails.env.development? || Rails.env.test?
- return url_for(new_params.merge({lang: locale.to_s}))
- end
-
- subdomain = Rails.env.preview? ? "preview-#{locale.to_s}" : locale.to_s
- return url_for(new_params.merge(host: "#{subdomain}.bikebike.org"))
- end
-
- return url if Rails.env.development? || Rails.env.test?
- return "https://preview-#{locale.to_s}.bikebike.org#{url}" if Rails.env.preview?
- "https://#{locale.to_s}.bikebike.org#{url}"
- end
-
- def registration_steps(conference = @conference)
- {
- pre: [:policy, :basic_info, :workshops],
- open: [:policy, :basic_info, :questions, :payment, :workshops]
- }[@this_conference.registration_status]
- end
-
- def registration_status(registration)
- return :unregistered if registration.nil?
- return registration.status
- end
-
- def sortable(objects, id = 'id', url: nil, &block)
- result = ''
- objects.each_index do |i|
- @this = objects[i]
- result += ''
- result += hidden_field_tag (id + "[#{i}]"), objects[i][id]
- result += hidden_field_tag ('position' + "[#{i}]"), i, :class => 'sortable-position'
- if block_given?
- result += capture(objects[i], &block)
- end
- result += ' '
- end
- result += ' '
- result.html_safe
- end
-
- def tabs object, tabs
- type = object.class.name.downcase
- tab_list = ''
-
- tabs.each do |tab|
- link = nil
- if self.respond_to?(type + '_' + tab.to_s + '_path')
- link = self.send(type + '_' + tab.to_s + '_path', object)
- elsif self.respond_to?(tab.to_s + '_' + type + '_path')
- link = self.send(tab.to_s + '_' + type + '_path', object)
- end
-
- c = ['tab', 'tab-' + (link ? tab.to_s : 'show')]
- if params[:action] == tab.to_s
- c << 'current'
- end
- link_html = ''
- if tab.is_a?(Hash)
- func = tab.keys[0]
- val = tab[func]
- args = val ? (val.is_a?(Array) ? (val.collect { |v| object[v] } ) : [object[val]] ) : nil
-
- link_html = link_to func.to_s.gsub(/_path$/, ''), args ? self.send(func, args) : self.send(func), :class => c
- else
- #x
- #link_html = link_to tab, link || object, :class => c
- end
- tab_list += link_html
- end
- ('
- ' +
- tab_list +
- '
- ').html_safe
- end
-
- def tabs!
- object = nil
- tabs = nil
- case params[:controller]
- when 'organizations'
- object = @organization
- tabs = OrganizationsHelper::TABS
- when 'conferences'
- object = @conference
- tabs = ConferencesHelper::TABS
- when 'workshops'
- object = [@conference, @workshop]
- tabs = WorkshopsHelper::TABS
- end
-
- if object && tabs
- return tabs object, tabs
- end
- end
-
- def sub_tabs object, tabs
- type = object.class.name.downcase
- tab_list = ''
-
- tabs.each do |tab|
- link = nil
- if self.respond_to?(type + '_' + tab.to_s + '_path')
- link = self.send(type + '_' + tab.to_s + '_path', object)
- elsif self.respond_to?(tab.to_s + '_' + type + '_path')
- link = self.send(tab.to_s + '_' + type + '_path', object)
- end
-
- c = ['sub-tab', 'sub-tab-' + (link ? tab.to_s : 'show')]
- if current_page?(link)
- c << 'current'
- end
- tab_list += link_to tab, link || object, :class => c
- end
- ('' + tab_list + ' ').html_safe
- end
-
- def sub_tabs!
- object = nil
- tabs = nil
- case params[:controller]
- when 'organizations'
- object = @organization
- tabs = OrganizationsHelper::SUB_TABS
- when 'conferences'
- object = @conference
- tabs = ConferencesHelper::SUB_TABS
- end
-
- if object && tabs
- return sub_tabs object, tabs
- end
- end
-
- def m(*args)
- _(*args) { |t|
- markdown(t)
- }
- end
-
- def markdown(object, attribute = nil)
- return '' unless object
- content = attribute ? object.send(attribute.to_s) : object
- @markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML.new({
- filter_html: true,
- hard_wrap: true,
- space_after_headers: true,
- fenced_code_blocks: true,
- link_attributes: { target: "_blank" }
- }), {
- autolink: true,
- disable_indented_code_blocks: true,
- superscript: true
- })
- @markdown.render(content).html_safe
- end
-
- def paragraph(object, attribute = nil)
- return '' unless object
- content = attribute ? object.send(attribute.to_s) : object
- result = ''
- if content =~ /<(p|span|h\d|div)[^>]*>/
- result = content.gsub(/\s*(style|class|id|width|height|font)=\".*?\"/, '')
- .gsub(/ /, ' ')
- .gsub(/<(\/)?\s*h\d\s*>/, '<\1h3>')
- .gsub(/(.*?) \s*( )+/, '
\1
')
- .gsub(/]*>\s*(.*?)\s*<\/span>/, '\1')
- .gsub(/\s*<\/p>/, '')
- .gsub(/<(\/)?div>/, '<\1p>')
- if !(result =~ /
]*>/)
- result = '
' + result + '
'
- end
- else
- result = markdown(object, attribute)
- end
- result.html_safe
- end
-
- def form_field(f, response = nil)
- id = 'field_' + f.id.to_s
- html = p(f, 'title')
-
- options = JSON.parse(f.options)
- if f.field_type == 'multiple'
- if f.help
- html += ('' + p(f, 'help') + '
').html_safe
- end
-
- opts = Hash.new
- options['options'].split(/\s*\n\s*/).each do |value|
- kv = value.split(/\s*\|\s*/, 2)
- opts[kv[0]] = kv[1]
- end
-
- val = response ? ActiveSupport::JSON.decode(response.data) : Hash.new
-
- if f.repeats?
- is_array = f.is_array?
- opts.each do |key, value|
- n = (id + (is_array ? ('_' + key) : '')).to_sym
- v = is_array ? (val ? val[key] : nil) : key
- o = {:label => value}
- if f.required
- options[:required] = true
- end
- html += _form_field(options['selection_type'], n, v, o)
- end
- else
- html += _form_field(options['selection_type'], id.to_sym, options_for_select(opts.invert, val), {})
- end
- else
- #html += field(id.to_sym, options['input_type'] + '_tag', label: false, placeholder: f.help, value: response ? ActiveSupport::JSON.decode(response.data) : nil, required: f.required)
- opts = {label: false, placeholder: f.help && f.help.length > 0 ? f.help : false}
- if f.required
- opts[:required] = true
- end
- html += _form_field(options['input_type'], id.to_sym, response ? ActiveSupport::JSON.decode(response.data) : nil, opts)
- end
-
- html.html_safe
- end
-
- def t(*a)
- _(*a)
- end
-
- def lookup_ip
- if request.remote_ip == '127.0.0.1' || request.remote_ip == '::1'
- session['remote_ip'] || (session['remote_ip'] = open("http://checkip.dyndns.org").first.gsub(/^.*\s([\d\.]+).*$/s, '\1').gsub(/[^\.\d]/, ''))
- else
- request.remote_ip
- end
- end
-
- def get_remote_location
- Geocoder.search(session['remote_ip'] || (session['remote_ip'] = open("http://checkip.dyndns.org").first.gsub(/^.*\s([\d\.]+).*$/s, '\1').gsub(/[^\.\d]/, '')), language: 'en').first
- end
-
- def lookup_ip_location
- begin
- if is_test? && ApplicationController::get_location.present?
- Geocoder.search(ApplicationController::get_location, language: 'en').first
- elsif request.remote_ip == '127.0.0.1' || request.remote_ip == '::1'
- get_remote_location
- else
- request.location || get_remote_location
- end
- rescue
- nil
- end
- end
-
- def hash_to_html_attributes(hash, prefix = '')
- attributes = ''
- if hash
- hash.each { |k,v|
- k = k.to_s
- if v
- if v.is_a?(Hash)
- attributes += hash_to_html_attributes(v, 'data-')
- else
- attributes += " #{k}=\"" + (v.is_a?(Array) ? v.join(' ') : v) + '"'
- end
- end
- }
- end
- attributes
- end
-
- def icon(id, attributes = nil)
- (' ').html_safe
- end
-
- def static_map(location, zoom, width, height)
- require 'fileutils'
- local_file_name = "#{location}-#{width}x#{height}z#{zoom}.png"
- file = File.join("public", "maps/#{local_file_name}")
- FileUtils.mkdir_p("public/maps") unless File.directory?("public/maps")
- if !File.exist?(file)
- url = "https://maps.googleapis.com/maps/api/staticmap?center=#{location}&zoom=#{zoom}&size=#{width}x#{height}&maptype=roadmap&markers=size:small%7C#{location}&key=AIzaSyAH7U8xUUb8IwDPy1wWuYGprzxf4E1Jj4o"
- require 'open-uri'
- open(file, 'wb') do |f|
- f << open(url).read
- end
- end
-
- cdn("/maps/#{local_file_name}")
- end
-
- def cdn(file)
- (Rails.application.config.action_controller.asset_host || '') + file
- end
-
- def is_production?
- Rails.env == 'production' || Rails.env == 'preview'
- end
-
- def is_test?
- Rails.env == 'test'
- end
-
- def subdomain
- request.env['SERVER_NAME'].gsub(/^(\w+)\..*$/, '\1')
- end
-
- def is_test_server?
- subdomain == 'test'
- end
-
- def rand_hash(length = 16, model = nil, field = nil)
- if field
- hash = rand_hash(length)
- while !model.to_s.to_s.singularize.classify.constantize.find_by(field => hash).nil?
- hash = rand_hash(length)
- end
- end
- rand(36**length).to_s(36)
- end
-
- def get_panoramio_image(location)
- if is_test?
- params[:image] = 'panoramio.jpg'
- params[:attribution_id] = 1234
- params[:attribution_user_id] = 5678
- params[:attribution_name] = 'Some Guy'
- params[:attribution_src] = 'panoramio'
- return params
- end
-
- location = location.city + ', ' + (location.territory ? location.territory + ' ' : '') + location.country
- $panoramios ||= Hash.new
- $panoramios[location] ||= 0
- $panoramios[location] += 1
- result = Geocoder.search(location, language: 'en').first
- if result
- points = Geocoder::Calculations.bounding_box([result.latitude, result.longitude], 5, { :unit => :km })
- options = {:set => :public, :size => :original, :from => 0, :to => 20, :mapfilter => false, :miny => points[0], :minx => points[1], :maxy => points[2], :maxx => points[3]}
- url = 'http://www.panoramio.com/map/get_panoramas.php?' + options.to_query
- response = JSON.parse(open(url).read)
- response['photos'].each { |img|
- if img['width'].to_i > 980
- if Organization.find_by(:cover_attribution_id => img['photo_id'], :cover_attribution_src => 'panoramio').nil? && Conference.find_by(:cover_attribution_id => img['photo_id'], :cover_attribution_src => 'panoramio').nil?
- params[:image] = img['photo_file_url']
- params[:attribution_id] = img['photo_id']
- params[:attribution_user_id] = img['owner_id']
- params[:attribution_name] = img['owner_name']
- params[:attribution_src] = 'panoramio'
- return params
- end
- end
- }
- end
- return nil
- end
-
- def get_secure_info(name)
- YAML.load(File.read(Rails.root.join("config/#{name.to_s}.yml")))[Rails.env].symbolize_keys
- end
-
- def location(location, locale = I18n.locale)
- return nil if location.blank?
-
- city = nil
- region = nil
- country = nil
- if location.is_a?(Location)
- country = location.country
- region = location.territory
- city = location.city
- elsif location.data.present? && location.data['address_components'].present?
- component_map = {
- 'locality' => :city,
- 'administrative_area_level_1' => :region,
- 'country' => :country
- }
- location.data['address_components'].each do | component |
- types = component['types']
- country = component['short_name'] if types.include? 'country'
- region = component['short_name'] if types.include? 'administrative_area_level_1'
- city = component['long_name'] if types.include? 'locality'
- end
- else
- country = location.data['country_code']
- region = location.data['region_code']
- city = location.data['city']
- end
-
- # we need cities for our logic, don't let this continue if we don't have one
- return nil unless city.present?
-
- hash = Hash.new
- region_translation = region.present? && country.present? ? _("geography.subregions.#{country}.#{region}", locale: locale) : ''
- country_translation = country.present? ? _("geography.countries.#{country}", locale: locale) : ''
- hash[:city] = _!(city) if city.present?
- hash[:region] = region_translation if region_translation.present?
- hash[:country] = country_translation if country_translation.present?
-
- # return the formatted location or the first value if we only have one value
- return hash.length > 1 ? _("geography.formats.#{hash.keys.join('_')}", locale: locale, vars: hash) : hash.values.first
- end
-
- def location_link(location)
- return '' unless location.present? && location.address.present?
- content_tag(:a, (_!location.address), href: "http://www.google.com/maps/place/#{location.latitude},#{location.longitude}")
- end
-
- def same_city?(location1, location2)
- return false unless location1.present? && location2.present?
-
- location1 = location(location1) unless location1.is_a?(String)
- location2 = location(location2) unless location2.is_a?(String)
-
- location1.eql? location2
- end
-
- def show_errors(field, value)
- return '' unless @errors && @errors[field].present?
-
- error_txt = _"errors.messages.fields.#{field.to_s}.#{@errors[field]}", :s, vars: { value: value }
-
- "#{error_txt}
".html_safe
- end
-
- def nav_link(link, title = nil, class_name = nil)
- if title.nil? && link.is_a?(Symbol)
- title = link
- link = send("#{link.to_s}_path")
- end
- if class_name.nil? && title.is_a?(Symbol)
- class_name = title
- end
- title = _"page_titles.#{title.to_s.titlecase.gsub(/\s/, '_')}"
- classes = []
- classes << class_name if class_name.present?
- classes << "strlen-#{strip_tags(title).length}"
- classes << 'current' if request.fullpath.start_with?(link.gsub(/^(.*?)\/$/, '\1'))
- link_to "#{title} ".html_safe, link, :class => classes
- end
-
- def date(date, format = :long)
- I18n.l(date.is_a?(String) ? Date.parse(date) : date, :format => format)
- end
-
- def time(time, format = :short)
- if time.is_a?(String)
- time = Date.parse(time)
- elsif time.is_a?(Float) || time.is_a?(Integer)
- time = DateTime.now.midnight + time.hours
- end
-
- I18n.l(time, :format => format)
- end
-
- def date_span(date1, date2)
- key = 'same_month'
- if date1.year != date2.year
- key = 'different_year'
- elsif date1.month != date2.month
- key = 'same_year'
- end
- d1 = I18n.l(date1.to_date, format: "span_#{key}_date_1".to_sym)
- d2 = I18n.l(date2.to_date, format: "span_#{key}_date_2".to_sym)
- _('date.date_span', vars: {:date_1 => d1, :date_2 => d2})
- end
-
- def time_length(length)
- hours = length.to_i
- minutes = ((length - hours) * 60).to_i
- hours = hours > 0 ? (I18n.t 'datetime.distance_in_words.x_hours', count: hours) : nil
- minutes = minutes > 0 ? (I18n.t 'datetime.distance_in_words.x_minutes', count: minutes) : nil
- return hours.present? ? (minutes.present? ? (I18n.t 'datetime.distance_in_words.x_and_y', x: hours, y: minutes) : hours) : minutes
- end
-
- def hour_span(time1, time2)
- (time2 - time1) / 3600
- end
-
- def hours(time1, time2)
- time_length hour_span(time1, time2)
- end
-
- def generate_confirmation(user, url, expiry = nil)
- ApplicationController::generate_confirmation(user, url, expiry)
- end
-
- def money(amount)
- return _!('$0.00') if amount == 0
- _!((amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '$\1.\2'))
- end
-
- def percent(p)
- return _!('0.00%') if p == 0
- _!((p * 10000).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2%'))
- end
-
- def data_set(header_type, header_key, attributes = {}, &block)
- raw_data_set(header_type, _(header_key), attributes, &block)
- end
-
- def raw_data_set(header_type, header, attributes = {}, &block)
- attributes[:class] = attributes[:class].split(' ') if attributes[:class].is_a?(String)
- attributes[:class] = [attributes[:class].to_s] if attributes[:class].is_a?(Symbol)
- attributes[:class] ||= []
- attributes[:class] << 'data-set'
- content_tag(:div, attributes) do
- content_tag(header_type, header, class: 'data-set-key') +
- content_tag(:div, class: 'data-set-value', &block)
- end
- end
-
- def conference_days_options(conference = nil)
- conference ||= @this_conference || @conference
- return [] unless conference
-
- dates = []
- day = conference.start_date - 7.days
- last_day = conference.end_date + 7.days
-
- while day <= last_day
- dates << day
- day += 1.day
- end
-
- return dates
- end
-
- def conference_days_options_list(period, conference = nil, format = nil)
- conference ||= @this_conference || @conference
- return [] unless conference
-
- days = []
-
- conference_days_options(conference).each do |day|
- belongs_to_periods = []
- belongs_to_periods << :before if day <= conference.start_date
- belongs_to_periods << :after if day >= conference.end_date
- belongs_to_periods << :before_plus_one if day <= (conference.start_date + 1.day)
- belongs_to_periods << :after_minus_one if day >= (conference.end_date - 1.day)
- belongs_to_periods << :during if day >= conference.start_date && day <= conference.end_date
- days << [date(day.to_date, format || :span_same_year_date_1), day.to_date] if belongs_to_periods.include?(period)
- end
- return days
- end
-
- def registration_status_options_list(conference = nil)
- conference ||= @this_conference || @conference
- return [] unless conference
-
- options = Array.new
- [:closed, :pre, :open].each do | opt |
- options << [(_"forms.labels.generic.registration_statuses.#{opt}"), opt]
- end
-
- return options
- end
-
- def day_select(value = nil, args = {})
- selectfield :day, value, conference_days_options_list(:during, nil, args[:format]), args
- end
-
- def hour_select(value = nil, args = {}, start_time = 8, end_time = 23.5, step = 0.5)
- time = start_time
- times = []
- while time <= end_time
- times << [time(DateTime.now.midnight + time.hours), time]
- time += step
- end
- selectfield :time, value, times, args
- end
-
- def length_select(value = nil, args = {}, min_length = 0.5, max_length = 6, step = 0.5)
- length = min_length
- lengths = []
- while length <= max_length
- lengths << [time_length(length), length]
- length += step
- end
- selectfield :time_span, value, lengths, args
- end
-
- def contact_reason_select
- reasons = []
- [:website, :conference].each do | reason |
- reasons << [ _("forms.labels.generic.reasons.#{reason.to_s}"), reason ]
- end
- [['Something about the website', :website]]
- selectfield :reason, nil, reasons, required: true, heading: 'articles.contact.headings.reason', label: false, full: true
- end
-
- def block_select(value = nil, args = {})
- blocks = {}
- @workshop_blocks.each_with_index do | info, block |
- info['days'].each do | day |
- blocks[(day.to_i * 10) + block] = [ "#{(I18n.t 'date.day_names')[day.to_i]} Block #{block + 1}", "#{day}:#{block}" ]
- end
- end
- selectfield :workshop_block, value, blocks.sort.to_h.values, args
- end
-
- def location_select(value = nil, args = {})
- locations = []
- if @this_conference.event_locations.present?
- @this_conference.event_locations.each do | location |
- locations << [ location.title, location.id ] unless ((args[:invalid_locations] || []).include? location.id)
- end
- end
- selectfield :event_location, value, locations, args
- end
-
- def location_name(id)
- begin
- location = EventLocation.find(id)
- rescue
- return ''
- end
- return '' unless location.present?
- return location.title
- end
-
- def host_options_list(hosts)
- options = [[nil, nil]]
- hosts.each do | id, registration |
- options << [registration.user.name, id]
- end
- return options
- end
-
- def registration_step_menu
- steps = current_registration_steps(@registration)
- return '' unless steps.present? && steps.length > 1
-
- pre_registration_steps = ''
- post_registration_steps = ''
- post_registration = false
-
- steps.each do | step |
- text = _"articles.conference_registration.headings.#{step[:name].to_s}"
-
- if step[:name] == :workshops
- post_registration = true
- end
-
- h = content_tag :li, class: [step[:enabled] ? :enabled : nil, @register_template == step[:name] ? :current : nil, post_registration ? :post : :pre].compact do
- if step[:enabled]
- content_tag :div, (link_to text, register_step_path(@this_conference.slug, step[:name])).html_safe, class: :step
- else
- content_tag :div, text, class: :step
- end
- end
-
- if post_registration
- post_registration_steps += h.html_safe
- else
- pre_registration_steps += h.html_safe
- end
- end
-
- html = (
- row class: 'flow-steps' do
- columns do
- (content_tag :ul, id: 'registration-steps' do
- pre_registration_steps.html_safe +
- post_registration_steps.html_safe
- end).html_safe
- end
- end
- )
-
- return html.html_safe
- end
-
- def broadcast_to(to, conference = nil)
- conference ||= @this_conference || @conference
-
- users = []
-
- case to.to_sym
- when :registered
- ConferenceRegistration.where(conference_id: conference.id).each do | r |
- users << r.user if ((r.steps_completed || []).include? 'questions') && r.user.present? && r.is_attending != 'n'
- end
- when :pre_registered
- ConferenceRegistration.where(conference_id: conference.id).each do | r |
- users << r.user if registration_status(r) == :preregistered && r.is_attending != 'n'
- end
- when :workshop_facilitators
- user_hash = {}
- Workshop.where(conference_id: conference.id).each do | w |
- w.active_facilitators.each do | u |
- user_hash[u.id] ||= u if u.present?
- end
- end
- users = user_hash.values
- when :unregistered
- ConferenceRegistration.where(conference_id: conference.id).each do | r |
- users << r.user if registration_status(r) == :unregistered && r.is_attending != 'n'
- end
- when :housing_providers
- ConferenceRegistration.where(conference_id: conference.id, can_provide_housing: true).each do | r |
- users << r.user if r.user.present?
- end
- when :guests
- ConferenceRegistration.where(conference_id: conference.id, housing: 'house').each do | r |
- users << r.user if r.user.present? && r.is_attending != 'n'
- end
- when :all
- User.all.each do | u |
- users << u if u.present? && (u.is_subscribed.nil? || u.is_subscribed)
- end
- end
-
- puts "Users:"
- users.each do | u |
- puts "\t#{u.id}\t#{u.email}"
- end
- return users
- end
-
- def broadcast_options(conference = nil)
- conference ||= @this_conference || @conference
-
- options = [
- :registered,
- :pre_registered,
- :workshop_facilitators,
- :unregistered,
- :housing_providers,
- :guests,
- :all
- ]
-
- if conference.registration_status != :open
- options -= [:registered, :guests]
- options -= [:pre_registered] unless conference.registration_status != :pre
- end
-
- return options
- end
-
- def admin_steps
- [:stats, :edit, :payment, :broadcast, :housing, :locations, :meals, :events, :workshop_times, :schedule]
- end
-
- def valid_admin_steps
- admin_steps + [:broadcast_sent, :organizations]
- end
-
- def admin_menu
- steps = ''
- first = true
- admin_steps.each do | step |
- steps += content_tag(:li, class: (step.to_s == @admin_step ? :current : nil)) do
- link_to _("menu.submenu.admin.#{step.to_s.titlecase.gsub(/\s/, '_')}"), first ?
- register_step_path(@this_conference.slug, :administration) :
- administration_step_path(@this_conference.slug, step.to_s)
- end
- first = false
- end
- content_tag :ul, steps.html_safe, id: 'registration-admin-menu'
- end
-
- def interest_button(workshop)
- interested = workshop.interested?(current_user) ? :remove_interest : :show_interest
- id = "#{interested.to_s.gsub('_', '-')}-#{workshop.id}"
- return (off_screen (_"forms.actions.aria.#{interested.to_s}"), id) +
- (button_tag interested, :value => :toggle_interest, :class => (workshop.interested?(current_user) ? :delete : :add), aria: { labelledby: id })
- end
-
- def interest_text(workshop)
- if workshop.interested?(current_user)
- return _'articles.workshops.info.you_are_interested_count', :vars => {:count => (workshop.interested_count - 1)}
- end
-
- return _'articles.workshops.info.interested_count', :vars => {:count => workshop.interested_count}
- end
-
- def host_guests_table(registration)
- id = registration.id
- html = ''
-
- @housing_data[id][:guests].each do | area, guests |
- guest_rows = ''
- guests.each do | guest_id, guest |
- status_html = ''
-
- @housing_data[id][:guest_data][guest_id][:errors].each do | error, value |
- if value.is_a?(Array)
- value.each do | v |
- status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: v))
- end
- else
- status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: value))
- end
- end
-
- @housing_data[id][:guest_data][guest_id][:warnings].each do | error, value |
- if value.is_a?(Array)
- value.each do | v |
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", v))
- end
- else
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", vars: value))
- end
- end
-
- if status_html.present?
- status_html = content_tag(:ul, status_html.html_safe)
- end
-
- guest_rows += content_tag :tr, id: "hosted-guest-#{guest_id}" do
- (content_tag :td, guest[:guest].user.name) +
- (content_tag :td do
- (guest[:guest].city +
- (content_tag :a, (_'actions.workshops.Remove'), href: '#', class: 'remove-guest', data: { guest: guest_id })).html_safe
- end) +
- (content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
- end
- end
-
- space_size = (@housing_data[id][:space][area] || 0)
-
- # add empty rows to represent empty guest spots
- for i in guests.size...space_size
- guest_rows += content_tag :tr, class: 'empty-space' do
- (content_tag :td, ' '.html_safe, colspan: 2) +
- (content_tag :td)
- end
- end
-
- status_html = ''
- if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
- @housing_data[id][:warnings][:space][area].each do | w |
- status_html += content_tag(:li, _("warnings.messages.housing.space.#{w.to_s}"))
- end
- end
- if status_html.present?
- status_html = content_tag(:ul, status_html.html_safe)
- end
-
- html += content_tag :tr do
- (content_tag :th, (_"forms.labels.generic.#{area}"), colspan: 2) +
- (content_tag :th, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
- end
- html += guest_rows
- html += content_tag :tr, class: 'place-guest' do
- content_tag :td, class: guests.size >= space_size ? 'full' : nil, colspan: 3 do
- content_tag :a, (_'forms.actions.generic.place_guest'), class: 'select-guest', href: '#', data: { host: id, space: area }
- end
- end
- end
-
- content_tag :table, html.html_safe, class: 'host-table'
- end
-
- def get_housing_match(host, guest, space)
- housing_data = guest.housing_data || []
-
- if housing_data['host'].present?
- if housing_data['host'] == host.id
- return space == housing_data['space'] ? :selected_space : :other_space
- end
-
- return :other_host
- end
-
- if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
- return :good_match
- end
-
- return :bad_match
- end
-
- def get_workshop_match(workshop, day, block, location)
- if workshop.event_location_id.present? && workshop.present?
- if (Date.parse params[:day]).wday == workshop.block['day'] && block == workshop.block['block'].to_i
- return :selected_space
- end
-
- if location.present? && location.id == workshop.event_location_id
- return :other_space
- end
-
- return :other_host
- end
-
- if location.present?
- needs = JSON.parse(workshop.needs || '[]').map &:to_sym
- amenities = JSON.parse(location.amenities || '[]').map &:to_sym
-
- if (needs & amenities).length < needs.length
- return :bad_match
- end
- end
-
- (((((@schedule[@day] || {})[:times] || {})[@time] || {})[:item] || {})[:workshops] || {}).each do | l, w |
- if w[:workshop].id != workshop.id
- f_a = w[:workshop].active_facilitators.map { | f | f.id }
- f_b = workshop.active_facilitators.map { | f | f.id }
- if (f_a & f_b).present?
- return :bad_match
- end
- end
- end
-
- # housing_data = guest.housing_data || []
-
- # if housing_data['host'].present?
- # if housing_data['host'] == host.id
- # return space == housing_data['space'] ? :selected_space : :other_space
- # end
-
- # return :other_host
- # end
-
- # if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
- # return :good_match
- # end
-
- # return :bad_match
- return :good_match
- end
-
- def space_matches?(host_space, guest_space)
- return false unless host_space.present? && guest_space.present?
-
- if host_space.to_s == 'bed_space' || host_space.to_s == 'floor_space'
- return guest_space.to_s == 'house'
- end
-
- return host_space.to_s == 'tent_space' && guest_space.to_s == 'tent'
- end
-
- def available_dates_match?(host, guest)
- return false unless host.housing_data['availability'].present? && host.housing_data['availability'][1].present?
- if host.housing_data['availability'][0] <= guest.arrival &&
- host.housing_data['availability'][1] >= guest.departure
- return true
- end
-
- return false
- end
-
- def host_guests_widget(registration)
- html = ''
- classes = ['host']
-
- id = registration.id
- @housing_data[id][:guests].each do | area, guests |
- max_space = @housing_data[id][:space][area] || 0
- area_name = (_"forms.labels.generic.#{area}")
- status_html = ''
- if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
- @housing_data[id][:warnings][:space][area].each do | w |
- status_html += content_tag(:div, _("warnings.housing.space.#{w.to_s}"), class: 'warning')
- end
- end
- space_html = content_tag(:h5, area_name + _!(" (#{guests.size.to_s}/#{max_space.to_s})") + status_html.html_safe)
- guest_items = ''
- guests.each do | guest_id, guest |
- guest_items += content_tag(:li, guest[:guest].user.name, id: "hosted-guest-#{guest_id}")
- end
- space_html += content_tag(:ul, guest_items.html_safe)
- space_html += button_tag :place_guest, type: :button, value: "#{area}:#{id}", class: [:small, 'place-guest', 'on-top-only', guests.size >= max_space ? (guests.size > max_space ? :overbooked : :booked) : nil, max_space > 0 ? nil : :unwanted]
- html += content_tag(:div, space_html, class: [:space, area, max_space > 0 || guests.size > 0 ? nil : 'on-top-only'])
- end
-
- classes << 'status-warning' if @housing_data[id][:warnings].present?
- classes << 'status-error' if @housing_data[id][:errors].present?
-
- return { html: html.html_safe, class: classes.join(' ') }
- end
-
- def signin_link
- @login_dlg ||= true
- link_to (_'forms.actions.generic.login'), settings_path, data: { 'sign-in': true }
- end
-
- def link_with_confirmation(link_text, confirmation_text, path, args = {})
- @confirmation_dlg ||= true
- args[:data] ||= {}
- args[:data][:confirmation] = true
- link_to path, args do
- (link_text.to_s + content_tag(:template, confirmation_text, class: 'message')).html_safe
- end
- end
-
- def link_info_dlg(link_text, info_text, info_title, args = {})
- @info_dlg ||= true
- args[:data] ||= {}
- args[:data]['info-title'] = info_title
- args[:data]['info-text'] = true
- content_tag(:a, args) do
- (link_text.to_s + content_tag(:template, info_text, class: 'message')).html_safe
- end
- end
-
- def button_with_confirmation(button_name, confirmation_text, args = {})
- @confirmation_dlg ||= true
- args[:data] ||= {}
- args[:data][:confirmation] = true
- button_tag button_name, args do
- ((_"forms.actions.generic.#{button_name.to_s}") + content_tag(:template, confirmation_text, class: 'message')).html_safe
- end
- end
-
- def richtext(text, reduce_headings = 2)
- return '' unless text.present?
- return _!(text).
- gsub(/<(\/?)h4>/, '<\1h' + (reduce_headings + 4).to_s + '>').
- gsub(/<(\/?)h3>/, '<\1h' + (reduce_headings + 3).to_s + '>').
- gsub(/<(\/?)h2>/, '<\1h' + (reduce_headings + 2).to_s + '>').
- gsub(/<(\/?)h1>/, '<\1h' + (reduce_headings + 1).to_s + '>').
- html_safe
- end
-
- def truncate(text)
- strip_tags(text.gsub('>', '> ')).gsub(/^(.{40,60})\s.*$/m, '\1…').html_safe
- end
-
- def textarea(name, value = nil, options = {})
- id = name.to_s.gsub('[', '_').gsub(']', '')
- label_id = "#{id}-label"
- description_id = nil
- html = ''
-
- if options[:heading].present?
- label_id = "#{name.to_s}-label" unless options[:label]
- html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: label_id)
- end
-
- if options[:label] == false
- label_id = options[:labelledby]
- elsif options[:label].present?
- html += label_tag(id, nil, id: label_id) do
- _(options[:label], :t, vars: options[:vars] || {})
- end
- else
- html += label_tag(id, nil, id: label_id)
- end
-
- if options[:help].present?
- description_id ||= "#{id}-desc"
- html += content_tag(:div, _(options[:help], :s, 2), id: description_id, class: 'input-field-help')
- end
-
- if options[:warning].present?
- description_id ||= "#{id}-desc"
- html += content_tag(:div, _(options[:warning], :s, 2), id: description_id, class: 'warning-info')
- end
-
- aria = {}
- aria[:labelledby] = label_id if label_id.present?
- aria[:describedby] = description_id if description_id.present?
- css_class = [
- options[:short] === true ? :short : nil
- ].compact
-
- if options[:plain]
- html += (text_area_tag name, value,
- id: id,
- lang: options[:lang],
- aria: aria,
- class: css_class
- )
- else
- html += content_tag(:div, value.present? ? value.html_safe : '',
- id: id,
- data: { name: name, 'edit-on': options[:edit_on] || :load },
- lang: options[:lang],
- aria: aria,
- tabindex: 0,
- class: [:textarea] + css_class
- )
-
- add_stylesheet :editor
- add_inline_script :pen
- add_inline_script :markdown
- add_inline_script :editor
- end
-
- html = content_tag(:div, html.html_safe, class: ['text-area-field', 'input-field']).html_safe
- html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present?
-
- return html.html_safe
- end
-
- def fieldset(name, options = {}, &block)
- html = ''
- label_id = nil
- description_id = nil
-
- if options[:heading].present?
- label_id ||= "#{name.to_s}-label"
- html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: label_id)
- end
-
- if options[:help].present?
- description_id ||= "#{name.to_s}-desc"
- html += content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
- end
-
- (html + content_tag(:fieldset, content_tag(:div, class: :fieldgroup, &block).html_safe,
- aria: {
- labelledby: label_id,
- describedby: description_id
- }
- )
- ).html_safe
- end
-
- def selectfield(name, value, select_options, options = {})
- textfield(name, value, options.merge({type: :select, options: select_options}))
- end
-
- def telephonefield(name, value, options = {})
- textfield(name, value, options.merge({type: :telephone}))
- end
-
- def numberfield(name, value, options = {})
- textfield(name, value, options.merge({type: :number}))
- end
-
- def searchfield(name, value, options = {})
- textfield(name, value, options.merge({type: :search}))
- end
-
- def emailfield(name, value, options = {})
- textfield(name, value, options.merge({type: :email}))
- end
-
- def passwordfield(name, value, options = {})
- textfield(name, value, options.merge({type: :password}))
- end
-
- def textfield(name, value, options = {})
- html = ''
- id = unique_id(name)
- description_id = nil
-
- if options[:heading].present?
- description_id ||= "#{id.to_s}-desc"
- html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: description_id)
- end
-
- if options[:help].present?
- description_id ||= "#{id.to_s}-desc"
- html += content_tag(:div, _(options[:help], :s, 2, vars: options[:vars] || {}), class: 'input-field-help', id: description_id)
- end
-
- html += show_errors name, value
-
- if options[:label].present?
- html += label_tag(id) do
- _(options[:label], :t, vars: options[:vars] || {})
- end
- elsif options[:label] != false
- html += label_tag id, (_"forms.labels.generic.#{name}")
- elsif options[:type] == :select
- # add an empty label so that the drop down button will still appear
- html += label_tag id, ''
- end
- input_options = {
- id: id,
- required: options[:required],
- lang: options[:lang],
- min: options[:min],
- max: options[:max],
- step: options[:step],
- aria: description_id ? { describedby: description_id } : nil
- }
-
- case name
- when :address
- input_options[:autocomplete] = 'address-line1'
- when :name
- input_options[:autocomplete] = 'name'
- when :location
- input_options[:autocomplete] = 'address-level2'
- when :email
- input_options[:autocomplete] = 'email'
- when :phone
- input_options[:autocomplete] = 'tel'
- when :paypal_email_address, :paypal_username, :paypal_password, :paypal_signature
- input_options[:autocomplete] = 'off'
- end
-
- case options[:type]
- when :select
- option_list = options_for_select(options[:options], value)
-
- # make sure that we have an empty option if the select is required
- if options[:required] && options[:options].first.present? && options[:options].first.last.present?
- option_list = (' ' + option_list).html_safe
- end
- html += select_tag(name, option_list, input_options)
- else
- input_options[:autocomplete] = 'off' if options[:type] == :search
- html += send("#{(options[:type] || :text).to_s}_field_tag", name, value, input_options)
- end
-
- html = content_tag(:div, html.html_safe,
- class: [
- "#{(options[:type] || :text).to_s}-field",
- 'input-field',
- value.present? ? nil : 'empty',
- options[:big] ? 'big' : nil,
- options[:small] ? 'small' : nil,
- options[:stretch] ? 'stretch-item' : nil,
- options[:full] ? 'full' : nil,
- options[:inline_label] ? 'inline-label' : nil,
- (@errors || {})[name].present? ? 'has-error' : nil
- ].compact)
-
- html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present?
-
- return html.html_safe
- end
-
- def radiobuttons(name, boxes, value, label_key, options = {})
- checkboxes(name, boxes, [value], label_key, options.merge({radiobuttons: true}))
- end
-
- def checkbox(name, value, label_key, options = {})
- checkboxes(name, [true], value, label_key, options)
- end
-
- def unique_id(id)
- id = id.to_s.gsub('[', '_').gsub(']', '')
-
- @_ids ||= {}
- @_ids[id] ||= 0
-
- new_id = id
-
- if @_ids[id] > 0
- new_id += "--#{@_ids[id]}"
- end
-
- @_ids[id] += 1
-
- return new_id
- end
-
- def checkboxes(name, boxes, values, label_key, options = {})
- html = ''
-
- label_id = nil
- description_id = nil
-
- if options[:heading].present?
- label_id ||= unique_id("#{name.to_s}-label")
- html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: label_id)
- end
-
- help = nil
-
- if options[:help].present?
- description_id ||= unique_id("#{name.to_s}-desc")
- help = content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
- end
-
- html += help if help.present? && !options[:right_help]
-
- boxes_html = ''
-
- is_single = !values.is_a?(Array)
- unless boxes.length > 0 && boxes.first.is_a?(Integer)
- values = values.present? ? values.map(&:to_s) : [] unless is_single
- boxes = boxes.map(&:to_s)
- end
-
- # convert the required value into a pure boolean
- required = !!options[:required]
-
- boxes.each do | box |
- checked = (is_single ? values.present? : values.include?(box))
- values -= [box] if checked && !is_single
- id = nil
- if options[:radiobuttons].present?
- id = unique_id("#{name.to_s}_#{box}")
- boxes_html += radio_button_tag(name, box, checked, id: id, required: required)
- else
- _name = (is_single ? name : "#{name.to_s}[#{box}]")
- id = unique_id(_name)
- boxes_html += check_box_tag(_name, 1, checked, data: { toggles: options[:toggles] }.compact, id: id, required: required)
- end
-
- # we only need the required attribute on one element
- required = false
-
- if is_single
- label = _(label_key.to_s)
- elsif box.is_a?(Integer)
- label = I18n.t(label_key.to_s)[box]
- else
- label = _("#{label_key.to_s}.#{box}")
- end
-
- boxes_html += label_tag(id, label)
- end
-
- if options[:other].present? && !is_single
- id = nil
- if options[:radiobuttons].present?
- id = unique_id("#{name.to_s}_other")
- boxes_html += radio_button_tag(name, :other, values.present?, id: id)
- else
- _name = "#{name.to_s}[other]"
- id = unique_id(_name)
- boxes_html += check_box_tag(_name, 1, values.present?, id: id)
- end
- boxes_html += label_tag id,
- content_tag(:div,
- text_field_tag("other_#{name.to_s}", values.first, placeholder: (_"#{label_key}.other"), required: values.present?),
- class: 'other')
- end
-
- html += content_tag(:fieldset, content_tag(:div, boxes_html.html_safe,
- class: [
- 'check-box-field',
- 'input-field',
- options[:vertical] ? 'vertical' : nil,
- options[:inline] ? 'inline' : nil,
- options[:small] ? 'small' : nil,
- options[:big] ? 'big' : nil
- ].compact).html_safe,
- aria: {
- labelledby: label_id,
- describedby: description_id
- },
- class: [
- options[:centered] ? 'centered' : nil,
- options[:right_help] ? 'right-help' : nil
- ].compact
- )
-
- html += help if help.present? && options[:right_help]
-
- return html.html_safe
- end
-
- def companion(registration)
- if registration.housing_data.present? && registration.housing_data['companions'].present? && registration.housing_data['companions'].first.present?
- companion_user = User.find_by_email(registration.housing_data['companions'].first)
-
- if companion_user.present?
- cr = ConferenceRegistration.where(user_id: companion_user.id).order(created_at: :desc).limit(1).first
-
- if cr.present? && ((cr.steps_completed || []).include? 'questions')
- return companion_user
- end
- end
- return :unregistered
- end
- return nil
- end
-
- def comment(comment)
- add_inline_script :time
- add_js_translation('datetime.distance_in_words')
-
- content_tag(:div, class: 'comment-body') do
- content_tag(:h4, comment.user.name, class: 'comment-title') +
- content_tag(:time, time(comment.created_at, :default), datetime: comment.created_at.to_s) +
- content_tag(:div, class: 'comment-text') do
- markdown comment.comment
- end
- end
- end
-
- def html_edit_table(excel_data, options = {})
- attributes = { class: options[:class], id: options[:id] }
- attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
-
- if options[:column_names].is_a? Hash
- return content_tag(:table, attributes) do
- max_columns = 0
- column_names = {}
- (content_tag(:thead) do
- headers = ''
- options[:column_names].each do | header_name, columns |
- column_names[header_name] ||= []
- headers += content_tag(:th, excel_data[:keys][header_name].present? ? _(excel_data[:keys][header_name]) : '', colspan: 2)
- row_count = columns.size
- columns.each do | column |
- column_names[header_name] << column
- if (options[:row_spans] || {})[column].present?
- row_count += (options[:row_spans][column] - 1)
- for i in 1...options[:row_spans][column]
- column_names[header_name] << false
- end
- end
- end
- max_columns = row_count if row_count > max_columns
- end
- content_tag(:tr, headers.html_safe)
- end) + (content_tag(:tbody) do
- rows = ''
-
- for i in 0...max_columns
- columns_html = ''
- column_names.each do | header_name, columns |
- column = columns[i]
- if column.present?
- attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
- if (options[:row_spans] || {})[column].present?
- attributes[:rowspan] = options[:row_spans][column]
- end
- columns_html += content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '', rowspan: attributes[:rowspan]) +
- edit_column(nil, column, nil, attributes, excel_data, options)
- elsif column != false
- columns_html += content_tag(:td, ' ', colspan: 2, class: :empty)
- end
- end
- rows += content_tag(:tr, columns_html.html_safe, { class: 'always-edit', data: { key: '' } })
- end
- rows.html_safe
- end)
- end
- else
- return content_tag(:table, attributes) do
- (content_tag(:tbody) do
- rows = ''
- excel_data[:columns].each do |column|
- if (excel_data[:column_types] || {})[column] != :table && ((options[:column_names] || []).include? column)
- rows += content_tag(:tr, { class: 'always-edit', data: { key: '' } }) do
- attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
- columns = content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '') +
- edit_column(nil, column, nil, attributes, excel_data, options)
- end
- end
- end
- rows.html_safe
- end)
- end
- end
- end
-
- def html_table(excel_data, options = {})
- options[:html] = true
- attributes = { class: options[:class], id: options[:id] }
- attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
- content_tag(:table, attributes) do
- (content_tag(:thead) do
- content_tag(:tr, excel_header_columns(excel_data))
- end) +
- content_tag(:tbody, excel_rows(excel_data, {}, options))
- end
- end
-
- def excel_table(excel_data)
- format_xls 'table' do
- workbook use_autowidth: true
- format bg_color: '333333'
- format 'td', font_name: 'Calibri', fg_color: '333333'
- format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff'
- format 'th.sub-table', font_name: 'Calibri', b: true, bg_color: 'DDDDDD', fg_color: '333333'
- format 'td.datetime', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333'
- format 'td.date.day', num_fmt: 14, font_name: 'Courier New', sz: 10, fg_color: '333333'
- format 'td.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333'
- format 'td.number', font_name: 'Courier New', sz: 10, fg_color: '333333'
- format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true
- end
-
- content_tag(:table) do
- (content_tag(:thead) do
- content_tag(:tr, excel_header_columns(excel_data))
- end) +
- content_tag(:tbody, excel_rows(excel_data))
- end
- end
-
- def excel_header_columns(data, padding = {}, class_name = nil)
- columns = ''
-
- data[:columns].each do |column|
- unless data[:column_types].present? && data[:column_types][column] == :table
- columns += content_tag(:th, data[:keys][column].present? ? _(data[:keys][column]) : '', class: class_name)
- end
- end
-
- pad_columns(columns, padding, :th)
- end
-
- def excel_empty_row(data, padding = {})
- columns = ''
-
- data[:columns].each do |column|
- unless data[:column_types].present? && data[:column_types][column] == :table
- columns += content_tag(:td)
- end
- end
-
- content_tag(:tr, pad_columns(columns, padding))
- end
-
- def pad_columns(columns, padding, column_type = :td)
- left = ''
-
- for i in 1..(padding['left'] || 0)
- left += content_tag(:td)
- end
-
- right = ''
- for i in 1..(padding['right'] || 0)
- right += content_tag(:td)
- end
-
- (left + columns + right).html_safe
- end
-
- def excel_columns(row, data, padding = {}, options = {})
- columns = ''
-
- data[:columns].each do |column|
- value = row[column].present? ? (_!row[column].to_s) : ''
- class_name = nil
- is_sub_table = false
-
- if data[:column_types].present? && data[:column_types][column].present?
- if data[:column_types][column] == :table
- is_sub_table = true
- else
- class_name = data[:column_types][column]
- end
- end
-
- unless is_sub_table
- attributes = { class: [class_name] }
- if options[:html] && row[:html_values].present? && row[:html_values][column].present?
- value = row[:html_values][column]
- end
-
- if options[:editable]
- attributes[:data] = { 'column-id' => column }
- end
-
- if (options[:column_names] || []).include? column
- attributes[:tabindex] = 0
- end
-
- columns += content_tag(:td, value, attributes)
- end
- end
-
- pad_columns(columns, padding)
- end
-
- def editor_columns(row, data, padding = {}, options = {})
- columns = ''
-
- data[:columns].each do |column|
- value = row[column].present? ? (_!row[column].to_s) : ''
- class_name = nil
- is_sub_table = false
-
- if data[:column_types].present? && data[:column_types][column].present?
- if data[:column_types][column] == :table
- is_sub_table = true
- else
- class_name = data[:column_types][column]
- end
- end
-
- unless is_sub_table
- attributes = { class: [class_name] }
-
- if options[:editable]
- attributes[:data] = { 'column-id' => column }
- end
-
- if (options[:column_names] || []).include? column
- columns += edit_column(row, column, value, attributes, data, options)
- else
- columns += content_tag(:td, value, attributes)
- end
-
- end
- end
-
- pad_columns(columns, padding)
- end
-
- def edit_column(row, column, value, attributes, data, options)
- attributes[:class] << 'has-editor'
- raw_value = row.present? ? (row[:raw_values][column] || value) : nil
-
- if row.present? && options[:html] && row[:html_values].present? && row[:html_values][column].present?
- value = row[:html_values][column]
- end
-
- editor_attributes = { class: 'cell-editor', data: { value: raw_value.to_s } }
-
- # create the control but add the original value to set the width and height
- editor_value = content_tag(:div, value, class: 'value')
- if (options[:column_options] || {})[column].present?
- value = (editor_value.html_safe + select_tag(column, options_for_select([['', '']] + options[:column_options][column], raw_value), editor_attributes)).html_safe
- elsif data[:column_types][column] == :text
- editor_attributes[:name] = column
- value = (editor_value.html_safe + content_tag(:textarea, raw_value, editor_attributes)).html_safe
- else
- editor_attributes[:name] = column
- editor_attributes[:value] = raw_value
- editor_attributes[:required] = :required if (options[:required_columns] || []).include? column
- type = data[:column_types][column] || :unknown
- editor_attributes[:type] = { money: :number, number: :number, email: :email }[type] || :text
- value = (editor_value.html_safe + content_tag(:input, nil, editor_attributes)).html_safe
- end
-
- return content_tag(:td, value, attributes)
- end
-
- def excel_sub_tables(row, data, padding = {}, options = {})
- rows = ''
-
- # shift the table right
- new_padding = {
- 'left' => (padding['right'] || 0) + 1,
- 'right' => (padding['right'] || 0) - 1
- }
-
- data[:columns].each do |column|
- if data[:column_types].present? && data[:column_types][column] == :table
- rows += content_tag(:tr, excel_header_columns(row[column], new_padding, 'sub-table'))
- rows += excel_rows(row[column], new_padding)
- rows += excel_empty_row(row[column], new_padding)
- end
-
- end
-
- rows.html_safe
- end
-
- def excel_rows(data, padding = {}, options = {})
- rows = ''
- data[:data].each do |row|
- attributes = {}
-
- if options[:primary_key].present?
- attributes[:data] = { key: row[options[:primary_key]] }
- end
-
- attributes[:class] = []
-
- if options[:editable]
- attributes[:class] << :editable
- end
-
- rows += content_tag(:tr, excel_columns(row, data, padding, options), attributes) +
- excel_sub_tables(row, data, padding)
- rows += content_tag(:tr, editor_columns(row, data, padding, options), class: :editor) if options[:editable]
- end
- rows.html_safe
- end
-
- def registrations_edit_table_options
- {
- id: 'create-table',
- class: ['registrations', 'admin-edit', 'always-editing'],
- primary_key: :id,
- column_names: {
- contact_info: [
- :name,
- :email,
- :is_subscribed,
- :city,
- :preferred_language
- ] + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym },
- questions: [
- :registration_fees_paid,
- :is_attending,
- :arrival,
- :departure,
- :housing,
- :bike,
- :food,
- :companion_email,
- :allergies,
- :other
- ],
- hosting: [
- :can_provide_housing,
- :address,
- :phone,
- :first_day,
- :last_day
- ] + ConferenceRegistration.all_spaces +
- ConferenceRegistration.all_considerations + [
- :notes
- ]
- },
- row_spans: {
- allergies: 3,
- other: 2
- },
- required_columns: [:name, :email],
- editable: administration_update_path(@this_conference.slug, :stats),
- column_options: @column_options
- }
- end
-
- def registrations_table_options
- {
- id: 'search-table',
- class: ['registrations', 'admin-edit'],
- primary_key: :id,
- column_names: [
- :registration_fees_paid,
- :is_attending,
- :is_subscribed,
- :city,
- :preferred_language,
- :arrival,
- :departure,
- :housing,
- :bike,
- :food,
- :companion_email,
- :allergies,
- :other,
- :can_provide_housing,
- :address,
- :phone,
- :first_day,
- :last_day,
- :notes
- ] +
- User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
- ConferenceRegistration.all_spaces +
- ConferenceRegistration.all_considerations,
- editable: administration_update_path(@this_conference.slug, :stats),
- column_options: @column_options
- }
- end
-
- private
- def _original_content(value, lang)
- content_tag(:div, (
- content_tag(:h4, _('translate.content.Translation_of')) +
- content_tag(:div, value, class: 'value', lang: lang)
- ).html_safe, class: 'original-text')
- end
-
- def _form_field(type, name, value, options)
- if type == 'check_box'
- self.send(type + '_tag', name, "1", value, options)
- else
- self.send(type + '_tag', name, value, options)
- end
- end
+ include ScheduleHelper
+
+ @@keyQueue = nil
+ @@translationsOnThisPage = nil
+ @@lastTranslation = nil
+ @@allTranslations = nil
+ @@no_banner = true
+ @@banner_attribution_details = nil
+ @@banner_image = nil
+ @@has_content = true
+ @@front_page = false
+ @@body_class = nil
+ @@test_location = nil
+
+ def init_vars
+ @@keyQueue = nil
+ @@no_banner = true
+ @@banner_attribution_details = nil
+ @@banner_image = nil
+ @@has_content = true
+ @@front_page = false
+ @@body_class = nil
+ end
+
+ def this_is_the_front_page
+ @@front_page = true
+ end
+
+ def header_is_fixed
+ @fixed_header = true
+ end
+
+ def is_header_fixed?
+ @fixed_header ||= false
+ end
+
+ def is_this_the_front_page?
+ return @@front_page
+ end
+
+ def header_classes
+ classes = Array.new
+ classes << 'fixed' if is_header_fixed?
+ return classes
+ end
+
+ def ThereAreTranslationsOnThisPage?
+ @@translationsOnThisPage
+ end
+
+ def get_all_translations
+ @@allTranslations
+ end
+
+ def title(page_title)
+ content_for(:title) { page_title.to_s }
+ end
+
+ def description(page_description)
+ content_for(:description) { page_description.to_s }
+ end
+
+ def banner_image(banner_image, name: nil, id: nil, user_id: nil, src: nil)
+ @@no_banner = false
+ @@banner_image = banner_image
+ if (name || id || user_id || src)
+ @@banner_attribution_details = {:name => name, :id => id, :user_id => user_id, :src => src}
+ end
+ content_for(:banner_image) { banner_image.to_s }
+ end
+
+ def banner_attrs(banner_image)
+ @@no_banner = false
+ if banner_image.length > 0
+ @@banner_image = banner_image
+ return {style: 'background-image: url(' + banner_image + ');', class: 'has-image' }
+ end
+ {class: 'no-image'}
+ end
+
+ def has_banner?
+ !@@no_banner
+ end
+
+ def has_content?
+ @@has_content
+ end
+
+ def has_no_content
+ @@has_content = false
+ end
+
+ def banner_title(banner_title)
+ @@no_banner = false
+ content_for(:banner) { ('
' + banner_title.to_s + '
').html_safe }
+ end
+
+ def add_stylesheet(sheet)
+ @stylesheets ||= []
+ @stylesheets << sheet unless @stylesheets.include?(sheet)
+ end
+
+ def stylesheets
+ html = ''
+ Rack::MiniProfiler.step('inject_css') do
+ html += inject_css!
+ end
+ (@stylesheets || []).each do |css|
+ Rack::MiniProfiler.step("inject_css #{css}") do
+ html += inject_css! css.to_s
+ end
+ end
+ html += stylesheet_link_tag 'i18n-debug' if request.params['i18nDebug']
+ return html.html_safe
+ end
+
+ def add_inline_script(script)
+ @_inline_scripts ||= []
+ script = Rails.application.assets.find_asset("#{script.to_s}.js").to_s
+ @_inline_scripts << script unless @_inline_scripts.include?(script)
+ end
+
+ def inline_scripts
+ return '' unless @_inline_scripts.present?
+ "".html_safe
+ end
+
+ def banner_attribution
+ if @@banner_image && @@banner_attribution_details
+ src = @@banner_attribution_details[:src]
+ attribution = ''
+ attribution.html_safe
+ end
+ end
+
+ def dom_ready(&block)
+ content_for(:dom_ready, &block)
+ end
+
+ def body_class(c)
+ @@body_class ||= Array.new
+ @@body_class << (c.is_a?(Array) ? c.join(' ') : c)
+ end
+
+ def page_style
+ classes = Array.new
+
+ classes << 'has-translations' if ThereAreTranslationsOnThisPage?
+ classes << 'no-content' unless @@has_content
+ classes << 'has-banner-image' if @@banner_image
+ classes << @@body_class.join(' ') if @@body_class
+ classes << 'fixed-banner' if is_header_fixed?
+
+ if params[:controller]
+ classes << params[:action]
+ unless params[:controller] == 'application'
+ classes << params[:controller]
+
+ if params[:action]
+ classes << "#{params[:controller]}-#{params[:action]}"
+ end
+ end
+ end
+ return classes
+ end
+
+ def yield_or_default(section, default = '')
+ content_for?(section) ? content_for(section) : default
+ end
+
+ def _translate_me(translation)
+ @@translationsOnThisPage = true
+ datakeys = ''
+ translation['vars'].each { |key, value| datakeys += ' data-var-' + key.to_s + '="' + value.to_s.gsub('"', '"') + '"' }
+ ('' + (translation['html'] || translation['untranslated']) + ' ').to_s.html_safe
+ end
+
+ def _do_translate(key, vars, behavior, behavior_size, locale)
+ translation = {'key' => key, 'lang' => '0', 'vars' => vars}
+ v = vars.dup
+ begin
+ v[:raise] = true
+ options = {:raise => true}
+ if locale
+ options[:locale] = locale.to_sym
+ end
+ translation['untranslated'] = I18n.translate(key, v, options)
+ translation['lang'] = locale.to_s
+ translation['is_translated'] = true
+
+ hash = Hash.new
+ translations = Translation.where(["locale = ? AND key LIKE ?", locale.to_s, key + '%']).take(6).each { |o| hash[o.key] = o.value }
+ translation['translated'] = hash.to_json.gsub('"', '"')
+ rescue I18n::MissingTranslationData
+ default_translation = I18n::MissingTranslationExceptionHandler.note(key, behavior, behavior_size)
+ translation['untranslated'] = default_translation
+ end
+ return translation
+ end
+
+ def _can_translate?()
+ false
+ end
+
+ def off_screen(text, id = nil)
+ content_tag(:span, text.html_safe, id: id, class: 'screen-reader-text')
+ end
+
+ def url_for_locale(locale, url = nil)
+ return url unless locale.present?
+
+ unless url.present?
+ new_params = params.merge({action: (params[:_original_action] || params[:action])})
+ new_params.delete(:_original_action)
+
+ if Rails.env.development? || Rails.env.test?
+ return url_for(new_params.merge({lang: locale.to_s}))
+ end
+
+ subdomain = Rails.env.preview? ? "preview-#{locale.to_s}" : locale.to_s
+ return url_for(new_params.merge(host: "#{subdomain}.bikebike.org"))
+ end
+
+ return url if Rails.env.development? || Rails.env.test?
+ return "https://preview-#{locale.to_s}.bikebike.org#{url}" if Rails.env.preview?
+ "https://#{locale.to_s}.bikebike.org#{url}"
+ end
+
+ def registration_steps(conference = @conference)
+ {
+ pre: [:policy, :basic_info, :workshops],
+ open: [:policy, :basic_info, :questions, :payment, :workshops]
+ }[@this_conference.registration_status]
+ end
+
+ def registration_status(registration)
+ return :unregistered if registration.nil?
+ return registration.status
+ end
+
+ def sortable(objects, id = 'id', url: nil, &block)
+ result = ''
+ objects.each_index do |i|
+ @this = objects[i]
+ result += ''
+ result += hidden_field_tag (id + "[#{i}]"), objects[i][id]
+ result += hidden_field_tag ('position' + "[#{i}]"), i, :class => 'sortable-position'
+ if block_given?
+ result += capture(objects[i], &block)
+ end
+ result += ' '
+ end
+ result += ''
+ result.html_safe
+ end
+
+ def tabs object, tabs
+ type = object.class.name.downcase
+ tab_list = ''
+
+ tabs.each do |tab|
+ link = nil
+ if self.respond_to?(type + '_' + tab.to_s + '_path')
+ link = self.send(type + '_' + tab.to_s + '_path', object)
+ elsif self.respond_to?(tab.to_s + '_' + type + '_path')
+ link = self.send(tab.to_s + '_' + type + '_path', object)
+ end
+
+ c = ['tab', 'tab-' + (link ? tab.to_s : 'show')]
+ if params[:action] == tab.to_s
+ c << 'current'
+ end
+ link_html = ''
+ if tab.is_a?(Hash)
+ func = tab.keys[0]
+ val = tab[func]
+ args = val ? (val.is_a?(Array) ? (val.collect { |v| object[v] } ) : [object[val]] ) : nil
+
+ link_html = link_to func.to_s.gsub(/_path$/, ''), args ? self.send(func, args) : self.send(func), :class => c
+ else
+ #x
+ #link_html = link_to tab, link || object, :class => c
+ end
+ tab_list += link_html
+ end
+ ('
+ ' +
+ tab_list +
+ '
+ ').html_safe
+ end
+
+ def tabs!
+ object = nil
+ tabs = nil
+ case params[:controller]
+ when 'organizations'
+ object = @organization
+ tabs = OrganizationsHelper::TABS
+ when 'conferences'
+ object = @conference
+ tabs = ConferencesHelper::TABS
+ when 'workshops'
+ object = [@conference, @workshop]
+ tabs = WorkshopsHelper::TABS
+ end
+
+ if object && tabs
+ return tabs object, tabs
+ end
+ end
+
+ def sub_tabs object, tabs
+ type = object.class.name.downcase
+ tab_list = ''
+
+ tabs.each do |tab|
+ link = nil
+ if self.respond_to?(type + '_' + tab.to_s + '_path')
+ link = self.send(type + '_' + tab.to_s + '_path', object)
+ elsif self.respond_to?(tab.to_s + '_' + type + '_path')
+ link = self.send(tab.to_s + '_' + type + '_path', object)
+ end
+
+ c = ['sub-tab', 'sub-tab-' + (link ? tab.to_s : 'show')]
+ if current_page?(link)
+ c << 'current'
+ end
+ tab_list += link_to tab, link || object, :class => c
+ end
+ ('' + tab_list + ' ').html_safe
+ end
+
+ def sub_tabs!
+ object = nil
+ tabs = nil
+ case params[:controller]
+ when 'organizations'
+ object = @organization
+ tabs = OrganizationsHelper::SUB_TABS
+ when 'conferences'
+ object = @conference
+ tabs = ConferencesHelper::SUB_TABS
+ end
+
+ if object && tabs
+ return sub_tabs object, tabs
+ end
+ end
+
+ def m(*args)
+ _(*args) { |t|
+ markdown(t)
+ }
+ end
+
+ def markdown(object, attribute = nil)
+ return '' unless object
+ content = attribute ? object.send(attribute.to_s) : object
+ @markdown ||= Redcarpet::Markdown.new(Redcarpet::Render::HTML.new({
+ filter_html: true,
+ hard_wrap: true,
+ space_after_headers: true,
+ fenced_code_blocks: true,
+ link_attributes: { target: "_blank" }
+ }), {
+ autolink: true,
+ disable_indented_code_blocks: true,
+ superscript: true
+ })
+ @markdown.render(content).html_safe
+ end
+
+ def paragraph(object, attribute = nil)
+ return '' unless object
+ content = attribute ? object.send(attribute.to_s) : object
+ result = ''
+ if content =~ /<(p|span|h\d|div)[^>]*>/
+ result = content.gsub(/\s*(style|class|id|width|height|font)=\".*?\"/, '')
+ .gsub(/ /, ' ')
+ .gsub(/<(\/)?\s*h\d\s*>/, '<\1h3>')
+ .gsub(/(.*?) \s*( )+/, '
\1
')
+ .gsub(/]*>\s*(.*?)\s*<\/span>/, '\1')
+ .gsub(/\s*<\/p>/, '')
+ .gsub(/<(\/)?div>/, '<\1p>')
+ if !(result =~ /
]*>/)
+ result = '
' + result + '
'
+ end
+ else
+ result = markdown(object, attribute)
+ end
+ result.html_safe
+ end
+
+ def form_field(f, response = nil)
+ id = 'field_' + f.id.to_s
+ html = p(f, 'title')
+
+ options = JSON.parse(f.options)
+ if f.field_type == 'multiple'
+ if f.help
+ html += ('' + p(f, 'help') + '
').html_safe
+ end
+
+ opts = Hash.new
+ options['options'].split(/\s*\n\s*/).each do |value|
+ kv = value.split(/\s*\|\s*/, 2)
+ opts[kv[0]] = kv[1]
+ end
+
+ val = response ? ActiveSupport::JSON.decode(response.data) : Hash.new
+
+ if f.repeats?
+ is_array = f.is_array?
+ opts.each do |key, value|
+ n = (id + (is_array ? ('_' + key) : '')).to_sym
+ v = is_array ? (val ? val[key] : nil) : key
+ o = {:label => value}
+ if f.required
+ options[:required] = true
+ end
+ html += _form_field(options['selection_type'], n, v, o)
+ end
+ else
+ html += _form_field(options['selection_type'], id.to_sym, options_for_select(opts.invert, val), {})
+ end
+ else
+ #html += field(id.to_sym, options['input_type'] + '_tag', label: false, placeholder: f.help, value: response ? ActiveSupport::JSON.decode(response.data) : nil, required: f.required)
+ opts = {label: false, placeholder: f.help && f.help.length > 0 ? f.help : false}
+ if f.required
+ opts[:required] = true
+ end
+ html += _form_field(options['input_type'], id.to_sym, response ? ActiveSupport::JSON.decode(response.data) : nil, opts)
+ end
+
+ html.html_safe
+ end
+
+ def t(*a)
+ _(*a)
+ end
+
+ def lookup_ip
+ if request.remote_ip == '127.0.0.1' || request.remote_ip == '::1'
+ session['remote_ip'] || (session['remote_ip'] = open("http://checkip.dyndns.org").first.gsub(/^.*\s([\d\.]+).*$/s, '\1').gsub(/[^\.\d]/, ''))
+ else
+ request.remote_ip
+ end
+ end
+
+ def get_remote_location
+ Geocoder.search(session['remote_ip'] || (session['remote_ip'] = open("http://checkip.dyndns.org").first.gsub(/^.*\s([\d\.]+).*$/s, '\1').gsub(/[^\.\d]/, '')), language: 'en').first
+ end
+
+ def lookup_ip_location
+ begin
+ if is_test? && ApplicationController::get_location.present?
+ Geocoder.search(ApplicationController::get_location, language: 'en').first
+ elsif request.remote_ip == '127.0.0.1' || request.remote_ip == '::1'
+ get_remote_location
+ else
+ request.location || get_remote_location
+ end
+ rescue
+ nil
+ end
+ end
+
+ def hash_to_html_attributes(hash, prefix = '')
+ attributes = ''
+ if hash
+ hash.each { |k,v|
+ k = k.to_s
+ if v
+ if v.is_a?(Hash)
+ attributes += hash_to_html_attributes(v, 'data-')
+ else
+ attributes += " #{k}=\"" + (v.is_a?(Array) ? v.join(' ') : v) + '"'
+ end
+ end
+ }
+ end
+ attributes
+ end
+
+ def icon(id, attributes = nil)
+ (' ').html_safe
+ end
+
+ def static_map(location, zoom, width, height)
+ require 'fileutils'
+ local_file_name = "#{location}-#{width}x#{height}z#{zoom}.png"
+ file = File.join("public", "maps/#{local_file_name}")
+ FileUtils.mkdir_p("public/maps") unless File.directory?("public/maps")
+ if !File.exist?(file)
+ url = "https://maps.googleapis.com/maps/api/staticmap?center=#{location}&zoom=#{zoom}&size=#{width}x#{height}&maptype=roadmap&markers=size:small%7C#{location}&key=AIzaSyAH7U8xUUb8IwDPy1wWuYGprzxf4E1Jj4o"
+ require 'open-uri'
+ open(file, 'wb') do |f|
+ f << open(url).read
+ end
+ end
+
+ cdn("/maps/#{local_file_name}")
+ end
+
+ def cdn(file)
+ (Rails.application.config.action_controller.asset_host || '') + file
+ end
+
+ def is_production?
+ Rails.env == 'production' || Rails.env == 'preview'
+ end
+
+ def is_test?
+ Rails.env == 'test'
+ end
+
+ def subdomain
+ request.env['SERVER_NAME'].gsub(/^(\w+)\..*$/, '\1')
+ end
+
+ def is_test_server?
+ subdomain == 'test'
+ end
+
+ def rand_hash(length = 16, model = nil, field = nil)
+ if field
+ hash = rand_hash(length)
+ while !model.to_s.to_s.singularize.classify.constantize.find_by(field => hash).nil?
+ hash = rand_hash(length)
+ end
+ end
+ rand(36**length).to_s(36)
+ end
+
+ def get_panoramio_image(location)
+ if is_test?
+ params[:image] = 'panoramio.jpg'
+ params[:attribution_id] = 1234
+ params[:attribution_user_id] = 5678
+ params[:attribution_name] = 'Some Guy'
+ params[:attribution_src] = 'panoramio'
+ return params
+ end
+
+ location = location.city + ', ' + (location.territory ? location.territory + ' ' : '') + location.country
+ $panoramios ||= Hash.new
+ $panoramios[location] ||= 0
+ $panoramios[location] += 1
+ result = Geocoder.search(location, language: 'en').first
+ if result
+ points = Geocoder::Calculations.bounding_box([result.latitude, result.longitude], 5, { :unit => :km })
+ options = {:set => :public, :size => :original, :from => 0, :to => 20, :mapfilter => false, :miny => points[0], :minx => points[1], :maxy => points[2], :maxx => points[3]}
+ url = 'http://www.panoramio.com/map/get_panoramas.php?' + options.to_query
+ response = JSON.parse(open(url).read)
+ response['photos'].each { |img|
+ if img['width'].to_i > 980
+ if Organization.find_by(:cover_attribution_id => img['photo_id'], :cover_attribution_src => 'panoramio').nil? && Conference.find_by(:cover_attribution_id => img['photo_id'], :cover_attribution_src => 'panoramio').nil?
+ params[:image] = img['photo_file_url']
+ params[:attribution_id] = img['photo_id']
+ params[:attribution_user_id] = img['owner_id']
+ params[:attribution_name] = img['owner_name']
+ params[:attribution_src] = 'panoramio'
+ return params
+ end
+ end
+ }
+ end
+ return nil
+ end
+
+ def get_secure_info(name)
+ YAML.load(File.read(Rails.root.join("config/#{name.to_s}.yml")))[Rails.env].symbolize_keys
+ end
+
+ def location(location, locale = I18n.locale)
+ return nil if location.blank?
+
+ city = nil
+ region = nil
+ country = nil
+ if location.is_a?(Location) || location.is_a?(City)
+ country = location.country
+ region = location.territory
+ city = location.city
+ elsif location.data.present? && location.data['address_components'].present?
+ component_map = {
+ 'locality' => :city,
+ 'administrative_area_level_1' => :region,
+ 'country' => :country
+ }
+ location.data['address_components'].each do | component |
+ types = component['types']
+ country = component['short_name'] if types.include? 'country'
+ region = component['short_name'] if types.include? 'administrative_area_level_1'
+ city = component['long_name'] if types.include? 'locality'
+ end
+ else
+ country = location.data['country_code']
+ region = location.data['region_code']
+ city = location.data['city']
+ end
+
+ # we need cities for our logic, don't let this continue if we don't have one
+ return nil unless city.present?
+
+ hash = Hash.new
+ region_translation = region.present? && country.present? ? _("geography.subregions.#{country}.#{region}", locale: locale) : ''
+ country_translation = country.present? ? _("geography.countries.#{country}", locale: locale) : ''
+ hash[:city] = _!(city) if city.present?
+ hash[:region] = region_translation if region_translation.present?
+ hash[:country] = country_translation if country_translation.present?
+
+ # return the formatted location or the first value if we only have one value
+ return hash.length > 1 ? _("geography.formats.#{hash.keys.join('_')}", locale: locale, vars: hash) : hash.values.first
+ end
+
+ def location_link(location)
+ return '' unless location.present? && location.address.present?
+ content_tag(:a, (_!location.address), href: "http://www.google.com/maps/place/#{location.latitude},#{location.longitude}")
+ end
+
+ def same_city?(location1, location2)
+ return false unless location1.present? && location2.present?
+
+ location1 = location(location1) unless location1.is_a?(String)
+ location2 = location(location2) unless location2.is_a?(String)
+
+ location1.eql? location2
+ end
+
+ def show_errors(field, value = nil)
+ return '' unless @errors && @errors[field].present?
+
+ error_txt = _"errors.messages.fields.#{field.to_s}.#{@errors[field]}", :s, vars: { value: value }
+
+ "#{error_txt}
".html_safe
+ end
+
+ def nav_link(link, title = nil, class_name = nil)
+ if title.nil? && link.is_a?(Symbol)
+ title = link
+ link = send("#{link.to_s}_path")
+ end
+ if class_name.nil? && title.is_a?(Symbol)
+ class_name = title
+ end
+ title = _"page_titles.#{title.to_s.titlecase.gsub(/\s/, '_')}"
+ classes = []
+ classes << class_name if class_name.present?
+ classes << "strlen-#{strip_tags(title).length}"
+ classes << 'current' if request.fullpath.start_with?(link.gsub(/^(.*?)\/$/, '\1'))
+ link_to "#{title} ".html_safe, link, :class => classes
+ end
+
+ def date(date, format = :long)
+ I18n.l(date.is_a?(String) ? Date.parse(date) : date, :format => format)
+ end
+
+ def time(time, format = :short)
+ if time.is_a?(String)
+ time = Date.parse(time)
+ elsif time.is_a?(Float) || time.is_a?(Integer)
+ time = DateTime.now.midnight + time.hours
+ end
+
+ I18n.l(time, :format => format)
+ end
+
+ def date_span(date1, date2)
+ key = 'same_month'
+ if date1.year != date2.year
+ key = 'different_year'
+ elsif date1.month != date2.month
+ key = 'same_year'
+ end
+ d1 = I18n.l(date1.to_date, format: "span_#{key}_date_1".to_sym)
+ d2 = I18n.l(date2.to_date, format: "span_#{key}_date_2".to_sym)
+ _('date.date_span', vars: {:date_1 => d1, :date_2 => d2})
+ end
+
+ def time_length(length)
+ hours = length.to_i
+ minutes = ((length - hours) * 60).to_i
+ hours = hours > 0 ? (I18n.t 'datetime.distance_in_words.x_hours', count: hours) : nil
+ minutes = minutes > 0 ? (I18n.t 'datetime.distance_in_words.x_minutes', count: minutes) : nil
+ return hours.present? ? (minutes.present? ? (I18n.t 'datetime.distance_in_words.x_and_y', x: hours, y: minutes) : hours) : minutes
+ end
+
+ def hour_span(time1, time2)
+ (time2 - time1) / 3600
+ end
+
+ def hours(time1, time2)
+ time_length hour_span(time1, time2)
+ end
+
+ def generate_confirmation(user, url, expiry = nil)
+ ApplicationController::generate_confirmation(user, url, expiry)
+ end
+
+ def money(amount)
+ return _!('$0.00') if amount == 0
+ _!((amount * 100).to_i.to_s.gsub(/^(.*)(\d\d)$/, '$\1.\2'))
+ end
+
+ def percent(p)
+ return _!('0.00%') if p == 0
+ _!((p * 10000).to_i.to_s.gsub(/^(.*)(\d\d)$/, '\1.\2%'))
+ end
+
+ def data_set(header_type, header_key, attributes = {}, &block)
+ raw_data_set(header_type, _(header_key), attributes, &block)
+ end
+
+ def raw_data_set(header_type, header, attributes = {}, &block)
+ attributes[:class] = attributes[:class].split(' ') if attributes[:class].is_a?(String)
+ attributes[:class] = [attributes[:class].to_s] if attributes[:class].is_a?(Symbol)
+ attributes[:class] ||= []
+ attributes[:class] << 'data-set'
+ content_tag(:div, attributes) do
+ content_tag(header_type, header, class: 'data-set-key') +
+ content_tag(:div, class: 'data-set-value', &block)
+ end
+ end
+
+ def conference_days_options(conference = nil)
+ conference ||= @this_conference || @conference
+ return [] unless conference
+
+ dates = []
+ day = conference.start_date - 7.days
+ last_day = conference.end_date + 7.days
+
+ while day <= last_day
+ dates << day
+ day += 1.day
+ end
+
+ return dates
+ end
+
+ def conference_days_options_list(period, conference = nil, format = nil)
+ conference ||= @this_conference || @conference
+ return [] unless conference
+
+ days = []
+
+ conference_days_options(conference).each do |day|
+ belongs_to_periods = []
+ belongs_to_periods << :before if day <= conference.start_date
+ belongs_to_periods << :after if day >= conference.end_date
+ belongs_to_periods << :before_plus_one if day <= (conference.start_date + 1.day)
+ belongs_to_periods << :after_minus_one if day >= (conference.end_date - 1.day)
+ belongs_to_periods << :during if day >= conference.start_date && day <= conference.end_date
+ days << [date(day.to_date, format || :span_same_year_date_1), day.to_date] if belongs_to_periods.include?(period)
+ end
+ return days
+ end
+
+ def registration_status_options_list(conference = nil)
+ conference ||= @this_conference || @conference
+ return [] unless conference
+
+ options = Array.new
+ [:closed, :pre, :open].each do | opt |
+ options << [(_"forms.labels.generic.registration_statuses.#{opt}"), opt]
+ end
+
+ return options
+ end
+
+ def month_select(value = nil, args = {})
+ options = (1..12).to_a.map { |month| [ (I18n.t "date.#{args[:format] || 'month_names'}")[month], month ] }
+ selectfield args[:name] || :month, value, options, args
+ end
+
+ def month_day_select(value = nil, args = {})
+ options = (1..31).to_a.map { |day| [ day, day ] }
+ selectfield args[:name] || :month_day, value, options, args
+ end
+
+ def day_select(value = nil, args = {})
+ selectfield :day, value, conference_days_options_list(:during, nil, args[:format]), args
+ end
+
+ def hour_select(value = nil, args = {}, start_time = 8, end_time = 23.5, step = 0.5)
+ time = start_time
+ times = []
+ while time <= end_time
+ times << [time(DateTime.now.midnight + time.hours), time]
+ time += step
+ end
+ selectfield :time, value, times, args
+ end
+
+ def length_select(value = nil, args = {}, min_length = 0.5, max_length = 6, step = 0.5)
+ length = min_length
+ lengths = []
+ while length <= max_length
+ lengths << [time_length(length), length]
+ length += step
+ end
+ selectfield :time_span, value, lengths, args
+ end
+
+ def contact_reason_select
+ reasons = []
+ [:website, :conference].each do | reason |
+ reasons << [ _("forms.labels.generic.reasons.#{reason.to_s}"), reason ]
+ end
+ [['Something about the website', :website]]
+ selectfield :reason, nil, reasons, required: true, heading: 'articles.contact.headings.reason', label: false, full: true
+ end
+
+ def block_select(value = nil, args = {})
+ blocks = {}
+ @workshop_blocks.each_with_index do | info, block |
+ info['days'].each do | day |
+ blocks[(day.to_i * 10) + block] = [ "#{(I18n.t 'date.day_names')[day.to_i]} Block #{block + 1}", "#{day}:#{block}" ]
+ end
+ end
+ selectfield :workshop_block, value, blocks.sort.to_h.values, args
+ end
+
+ def location_select(value = nil, args = {})
+ locations = []
+ if @this_conference.event_locations.present?
+ @this_conference.event_locations.each do | location |
+ locations << [ location.title, location.id ] unless ((args[:invalid_locations] || []).include? location.id)
+ end
+ end
+ selectfield :event_location, value, locations, args
+ end
+
+ def location_name(id)
+ begin
+ location = EventLocation.find(id)
+ rescue
+ return ''
+ end
+ return '' unless location.present?
+ return location.title
+ end
+
+ def host_options_list(hosts)
+ options = [[nil, nil]]
+ hosts.each do | id, registration |
+ options << [registration.user.name, id]
+ end
+ return options
+ end
+
+ def registration_step_menu
+ steps = current_registration_steps(@registration)
+ return '' unless steps.present? && steps.length > 1
+
+ pre_registration_steps = ''
+ post_registration_steps = ''
+ post_registration = false
+
+ steps.each do | step |
+ text = _"articles.conference_registration.headings.#{step[:name].to_s}"
+
+ if step[:name] == :workshops
+ post_registration = true
+ end
+
+ h = content_tag :li, class: [step[:enabled] ? :enabled : nil, @register_template == step[:name] ? :current : nil, post_registration ? :post : :pre].compact do
+ if step[:enabled]
+ content_tag :div, (link_to text, register_step_path(@this_conference.slug, step[:name])).html_safe, class: :step
+ else
+ content_tag :div, text, class: :step
+ end
+ end
+
+ if post_registration
+ post_registration_steps += h.html_safe
+ else
+ pre_registration_steps += h.html_safe
+ end
+ end
+
+ html = (
+ row class: 'flow-steps' do
+ columns do
+ (content_tag :ul, id: 'registration-steps' do
+ pre_registration_steps.html_safe +
+ post_registration_steps.html_safe
+ end).html_safe
+ end
+ end
+ )
+
+ return html.html_safe
+ end
+
+ def broadcast_to(to, conference = nil)
+ conference ||= @this_conference || @conference
+
+ users = []
+
+ case to.to_sym
+ when :registered
+ ConferenceRegistration.where(conference_id: conference.id).each do | r |
+ users << r.user if ((r.steps_completed || []).include? 'questions') && r.user.present? && r.is_attending != 'n'
+ end
+ when :pre_registered
+ ConferenceRegistration.where(conference_id: conference.id).each do | r |
+ users << r.user if registration_status(r) == :preregistered && r.is_attending != 'n'
+ end
+ when :workshop_facilitators
+ user_hash = {}
+ Workshop.where(conference_id: conference.id).each do | w |
+ w.active_facilitators.each do | u |
+ user_hash[u.id] ||= u if u.present?
+ end
+ end
+ users = user_hash.values
+ when :unregistered
+ ConferenceRegistration.where(conference_id: conference.id).each do | r |
+ users << r.user if registration_status(r) == :unregistered && r.is_attending != 'n'
+ end
+ when :housing_providers
+ ConferenceRegistration.where(conference_id: conference.id, can_provide_housing: true).each do | r |
+ users << r.user if r.user.present?
+ end
+ when :guests
+ ConferenceRegistration.where(conference_id: conference.id, housing: 'house').each do | r |
+ users << r.user if r.user.present? && r.is_attending != 'n'
+ end
+ when :all
+ User.all.each do | u |
+ users << u if u.present? && (u.is_subscribed.nil? || u.is_subscribed)
+ end
+ end
+
+ puts "Users:"
+ users.each do | u |
+ puts "\t#{u.id}\t#{u.email}"
+ end
+ return users
+ end
+
+ def broadcast_options(conference = nil)
+ conference ||= @this_conference || @conference
+
+ options = [
+ :registered,
+ :pre_registered,
+ :workshop_facilitators,
+ :unregistered,
+ :housing_providers,
+ :guests,
+ :all
+ ]
+
+ if conference.registration_status != :open
+ options -= [:registered, :guests]
+ options -= [:pre_registered] unless conference.registration_status != :pre
+ end
+
+ return options
+ end
+
+ def admin_steps # deprecated
+ [:stats, :edit, :payment, :broadcast, :housing, :locations, :meals, :events, :workshop_times, :schedule]
+ end
+
+ def administration_steps
+ {
+ info: [:administrators, :dates, :description, :poster],
+ payment: [:payment_message, :suggested_amounts, :paypal],
+ registration: [:registration_status, :stats, :registrations, :broadcast],
+ housing: [:providers, :housing],
+ events: [:locations, :meals, :events],
+ schedule: [:workshop_times, :schedule, :publish_schedule]
+ }
+ end
+
+ def administration_sub_steps
+ {
+ location_edit: :locations,
+ event_edit: :events
+ }
+ end
+
+ def get_administration_group(administration_step)
+ admin_step = administration_step.to_sym
+ admin_step = administration_sub_steps[admin_step] if administration_sub_steps[admin_step].present?
+ administration_steps.each do | group, steps |
+ steps.each do | step |
+ return group if step == admin_step
+ end
+ end
+
+ return nil
+ end
+
+ def valid_admin_steps # deprecated
+ admin_steps + [:broadcast_sent, :organizations]
+ end
+
+ def admin_menu
+ steps = ''
+ first = true
+ admin_steps.each do | step |
+ steps += content_tag(:li, class: (step.to_s == @admin_step ? :current : nil)) do
+ link_to _("menu.submenu.admin.#{step.to_s.titlecase.gsub(/\s/, '_')}"), first ?
+ register_step_path(@this_conference.slug, :administration) :
+ administration_step_path(@this_conference.slug, step.to_s)
+ end
+ first = false
+ end
+ content_tag :ul, steps.html_safe, id: 'registration-admin-menu'
+ end
+
+ def admin_update_form(options = {}, &block)
+ form_tag(administration_update_path(@this_conference.slug, @admin_step), options, &block)
+ end
+
+ def interest_button(workshop)
+ interested = workshop.interested?(current_user) ? :remove_interest : :show_interest
+ id = "#{interested.to_s.gsub('_', '-')}-#{workshop.id}"
+ return (off_screen (_"forms.actions.aria.#{interested.to_s}"), id) +
+ (button_tag interested, :value => :toggle_interest, :class => (workshop.interested?(current_user) ? :delete : :add), aria: { labelledby: id })
+ end
+
+ def interest_text(workshop)
+ if workshop.interested?(current_user)
+ return _'articles.workshops.info.you_are_interested_count', :vars => {:count => (workshop.interested_count - 1)}
+ end
+
+ return _'articles.workshops.info.interested_count', :vars => {:count => workshop.interested_count}
+ end
+
+ def host_guests_table(registration)
+ id = registration.id
+ html = ''
+
+ @housing_data[id][:guests].each do | area, guests |
+ guest_rows = ''
+ guests.each do | guest_id, guest |
+ status_html = ''
+
+ @housing_data[id][:guest_data][guest_id][:errors].each do | error, value |
+ if value.is_a?(Array)
+ value.each do | v |
+ status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: v))
+ end
+ else
+ status_html += content_tag(:li, _("errors.messages.housing.space.#{error.to_s}", vars: value))
+ end
+ end
+
+ @housing_data[id][:guest_data][guest_id][:warnings].each do | error, value |
+ if value.is_a?(Array)
+ value.each do | v |
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", v))
+ end
+ else
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{error.to_s}", vars: value))
+ end
+ end
+
+ if status_html.present?
+ status_html = content_tag(:ul, status_html.html_safe)
+ end
+
+ guest_rows += content_tag :tr, id: "hosted-guest-#{guest_id}" do
+ (content_tag :td, guest[:guest].user.name) +
+ (content_tag :td do
+ (guest[:guest].city +
+ (content_tag :a, (_'actions.workshops.Remove'), href: '#', class: 'remove-guest', data: { guest: guest_id })).html_safe
+ end) +
+ (content_tag :td, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
+ end
+ end
+
+ space_size = (@housing_data[id][:space][area] || 0)
+
+ # add empty rows to represent empty guest spots
+ for i in guests.size...space_size
+ guest_rows += content_tag :tr, class: 'empty-space' do
+ (content_tag :td, ' '.html_safe, colspan: 2) +
+ (content_tag :td)
+ end
+ end
+
+ status_html = ''
+ if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
+ @housing_data[id][:warnings][:space][area].each do | w |
+ status_html += content_tag(:li, _("warnings.messages.housing.space.#{w.to_s}"))
+ end
+ end
+ if status_html.present?
+ status_html = content_tag(:ul, status_html.html_safe)
+ end
+
+ html += content_tag :tr do
+ (content_tag :th, (_"forms.labels.generic.#{area}"), colspan: 2) +
+ (content_tag :th, status_html.html_safe, class: [:state, status_html.present? ? :unhappy : :happy])
+ end
+ html += guest_rows
+ html += content_tag :tr, class: 'place-guest' do
+ content_tag :td, class: guests.size >= space_size ? 'full' : nil, colspan: 3 do
+ content_tag :a, (_'forms.actions.generic.place_guest'), class: 'select-guest', href: '#', data: { host: id, space: area }
+ end
+ end
+ end
+
+ content_tag :table, html.html_safe, class: 'host-table'
+ end
+
+ def get_housing_match(host, guest, space)
+ housing_data = guest.housing_data || []
+
+ if housing_data['host'].present?
+ if housing_data['host'] == host.id
+ return space == housing_data['space'] ? :selected_space : :other_space
+ end
+
+ return :other_host
+ end
+
+ if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
+ return :good_match
+ end
+
+ return :bad_match
+ end
+
+ def get_workshop_match(workshop, day, block, location)
+ if workshop.event_location_id.present? && workshop.present?
+ if (Date.parse params[:day]).wday == workshop.block['day'] && block == workshop.block['block'].to_i
+ return :selected_space
+ end
+
+ if location.present? && location.id == workshop.event_location_id
+ return :other_space
+ end
+
+ return :other_host
+ end
+
+ if location.present?
+ needs = JSON.parse(workshop.needs || '[]').map &:to_sym
+ amenities = JSON.parse(location.amenities || '[]').map &:to_sym
+
+ if (needs & amenities).length < needs.length
+ return :bad_match
+ end
+ end
+
+ (((((@schedule[@day] || {})[:times] || {})[@time] || {})[:item] || {})[:workshops] || {}).each do | l, w |
+ if w[:workshop].id != workshop.id
+ f_a = w[:workshop].active_facilitators.map { | f | f.id }
+ f_b = workshop.active_facilitators.map { | f | f.id }
+ if (f_a & f_b).present?
+ return :bad_match
+ end
+ end
+ end
+
+ # housing_data = guest.housing_data || []
+
+ # if housing_data['host'].present?
+ # if housing_data['host'] == host.id
+ # return space == housing_data['space'] ? :selected_space : :other_space
+ # end
+
+ # return :other_host
+ # end
+
+ # if space_matches?(space, guest.housing) && available_dates_match?(host, guest)
+ # return :good_match
+ # end
+
+ # return :bad_match
+ return :good_match
+ end
+
+ def space_matches?(host_space, guest_space)
+ return false unless host_space.present? && guest_space.present?
+
+ if host_space.to_s == 'bed_space' || host_space.to_s == 'floor_space'
+ return guest_space.to_s == 'house'
+ end
+
+ return host_space.to_s == 'tent_space' && guest_space.to_s == 'tent'
+ end
+
+ def available_dates_match?(host, guest)
+ return false unless host.housing_data['availability'].present? && host.housing_data['availability'][1].present?
+ if host.housing_data['availability'][0] <= guest.arrival &&
+ host.housing_data['availability'][1] >= guest.departure
+ return true
+ end
+
+ return false
+ end
+
+ def host_guests_widget(registration)
+ html = ''
+ classes = ['host']
+
+ id = registration.id
+ @housing_data[id][:guests].each do | area, guests |
+ max_space = @housing_data[id][:space][area] || 0
+ area_name = (_"forms.labels.generic.#{area}")
+ status_html = ''
+ if @housing_data[id][:warnings].present? && @housing_data[id][:warnings][:space].present? && @housing_data[id][:warnings][:space][area].present?
+ @housing_data[id][:warnings][:space][area].each do | w |
+ status_html += content_tag(:div, _("warnings.housing.space.#{w.to_s}"), class: 'warning')
+ end
+ end
+ space_html = content_tag(:h5, area_name + _!(" (#{guests.size.to_s}/#{max_space.to_s})") + status_html.html_safe)
+ guest_items = ''
+ guests.each do | guest_id, guest |
+ guest_items += content_tag(:li, guest[:guest].user.name, id: "hosted-guest-#{guest_id}")
+ end
+ space_html += content_tag(:ul, guest_items.html_safe)
+ space_html += button_tag :place_guest, type: :button, value: "#{area}:#{id}", class: [:small, 'place-guest', 'on-top-only', guests.size >= max_space ? (guests.size > max_space ? :overbooked : :booked) : nil, max_space > 0 ? nil : :unwanted]
+ html += content_tag(:div, space_html, class: [:space, area, max_space > 0 || guests.size > 0 ? nil : 'on-top-only'])
+ end
+
+ classes << 'status-warning' if @housing_data[id][:warnings].present?
+ classes << 'status-error' if @housing_data[id][:errors].present?
+
+ return { html: html.html_safe, class: classes.join(' ') }
+ end
+
+ def signin_link
+ @login_dlg ||= true
+ link_to (_'forms.actions.generic.login'), settings_path, data: { 'sign-in': true }
+ end
+
+ def link_with_confirmation(link_text, confirmation_text, path, args = {})
+ @confirmation_dlg ||= true
+ args[:data] ||= {}
+ args[:data][:confirmation] = true
+ link_to path, args do
+ (link_text.to_s + content_tag(:template, confirmation_text, class: 'message')).html_safe
+ end
+ end
+
+ def link_info_dlg(link_text, info_text, info_title, args = {})
+ @info_dlg ||= true
+ args[:data] ||= {}
+ args[:data]['info-title'] = info_title
+ args[:data]['info-text'] = true
+ content_tag(:a, args) do
+ (link_text.to_s + content_tag(:template, info_text, class: 'message')).html_safe
+ end
+ end
+
+ def button_with_confirmation(button_name, confirmation_text = nil, args = {})
+ if confirmation_text.is_a? Hash
+ args = confirmation_text
+ confirmation_text = nil
+ end
+
+ confirmation_text ||= (_"forms.confirmations.#{button_name.to_s}", :p)
+ @confirmation_dlg ||= true
+ args[:data] ||= {}
+ args[:data][:confirmation] = true
+ button_tag button_name, args do
+ ((_"forms.actions.generic.#{button_name.to_s}") + content_tag(:template, confirmation_text, class: 'message')).html_safe
+ end
+ end
+
+ def richtext(text, reduce_headings = 2)
+ return '' unless text.present?
+ return _!(text).
+ gsub(/<(\/?)h4>/, '<\1h' + (reduce_headings + 4).to_s + '>').
+ gsub(/<(\/?)h3>/, '<\1h' + (reduce_headings + 3).to_s + '>').
+ gsub(/<(\/?)h2>/, '<\1h' + (reduce_headings + 2).to_s + '>').
+ gsub(/<(\/?)h1>/, '<\1h' + (reduce_headings + 1).to_s + '>').
+ html_safe
+ end
+
+ def truncate(text)
+ strip_tags(text.gsub('>', '> ')).gsub(/^(.{40,60})\s.*$/m, '\1…').html_safe
+ end
+
+ def translate_fields(object, field_options = {}, options = {})
+ html = ''
+ nav = ''
+
+ # set the selected locale
+ selected_locale = (options[:locale] || object.locale || I18n.locale).to_sym
+
+ I18n.backend.enabled_locales.each do | locale |
+ # ses if this should b the selected field
+ class_name = selected_locale == locale.to_sym ? 'selected' : nil
+
+ # add the locale to the nav
+ nav += content_tag(:li,
+ content_tag(:a, _("languages.#{locale}"), href: 'javascript:void(0)'),
+ class: class_name, data: { locale: locale }).html_safe
+
+ fields = ''
+ field_options.each do | name, __options |
+ _options = __options.deep_dup
+ # add the field
+ value = object.is_a?(Hash) ? object[locale.to_sym] : object.get_column_for_locale!(name, locale)
+
+ # use the default value if we need to
+ if _options[:default].present? && value.blank?
+ value = _(_options[:default], locale: locale)
+ end
+
+ _options[:index] = locale
+ _options[:lang] = locale
+ _options[:parent_options] = { lang: locale }
+ type = "#{_options[:type].to_s}"
+ _options.delete(:type)
+
+ fields += self.send(type, name, value, _options).html_safe
+ end
+
+ html += content_tag(:li, fields.html_safe, class: class_name, data: { locale: locale }).html_safe
+ end
+
+ if options[:class].nil?
+ options[:class] = []
+ elsif options[:class].is_a?(String)
+ options[:class] = [options[:class]]
+ end
+ options[:class] += ['translator', 'multi-field-translator']
+
+ (fieldset(nil, options) do
+ content_tag(:ul, nav.html_safe, class: 'locale-select').html_safe +
+ content_tag(:ul, html.html_safe, class: 'text-editors').html_safe
+ end).html_safe
+ end
+
+ def translate_textarea(name, object, property = nil, options = {})
+ html = ''
+ nav = ''
+
+ # see if options was passed in as property
+ if options.blank? && property.is_a?(Hash)
+ options = property
+ property = nil
+ end
+
+ # set the selected locale
+ selected_locale = (options[:locale] || object.locale || I18n.locale).to_sym
+
+ I18n.backend.enabled_locales.each do | locale |
+ # ses if this should b the selected field
+ class_name = selected_locale == locale.to_sym ? 'selected' : nil
+
+ # add the locale to the nav
+ nav += content_tag(:li,
+ content_tag(:a, _("languages.#{locale}"), href: 'javascript:void(0)'),
+ class: class_name, data: { locale: locale }).html_safe
+
+ # add the field
+ value = object.is_a?(Hash) ? object[locale.to_sym] : object.get_column_for_locale!(name, locale)
+
+ # use the default value if we need to
+ if options[:default].present? && value.blank?
+ value = _(options[:default], locale: locale)
+ end
+
+ html += content_tag(:li, textarea(name, value, {
+ label: false,
+ edit_on: options[:edit_on],
+ parent_options: {
+ lang: locale
+ },
+ index: locale
+ }).html_safe, class: class_name, data: { locale: locale }).html_safe
+ end
+
+ if options[:class].nil?
+ options[:class] = []
+ elsif options[:class].is_a?(String)
+ options[:class] = [options[:class]]
+ end
+ options[:class] += ['translator']
+
+ (fieldset(name, options) do
+ content_tag(:ul, nav.html_safe, class: 'locale-select').html_safe +
+ content_tag(:ul, html.html_safe, class: 'text-editors').html_safe
+ end).html_safe
+ end
+
+ def textarea(name, value = nil, options = {})
+ id = unique_id(name)
+ label_id = "#{id}-label"
+ description_id = nil
+ html = ''
+
+ if options[:heading].present?
+ label_id = "#{name.to_s}-label" unless options[:label]
+ html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: label_id)
+ end
+
+ if options[:label] == false
+ label_id = options[:labelledby]
+ elsif options[:label].present?
+ html += label_tag(id, nil, id: label_id) do
+ _(options[:label], :t, vars: options[:vars] || {})
+ end
+ else
+ html += label_tag(id, nil, id: label_id)
+ end
+
+ if options[:help].present?
+ description_id ||= "#{id}-desc"
+ html += content_tag(:div, _(options[:help], :s, 2), id: description_id, class: 'input-field-help')
+ end
+
+ if options[:warning].present?
+ description_id ||= "#{id}-desc"
+ html += content_tag(:div, _(options[:warning], :s, 2), id: description_id, class: 'warning-info')
+ end
+
+ aria = {}
+ aria[:labelledby] = label_id if label_id.present?
+ aria[:describedby] = description_id if description_id.present?
+ css_class = [
+ options[:short] === true ? :short : nil
+ ].compact
+
+ html_name = name.to_s + (options[:index] ? "[#{options[:index]}]" : '')
+ if options[:plain]
+ html += (text_area_tag html_name, value,
+ id: id,
+ lang: options[:lang],
+ aria: aria,
+ class: css_class
+ )
+ else
+ html += content_tag(:div, value.present? ? value.html_safe : '',
+ id: id,
+ data: { name: html_name, 'edit-on': options[:edit_on] || :load },
+ lang: options[:lang],
+ aria: aria,
+ tabindex: 0,
+ class: [:textarea] + css_class
+ )
+
+ add_stylesheet :editor
+ add_inline_script :pen
+ add_inline_script :markdown
+ add_inline_script :editor
+ end
+
+ parent_options = options[:parent_options] || {}
+ if parent_options[:class].nil?
+ parent_options[:class] = []
+ elsif parent_options[:class].is_a?(String)
+ parent_options[:class] = [parent_options[:class]]
+ end
+
+ parent_options[:class] += ['text-area-field', 'input-field']
+ html = content_tag(:div, html.html_safe, parent_options).html_safe
+ html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present?
+
+ return html.html_safe
+ end
+
+ def fieldset(name = nil, options = {}, &block)
+ html = ''
+ label = ''
+ description = ''
+ description_id = nil
+ errors = ''
+
+ if name.present?
+ if options[:label] != false
+ label = content_tag(:legend,
+ _((
+ options[:label].is_a?(String) ?
+ options[:label] :
+ "forms.labels.generic.#{name}"), :t, vars: options[:vars] || {}))
+ end
+
+ if options[:help].present?
+ description_id = unique_id("#{name.to_s}-desc")
+ description = content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
+ end
+
+ errors = (show_errors name)
+ end
+
+ html = label + errors + description + content_tag(:div, class: :fieldgroup, &block)
+
+ aria = description_id.present? ? { describedby: description_id } : nil
+ (content_tag(:fieldset, html.html_safe,
+ aria: aria,
+ class: ((options[:class] || []) + [
+ options[:inline] ? :inline : nil,
+ options[:inline_label] ? 'inline-label' : nil,
+ errors.present? ? 'has-error' : nil
+ ]).compact
+ )
+ ).html_safe
+ end
+
+ def selectfield(name, value, select_options, options = {})
+ unless select_options.first.is_a?(Array)
+ so = select_options
+ select_options = []
+ so.each do | opt |
+ select_options << [ I18n.t("forms.options.#{name.to_s}.#{opt.to_s}"), opt]
+ end
+ end
+ textfield(name, value, options.merge({type: :select, options: select_options}))
+ end
+
+ def telephonefield(name, value, options = {})
+ textfield(name, value, options.merge({type: :telephone}))
+ end
+
+ def numberfield(name, value, options = {})
+ textfield(name, value, options.merge({type: :number}))
+ end
+
+ def searchfield(name, value, options = {})
+ textfield(name, value, options.merge({type: :search}))
+ end
+
+ def userfield(name, value, options = {})
+ # eventually this will be a dynamic field to find users, for now we'll just use emails
+ # add_inline_script :userfield
+ emailfield(name, value, options)# .merge({
+ # parent_options: { class: ['user-field'] },
+ # after: content_tag(:div, '', class: 'user-name')
+ # }))
+ end
+
+ def emailfield(name, value, options = {})
+ textfield(name, value, options.merge({type: :email}))
+ end
+
+ def filefield(name, value, options = {})
+ textfield(name, value, options.merge({type: :file}))
+ end
+
+ def passwordfield(name, value, options = {})
+ textfield(name, value, options.merge({type: :password}))
+ end
+
+ def textfield(name, value, options = {})
+ html = ''
+ id = unique_id(name)
+ description_id = nil
+
+ if options[:heading].present?
+ description_id ||= "#{id.to_s}-desc"
+ html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: description_id)
+ end
+
+ if options[:help].present?
+ description_id ||= "#{id.to_s}-desc"
+ html += content_tag(:div, _(options[:help], :s, 2, vars: options[:vars] || {}), class: 'input-field-help', id: description_id)
+ end
+
+ html += show_errors name, value
+
+ inside_label = ''
+
+ if options[:type] == :file
+ inside_label = (content_tag(:div, class: 'file-field-selector') do
+ (options[:preview] ? content_tag(:img, nil, src: value.present? ? value.url : nil).html_safe : '').html_safe +
+ content_tag(:div, (value.present? ? File.basename(value.url) : (_'forms.labels.generic.no_file_selected')), class: 'file-field-name ' + (value.present? ? 'selected' : 'unselected')).html_safe +
+ content_tag(:a, (_'forms.actions.generic.select_file'), class: :button)
+ end)
+ end
+
+ label_text = nil
+ if options[:label].present?
+ label_text = _(options[:label], :t, vars: options[:vars] || {})
+ elsif options[:label] != false
+ label_text = (_"forms.labels.generic.#{name}")
+ elsif options[:type] == :select || options[:type] == :file
+ # add an empty label so that the drop down button will still appear
+ label_text = ''
+ end
+
+ label_options = {}
+ # let the label be selected if the input is hidden
+ label_options[:tabindex] = 0 if options[:type] == :file
+
+ unless label_text.nil?
+ html += label_tag id, (label_text + inside_label).html_safe
+ end
+
+ input_options = {
+ id: id,
+ required: options[:required],
+ lang: options[:lang],
+ min: options[:min],
+ max: options[:max],
+ step: options[:step],
+ aria: description_id ? { describedby: description_id } : nil
+ }
+
+ case name
+ when :address
+ input_options[:autocomplete] = 'address-line1'
+ when :name
+ input_options[:autocomplete] = 'name'
+ when :location
+ input_options[:autocomplete] = 'address-level2'
+ when :email
+ input_options[:autocomplete] = 'email'
+ when :phone
+ input_options[:autocomplete] = 'tel'
+ when :paypal_email_address, :paypal_username, :paypal_password, :paypal_signature
+ input_options[:autocomplete] = 'off'
+ end
+
+ case options[:type]
+ when :select
+ option_list = options_for_select(options[:options], value)
+
+ # make sure that we have an empty option if the select is required
+ if options[:required] && options[:options].first.present? && options[:options].first.last.present?
+ option_list = (' ' + option_list).html_safe
+ end
+ html += select_tag(name, option_list, input_options)
+ when :file
+ add_inline_script :filefield
+ input_options[:tabindex] = '-1'
+ html += off_screen(file_field_tag name, input_options)
+ else
+ input_options[:autocomplete] = 'off' if options[:type] == :search
+ html += send("#{(options[:type] || :text).to_s}_field_tag", name, value, input_options)
+ end
+
+ if options[:after].present?
+ html += options[:after].html_safe
+ end
+
+ html = content_tag(:div, html.html_safe,
+ class: [
+ "#{(options[:type] || :text).to_s}-field",
+ 'input-field',
+ value.present? ? nil : 'empty',
+ options[:big] ? 'big' : nil,
+ options[:small] ? 'small' : nil,
+ options[:stretch] ? 'stretch-item' : nil,
+ options[:full] ? 'full' : nil,
+ options[:inline_label] ? 'inline-label' : nil,
+ (@errors || {})[name].present? ? 'has-error' : nil
+ ].compact + (((options[:parent_options] || {})[:class]) || []))
+
+ html += _original_content(options[:original_value], options[:original_lang]) if options[:original_value].present?
+
+ return html.html_safe
+ end
+
+ def radiobuttons(name, boxes, value, label_key, options = {})
+ checkboxes(name, boxes, [value], label_key, options.merge({radiobuttons: true}))
+ end
+
+ def checkbox(name, value, label_key, options = {})
+ checkboxes(name, [true], value, label_key, options)
+ end
+
+ def unique_id(id)
+ id = id.to_s.gsub('[', '_').gsub(']', '')
+
+ @_ids ||= {}
+ @_ids[id] ||= 0
+
+ new_id = id
+
+ if @_ids[id] > 0
+ new_id += "--#{@_ids[id]}"
+ end
+
+ @_ids[id] += 1
+
+ return new_id
+ end
+
+ def checkboxes(name, boxes, values, label_key, options = {})
+ html = ''
+
+ label_id = nil
+ description_id = nil
+
+ if options[:heading].present?
+ label_id ||= unique_id("#{name.to_s}-label")
+ html += content_tag(:h3, _(options[:heading], :t, vars: options[:vars] || {}), id: label_id)
+ end
+
+ help = nil
+
+ if options[:help].present?
+ description_id ||= unique_id("#{name.to_s}-desc")
+ help = content_tag(:div, _(options[:help], :s, 2), class: 'input-field-help', id: description_id)
+ end
+
+ html += help if help.present? && !options[:right_help]
+
+ boxes_html = ''
+
+ labels = nil
+ is_single = !values.is_a?(Array)
+ if boxes.length > 0
+ if boxes.first.is_a?(Array)
+ labels = boxes.map(&:first)
+ boxes = boxes.map(&:last)
+ end
+ elsif !boxes.first.is_a?(Integer)
+ values = values.present? ? values.map(&:to_s) : [] unless is_single
+ boxes = boxes.map(&:to_s)
+ end
+
+ # convert the required value into a pure boolean
+ required = !!options[:required]
+
+ boxes.each_with_index do | box, i |
+ checked = (is_single ? values.present? : values.include?(box))
+ values -= [box] if checked && !is_single
+ id = nil
+ if options[:radiobuttons].present?
+ id = unique_id("#{name.to_s}_#{box}")
+ boxes_html += radio_button_tag(name, box, checked, id: id, required: required)
+ else
+ _name = (is_single ? name : "#{name.to_s}[#{box}]")
+ id = unique_id(_name)
+ boxes_html += check_box_tag(_name, 1, checked, data: { toggles: options[:toggles] }.compact, id: id, required: required)
+ end
+
+ # we only need the required attribute on one element
+ required = false
+
+ if labels.present?
+ label = labels[i]
+ elsif is_single
+ label = _(label_key.to_s)
+ elsif box.is_a?(Integer)
+ label = I18n.t(label_key.to_s)[box]
+ else
+ label = _("#{label_key.to_s}.#{box}")
+ end
+
+ boxes_html += label_tag(id, label)
+ end
+
+ if options[:other].present? && !is_single
+ id = nil
+ if options[:radiobuttons].present?
+ id = unique_id("#{name.to_s}_other")
+ boxes_html += radio_button_tag(name, :other, values.present?, id: id)
+ else
+ _name = "#{name.to_s}[other]"
+ id = unique_id(_name)
+ boxes_html += check_box_tag(_name, 1, values.present?, id: id)
+ end
+ boxes_html += label_tag id,
+ content_tag(:div,
+ text_field_tag("other_#{name.to_s}", values.first, placeholder: (_"#{label_key}.other"), required: values.present?),
+ class: 'other')
+ end
+
+ html += content_tag(:fieldset, content_tag(:div, boxes_html.html_safe,
+ class: [
+ 'check-box-field',
+ 'input-field',
+ options[:vertical] ? 'vertical' : nil,
+ options[:inline] ? 'inline' : nil,
+ options[:small] ? 'small' : nil,
+ options[:big] ? 'big' : nil
+ ].compact).html_safe,
+ aria: {
+ labelledby: label_id,
+ describedby: description_id
+ },
+ class: [
+ options[:centered] ? 'centered' : nil,
+ options[:right_help] ? 'right-help' : nil
+ ].compact
+ )
+
+ html += help if help.present? && options[:right_help]
+
+ return html.html_safe
+ end
+
+ def companion(registration)
+ if registration.housing_data.present? && registration.housing_data['companions'].present? && registration.housing_data['companions'].first.present?
+ companion_user = User.find_by_email(registration.housing_data['companions'].first)
+
+ if companion_user.present?
+ cr = ConferenceRegistration.where(user_id: companion_user.id).order(created_at: :desc).limit(1).first
+
+ if cr.present? && ((cr.steps_completed || []).include? 'questions')
+ return companion_user
+ end
+ end
+ return :unregistered
+ end
+ return nil
+ end
+
+ def comment(comment)
+ add_inline_script :time
+ add_js_translation('datetime.distance_in_words')
+
+ content_tag(:div, class: 'comment-body') do
+ content_tag(:h4, comment.user.name, class: 'comment-title') +
+ content_tag(:time, time(comment.created_at, :default), datetime: comment.created_at.to_s) +
+ content_tag(:div, class: 'comment-text') do
+ markdown comment.comment
+ end
+ end
+ end
+
+ def html_edit_table(excel_data, options = {})
+ attributes = { class: options[:class], id: options[:id] }
+ attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
+
+ if options[:column_names].is_a? Hash
+ return content_tag(:table, attributes) do
+ max_columns = 0
+ column_names = {}
+ (content_tag(:thead) do
+ headers = ''
+ options[:column_names].each do | header_name, columns |
+ column_names[header_name] ||= []
+ headers += content_tag(:th, excel_data[:keys][header_name].present? ? _(excel_data[:keys][header_name]) : '', colspan: 2)
+ row_count = columns.size
+ columns.each do | column |
+ column_names[header_name] << column
+ if (options[:row_spans] || {})[column].present?
+ row_count += (options[:row_spans][column] - 1)
+ for i in 1...options[:row_spans][column]
+ column_names[header_name] << false
+ end
+ end
+ end
+ max_columns = row_count if row_count > max_columns
+ end
+ content_tag(:tr, headers.html_safe)
+ end) + (content_tag(:tbody) do
+ rows = ''
+
+ for i in 0...max_columns
+ columns_html = ''
+ column_names.each do | header_name, columns |
+ column = columns[i]
+ if column.present?
+ attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
+ if (options[:row_spans] || {})[column].present?
+ attributes[:rowspan] = options[:row_spans][column]
+ end
+ columns_html += content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '', rowspan: attributes[:rowspan]) +
+ edit_column(nil, column, nil, attributes, excel_data, options)
+ elsif column != false
+ columns_html += content_tag(:td, ' ', colspan: 2, class: :empty)
+ end
+ end
+ rows += content_tag(:tr, columns_html.html_safe, { class: 'always-edit', data: { key: '' } })
+ end
+ rows.html_safe
+ end)
+ end
+ else
+ return content_tag(:table, attributes) do
+ (content_tag(:tbody) do
+ rows = ''
+ excel_data[:columns].each do |column|
+ if (excel_data[:column_types] || {})[column] != :table && ((options[:column_names] || []).include? column)
+ rows += content_tag(:tr, { class: 'always-edit', data: { key: '' } }) do
+ attributes = { class: [excel_data[:column_types][column]], data: { 'column-id' => column } }
+ columns = content_tag(:th, excel_data[:keys][column].present? ? _(excel_data[:keys][column]) : '') +
+ edit_column(nil, column, nil, attributes, excel_data, options)
+ end
+ end
+ end
+ rows.html_safe
+ end)
+ end
+ end
+ end
+
+ def html_table(excel_data, options = {})
+ options[:html] = true
+ attributes = { class: options[:class], id: options[:id] }
+ attributes[:data] = { 'update-url' => options[:editable] } if options[:editable].present?
+ content_tag(:table, attributes) do
+ (content_tag(:thead) do
+ content_tag(:tr, excel_header_columns(excel_data))
+ end) +
+ content_tag(:tbody, excel_rows(excel_data, {}, options))
+ end
+ end
+
+ def excel_table(excel_data)
+ format_xls 'table' do
+ workbook use_autowidth: true
+ format bg_color: '333333'
+ format 'td', font_name: 'Calibri', fg_color: '333333'
+ format 'th', font_name: 'Calibri', b: true, bg_color: '333333', fg_color: 'ffffff'
+ format 'th.sub-table', font_name: 'Calibri', b: true, bg_color: 'DDDDDD', fg_color: '333333'
+ format 'td.datetime', num_fmt: 22, font_name: 'Courier New', sz: 10, fg_color: '333333'
+ format 'td.date.day', num_fmt: 14, font_name: 'Courier New', sz: 10, fg_color: '333333'
+ format 'td.money', num_fmt: 2, font_name: 'Courier New', sz: 10, fg_color: '333333'
+ format 'td.number', font_name: 'Courier New', sz: 10, fg_color: '333333'
+ format 'td.bold', font_name: 'Calibri', fg_color: '333333', b: true
+ end
+
+ content_tag(:table) do
+ (content_tag(:thead) do
+ content_tag(:tr, excel_header_columns(excel_data))
+ end) +
+ content_tag(:tbody, excel_rows(excel_data))
+ end
+ end
+
+ def excel_header_columns(data, padding = {}, class_name = nil)
+ columns = ''
+
+ data[:columns].each do |column|
+ unless data[:column_types].present? && data[:column_types][column] == :table
+ columns += content_tag(:th, data[:keys][column].present? ? _(data[:keys][column]) : '', class: class_name)
+ end
+ end
+
+ pad_columns(columns, padding, :th)
+ end
+
+ def excel_empty_row(data, padding = {})
+ columns = ''
+
+ data[:columns].each do |column|
+ unless data[:column_types].present? && data[:column_types][column] == :table
+ columns += content_tag(:td)
+ end
+ end
+
+ content_tag(:tr, pad_columns(columns, padding))
+ end
+
+ def pad_columns(columns, padding, column_type = :td)
+ left = ''
+
+ for i in 1..(padding['left'] || 0)
+ left += content_tag(:td)
+ end
+
+ right = ''
+ for i in 1..(padding['right'] || 0)
+ right += content_tag(:td)
+ end
+
+ (left + columns + right).html_safe
+ end
+
+ def excel_columns(row, data, padding = {}, options = {})
+ columns = ''
+
+ data[:columns].each do |column|
+ value = row[column].present? ? (_!row[column].to_s) : ''
+ class_name = nil
+ is_sub_table = false
+
+ if data[:column_types].present? && data[:column_types][column].present?
+ if data[:column_types][column] == :table
+ is_sub_table = true
+ else
+ class_name = data[:column_types][column]
+ end
+ end
+
+ unless is_sub_table
+ attributes = { class: [class_name] }
+ if options[:html] && row[:html_values].present? && row[:html_values][column].present?
+ value = row[:html_values][column]
+ end
+
+ if options[:editable]
+ attributes[:data] = { 'column-id' => column }
+ end
+
+ if (options[:column_names] || []).include? column
+ attributes[:tabindex] = 0
+ end
+
+ columns += content_tag(:td, value, attributes)
+ end
+ end
+
+ pad_columns(columns, padding)
+ end
+
+ def editor_columns(row, data, padding = {}, options = {})
+ columns = ''
+
+ data[:columns].each do |column|
+ value = row[column].present? ? (_!row[column].to_s) : ''
+ class_name = nil
+ is_sub_table = false
+
+ if data[:column_types].present? && data[:column_types][column].present?
+ if data[:column_types][column] == :table
+ is_sub_table = true
+ else
+ class_name = data[:column_types][column]
+ end
+ end
+
+ unless is_sub_table
+ attributes = { class: [class_name] }
+
+ if options[:editable]
+ attributes[:data] = { 'column-id' => column }
+ end
+
+ if (options[:column_names] || []).include? column
+ columns += edit_column(row, column, value, attributes, data, options)
+ else
+ columns += content_tag(:td, value, attributes)
+ end
+
+ end
+ end
+
+ pad_columns(columns, padding)
+ end
+
+ def edit_column(row, column, value, attributes, data, options)
+ attributes[:class] << 'has-editor'
+ raw_value = row.present? ? (row[:raw_values][column] || value) : nil
+
+ if row.present? && options[:html] && row[:html_values].present? && row[:html_values][column].present?
+ value = row[:html_values][column]
+ end
+
+ editor_attributes = { class: 'cell-editor', data: { value: raw_value.to_s } }
+
+ # create the control but add the original value to set the width and height
+ editor_value = content_tag(:div, value, class: 'value')
+ if (options[:column_options] || {})[column].present?
+ value = (editor_value.html_safe + select_tag(column, options_for_select([['', '']] + options[:column_options][column], raw_value), editor_attributes)).html_safe
+ elsif data[:column_types][column] == :text
+ editor_attributes[:name] = column
+ value = (editor_value.html_safe + content_tag(:textarea, raw_value, editor_attributes)).html_safe
+ else
+ editor_attributes[:name] = column
+ editor_attributes[:value] = raw_value
+ editor_attributes[:required] = :required if (options[:required_columns] || []).include? column
+ type = data[:column_types][column] || :unknown
+ editor_attributes[:type] = { money: :number, number: :number, email: :email }[type] || :text
+ value = (editor_value.html_safe + content_tag(:input, nil, editor_attributes)).html_safe
+ end
+
+ return content_tag(:td, value, attributes)
+ end
+
+ def excel_sub_tables(row, data, padding = {}, options = {})
+ rows = ''
+
+ # shift the table right
+ new_padding = {
+ 'left' => (padding['right'] || 0) + 1,
+ 'right' => (padding['right'] || 0) - 1
+ }
+
+ data[:columns].each do |column|
+ if data[:column_types].present? && data[:column_types][column] == :table
+ rows += content_tag(:tr, excel_header_columns(row[column], new_padding, 'sub-table'))
+ rows += excel_rows(row[column], new_padding)
+ rows += excel_empty_row(row[column], new_padding)
+ end
+
+ end
+
+ rows.html_safe
+ end
+
+ def excel_rows(data, padding = {}, options = {})
+ rows = ''
+ data[:data].each do |row|
+ attributes = {}
+
+ if options[:primary_key].present?
+ attributes[:data] = { key: row[options[:primary_key]] }
+ end
+
+ attributes[:class] = []
+
+ if options[:editable]
+ attributes[:class] << :editable
+ end
+
+ rows += content_tag(:tr, excel_columns(row, data, padding, options), attributes) +
+ excel_sub_tables(row, data, padding)
+ rows += content_tag(:tr, editor_columns(row, data, padding, options), class: :editor) if options[:editable]
+ end
+ rows.html_safe
+ end
+
+ def registrations_edit_table_options
+ {
+ id: 'create-table',
+ class: ['registrations', 'admin-edit', 'always-editing'],
+ primary_key: :id,
+ column_names: {
+ contact_info: [
+ :name,
+ :email,
+ :is_subscribed,
+ :city,
+ :preferred_language
+ ] + User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym },
+ questions: [
+ :registration_fees_paid,
+ :is_attending,
+ :arrival,
+ :departure,
+ :housing,
+ :bike,
+ :food,
+ :companion_email,
+ :allergies,
+ :other
+ ],
+ hosting: [
+ :can_provide_housing,
+ :address,
+ :phone,
+ :first_day,
+ :last_day
+ ] + ConferenceRegistration.all_spaces +
+ ConferenceRegistration.all_considerations + [
+ :notes
+ ]
+ },
+ row_spans: {
+ allergies: 3,
+ other: 2
+ },
+ required_columns: [:name, :email],
+ editable: administration_update_path(@this_conference.slug, @admin_step),
+ column_options: @column_options
+ }
+ end
+
+ def registrations_table_options
+ {
+ id: 'search-table',
+ class: ['registrations', 'admin-edit'],
+ primary_key: :id,
+ column_names: [
+ :registration_fees_paid,
+ :is_attending,
+ :is_subscribed,
+ :city,
+ :preferred_language,
+ :arrival,
+ :departure,
+ :housing,
+ :bike,
+ :food,
+ :companion_email,
+ :allergies,
+ :other,
+ :can_provide_housing,
+ :address,
+ :phone,
+ :first_day,
+ :last_day,
+ :notes
+ ] +
+ User.AVAILABLE_LANGUAGES.map { |l| "language_#{l}".to_sym } +
+ ConferenceRegistration.all_spaces +
+ ConferenceRegistration.all_considerations,
+ editable: administration_update_path(@this_conference.slug, @admin_step),
+ column_options: @column_options
+ }
+ end
+
+ private
+ def _original_content(value, lang)
+ content_tag(:div, (
+ content_tag(:h4, _('translate.content.Translation_of')) +
+ content_tag(:div, value, class: 'value', lang: lang)
+ ).html_safe, class: 'original-text')
+ end
+
+ def _form_field(type, name, value, options)
+ if type == 'check_box'
+ self.send(type + '_tag', name, "1", value, options)
+ else
+ self.send(type + '_tag', name, value, options)
+ end
+ end
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 579d977..d01221a 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -1,180 +1,180 @@
require 'diffy'
class UserMailer < ActionMailer::Base
- add_template_helper(ApplicationHelper)
- include LinguaFrancaHelper
-
- before_filter :set_host
-
- default from: "Bike!Bike! "
-
- def email_confirmation(confirmation)
- @confirmation = EmailConfirmation.find(confirmation) if confirmation.present?
- I18n.locale = @confirmation.user.locale if @confirmation.user.locale.present?
- @subject = _'email.subject.confirm_email','Please confirm your email address'
- mail to: @confirmation.user.named_email, subject: @subject
- end
-
- def registration_confirmation(registration)
- @registration = ConferenceRegistration.find(registration) if registration.present?
- @conference = @registration.conference
- @user = @registration.user
- I18n.locale = @user.locale if @user.locale.present?
- @subject = @conference.registration_status.to_sym == :pre ?
- _(
- 'email.subject.pre_registration_confirmed',
- "Thank you for pre-registering for #{@conference.title}",
- :vars => {:conference_title => @conference.title}
- ) : _(
- 'email.subject.registration_confirmed',
- "Thank you for registering for #{@conference.title}",
- :vars => {:conference_title => @conference.title}
- )
- mail to: @user.named_email, subject: @subject
- end
-
- def broadcast(host, subject, content, user, conference)
- @host = host
- @content = content
- @banner = nil
- @conference = Conference.find(conference) if conference.present?
- @user = User.find(user) if user.present?
- @subject = "[#{@conference ? @conference.title : 'Bike!Bike!'}] #{subject}"
- if @user && @user.named_email
- mail to: @user.named_email, subject: @subject
- end
- end
-
- def workshop_facilitator_request(workshop, requester, message)
- @workshop = Workshop.find(workshop) if workshop.present?
- @requester = User.find(requester) if requester.present?
- addresses = []
- I18n.locale = @workshop.active_facilitators.first.locale if @workshop.active_facilitators.first.locale.present?
- @workshop.active_facilitators.each do |f|
- addresses << f.named_email
- end
- @message = message
- @conference = Conference.find(@workshop.conference_id)
- @subject = _('email.subject.workshop_facilitator_request',
- "Request to facilitate #{@workshop.title} from #{@requester.name}",
- :vars => {:workshop_title => @workshop.title, :requester_name => @requester.firstname})
- mail to: addresses, reply_to: addresses + [@requester.named_email], subject: @subject
- end
-
- def workshop_facilitator_request_approved(workshop, user)
- @workshop = Workshop.find(workshop) if workshop.present?
- @conference = Conference.find(@workshop.conference_id)
- @user = User.find(user) if user.present?
- I18n.locale = @user.locale if @user.locale.present?
- @subject = (_'email.subject.workshop_request_approved',
- "You have been added as a facilitator of #{@workshop.title}",
- :vars => {:workshop_title => @workshop.title})
- mail to: @user.named_email, subject: @subject
- end
-
- def workshop_facilitator_request_denied(workshop, user)
- @workshop = Workshop.find(workshop) if workshop.present?
- @conference = @workshop.conference
- @user = User.find(user) if user.present?
- I18n.locale = @user.locale if @user.present? && @user.locale.present?
- @subject = (_'email.subject.workshop_request_denied',
- "Your request to facilitate #{@workshop.title} has been denied",
- :vars => {:workshop_title => @workshop.title})
- mail to: @user.named_email, subject: @subject
- end
-
- def workshop_translated(workshop, data, locale, user, translator)
- @workshop = Workshop.find(workshop) if workshop.present?
- @data = data
- @locale = locale
- @locale_name = language_name(locale)
- @user = User.find(user) if user.present?
- I18n.locale = @user.locale if @user.present? && @user.locale.present?
- @translator = User.find(translator) if translator.present?
- @subject = (_'email.subject.workshop_translated',
- "The #{@locale_name} translation for #{@workshop.title} has been modified",
- vars: {language: @language_name, workshop_title: @workshop.title})
-
- @wrapper_id = :full_width
-
- mail to: @user.named_email, subject: @subject
- end
-
- def workshop_original_content_changed(workshop, data, user, translator)
- @workshop = Workshop.find(workshop) if workshop.present?
- @data = data
- @user = User.find(user) if user.present?
- I18n.locale = @user.locale if @user.present? && @user.locale.present?
- @translator = User.find(translator) if translator.present?
- @subject = (_'email.subject.workshop_original_content_changed',
- "Original content for #{@workshop.title} has been modified",
- vars: {workshop_title: @workshop.title})
- @data.each do |field, values|
- diff = Diffy::Diff.new(values[:old], values[:new])
- @data[field][:diff] = {
- text: diff.to_s(:text),
- html: diff.to_s(:html)
- }
- end
-
- @wrapper_id = :full_width
-
- mail to: @user.named_email, subject: @subject
- end
-
- def workshop_comment(workshop, comment, user)
- @workshop = Workshop.find(workshop) if workshop.present?
- @comment = Comment.find(comment) if comment.present?
- @user = User.find(user) if user.present?
- I18n.locale = @user.locale if @user.present? && @user.locale.present?
-
- if @comment.reply?
- @subject = (_'email.subject.workshop_comment.reply', vars: { user_name: @comment.user.name })
- else
- @subject = (_'email.subject.workshop_comment.comment', vars: { user_name: @comment.user.name, workshop_title: @workshop.title })
- end
-
- mail to: @user.named_email, subject: @subject
- end
-
- def error_report(subject, message, report, exception, request, params, user, time = nil)
- @subject = subject
- @message = message
- @report = report
- @exception = exception
- @request = request
- @params = params
- @time = time
- @user = User.find(user) if user.present?
- mail to: 'goodgodwin@hotmail.com', subject: @subject
- end
-
- def contact(from, subject, message, email_list)
- @message = message
- @subject = subject
- @from = from.is_a?(Integer) ? User.find(from) : from
-
- mail to: email_list.join(', '), subject: @subject, reply_to: @from.is_a?(User) ? @from.named_email : @from
- end
-
- def contact_details(from, subject, message, request, params)
- @message = message
- @subject = "Details for: \"#{subject}\""
- @from = from.is_a?(Integer) ? User.find(from) : from
- @request = request
- @params = params
-
- mail to: 'goodgodwin@hotmail.com', subject: @subject
- end
-
- private
- def set_host(*args)
- if Rails.env.production?
- @host = "https://#{I18n.locale.to_s}.bikebike.org"
- elsif Rails.env.preview?
- @host = "https://preview-#{I18n.locale.to_s}.bikebike.org"
- else
- @host = UserMailer.default_url_options[:host]
- end
- end
+ add_template_helper(ApplicationHelper)
+ include LinguaFrancaHelper
+
+ before_filter :set_host
+
+ default from: "Bike!Bike! "
+
+ def email_confirmation(confirmation)
+ @confirmation = EmailConfirmation.find(confirmation) if confirmation.present?
+ I18n.locale = @confirmation.user.locale if @confirmation.user.locale.present?
+ @subject = _'email.subject.confirm_email','Please confirm your email address'
+ mail to: @confirmation.user.named_email, subject: @subject
+ end
+
+ def registration_confirmation(registration)
+ @registration = ConferenceRegistration.find(registration) if registration.present?
+ @conference = @registration.conference
+ @user = @registration.user
+ I18n.locale = @user.locale if @user.locale.present?
+ @subject = @conference.registration_status.to_sym == :pre ?
+ _(
+ 'email.subject.pre_registration_confirmed',
+ "Thank you for pre-registering for #{@conference.title}",
+ :vars => {:conference_title => @conference.title}
+ ) : _(
+ 'email.subject.registration_confirmed',
+ "Thank you for registering for #{@conference.title}",
+ :vars => {:conference_title => @conference.title}
+ )
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def broadcast(host, subject, content, user, conference)
+ @host = host
+ @content = content
+ @banner = nil
+ @conference = Conference.find(conference) if conference.present?
+ @user = User.find(user) if user.present?
+ @subject = "[#{@conference ? @conference.title : 'Bike!Bike!'}] #{subject}"
+ if @user && @user.named_email
+ mail to: @user.named_email, subject: @subject
+ end
+ end
+
+ def workshop_facilitator_request(workshop, requester, message)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @requester = User.find(requester) if requester.present?
+ addresses = []
+ I18n.locale = @workshop.active_facilitators.first.locale if @workshop.active_facilitators.first.locale.present?
+ @workshop.active_facilitators.each do |f|
+ addresses << f.named_email
+ end
+ @message = message
+ @conference = Conference.find(@workshop.conference_id)
+ @subject = _('email.subject.workshop_facilitator_request',
+ "Request to facilitate #{@workshop.title} from #{@requester.name}",
+ :vars => {:workshop_title => @workshop.title, :requester_name => @requester.firstname})
+ mail to: addresses, reply_to: addresses + [@requester.named_email], subject: @subject
+ end
+
+ def workshop_facilitator_request_approved(workshop, user)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @conference = Conference.find(@workshop.conference_id)
+ @user = User.find(user) if user.present?
+ I18n.locale = @user.locale if @user.locale.present?
+ @subject = (_'email.subject.workshop_request_approved',
+ "You have been added as a facilitator of #{@workshop.title}",
+ :vars => {:workshop_title => @workshop.title})
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def workshop_facilitator_request_denied(workshop, user)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @conference = @workshop.conference
+ @user = User.find(user) if user.present?
+ I18n.locale = @user.locale if @user.present? && @user.locale.present?
+ @subject = (_'email.subject.workshop_request_denied',
+ "Your request to facilitate #{@workshop.title} has been denied",
+ :vars => {:workshop_title => @workshop.title})
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def workshop_translated(workshop, data, locale, user, translator)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @data = data
+ @locale = locale
+ @locale_name = language_name(locale)
+ @user = User.find(user) if user.present?
+ I18n.locale = @user.locale if @user.present? && @user.locale.present?
+ @translator = User.find(translator) if translator.present?
+ @subject = (_'email.subject.workshop_translated',
+ "The #{@locale_name} translation for #{@workshop.title} has been modified",
+ vars: {language: @language_name, workshop_title: @workshop.title})
+
+ @wrapper_id = :full_width
+
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def workshop_original_content_changed(workshop, data, user, translator)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @data = data
+ @user = User.find(user) if user.present?
+ I18n.locale = @user.locale if @user.present? && @user.locale.present?
+ @translator = User.find(translator) if translator.present?
+ @subject = (_'email.subject.workshop_original_content_changed',
+ "Original content for #{@workshop.title} has been modified",
+ vars: {workshop_title: @workshop.title})
+ @data.each do |field, values|
+ diff = Diffy::Diff.new(values[:old], values[:new])
+ @data[field][:diff] = {
+ text: diff.to_s(:text),
+ html: diff.to_s(:html)
+ }
+ end
+
+ @wrapper_id = :full_width
+
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def workshop_comment(workshop, comment, user)
+ @workshop = Workshop.find(workshop) if workshop.present?
+ @comment = Comment.find(comment) if comment.present?
+ @user = User.find(user) if user.present?
+ I18n.locale = @user.locale if @user.present? && @user.locale.present?
+
+ if @comment.reply?
+ @subject = (_'email.subject.workshop_comment.reply', vars: { user_name: @comment.user.name })
+ else
+ @subject = (_'email.subject.workshop_comment.comment', vars: { user_name: @comment.user.name, workshop_title: @workshop.title })
+ end
+
+ mail to: @user.named_email, subject: @subject
+ end
+
+ def error_report(subject, message, report, exception, request, params, user, time = nil)
+ @subject = subject
+ @message = message
+ @report = report
+ @exception = exception
+ @request = request
+ @params = params
+ @time = time
+ @user = User.find(user) if user.present?
+ mail to: 'goodgodwin@hotmail.com', subject: @subject
+ end
+
+ def contact(from, subject, message, email_list)
+ @message = message
+ @subject = subject
+ @from = from.is_a?(Integer) ? User.find(from) : from
+
+ mail to: email_list.join(', '), subject: @subject, reply_to: @from.is_a?(User) ? @from.named_email : @from
+ end
+
+ def contact_details(from, subject, message, request, params)
+ @message = message
+ @subject = "Details for: \"#{subject}\""
+ @from = from.is_a?(Integer) ? User.find(from) : from
+ @request = request
+ @params = params
+
+ mail to: 'goodgodwin@hotmail.com', subject: @subject
+ end
+
+ private
+ def set_host(*args)
+ if Rails.env.production?
+ @host = "https://#{I18n.locale.to_s}.bikebike.org"
+ elsif Rails.env.preview?
+ @host = "https://preview-#{I18n.locale.to_s}.bikebike.org"
+ else
+ @host = UserMailer.default_url_options[:host]
+ end
+ end
end
diff --git a/app/models/city.rb b/app/models/city.rb
new file mode 100644
index 0000000..79554d7
--- /dev/null
+++ b/app/models/city.rb
@@ -0,0 +1,77 @@
+require 'geocoder'
+require 'geocoder/railtie'
+require 'geocoder/calculations'
+
+Geocoder::Railtie.insert
+
+class City < ActiveRecord::Base
+ geocoded_by :address
+ translates :city
+
+ reverse_geocoded_by :latitude, :longitude, :address => :full_address
+ after_validation :geocode, if: ->(obj){ obj.country_changed? or obj.territory_changed? or obj.city_changed? or obj.latitude.blank? or obj.longitude.blank? }
+
+ def address
+ ([city!, territory, country] - [nil, '']).join(', ')
+ end
+
+ def get_translation(locale)
+ location = Geocoder.search(address, language: locale.to_s).first
+
+ location.data['address_components'].each do | component |
+ if component['types'].first == 'locality'
+ return component['short_name']
+ end
+ end
+
+ return nil
+ end
+
+ def translate_city(locale)
+ translation = get_translation(locale)
+ set_column_for_locale(:city, locale, translation)
+ save!
+
+ return translation
+ end
+
+ def self.search(str)
+ cache = CityCache.search(str)
+
+ # return the city if this search is in our cache
+ return cache.city if cache.present?
+
+ # look up the city in the geocoder
+ location = Geocoder.search(str, language: 'en').first
+
+ # see if the city is already present in our database
+ city = City.find_by_place_id(location.data['place_id'])
+
+ # return the city if we found it in the db already
+ return city if city.present?
+
+ # otherwise build a new city
+ component_alises = {
+ 'locality' => :city,
+ 'administrative_area_level_1' => :territory,
+ 'country' => :country
+ }
+ city_data = {
+ locale: :en,
+ latitude: location.data['geometry']['location']['lat'],
+ longitude: location.data['geometry']['location']['lng'],
+ place_id: location.data['place_id']
+ }
+ location.data['address_components'].each do | component |
+ property = component_alises[component['types'].first]
+ city_data[property] = component['short_name'] if property.present?
+ end
+
+ # save the new city
+ city = City.new(city_data)
+ city.save!
+
+ # and return it
+ return city
+ end
+end
diff --git a/app/models/city_cache.rb b/app/models/city_cache.rb
new file mode 100644
index 0000000..d96f501
--- /dev/null
+++ b/app/models/city_cache.rb
@@ -0,0 +1,9 @@
+class CityCache < ActiveRecord::Base
+ self.table_name = :city_cache
+
+ belongs_to :city
+
+ def self.search(str)
+ CityCache.find_by_search(str.downcase)
+ end
+end
diff --git a/app/models/comment.rb b/app/models/comment.rb
index 1c3ddc6..a82e08c 100644
--- a/app/models/comment.rb
+++ b/app/models/comment.rb
@@ -1,27 +1,27 @@
class Comment < ActiveRecord::Base
- belongs_to :user
+ belongs_to :user
- def comment_object
- model_type.classify.constantize.find(model_id)
- end
+ def comment_object
+ model_type.classify.constantize.find(model_id)
+ end
- def set_model(model)
- model_type = model.class.name.tableize
- model_id = model.id
- end
+ def set_model(model)
+ model_type = model.class.name.tableize
+ model_id = model.id
+ end
- def self.for(model)
- where(model_type: model.class.name.tableize, model_id: model.id).order(created_at: :asc)
- end
+ def self.for(model)
+ where(model_type: model.class.name.tableize, model_id: model.id).order(created_at: :asc)
+ end
- def self.create_for(model, user, comment)
- create(
- model_type: model.class.name.tableize,
- model_id: model.id,
- user_id: user.id,
- comment: comment
- )
- end
+ def self.create_for(model, user, comment)
+ create(
+ model_type: model.class.name.tableize,
+ model_id: model.id,
+ user_id: user.id,
+ comment: comment
+ )
+ end
def add_comment(user, comment)
Comment.create_for(self, user, comment)
@@ -32,6 +32,6 @@ class Comment < ActiveRecord::Base
end
def reply?
- model_type == 'comments'
+ model_type == 'comments'
end
end
diff --git a/app/models/conference.rb b/app/models/conference.rb
index 598acfb..3b5195d 100644
--- a/app/models/conference.rb
+++ b/app/models/conference.rb
@@ -1,71 +1,136 @@
class Conference < ActiveRecord::Base
- translates :info, :title, :payment_message
-
- mount_uploader :cover, CoverUploader
- mount_uploader :poster, PosterUploader
-
- belongs_to :conference_type
-
- has_many :conference_host_organizations, :dependent => :destroy
- has_many :organizations, :through => :conference_host_organizations
- has_many :event_locations
-
- #has_many :conference_registration_form_fields, :order => 'position ASC', :dependent => :destroy#, :class_name => '::ConferenceRegistrationFormField'
- #has_many :registration_form_fields, :through => :conference_registration_form_fields
-
- has_many :workshops
-
- accepts_nested_attributes_for :conference_host_organizations, :reject_if => proc {|u| u[:organization_id].blank?}, :allow_destroy => true
-
- def to_param
- slug
- end
-
- def host?(user)
- return false unless user.present?
- organizations.each do |o|
- return true if o.host?(user)
- end
- return false
- end
-
- def url(action = :show)
- path(action)
- end
-
- def path(action = :show)
- action = action.to_sym
- '/conferences/' + conference_type.slug + '/' + slug + (action == :show ? '' : '/' + action.to_s)
- end
-
- def location
- organizations.first.location
- end
-
- def registered?(user)
- registration = ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id)
- return registration ? registration.is_attending : false
- end
-
- def registration_exists?(user)
- ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id).present?
- end
-
- def registration_open
- registration_status == :open
- end
-
- def registration_status
- s = read_attribute(:registration_status)
- s.present? ? s.to_sym : nil
- end
-
- def registration_status=(new_registration_status)
- write_attribute :registration_status, new_registration_status.to_s
- end
-
- def self.default_payment_amounts
- [25, 50, 100]
- end
+ translates :info, :title, :payment_message
+
+ mount_uploader :cover, CoverUploader
+ mount_uploader :poster, PosterUploader
+
+ belongs_to :conference_type
+ belongs_to :city
+
+ has_many :conference_host_organizations, dependent: :destroy
+ has_many :organizations, through: :conference_host_organizations
+ has_many :conference_administrators, dependent: :destroy
+ has_many :administrators, through: :conference_administrators, source: :user
+ has_many :event_locations
+
+ has_many :workshops
+
+ accepts_nested_attributes_for :conference_host_organizations, reject_if: proc {|u| u[:organization_id].blank?}, allow_destroy: true
+
+ before_create :make_slug
+
+ def to_param
+ slug
+ end
+
+ def host_organization?(org)
+ return false unless org.present?
+ org_id = org.is_a?(Organization) ? org.id : org
+
+ organizations.each do |o|
+ return true if o.id = org_id
+ end
+
+ return false
+ end
+
+ def host?(user)
+ if user.present?
+ return true if user.administrator?
+
+ conference_administrators.each do |u|
+ return true if user.id == u.id
+ end
+
+ organizations.each do |o|
+ return true if o.host?(user)
+ end
+ end
+ return false
+ end
+
+ def url(action = :show)
+ path(action)
+ end
+
+ def path(action = :show)
+ action = action.to_sym
+ '/conferences/' + conference_type.slug + '/' + slug + (action == :show ? '' : '/' + action.to_s)
+ end
+
+ def location
+ return nil unless organizations.present?
+ organizations.first.location
+ end
+
+ def registered?(user)
+ registration = ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id)
+ return registration ? registration.is_attending : false
+ end
+
+ def registration_exists?(user)
+ ConferenceRegistration.find_by(:user_id => user.id, :conference_id => id).present?
+ end
+
+ def registration_open
+ registration_status == :open
+ end
+
+ def registration_status
+ s = read_attribute(:registration_status)
+ s.present? ? s.to_sym : nil
+ end
+
+ def registration_status=(new_registration_status)
+ write_attribute :registration_status, new_registration_status.to_s
+ end
+
+ def make_slug(reset = false)
+ if reset
+ self.slug = nil
+ end
+
+ self.slug ||= Conference.generate_slug(
+ conferencetype || :annual,
+ conference_year,
+ city_name.gsub(/\s/, '')
+ )
+ end
+
+ def city_name
+ return city.city if city.present?
+ return location.present? ? location.city : nil
+ end
+
+ def conference_year
+ self.year || (end_date.present? ? end_date.year : nil)
+ end
+
+ def over?
+ return false unless end_date.present?
+ return end_date < DateTime.now
+ end
+
+ def self.default_payment_amounts
+ [25, 50, 100]
+ end
+
+ def self.conference_types
+ {
+ annual: '%{city}%{year}',
+ n: 'North%{year}',
+ s: 'South%{year}',
+ e: 'East%{year}',
+ w: 'West%{year}',
+ ne: 'Northeast%{year}',
+ nw: 'Northwest%{year}',
+ se: 'Southeast%{year}',
+ sw: 'Southwest%{year}'
+ }
+ end
+
+ def self.generate_slug(type, year, city)
+ Conference.conference_types[(type || :annual).to_sym].gsub('%{city}', city).gsub('%{year}', year.to_s)
+ end
end
diff --git a/app/models/conference_administrator.rb b/app/models/conference_administrator.rb
new file mode 100644
index 0000000..2fd7823
--- /dev/null
+++ b/app/models/conference_administrator.rb
@@ -0,0 +1,4 @@
+class ConferenceAdministrator < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :conference
+end
diff --git a/app/models/conference_registration.rb b/app/models/conference_registration.rb
index fc2408b..369de28 100644
--- a/app/models/conference_registration.rb
+++ b/app/models/conference_registration.rb
@@ -1,58 +1,58 @@
class ConferenceRegistration < ActiveRecord::Base
- belongs_to :conference
- belongs_to :user
- has_many :conference_registration_responses
-
- AttendingOptions = [:yes, :no]
-
- def languages
- user.languages
- end
-
- def self.all_housing_options
- [:none, :tent, :house]
- end
-
- def self.all_spaces
- [:bed_space, :floor_space, :tent_space]
- end
-
- def self.all_bike_options
- [:yes, :no]
- end
-
- def self.all_food_options
- [:meat, :vegetarian, :vegan]
- end
-
- def self.all_considerations
- [:vegan, :smoking, :pets, :quiet]
- end
-
- def status(was = false)
- return :unregistered if user.firstname.blank? || self.send(was ? :city_was : :city).blank?
- return :registered if self.send(was ? :housing_was : :housing).present? || (self.send(was ? :can_provide_housing_was : :can_provide_housing) && (self.send(was ? :housing_data_was : :housing_data) || {})['availability'].present?)
- return :preregistered
- end
-
- around_update :check_status
-
- def check_status
- yield
-
- old_status = status(true)
- new_status = status
-
- if old_status.present? && old_status != new_status
- if (conference.registration_status == :pre && new_status == :preregistered) ||
- (conference.registration_status == :open && new_status == :registered)
-
- UserMailer.send_mail :registration_confirmation do
- {
- :args => self
- }
- end
- end
- end
- end
+ belongs_to :conference
+ belongs_to :user
+ has_many :conference_registration_responses
+
+ AttendingOptions = [:yes, :no]
+
+ def languages
+ user.languages
+ end
+
+ def self.all_housing_options
+ [:none, :tent, :house]
+ end
+
+ def self.all_spaces
+ [:bed_space, :floor_space, :tent_space]
+ end
+
+ def self.all_bike_options
+ [:yes, :no]
+ end
+
+ def self.all_food_options
+ [:meat, :vegetarian, :vegan]
+ end
+
+ def self.all_considerations
+ [:vegan, :smoking, :pets, :quiet]
+ end
+
+ def status(was = false)
+ return :unregistered if user.nil? || user.firstname.blank? || self.send(was ? :city_was : :city).blank?
+ return :registered if self.send(was ? :housing_was : :housing).present? || (self.send(was ? :can_provide_housing_was : :can_provide_housing) && (self.send(was ? :housing_data_was : :housing_data) || {})['availability'].present?)
+ return :preregistered
+ end
+
+ around_update :check_status
+
+ def check_status
+ yield
+
+ old_status = status(true)
+ new_status = status
+
+ if old_status.present? && old_status != new_status
+ if (conference.registration_status == :pre && new_status == :preregistered) ||
+ (conference.registration_status == :open && new_status == :registered)
+
+ UserMailer.send_mail :registration_confirmation do
+ {
+ :args => self
+ }
+ end
+ end
+ end
+ end
end
diff --git a/app/models/event.rb b/app/models/event.rb
index 430d100..cf4677f 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -1,20 +1,20 @@
class Event < ActiveRecord::Base
- translates :info, :title
+ translates :info, :title
- belongs_to :conference
- belongs_to :event_location
+ belongs_to :conference
+ belongs_to :event_location
- def conference_day
- return nil unless start_time.present? && end_time.present?
+ def conference_day
+ return nil unless start_time.present? && end_time.present?
- start_day = conference.start_date.change(hour: 0, minute: 0, second: 0)
- w_start_day = start_time.change(hour: 0, minute: 0, second: 0)
- return (((w_start_day - start_day) / 86400) + 1).to_i
- end
+ start_day = conference.start_date.change(hour: 0, minute: 0, second: 0)
+ w_start_day = start_time.change(hour: 0, minute: 0, second: 0)
+ return (((w_start_day - start_day) / 86400) + 1).to_i
+ end
- def duration
- return nil unless start_time.present? && end_time.present?
- ((end_time - start_time) / 60).to_i
- end
+ def duration
+ return nil unless start_time.present? && end_time.present?
+ ((end_time - start_time) / 60).to_i
+ end
end
diff --git a/app/models/locations_organization.rb b/app/models/locations_organization.rb
index e3c52f6..60a2d25 100644
--- a/app/models/locations_organization.rb
+++ b/app/models/locations_organization.rb
@@ -1,6 +1,6 @@
-class LocationsOrganization < ActiveRecord::Base
- belongs_to :location
- belongs_to :organization
-
- self.primary_key = :location_id
-end
+class LocationsOrganization < ActiveRecord::Base
+ belongs_to :location
+ belongs_to :organization
+
+ self.primary_key = :location_id
+end
diff --git a/app/models/organization.rb b/app/models/organization.rb
index 5117fd1..3b46249 100644
--- a/app/models/organization.rb
+++ b/app/models/organization.rb
@@ -1,71 +1,77 @@
class Organization < ActiveRecord::Base
- mount_uploader :logo, LogoUploader
- mount_uploader :avatar, AvatarUploader
- mount_uploader :cover, CoverUploader
+ mount_uploader :logo, LogoUploader
+ mount_uploader :avatar, AvatarUploader
+ mount_uploader :cover, CoverUploader
- has_many :locations_organization
- has_many :locations, :through => :locations_organization
+ has_many :locations_organization
+ has_many :locations, through: :locations_organization
- has_many :user_organization_relationships, :dependent => :destroy
- has_many :users, :through => :user_organization_relationships
+ has_many :user_organization_relationships, dependent: :destroy
+ has_many :users, through: :user_organization_relationships
- accepts_nested_attributes_for :locations, :reject_if => proc {|l| l[id].blank?}
- accepts_nested_attributes_for :user_organization_relationships, :reject_if => proc {|u| u[:user_id].blank?}, :allow_destroy => true
- before_create :make_slug
+ accepts_nested_attributes_for :locations, :reject_if => proc {|l| l[id].blank?}
+ accepts_nested_attributes_for :user_organization_relationships, :reject_if => proc {|u| u[:user_id].blank?}, :allow_destroy => true
+ before_create :make_slug
- def location
- locations.first
- end
+ def location
+ locations.first
+ end
- def longitude
- location.longitude
- end
+ def longitude
+ location.longitude
+ end
- def latitude
- location.latitude
- end
+ def latitude
+ location.latitude
+ end
- def to_param
- slug
- end
+ def to_param
+ slug
+ end
- def host?(user)
- return false unless user.present?
- return true if user.administrator?
-
- users.each do |u|
- return true if u.id == user.id
- end
- return false
- end
+ def host?(user)
+ return false unless user.present?
+ return true if user.administrator?
+
+ users.each do |u|
+ return true if u.id == user.id
+ end
+ return false
+ end
- def generate_slug(name, location = nil)
- s = name.gsub(/[^a-z1-9]+/i, '-').chomp('-').gsub(/\-([A-Z])/, '\1')
- if Organization.find_by(:slug => s).present? && !location.nil?
- if location.city.present?
- s += '-' + location.city
- end
- if Organization.find_by(:slug => s).present? && location.territory.present?
- s += '-' + location.territory
- end
- if Organization.find_by(:slug => s).present?
- s += '-' + location.country
- end
- end
- attempt = 1
- ss = s
+ def generate_slug(name, location = nil)
+ s = name.gsub(/[^a-z1-9]+/i, '-').chomp('-').gsub(/\-([A-Z])/, '\1')
+ if Organization.find_by(:slug => s).present? && !location.nil?
+ if location.city.present?
+ s += '-' + location.city
+ end
+ if Organization.find_by(:slug => s).present? && location.territory.present?
+ s += '-' + location.territory
+ end
+ if Organization.find_by(:slug => s).present?
+ s += '-' + location.country
+ end
+ end
+ attempt = 1
+ ss = s
- while Organization.find_by(:slug => s)
- attempt += 1
- s = ss + '-' + attempt.to_s
- end
- s
- end
+ while Organization.find_by(:slug => s)
+ attempt += 1
+ s = ss + '-' + attempt.to_s
+ end
+ s
+ end
- private
- def make_slug
- if !self.slug
- self.slug = generate_slug(self.name, self.locations && self.locations[0])
- end
- end
+ def self.find_by_city(city)
+ Organization.joins(:locations).where(locations: {
+ city_id: city.is_a?(City) ? city.id : city
+ })
+ end
+
+ private
+ def make_slug
+ if !self.slug
+ self.slug = generate_slug(self.name, self.locations && self.locations[0])
+ end
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index df4e5d2..33d17e3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,62 +1,67 @@
class User < ActiveRecord::Base
- authenticates_with_sorcery! do |config|
+ authenticates_with_sorcery! do |config|
config.authentications_class = Authentication
end
- validates :email, uniqueness: true
+ validates :email, uniqueness: true
- mount_uploader :avatar, AvatarUploader
+ mount_uploader :avatar, AvatarUploader
- has_many :user_organization_relationships
- has_many :organizations, through: :user_organization_relationships
- has_many :authentications, :dependent => :destroy
- accepts_nested_attributes_for :authentications
+ has_many :user_organization_relationships
+ has_many :organizations, through: :user_organization_relationships
+ has_many :conferences, through: :conference_administrators
+ has_many :authentications, :dependent => :destroy
+ accepts_nested_attributes_for :authentications
- before_update do |user|
- user.locale ||= I18n.locale
- end
+ before_update do |user|
+ user.locale ||= I18n.locale
+ end
- before_save do |user|
- user.locale ||= I18n.locale
- end
+ before_save do |user|
+ user.locale ||= I18n.locale
+ end
- def can_translate?(to_locale = nil, from_locale = nil)
- is_translator unless to_locale.present?
+ def can_translate?(to_locale = nil, from_locale = nil)
+ is_translator unless to_locale.present?
- from_locale = I18n.locale unless from_locale.present?
- return languages.present? &&
- to_locale.to_s != from_locale.to_s &&
- languages.include?(to_locale.to_s) &&
- languages.include?(from_locale.to_s)
- end
+ from_locale = I18n.locale unless from_locale.present?
+ return languages.present? &&
+ to_locale.to_s != from_locale.to_s &&
+ languages.include?(to_locale.to_s) &&
+ languages.include?(from_locale.to_s)
+ end
- def name
- firstname || username || email
- end
+ def name
+ firstname || username || email
+ end
- def named_email
- name = firstname || username
- return email unless name
- return "#{name} <#{email}>"
- end
+ def named_email
+ name = firstname || username
+ return email unless name
+ return "#{name} <#{email}>"
+ end
- def administrator?
- role == 'administrator'
- end
+ def administrator?
+ role == 'administrator'
+ end
- def self.AVAILABLE_LANGUAGES
- [:en, :es, :fr, :ar]
- end
+ def self.AVAILABLE_LANGUAGES
+ [:en, :es, :fr, :ar]
+ end
- def self.get(email)
- user = where(email: email).first
+ def self.get(email)
+ user = where(email: email).first
- unless user
- user = new(email: email)
- user.save!
- end
+ unless user
+ user = new(email: email)
+ user.save!
+ end
+
+ return user
+ end
- return user
- end
+ def self.find_user(email)
+ User.where('lower(email) = ?', email.downcase).first
+ end
end
diff --git a/app/models/user_organization_relationship.rb b/app/models/user_organization_relationship.rb
index 24c8d94..789182a 100644
--- a/app/models/user_organization_relationship.rb
+++ b/app/models/user_organization_relationship.rb
@@ -1,11 +1,11 @@
-class UserOrganizationRelationship < ActiveRecord::Base
- belongs_to :user
- belongs_to :organization
-
- Administrator = 'administrator'
- Member = 'member'
-
- DefaultRelationship = Member
-
- AllRelationships = [Administrator, Member]
-end
+class UserOrganizationRelationship < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :organization
+
+ Administrator = 'administrator'
+ Member = 'member'
+
+ DefaultRelationship = Member
+
+ AllRelationships = [Administrator, Member]
+end
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index c9a2781..4e4ad8e 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -3,102 +3,102 @@ require 'carrierwave/processing/mini_magick'
class AvatarUploader < CarrierWave::Uploader::Base
- include CarrierWave::ImageOptimizer
- include CarrierWave::MiniMagick
-
- # Include RMagick or MiniMagick support:
- # include CarrierWave::RMagick
- # include CarrierWave::MiniMagick
-
- # Choose what kind of storage to use for this uploader:
-
- storage :file
- process :optimize
-
- @@sizes = {:thumb => [120, 120], :icon => [48, 48], :preview => [360, 120], :normal => [512, 512]}
- # storage :fog
-
- # Override the directory where uploaded files will be stored.
- # This is a sensible default for uploaders that are meant to be mounted:
- def store_dir
- "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
- end
-
- # Provide a default URL as a default if there hasn't been a file uploaded:
- def default_url
- # # For Rails 3.1+ asset pipeline compatibility:
- # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
- #
- #"/images/fallback/" + [version_name, "default.png"].compact.join('_')
- "http://placehold.it/" + (@@sizes[version_name] || [300, 300]).join('x')
- end
-
- # Process files as they are uploaded:
- # process :scale => [200, 300]
- #
- #def scale(width, height)
- #end
-
- # Create different versions of your uploaded files:
- version :thumb do
- process :resize_to_fill => @@sizes[:thumb]
- end
-
- version :icon do
- process :resize_to_fill => @@sizes[:icon]
- end
-
- version :preview do
- process :resize_to_fit => @@sizes[:preview]
- end
-
- version :normal do
- process :resize_to_fit => @@sizes[:normal]
- end
-
- # Add a white list of extensions which are allowed to be uploaded.
- # For images you might use something like this:
- # def extension_white_list
- # %w(jpg jpeg gif png)
- # end
-
- # Override the filename of the uploaded files:
- # Avoid using model.id or version_name here, see uploader/store.rb for details.
- # def filename
- # "something.jpg" if original_filename
- # end
-
- def image
- @image ||= MiniMagick::Image.open(file.path)
- end
-
- def is_landscape?
- image['width'] > (image['height'] * 1.25)
- end
-
- #def recreate_versions!(*versions)
- # if !current_path.nil?
- # current_path = "'" + (current_path || '') + "'"
- # end
- # super(*versions)
- #end
-
-# def manipulate!
-# cache_stored_file! if !cached?
-# image = ::MiniMagick::Image.open(current_path)
+ include CarrierWave::ImageOptimizer
+ include CarrierWave::MiniMagick
+
+ # Include RMagick or MiniMagick support:
+ # include CarrierWave::RMagick
+ # include CarrierWave::MiniMagick
+
+ # Choose what kind of storage to use for this uploader:
+
+ storage :file
+ process :optimize
+
+ @@sizes = {:thumb => [120, 120], :icon => [48, 48], :preview => [360, 120], :normal => [512, 512]}
+ # storage :fog
+
+ # Override the directory where uploaded files will be stored.
+ # This is a sensible default for uploaders that are meant to be mounted:
+ def store_dir
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
+ end
+
+ # Provide a default URL as a default if there hasn't been a file uploaded:
+ # def default_url
+ # # For Rails 3.1+ asset pipeline compatibility:
+ # # ActionController::Base.helpers.asset_path("fallback/" + [version_name, "default.png"].compact.join('_'))
+ #
+ #"/images/fallback/" + [version_name, "default.png"].compact.join('_')
+ # "http://placehold.it/" + (@@sizes[version_name] || [300, 300]).join('x')
+ #end
+
+ # Process files as they are uploaded:
+ # process :scale => [200, 300]
+ #
+ #def scale(width, height)
+ #end
+
+ # Create different versions of your uploaded files:
+ version :thumb do
+ process :resize_to_fill => @@sizes[:thumb]
+ end
+
+ version :icon do
+ process :resize_to_fill => @@sizes[:icon]
+ end
+
+ version :preview do
+ process :resize_to_fit => @@sizes[:preview]
+ end
+
+ version :normal do
+ process :resize_to_fit => @@sizes[:normal]
+ end
+
+ # Add a white list of extensions which are allowed to be uploaded.
+ # For images you might use something like this:
+ # def extension_white_list
+ # %w(jpg jpeg gif png)
+ # end
+
+ # Override the filename of the uploaded files:
+ # Avoid using model.id or version_name here, see uploader/store.rb for details.
+ # def filename
+ # "something.jpg" if original_filename
+ # end
+
+ def image
+ @image ||= MiniMagick::Image.open(file.path)
+ end
+
+ def is_landscape?
+ image['width'] > (image['height'] * 1.25)
+ end
+
+ #def recreate_versions!(*versions)
+ # if !current_path.nil?
+ # current_path = "'" + (current_path || '') + "'"
+ # end
+ # super(*versions)
+ #end
+
+# def manipulate!
+# cache_stored_file! if !cached?
+# image = ::MiniMagick::Image.open(current_path)
#
-# begin
-# image.format(@format.to_s.downcase) if @format
-# image = yield(image)
-# image.write(current_path)
-# image.run_command("identify", '"' + current_path + '"')
-# ensure
-# image.destroy!
-# end
-# rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
-# default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
-# message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
-# raise CarrierWave::ProcessingError, message
-# end
+# begin
+# image.format(@format.to_s.downcase) if @format
+# image = yield(image)
+# image.write(current_path)
+# image.run_command("identify", '"' + current_path + '"')
+# ensure
+# image.destroy!
+# end
+# rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
+# default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
+# message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
+# raise CarrierWave::ProcessingError, message
+# end
end
diff --git a/app/uploaders/poster_uploader.rb b/app/uploaders/poster_uploader.rb
index 9f47afd..b6e2220 100644
--- a/app/uploaders/poster_uploader.rb
+++ b/app/uploaders/poster_uploader.rb
@@ -3,67 +3,67 @@ require 'carrierwave/processing/mini_magick'
class PosterUploader < CarrierWave::Uploader::Base
- include CarrierWave::ImageOptimizer
- include CarrierWave::MiniMagick
+ include CarrierWave::ImageOptimizer
+ include CarrierWave::MiniMagick
- storage :file
- process :optimize
+ storage :file
+ process :optimize
- @@sizes = {
- :thumb => [120, 120],
- :icon => [48, 48],
- :preview => [512, 512],
- :full => [1024, 1024]
- }
+ @@sizes = {
+ :thumb => [120, 120],
+ :icon => [48, 48],
+ :preview => [512, 512],
+ :full => [1024, 1024]
+ }
- def store_dir
- "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
- end
+ def store_dir
+ "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
+ end
- version :thumb do
- process :resize_to_fill => @@sizes[:thumb]
- end
+ version :thumb do
+ process :resize_to_fill => @@sizes[:thumb]
+ end
- version :icon do
- process :resize_to_fill => @@sizes[:icon]
- end
+ version :icon do
+ process :resize_to_fill => @@sizes[:icon]
+ end
- version :preview do
- process :resize_to_fit => @@sizes[:preview]
- end
+ version :preview do
+ process :resize_to_fit => @@sizes[:preview]
+ end
- version :full do
- process :resize_to_fit => @@sizes[:full]
- end
+ version :full do
+ process :resize_to_fit => @@sizes[:full]
+ end
- def image
- @image ||= MiniMagick::Image.open(file.path)
- end
+ def image
+ @image ||= MiniMagick::Image.open(file.path)
+ end
- def is_landscape?
- image['width'] > image['height']
- end
+ def is_landscape?
+ image['width'] > image['height']
+ end
- def manipulate!
- cache_stored_file! if !cached?
- image = ::MiniMagick::Image.open(current_path)
+ def manipulate!
+ cache_stored_file! if !cached?
+ image = ::MiniMagick::Image.open(current_path)
- begin
- image.format(@format.to_s.downcase) if @format
- image = yield(image)
- image.write(current_path)
- begin
- image.run_command("identify", current_path)
- rescue
- image.run_command("identify", '"' + current_path + '"')
- end
- ensure
- image.destroy!
- end
- rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
- default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
- message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
- raise CarrierWave::ProcessingError, message
- end
+ begin
+ image.format(@format.to_s.downcase) if @format
+ image = yield(image)
+ image.write(current_path)
+ begin
+ image.run_command("identify", current_path)
+ rescue
+ image.run_command("identify", '"' + current_path + '"')
+ end
+ ensure
+ image.destroy!
+ end
+ rescue ::MiniMagick::Error, ::MiniMagick::Invalid => e
+ default = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :locale => :en)
+ message = I18n.translate(:"errors.messages.mini_magick_processing_error", :e => e, :default => default)
+ raise CarrierWave::ProcessingError, message
+ end
end
diff --git a/app/views/admin/new.html.haml b/app/views/admin/new.html.haml
new file mode 100644
index 0000000..0730908
--- /dev/null
+++ b/app/views/admin/new.html.haml
@@ -0,0 +1,24 @@
+- body_class 'banner-bottom'
+= render :partial => 'application/header', :locals => {:page_group => :administration, :page_key => 'Administration', :image_file => 'admin.jpg'}
+
+%article
+ = row do
+ = form_tag save_conference_path, class: 'composition' do
+ = columns(medium: 12) do
+ %h2=_@page_title, :t
+ = hidden_field_tag :id, @this_conference.id if @this_conference.id.present?
+
+ = columns(medium: 12) do
+ = textfield :city, location(@this_conference.city), required: true, big: true
+ = columns(medium: 12, class: 'flex-column') do
+ = selectfield :type, @this_conference.conferencetype, Conference.conference_types.keys.map { |k| [(_"forms.options.conferences.types.#{k}"), k]}, required: true, stretch: true
+ = numberfield :year, @this_conference.conference_year || Date.today.year, required: true
+ = columns(medium: 6) do
+ = checkbox :is_public, @this_conference.is_public != false, 'forms.labels.generic.is_public'
+ = columns(medium: 6) do
+ = checkbox :is_featured, @this_conference.is_featured != false, 'forms.labels.generic.is_featured'
+ = columns(medium: 12) do
+ .actions.next-prev
+ = button_tag :save, value: :save
+ - if @this_conference.id.present?
+ = button_with_confirmation :delete, value: :delete, class: 'delete'
diff --git a/app/views/application/_header.html.haml b/app/views/application/_header.html.haml
index 9d273a1..8f77b99 100644
--- a/app/views/application/_header.html.haml
+++ b/app/views/application/_header.html.haml
@@ -1,20 +1,20 @@
- content_for :banner do
- - image = image_path(image_file || 'empty-racks.jpg')
- - style = nil
- - cover = nil
- - figure = nil
- - if capable_of(:css_mixblendmode)
- - cover = "
"
- - else
- - style = "background-image: url(#{image})"
- #header-title.short{style: style}
- = (render banner_image, {:image => image}) if defined?(banner_image) == "local-variable"
- = cover.html_safe if cover
- - if @page_title.present? || defined?(page_group)
- - content_for :title do
- =@page_title.present? ? I18n.t(@page_title, @page_title_vars) : I18n.t("page_titles.#{page_group.to_s}.#{page_key.to_s}")
- = row do
- = columns do
- %h1=_(@main_title || "page_titles.#{page_group.to_s}.#{page_key.to_s}", :t, @main_title_vars)
- - content_for :og_image do
- = image
+ - image = image_path(image_file || 'empty-racks.jpg')
+ - style = nil
+ - cover = nil
+ - figure = nil
+ - if capable_of(:css_mixblendmode)
+ - cover = "
"
+ - else
+ - style = "background-image: url(#{image})"
+ #header-title.short{style: style}
+ = (render banner_image, {:image => image}) if defined?(banner_image) == "local-variable"
+ = cover.html_safe if cover
+ - if @page_title.present? || defined?(page_group)
+ - content_for :title do
+ = I18n.t(@page_title || "page_titles.#{page_group.to_s}.#{page_key.to_s}", @page_title_vars)
+ = row do
+ = columns do
+ %h1=_(@main_title || "page_titles.#{page_group.to_s}.#{page_key.to_s}", :t, @main_title_vars || (@page_title_vars.present? && @page_title.blank? ? { vars: @page_title_vars } : nil))
+ - content_for :og_image do
+ = image
diff --git a/app/views/application/home.html.haml b/app/views/application/home.html.haml
index f1705f9..8e827c1 100644
--- a/app/views/application/home.html.haml
+++ b/app/views/application/home.html.haml
@@ -1,17 +1,5 @@
-- this_is_the_front_page
-- if @conference
- = render 'conferences/header'
- %article
- = row do
- = columns(medium: 10, push: {medium: 1}) do
- %h2=_!@conference.title
- = richtext @conference.info
- - if @conference.registration_status == :open
- - if @conference.workshop_schedule_published
- - add_inline_script :home_schedule
- %h3=_'articles.workshops.headings.Schedule'
- = render 'conferences/admin/schedule'
- - else
- %h3=_'articles.workshops.headings.Proposed_Workshops'
- %p=_'articles.workshops.paragraphs.Proposed_Workshops'
- = render 'workshops/workshop_previews', :workshops => (@conference.workshops.sort { |a, b| a.title.downcase <=> b.title.downcase })
+- content_for :og_image do
+ = @conference.poster.full.url || image_path('default_poster.jpg')
+- if @conferences
+ - @conferences.each do | conference |
+ = render 'conferences/conference', conference: conference, links: [ :read_more, :register ]
diff --git a/app/views/application/user_settings.html.haml b/app/views/application/user_settings.html.haml
index d2a32c7..911085f 100644
--- a/app/views/application/user_settings.html.haml
+++ b/app/views/application/user_settings.html.haml
@@ -1,25 +1,25 @@
= render :partial => 'application/header', :locals => {:image_file => @banner_image || 'grafitti.jpg'}
%article
- = row do
- = columns do
- - if logged_in?
- %h2=_'articles.user_settings.headings.Your_Account'
- - if @conference.present? && (@conference.registration_status == :pre || @conference.registration_status == :open)
- %p=_'articles.user_settings.paragraphs.conference_registration', :t
- = link_to (_'actions.conference.edit_registration'), register_path(@conference.slug), class: :button
- - if @conferences.present?
- %h3=_'articles.user_settings.headings.Your_Conferences'
- .link-dump
- - @conferences.each do | conference |
- = link_to (_!conference.title), administration_step_path(conference.slug, :edit), class: :button
+ = row do
+ = columns do
+ - if logged_in?
+ %h2=_'articles.user_settings.headings.Your_Account'
+ - if @conference.present? && (@conference.registration_status == :pre || @conference.registration_status == :open)
+ %p=_'articles.user_settings.paragraphs.conference_registration', :t
+ = link_to (_'actions.conference.edit_registration'), register_path(@conference.slug), class: :button
+ - if @conferences.present?
+ %h3=_'articles.user_settings.headings.Your_Conferences'
+ .link-dump
+ - @conferences.each do | conference |
+ = link_to (_!conference.title), administration_step_path(conference.slug, :edit), class: :button
- = form_tag update_settings_path do
- = textfield :name, current_user.name, required: true, heading: 'articles.conference_registration.headings.name', big: true
- = checkboxes :languages, User.AVAILABLE_LANGUAGES, current_user.languages || [I18n.locale], 'languages', heading: 'articles.conference_registration.headings.languages'
- = radiobuttons :preferred_language, I18n.backend.enabled_locales, current_user.locale || I18n.locale, 'languages', heading: 'articles.conference_registration.headings.preferred_language'
- = checkbox :email_subscribe, current_user.is_subscribed != false, 'articles.user_settings.email_subscribe', heading: 'articles.user_settings.headings.email_subscribe', help: 'articles.user_settings.paragraphs.email_subscribe', inline: true, right_help: true
- .actions
- = button_tag :save, value: :save
- - else
- %h2=_'forms.actions.generic.login'
- = render 'login'
\ No newline at end of file
+ = form_tag update_settings_path do
+ = textfield :name, current_user.name, required: true, heading: 'articles.conference_registration.headings.name', big: true
+ = checkboxes :languages, User.AVAILABLE_LANGUAGES, current_user.languages || [I18n.locale], 'languages', heading: 'articles.conference_registration.headings.languages'
+ = radiobuttons :preferred_language, I18n.backend.enabled_locales, current_user.locale || I18n.locale, 'languages', heading: 'articles.conference_registration.headings.preferred_language'
+ = checkbox :email_subscribe, current_user.is_subscribed != false, 'articles.user_settings.email_subscribe', heading: 'articles.user_settings.headings.email_subscribe', help: 'articles.user_settings.paragraphs.email_subscribe', inline: true, right_help: true
+ .actions
+ = button_tag :save, value: :save
+ - else
+ %h2=_'forms.actions.generic.login'
+ = render 'login'
\ No newline at end of file
diff --git a/app/views/conference_administration/_administrators.html.haml b/app/views/conference_administration/_administrators.html.haml
new file mode 100644
index 0000000..b8d65e5
--- /dev/null
+++ b/app/views/conference_administration/_administrators.html.haml
@@ -0,0 +1,41 @@
+= columns(large: 8, push: { large: 2}) do
+ %h3=_'articles.admin.info.headings.Host_Organizations'
+ %p=_'articles.admin.info.descriptions.Host_Organizations', vars: { city_name: @this_conference.city.city }
+ = admin_update_form do
+ = checkboxes :organizations, (@organizations.map { |org| [org.name, org.id] }), @this_conference.organizations.map(&:id), 'test.test', vertical: true, big: true
+ .actions.right.small
+ = button_tag :save, value: :set_organizations
+ - @this_conference.organizations.each do | organization |
+ %h4=organization.name
+ - if organization.users.present?
+ .details.org-members
+ - organization.users.each do | user |
+ = raw_data_set(:h5, user.name) do
+ = user.email
+ - unless user.id == current_user.id && !current_user.administrator?
+ = admin_update_form class: [:inline, :right] do
+ = hidden_field_tag :user_id, user.id
+ = hidden_field_tag :org_id, organization.id
+ = button_tag :remove_member, value: :remove_org_member, class: [:small, :delete]
+ = admin_update_form class: 'mini-flex-form' do
+ = hidden_field_tag :org_id, organization.id
+ = emailfield :email, nil, required: true
+ = button_tag :add_member, value: :add_org_member, class: :small
+
+ %h3=_'articles.admin.info.headings.External_Administrators'
+ %p=_'articles.admin.info.descriptions.External_Administrators'
+ - if @this_conference.administrators.present?
+ .details.org-members
+ - @this_conference.administrators.each do | user |
+ = raw_data_set(:h5, user.name) do
+ = user.email
+ - unless user.id == current_user.id && !current_user.administrator?
+ = admin_update_form class: [:inline, :right] do
+ = hidden_field_tag :user_id, user.id
+ = button_tag :remove_member, value: :remove_administrator, class: [:small, :delete]
+ = admin_update_form class: 'mini-flex-form' do
+ = userfield :email, nil, required: true
+ -#= emailfield :email, nil, required: true
+ = button_tag :add_member, value: :add_administrator, class: :small
+= columns(large: 2) do
+
diff --git a/app/views/conference_administration/_broadcast.html.haml b/app/views/conference_administration/_broadcast.html.haml
new file mode 100644
index 0000000..2990e25
--- /dev/null
+++ b/app/views/conference_administration/_broadcast.html.haml
@@ -0,0 +1,23 @@
+= columns(medium: 12) do
+ = admin_update_form do
+ - if @broadcast_step == :preview || @broadcast_step == :test
+ = hidden_field_tag :subject, @subject
+ = hidden_field_tag :body, @body
+ = hidden_field_tag :send_to, @send_to
+ - if @broadcast_step == :preview
+ %p= _'articles.conference_registration.paragraphs.admin.broadcast.test', vars: { send_to_count: "#{(@send_to_count || 0)} ".html_safe }
+ - else
+ .warning-info.make-room= _'articles.conference_registration.paragraphs.admin.broadcast.preview', vars: { send_to_count: "#{(@send_to_count || 0)} ".html_safe }
+ .test-preview
+ %h3=@subject
+ = richtext @body, 4
+ .actions.right
+ = button_tag :test, value: :test, class: :secondary if @broadcast_step == :preview
+ = button_with_confirmation :send, (_'modals.admin.broadcast.confirm', vars: { number: "#{(@send_to_count || 0)} ".html_safe }), value: :send, class: :delete if @broadcast_step == :test
+ = button_tag :edit, value: :edit
+ - else
+ = selectfield :send_to, nil, broadcast_options, full: true
+ = textfield :subject, @subject, required: true, big: true
+ = textarea :body, @body, lang: @this_conference.locale, edit_on: :focus
+ .actions.right
+ = button_tag :preview, value: :preview
diff --git a/app/views/conferences/admin/_broadcast_sent.html.haml b/app/views/conference_administration/_broadcast_sent.html.haml
similarity index 100%
rename from app/views/conferences/admin/_broadcast_sent.html.haml
rename to app/views/conference_administration/_broadcast_sent.html.haml
diff --git a/app/views/conference_administration/_dates.html.haml b/app/views/conference_administration/_dates.html.haml
new file mode 100644
index 0000000..b7345b5
--- /dev/null
+++ b/app/views/conference_administration/_dates.html.haml
@@ -0,0 +1,17 @@
+= admin_update_form do
+ = row do
+ = columns(medium: 6, push: { medium: 1 }) do
+ = fieldset :start_date, inline: true, inline_label: true do
+ = month_select @start_month, name: :start_month, label: false
+ = month_day_select @start_day, name: :start_day, label: false
+
+ = row do
+ = columns(medium: 6, push: { medium: 1 }) do
+ = fieldset :end_date, inline: true, inline_label: true do
+ = month_select @end_month, name: :end_month, label: false
+ = month_day_select @end_day, name: :end_day, label: false
+
+ = row do
+ = columns(medium: 6, push: { medium: 1 }) do
+ .actions
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_description.html.haml b/app/views/conference_administration/_description.html.haml
new file mode 100644
index 0000000..399ba00
--- /dev/null
+++ b/app/views/conference_administration/_description.html.haml
@@ -0,0 +1,5 @@
+= columns(medium: 12) do
+ = admin_update_form do
+ = translate_textarea :info, @this_conference, label: 'articles.conference_registration.headings.admin.edit.info', help: 'articles.conference_registration.paragraphs.admin.edit.info', edit_on: :focus
+ .actions.right
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_events.html.haml b/app/views/conference_administration/_events.html.haml
new file mode 100644
index 0000000..bc4363e
--- /dev/null
+++ b/app/views/conference_administration/_events.html.haml
@@ -0,0 +1,41 @@
+- if @this_conference.event_locations.blank?
+ = columns(medium: 12) do
+ .warning-info=_'articles.admin.events.no_locations_warning'
+- else
+ = columns(medium: 12) do
+ - if @events.present? && @event.id.blank?
+ %table.events.admin-edit
+ %tr
+ %th=_'forms.labels.generic.title'
+ %th=_'forms.labels.generic.event_location'
+ %th=_'forms.labels.generic.day'
+ %th=_'forms.labels.generic.time'
+ %th=_'forms.labels.generic.time_span'
+ %th.form
+ - @events.each do | event |
+ %tr
+ %th=event.title
+ %td=_!(event.event_location.present? ? event.event_location.title : '')
+ %td=date(event.start_time.to_date, :weekday)
+ %td=time(event.start_time, :short)
+ %td=hours(event.start_time, event.end_time)
+ %td.form
+ = admin_update_form do
+ = hidden_field_tag :id, event.id
+ = link_to (_'forms.actions.generic.edit'), edit_event_path(@this_conference, event.id), class: [:button, :small, :modify]
+ = button_with_confirmation :delete, (_'modals.admin.generic.delete.confirm', :p, vars: { title: event.title }), value: :delete, class: [:delete, :small]
+
+ = columns(medium: 12) do
+ %h3=_"articles.admin.locations.headings.#{@event.id.present? ? 'edit' : 'add'}_event", :t
+
+ = admin_update_form do
+ = hidden_field_tag :id, @event.id if @event.id.present?
+ .flex-inputs
+ = location_select @event.event_location_id, small: true, stretch: true
+ = day_select @day, small: true, format: :weekday
+ = hour_select @time, small: true
+ = length_select @length, small: true
+ = translate_fields @event, { title: { type: :textfield, big: true, label: 'forms.labels.generic.title' }, info: { type: :textarea, label: 'forms.labels.generic.info', edit_on: :focus } }
+ .actions.next-prev
+ = button_tag :save, value: :save
+ = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true if @event.id.present?
diff --git a/app/views/conference_administration/_hosts_table.html.haml b/app/views/conference_administration/_hosts_table.html.haml
new file mode 100644
index 0000000..804754c
--- /dev/null
+++ b/app/views/conference_administration/_hosts_table.html.haml
@@ -0,0 +1,11 @@
+.guests-housed
+ %h5 Guests Housed:
+ .data="#{@guests_housed} / #{@guests.size}"
+
+%table.hosts.admin-edit
+ - @hosts.each do | id, registration |
+ %tr.host
+ %th
+ .name=registration.user.name
+ .address=registration.housing_data['address']
+ %td.inner-table{colspan: 2}=host_guests_table(registration)
diff --git a/app/views/conference_administration/_housing.html.haml b/app/views/conference_administration/_housing.html.haml
new file mode 100644
index 0000000..42030e2
--- /dev/null
+++ b/app/views/conference_administration/_housing.html.haml
@@ -0,0 +1,10 @@
+- add_inline_script :housing
+= columns(medium: 12) do
+ = admin_update_form id: 'housing-table-form' do
+ #housing-table= render partial: 'hosts_table'
+ #guest-selector
+ = admin_update_form class: 'guest-dlg', id: 'guest-list-table' do
+ %h3 Select a Guest
+ #table
+ .actions
+ = link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, @admin_step, :format => :xlsx), class: [:button, :download]
diff --git a/app/views/conference_administration/_locations.html.haml b/app/views/conference_administration/_locations.html.haml
new file mode 100644
index 0000000..2594798
--- /dev/null
+++ b/app/views/conference_administration/_locations.html.haml
@@ -0,0 +1,42 @@
+- unless @location.present?
+ - if @locations.present?
+ = columns(medium: 12) do
+ %table.locations.admin-edit
+ %tr
+ %th=_'forms.labels.generic.title'
+ %th=_'forms.labels.generic.address'
+ %th=_'articles.workshops.headings.space'
+ %th=_'articles.admin.locations.headings.amenities'
+ %th.form
+ - @locations.each do | location |
+ %tr
+ %th=_!(location.title || '')
+ %td=location_link location
+ %td=location.space.present? ? (_"workshop.options.space.#{location.space}") : ''
+ %td
+ - amenities = location.amenities.present? ? JSON.parse(location.amenities) : []
+ =_!(amenities.collect { |amenity| _"workshop.options.needs.#{amenity}" }).join(', ')
+ %td.form
+ = admin_update_form do
+ = hidden_field_tag :id, location.id
+ = link_to (_'forms.actions.generic.edit'), edit_location_path(@this_conference, location.id), class: [:button, :small, :modify]
+ = button_with_confirmation :delete, (_'modals.admin.generic.delete.confirm', :p, vars: { title: location.title }), value: :delete, class: [:delete, :small]
+= admin_update_form do
+ = columns(medium: 12) do
+ %h5=_"articles.admin.locations.headings.#{@location.present? ? 'edit' : 'add'}_location", :t
+ = hidden_field_tag :id, @location.id if @location.present?
+ = textfield :title, @location.present? ? @location.title : nil, required: true, big: true, help: 'articles.admin.locations.paragraphs.title'
+ .flex-column.address-form
+ = textfield :address, @location.present? ? @location.address : nil, required: true, help: 'articles.admin.locations.paragraphs.address', stretch: true
+ .city=location(@this_conference.city)
+ = columns(medium: 6) do
+ = radiobuttons :space, EventLocation.all_spaces, @space, 'workshop.options.space', vertical: true, heading: 'articles.workshops.headings.space', required: true, help: 'articles.admin.locations.paragraphs.space'
+ = columns(medium: 6) do
+ = checkboxes :needs, EventLocation.all_amenities, @amenities || [], 'workshop.options.needs', vertical: true, heading: 'articles.admin.locations.headings.amenities', help: 'articles.admin.locations.paragraphs.amenities'
+ = columns(medium: 12) do
+ .actions.next-prev
+ - if @location.present?
+ = button_tag :save, value: :save
+ = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true
+ - else
+ = button_tag :create, value: :create
\ No newline at end of file
diff --git a/app/views/conference_administration/_meals.html.haml b/app/views/conference_administration/_meals.html.haml
new file mode 100644
index 0000000..78eb924
--- /dev/null
+++ b/app/views/conference_administration/_meals.html.haml
@@ -0,0 +1,35 @@
+= columns(medium: 12) do
+ - if @this_conference.event_locations.present?
+ - if @this_conference.meals.present?
+ %table.meals.admin-edit
+ %tr
+ %th=_'forms.labels.generic.title'
+ %th=_'forms.labels.generic.info'
+ %th=_'forms.labels.generic.event_location'
+ %th=_'forms.labels.generic.day'
+ %th=_'forms.labels.generic.time'
+ %th.form
+ - @meals.each do | time, meal |
+ %tr
+ %th
+ =_!(meal['title'] || '')
+ %td=_!(meal['info'] || '')
+ %td=_!location_name(meal['location'].to_i)
+ %td=date(meal['day'], :weekday)
+ %td=time(meal['time'].to_f)
+ %td.form
+ = admin_update_form do
+ = hidden_field_tag :meal, time
+ = button_tag :delete, value: :delete, class: [:small, :delete]
+ = admin_update_form do
+ %h3=_'articles.admin.locations.headings.add_meal', :t
+ .flex-inputs
+ = location_select nil, small: true, stretch: true
+ = day_select nil, small: true, format: :weekday
+ = hour_select nil, small: true
+ = textfield :title, nil, required: true, big: true, help: 'articles.admin.locations.paragraphs.meal_title'
+ = textfield :info, nil, help: 'articles.admin.locations.paragraphs.meal_info'
+ .actions.next-prev
+ = button_tag :add_meal, value: :add_meal
+ - else
+ .warning-info=_'articles.admin.meals.no_locations_warning'
\ No newline at end of file
diff --git a/app/views/conference_administration/_payment_message.html.haml b/app/views/conference_administration/_payment_message.html.haml
new file mode 100644
index 0000000..c902889
--- /dev/null
+++ b/app/views/conference_administration/_payment_message.html.haml
@@ -0,0 +1,6 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, @admin_step) do
+ = translate_textarea :payment_message, @this_conference, default: 'articles.conference_registration.paragraphs.Payment', help: 'articles.conference_registration.paragraphs.admin.payment.message', edit_on: :focus, short: true
+
+ .actions.right
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_paypal.html.haml b/app/views/conference_administration/_paypal.html.haml
new file mode 100644
index 0000000..96ea5b4
--- /dev/null
+++ b/app/views/conference_administration/_paypal.html.haml
@@ -0,0 +1,8 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, @admin_step) do
+ = emailfield :paypal_email_address, @this_conference.paypal_email_address || @this_conference.email_address || (@this_conference.organizations.present? && @this_conference.organizations.first.present? ? @this_conference.organizations.first.email_address : nil)
+ = textfield :paypal_username, @this_conference.paypal_username
+ = passwordfield :paypal_password, @this_conference.paypal_password
+ = textfield :paypal_signature, @this_conference.paypal_signature
+ .actions.right
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_poster.html.haml b/app/views/conference_administration/_poster.html.haml
new file mode 100644
index 0000000..97f254b
--- /dev/null
+++ b/app/views/conference_administration/_poster.html.haml
@@ -0,0 +1,5 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, :poster), multipart: true do
+ = filefield :poster, @this_conference.poster, required: true, label: false, preview: true
+ .actions.left
+ = button_tag :upload, value: :upload, id: 'upload-file'
diff --git a/app/views/conference_administration/_providers.html.haml b/app/views/conference_administration/_providers.html.haml
new file mode 100644
index 0000000..6bad602
--- /dev/null
+++ b/app/views/conference_administration/_providers.html.haml
@@ -0,0 +1,2 @@
+= columns(medium: 12) do
+ TO COME
\ No newline at end of file
diff --git a/app/views/conference_administration/_publish_schedule.html.haml b/app/views/conference_administration/_publish_schedule.html.haml
new file mode 100644
index 0000000..c2ed962
--- /dev/null
+++ b/app/views/conference_administration/_publish_schedule.html.haml
@@ -0,0 +1,8 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, @admin_step) do
+ - if @this_conference.workshop_schedule_published
+ %p=_'articles.conference_registration.paragraphs.admin.schedule.published', :p
+ .actions= button_tag :un_publish, value: :publish, class: :delete
+ - else
+ %p=_'articles.conference_registration.paragraphs.admin.schedule.un_published', :p
+ .actions= button_tag :publish, value: :publish
diff --git a/app/views/conference_administration/_registration_status.html.haml b/app/views/conference_administration/_registration_status.html.haml
new file mode 100644
index 0000000..7558d58
--- /dev/null
+++ b/app/views/conference_administration/_registration_status.html.haml
@@ -0,0 +1,5 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, @admin_step) do
+ = selectfield :registration_status, @this_conference.registration_status || 'closed', registration_status_options_list, inline_label: true
+ .actions.left
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_registrations.html.haml b/app/views/conference_administration/_registrations.html.haml
new file mode 100644
index 0000000..25f07e0
--- /dev/null
+++ b/app/views/conference_administration/_registrations.html.haml
@@ -0,0 +1,17 @@
+- add_inline_script :registrations
+= columns(medium: 12) do
+ .goes-fullscreen#registrations-table
+ .flex-column
+ = searchfield :search, nil, big: true, stretch: true
+ %a.button{data: { expands: 'registrations-table' }}='expand'
+ %a.button.delete{data: { contracts: 'registrations-table' }}='close'
+ %a.button.modify{data: { 'opens-modal': 'new-registration' }}='+'
+ .table-scroller
+ = html_table @excel_data, registrations_table_options
+ = admin_update_form id: 'new-registration', class: 'modal-edit' do
+ .modal-edit-overlay{data: { 'closes-modal': 'new-registration' }}
+ .modal-edit-content
+ = html_edit_table @excel_data, registrations_edit_table_options
+ .actions.right
+ %a.button.subdued{data: { 'closes-modal': 'new-registration' }}='Cancel'
+ = button_tag :save, value: :save, class: :modify
diff --git a/app/views/conference_administration/_schedule.html.haml b/app/views/conference_administration/_schedule.html.haml
new file mode 100644
index 0000000..03ffdbe
--- /dev/null
+++ b/app/views/conference_administration/_schedule.html.haml
@@ -0,0 +1,85 @@
+= columns(medium: 12) do
+ - conference = @this_conference || @conference
+ - if conference.event_locations.blank? && @entire_page
+ .warning-info=_'articles.admin.schedule.no_locations_warning'
+ - else
+ - add_inline_script :schedule if @entire_page
+ #schedule-preview
+ - @schedule.each do | day, data |
+ %h4=date(day, :weekday)
+ %table.schedule{class: [data[:locations].present? ? 'has-locations' : 'no-locations', "locations-#{data[:num_locations]}"]}
+ - if data[:locations].present? && data[:locations].values.first != :add
+ %thead
+ %tr
+ %th.corner
+ - data[:locations].each do | id, location |
+ %th=location.is_a?(Symbol) ? '' : location.title
+ %tbody
+ - data[:times].each do | time, time_data |
+ %tr
+ - rowspan = (time_data[:length] * 2).to_i
+ %th=time(time)
+ - if time_data[:type] == :workshop
+ - data[:locations].each do | id, location |
+ - if time_data[:item][:workshops][id].present?
+ - workshop = time_data[:item][:workshops][id][:workshop]
+ - status = time_data[:item][:workshops][id][:status]
+ - else
+ - workshop = status = nil
+ %td{class: [time_data[:type], workshop.present? ? :filled : :open], rowspan: rowspan, data: workshop.present? ? nil : { block: time_data[:item][:block], day: day, location: id }}
+ - if workshop.present? && workshop.event_location.present?
+ = link_to view_workshop_path(@conference.slug, workshop.id), class: 'event-detail-link' do
+ .details
+ .title=workshop.title
+ %template.event-details{data: { href: view_workshop_path(@conference.slug, workshop.id) }}
+ %h1.title=workshop.title
+ %p.address
+ = workshop.event_location.title + _!(': ')
+ = location_link workshop.event_location
+ .workshop-description= richtext workshop.info, 1
+ - if @can_edit
+ = form_tag administration_update_path(conference.slug, @admin_step), class: 'deschedule-workshop' do
+ .status
+ .conflict-score
+ %span.title Conflicts:
+ %span.value="#{status[:conflict_score]} / #{workshop.interested.size}"
+ - if status[:errors].present?
+ .errors
+ - status[:errors].each do | error |
+ .error=_"errors.messages.schedule.#{error[:name].to_s}", vars: error[:i18nVars]
+ = hidden_field_tag :id, workshop.id
+ = button_tag :deschedule, value: :deschedule_workshop, class: [:delete, :small]
+ - elsif @can_edit
+ .title="Block #{time_data[:item][:block] + 1}"
+ - elsif time_data[:type] != :nil
+ %td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
+ - case time_data[:type]
+ - when :meal
+ - location = EventLocation.where(id: time_data[:item]['location'].to_i).first
+ - if location.present?
+ %a.event-detail-link
+ .details
+ .title= time_data[:item]['title']
+ .location= location.title
+ %template.event-details
+ %h1.title=time_data[:item]['title']
+ %p.address
+ = location.title + _!(': ')
+ = location_link location
+ - when :event
+ - if time_data[:item].event_location.present?
+ %a.event-detail-link
+ .details
+ .title= time_data[:item][:title]
+ .location= time_data[:item].event_location.title
+ %template.event-details
+ %h1.title=time_data[:item][:title]
+ %p.address
+ = time_data[:item].event_location.title + _!(': ')
+ = location_link time_data[:item].event_location
+ = richtext time_data[:item][:info], 1
+ - if @entire_page
+ #workshop-selector
+ = form_tag administration_update_path(@this_conference.slug, @admin_step), class: 'workshop-dlg', id: 'workshop-table-form' do
+ %h3 Select a Workshop
+ #table
diff --git a/app/views/conference_administration/_select_guest_table.html.haml b/app/views/conference_administration/_select_guest_table.html.haml
new file mode 100644
index 0000000..e33c2fc
--- /dev/null
+++ b/app/views/conference_administration/_select_guest_table.html.haml
@@ -0,0 +1,46 @@
+= hidden_field_tag :host, host.id
+.host-field
+ %h4.inline=_'forms.labels.generic.name'
+ %span.plain-value= host.user.name
+.host-field
+ %h4.inline=_'articles.conference_registration.headings.host.availability'
+ %span.plain-value= host.housing_data['availability'].present? && host.housing_data['availability'][1].present? ? date_span(host.housing_data['availability'][0].to_date, host.housing_data['availability'][1].to_date) : ''
+- if host.housing_data['considerations'].present?
+ .host-field
+ %h4.inline=_'articles.conference_registration.headings.host.considerations'
+ %span.plain-value= (host.housing_data['considerations'].map { | consideration | _"articles.conference_registration.host.considerations.#{consideration}" }).join(', ')
+- if sanitize(host.housing_data['notes'], tags: []).present?
+ .host-field
+ %h4=_'articles.conference_registration.headings.host.notes'
+ %blockquote= host.housing_data['notes'].html_safe
+%table.guests.admin-edit
+ %tr
+ %th.corner
+ %th=_'forms.labels.generic.city'
+ %th=_'forms.labels.generic.housing'
+ %th=_'articles.admin.housing.headings.arrival_departure'
+ %th=_'articles.conference_registration.headings.companion'
+ %th=_'forms.labels.generic.food'
+ %th=_'forms.labels.generic.allergies'
+ %th=_'forms.labels.generic.other'
+ - @guests.each do | id, registration |
+ %tr.selectable{class: get_housing_match(host, registration, space).to_s.gsub('_', '-'), data: {host: host.id, guest: id, space: space}}
+ %th=registration.user.name
+ %td=registration.city
+ %td=registration.housing.present? ? (_"articles.conference_registration.questions.housing.#{registration.housing}") : ''
+ %td=date_span(registration.arrival.to_date, registration.departure.to_date)
+ - companion = companion(registration)
+ %td=companion.present? ? (companion.is_a?(User) ? companion.named_email : (_"articles.conference_registration.terms.registration_status.#{companion}")) : ''
+ %td=registration.food.present? ? (_"articles.conference_registration.questions.food.#{registration.food}") : ''
+ %td=registration.allergies
+ %td
+ .p=registration.other
+
+.legend
+ %h4 Legend
+ %ul
+ %li.good-match Good Match
+ %li.bad-match Poor Match
+ %li.selected-space Also in this space
+ %li.other-space Also with this host
+ %li.other-host Already hosted
diff --git a/app/views/conferences/admin/_select_workshop_table.html.haml b/app/views/conference_administration/_select_workshop_table.html.haml
similarity index 100%
rename from app/views/conferences/admin/_select_workshop_table.html.haml
rename to app/views/conference_administration/_select_workshop_table.html.haml
diff --git a/app/views/conference_administration/_stats.html.haml b/app/views/conference_administration/_stats.html.haml
new file mode 100644
index 0000000..5cd1972
--- /dev/null
+++ b/app/views/conference_administration/_stats.html.haml
@@ -0,0 +1,21 @@
+= columns(medium: 12) do
+ .details
+ = data_set(:h3, 'articles.admin.stats.headings.completed_registrations') do
+ = (@completed_registrations || 0).to_s
+ = data_set(:h3, 'articles.admin.stats.headings.incomplete_registrations') do
+ = ((@registration_count - @completed_registrations) || 0).to_s
+ = data_set(:h3, 'articles.admin.stats.headings.bikes') do
+ = (@completed_registrations || 0) > 0 ? "#{@bikes} (#{number_to_percentage(@bikes / @completed_registrations.to_f * 100.0)})" : "0"
+ = data_set(:h3, 'articles.admin.stats.headings.food.meat') do
+ = (@food[:all] || 0) > 0 ? "#{@food[:meat]} (#{number_to_percentage(@food[:meat] / @food[:all].to_f * 100.0)})" : "0"
+ = data_set(:h3, 'articles.admin.stats.headings.food.vegetarian') do
+ = (@food[:all] || 0) > 0 ? "#{@food[:vegetarian]} (#{number_to_percentage(@food[:vegetarian] / @food[:all].to_f * 100.0)})" : "0"
+ = data_set(:h3, 'articles.admin.stats.headings.food.vegan') do
+ = (@food[:all] || 0) > 0 ? "#{@food[:vegan]} (#{number_to_percentage(@food[:vegan] / @food[:all].to_f * 100.0)})" : "0"
+ = data_set(:h3, 'articles.admin.stats.headings.donation_count') do
+ = (@completed_registrations || 0) > 0 ? "#{@donation_count} (#{number_to_percentage(@donation_count / @completed_registrations.to_f * 100.0)})" : "0"
+ = data_set(:h3, 'articles.admin.stats.headings.donation_total') do
+ = "$#{@donations || 0.00}"
+ .actions
+ = link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :stats, :format => :xlsx), class: [:button, :download]
+ = link_to (_'links.download.Organizations_Excel'), administration_step_path(@this_conference.slug, :organizations, :format => :xlsx), class: [:button, :download, :subdued]
diff --git a/app/views/conference_administration/_suggested_amounts.html.haml b/app/views/conference_administration/_suggested_amounts.html.haml
new file mode 100644
index 0000000..7cd7f5d
--- /dev/null
+++ b/app/views/conference_administration/_suggested_amounts.html.haml
@@ -0,0 +1,8 @@
+= columns(medium: 12) do
+ = form_tag administration_update_path(@this_conference.slug, @admin_step) do
+ = fieldset :payment_amounts, label: false do
+ - payment_amounts = @this_conference.payment_amounts.present? ? @this_conference.payment_amounts : Conference.default_payment_amounts
+ - for i in 1..5 do
+ = numberfield "payment_amounts[#{i - 1}]", payment_amounts[i - 1], step: 0.01, min: 0.00, label: false
+ .actions.right
+ = button_tag :save, value: :save
diff --git a/app/views/conference_administration/_workshop_times.html.haml b/app/views/conference_administration/_workshop_times.html.haml
new file mode 100644
index 0000000..c175b73
--- /dev/null
+++ b/app/views/conference_administration/_workshop_times.html.haml
@@ -0,0 +1,19 @@
+= columns(medium: 12) do
+ .table.workshop-blocks
+ .table-tr.header
+ .table-th=_'forms.labels.generic.block_number'
+ .table-th=_'forms.labels.generic.time'
+ .table-th=_'forms.labels.generic.length'
+ .table-th=_'forms.labels.generic.days'
+ .table-th.form
+ - @workshop_blocks.each_with_index do | info, block |
+ - is_new = info['time'].blank?
+ = form_tag administration_update_path(@this_conference.slug, @admin_step), class: ['table-tr', is_new ? 'new' : 'saved'] do
+ .table-th.center.big= is_new ? '' : (block + 1)
+ .table-td=hour_select info['time'], small: true, label: false
+ .table-td=length_select info['length'], {small: true, label: false}, 0.5, 2
+ .table-td=checkboxes :days, @block_days, info['days'].map(&:to_i), 'date.day_names', vertical: true, small: true
+ .table-td.form
+ = hidden_field_tag :workshop_block, block
+ = button_tag :delete_block, value: :delete_block, class: [:small, :delete] if block == @workshop_blocks.length - 2
+ = button_tag (is_new ? :add_block : :update_block), value: :save_block, class: [:small, :add]
diff --git a/app/views/conference_administration/administration.html.haml b/app/views/conference_administration/administration.html.haml
new file mode 100644
index 0000000..e42acf3
--- /dev/null
+++ b/app/views/conference_administration/administration.html.haml
@@ -0,0 +1,27 @@
+- body_class 'banner-bottom' unless @this_conference.poster.present?
+- content_for :banner do
+ = render :partial => 'application/header', :locals => { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
+
+%article
+ = row do
+ = columns(medium: 12) do
+ = columns(medium: 12, id: :conferences, class: 'list-view') do
+ %h2=@this_conference.title
+ %p=_'articles.admin.paragraphs.administration', :p
+ %ul.break
+ - administration_steps.each do | step, actions |
+ %li
+ .info
+ %h3=_"articles.admin.#{step}.heading", :t
+ .help
+ %p=_"articles.admin.#{step}.description", :p
+
+ .actions.figures
+ - actions.each do | action |
+ - action_text = (_"articles.admin.#{step}.headings.#{action}", :t)
+ .figure
+ = link_to administration_step_path(@this_conference.slug, action.to_s) do
+ %header= action_text
+ .body
+ = svg "admin/#{action.to_s}", action_text
+ .description=(_"articles.admin.#{step}.descriptions.#{action}", :s)
diff --git a/app/views/conference_administration/administration_step.html.haml b/app/views/conference_administration/administration_step.html.haml
new file mode 100644
index 0000000..b35ea89
--- /dev/null
+++ b/app/views/conference_administration/administration_step.html.haml
@@ -0,0 +1,38 @@
+- body_class 'banner-bottom' unless @this_conference.poster.present?
+- content_for :banner do
+ = render :partial => 'application/header', :locals => { page_group: :administration, page_key: 'Administration', image_file: @this_conference.poster_url || 'admin.jpg'}
+
+%article{id: "admin-#{@admin_step}"}
+ = row do
+ = columns(medium: 12) do
+ %h2.floating=(_"articles.admin.#{@admin_group}.headings.#{@admin_step}", :t)
+ = row do
+ = columns(medium: 12) do
+ %nav.sub-nav
+ %ul
+ %li=link_to (_'articles.admin.headings.back'), administrate_conference_path(@this_conference.slug)
+ - administration_steps[@admin_group].each do | step |
+ %li
+ - title = (_"articles.admin.#{@admin_group}.headings.#{step}", :t)
+ - if step == @admin_step.to_sym
+ = title
+ - else
+ = link_to title, administration_step_path(@this_conference.slug, step.to_s)
+ - if @success_message.present?
+ = row do
+ = columns(class: 'info-messages') do
+ .success-info.info-message=_"success.messages.admin.#{@success_message}", :s
+ - if @error_message.present?
+ = row do
+ = columns(class: 'info-messages') do
+ .error-info.info-message=_"errors.messages.admin.#{@error_message}", :s
+ - if @warnings.present?
+ = row class: 'warnings', tag: :ul do
+ - @warnings.each do | warning |
+ = columns tag: :li, class: 'warning-info info-message' do
+ = warning
+ = row do
+ = columns(medium: 12) do
+ %p=((_"articles.admin.#{@admin_group}.descriptions.#{@admin_step}", :s)) unless @hide_description === true
+ = row do
+ = render @admin_step
diff --git a/app/views/conferences/_administration.html.haml b/app/views/conferences/_administration.html.haml
index 62c36c5..6cb87d9 100644
--- a/app/views/conferences/_administration.html.haml
+++ b/app/views/conferences/_administration.html.haml
@@ -1,6 +1,6 @@
= columns(medium: 3, large: 2) do
- = admin_menu
+ = admin_menu
= columns(medium: 9, large: 10) do
- %h3.subtitle=_("menu.submenu.admin.#{@admin_step.titlecase.gsub(/\s/, '_')}")
- %p=(_"articles.admin.#{@admin_step.gsub(/\s/, '_')}.description", :p) unless @hide_description === true
- %div{id: "admin-#{@admin_step}"}= render "conferences/admin/#{@admin_step}"
+ %h3.subtitle=_("menu.submenu.admin.#{@admin_step.titlecase.gsub(/\s/, '_')}")
+ %p=(_"articles.admin.#{@admin_step.gsub(/\s/, '_')}.description", :p) unless @hide_description === true
+ %div{id: "admin-#{@admin_step}"}= render "conferences/admin/#{@admin_step}"
diff --git a/app/views/conferences/_conference.html.haml b/app/views/conferences/_conference.html.haml
new file mode 100644
index 0000000..eb41317
--- /dev/null
+++ b/app/views/conferences/_conference.html.haml
@@ -0,0 +1,34 @@
+- links ||= [ :register ]
+- sections ||= [ :info ]
+%article
+ = row(tag: :header) do
+ = columns(class: 'conference-banner') do
+ .title
+ %h1=_!conference.title
+ .details
+ %h2.primary=location(conference.city || conference.location) if conference.city_name.present?
+ - if conference.start_date.present? && conference.end_date.present?
+ .secondary
+ = date_span(conference.start_date.to_date, conference.end_date.to_date)
+ - if conference.poster.present?
+ %img{src: conference.poster.full.url || image_path('default_poster.jpg'), role: :presentation, alt: (_'images.conference.poster', vars: { conference_title: conference.title })}
+
+ = row(class: 'conference-details') do
+ = columns(medium: 10, push: {medium: 1}) do
+ %h2=_!conference.title if conference.poster.present?
+ = richtext conference.info
+ - if conference.registration_status == :open && sections.include?(:workshops)
+ - if conference.workshop_schedule_published
+ - add_inline_script :home_schedule
+ %h3=_'articles.workshops.headings.Schedule'
+ = render 'conferences/admin/schedule'
+ - else
+ %h3=_'articles.workshops.headings.Proposed_Workshops'
+ %p=_'articles.workshops.paragraphs.Proposed_Workshops'
+ = render 'workshops/workshop_previews', :workshops => (conference.workshops.sort { |a, b| a.title.downcase <=> b.title.downcase })
+ .links
+ = (link_to (_'forms.actions.generic.register'), register_path(conference.slug), class: [:button, :register]) if links.include?(:register) && conference.registration_status == :open
+ = (link_to (_'articles.workshops.info.read_more'), conference_path(conference.slug), class: :button) if links.include?(:read_more)
+ = (link_to (_'forms.actions.generic.administrate'), administrate_conference_path(conference.slug), class: [:button]) if links.include?(:administrate)
+ = (link_to (_'forms.actions.generic.edit'), edit_conference_path(conference.slug), class: [:button, :subdued]) if links.include?(:edit)
+
diff --git a/app/views/conferences/_header.html.haml b/app/views/conferences/_header.html.haml
index 554e5c4..6e7cfd8 100644
--- a/app/views/conferences/_header.html.haml
+++ b/app/views/conferences/_header.html.haml
@@ -1,10 +1,10 @@
- content_for :banner do
.title
- %h1=_!@conference.title
+ %h1=_!@this_conference.title
.details
- %h2.primary=location(@conference.organizations.first.locations.first)
+ %h2.primary=location(@this_conference.city)
.secondary
- = date_span(@conference.start_date.to_date, @conference.end_date.to_date)
- %img{src: @conference.poster.full.url || image_path('default_poster.jpg'), role: :presentation, alt: (_'images.conference.poster', vars: { conference_title: @conference.title })}
+ = date_span(@this_conference.start_date.to_date, @this_conference.end_date.to_date)
+ %img{src: @this_conference.poster.full.url || image_path('default_poster.jpg'), role: :presentation, alt: (_'images.conference.poster', vars: { conference_title: @this_conference.title })}
- content_for :og_image do
- = @conference.poster.full.url || image_path('default_poster.jpg')
+ = @this_conference.poster.full.url || image_path('default_poster.jpg')
diff --git a/app/views/conferences/_hosting.html.haml b/app/views/conferences/_hosting.html.haml
index f8927eb..fbf596c 100644
--- a/app/views/conferences/_hosting.html.haml
+++ b/app/views/conferences/_hosting.html.haml
@@ -1,20 +1,20 @@
= columns(medium: 12) do
- = form_tag register_path(@this_conference.slug) do
- = checkbox :can_provide_housing, @registration.can_provide_housing, 'articles.conference_registration.can_provide_housing', heading: 'articles.conference_registration.headings.can_provide_housing', help: 'articles.conference_registration.paragraphs.can_provide_housing', inline: true, toggles: 'hosting-options', centered: true
- #hosting-options
- = checkbox :not_attending, @registration.is_attending == 'n', 'articles.conference_registration.not_attending', help: 'articles.conference_registration.paragraphs.not_attending', inline: true, right_help: true
- = textfield :address, @hosting_data['address'], required: true, heading: 'articles.conference_registration.headings.host.address', help: 'articles.conference_registration.paragraphs.host.address'
- = telephonefield :phone, @hosting_data['phone'], required: true
- = fieldset :space, heading: 'articles.conference_registration.headings.host.space', help: 'articles.conference_registration.paragraphs.host.space' do
- - ConferenceRegistration.all_spaces.each do | space |
- = numberfield space, @hosting_data['space'][space.to_s] || 0, min: 0, required: true
- = fieldset :hosting_dates, heading: 'articles.conference_registration.headings.host.availability', help: 'articles.conference_registration.paragraphs.host.availability' do
- - first_day_options = conference_days_options_list(:before)
- - last_day_options = conference_days_options_list(:after)
- = selectfield :first_day, @hosting_data['availability'][0] || first_day_options.first.last, first_day_options
- = selectfield :last_day, @hosting_data['availability'][1] || last_day_options.last.last, last_day_options
- = checkboxes :considerations, ConferenceRegistration.all_considerations, @hosting_data['considerations'], 'articles.conference_registration.host.considerations', heading: 'articles.conference_registration.headings.host.considerations', help: 'articles.conference_registration.paragraphs.host.considerations', vertical: true
- = textarea :notes, @hosting_data['notes'], help: 'articles.conference_registration.paragraphs.host.notes', edit_on: :focus
- .actions.next-prev
- = button_tag (params[:step] == :save ? :save : :next), value: :hosting
- = button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true
+ = form_tag register_path(@this_conference.slug) do
+ = checkbox :can_provide_housing, @registration.can_provide_housing, 'articles.conference_registration.can_provide_housing', heading: 'articles.conference_registration.headings.can_provide_housing', help: 'articles.conference_registration.paragraphs.can_provide_housing', inline: true, toggles: 'hosting-options', centered: true
+ #hosting-options
+ = checkbox :not_attending, @registration.is_attending == 'n', 'articles.conference_registration.not_attending', help: 'articles.conference_registration.paragraphs.not_attending', inline: true, right_help: true
+ = textfield :address, @hosting_data['address'], required: true, heading: 'articles.conference_registration.headings.host.address', help: 'articles.conference_registration.paragraphs.host.address'
+ = telephonefield :phone, @hosting_data['phone'], required: true
+ = fieldset :space, heading: 'articles.conference_registration.headings.host.space', help: 'articles.conference_registration.paragraphs.host.space' do
+ - ConferenceRegistration.all_spaces.each do | space |
+ = numberfield space, @hosting_data['space'][space.to_s] || 0, min: 0, required: true
+ = fieldset :hosting_dates, heading: 'articles.conference_registration.headings.host.availability', help: 'articles.conference_registration.paragraphs.host.availability' do
+ - first_day_options = conference_days_options_list(:before)
+ - last_day_options = conference_days_options_list(:after)
+ = selectfield :first_day, @hosting_data['availability'][0] || first_day_options.first.last, first_day_options
+ = selectfield :last_day, @hosting_data['availability'][1] || last_day_options.last.last, last_day_options
+ = checkboxes :considerations, ConferenceRegistration.all_considerations, @hosting_data['considerations'], 'articles.conference_registration.host.considerations', heading: 'articles.conference_registration.headings.host.considerations', help: 'articles.conference_registration.paragraphs.host.considerations', vertical: true
+ = textarea :notes, @hosting_data['notes'], help: 'articles.conference_registration.paragraphs.host.notes', edit_on: :focus
+ .actions.next-prev
+ = button_tag (params[:step] == :save ? :save : :next), value: :hosting
+ = button_tag :previous, value: :prev_contact_info, class: :subdued, formnovalidate: true
diff --git a/app/views/conferences/_page_header.html.haml b/app/views/conferences/_page_header.html.haml
index 1dbbdee..bd4e46e 100644
--- a/app/views/conferences/_page_header.html.haml
+++ b/app/views/conferences/_page_header.html.haml
@@ -1 +1 @@
-= render :partial => 'application/header', :locals => {:page_group => :conferences, :page_key => page_key, :image_file => @conference.poster_url}
+= render :partial => 'application/header', :locals => {:page_group => :conferences, :page_key => page_key, :image_file => @this_conference.poster_url}
diff --git a/app/views/conferences/_payment.html.haml b/app/views/conferences/_payment.html.haml
index ceeddd5..4a5143e 100644
--- a/app/views/conferences/_payment.html.haml
+++ b/app/views/conferences/_payment.html.haml
@@ -1,29 +1,29 @@
= columns(medium: 12) do
- - if @registration.registration_fees_paid.present?
- %p
- %strong=_'articles.conference_registration.paragraphs.Payment_Made', :p, vars: { fees_paid: (number_to_currency @registration.registration_fees_paid, unit: '$') }
- = _'articles.conference_registration.paragraphs.Payment_Add'
- = @this_conference.payment_message.html_safe if @this_conference.payment_message.present?
- - elsif @this_conference.payment_message.present?
- = @this_conference.payment_message.html_safe
- - else
- %p=@this_conference.payment_message || (_'articles.conference_registration.paragraphs.Payment', :p)
+ - if @registration.registration_fees_paid.present?
+ %p
+ %strong=_'articles.conference_registration.paragraphs.Payment_Made', :p, vars: { fees_paid: (number_to_currency @registration.registration_fees_paid, unit: '$') }
+ = _'articles.conference_registration.paragraphs.Payment_Add'
+ = @this_conference.payment_message.html_safe if @this_conference.payment_message.present?
+ - elsif @this_conference.payment_message.present?
+ = @this_conference.payment_message.html_safe
+ - else
+ %p=@this_conference.payment_message || (_'articles.conference_registration.paragraphs.Payment', :p)
= columns(large: 9, push: 1) do
- = form_tag register_path(@this_conference.slug), class: :payment do
- = hidden_field_tag :button, :payment
- - payment_amounts = @this_conference.payment_amounts.present? ? @this_conference.payment_amounts : Conference.default_payment_amounts
- .graded-options{class: "option-count-#{payment_amounts.size}"}
- - payment_amounts.each_with_index do | amount, i |
- = button_tag :amount_25, name: :amount, value: amount.to_f.to_s, class: "option-#{i + 1}" do
- =_! (number_to_currency amount, unit: '$')
- = form_tag register_path(@this_conference.slug), class: ['custom-payment', :centered] do
- %span.currency=_!'$'
- = numberfield :amount, nil, required: true, step: 0.01, min: 0.00, label: false
- = button_tag :custom_amount, value: :payment
- %p=_'articles.conference_registration.paragraphs.currency','(amounts are in $USD)'
- = form_tag register_path(@this_conference.slug), class: :payment do
- = hidden_field_tag :button, :payment
- .actions.skip
- = button_tag :skip, name: :amount, value: '0.0'
+ = form_tag register_path(@this_conference.slug), class: :payment do
+ = hidden_field_tag :button, :payment
+ - payment_amounts = @this_conference.payment_amounts.present? ? @this_conference.payment_amounts : Conference.default_payment_amounts
+ .graded-options{class: "option-count-#{payment_amounts.size}"}
+ - payment_amounts.each_with_index do | amount, i |
+ = button_tag :amount_25, name: :amount, value: amount.to_f.to_s, class: "option-#{i + 1}" do
+ =_! (number_to_currency amount, unit: '$')
+ = form_tag register_path(@this_conference.slug), class: ['custom-payment', :centered] do
+ %span.currency=_!'$'
+ = numberfield :amount, nil, required: true, step: 0.01, min: 0.00, label: false
+ = button_tag :custom_amount, value: :payment
+ %p=_'articles.conference_registration.paragraphs.currency','(amounts are in $USD)'
+ = form_tag register_path(@this_conference.slug), class: :payment do
+ = hidden_field_tag :button, :payment
+ .actions.skip
+ = button_tag :skip, name: :amount, value: '0.0'
= columns(large: 2)
diff --git a/app/views/conferences/_register_new_organization.html.haml b/app/views/conferences/_register_new_organization.html.haml
index 966cf2e..b5d4c3a 100644
--- a/app/views/conferences/_register_new_organization.html.haml
+++ b/app/views/conferences/_register_new_organization.html.haml
@@ -1,29 +1,29 @@
-- org_index = session[:registration][:new_org_index] || 0
-= hidden_field_tag :new_org_index, org_index
-- if session[:registration][:new_organization].length > 1
- %h3=_'registration.new_organization.list.title','Your New Organizations'
- %ul.columns.medium-11.medium-offset-1.end
- - session[:registration][:new_organization].each_with_index do |new_organization, index|
- - if new_organization.present? && new_organization[:name].present?
- %li
- - if index == org_index && new_organization[:name]
- %strong=new_organization[:name]
- - else
- = new_organization[:name]
-%h3=_'registration.new_organization.title','Your Organization Information'
-.columns.medium-12
- = text_field_tag :organization_name, session[:registration][:new_organization][org_index][:name], :required => true
-.columns.medium-12= text_area_tag :organization_info, session[:registration][:new_organization][org_index][:info], :data => {:editor => ""}
-.columns.medium-7
- = email_field_tag :organization_email, session[:registration][:new_organization][org_index][:email], :required => true
- = text_field_tag :organization_street, session[:registration][:new_organization][org_index][:street], :required => true
- = text_field_tag :organization_city, session[:registration][:new_organization][org_index][:city], :required => true
- = country_select_tag :organization_country, session[:registration][:new_organization][org_index][:country], :required => true
- = subregion_select_tag :organization_territory, session[:registration][:new_organization][org_index][:territory], session[:registration][:new_organization][org_index][:country] || 'US', html: {class: session[:registration][:new_organization][org_index][:country] ? 'can' : 'cant', data: {:country => session[:registration][:new_organization][org_index][:country]}}
-.columns.medium-5
- = image_field_tag :logo
-.columns
- = check_box_tag :add_another_org, (org_index <= session[:registration][:new_organization].length - 1)
-
-- content_for :footer_scripts do
- = javascript_include_tag 'editor'
\ No newline at end of file
+- org_index = session[:registration][:new_org_index] || 0
+= hidden_field_tag :new_org_index, org_index
+- if session[:registration][:new_organization].length > 1
+ %h3=_'registration.new_organization.list.title','Your New Organizations'
+ %ul.columns.medium-11.medium-offset-1.end
+ - session[:registration][:new_organization].each_with_index do |new_organization, index|
+ - if new_organization.present? && new_organization[:name].present?
+ %li
+ - if index == org_index && new_organization[:name]
+ %strong=new_organization[:name]
+ - else
+ = new_organization[:name]
+%h3=_'registration.new_organization.title','Your Organization Information'
+.columns.medium-12
+ = text_field_tag :organization_name, session[:registration][:new_organization][org_index][:name], :required => true
+.columns.medium-12= text_area_tag :organization_info, session[:registration][:new_organization][org_index][:info], :data => {:editor => ""}
+.columns.medium-7
+ = email_field_tag :organization_email, session[:registration][:new_organization][org_index][:email], :required => true
+ = text_field_tag :organization_street, session[:registration][:new_organization][org_index][:street], :required => true
+ = text_field_tag :organization_city, session[:registration][:new_organization][org_index][:city], :required => true
+ = country_select_tag :organization_country, session[:registration][:new_organization][org_index][:country], :required => true
+ = subregion_select_tag :organization_territory, session[:registration][:new_organization][org_index][:territory], session[:registration][:new_organization][org_index][:country] || 'US', html: {class: session[:registration][:new_organization][org_index][:country] ? 'can' : 'cant', data: {:country => session[:registration][:new_organization][org_index][:country]}}
+.columns.medium-5
+ = image_field_tag :logo
+.columns
+ = check_box_tag :add_another_org, (org_index <= session[:registration][:new_organization].length - 1)
+
+- content_for :footer_scripts do
+ = javascript_include_tag 'editor'
\ No newline at end of file
diff --git a/app/views/conferences/_stats.html.haml b/app/views/conferences/_stats.html.haml
index 4ca98d3..316169e 100644
--- a/app/views/conferences/_stats.html.haml
+++ b/app/views/conferences/_stats.html.haml
@@ -1,59 +1,59 @@
= columns(medium: 12) do
- %h2=_'articles.conference_registration.headings.Stats'
- %p=_'articles.conference_registration.paragraphs.Stats', :p
+ %h2=_'articles.conference_registration.headings.Stats'
+ %p=_'articles.conference_registration.paragraphs.Stats', :p
= columns(medium: 6) do
- %ul.stats
- %li
- %h3=_'articles.conference_registration.terms.Total_Registrations'
- .stat.important=_!@total_registrations
- %li.money
- %h3=_'articles.conference_registration.terms.Total_Donations'
- .stat=money @total_donations
- %li
- %h3=_'articles.conference_registration.terms.Donation_Count'
- .stat
- = _!"#{@donation_count} / #{@total_registrations}"
- %li
- %h3=_'articles.conference_registration.terms.Housing'
- .breakdown
- - @housing.each do |h, v|
- - unless h == :none
- .stat-with-label
- .label=_"articles.conference_registration.questions.housing.#{h}"
- .stat=_!v
- %li
- %h3=_'articles.conference_registration.terms.Bikes'
- .breakdown
- .stat=_!"#{@bike_count} / #{@total_registrations}"
- - @bikes.each do |h, v|
- - unless h == :none
- .stat-with-label
- .label=_"articles.conference_registration.questions.bikes.#{h}"
- .stat.percent=_!percent(v.to_f / @bike_count)
- %li
- %h3=_'articles.conference_registration.terms.Food'
- .breakdown
- - @food.each do |h, v|
- .stat-with-label
- .label=_"articles.conference_registration.questions.food.#{h}"
- .stat.percent=_!percent(v.to_f / @total_registrations)
- %li
- %h3=_'articles.conference_registration.terms.Languages'
- .breakdown
- - @languages.each do |h, v|
- .stat-with-label
- .label=_"languages.#{h}"
- .stat.percent=_!percent(v.to_f / @total_registrations)
+ %ul.stats
+ %li
+ %h3=_'articles.conference_registration.terms.Total_Registrations'
+ .stat.important=_!@total_registrations
+ %li.money
+ %h3=_'articles.conference_registration.terms.Total_Donations'
+ .stat=money @total_donations
+ %li
+ %h3=_'articles.conference_registration.terms.Donation_Count'
+ .stat
+ = _!"#{@donation_count} / #{@total_registrations}"
+ %li
+ %h3=_'articles.conference_registration.terms.Housing'
+ .breakdown
+ - @housing.each do |h, v|
+ - unless h == :none
+ .stat-with-label
+ .label=_"articles.conference_registration.questions.housing.#{h}"
+ .stat=_!v
+ %li
+ %h3=_'articles.conference_registration.terms.Bikes'
+ .breakdown
+ .stat=_!"#{@bike_count} / #{@total_registrations}"
+ - @bikes.each do |h, v|
+ - unless h == :none
+ .stat-with-label
+ .label=_"articles.conference_registration.questions.bikes.#{h}"
+ .stat.percent=_!percent(v.to_f / @bike_count)
+ %li
+ %h3=_'articles.conference_registration.terms.Food'
+ .breakdown
+ - @food.each do |h, v|
+ .stat-with-label
+ .label=_"articles.conference_registration.questions.food.#{h}"
+ .stat.percent=_!percent(v.to_f / @total_registrations)
+ %li
+ %h3=_'articles.conference_registration.terms.Languages'
+ .breakdown
+ - @languages.each do |h, v|
+ .stat-with-label
+ .label=_"languages.#{h}"
+ .stat.percent=_!percent(v.to_f / @total_registrations)
= columns(medium: 6) do
- .allergies
- %h3=_'articles.conference_registration.headings.Allergies'
- %ul
- - @allergies.each do |a|
- %li
- %p=_!a
- .other
- %h3=_'articles.conference_registration.headings.other'
- %ul
- - @other.each do |o|
- %li
- %p=_!o
+ .allergies
+ %h3=_'articles.conference_registration.headings.Allergies'
+ %ul
+ - @allergies.each do |a|
+ %li
+ %p=_!a
+ .other
+ %h3=_'articles.conference_registration.headings.other'
+ %ul
+ - @other.each do |o|
+ %li
+ %p=_!o
diff --git a/app/views/conferences/admin/_broadcast.html.haml b/app/views/conferences/admin/_broadcast.html.haml
deleted file mode 100644
index 34314a0..0000000
--- a/app/views/conferences/admin/_broadcast.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-= form_tag administration_update_path(@this_conference.slug, :broadcast) do
- - if @broadcast_step == :preview || @broadcast_step == :test
- = hidden_field_tag :subject, @subject
- = hidden_field_tag :body, @body
- = hidden_field_tag :send_to, @send_to
- - if @broadcast_step == :preview
- %p= _'articles.conference_registration.paragraphs.admin.broadcast.test', vars: { send_to_count: "#{(@send_to_count || 0)} ".html_safe }
- - else
- .warning-info.make-room= _'articles.conference_registration.paragraphs.admin.broadcast.preview', vars: { send_to_count: "#{(@send_to_count || 0)} ".html_safe }
- .test-preview
- %h4=@subject
- = richtext @body, 4
- .actions.right
- = button_tag :test, value: :test, class: :secondary if @broadcast_step == :preview
- = button_with_confirmation :send, (_'modals.admin.broadcast.confirm', vars: { number: "#{(@send_to_count || 0)} ".html_safe }), value: :send, class: :delete if @broadcast_step == :test
- = button_tag :edit, value: :edit
- - else
- = selectfield :send_to, nil, broadcast_options, full: true
- = textfield :subject, @subject, required: true, big: true
- = textarea :body, @body, lang: @this_conference.locale, edit_on: :focus
- .actions.right
- = button_tag :preview, value: :preview
diff --git a/app/views/conferences/admin/_edit.html.haml b/app/views/conferences/admin/_edit.html.haml
deleted file mode 100644
index 3f395a2..0000000
--- a/app/views/conferences/admin/_edit.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-= form_tag administration_update_path(@this_conference.slug, :edit) do
- = selectfield :registration_status, @this_conference.registration_status || 'closed', registration_status_options_list, small: true, inline_label: true
- = textarea :info, @this_conference.info!, heading: 'articles.conference_registration.headings.admin.edit.info', help: 'articles.conference_registration.paragraphs.admin.edit.info', lang: @this_conference.locale, edit_on: :focus
- - I18n.backend.enabled_locales.each do | locale |
- - if locale.to_s != @this_conference.locale.to_s
- = textarea "info_translations[#{locale.to_s}]", @this_conference._info(locale), label: 'translate.pages.Locale_Translation', vars: { language: _("languages.#{locale}") }, lang: locale, edit_on: :focus
- .actions.right
- = button_tag :save, value: :save
-%h4=_'articles.admin.edit.headings.host_organizations'
-- @this_conference.organizations.each do | organization |
- %p=organization.name
- %h5=_'articles.admin.edit.headings.members'
- .details.org-members.inline
- - organization.users.each do | user |
- = raw_data_set(:h6, user.name) do
- = user.email
- = form_tag administration_update_path(@this_conference.slug, :edit), class: 'mini-flex-form' do
- = hidden_field_tag :org_id, organization.id
- = emailfield :email, nil, required: true
- = button_tag :add_member, value: :add_member, class: :small
diff --git a/app/views/conferences/admin/_events.html.haml b/app/views/conferences/admin/_events.html.haml
deleted file mode 100644
index 4ca747d..0000000
--- a/app/views/conferences/admin/_events.html.haml
+++ /dev/null
@@ -1,42 +0,0 @@
-- if @this_conference.event_locations.blank?
- .warning-info=_'articles.admin.events.no_locations_warning'
-- else
- - if @events.present?
- %table.events.admin-edit
- %tr
- %th=_'forms.labels.generic.title'
- %th=_'forms.labels.generic.event_location'
- %th=_'forms.labels.generic.day'
- %th=_'forms.labels.generic.time'
- %th=_'forms.labels.generic.time_span'
- %th.form
- - @events.each do | event |
- %tr
- %th=event.title
- %td=_!(event.event_location.present? ? event.event_location.title : '')
- %td=date(event.start_time.to_date, :weekday)
- %td=time(event.start_time, :short)
- %td=hours(event.start_time, event.end_time)
- %td.form
- = form_tag administration_update_path(@this_conference.slug, :events) do
- = hidden_field_tag :id, event.id
- = button_tag :edit, value: :edit, class: :small
- = button_with_confirmation :delete, (_'modals.admin.generic.delete.confirm', :p, vars: { title: event.title }), value: :delete, class: [:delete, :small]
-
- %h4=_"articles.admin.locations.headings.#{@event.id.present? ? 'edit' : 'add'}_event", :t
- = form_tag administration_update_path(@this_conference.slug, :events) do
- = hidden_field_tag :id, @event.id if @event.id.present?
- = textfield :title, @event.title!, required: true, big: true, lang: @event.locale
- = textarea :info, @event.info!, heading: 'articles.conference_registration.headings.admin.edit.info', help: 'articles.conference_registration.paragraphs.admin.events.info', lang: @event.locale, edit_on: :focus
- .flex-inputs
- = location_select @event.event_location_id, small: true, stretch: true
- = day_select @day, small: true, format: :weekday
- = hour_select @time, small: true
- = length_select @length, small: true
- - I18n.backend.enabled_locales.each do | locale |
- - if locale.to_s != @event.locale.to_s
- = textfield "title_translations[#{locale.to_s}]", @event._title(locale), big: true, heading: 'translate.pages.Locale_Translation', vars: { language: _("languages.#{locale}") }, label: 'forms.labels.generic.title', lang: locale
- = textarea "info_translations[#{locale.to_s}]", @event._info(locale), lang: locale, label: 'forms.labels.generic.info', edit_on: :focus
- .actions.next-prev
- = button_tag :save, value: :save
- = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true if @event.id.present?
diff --git a/app/views/conferences/admin/_hosts_table.html.haml b/app/views/conferences/admin/_hosts_table.html.haml
deleted file mode 100644
index 021380d..0000000
--- a/app/views/conferences/admin/_hosts_table.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-.guests-housed
- %h5 Guests Housed:
- .data="#{@guests_housed} / #{@guests.size}"
-
-%table.hosts.admin-edit
- - @hosts.each do | id, registration |
- %tr.host
- %th
- .name=registration.user.name
- .address=registration.housing_data['address']
- %td.inner-table{colspan: 2}=host_guests_table(registration)
diff --git a/app/views/conferences/admin/_housing.html.haml b/app/views/conferences/admin/_housing.html.haml
deleted file mode 100644
index 68ea8cb..0000000
--- a/app/views/conferences/admin/_housing.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-- add_inline_script :housing
-= form_tag administration_update_path(@this_conference.slug, :housing), id: 'housing-table-form' do
- #housing-table= render partial: 'conferences/admin/hosts_table'
-#guest-selector
- = form_tag administration_update_path(@this_conference.slug, :housing), class: 'guest-dlg', id: 'guest-list-table' do
- %h3 Select a Guest
- #table
-.actions
- = link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :housing, :format => :xlsx), class: [:button, :download]
-
diff --git a/app/views/conferences/admin/_locations.html.haml b/app/views/conferences/admin/_locations.html.haml
deleted file mode 100644
index ca891c2..0000000
--- a/app/views/conferences/admin/_locations.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- unless @location.present?
- - if @locations.present?
- %table.locations.admin-edit
- %tr
- %th=_'forms.labels.generic.title'
- %th=_'forms.labels.generic.address'
- %th=_'articles.workshops.headings.space'
- %th=_'articles.admin.locations.headings.amenities'
- %th.form
- - @locations.each do | location |
- %tr
- %th=_!(location.title || '')
- %td=location_link location
- %td=location.space.present? ? (_"workshop.options.space.#{location.space}") : ''
- %td
- - amenities = location.amenities.present? ? JSON.parse(location.amenities) : []
- =_!(amenities.collect { |amenity| _"workshop.options.needs.#{amenity}" }).join(', ')
- %td.form
- = form_tag administration_update_path(@this_conference.slug, :locations) do
- = hidden_field_tag :id, location.id
- = button_tag :edit, value: :edit, class: :small
- = button_with_confirmation :delete, (_'modals.admin.generic.delete.confirm', :p, vars: { title: location.title }), value: :delete, class: [:delete, :small]
-%h4=_"articles.admin.locations.headings.#{@location.present? ? 'edit' : 'add'}_location", :t
-= form_tag administration_update_path(@this_conference.slug, :locations) do
- = hidden_field_tag :id, @location.id if @location.present?
- = textfield :title, @location.present? ? @location.title : nil, required: true, big: true, help: 'articles.admin.locations.paragraphs.title'
- = textfield :address, @location.present? ? @location.address : nil, required: true, help: 'articles.admin.locations.paragraphs.address'
- = columns(medium: 6) do
- = radiobuttons :space, EventLocation.all_spaces, @location.present? ? @location.space : nil, 'workshop.options.space', vertical: true, heading: 'articles.workshops.headings.space', required: true, help: 'articles.admin.locations.paragraphs.space'
- = columns(medium: 6) do
- = checkboxes :needs, EventLocation.all_amenities, @location.present? ? JSON.parse(@location.amenities) : [], 'workshop.options.needs', vertical: true, heading: 'articles.admin.locations.headings.amenities', help: 'articles.admin.locations.paragraphs.amenities'
- .actions.next-prev
- - if @location.present?
- = button_tag :save, value: :save
- = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true
- - else
- = button_tag :create, value: :create
\ No newline at end of file
diff --git a/app/views/conferences/admin/_meals.html.haml b/app/views/conferences/admin/_meals.html.haml
deleted file mode 100644
index 4684867..0000000
--- a/app/views/conferences/admin/_meals.html.haml
+++ /dev/null
@@ -1,34 +0,0 @@
-- if @this_conference.event_locations.present?
- - if @this_conference.meals.present?
- %table.meals.admin-edit
- %tr
- %th=_'forms.labels.generic.title'
- %th=_'forms.labels.generic.info'
- %th=_'forms.labels.generic.event_location'
- %th=_'forms.labels.generic.day'
- %th=_'forms.labels.generic.time'
- %th.form
- - @meals.each do | time, meal |
- %tr
- %th
- =_!(meal['title'] || '')
- %td=_!(meal['info'] || '')
- %td=_!location_name(meal['location'].to_i)
- %td=date(meal['day'], :weekday)
- %td=time(meal['time'].to_f)
- %td.form
- = form_tag administration_update_path(@this_conference.slug, :meals) do
- = hidden_field_tag :meal, time
- = button_tag :delete, value: :delete, class: [:small, :delete]
- = form_tag administration_update_path(@this_conference.slug, :meals) do
- %h4=_'articles.admin.locations.headings.add_meal', :t
- .flex-inputs
- = location_select nil, small: true, stretch: true
- = day_select nil, small: true, format: :weekday
- = hour_select nil, small: true
- = textfield :title, nil, required: true, big: true, help: 'articles.admin.locations.paragraphs.meal_title'
- = textfield :info, nil, help: 'articles.admin.locations.paragraphs.meal_info'
- .actions.next-prev
- = button_tag :add_meal, value: :add_meal
-- else
- .warning-info=_'articles.admin.meals.no_locations_warning'
\ No newline at end of file
diff --git a/app/views/conferences/admin/_payment.html.haml b/app/views/conferences/admin/_payment.html.haml
deleted file mode 100644
index d6a2887..0000000
--- a/app/views/conferences/admin/_payment.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-= form_tag administration_update_path(@this_conference.slug, :payment) do
- = textarea :payment_message, (@this_conference.payment_message! || "#{I18n.t('articles.conference_registration.paragraphs.Payment', locale: @this_conference.locale)}
"), help: 'articles.conference_registration.paragraphs.admin.payment.message', lang: @this_conference.locale, edit_on: :focus, short: true
- - I18n.backend.enabled_locales.each do | locale |
- - if locale.to_s != @this_conference.locale.to_s
- = textarea "payment_message_translations[#{locale.to_s}]", (@this_conference._payment_message(locale) || "#{I18n.t('articles.conference_registration.paragraphs.Payment', locale: locale)}
"), label: 'translate.pages.Locale_Translation', vars: { language: _("languages.#{locale}") }, lang: locale, edit_on: :focus, short: true
-
- %h4=_'articles.admin.payment.headings.payment_amounts', :t
- = fieldset :payment_amounts, help: 'articles.admin.payment.paragraphs.payment_amounts' do
- - payment_amounts = @this_conference.payment_amounts.present? ? @this_conference.payment_amounts : Conference.default_payment_amounts
- - for i in 1..5 do
- = numberfield "payment_amounts[#{i - 1}]", payment_amounts[i - 1], step: 0.01, min: 0.00, label: false
-
- %h4=_'articles.admin.edit.headings.paypal_info', :t
- %p=(_'articles.admin.edit.paragraphs.paypal_info', :p).html_safe
- = emailfield :paypal_email_address, @this_conference.paypal_email_address || @this_conference.email_address || (@this_conference.organizations.present? && @this_conference.organizations.first.present? ? @this_conference.organizations.first.email_address : nil)
- = textfield :paypal_username, @this_conference.paypal_username
- = passwordfield :paypal_password, @this_conference.paypal_password
- = textfield :paypal_signature, @this_conference.paypal_signature
- .actions.right
- = button_tag :save, value: :save
diff --git a/app/views/conferences/admin/_schedule.html.haml b/app/views/conferences/admin/_schedule.html.haml
deleted file mode 100644
index f87ed6f..0000000
--- a/app/views/conferences/admin/_schedule.html.haml
+++ /dev/null
@@ -1,93 +0,0 @@
-- conference = @this_conference || @conference
-- if conference.event_locations.blank? && @entire_page
- .warning-info=_'articles.admin.schedule.no_locations_warning'
-- else
- - if @entire_page
- - add_inline_script :schedule
- = form_tag administration_update_path(conference.slug, :schedule) do
- - if conference.workshop_schedule_published
- %p=_'articles.conference_registration.paragraphs.admin.schedule.published', :p
- .actions= button_tag :un_publish, value: :publish, class: :delete
- - else
- %p=_'articles.conference_registration.paragraphs.admin.schedule.un_published', :p
- .actions= button_tag :publish, value: :publish
-
- #schedule-preview
- - @schedule.each do | day, data |
- %h4=date(day, :weekday)
- %table.schedule{class: [data[:locations].present? ? 'has-locations' : 'no-locations', "locations-#{data[:num_locations]}"]}
- - if data[:locations].present? && data[:locations].values.first != :add
- %thead
- %tr
- %th.corner
- - data[:locations].each do | id, location |
- %th=location.is_a?(Symbol) ? '' : location.title
- %tbody
- - data[:times].each do | time, time_data |
- %tr
- - rowspan = (time_data[:length] * 2).to_i
- %th=time(time)
- - if time_data[:type] == :workshop
- - data[:locations].each do | id, location |
- - if time_data[:item][:workshops][id].present?
- - workshop = time_data[:item][:workshops][id][:workshop]
- - status = time_data[:item][:workshops][id][:status]
- - else
- - workshop = status = nil
- %td{class: [time_data[:type], workshop.present? ? :filled : :open], rowspan: rowspan, data: workshop.present? ? nil : { block: time_data[:item][:block], day: day, location: id }}
- - if workshop.present? && workshop.event_location.present?
- = link_to view_workshop_path(@conference.slug, workshop.id), class: 'event-detail-link' do
- .details
- .title=workshop.title
- %template.event-details{data: { href: view_workshop_path(@conference.slug, workshop.id) }}
- %h1.title=workshop.title
- %p.address
- = workshop.event_location.title + _!(': ')
- = location_link workshop.event_location
- .workshop-description= richtext workshop.info, 1
- - if @can_edit
- = form_tag administration_update_path(conference.slug, :schedule), class: 'deschedule-workshop' do
- .status
- .conflict-score
- %span.title Conflicts:
- %span.value="#{status[:conflict_score]} / #{workshop.interested.size}"
- - if status[:errors].present?
- .errors
- - status[:errors].each do | error |
- .error=_"errors.messages.schedule.#{error[:name].to_s}", vars: error[:i18nVars]
- = hidden_field_tag :id, workshop.id
- = button_tag :deschedule, value: :deschedule_workshop, class: [:delete, :small]
- - elsif @can_edit
- .title="Block #{time_data[:item][:block] + 1}"
- - elsif time_data[:type] != :nil
- %td{class: time_data[:type], rowspan: rowspan, colspan: data[:locations].present? ? data[:locations].size : 1}
- - case time_data[:type]
- - when :meal
- - location = EventLocation.where(id: time_data[:item]['location'].to_i).first
- - if location.present?
- %a.event-detail-link
- .details
- .title= time_data[:item]['title']
- .location= location.title
- %template.event-details
- %h1.title=time_data[:item]['title']
- %p.address
- = location.title + _!(': ')
- = location_link location
- - when :event
- - if time_data[:item].event_location.present?
- %a.event-detail-link
- .details
- .title= time_data[:item][:title]
- .location= time_data[:item].event_location.title
- %template.event-details
- %h1.title=time_data[:item][:title]
- %p.address
- = time_data[:item].event_location.title + _!(': ')
- = location_link time_data[:item].event_location
- = richtext time_data[:item][:info], 1
- - if @entire_page
- #workshop-selector
- = form_tag administration_update_path(@this_conference.slug, :schedule), class: 'workshop-dlg', id: 'workshop-table-form' do
- %h3 Select a Workshop
- #table
diff --git a/app/views/conferences/admin/_select_guest_table.html.haml b/app/views/conferences/admin/_select_guest_table.html.haml
deleted file mode 100644
index 704148a..0000000
--- a/app/views/conferences/admin/_select_guest_table.html.haml
+++ /dev/null
@@ -1,46 +0,0 @@
-= hidden_field_tag :host, host.id
-.host-field
- %h4.inline=_'forms.labels.generic.name'
- %span.plain-value= host.user.name
-.host-field
- %h4.inline=_'articles.conference_registration.headings.host.availability'
- %span.plain-value= host.housing_data['availability'].present? && host.housing_data['availability'][1].present? ? date_span(host.housing_data['availability'][0].to_date, host.housing_data['availability'][1].to_date) : ''
-- if host.housing_data['considerations'].present?
- .host-field
- %h4.inline=_'articles.conference_registration.headings.host.considerations'
- %span.plain-value= (host.housing_data['considerations'].map { | consideration | _"articles.conference_registration.host.considerations.#{consideration}" }).join(', ')
-- if sanitize(host.housing_data['notes'], tags: []).present?
- .host-field
- %h4=_'articles.conference_registration.headings.host.notes'
- %blockquote= host.housing_data['notes'].html_safe
-%table.guests.admin-edit
- %tr
- %th.corner
- %th=_'forms.labels.generic.city'
- %th=_'forms.labels.generic.housing'
- %th=_'articles.admin.housing.headings.arrival_departure'
- %th=_'articles.conference_registration.headings.companion'
- %th=_'forms.labels.generic.food'
- %th=_'forms.labels.generic.allergies'
- %th=_'forms.labels.generic.other'
- - @guests.each do | id, registration |
- %tr.selectable{class: get_housing_match(host, registration, space).to_s.gsub('_', '-'), data: {host: host.id, guest: id, space: space}}
- %th=registration.user.name
- %td=registration.city
- %td=registration.housing.present? ? (_"articles.conference_registration.questions.housing.#{registration.housing}") : ''
- %td=date_span(registration.arrival.to_date, registration.departure.to_date)
- - companion = companion(registration)
- %td=companion.present? ? (companion.is_a?(User) ? companion.named_email : (_"articles.conference_registration.terms.registration_status.#{companion}")) : ''
- %td=registration.food.present? ? (_"articles.conference_registration.questions.food.#{registration.food}") : ''
- %td=registration.allergies
- %td
- .p=registration.other
-
-.legend
- %h4 Legend
- %ul
- %li.good-match Good Match
- %li.bad-match Poor Match
- %li.selected-space Also in this space
- %li.other-space Also with this host
- %li.other-host Already hosted
diff --git a/app/views/conferences/admin/_stats.html.haml b/app/views/conferences/admin/_stats.html.haml
deleted file mode 100644
index 6d70176..0000000
--- a/app/views/conferences/admin/_stats.html.haml
+++ /dev/null
@@ -1,37 +0,0 @@
-- add_inline_script :registrations
-.details
- = data_set(:h4, 'articles.admin.stats.headings.completed_registrations') do
- = (@completed_registrations || 0).to_s
- = data_set(:h4, 'articles.admin.stats.headings.incomplete_registrations') do
- = ((@registration_count - @completed_registrations) || 0).to_s
- = data_set(:h4, 'articles.admin.stats.headings.bikes') do
- = (@completed_registrations || 0) > 0 ? "#{@bikes} (#{number_to_percentage(@bikes / @completed_registrations.to_f * 100.0)})" : "0"
- = data_set(:h4, 'articles.admin.stats.headings.food.meat') do
- = (@food[:all] || 0) > 0 ? "#{@food[:meat]} (#{number_to_percentage(@food[:meat] / @food[:all].to_f * 100.0)})" : "0"
- = data_set(:h4, 'articles.admin.stats.headings.food.vegetarian') do
- = (@food[:all] || 0) > 0 ? "#{@food[:vegetarian]} (#{number_to_percentage(@food[:vegetarian] / @food[:all].to_f * 100.0)})" : "0"
- = data_set(:h4, 'articles.admin.stats.headings.food.vegan') do
- = (@food[:all] || 0) > 0 ? "#{@food[:vegan]} (#{number_to_percentage(@food[:vegan] / @food[:all].to_f * 100.0)})" : "0"
- = data_set(:h4, 'articles.admin.stats.headings.donation_count') do
- = (@completed_registrations || 0) > 0 ? "#{@donation_count} (#{number_to_percentage(@donation_count / @completed_registrations.to_f * 100.0)})" : "0"
- = data_set(:h4, 'articles.admin.stats.headings.donation_total') do
- = "$#{@donations || 0.00}"
-.actions
- = link_to (_'links.download.Excel'), administration_step_path(@this_conference.slug, :stats, :format => :xlsx), class: [:button, :download]
- = link_to (_'links.download.Organizations_Excel'), administration_step_path(@this_conference.slug, :organizations, :format => :xlsx), class: [:button, :download, :subdued]
-%h4=_'articles.admin.stats.headings.Registrations'
-.goes-fullscreen#registrations-table
- .flex-column
- = searchfield :search, nil, big: true, stretch: true
- %a.button{data: { expands: 'registrations-table' }}='expand'
- %a.button.delete{data: { contracts: 'registrations-table' }}='close'
- %a.button.modify{data: { 'opens-modal': 'new-registration' }}='+'
- .table-scroller
- = html_table @excel_data, registrations_table_options
- = form_tag administration_update_path(@this_conference.slug, :stats), id: 'new-registration', class: 'modal-edit' do
- .modal-edit-overlay{data: { 'closes-modal': 'new-registration' }}
- .modal-edit-content
- =html_edit_table @excel_data, registrations_edit_table_options
- .actions.right
- %a.button.subdued{data: { 'closes-modal': 'new-registration' }}='Cancel'
- = button_tag :save, value: :save, class: :modify
diff --git a/app/views/conferences/admin/_workshop_times.html.haml b/app/views/conferences/admin/_workshop_times.html.haml
deleted file mode 100644
index d4f440b..0000000
--- a/app/views/conferences/admin/_workshop_times.html.haml
+++ /dev/null
@@ -1,18 +0,0 @@
-.table.workshop-blocks
- .table-tr.header
- .table-th=_'forms.labels.generic.block_number'
- .table-th=_'forms.labels.generic.time'
- .table-th=_'forms.labels.generic.length'
- .table-th=_'forms.labels.generic.days'
- .table-th.form
- - @workshop_blocks.each_with_index do | info, block |
- - is_new = info['time'].blank?
- = form_tag administration_update_path(@this_conference.slug, :workshop_times), class: ['table-tr', is_new ? 'new' : 'saved'] do
- .table-th.center.big= is_new ? '' : (block + 1)
- .table-td=hour_select info['time'], small: true, label: false
- .table-td=length_select info['length'], {small: true, label: false}, 0.5, 2
- .table-td=checkboxes :days, @block_days, info['days'].map(&:to_i), 'date.day_names', vertical: true, small: true
- .table-td.form
- = hidden_field_tag :workshop_block, block
- = button_tag :delete_block, value: :delete_block, class: [:small, :delete] if block == @workshop_blocks.length - 2
- = button_tag (is_new ? :add_block : :update_block), value: :save_block, class: [:small, :add]
diff --git a/app/views/conferences/broadcast.html.haml b/app/views/conferences/broadcast.html.haml
index 31e1ad7..e409886 100644
--- a/app/views/conferences/broadcast.html.haml
+++ b/app/views/conferences/broadcast.html.haml
@@ -1,35 +1,35 @@
= render 'page_header', :page_key => 'Email_Participants'
%article
- = row do
- = columns(medium: 12) do
- - if @email_sent == :yes
- %p=_('articles.conference_registration.paragraphs.participants_emailed',"All participants of #{@this_conference.title} have been emailed.", :vars => {:conference_title => @this_conference.title})
- - if @email_sent != :yes
- =m('articles.conference_registration.paragraphs.Email_Participants','This page is used to contact all participants. Text can be entered as [Markdown](http://daringfireball.net/projects/markdown/basics). Pressing \'test\' will send the email only to you, make sure you do this and use caution in general before pressing \'send\'')
- - if @email_sent == :yes || @email_sent == :preview
- %h2=_'articles.conference_registration.headings.Preview'
- .email-preview
- %h3=@subject
- =markdown @content
- - if @email_sent != :yes
- = form_tag broadcast_path(@this_conference.slug), class: 'composition' do
- - if @email_sent == :preview
- = hidden_field_tag :subject, @subject
- = hidden_field_tag :content, @content
- - else
- .text-field.input-field.big
- = label_tag :subject
- = text_field_tag :subject, @subject, :required => true
- .select-field.input-field
- = label_tag :content
- = text_area_tag :content, @content, :required => true
- .actions.right
- - if @email_sent == :test
- .note=_('articles.conference_registration.notes.Test_Email_Sent',"An email was sent to #{current_user.email}", vars: {:email_address => current_user.email})
- - if @email_sent == :preview # let the user send the
- = button_tag :edit, :value => :edit
- = button_tag :send, :value => :send
- - else
- = button_tag :preview, :value => :preview
- = button_tag :test, :value => :test
+ = row do
+ = columns(medium: 12) do
+ - if @email_sent == :yes
+ %p=_('articles.conference_registration.paragraphs.participants_emailed',"All participants of #{@this_conference.title} have been emailed.", :vars => {:conference_title => @this_conference.title})
+ - if @email_sent != :yes
+ =m('articles.conference_registration.paragraphs.Email_Participants','This page is used to contact all participants. Text can be entered as [Markdown](http://daringfireball.net/projects/markdown/basics). Pressing \'test\' will send the email only to you, make sure you do this and use caution in general before pressing \'send\'')
+ - if @email_sent == :yes || @email_sent == :preview
+ %h2=_'articles.conference_registration.headings.Preview'
+ .email-preview
+ %h3=@subject
+ =markdown @content
+ - if @email_sent != :yes
+ = form_tag broadcast_path(@this_conference.slug), class: 'composition' do
+ - if @email_sent == :preview
+ = hidden_field_tag :subject, @subject
+ = hidden_field_tag :content, @content
+ - else
+ .text-field.input-field.big
+ = label_tag :subject
+ = text_field_tag :subject, @subject, :required => true
+ .select-field.input-field
+ = label_tag :content
+ = text_area_tag :content, @content, :required => true
+ .actions.right
+ - if @email_sent == :test
+ .note=_('articles.conference_registration.notes.Test_Email_Sent',"An email was sent to #{current_user.email}", vars: {:email_address => current_user.email})
+ - if @email_sent == :preview # let the user send the
+ = button_tag :edit, :value => :edit
+ = button_tag :send, :value => :send
+ - else
+ = button_tag :preview, :value => :preview
+ = button_tag :test, :value => :test
diff --git a/app/views/conferences/list.html.haml b/app/views/conferences/list.html.haml
new file mode 100644
index 0000000..7bc689f
--- /dev/null
+++ b/app/views/conferences/list.html.haml
@@ -0,0 +1,39 @@
+= javascript_include_tag 'topojson.js'
+- add_inline_script :map
+- content_for :title do
+ =I18n.t'articles.conferences.headings.Conference_List'
+
+- content_for :banner do
+ #header-title.map
+ %svg#map.loading{preserveAspectRatio: 'xMinYMin slice', viewBox: "0 0 1880 550"}
+ #tooltip
+
+%article
+ = row do
+ = columns(medium: 12) do
+ %h2=_"articles.conferences.headings.conferences", :t
+ %p=_"articles.conferences.paragraphs.conferences", :p
+ - if logged_in? && current_user.administrator?
+ .actions.left= link_to (_'forms.actions.generic.create'), new_conference_path, class: ['button', 'accented']
+ = row do
+ = columns(medium: 12, id: :conferences, class: 'list-view') do
+ - @conference_list.each do | type, list |
+ %h3=_"articles.conferences.headings.types.#{type.to_s}", :t
+ %ul
+ - list.each do | conference |
+ %li.conference{id: "conference-#{conference.id}", data: {a: conference.city.latitude, o: conference.city.longitude, y: conference.year, t: conference.conferencetype}}
+ .info
+ %h4.title= conference.title
+ .conference-details
+ .location=location(conference.city)
+ .date= date_span(conference.start_date.to_date, conference.end_date.to_date)
+ .actions
+ = link_to (_'articles.workshops.info.read_more'), conference_path(conference.slug), class: ['button', 'conference-link', 'small']
+ - if logged_in?
+ - if conference.host? current_user
+ = link_to (_'forms.actions.generic.administrate'), administrate_conference_path(conference.slug), class: [:button, :modify, :small]
+ - if current_user.administrator?
+ = link_to (_'forms.actions.generic.edit'), edit_conference_path(conference.slug), class: [:button, :subdued, :small]
+ - if conference.poster.present?
+ .img
+ %img{src: conference.poster.preview.url}
diff --git a/app/views/conferences/register.html.haml b/app/views/conferences/register.html.haml
index ebc425d..6777e13 100644
--- a/app/views/conferences/register.html.haml
+++ b/app/views/conferences/register.html.haml
@@ -1,13 +1,13 @@
-= render :partial => 'page_header', :locals => {:page_key => 'Conference_Registration'}
+= render :partial => 'page_header', :locals => {:page_key => 'Conference_Administration'}
- if @warnings.present?
- = row class: 'warnings', tag: :ul do
- - @warnings.each do | warning |
- = columns tag: :li do
- = warning
+ = row class: 'warnings', tag: :ul do
+ - @warnings.each do | warning |
+ = columns tag: :li do
+ = warning
%article
- = row do
- = columns(medium: 12) do
- %h2=_(@page_title)
- = registration_step_menu
- = render @register_template.to_s
+ = row do
+ = columns(medium: 12) do
+ %h2=_(@page_title)
+ = registration_step_menu
+ = render @register_template.to_s
diff --git a/app/views/conferences/show.html.haml b/app/views/conferences/show.html.haml
index 3469e44..cf4ad75 100644
--- a/app/views/conferences/show.html.haml
+++ b/app/views/conferences/show.html.haml
@@ -1,34 +1,34 @@
-- location = @conference.organizations.first.locations.first
-- location_name = location.city + ', ' + (location.territory ? Carmen::Country.coded(location.country).subregions.coded(location.territory).name : location.country)
-
-- title @conference.title
-- description "#{@conference.title} conference in #{location_name} for DIY bicycle collectives, co-ops, and advocacy groups"
-= render 'header'
-%article.row
- .columns.large-10
- - if @register_step
- %h2='Conference Registration'
- - if @actions
- = form_tag (@conference.url + '/register/').gsub(/\/\/+/, '/'), :method => :post, :multipart => @multipart do
- = hidden_field_tag :step, @register_step
- - if @error_message
- .columns.medium-8.medium-centered
- %p.error=@error_message
- = render @register_template
- .columns= form_actions @actions
- - else
- = render @register_template
- - else
- %h2=('About '+@conference.title)
- =p @conference, :info
- - if @conference.registration_open
- .columns.small-12.centered
- %a.button.register-now{href: @conference.url(:register)}
- =_'conference.Register_Now'
-
-
-- content_for :side_bar do
- %h5= @conference.title+' is hosted by:'
- %ul.preview-list.org-list.small-block-grid-2.large-block-grid-1
- - @conference.organizations.each do |organization|
- %li=render 'organizations/preview', :organization => organization
\ No newline at end of file
+- location = @conference.organizations.first.locations.first
+- location_name = location.city + ', ' + (location.territory ? Carmen::Country.coded(location.country).subregions.coded(location.territory).name : location.country)
+
+- title @conference.title
+- description "#{@conference.title} conference in #{location_name} for DIY bicycle collectives, co-ops, and advocacy groups"
+= render 'header'
+%article.row
+ .columns.large-10
+ - if @register_step
+ %h2='Conference Registration'
+ - if @actions
+ = form_tag (@conference.url + '/register/').gsub(/\/\/+/, '/'), :method => :post, :multipart => @multipart do
+ = hidden_field_tag :step, @register_step
+ - if @error_message
+ .columns.medium-8.medium-centered
+ %p.error=@error_message
+ = render @register_template
+ .columns= form_actions @actions
+ - else
+ = render @register_template
+ - else
+ %h2=('About '+@conference.title)
+ =p @conference, :info
+ - if @conference.registration_open
+ .columns.small-12.centered
+ %a.button.register-now{href: @conference.url(:register)}
+ =_'conference.Register_Now'
+
+
+- content_for :side_bar do
+ %h5= @conference.title+' is hosted by:'
+ %ul.preview-list.org-list.small-block-grid-2.large-block-grid-1
+ - @conference.organizations.each do |organization|
+ %li=render 'organizations/preview', :organization => organization
\ No newline at end of file
diff --git a/app/views/conferences/view.html.haml b/app/views/conferences/view.html.haml
new file mode 100644
index 0000000..55eca23
--- /dev/null
+++ b/app/views/conferences/view.html.haml
@@ -0,0 +1,5 @@
+- content_for :og_image do
+ = @this_conference.poster.full.url || image_path('default_poster.jpg')
+- content_for :title do
+ =@this_conference.title
+= render 'conferences/conference', conference: @this_conference, links: @links
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index ba5f032..f1d5a0f 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -1,83 +1,84 @@
!!!
%html{ lang: I18n.locale.to_s }
- %head
- %meta{ charset: 'utf-8' }
- %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }
- - title = yield :title
- %title=_!('Bike!Bike!' + (content_for?(:title) ? " - #{title}" : ''))
- %meta{ name: 'description', content: (yield_or_default :description, I18n.t('page_descriptions.home')) }
- = csrf_meta_tags
- = stylesheets
- %link{ href: asset_path(@favicon), rel: 'shortcut icon', type: 'image/x-icon' }
- %link{ href: asset_path(@favicon), rel: 'icon', type: 'image/x-icon' }
- - @alt_lang_urls.each do |locale, url|
- %link{ rel: :alternate, hreflang: locale, href: url }
- - if content_for?(:og_image)
- - og_image = yield :og_image
- - og_image = request.base_url + og_image
- %meta{property: 'og:title', content: title}
- %meta{property: 'og:type', content: 'website'}
- %meta{property: 'og:image', content: og_image}
- %meta{name: "theme-color", content: @theme_colour}
- = yield :head
+ %head
+ %meta{ charset: 'utf-8' }
+ %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }
+ - title = yield :title
+ %title=_!('Bike!Bike!' + (content_for?(:title) ? " - #{title}" : ''))
+ %meta{ name: 'description', content: (yield_or_default :description, I18n.t('page_descriptions.home')) }
+ = csrf_meta_tags
+ = stylesheets
+ %link{ href: asset_path(@favicon), rel: 'shortcut icon', type: 'image/x-icon' }
+ %link{ href: asset_path(@favicon), rel: 'icon', type: 'image/x-icon' }
+ - @alt_lang_urls.each do |locale, url|
+ %link{ rel: :alternate, hreflang: locale, href: url }
+ - if content_for?(:og_image)
+ - og_image = yield :og_image
+ - og_image = request.base_url + og_image
+ %meta{property: 'og:title', content: title}
+ %meta{property: 'og:type', content: 'website'}
+ %meta{property: 'og:image', content: og_image}
+ %meta{name: "theme-color", content: @theme_colour}
+ = yield :head
- %body{ class: page_style }
- #primary-content
- = render 'shared/navbar'
- %main#main
- %header#banner=yield :banner
- - if @submenu
- =row do
- = columns(medium: 12) do
- %nav.sub-menu
- - @submenu.each do |href,key|
- %a{href: href, class: request.fullpath.start_with?(href) ? 'current' : nil}=_"menu.submenu.#{key}"
- - if has_content?
- #content=yield
- - else
- = yield
- #footer
- %footer= render 'shared/footer'
- #content-overlay
- #overlay
- .dlg#contact-dlg
- .dlg-content
- %h2.title=_'articles.contact.headings.contact'
- .dlg-inner= render 'contact', cancel_btn: true
- - if @confirmation_dlg.present?
- .dlg#confirmation-dlg
- .dlg-content
- %h2.title=_'modals.confirm'
- .dlg-inner
- %p.message=''
- %a.button.confirm=_'modals.yes_button'
- %button.delete.close=_'modals.no_button'
- - if @info_dlg.present?
- .dlg#info-dlg
- .dlg-content
- %h2.title=_'modals.info'
- .dlg-inner
- %p.message=''
- %button.close=_'modals.done_button'
- - if @login_dlg.present?
- .dlg#login-dlg
- .dlg-content
- %h2.title=_'forms.actions.generic.login'
- .dlg-inner
- = form_tag do_confirm_path, class: 'flex-form' do
- = hidden_field_tag :dest, settings_path
- = emailfield :email, nil, big: true
- = button_tag :continue, :value => :confirm_email
- .flex-form
- = link_to (_'forms.actions.generic.facebook_sign_in','Facebook Sign In'), auth_at_provider_path(provider: :facebook, dest: settings_path), class: [:button, :facebook]
- %button.close.subdued=_'forms.actions.generic.cancel'
-
- - if @event_dlg.present?
- .event-dlg#event-dlg{ data: { type: :event } }
- .event-details
- .actions.right
- %a.more-details.button{href: '#'}=_'articles.workshops.info.read_more'
- %button.close-btn.subdued=_'forms.actions.generic.close'
- = yield :footer_scripts if content_for?(:footer_scripts)
- = inline_scripts
- = emit_js_translations
+ %body{ class: page_style }
+ #primary-content
+ = render 'shared/navbar'
+ %main#main
+ - if content_for?(:banner)
+ %header#banner=yield :banner
+ - if @submenu
+ =row do
+ = columns(medium: 12) do
+ %nav.sub-menu
+ - @submenu.each do |href,key|
+ %a{href: href, class: request.fullpath.start_with?(href) ? 'current' : nil}=_"menu.submenu.#{key}"
+ - if has_content?
+ #content=yield
+ - else
+ = yield
+ #footer
+ %footer= render 'shared/footer'
+ #content-overlay
+ #overlay
+ .dlg#contact-dlg
+ .dlg-content
+ %h2.title=_'articles.contact.headings.contact'
+ .dlg-inner= render 'contact', cancel_btn: true
+ - if @confirmation_dlg.present?
+ .dlg#confirmation-dlg
+ .dlg-content
+ %h2.title=_'modals.confirm'
+ .dlg-inner
+ %p.message=''
+ %a.button.confirm=_'modals.yes_button'
+ %button.delete.close=_'modals.no_button'
+ - if @info_dlg.present?
+ .dlg#info-dlg
+ .dlg-content
+ %h2.title=_'modals.info'
+ .dlg-inner
+ %p.message=''
+ %button.close=_'modals.done_button'
+ - if @login_dlg.present?
+ .dlg#login-dlg
+ .dlg-content
+ %h2.title=_'forms.actions.generic.login'
+ .dlg-inner
+ = form_tag do_confirm_path, class: 'flex-form' do
+ = hidden_field_tag :dest, settings_path
+ = emailfield :email, nil, big: true
+ = button_tag :continue, :value => :confirm_email
+ .flex-form
+ = link_to (_'forms.actions.generic.facebook_sign_in','Facebook Sign In'), auth_at_provider_path(provider: :facebook, dest: settings_path), class: [:button, :facebook]
+ %button.close.subdued=_'forms.actions.generic.cancel'
+
+ - if @event_dlg.present?
+ .event-dlg#event-dlg{ data: { type: :event } }
+ .event-details
+ .actions.right
+ %a.more-details.button{href: '#'}=_'articles.workshops.info.read_more'
+ %button.close-btn.subdued=_'forms.actions.generic.close'
+ = yield :footer_scripts if content_for?(:footer_scripts)
+ = inline_scripts
+ = emit_js_translations
diff --git a/app/views/schedule/_programme.html.haml b/app/views/schedule/_programme.html.haml
index 50c3346..6808fe5 100644
--- a/app/views/schedule/_programme.html.haml
+++ b/app/views/schedule/_programme.html.haml
@@ -1,38 +1,38 @@
.programme
- - schedule.each do |day, day_schedule|
- .programme-day
- %h2=I18n.l(conference.start_date + (day - 1).days, :format => "%A")
- - (0...day_parts.length).each do |day_part|
- .programme-day-part
- - times = schedule_start_and_end_times(day_part, day_parts, day_schedule)
- - if times.present?
- %h3=_"articles.headings.schedule.day_parts.#{day_parts.keys[day_part].to_s}" if day_parts.length > 1
- %table.schedule
- %tr
- %th
- - (times.first...times.last).step(0.5).each do |t|
- - t = t.to_i if t == t.to_i
- %th=I18n.l(Date.today + t.hours, :format => :short)
- - day_schedule[:locations].each do |location, location_schedule|
- %tr
- %th
- %a{href: "https://maps.google.com/maps?q=#{URI.escape((locations[location.to_s].address || '').gsub(/\s+/, '+'))}", target: :_blank}
- =locations[location.to_s].title
- - skip = 0
- - (times.first...times.last).step(0.5).each do |t|
- - t = t.to_i if t == t.to_i
- - if location_schedule[t].present?
- - workshop = location_schedule[t]
- - w = get_workshop(workshop, workshops, events)
- %td{:class => workshop_classes(w, show_interest) + [show_previews && workshop[:type] == :workshop ? 'previewable' : nil], :colspan => (workshop[:span] * 2), :id => "workshop-#{w.id}"}
- .title= w.title
- - if show_previews && workshop[:type] == :workshop
- .info
- %a.close{href: "#!"}
- = render 'workshops/show', :workshop => w, :preview => true
- %a{class: 'preview', href: "#workshop-#{w.id}"}
- - skip = workshop[:span] - 0.5
- - elsif skip > 0
- - skip -= 0.5
- - else
- %td.empty
+ - schedule.each do |day, day_schedule|
+ .programme-day
+ %h2=I18n.l(conference.start_date + (day - 1).days, :format => "%A")
+ - (0...day_parts.length).each do |day_part|
+ .programme-day-part
+ - times = schedule_start_and_end_times(day_part, day_parts, day_schedule)
+ - if times.present?
+ %h3=_"articles.headings.schedule.day_parts.#{day_parts.keys[day_part].to_s}" if day_parts.length > 1
+ %table.schedule
+ %tr
+ %th
+ - (times.first...times.last).step(0.5).each do |t|
+ - t = t.to_i if t == t.to_i
+ %th=I18n.l(Date.today + t.hours, :format => :short)
+ - day_schedule[:locations].each do |location, location_schedule|
+ %tr
+ %th
+ %a{href: "https://maps.google.com/maps?q=#{URI.escape((locations[location.to_s].address || '').gsub(/\s+/, '+'))}", target: :_blank}
+ =locations[location.to_s].title
+ - skip = 0
+ - (times.first...times.last).step(0.5).each do |t|
+ - t = t.to_i if t == t.to_i
+ - if location_schedule[t].present?
+ - workshop = location_schedule[t]
+ - w = get_workshop(workshop, workshops, events)
+ %td{:class => workshop_classes(w, show_interest) + [show_previews && workshop[:type] == :workshop ? 'previewable' : nil], :colspan => (workshop[:span] * 2), :id => "workshop-#{w.id}"}
+ .title= w.title
+ - if show_previews && workshop[:type] == :workshop
+ .info
+ %a.close{href: "#!"}
+ = render 'workshops/show', :workshop => w, :preview => true
+ %a{class: 'preview', href: "#workshop-#{w.id}"}
+ - skip = workshop[:span] - 0.5
+ - elsif skip > 0
+ - skip -= 0.5
+ - else
+ %td.empty
diff --git a/app/views/schedule/edit.html.haml b/app/views/schedule/edit.html.haml
index bcc06a0..fc410d9 100644
--- a/app/views/schedule/edit.html.haml
+++ b/app/views/schedule/edit.html.haml
@@ -1,77 +1,77 @@
= render 'conferences/page_header', :page_key => 'Edit_Schedule'
%article
- = form_tag save_schedule_path(@this_conference.slug), class: 'composition' do
- = row do
- = columns(medium: 12) do
- = render 'schedule/programme', :schedule => @schedule, :conference => @this_conference, :workshops => @workshops, :events => @events, :locations => @location_hash, :show_interest => false, :day_parts => @day_parts, :show_previews => false
- = row do
- = columns(medium: 12) do
- - if @error_count && @error_count > 0
- %h3.errors
- =_'errors.schedule.errors',"Errors:"
- = @error_count
- - if @conflict_score && @conflict_score > 0
- %h3.conflict-score
- =_'errors.schedule.conflict_score',"Interest Conflicts:"
- = @conflict_score
- = row do
- = columns(medium: 12) do
- = (hidden_field_tag :location_id, @location.id) if @location
- .actions
- - if @this_conference.workshop_schedule_published
- = button_tag :Unpublish, :value => :unpublish, :class => 'delete'
- - elsif @error_count < 1
- = button_tag :Publish, :value => :publish
- = button_tag :Preview, :value => :preview, :class => 'secondary'
- - unless @this_conference.workshop_schedule_published && @error_count > 0
- = button_tag :save, :value => :save
- - unless @saved
- .unsaved=_'errors.schedule.unsaved','Your changes will not be saved until you press Save or Publish'
- = row do
- = columns(medium: 6) do
- %h2=_"articles.headings.schedule.day_parts.Workshops"
- %ul.all-workshops
- - @workshops.each do |i|
- - error = @errors["w#{i.id}"]
- - warnings = @warnings["w#{i.id}"]
- %li{class: error.present? ? :error : nil}
- %h3=i.title
- .workshop-interest=_'articles.workshops.info.interested_count', "#{i.interested_count} people are interested in this workshop", :vars => {:count => i.interested_count}
- .time
- = select_tag "workshop_day[#{i.id}]", options_for_select(@days, i.conference_day), :include_blank => true
- = select_tag "workshop_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : nil), :include_blank => true
- = select_tag "workshop_duration[#{i.id}]", options_for_select(@workshop_durations, i.duration || 60)
- .location
- = select_tag "workshop_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true
- - if warnings
- %ul.warnings
- - warnings.each do |warning|
- %li=warning
- - if error
- .error-description=error
- = columns(medium: 6) do
- %h2=_"articles.headings.schedule.day_parts.Events"
- %ul.all-events
- - @events.each do |i|
- - error = @errors["e#{i.id}"]
- %li{:class => [i.event_type, error.present? ? :error : nil]}
- %h3=i.title
- .time
- = select_tag "event_day[#{i.id}]", options_for_select(@days, i.conference_day)
- = select_tag "event_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : '12:00')
- = select_tag "event_duration[#{i.id}]", options_for_select(@event_durations, i.duration || 60)
- .location
- = select_tag "event_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true
- - if error
- .error-description=error
- %h2=_"articles.headings.schedule.day_parts.Day_Parts"
- %ul.day_parts
- - [:morning, :afternoon, :evening].each do |day_part|
- %li
- %h4
- =_"articles.headings.schedule.day_parts.#{day_part.to_s}"
- - h = (Date.today + @day_parts[day_part.to_s].to_f.hours).strftime('%R')
- - if day_part == :morning
- .select=h
- - else
- = select_tag "day_parts[#{day_part.to_s}]", options_for_select(@hours, h)
+ = form_tag save_schedule_path(@this_conference.slug), class: 'composition' do
+ = row do
+ = columns(medium: 12) do
+ = render 'schedule/programme', :schedule => @schedule, :conference => @this_conference, :workshops => @workshops, :events => @events, :locations => @location_hash, :show_interest => false, :day_parts => @day_parts, :show_previews => false
+ = row do
+ = columns(medium: 12) do
+ - if @error_count && @error_count > 0
+ %h3.errors
+ =_'errors.schedule.errors',"Errors:"
+ = @error_count
+ - if @conflict_score && @conflict_score > 0
+ %h3.conflict-score
+ =_'errors.schedule.conflict_score',"Interest Conflicts:"
+ = @conflict_score
+ = row do
+ = columns(medium: 12) do
+ = (hidden_field_tag :location_id, @location.id) if @location
+ .actions
+ - if @this_conference.workshop_schedule_published
+ = button_tag :Unpublish, :value => :unpublish, :class => 'delete'
+ - elsif @error_count < 1
+ = button_tag :Publish, :value => :publish
+ = button_tag :Preview, :value => :preview, :class => 'secondary'
+ - unless @this_conference.workshop_schedule_published && @error_count > 0
+ = button_tag :save, :value => :save
+ - unless @saved
+ .unsaved=_'errors.schedule.unsaved','Your changes will not be saved until you press Save or Publish'
+ = row do
+ = columns(medium: 6) do
+ %h2=_"articles.headings.schedule.day_parts.Workshops"
+ %ul.all-workshops
+ - @workshops.each do |i|
+ - error = @errors["w#{i.id}"]
+ - warnings = @warnings["w#{i.id}"]
+ %li{class: error.present? ? :error : nil}
+ %h3=i.title
+ .workshop-interest=_'articles.workshops.info.interested_count', "#{i.interested_count} people are interested in this workshop", :vars => {:count => i.interested_count}
+ .time
+ = select_tag "workshop_day[#{i.id}]", options_for_select(@days, i.conference_day), :include_blank => true
+ = select_tag "workshop_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : nil), :include_blank => true
+ = select_tag "workshop_duration[#{i.id}]", options_for_select(@workshop_durations, i.duration || 60)
+ .location
+ = select_tag "workshop_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true
+ - if warnings
+ %ul.warnings
+ - warnings.each do |warning|
+ %li=warning
+ - if error
+ .error-description=error
+ = columns(medium: 6) do
+ %h2=_"articles.headings.schedule.day_parts.Events"
+ %ul.all-events
+ - @events.each do |i|
+ - error = @errors["e#{i.id}"]
+ %li{:class => [i.event_type, error.present? ? :error : nil]}
+ %h3=i.title
+ .time
+ = select_tag "event_day[#{i.id}]", options_for_select(@days, i.conference_day)
+ = select_tag "event_hour[#{i.id}]", options_for_select(@hours, i.start_time ? i.start_time.strftime('%R') : '12:00')
+ = select_tag "event_duration[#{i.id}]", options_for_select(@event_durations, i.duration || 60)
+ .location
+ = select_tag "event_location[#{i.id}]", options_from_collection_for_select(@locations, :id, :title, i.event_location_id), :include_blank => true
+ - if error
+ .error-description=error
+ %h2=_"articles.headings.schedule.day_parts.Day_Parts"
+ %ul.day_parts
+ - [:morning, :afternoon, :evening].each do |day_part|
+ %li
+ %h4
+ =_"articles.headings.schedule.day_parts.#{day_part.to_s}"
+ - h = (Date.today + @day_parts[day_part.to_s].to_f.hours).strftime('%R')
+ - if day_part == :morning
+ .select=h
+ - else
+ = select_tag "day_parts[#{day_part.to_s}]", options_for_select(@hours, h)
diff --git a/app/views/schedule/show.html.haml b/app/views/schedule/show.html.haml
index 27b60b2..8e527ed 100644
--- a/app/views/schedule/show.html.haml
+++ b/app/views/schedule/show.html.haml
@@ -1,29 +1,29 @@
= render 'conferences/page_header', :page_key => 'Schedule'
%article
- - if @this_conference.host?(current_user)
- = row do
- = columns(medium: 12) do
- - if @locations.present? && @events.present?
- .actions.left
- = link_to (_'actions.schedule.edit','Edit Schedule'), edit_schedule_path(@this_conference.slug), class: [:button]
- = row do
- = columns(medium: 6) do
- %h2=_'articles.schedule.headings.Events'
- - if @events
- %ul.events
- - @events.each do |event|
- %li
- %h3=_!event.title
- = link_to (_'actions.events.edit','Edit'), edit_event_path(@this_conference.slug, event.id), class: [:button, :modify]
- .actions.left
- = link_to (_'actions.events.create','Add Event'), add_event_path(@this_conference.slug), class: [:button]
- = columns(medium: 6) do
- %h2=_'articles.schedule.headings.Locations'
- - if @locations
- %ul.locations
- - @locations.each do |location|
- %li
- %h3=_!location.title
- = link_to (_'actions.locations.edit','Edit'), edit_location_path(@this_conference.slug, location.id), class: [:button, :modify]
- .actions.left
- = link_to (_'actions.schedule.edit','Add Location'), add_location_path(@this_conference.slug), class: [:button]
+ - if @this_conference.host?(current_user)
+ = row do
+ = columns(medium: 12) do
+ - if @locations.present? && @events.present?
+ .actions.left
+ = link_to (_'actions.schedule.edit','Edit Schedule'), edit_schedule_path(@this_conference.slug), class: [:button]
+ = row do
+ = columns(medium: 6) do
+ %h2=_'articles.schedule.headings.Events'
+ - if @events
+ %ul.events
+ - @events.each do |event|
+ %li
+ %h3=_!event.title
+ = link_to (_'actions.events.edit','Edit'), edit_event_path(@this_conference.slug, event.id), class: [:button, :modify]
+ .actions.left
+ = link_to (_'actions.events.create','Add Event'), add_event_path(@this_conference.slug), class: [:button]
+ = columns(medium: 6) do
+ %h2=_'articles.schedule.headings.Locations'
+ - if @locations
+ %ul.locations
+ - @locations.each do |location|
+ %li
+ %h3=_!location.title
+ = link_to (_'actions.locations.edit','Edit'), edit_location_path(@this_conference.slug, location.id), class: [:button, :modify]
+ .actions.left
+ = link_to (_'actions.schedule.edit','Add Location'), add_location_path(@this_conference.slug), class: [:button]
diff --git a/app/views/shared/_footer.html.haml b/app/views/shared/_footer.html.haml
index d65a8d7..bb588e7 100644
--- a/app/views/shared/_footer.html.haml
+++ b/app/views/shared/_footer.html.haml
@@ -1,28 +1,31 @@
-.external
- .facebook
- =_'links.footer.help_text.facebook', 'Join our facebook group' do |title|
- %a{href: 'https://www.facebook.com/groups/648758205249998/', target: :_blank, title: title}
- =svg_sprite 'icons', 'bb-icon-fb', 'facebook logo'
- .github
- %a{href: 'https://github.com/bikebike/BikeBike', target: :_blank}
- = off_screen(_'links.footer.text.Help_contribute')
- = svg_sprite 'icons', 'bb-icon-github', 'github logo'
-.user-controls
- - if logged_in?
- = link_to (_!current_user.name), settings_path, class: 'my-account'
- = _!' | '
- = link_to (_'forms.actions.generic.Log_out'), logout_path, class: 'logout'
- - else
- = signin_link
-%ul.locales
- - @alt_lang_urls.each do |locale, url|
- %li
- - locale_translation = language_name(locale, true)
- %a{href: url, lang: locale}
- =_'translate.content.change_locale', "Read in #{locale_translation}", vars: {language: locale_translation}, locale: locale if locale != I18n.locale.to_s
-.site-info
- .contact-us=link_to (_'links.footer.help_text.contact'), contact_path, id: 'contact-link'
- .copy
- =_'links.footer.help_text.contributors', 'Who contributed to building this website' do |title|
- =link_to :humans_txt, {title: title} do
- =((_!"©#{Date.today.strftime("%Y")} Bike!Bike!") || '').html_safe
+- protect do
+ .external
+ .facebook
+ =_'links.footer.help_text.facebook', 'Join our facebook group' do |title|
+ %a{href: 'https://www.facebook.com/groups/648758205249998/', target: :_blank, title: title}
+ -#= svg_sprite 'icons', 'bb-icon-fb', 'facebook logo'
+ = svg 'facebook', 'facebook logo'
+ .github
+ =_'links.footer.text.Help_contribute' do |title|
+ %a{href: 'https://github.com/bikebike/BikeBike', target: :_blank, title: title}
+ -#= svg_sprite 'icons', 'bb-icon-github', 'github logo'
+ = svg 'github', 'github logo'
+ .user-controls
+ - if logged_in?
+ = link_to (_!current_user.name), settings_path, class: 'my-account'
+ = _!' | '
+ = link_to (_'forms.actions.generic.Log_out'), logout_path, class: 'logout'
+ - else
+ = signin_link
+ %ul.locales
+ -# - @alt_lang_urls.each do |locale, url|
+ -# %li
+ -# - locale_translation = language_name(locale, true)
+ -# %a{href: url, lang: locale}
+ -# =_'translate.content.change_locale', "Read in #{locale_translation}", vars: {language: locale_translation}, locale: locale if locale != I18n.locale.to_s
+ .site-info
+ .contact-us=link_to (_'links.footer.help_text.contact'), contact_path, id: 'contact-link'
+ .copy
+ =_'links.footer.help_text.contributors', 'Who contributed to building this website' do |title|
+ =link_to :humans_txt, {title: title} do
+ =((_!"©#{Date.today.strftime("%Y")} Bike!Bike!") || '').html_safe
diff --git a/app/views/shared/_navbar.html.haml b/app/views/shared/_navbar.html.haml
index 9649949..426314c 100644
--- a/app/views/shared/_navbar.html.haml
+++ b/app/views/shared/_navbar.html.haml
@@ -1,21 +1,27 @@
%nav
- #main-nav
- - begin
- = row(class: 'inner-nav') do
- = columns(medium: 4, class: 'inner-nav') do
- = link_to home_path, :class => 'logo' do
- = svg_sprite('icons', "bb-icon-logo", "Logo")
- = svg_sprite('icons', "bb-icon-logo-text", "Logo Text")
- = off_screen _!'Bike Bike'
- = columns(medium: 8, class: 'nav') do
- = nav_link :about
- = nav_link :policy
- - case (@conference && @conference.registration_status ? @conference.registration_status.to_sym : :closed)
- - when :pre
- = nav_link register_path(@conference.slug), :pre_register, :register
- - when :open
- = nav_link register_path(@conference.slug), :register
- - else
- = render 'shared/donate_button', :email_address => (@conference.paypal_email_address || @conference.email_address || (@conference.organizations.present? && @conference.organizations.first.present? ? @conference.organizations.first.email_address : nil))
- - rescue
-
\ No newline at end of file
+ #main-nav
+ = row(class: 'inner-nav') do
+ = columns(medium: 3) do
+ - protect do
+ = link_to home_path, :class => 'logo' do
+ -#= svg_sprite('icons', "bb-icon-logo", "Logo")
+ -#= svg_sprite('icons', "bb-icon-logo-text", "Logo Text")
+ = svg 'logo', (_!'Bike Bike')
+ = svg 'logo-text', (_!'Bike Bike')
+ = off_screen _!'Bike Bike'
+ = columns(medium: 6, class: 'nav') do
+ - protect do
+ = nav_link :about
+ = nav_link :policy
+ = nav_link :conferences
+ = columns(medium: 3, class: 'locale-nav') do
+ - protect do
+ %ul.locales
+ - @alt_lang_urls.each do |locale, url|
+ %li{class: locale == I18n.locale.to_s ? 'current' : nil}
+ - locale_translation = language_name(locale, true)
+ - translation_text = (_'translate.content.change_locale', "Read in #{locale_translation}", vars: {language: locale_translation}, locale: locale)
+ %a{href: url, lang: locale, title: translation_text}
+ %span{aria: {hidden: true}}=locale
+ = off_screen translation_text
+
\ No newline at end of file
diff --git a/app/views/user_mailer/conference_registration_confirmed_email.html.haml b/app/views/user_mailer/conference_registration_confirmed_email.html.haml
index 1a68ed3..f563a66 100644
--- a/app/views/user_mailer/conference_registration_confirmed_email.html.haml
+++ b/app/views/user_mailer/conference_registration_confirmed_email.html.haml
@@ -1,10 +1,10 @@
-%img{src: "#{@conference.poster.full.url}", style: "max-width: 100%;"}
-%div{style: 'padding: 25px'}
- %h1=_'register.email.registration_confirmed.thank_you',"Hi #{@data[:user][:username]}, thank you for completing your registration. We'll see you at Bike!Bike!"
- %h2=_'register.email.registration_confirmed.please_pay',"If you have not already done so, we ask that you pay the registration donation as soon as you can. At your convenience please visit the link below."
- %div{style: 'text-align:center'}
- %h1{style: 'background-color:#F11845;text-align:center;display:inline-block;padding: 5px 20px 10px;'}
- %a{href: @confirmation_url, style: 'color:#FFF;text-decoration:none;'}
- =_'register.email.registration_confirmed.pay_now',"Pay Registration Fees"
- %p=(_'register.email.registration_confirmed.info',"We'll have housing, loaner bikes, and food arranged for your arrival. If you have any other questions or concerns, please email bikebike2014columbus@gmail.com").gsub(/(\w+@\w+\.\w{2,3})/, '\1 ').html_safe
- %p=_'register.email.registration_confirmed.contact',"For urgent/emergency matters, you can reach our Outreach Coordinator, Reda, at 503-984-9191 or Jason at 614-364-3636."
\ No newline at end of file
+%img{src: "#{@conference.poster.full.url}", style: "max-width: 100%;"}
+%div{style: 'padding: 25px'}
+ %h1=_'register.email.registration_confirmed.thank_you',"Hi #{@data[:user][:username]}, thank you for completing your registration. We'll see you at Bike!Bike!"
+ %h2=_'register.email.registration_confirmed.please_pay',"If you have not already done so, we ask that you pay the registration donation as soon as you can. At your convenience please visit the link below."
+ %div{style: 'text-align:center'}
+ %h1{style: 'background-color:#F11845;text-align:center;display:inline-block;padding: 5px 20px 10px;'}
+ %a{href: @confirmation_url, style: 'color:#FFF;text-decoration:none;'}
+ =_'register.email.registration_confirmed.pay_now',"Pay Registration Fees"
+ %p=(_'register.email.registration_confirmed.info',"We'll have housing, loaner bikes, and food arranged for your arrival. If you have any other questions or concerns, please email bikebike2014columbus@gmail.com").gsub(/(\w+@\w+\.\w{2,3})/, '\1 ').html_safe
+ %p=_'register.email.registration_confirmed.contact',"For urgent/emergency matters, you can reach our Outreach Coordinator, Reda, at 503-984-9191 or Jason at 614-364-3636."
\ No newline at end of file
diff --git a/app/views/user_mailer/conference_registration_email.html.haml b/app/views/user_mailer/conference_registration_email.html.haml
index 60d2faa..97b8c74 100644
--- a/app/views/user_mailer/conference_registration_email.html.haml
+++ b/app/views/user_mailer/conference_registration_email.html.haml
@@ -1,7 +1,7 @@
-%img{src: "#{@conference.poster.full.url}", style: "max-width: 100%;"}
-%div{style: 'padding: 25px'}
- %h1=_'register.email.registration.please_confirm',"Hi #{@data[:user][:username]}, please confirm your registration for #{@conference.title}", vars: {:username => @data[:user][:username]}
- %div{style: 'text-align:center'}
- %h1{style: 'background-color:#F11845;text-align:center;display:inline-block;padding: 5px 20px 10px;'}
- %a{href: @confirmation_url, style: 'color:#FFF;text-decoration:none;'}
- =_'register.email.registration.confirm_button',"Yes I'm Really Coming!"
\ No newline at end of file
+%img{src: "#{@conference.poster.full.url}", style: "max-width: 100%;"}
+%div{style: 'padding: 25px'}
+ %h1=_'register.email.registration.please_confirm',"Hi #{@data[:user][:username]}, please confirm your registration for #{@conference.title}", vars: {:username => @data[:user][:username]}
+ %div{style: 'text-align:center'}
+ %h1{style: 'background-color:#F11845;text-align:center;display:inline-block;padding: 5px 20px 10px;'}
+ %a{href: @confirmation_url, style: 'color:#FFF;text-decoration:none;'}
+ =_'register.email.registration.confirm_button',"Yes I'm Really Coming!"
\ No newline at end of file
diff --git a/app/views/user_mailer/contact_details.html.haml b/app/views/user_mailer/contact_details.html.haml
index 1c48094..3ff8793 100644
--- a/app/views/user_mailer/contact_details.html.haml
+++ b/app/views/user_mailer/contact_details.html.haml
@@ -1,46 +1,46 @@
%h1 From
%table.error-report
- %tr
- %th Key
- %th Value
- %tr
- %td User ID
- %td=@from.is_a?(User) ? @from.id : ''
- %tr
- %td User Name
- %td=@from.is_a?(User) ? @from.name : ''
- %tr
- %td User Email
- %td=@from.is_a?(User) ? @from.email : @from
- %tr
- %td IP Address
- %td=@request['remote_ip']
- %tr
- %td UUID
- %td=@request['uuid']
- %tr
- %td URL
- %td=@request['original_url']
+ %tr
+ %th Key
+ %th Value
+ %tr
+ %td User ID
+ %td=@from.is_a?(User) ? @from.id : ''
+ %tr
+ %td User Name
+ %td=@from.is_a?(User) ? @from.name : ''
+ %tr
+ %td User Email
+ %td=@from.is_a?(User) ? @from.email : @from
+ %tr
+ %td IP Address
+ %td=@request['remote_ip']
+ %tr
+ %td UUID
+ %td=@request['uuid']
+ %tr
+ %td URL
+ %td=@request['original_url']
%h1 Message
%blockquote=markdown(@message)
%h1 Params
%table.error-report
- %tr
- %th Key
- %th Value
- - @params.each do | key, value |
- %tr
- %td=key
- %td=value
+ %tr
+ %th Key
+ %th Value
+ - @params.each do | key, value |
+ %tr
+ %td=key
+ %td=value
%h1 Request Environment
%table.error-report
- %tr
- %th Key
- %th Value
- - @request['env'].each do | key, value |
- %tr
- %td=key
- %td=value
\ No newline at end of file
+ %tr
+ %th Key
+ %th Value
+ - @request['env'].each do | key, value |
+ %tr
+ %td=key
+ %td=value
\ No newline at end of file
diff --git a/app/views/user_mailer/email_confirmation.html.haml b/app/views/user_mailer/email_confirmation.html.haml
index c70e0c5..9a194a0 100644
--- a/app/views/user_mailer/email_confirmation.html.haml
+++ b/app/views/user_mailer/email_confirmation.html.haml
@@ -1,6 +1,6 @@
%p=_'email.confirmation.paragraph.please_confirm','Hello! To gain access to registration and other features of Bike!Bike!, please confirm your email address by clicking on following link:'
- link = "#{@host}/confirm/#{@confirmation.token}"
%p
- %h3
- %b
- %a{href: link}=_'email.confirmation.link.please_confirm'
+ %h3
+ %b
+ %a{href: link}=_'email.confirmation.link.please_confirm'
diff --git a/app/views/user_mailer/error_report.html.haml b/app/views/user_mailer/error_report.html.haml
index 1ca2c22..f43ff07 100644
--- a/app/views/user_mailer/error_report.html.haml
+++ b/app/views/user_mailer/error_report.html.haml
@@ -2,52 +2,52 @@
%pre=@report
- if @exception.present?
- %h1 Backtrace
- %pre=@exception
+ %h1 Backtrace
+ %pre=@exception
%h1 Details
%table.error-report
- %tr
- %th Key
- %th Value
- %tr
- %td User ID
- %td=@user.present? ? @user.id : ''
- %tr
- %td User Name
- %td=@user.present? ? @user.name : ''
- %tr
- %td User Email
- %td=@user.present? ? @user.email : ''
- %tr
- %td IP Address
- %td=@request['remote_ip']
- %tr
- %td UUID
- %td=@request['uuid']
- %tr
- %td URL
- %td=@request['original_url']
- %tr
- %td Time
- %td=@time
+ %tr
+ %th Key
+ %th Value
+ %tr
+ %td User ID
+ %td=@user.present? ? @user.id : ''
+ %tr
+ %td User Name
+ %td=@user.present? ? @user.name : ''
+ %tr
+ %td User Email
+ %td=@user.present? ? @user.email : ''
+ %tr
+ %td IP Address
+ %td=@request['remote_ip']
+ %tr
+ %td UUID
+ %td=@request['uuid']
+ %tr
+ %td URL
+ %td=@request['original_url']
+ %tr
+ %td Time
+ %td=@time
%h1 Params
%table.error-report
- %tr
- %th Key
- %th Value
- - @params.each do | key, value |
- %tr
- %td=key
- %td=value
+ %tr
+ %th Key
+ %th Value
+ - @params.each do | key, value |
+ %tr
+ %td=key
+ %td=value
%h1 Request Environment
%table.error-report
- %tr
- %th Key
- %th Value
- - @request['env'].each do | key, value |
- %tr
- %td=key
- %td=value
+ %tr
+ %th Key
+ %th Value
+ - @request['env'].each do | key, value |
+ %tr
+ %td=key
+ %td=value
diff --git a/app/views/user_mailer/registration_confirmation.html.haml b/app/views/user_mailer/registration_confirmation.html.haml
index a523599..0152054 100644
--- a/app/views/user_mailer/registration_confirmation.html.haml
+++ b/app/views/user_mailer/registration_confirmation.html.haml
@@ -1,8 +1,8 @@
%p=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname}
- if @conference.registration_status.to_sym == :pre
- %p=_'email.registration.paragraph.pre_registered', :vars => {:conference_title => @conference.title}
+ %p=_'email.registration.paragraph.pre_registered', :vars => {:conference_title => @conference.title}
- else
- %p=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title}
+ %p=_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title}
- %p=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city}
+ %p=_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city}
diff --git a/app/views/user_mailer/registration_confirmation.text.haml b/app/views/user_mailer/registration_confirmation.text.haml
new file mode 100644
index 0000000..91c494b
--- /dev/null
+++ b/app/views/user_mailer/registration_confirmation.text.haml
@@ -0,0 +1,8 @@
+=_'email.general.paragraph.thank_you', "Thank you #{@user.firstname},", :vars => {:name => @user.firstname}
+
+- if @conference.registration_status.to_sym == :pre
+ =_'email.general.paragraph.pre_registered', "You have successfully pre-registered for #{@conference.title}. We will let you know when registration is fully open and if there is any important news about the conference that you should know about. We encourage you to create or volunteer to facilitate workshops soon. You can do that, or change your registration details at any time by clicking on the pre-register link again.", :vars => {:conference_title => @conference.title}
+- else
+ =_'email.registration.paragraph.confirmed', :vars => {:conference_title => @conference.title}
+
+ =_'email.general.paragraph.see_you', :vars => {:conference_location => @conference.location.city}
\ No newline at end of file
diff --git a/app/views/user_mailer/workshop_comment.html.haml b/app/views/user_mailer/workshop_comment.html.haml
index f3a2cdb..212f052 100644
--- a/app/views/user_mailer/workshop_comment.html.haml
+++ b/app/views/user_mailer/workshop_comment.html.haml
@@ -1,15 +1,15 @@
%p=_('email.workshop_comment.paragraph.user_said', vars: {user_name: @comment.user.name})
%blockquote
- =_!@comment.comment
+ =_!@comment.comment
- - if @comment.reply?
- - original_comment = @comment.comment_object
- %p=_('email.workshop_comment.paragraph.user_said', vars: {user_name: original_comment.user.name})
- %blockquote
- =_!original_comment.comment
+ - if @comment.reply?
+ - original_comment = @comment.comment_object
+ %p=_('email.workshop_comment.paragraph.user_said', vars: {user_name: original_comment.user.name})
+ %blockquote
+ =_!original_comment.comment
%p
- =_'email.workshop.paragraph.view_workshop'
- - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
- %a{href: workshop_link}=workshop_link
+ =_'email.workshop.paragraph.view_workshop'
+ - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
+ %a{href: workshop_link}=workshop_link
diff --git a/app/views/user_mailer/workshop_facilitator_request.html.haml b/app/views/user_mailer/workshop_facilitator_request.html.haml
index b39766b..421ee67 100644
--- a/app/views/user_mailer/workshop_facilitator_request.html.haml
+++ b/app/views/user_mailer/workshop_facilitator_request.html.haml
@@ -3,8 +3,8 @@
%blockquote=CGI::escapeHTML(@message || '').gsub(/\n/, ' ').html_safe
%p
- =_'email.workshop.paragraph.request_instructions',"You can approve or deny this request on your workshop page: "
- - workshop_link = @host + view_workshop_path(@conference.slug, @workshop.id)
- %a{href: workshop_link}=workshop_link
+ =_'email.workshop.paragraph.request_instructions',"You can approve or deny this request on your workshop page: "
+ - workshop_link = @host + view_workshop_path(@conference.slug, @workshop.id)
+ %a{href: workshop_link}=workshop_link
%p=_'email.workshop.paragraph.request_reply_instructions',"You can also reply directly to this email to ask follow-up questions."
diff --git a/app/views/user_mailer/workshop_original_content_changed.html.haml b/app/views/user_mailer/workshop_original_content_changed.html.haml
index e541e62..d7a88ff 100644
--- a/app/views/user_mailer/workshop_original_content_changed.html.haml
+++ b/app/views/user_mailer/workshop_original_content_changed.html.haml
@@ -1,14 +1,14 @@
%p=_('email.translations.paragraph.workshop_original_content_changed', "#{@translator.name} has modified the original content for #{@workshop.title}. Translations may need to be updated.", vars: {user_name: @translator.firstname, workshop_title: @workshop.title})
- @data.each do |field, values|
- %h2=_"forms.labels.generic.#{field.to_s}"
- %b=_'email.translations.headings.new_value'
- %blockquote=_!values[:new].html_safe
- %b=_'email.translations.headings.old_value'
- - if values[:old].present?
- %blockquote=_!values[:old].html_safe
+ %h2=_"forms.labels.generic.#{field.to_s}"
+ %b=_'email.translations.headings.new_value'
+ %blockquote=_!values[:new].html_safe
+ %b=_'email.translations.headings.old_value'
+ - if values[:old].present?
+ %blockquote=_!values[:old].html_safe
%p
- =_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
- - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
- %a{href: workshop_link}=workshop_link
+ =_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
+ - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
+ %a{href: workshop_link}=workshop_link
diff --git a/app/views/user_mailer/workshop_translated.html.haml b/app/views/user_mailer/workshop_translated.html.haml
index 6d93bb3..5cd76aa 100644
--- a/app/views/user_mailer/workshop_translated.html.haml
+++ b/app/views/user_mailer/workshop_translated.html.haml
@@ -1,14 +1,14 @@
%p=_('email.translations.paragraph.workshop_translated', "#{@translator.name} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title})
- @data.each do |field, values|
- %h2=_"forms.labels.generic.#{field.to_s}"
- %b=_'email.translations.headings.new_value'
- %blockquote=_!values[:new].html_safe
- %b=_'email.translations.headings.old_value'
- - if values[:old].present?
- %blockquote=_!values[:old].html_safe
+ %h2=_"forms.labels.generic.#{field.to_s}"
+ %b=_'email.translations.headings.new_value'
+ %blockquote=_!values[:new].html_safe
+ %b=_'email.translations.headings.old_value'
+ - if values[:old].present?
+ %blockquote=_!values[:old].html_safe
%p
- =_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
- - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
- %a{href: workshop_link}=workshop_link
+ =_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
+ - workshop_link = @host + view_workshop_path(@workshop.conference.slug, @workshop.id)
+ %a{href: workshop_link}=workshop_link
diff --git a/app/views/user_mailer/workshop_translated.text.haml b/app/views/user_mailer/workshop_translated.text.haml
new file mode 100644
index 0000000..341e4ba
--- /dev/null
+++ b/app/views/user_mailer/workshop_translated.text.haml
@@ -0,0 +1,25 @@
+=_('email.translations.paragraph.workshop_translated', "#{@translator.firstname} has modified the #{@locale_name} translation for #{@workshop.title}.", vars: {user_name: @translator.firstname, language: @locale_name, workshop_title: @workshop.title})
+
+
+- @data.each do |field, values|
+ =(_!'** ') + (_"forms.labels.generic.#{field.to_s}")
+ =(_!' - ') + (_'email.translations.headings.new_value')
+ =(_!' ------------------------------ ')
+ =_!values[:new]
+ =(_!' ------------------------------ ')
+
+
+ =(_!' - ') + (_'email.translations.headings.old_value')
+ =(_!' ------------------------------ ')
+ =_!values[:old]
+ =(_!' ------------------------------ ')
+
+
+ =(_!' - ') + (_'email.translations.headings.diff')
+ =(_!' ------------------------------ ')
+ =(_!values[:diff][:text])
+ =(_!' ------------------------------ ')
+
+
+=_'email.workshop.paragraph.view_workshop',"You can view the workshop here: "
+= view_workshop_path(@workshop.conference.slug, @workshop.id)
diff --git a/app/views/workshops/_show.html.haml b/app/views/workshops/_show.html.haml
index f7a8557..5f6ef3a 100644
--- a/app/views/workshops/_show.html.haml
+++ b/app/views/workshops/_show.html.haml
@@ -1,89 +1,89 @@
- is_facilitator = workshop.active_facilitator?(current_user)
= row class: 'view-object' do
- = columns(medium: 12) do
- %h2=_!workshop.title
- .workshop-interest
- - if workshop.can_show_interest?(current_user)
- = form_tag toggle_workshop_interest_path(workshop.conference.slug, workshop.id), class: 'js-xhr' do
- %span.interest-text=interest_text(workshop)
- %span.interest-button=interest_button(workshop)
- - else
- %span.interest-text=interest_text(workshop)
- = richtext workshop.info
- - if preview.blank? && translations_available_for_editing
- .actions
- - translations_available_for_editing.each do |locale|
- = link_to (_'actions.workshops.Translate', "Translate into #{language_name(locale)}", :vars => {:language => language_name(locale)}), translate_workshop_url(workshop.conference.slug, workshop.id, locale), :class => [:button, :translate]
- = columns(medium: 6) do
- %h3=_'articles.workshops.headings.facilitators'
- .facilitators
- - workshop.workshop_facilitators.each do |f|
- - u = User.find(f.user_id)
- - is_this_user = (f.user_id == current_user.id)
- - if logged_in? && (workshop.public_facilitator?(u) || is_this_user || is_facilitator)
- .facilitator
- .name=_!u.name
- .role
- =_"roles.workshops.facilitator.#{workshop.role(u).to_s}"
- - if is_facilitator && preview.blank?
- .details
- .email=u.email
- - if f.role.to_sym == :requested
- =(link_to (_'actions.workshops.Approve'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'approve'), :class => [:button, :modify])
- =(link_to (_'actions.workshops.Deny'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'deny'), :class => [:button, :delete])
- - elsif workshop.can_remove?(current_user, u)
- =(link_with_confirmation (_'actions.workshops.Make_Owner'), (_'modals.workshops.facilitators.confirm_transfer_ownership', vars: { user_name: u.name}),approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'switch_ownership'), :class => [:button, :modify]) unless f.role.to_sym == :creator || !workshop.creator?(current_user)
- =(link_with_confirmation (_"actions.workshops.#{is_this_user ? 'Leave' : 'Remove'}"), (_"modals.workshops.facilitators.confirm_remove#{is_this_user ? '_self' : ''}", vars: { user_name: u.name}), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => [:button, :delete])
- - if is_this_user && workshop.requested_collaborator?(current_user)
- .details
- =(link_with_confirmation (_'actions.workshops.Cancel_Request'), (_'modals.workshops.facilitators.confirm_cancel_request'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => [:button, :delete])
- - unless preview.present?
- =(link_to (_'actions.workshops.Facilitate'), facilitate_workshop_path(workshop.conference.slug, workshop.id), :class => [:button, workshop.needs_facilitators ? :accented : :subdued]) unless workshop.facilitator?(current_user)
- - if is_facilitator
- %h4=_'articles.workshops.headings.add_facilitator','Add a facilitator'
- = form_tag workshop_add_facilitator_path(workshop.conference.slug, workshop.id), :class => 'add-facilitator mini-flex-form' do
- .email-field.input-field
- = email_field_tag :email, nil, required: true
- = label_tag :email
- = off_screen (_'forms.actions.aria.add'), 'add-new-desc'
- = button_tag :add, aria: { labelledby: 'add-new-desc' }
- - languages = JSON.parse(workshop.languages || '[]')
- - if languages.present?
- = columns(medium: 6) do
- %h3=_'articles.workshops.headings.languages','Workshop Language'
- %p= _!((languages.map { |x| _"languages.#{x}" }).join(', ').to_s.html_safe)
- - if workshop.theme.present?
- = columns(medium: 6) do
- %h3=_'articles.workshops.headings.theme','Theme'
- %p= Workshop.all_themes.include?((workshop.theme || '').to_sym) ? (_"workshop.options.theme.#{workshop.theme}") : workshop.theme
- - if is_facilitator || workshop.conference.host?(current_user)
- - needs = JSON.parse(workshop.needs || '[]')
- - if needs.present?
- = columns(medium: 6) do
- %h3=_'articles.workshops.headings.needs','What do you need?'
- %p= _!((needs.map { |x| _"workshop.options.needs.#{x}" }).join(', ').to_s.html_safe)
- - if workshop.notes.present?
- = columns(medium: 12, class: 'workshop-notes') do
- %h3=_'articles.workshops.headings.notes','Notes'
- = richtext workshop.notes, 3
- = columns(medium: 12, id: :comments) do
- %h3=_'articles.workshops.headings.Comments'
- %ul.comments
- - workshop.comments.each do | comment |
- %li.comment{id: "comment-#{comment.id}"}
- = comment(comment)
- - sub_comments = comment.comments
- - if sub_comments.present?
- %ul.sub-comments.comments
- - sub_comments.each do | sub_comment |
- %li.sub-comment.comment{id: "comment-#{sub_comment.id}"}
- = comment(sub_comment)
- = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do
- = hidden_field_tag :comment_id, comment.id
- = textarea :reply, nil, plain: true, required: true, label: false, labelledby: "replyto-#{comment.id}"
- .actions.right
- = button_tag :reply, value: :reply, data: {opens: "#comment-#{comment.id} form", focus: :textarea}, class: :small, id: "replyto-#{comment.id}"
- = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do
- = textarea :comment, nil, plain: true, required: true, label: false, labelledby: :add_comment
- .actions.right
- = button_tag :add_comment, value: :add_comment, id: :add_comment
+ = columns(medium: 12) do
+ %h2=_!workshop.title
+ .workshop-interest
+ - if workshop.can_show_interest?(current_user)
+ = form_tag toggle_workshop_interest_path(workshop.conference.slug, workshop.id), class: 'js-xhr' do
+ %span.interest-text=interest_text(workshop)
+ %span.interest-button=interest_button(workshop)
+ - else
+ %span.interest-text=interest_text(workshop)
+ = richtext workshop.info
+ - if preview.blank? && translations_available_for_editing
+ .actions
+ - translations_available_for_editing.each do |locale|
+ = link_to (_'actions.workshops.Translate', "Translate into #{language_name(locale)}", :vars => {:language => language_name(locale)}), translate_workshop_url(workshop.conference.slug, workshop.id, locale), :class => [:button, :translate]
+ = columns(medium: 6) do
+ %h3=_'articles.workshops.headings.facilitators'
+ .facilitators
+ - workshop.workshop_facilitators.each do |f|
+ - u = User.find(f.user_id)
+ - is_this_user = (f.user_id == current_user.id)
+ - if logged_in? && (workshop.public_facilitator?(u) || is_this_user || is_facilitator)
+ .facilitator
+ .name=_!u.name
+ .role
+ =_"roles.workshops.facilitator.#{workshop.role(u).to_s}"
+ - if is_facilitator && preview.blank?
+ .details
+ .email=u.email
+ - if f.role.to_sym == :requested
+ =(link_to (_'actions.workshops.Approve'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'approve'), :class => [:button, :modify])
+ =(link_to (_'actions.workshops.Deny'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'deny'), :class => [:button, :delete])
+ - elsif workshop.can_remove?(current_user, u)
+ =(link_with_confirmation (_'actions.workshops.Make_Owner'), (_'modals.workshops.facilitators.confirm_transfer_ownership', vars: { user_name: u.name}),approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'switch_ownership'), :class => [:button, :modify]) unless f.role.to_sym == :creator || !workshop.creator?(current_user)
+ =(link_with_confirmation (_"actions.workshops.#{is_this_user ? 'Leave' : 'Remove'}"), (_"modals.workshops.facilitators.confirm_remove#{is_this_user ? '_self' : ''}", vars: { user_name: u.name}), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => [:button, :delete])
+ - if is_this_user && workshop.requested_collaborator?(current_user)
+ .details
+ =(link_with_confirmation (_'actions.workshops.Cancel_Request'), (_'modals.workshops.facilitators.confirm_cancel_request'), approve_facilitate_workshop_request_path(workshop.conference.slug, workshop.id, f.user_id, 'remove'), :class => [:button, :delete])
+ - unless preview.present?
+ =(link_to (_'actions.workshops.Facilitate'), facilitate_workshop_path(workshop.conference.slug, workshop.id), :class => [:button, workshop.needs_facilitators ? :accented : :subdued]) unless workshop.facilitator?(current_user)
+ - if is_facilitator
+ %h4=_'articles.workshops.headings.add_facilitator','Add a facilitator'
+ = form_tag workshop_add_facilitator_path(workshop.conference.slug, workshop.id), :class => 'add-facilitator mini-flex-form' do
+ .email-field.input-field
+ = email_field_tag :email, nil, required: true
+ = label_tag :email
+ = off_screen (_'forms.actions.aria.add'), 'add-new-desc'
+ = button_tag :add, aria: { labelledby: 'add-new-desc' }
+ - languages = JSON.parse(workshop.languages || '[]')
+ - if languages.present?
+ = columns(medium: 6) do
+ %h3=_'articles.workshops.headings.languages','Workshop Language'
+ %p= _!((languages.map { |x| _"languages.#{x}" }).join(', ').to_s.html_safe)
+ - if workshop.theme.present?
+ = columns(medium: 6) do
+ %h3=_'articles.workshops.headings.theme','Theme'
+ %p= Workshop.all_themes.include?((workshop.theme || '').to_sym) ? (_"workshop.options.theme.#{workshop.theme}") : workshop.theme
+ - if is_facilitator || workshop.conference.host?(current_user)
+ - needs = JSON.parse(workshop.needs || '[]')
+ - if needs.present?
+ = columns(medium: 6) do
+ %h3=_'articles.workshops.headings.needs','What do you need?'
+ %p= _!((needs.map { |x| _"workshop.options.needs.#{x}" }).join(', ').to_s.html_safe)
+ - if workshop.notes.present?
+ = columns(medium: 12, class: 'workshop-notes') do
+ %h3=_'articles.workshops.headings.notes','Notes'
+ = richtext workshop.notes, 3
+ = columns(medium: 12, id: :comments) do
+ %h3=_'articles.workshops.headings.Comments'
+ %ul.comments
+ - workshop.comments.each do | comment |
+ %li.comment{id: "comment-#{comment.id}"}
+ = comment(comment)
+ - sub_comments = comment.comments
+ - if sub_comments.present?
+ %ul.sub-comments.comments
+ - sub_comments.each do | sub_comment |
+ %li.sub-comment.comment{id: "comment-#{sub_comment.id}"}
+ = comment(sub_comment)
+ = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do
+ = hidden_field_tag :comment_id, comment.id
+ = textarea :reply, nil, plain: true, required: true, label: false, labelledby: "replyto-#{comment.id}"
+ .actions.right
+ = button_tag :reply, value: :reply, data: {opens: "#comment-#{comment.id} form", focus: :textarea}, class: :small, id: "replyto-#{comment.id}"
+ = form_tag workshop_comment_path(workshop.conference.slug, workshop.id) do
+ = textarea :comment, nil, plain: true, required: true, label: false, labelledby: :add_comment
+ .actions.right
+ = button_tag :add_comment, value: :add_comment, id: :add_comment
diff --git a/app/views/workshops/_workshop_previews.html.haml b/app/views/workshops/_workshop_previews.html.haml
index 14219d8..2c1bfa8 100644
--- a/app/views/workshops/_workshop_previews.html.haml
+++ b/app/views/workshops/_workshop_previews.html.haml
@@ -1,15 +1,15 @@
%ul.workshop-list
- - workshops.sort_by{ |w| w.title.downcase }.each do |w|
- - is_interested = w.interested?(current_user)
- %li{class: [is_interested ? :interested : nil]}
- %h4.title=w.title
- .workshop-interest
- - if w.can_show_interest?(current_user)
- = form_tag toggle_workshop_interest_path(w.conference.slug, w.id), class: 'js-xhr' do
- %span.interest-text=interest_text(w)
- %span.interest-button=interest_button(w)
- - elsif w.interested_count > 0
- %span.interest-text=interest_text(w)
- .workshop-description=richtext w.info, 4
- .actions.right
- = link_to (_'articles.workshops.info.read_more'), view_workshop_path(w.conference.slug, w.id), class: ['workshop-link', :button, :small]
+ - workshops.sort_by{ |w| w.title.downcase }.each do |w|
+ - is_interested = w.interested?(current_user)
+ %li{class: [is_interested ? :interested : nil]}
+ %h4.title=w.title
+ .workshop-interest
+ - if w.can_show_interest?(current_user)
+ = form_tag toggle_workshop_interest_path(w.conference.slug, w.id), class: 'js-xhr' do
+ %span.interest-text=interest_text(w)
+ %span.interest-button=interest_button(w)
+ - elsif w.interested_count > 0
+ %span.interest-text=interest_text(w)
+ .workshop-description=richtext w.info, 4
+ .actions.right
+ = link_to (_'articles.workshops.info.read_more'), view_workshop_path(w.conference.slug, w.id), class: ['workshop-link', :button, :small]
diff --git a/app/views/workshops/index.html.haml b/app/views/workshops/index.html.haml
index 62c0826..0cd1bba 100644
--- a/app/views/workshops/index.html.haml
+++ b/app/views/workshops/index.html.haml
@@ -1,18 +1,18 @@
= render 'conferences/page_header', :page_key => 'Workshops'
%article
- = row do
- = columns(medium: 12) do
- %h2=_'articles.workshops.headings.Workshops'
- = row do
- = columns(medium: 12) do
- %p=_'articles.workshops.paragraphs.Workshops', :p
- = row do
- = columns(medium: 12) do
- %h3=_'articles.workshops.headings.Your_Workshops'
- = render 'workshops/workshop_previews', :workshops => @my_workshops
- = link_to (_'actions.workshops.create','New Workshop'), create_workshop_path(@this_conference.slug), class: [:button, :modify] if @conference.registration_open
- = row do
- = columns(medium: 12) do
- %h3=_'articles.workshops.headings.All_Workshops'
- = render 'workshops/workshop_previews', :workshops => @workshops
\ No newline at end of file
+ = row do
+ = columns(medium: 12) do
+ %h2=_'articles.workshops.headings.Workshops'
+ = row do
+ = columns(medium: 12) do
+ %p=_'articles.workshops.paragraphs.Workshops', :p
+ = row do
+ = columns(medium: 12) do
+ %h3=_'articles.workshops.headings.Your_Workshops'
+ = render 'workshops/workshop_previews', :workshops => @my_workshops
+ = link_to (_'actions.workshops.create','New Workshop'), create_workshop_path(@this_conference.slug), class: [:button, :modify] if @conference.registration_open
+ = row do
+ = columns(medium: 12) do
+ %h3=_'articles.workshops.headings.All_Workshops'
+ = render 'workshops/workshop_previews', :workshops => @workshops
\ No newline at end of file
diff --git a/app/views/workshops/new.html.haml b/app/views/workshops/new.html.haml
index 11dc448..204619f 100644
--- a/app/views/workshops/new.html.haml
+++ b/app/views/workshops/new.html.haml
@@ -1,41 +1,41 @@
= render 'conferences/page_header', :page_key => 'Conference_Registration'
%article
- = row do
- = form_tag save_workshop_path(@this_conference.slug), class: 'composition' do
- = columns(medium: 12) do
- - if @workshop.id.present?
- = hidden_field_tag :workshop_id, @workshop.id
- - if @is_translating
- %h2=_(@page_title, vars: @page_title_vars)
- = hidden_field_tag :translation, @translation
- - else
- %h2=_@page_title, :t
- - else
- %h2=_@page_title, :t
+ = row do
+ = form_tag save_workshop_path(@this_conference.slug), class: 'composition' do
+ = columns(medium: 12) do
+ - if @workshop.id.present?
+ = hidden_field_tag :workshop_id, @workshop.id
+ - if @is_translating
+ %h2=_(@page_title, vars: @page_title_vars)
+ = hidden_field_tag :translation, @translation
+ - else
+ %h2=_@page_title, :t
+ - else
+ %h2=_@page_title, :t
- = registration_step_menu
-
- = textfield :title, @title, required: true, lang: @translation, big: true, original_value: @is_translating ? @workshop.title! : nil, original_lang: @workshop.locale
- = textarea :info, @info, help: 'articles.workshops.paragraphs.info', lang: @translation, original_value: @is_translating ? richtext(@workshop.info!, 4).html_safe : nil, original_lang: @workshop.locale
- - if !@is_translating && (@workshop.id.blank? || @can_edit)
- = columns(medium: 6) do
- = checkboxes :languages, User.AVAILABLE_LANGUAGES, @languages, 'languages', vertical: true, heading: 'articles.workshops.headings.languages', help: 'articles.workshops.paragraphs.languages'
- = radiobuttons :theme, Workshop.all_themes, @workshop.theme, 'workshop.options.theme', vertical: true, heading: 'articles.workshops.headings.theme', help: 'articles.workshops.paragraphs.theme', other: true
- = columns(medium: 6) do
- = checkboxes :needs, Workshop.all_needs, JSON.parse(@workshop.needs || '[]'), 'workshop.options.needs', vertical: true, heading: 'articles.workshops.headings.needs', help: 'articles.workshops.paragraphs.needs'
- = radiobuttons :space, Workshop.all_spaces, @workshop.space, 'workshop.options.space', vertical: true, heading: 'articles.workshops.headings.space', help: 'articles.workshops.paragraphs.space'
+ = registration_step_menu
+
+ = textfield :title, @title, required: true, lang: @translation, big: true, original_value: @is_translating ? @workshop.title! : nil, original_lang: @workshop.locale
+ = textarea :info, @info, help: 'articles.workshops.paragraphs.info', lang: @translation, original_value: @is_translating ? richtext(@workshop.info!, 4).html_safe : nil, original_lang: @workshop.locale
+ - if !@is_translating && (@workshop.id.blank? || @can_edit)
+ = columns(medium: 6) do
+ = checkboxes :languages, User.AVAILABLE_LANGUAGES, @languages, 'languages', vertical: true, heading: 'articles.workshops.headings.languages', help: 'articles.workshops.paragraphs.languages'
+ = radiobuttons :theme, Workshop.all_themes, @workshop.theme, 'workshop.options.theme', vertical: true, heading: 'articles.workshops.headings.theme', help: 'articles.workshops.paragraphs.theme', other: true
+ = columns(medium: 6) do
+ = checkboxes :needs, Workshop.all_needs, JSON.parse(@workshop.needs || '[]'), 'workshop.options.needs', vertical: true, heading: 'articles.workshops.headings.needs', help: 'articles.workshops.paragraphs.needs'
+ = radiobuttons :space, Workshop.all_spaces, @workshop.space, 'workshop.options.space', vertical: true, heading: 'articles.workshops.headings.space', help: 'articles.workshops.paragraphs.space'
- %h3#needs_facilitators-label=_'articles.workshops.headings.needs_facilitators','Looking for help?'
- .input-field-help#needs_facilitators-desc=_'articles.workshops.paragraphs.needs_facilitators', :s, 2
- %fieldset.check-box-field.vertical.input-field{aria: {labeledby: 'needs_facilitators-label', describedby: 'needs_facilitators-desc'}}
- = check_box_tag :needs_facilitators, 1, @workshop.needs_facilitators
- = label_tag :needs_facilitators do
- = _'workshop.options.needs_facilitators', 'Needs Additional Facilitators'
- = columns(medium: 12) do
- = textarea :notes, @workshop.notes, warning: 'articles.workshops.paragraphs.notes'
- = columns(medium: 12) do
- .actions.next-prev
- = button_tag :save, value: :save
- = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true
+ %h3#needs_facilitators-label=_'articles.workshops.headings.needs_facilitators','Looking for help?'
+ .input-field-help#needs_facilitators-desc=_'articles.workshops.paragraphs.needs_facilitators', :s, 2
+ %fieldset.check-box-field.vertical.input-field{aria: {labeledby: 'needs_facilitators-label', describedby: 'needs_facilitators-desc'}}
+ = check_box_tag :needs_facilitators, 1, @workshop.needs_facilitators
+ = label_tag :needs_facilitators do
+ = _'workshop.options.needs_facilitators', 'Needs Additional Facilitators'
+ = columns(medium: 12) do
+ = textarea :notes, @workshop.notes, warning: 'articles.workshops.paragraphs.notes'
+ = columns(medium: 12) do
+ .actions.next-prev
+ = button_tag :save, value: :save
+ = button_tag :cancel, value: :cancel, class: :subdued, formnovalidate: true
diff --git a/app/views/workshops/show.html.haml b/app/views/workshops/show.html.haml
index 88bf3d2..7486746 100644
--- a/app/views/workshops/show.html.haml
+++ b/app/views/workshops/show.html.haml
@@ -3,9 +3,9 @@
= registration_step_menu
%article
- = render 'workshops/show', :workshop => @workshop, :translations_available_for_editing => @translations_available_for_editing, :preview => false
- = row do
- = columns(medium: 12) do
- .actions.next-prev
- = (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => [:button, :modify]) if @workshop.can_edit?(current_user)
- = (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), :class => 'button delete') if @workshop.can_delete?(current_user)
+ = render 'workshops/show', :workshop => @workshop, :translations_available_for_editing => @translations_available_for_editing, :preview => false
+ = row do
+ = columns(medium: 12) do
+ .actions.next-prev
+ = (link_to (_'actions.workshops.Edit'), edit_workshop_path(@this_conference.slug, @workshop.id), :class => [:button, :modify]) if @workshop.can_edit?(current_user)
+ = (link_to (_'actions.workshops.Delete'), delete_workshop_path(@this_conference.slug, @workshop.id), :class => 'button delete') if @workshop.can_delete?(current_user)
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
index 6ec68b7..deb050f 100644
--- a/config/initializers/assets.rb
+++ b/config/initializers/assets.rb
@@ -8,4 +8,4 @@ Rails.application.config.assets.version = '1.0'
# Precompile additional assets.
# application.js, application.css, and all non-JS/CSS in app/assets folder are already added.
-Rails.application.config.assets.precompile += %w( user-mailer.css map.js pen.js time.js editor.js markdown.js html2canvas.js main.js registrations.js housing.js schedule.js home_schedule.js favicon.ico )
+Rails.application.config.assets.precompile += %w( topojson.js pen.js *.js user-mailer.css favicon.ico )
diff --git a/config/initializers/geocoder.rb b/config/initializers/geocoder.rb
index e0a08de..49e4b0a 100644
--- a/config/initializers/geocoder.rb
+++ b/config/initializers/geocoder.rb
@@ -1,21 +1,21 @@
-# config/initializers/geocoder.rb
-Geocoder.configure(
- # geocoding service (see below for supported options):
- :lookup => :google,
-
- # IP address geocoding service (see below for supported options):
- :ip_lookup => :freegeoip,
-
- # to use an API key:
- # :api_key => "AIzaSyDitM1lyVWkrumteDvSkje6GiIKYyHlAXM",
-
- # geocoding service request timeout, in seconds (default 3):
- :timeout => 5,
-
- # set default units to kilometers:
- :units => :km,
-
- # caching (see below for details):
- #:cache => Redis.new,
- #:cache_prefix => "..."
-)
+# config/initializers/geocoder.rb
+Geocoder.configure(
+ # geocoding service (see below for supported options):
+ :lookup => :google,
+
+ # IP address geocoding service (see below for supported options):
+ :ip_lookup => :freegeoip,
+
+ # to use an API key:
+ # :api_key => "AIzaSyDitM1lyVWkrumteDvSkje6GiIKYyHlAXM",
+
+ # geocoding service request timeout, in seconds (default 3):
+ :timeout => 5,
+
+ # set default units to kilometers:
+ :units => :km,
+
+ # caching (see below for details):
+ #:cache => Redis.new,
+ #:cache_prefix => "..."
+)
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 2c333c3..1f55cb0 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -171,9 +171,37 @@ en:
empty: 'Please select a space'
email:
exists: An account with this email address already exists
+ start_date:
+ start_date_after_end_date: Start date must be before end date
+ error: Error setting start date
+ end_date:
+ error: Error setting end date
housing:
space:
companions: This host wishes to be housed with %{name}
+ admin:
+ administrators: Error updating organizations
+ dates: Error updating dates
+ description: Error saving your conference description
+ poster: An error occurred uploading your conference poster
+ payment_message: An error occurred saving your the payment message
+ suggested_amounts: An error occurred saving your payment amounts
+ paypal: An error occurred saving your paypal details
+ registration_status: An error occurred updating your conference registration status
+ registrations: Error updaing registration
+ broadcast: Error sending your message
+ providers: Error saving housing provider
+ housing: Error
+ locations: Error saving your location
+ meals: Error saving your meal
+ events: Error saving your event
+ workshop_times: Error saving your workshop time
+ schedule: Error
+ publish_schedule: An error occurred while publishing your schedule
+ error_adding_org_member: Error adding user to organization
+ error_removing_org_member: Error removing user from organization
+ error_adding_administrator: Error adding administrator
+ error_removing_administrator: Error removing administrator
template:
body: 'There were problems with the following fields:'
header:
@@ -183,11 +211,38 @@ en:
messages:
location_corrected: Your location was corrected from "%{original}" to "%{corrected}". If this doesn't reflect your intended location, you can change this again in the contact info step.
housing:
- space:
- overbooked: This space is overbooked
- companions: This guest wishes to be housed with %{name}
- space: This guest wishes to be housed in a %{expected}
- dates: This guest's arrival or departure dates conflict with the host's availability dates
+ space:
+ overbooked: This space is overbooked
+ companions: This guest wishes to be housed with %{name}
+ space: This guest wishes to be housed in a %{expected}
+ dates: This guest's arrival or departure dates conflict with the host's availability dates
+ success:
+ messages:
+ admin:
+ administrators: Organizations updated
+ dates: Conference dates updated successfully
+ description: Conference description updated successfully
+ poster: Conference poster uploaded
+ payment_message: Your payment message has been updated
+ suggested_amounts: Your suggested payment amounts have been updated
+ paypal: Your paypal information has been updated
+ registration_status: Your conference registration status has been updated
+ stats: ''
+ registrations: Registration updated
+ broadcast: Message sent
+ providers: Provider updated
+ housing: Housing updated
+ locations: Location saved
+ meals: Meal saved
+ events: Event saved
+ workshop_times: Workshop times saved
+ schedule: Schedule updated
+ publish_schedule: Your schedule has been published
+ unpublish_schedule: Your schedule has been un-published
+ org_member_added: User added to organization
+ org_member_removed: User removed from organization
+ administrator_added: Administrator added to conference
+ administrator_removed: Administrator removed from conference
helpers:
select:
prompt: Please select
@@ -873,7 +928,20 @@ en:
delete:
confirm: You are about to delete ‘%{title}’. Are you sure you want to continue?
articles:
+ conferences:
+ headings:
+ conferences: National and Regional Conferences
+ Conference_List: Conferences
+ types:
+ future: Upcoming conferences
+ passed: Passed conferences
+ paragraphs:
+ conferences: Bike!Bike! conferences are held nationally once a year in a different city in North America; regional conferences can be held anywhere, anytime.
admin:
+ headings:
+ back: Back to Administration
+ paragraphs:
+ administration: From this dashboard you can configure your conference details, open registration, view statictics, compile your workshop scheulde, and more.
stats:
description: On this page you can view a breakdown of the registration process so far as well as download more detailed data in spreadsheets.
headings:
@@ -891,21 +959,47 @@ en:
meals:
description: On this page you can schedule the meals that you will be serving.
no_locations_warning: Before you can add meals, you must first add locations.
+ heading: Meals
events:
description: On this page you can schedule events. Events are any type of event that isn't a meal or a workshop. You can scheulde group meetings, parties, or group ride for example.
no_locations_warning: Before you can add events, you must first add locations.
+ heading: Events
+ headings:
+ locations: Locations
+ meals: Meals
+ events: Events
+ descriptions:
+ locations: Create the list of locations that you will be using for events, meals, and workshops.
+ meals: List the meals that you will be providing, the meals will be added to your schedule
+ events: Create event details. These events should be any type of event other than meals and workshops such as meeting, rides, and parties. The events will be added to your schedule.
schedule:
description: On this page you can schedule workshops and publish your schedule to the front page.
no_locations_warning: Before you can schedule workshops, you must first add locations.
+ heading: Schedule
+ headings:
+ workshop_times: Workshop Times
+ schedule: Schedule Workshops
+ publish_schedule: Publish Your Schedule
+ descriptions:
+ workshop_times: Set up blocks of time each day when workshops will be held.
+ schedule: Set a time and location for all proposed workshops.
+ publish_schedule: Once you have finalized your schedule, make it public here.
housing:
- description: The housing tool can be used to help you set up visitors that have requested to be housed with a host. When you see an error icon beside a host's space or a guest's details, hover over it to see warnings. Ideally you should only check marks which will indicate that all hosts and guests are happy.
+ description: Arrange housing for registratnts visiting your city. Pair each registratnt who has asked for housing with a registratnt that has volunteered to house guests base on their individual needs and wants.
+ heading: Housing
headings:
hosts: Hosts
guests: Guests
email: Email
housing: Preference
arrival_departure: In City
+ providers: Housing Providers
+ housing: Arrange Housing
+ descriptions:
+ providers: Although most housing providers shuold be encouraged to supply their own details through the registration process, you can add and edit housing provider details manually here.
+ housing: Pair each housing provider with a list of guests.
locations:
+ heading: Locations
description: Locations are used to schedule workshops, events, and meals. Once your schedule is published, users will be able to see the name and address and be given a link to a map so that they can find their way.
headings:
add_event: Add Event
@@ -929,18 +1023,56 @@ en:
paypal_info: PayPal Info
paragraphs:
paypal_info: PayPal info is used for donations and fee payments. You must enter all fields to enable online fee payments, see the PayPal API credential guide for more information.
+ info:
+ heading: Basic Info
+ headings:
+ dates: Conference Dates
+ administrators: Administrators and Organizations
+ description: Description
+ poster: Poster
+ Host_Organizations: Host Organizations
+ External_Administrators: External Administrators
+ description: These are the basic details about your conference that you will likely want to configure before enabling your conference. One ready, contact the site administrator to make the conference public.
+ descriptions:
+ dates: Set your conference start and end dates
+ description: Modify the text that is displayed on the front page
+ poster: Upload your conference poster
+ administrators: Set the conference host organizations and other members who have access to these administration tools
+ Host_Organizations: Select all organizations from known organizations in %{city_name} that will be helping to host your conference. All members of each organization will be granted access to these administration tools, if you require administrators that are not members of an organization, you can add them below. If an organization is not listed here, please contact a site administrator.
+ External_Administrators: Users from outside of organizations in your city can be granted administration privledges here.
+ registration:
+ heading: Registration
+ headings:
+ registration_status: Registration Status
+ stats: Statistics
+ registrations: Modify Registrations
+ broadcast: Contact Users
+ description: Open or close registration, view registration statistics, modify information subbmitted by registratnts and contact users.
+ descriptions:
+ registration_status: Open or close registration to your conference.
+ stats: View a breakdown of statictics, how many users have registered, how much money have been collected, etc.
+ registrations: View and edit all data collected through the registration process.
+ broadcast: Send emails to targeted subsets of users.
broadcast:
+ heading: Broadcast
description: The broadcast tool is used to contact users through email. You can send messages en masse to select groups of users.
broadcast_sent:
description: Your message has been sent.
workshop_times:
+ heading: Workshop Times
description: Before you scheulde workshops, you must first create blocks of time when the workshops will be.
payment:
- description: Here you can set up how users will pay you for registration
+ heading: Payment
+ description: If you wish to collect dontaions and registration fees, you will need to confgure your payment details
headings:
- payment_amounts: Suggested Payment Amounts
- paragraphs:
- payment_amounts: Enter up to five suggested payment amounts. Users will still be able to select any amount including none but these amounts will show up as easy to select buttons on the 'Donations' page.
+ payment_message: Payment Message
+ suggested_amounts: Suggested Payment Amounts
+ paypal: PayPal Info
+ descriptions:
+ payment_message: Set the message that is displayed on the registration fee page. A default message will be supplied but if you wish to communicate how funds will be used you may want to set a personal message.
+ suggested_amounts: Enter up to five suggested payment amounts. Users will still be able to select any amount including none but these amounts will show up as easy to select buttons on the 'Donations' page.
+ paypal:
+ To enable PayPal payments, you will need to supply information on your organization's PayPal account
contact:
headings:
contact: Send us a question or a complement
@@ -970,6 +1102,7 @@ en:
conference_registration:
headings:
Administration: Administration
+ Conference_Administration: '%{title} Administration'
administration: Administration
Policy_Agreement: Safer Space Agreement
policy: Policy
@@ -1224,6 +1357,8 @@ en:
forms:
labels:
generic:
+ start_date: Start Date
+ end_date: End Date
search: Search
payment_message: Payment Message
other: Disabilities, housing preferences, etc.
@@ -1279,8 +1414,12 @@ en:
bike: Bike
food: Food
housing: Housing
+ no_file_selected: No file selected
actions:
generic:
+ upload: Upload
+ select_file: Select a file
+ administrate: Administrate
login: Sign In
Log_out: Sign out
agree: I Agree
@@ -1311,6 +1450,7 @@ en:
add_comment: Add Comment
reply: Reply
add_member: Add
+ remove_member: Remove
publish: Publish Schedule
un_publish: Un-Publish Schedule
add_block: Add
@@ -1333,7 +1473,22 @@ en:
housing_providers: Housing providers
guests: Everyone who has requested housing
all: Everyone
+ conferences:
+ types:
+ annual: 'Annual Bike!Bike!'
+ 'n': 'North'
+ s: 'South'
+ e: 'East'
+ w: 'West'
+ ne: 'Northeast'
+ nw: 'Northwest'
+ se: 'Southeast'
+ sw: 'Southwest'
+ confirmations:
+ delete: Are you sure you want to delete?
page_titles:
+ administration:
+ Administration: '%{title} Administration'
'403':
Access_Denied: Access Denied
Please_Confirm_Email: Please confirm your email
@@ -1345,6 +1500,7 @@ en:
About_BikeBike: About Bike!Bike!
contact:
Contact_Us: Contact Us
+ Conferences: Conferences
conferences:
Conference_Registration: Conference Registration
Create_Workshop: Create a Workshop
@@ -1408,6 +1564,19 @@ en:
Events: Events
Schedule: Schedule
Workshop_Times: Workshop Times
+ info: Basic Info
+ registration: Registration
+ edit: Edit
+ stats: Stats
+ payment: Payment
+ broadcast: Broadcast
+ broadcast_sent: Broadcast Sent
+ housing: Housing
+ locations: Locations
+ meals: Meals
+ events: Events
+ schedule: Schedule
+ workshop_times: Workshop Times
actions:
workshops:
create: New Workshop
@@ -1470,6 +1639,8 @@ en:
other: Other
race_gender: Race, Gender, or Class Politics
needs_facilitators: Needs Additional Facilitators
+ user:
+ not_found: Unregistered
email:
confirmation:
paragraph:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index ad3c776..14281c4 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -742,6 +742,15 @@ es:
donate:
button_label: Donar
articles:
+ conferences:
+ headings:
+ conferences: Conferencias nacionales y regionales
+ Conference_List: Conferencias
+ types:
+ future: Próximas conferencias
+ passed: Conferencias aprobadas
+ paragraphs:
+ conferences: Bike!Bike! conferencias se llevan a cabo nacionalmente una vez al año en una ciudad diferente en América del Norte; Las conferencias regionales pueden celebrarse en cualquier lugar y en cualquier momento.
conference_registration:
headings:
Workshops_Looking_For_Facilitators: Talleres que necesitan facilitadorxs
@@ -1012,6 +1021,8 @@ es:
companion: Acompañante
actions:
generic:
+ create: Crear
+ administrate: Administrar
add_comment: Añadir comentario
agree: Estoy en acuerdo
register: Registrarse
@@ -1054,6 +1065,7 @@ es:
download:
Excel: Descarga los datos en formato excel
page_titles:
+ Conferences: Conferencias
About_BikeBike: Acerca de Bike!Bike!
about:
About_BikeBike: Quienes Bike!Bike!
diff --git a/config/routes.rb b/config/routes.rb
index b3ff187..b0063bb 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,71 +1,78 @@
BikeBike::Application.routes.draw do
- get '/organizations/json' => 'organizations#json', :as => :organizations_json
+ # Conferences
+ get '/conferences' => 'conferences#list', as: :conferences
+
+ # Administrator only
+ get '/conferences/new' => 'admin#new', as: :new_conference
+ post '/conferences/save' => 'admin#save', as: :save_conference
+ get '/conferences/:slug/edit' => 'admin#edit', as: :edit_conference
+ get '/conferences/:slug' => 'conferences#view', as: :conference
- get '/conferences/:slug/edit' => 'conferences#edit', :as => :edit_conference
- post '/conferences/:slug/save' => 'conferences#save', :as => :save_conference
+ # Conference registration
+ match '/conferences/:slug/register' => 'conferences#register', as: :register, via: [:get, :post]
+ get '/conferences/:slug/register/:step' => 'conferences#register', as: :register_step
+ get '/conferences/:slug/register/:button/:confirmation_token' => 'conferences#register', as: :register_paypal_confirm
+
+ # Conference administratin
+ get '/conferences/:slug/administration' => 'conference_administration#administration', as: :administrate_conference
+ get '/conferences/:slug/administration/:step' => 'conference_administration#administration_step', as: :administration_step
+ post '/conferences/:slug/administration/update/:step' => 'conference_administration#admin_update', as: :administration_update
+ get '/conferences/:slug/administration/events/edit/:id' => 'conference_administration#edit_event', as: :edit_event
+ get '/conferences/:slug/administration/locations/edit/:id' => 'conference_administration#edit_location', as: :edit_location
- match '/conferences/:slug/register' => 'conferences#register', :as => :register, via: [:get, :post]
- get '/conferences/:slug/register/:step' => 'conferences#register', :as => :register_step
- get '/conferences/:slug/register/administration/:admin_step' => 'conferences#register', :as => :administration_step
- post '/conferences/:slug/register/administration/update/:admin_step' => 'conferences#admin_update', :as => :administration_update
- match '/conferences/:slug/broadcast' => 'conferences#broadcast', :as => :broadcast, via: [:get, :post]
- get '/conferences/:slug/stats' => 'conferences#stats', :as => :stats
- get '/conferences/:slug/register/:button/:confirmation_token' => 'conferences#register', :as => :register_paypal_confirm
-
- get '/conferences/:slug/schedule' => 'conferences#schedule', :as => :schedule
- get '/conferences/:slug/schedule/edit' => 'conferences#edit_schedule', :as => :edit_schedule
- post '/conferences/:slug/schedule/save' => 'conferences#save_schedule', :as => :save_schedule
-
- get '/conferences/:slug/schedule/location/add' => 'conferences#add_location', :as => :add_location
- post '/conferences/:slug/schedule/location/save' => 'conferences#save_location', :as => :save_location
- get '/conferences/:slug/schedule/location/:id' => 'conferences#view_location', :as => :view_location
- get '/conferences/:slug/schedule/location/:id/edit' => 'conferences#edit_location', :as => :edit_location
+ # Workshops
+ get '/conferences/:slug/workshops' => 'workshops#workshops', as: :workshops
+ match '/conferences/:slug/workshops/create' => 'workshops#create_workshop', as: :create_workshop, via: [:get, :post]
+ post '/conferences/:slug/workshops/save' => 'workshops#save_workshop', as: :save_workshop
+ get '/conferences/:slug/workshops/:workshop_id' => 'workshops#view_workshop', as: :view_workshop
+ post '/conferences/:slug/workshops/:workshop_id/toggle-interest' => 'workshops#toggle_workshop_interest', as: :toggle_workshop_interest
+ match '/conferences/:slug/workshops/:workshop_id/edit' => 'workshops#edit_workshop', as: :edit_workshop, via: [:get, :post]
+ match '/conferences/:slug/workshops/:workshop_id/translate/:locale' => 'workshops#translate_workshop', as: :translate_workshop, via: [:get, :post]
+ match '/conferences/:slug/workshops/:workshop_id/delete' => 'workshops#delete_workshop', as: :delete_workshop, via: [:get, :post]
+ post '/conferences/:slug/workshops/:workshop_id/comment' => 'workshops#add_comment', as: :workshop_comment
+ get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'workshops#facilitate_workshop', as: :facilitate_workshop
+ post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'workshops#facilitate_request', as: :facilitate_workshop_request
+ get '/conferences/:slug/workshops/:workshop_id/facilitate_request/:user_id/:approve_or_deny' => 'workshops#approve_facilitate_request', as: :approve_facilitate_workshop_request
+ get '/conferences/:slug/workshops/:workshop_id/facilitate/sent' => 'workshops#sent_facilitate_request', as: :sent_facilitate_workshop
+ post '/conferences/:slug/workshops/:workshop_id/add_facilitator' => 'workshops#add_workshop_facilitator', as: :workshop_add_facilitator
- get '/conferences/:slug/schedule/event/add' => 'conferences#add_event', :as => :add_event
- post '/conferences/:slug/schedule/event/save' => 'conferences#save_event', :as => :save_event
- get '/conferences/:slug/schedule/event/:id' => 'conferences#view_event', :as => :view_event
- get '/conferences/:slug/schedule/event/:id/edit' => 'conferences#edit_event', :as => :edit_event
+ # User pages
+ match '/user/logout' => 'application#user_logout', as: :logout, via: [:get, :post]
+ get '/user' => 'application#user_settings', as: :settings
+ post '/user/update' => 'application#update_user_settings', as: :update_settings
+ post '/user/find' => 'application#find_user', as: :find_user
- # get '/conferences/:slug/workshops' => 'conferences#workshops', :as => :workshops
- match '/conferences/:slug/workshops/create' => 'conferences#create_workshop', :as => :create_workshop, via: [:get, :post]
- post '/conferences/:slug/workshops/save' => 'conferences#save_workshop', :as => :save_workshop
- get '/conferences/:slug/workshops/:workshop_id' => 'conferences#view_workshop', :as => :view_workshop
- post '/conferences/:slug/workshops/:workshop_id/toggle-interest' => 'conferences#toggle_workshop_interest', :as => :toggle_workshop_interest
- match '/conferences/:slug/workshops/:workshop_id/edit' => 'conferences#edit_workshop', :as => :edit_workshop, via: [:get, :post]
- match '/conferences/:slug/workshops/:workshop_id/translate/:locale' => 'conferences#translate_workshop', :as => :translate_workshop, via: [:get, :post]
- match '/conferences/:slug/workshops/:workshop_id/delete' => 'conferences#delete_workshop', :as => :delete_workshop, via: [:get, :post]
- post '/conferences/:slug/workshops/:workshop_id/comment' => 'conferences#add_comment', :as => :workshop_comment
- get '/conferences/:slug/workshops/:workshop_id/facilitate' => 'conferences#facilitate_workshop', :as => :facilitate_workshop
- post '/conferences/:slug/workshops/:workshop_id/facilitate_request' => 'conferences#facilitate_request', :as => :facilitate_workshop_request
- get '/conferences/:slug/workshops/:workshop_id/facilitate_request/:user_id/:approve_or_deny' => 'conferences#approve_facilitate_request', :as => :approve_facilitate_workshop_request
- get '/conferences/:slug/workshops/:workshop_id/facilitate/sent' => 'conferences#sent_facilitate_request', :as => :sent_facilitate_workshop
- post '/conferences/:slug/workshops/:workshop_id/add_facilitator' => 'conferences#add_workshop_facilitator', :as => :workshop_add_facilitator
+ # OAuth enpoints
+ match '/oauth/callback' => 'oauths#callback', via: [:get, :post]
+ get '/oauth/update' => 'oauths#update', as: :oauth_update
+ post '/oauth/save' => 'oauths#save', as: :oauth_save
+ get '/oauth/:provider' => 'oauths#oauth', as: :auth_at_provider
- get '/robots.txt' => 'application#robots', :as => :robots_txt
- get '/humans.txt' => 'application#humans', :as => :humans_txt
+ # User confirmation pages
+ get '/confirm/:token' => 'application#confirm', as: :confirm
+ match '/doconfirm' => 'application#do_confirm', as: :do_confirm, via: [:get, :post]
- get '/confirm/:token' => 'application#confirm', :as => :confirm
- match '/doconfirm' => 'application#do_confirm', :as => :do_confirm, via: [:get, :post]
+ # Contact
+ get '/contact' => 'application#contact', as: :contact
+ post '/contact/send' => 'application#contact_send', as: :contact_send
+ get '/contact/sent' => 'application#contact_sent', as: :contact_sent
- match '/user/logout' => 'application#user_logout', :as => :logout, :via => [:get, :post]
- get '/contact' => 'application#contact', :as => :contact
- post '/contact/send' => 'application#contact_send', :as => :contact_send
- get '/contact/sent' => 'application#contact_sent', :as => :contact_sent
- get '/user' => 'application#user_settings', :as => :settings
- post '/user/update' => 'application#update_user_settings', :as => :update_settings
- match '/oauth/callback' => 'oauths#callback', :via => [:get, :post]
- get '/oauth/update' => 'oauths#update', :as => :oauth_update
- post '/oauth/save' => 'oauths#save', :as => :oauth_save
- get '/oauth/:provider' => 'oauths#oauth', :as => :auth_at_provider
+ # Static pages
+ get '/about' => 'application#about', as: :about
+ get '/policy' => 'application#policy', as: :policy
- post '/js_error' => 'application#js_error'
- get '/error_403' => 'application#do_403'
- get '/error_404' => 'application#error_404'
- get '/error_500' => 'application#error_500'
- get '/404' => 'application#error_404'
- get '/about' => 'application#about', :as => :about
- get '/policy' => 'application#policy', :as => :policy
- root 'application#home', :as => :home
+ # Site info
+ get '/robots.txt' => 'application#robots', as: :robots_txt
+ get '/humans.txt' => 'application#humans', as: :humans_txt
+
+ # Error pages
+ post '/js_error' => 'application#js_error'
+ get '/error_403' => 'application#do_403' unless Rails.env.production?
+ get '/error_404' => 'application#error_404' unless Rails.env.production?
+ get '/error_500' => 'application#error_500' unless Rails.env.production?
+
+ # Home page
+ root 'application#home', as: :home
end
diff --git a/db/migrate/20161008210246_add_type_to_conference.rb b/db/migrate/20161008210246_add_type_to_conference.rb
new file mode 100644
index 0000000..c939160
--- /dev/null
+++ b/db/migrate/20161008210246_add_type_to_conference.rb
@@ -0,0 +1,5 @@
+class AddTypeToConference < ActiveRecord::Migration
+ def change
+ add_column :conferences, :type, :string
+ end
+end
diff --git a/db/migrate/20161009004744_create_cities.rb b/db/migrate/20161009004744_create_cities.rb
new file mode 100644
index 0000000..6747061
--- /dev/null
+++ b/db/migrate/20161009004744_create_cities.rb
@@ -0,0 +1,14 @@
+class CreateCities < ActiveRecord::Migration
+ def change
+ create_table :cities do |t|
+ t.string :city
+ t.string :territory
+ t.string :country
+ t.float :latitude
+ t.float :longitude
+ t.string :locale
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20161009005122_create_city_matches.rb b/db/migrate/20161009005122_create_city_matches.rb
new file mode 100644
index 0000000..c3701b8
--- /dev/null
+++ b/db/migrate/20161009005122_create_city_matches.rb
@@ -0,0 +1,10 @@
+class CreateCityMatches < ActiveRecord::Migration
+ def change
+ create_table :city_matches do |t|
+ t.string :search
+ t.integer :city_id
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20161009194821_add_year_to_conference.rb b/db/migrate/20161009194821_add_year_to_conference.rb
new file mode 100644
index 0000000..7e56072
--- /dev/null
+++ b/db/migrate/20161009194821_add_year_to_conference.rb
@@ -0,0 +1,5 @@
+class AddYearToConference < ActiveRecord::Migration
+ def change
+ add_column :conferences, :year, :integer
+ end
+end
diff --git a/db/migrate/20161009200252_add_city_id_to_conference.rb b/db/migrate/20161009200252_add_city_id_to_conference.rb
new file mode 100644
index 0000000..2714b8d
--- /dev/null
+++ b/db/migrate/20161009200252_add_city_id_to_conference.rb
@@ -0,0 +1,5 @@
+class AddCityIdToConference < ActiveRecord::Migration
+ def change
+ add_column :conferences, :city_id, :integer
+ end
+end
diff --git a/db/migrate/20161129010755_add_is_public_to_conference.rb b/db/migrate/20161129010755_add_is_public_to_conference.rb
new file mode 100644
index 0000000..4c38f67
--- /dev/null
+++ b/db/migrate/20161129010755_add_is_public_to_conference.rb
@@ -0,0 +1,9 @@
+class AddIsPublicToConference < ActiveRecord::Migration
+ def change
+ add_column :conferences, :is_public, :boolean
+
+ Conference.all.each do |c|
+ c.update_attribute :is_public, true
+ end
+ end
+end
diff --git a/db/migrate/20161129014652_add_is_featured_to_conference.rb b/db/migrate/20161129014652_add_is_featured_to_conference.rb
new file mode 100644
index 0000000..a3b6d39
--- /dev/null
+++ b/db/migrate/20161129014652_add_is_featured_to_conference.rb
@@ -0,0 +1,9 @@
+class AddIsFeaturedToConference < ActiveRecord::Migration
+ def change
+ add_column :conferences, :is_featured, :boolean
+
+ Conference.all.each do |c|
+ c.update_attribute :is_featured, (c.slug == 'Detroit2016')
+ end
+ end
+end
diff --git a/db/migrate/20161129035601_rename_conference_type.rb b/db/migrate/20161129035601_rename_conference_type.rb
new file mode 100644
index 0000000..a6454f4
--- /dev/null
+++ b/db/migrate/20161129035601_rename_conference_type.rb
@@ -0,0 +1,9 @@
+class RenameConferenceType < ActiveRecord::Migration
+ def change
+ rename_column :conferences, :type, :conferencetype
+
+ Conference.all.each do |c|
+ c.update_attribute :conferencetype, (c.conference_type == 5 ? 'regional' : 'annual')
+ end
+ end
+end
diff --git a/db/migrate/20161130042227_rename_city_matches.rb b/db/migrate/20161130042227_rename_city_matches.rb
new file mode 100644
index 0000000..e664bd0
--- /dev/null
+++ b/db/migrate/20161130042227_rename_city_matches.rb
@@ -0,0 +1,39 @@
+require 'geocoder/calculations'
+
+class RenameCityMatches < ActiveRecord::Migration
+ def change
+ rename_table :city_matches, :city_cache
+
+ Conference.all.each do |c|
+ conference_location = c.location
+
+ if conference_location.present?
+ location = Geocoder.search("#{conference_location.city}, #{conference_location.territory}, #{conference_location.country}", language: 'en').first
+
+ component_alises = {
+ 'locality' => :city,
+ 'administrative_area_level_1' => :territory,
+ 'country' => :country
+ }
+ city_data = {
+ locale: :en,
+ latitude: location.data['geometry']['location']['lat'],
+ longitude: location.data['geometry']['location']['lng']
+ }
+ location.data['address_components'].each do | component |
+ property = component_alises[component['types'].first]
+ city_data[property] = component['short_name'] if property.present?
+ end
+
+ city = City.where(city: city_data[:city], territory: city_data[:territory], country: city_data[:country]).first
+
+ unless city.present?
+ city = City.new(city_data)
+ city.save!
+ end
+
+ c.update_attribute :city_id, city.id
+ end
+ end
+ end
+end
diff --git a/db/migrate/20161201011655_add_place_id_to_city.rb b/db/migrate/20161201011655_add_place_id_to_city.rb
new file mode 100644
index 0000000..2765e3a
--- /dev/null
+++ b/db/migrate/20161201011655_add_place_id_to_city.rb
@@ -0,0 +1,13 @@
+require 'geocoder/calculations'
+
+class AddPlaceIdToCity < ActiveRecord::Migration
+ def change
+ add_column :cities, :place_id, :string
+
+ City.all.each do |c|
+ location = Geocoder.search(c.address, language: 'en').first
+ c.place_id = location.data['place_id']
+ c.save!
+ end
+ end
+end
diff --git a/db/migrate/20161201015559_add_city_id_to_location.rb b/db/migrate/20161201015559_add_city_id_to_location.rb
new file mode 100644
index 0000000..75830cd
--- /dev/null
+++ b/db/migrate/20161201015559_add_city_id_to_location.rb
@@ -0,0 +1,11 @@
+class AddCityIdToLocation < ActiveRecord::Migration
+ def change
+ add_column :locations, :city_id, :integer
+
+ Location.all.each do |l|
+ city = City.search(([l.city, l.territory, l.country] - [nil, '']).join(', '))
+ l.city_id = city.id
+ l.save!
+ end
+ end
+end
diff --git a/db/migrate/20161201021349_add_spanish_city_translations.rb b/db/migrate/20161201021349_add_spanish_city_translations.rb
new file mode 100644
index 0000000..2aa475d
--- /dev/null
+++ b/db/migrate/20161201021349_add_spanish_city_translations.rb
@@ -0,0 +1,9 @@
+class AddSpanishCityTranslations < ActiveRecord::Migration
+ def change
+ City.all.each do |c|
+ city = c.get_translation(:es)
+ c.set_column_for_locale(:city, :es, city, 0) unless city.blank? || city == c.get_column_for_locale(:city, :es)
+ c.save!
+ end
+ end
+end
diff --git a/db/migrate/20161201043930_set_conference_types.rb b/db/migrate/20161201043930_set_conference_types.rb
new file mode 100644
index 0000000..784145b
--- /dev/null
+++ b/db/migrate/20161201043930_set_conference_types.rb
@@ -0,0 +1,20 @@
+class SetConferenceTypes < ActiveRecord::Migration
+ def change
+ types = {
+ 'Bike!Bike! 2013' => :annual,
+ 'Bike!Bike! Southeast 2014' => :se,
+ 'Bike!Bike! North 2014' => :n,
+ 'Ohio! Ohio!' => :ne,
+ 'Bike!Bike! 2014' => :annual,
+ 'Bike!Bike! 2015' => :annual,
+ 'Bike!Bike! 2016' => :annual
+ }
+ Conference.all.each do |c|
+ c.conferencetype = types[c.title] || :annual
+ c.year ||= c.end_date.year
+ c.slug = nil
+ c.make_slug
+ c.save!
+ end
+ end
+end
diff --git a/db/migrate/20161207021236_create_conference_administrators.rb b/db/migrate/20161207021236_create_conference_administrators.rb
new file mode 100644
index 0000000..19b7f46
--- /dev/null
+++ b/db/migrate/20161207021236_create_conference_administrators.rb
@@ -0,0 +1,10 @@
+class CreateConferenceAdministrators < ActiveRecord::Migration
+ def change
+ create_table :conference_administrators do |t|
+ t.integer :conference_id
+ t.integer :user_id
+
+ t.timestamps null: false
+ end
+ end
+end
diff --git a/db/migrate/20161210212817_change_conference_datetimes_to_dates.rb b/db/migrate/20161210212817_change_conference_datetimes_to_dates.rb
new file mode 100644
index 0000000..9972614
--- /dev/null
+++ b/db/migrate/20161210212817_change_conference_datetimes_to_dates.rb
@@ -0,0 +1,6 @@
+class ChangeConferenceDatetimesToDates < ActiveRecord::Migration
+ def change
+ change_column :conferences, :start_date, :date
+ change_column :conferences, :end_date, :date
+ end
+end
diff --git a/db/migrate/20161211065022_replace_missing_locales.rb b/db/migrate/20161211065022_replace_missing_locales.rb
new file mode 100644
index 0000000..f3a2c7a
--- /dev/null
+++ b/db/migrate/20161211065022_replace_missing_locales.rb
@@ -0,0 +1,19 @@
+class ReplaceMissingLocales < ActiveRecord::Migration
+ def change
+ Conference.where(locale: nil).each do |c|
+ c.update_attribute :locale, 'en'
+ end
+
+ Workshop.where(locale: nil).each do |c|
+ c.update_attribute :locale, 'en'
+ end
+
+ Event.where(locale: nil).each do |c|
+ c.update_attribute :locale, 'en'
+ end
+
+ City.where(locale: nil).each do |c|
+ c.update_attribute :locale, 'en'
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5c5b82e..554c7ae 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20161006021205) do
+ActiveRecord::Schema.define(version: 20161211065022) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -24,6 +24,25 @@ ActiveRecord::Schema.define(version: 20161006021205) do
t.datetime "updated_at"
end
+ create_table "cities", force: :cascade do |t|
+ t.string "city"
+ t.string "territory"
+ t.string "country"
+ t.float "latitude"
+ t.float "longitude"
+ t.string "locale"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ t.string "place_id"
+ end
+
+ create_table "city_cache", force: :cascade do |t|
+ t.string "search"
+ t.integer "city_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "comments", force: :cascade do |t|
t.string "model_type"
t.integer "model_id"
@@ -33,6 +52,13 @@ ActiveRecord::Schema.define(version: 20161006021205) do
t.integer "user_id"
end
+ create_table "conference_administrators", force: :cascade do |t|
+ t.integer "conference_id"
+ t.integer "user_id"
+ t.datetime "created_at", null: false
+ t.datetime "updated_at", null: false
+ end
+
create_table "conference_admins", force: :cascade do |t|
t.integer "conference_id"
t.integer "user_id"
@@ -107,8 +133,8 @@ ActiveRecord::Schema.define(version: 20161006021205) do
create_table "conferences", force: :cascade do |t|
t.string "title"
t.string "slug"
- t.datetime "start_date"
- t.datetime "end_date"
+ t.date "start_date"
+ t.date "end_date"
t.text "info"
t.string "poster"
t.string "cover"
@@ -139,6 +165,11 @@ ActiveRecord::Schema.define(version: 20161006021205) do
t.json "workshop_blocks"
t.text "payment_message"
t.json "payment_amounts"
+ t.string "conferencetype"
+ t.integer "year"
+ t.integer "city_id"
+ t.boolean "is_public"
+ t.boolean "is_featured"
end
create_table "delayed_jobs", force: :cascade do |t|
@@ -222,6 +253,7 @@ ActiveRecord::Schema.define(version: 20161006021205) do
t.string "city"
t.string "street"
t.string "postal_code"
+ t.integer "city_id"
end
add_index "locations", ["latitude", "longitude"], name: "index_locations_on_latitude_and_longitude", using: :btree
diff --git a/vendor/assets/javascripts/topojson.js b/vendor/assets/javascripts/topojson.js
new file mode 100644
index 0000000..7dffd43
--- /dev/null
+++ b/vendor/assets/javascripts/topojson.js
@@ -0,0 +1,5 @@
+!function(){function n(n){return n&&(n.ownerDocument||n.document||n).documentElement}function t(n){return n&&(n.ownerDocument&&n.ownerDocument.defaultView||n.document&&n||n.defaultView)}function e(n,t){return t>n?-1:n>t?1:n>=t?0:0/0}function r(n){return null===n?0/0:+n}function u(n){return!isNaN(n)}function i(n){return{left:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)<0?r=i+1:u=i}return r},right:function(t,e,r,u){for(arguments.length<3&&(r=0),arguments.length<4&&(u=t.length);u>r;){var i=r+u>>>1;n(t[i],e)>0?u=i:r=i+1}return r}}}function o(n){return n.length}function a(n){for(var t=1;n*t%1;)t*=10;return t}function c(n,t){for(var e in t)Object.defineProperty(n.prototype,e,{value:t[e],enumerable:!1})}function l(){this._=Object.create(null)}function s(n){return(n+="")===pa||n[0]===va?va+n:n}function f(n){return(n+="")[0]===va?n.slice(1):n}function h(n){return s(n)in this._}function g(n){return(n=s(n))in this._&&delete this._[n]}function p(){var n=[];for(var t in this._)n.push(f(t));return n}function v(){var n=0;for(var t in this._)++n;return n}function d(){for(var n in this._)return!1;return!0}function m(){this._=Object.create(null)}function y(n){return n}function M(n,t,e){return function(){var r=e.apply(t,arguments);return r===t?n:r}}function x(n,t){if(t in n)return t;t=t.charAt(0).toUpperCase()+t.slice(1);for(var e=0,r=da.length;r>e;++e){var u=da[e]+t;if(u in n)return u}}function b(){}function _(){}function w(n){function t(){for(var t,r=e,u=-1,i=r.length;++ue;e++)for(var u,i=n[e],o=0,a=i.length;a>o;o++)(u=i[o])&&t(u,o,e);return n}function Z(n){return ya(n,Sa),n}function V(n){var t,e;return function(r,u,i){var o,a=n[i].update,c=a.length;for(i!=e&&(e=i,t=0),u>=t&&(t=u+1);!(o=a[t])&&++t0&&(n=n.slice(0,a));var l=ka.get(n);return l&&(n=l,c=B),a?t?u:r:t?b:i}function $(n,t){return function(e){var r=ta.event;ta.event=e,t[0]=this.__data__;try{n.apply(this,t)}finally{ta.event=r}}}function B(n,t){var e=$(n,t);return function(n){var t=this,r=n.relatedTarget;r&&(r===t||8&r.compareDocumentPosition(t))||e.call(t,n)}}function W(e){var r=".dragsuppress-"+ ++Aa,u="click"+r,i=ta.select(t(e)).on("touchmove"+r,S).on("dragstart"+r,S).on("selectstart"+r,S);if(null==Ea&&(Ea="onselectstart"in e?!1:x(e.style,"userSelect")),Ea){var o=n(e).style,a=o[Ea];o[Ea]="none"}return function(n){if(i.on(r,null),Ea&&(o[Ea]=a),n){var t=function(){i.on(u,null)};i.on(u,function(){S(),t()},!0),setTimeout(t,0)}}}function J(n,e){e.changedTouches&&(e=e.changedTouches[0]);var r=n.ownerSVGElement||n;if(r.createSVGPoint){var u=r.createSVGPoint();if(0>Na){var i=t(n);if(i.scrollX||i.scrollY){r=ta.select("body").append("svg").style({position:"absolute",top:0,left:0,margin:0,padding:0,border:"none"},"important");var o=r[0][0].getScreenCTM();Na=!(o.f||o.e),r.remove()}}return Na?(u.x=e.pageX,u.y=e.pageY):(u.x=e.clientX,u.y=e.clientY),u=u.matrixTransform(n.getScreenCTM().inverse()),[u.x,u.y]}var a=n.getBoundingClientRect();return[e.clientX-a.left-n.clientLeft,e.clientY-a.top-n.clientTop]}function G(){return ta.event.changedTouches[0].identifier}function K(n){return n>0?1:0>n?-1:0}function Q(n,t,e){return(t[0]-n[0])*(e[1]-n[1])-(t[1]-n[1])*(e[0]-n[0])}function nt(n){return n>1?0:-1>n?qa:Math.acos(n)}function tt(n){return n>1?Ra:-1>n?-Ra:Math.asin(n)}function et(n){return((n=Math.exp(n))-1/n)/2}function rt(n){return((n=Math.exp(n))+1/n)/2}function ut(n){return((n=Math.exp(2*n))-1)/(n+1)}function it(n){return(n=Math.sin(n/2))*n}function ot(){}function at(n,t,e){return this instanceof at?(this.h=+n,this.s=+t,void(this.l=+e)):arguments.length<2?n instanceof at?new at(n.h,n.s,n.l):bt(""+n,_t,at):new at(n,t,e)}function ct(n,t,e){function r(n){return n>360?n-=360:0>n&&(n+=360),60>n?i+(o-i)*n/60:180>n?o:240>n?i+(o-i)*(240-n)/60:i}function u(n){return Math.round(255*r(n))}var i,o;return n=isNaN(n)?0:(n%=360)<0?n+360:n,t=isNaN(t)?0:0>t?0:t>1?1:t,e=0>e?0:e>1?1:e,o=.5>=e?e*(1+t):e+t-e*t,i=2*e-o,new mt(u(n+120),u(n),u(n-120))}function lt(n,t,e){return this instanceof lt?(this.h=+n,this.c=+t,void(this.l=+e)):arguments.length<2?n instanceof lt?new lt(n.h,n.c,n.l):n instanceof ft?gt(n.l,n.a,n.b):gt((n=wt((n=ta.rgb(n)).r,n.g,n.b)).l,n.a,n.b):new lt(n,t,e)}function st(n,t,e){return isNaN(n)&&(n=0),isNaN(t)&&(t=0),new ft(e,Math.cos(n*=Da)*t,Math.sin(n)*t)}function ft(n,t,e){return this instanceof ft?(this.l=+n,this.a=+t,void(this.b=+e)):arguments.length<2?n instanceof ft?new ft(n.l,n.a,n.b):n instanceof lt?st(n.h,n.c,n.l):wt((n=mt(n)).r,n.g,n.b):new ft(n,t,e)}function ht(n,t,e){var r=(n+16)/116,u=r+t/500,i=r-e/200;return u=pt(u)*Xa,r=pt(r)*$a,i=pt(i)*Ba,new mt(dt(3.2404542*u-1.5371385*r-.4985314*i),dt(-.969266*u+1.8760108*r+.041556*i),dt(.0556434*u-.2040259*r+1.0572252*i))}function gt(n,t,e){return n>0?new lt(Math.atan2(e,t)*Pa,Math.sqrt(t*t+e*e),n):new lt(0/0,0/0,n)}function pt(n){return n>.206893034?n*n*n:(n-4/29)/7.787037}function vt(n){return n>.008856?Math.pow(n,1/3):7.787037*n+4/29}function dt(n){return Math.round(255*(.00304>=n?12.92*n:1.055*Math.pow(n,1/2.4)-.055))}function mt(n,t,e){return this instanceof mt?(this.r=~~n,this.g=~~t,void(this.b=~~e)):arguments.length<2?n instanceof mt?new mt(n.r,n.g,n.b):bt(""+n,mt,ct):new mt(n,t,e)}function yt(n){return new mt(n>>16,n>>8&255,255&n)}function Mt(n){return yt(n)+""}function xt(n){return 16>n?"0"+Math.max(0,n).toString(16):Math.min(255,n).toString(16)}function bt(n,t,e){var r,u,i,o=0,a=0,c=0;if(r=/([a-z]+)\((.*)\)/i.exec(n))switch(u=r[2].split(","),r[1]){case"hsl":return e(parseFloat(u[0]),parseFloat(u[1])/100,parseFloat(u[2])/100);case"rgb":return t(kt(u[0]),kt(u[1]),kt(u[2]))}return(i=Ga.get(n.toLowerCase()))?t(i.r,i.g,i.b):(null==n||"#"!==n.charAt(0)||isNaN(i=parseInt(n.slice(1),16))||(4===n.length?(o=(3840&i)>>4,o=o>>4|o,a=240&i,a=a>>4|a,c=15&i,c=c<<4|c):7===n.length&&(o=(16711680&i)>>16,a=(65280&i)>>8,c=255&i)),t(o,a,c))}function _t(n,t,e){var r,u,i=Math.min(n/=255,t/=255,e/=255),o=Math.max(n,t,e),a=o-i,c=(o+i)/2;return a?(u=.5>c?a/(o+i):a/(2-o-i),r=n==o?(t-e)/a+(e>t?6:0):t==o?(e-n)/a+2:(n-t)/a+4,r*=60):(r=0/0,u=c>0&&1>c?0:r),new at(r,u,c)}function wt(n,t,e){n=St(n),t=St(t),e=St(e);var r=vt((.4124564*n+.3575761*t+.1804375*e)/Xa),u=vt((.2126729*n+.7151522*t+.072175*e)/$a),i=vt((.0193339*n+.119192*t+.9503041*e)/Ba);return ft(116*u-16,500*(r-u),200*(u-i))}function St(n){return(n/=255)<=.04045?n/12.92:Math.pow((n+.055)/1.055,2.4)}function kt(n){var t=parseFloat(n);return"%"===n.charAt(n.length-1)?Math.round(2.55*t):t}function Et(n){return"function"==typeof n?n:function(){return n}}function At(n){return function(t,e,r){return 2===arguments.length&&"function"==typeof e&&(r=e,e=null),Nt(t,e,n,r)}}function Nt(n,t,e,r){function u(){var n,t=c.status;if(!t&&zt(c)||t>=200&&300>t||304===t){try{n=e.call(i,c)}catch(r){return void o.error.call(i,r)}o.load.call(i,n)}else o.error.call(i,c)}var i={},o=ta.dispatch("beforesend","progress","load","error"),a={},c=new XMLHttpRequest,l=null;return!this.XDomainRequest||"withCredentials"in c||!/^(http(s)?:)?\/\//.test(n)||(c=new XDomainRequest),"onload"in c?c.onload=c.onerror=u:c.onreadystatechange=function(){c.readyState>3&&u()},c.onprogress=function(n){var t=ta.event;ta.event=n;try{o.progress.call(i,c)}finally{ta.event=t}},i.header=function(n,t){return n=(n+"").toLowerCase(),arguments.length<2?a[n]:(null==t?delete a[n]:a[n]=t+"",i)},i.mimeType=function(n){return arguments.length?(t=null==n?null:n+"",i):t},i.responseType=function(n){return arguments.length?(l=n,i):l},i.response=function(n){return e=n,i},["get","post"].forEach(function(n){i[n]=function(){return i.send.apply(i,[n].concat(ra(arguments)))}}),i.send=function(e,r,u){if(2===arguments.length&&"function"==typeof r&&(u=r,r=null),c.open(e,n,!0),null==t||"accept"in a||(a.accept=t+",*/*"),c.setRequestHeader)for(var s in a)c.setRequestHeader(s,a[s]);return null!=t&&c.overrideMimeType&&c.overrideMimeType(t),null!=l&&(c.responseType=l),null!=u&&i.on("error",u).on("load",function(n){u(null,n)}),o.beforesend.call(i,c),c.send(null==r?null:r),i},i.abort=function(){return c.abort(),i},ta.rebind(i,o,"on"),null==r?i:i.get(Ct(r))}function Ct(n){return 1===n.length?function(t,e){n(null==t?e:null)}:n}function zt(n){var t=n.responseType;return t&&"text"!==t?n.response:n.responseText}function qt(){var n=Lt(),t=Tt()-n;t>24?(isFinite(t)&&(clearTimeout(tc),tc=setTimeout(qt,t)),nc=0):(nc=1,rc(qt))}function Lt(){var n=Date.now();for(ec=Ka;ec;)n>=ec.t&&(ec.f=ec.c(n-ec.t)),ec=ec.n;return n}function Tt(){for(var n,t=Ka,e=1/0;t;)t.f?t=n?n.n=t.n:Ka=t.n:(t.t8?function(n){return n/e}:function(n){return n*e},symbol:n}}function Pt(n){var t=n.decimal,e=n.thousands,r=n.grouping,u=n.currency,i=r&&e?function(n,t){for(var u=n.length,i=[],o=0,a=r[0],c=0;u>0&&a>0&&(c+a+1>t&&(a=Math.max(1,t-c)),i.push(n.substring(u-=a,u+a)),!((c+=a+1)>t));)a=r[o=(o+1)%r.length];return i.reverse().join(e)}:y;return function(n){var e=ic.exec(n),r=e[1]||" ",o=e[2]||">",a=e[3]||"-",c=e[4]||"",l=e[5],s=+e[6],f=e[7],h=e[8],g=e[9],p=1,v="",d="",m=!1,y=!0;switch(h&&(h=+h.substring(1)),(l||"0"===r&&"="===o)&&(l=r="0",o="="),g){case"n":f=!0,g="g";break;case"%":p=100,d="%",g="f";break;case"p":p=100,d="%",g="r";break;case"b":case"o":case"x":case"X":"#"===c&&(v="0"+g.toLowerCase());case"c":y=!1;case"d":m=!0,h=0;break;case"s":p=-1,g="r"}"$"===c&&(v=u[0],d=u[1]),"r"!=g||h||(g="g"),null!=h&&("g"==g?h=Math.max(1,Math.min(21,h)):("e"==g||"f"==g)&&(h=Math.max(0,Math.min(20,h)))),g=oc.get(g)||Ut;var M=l&&f;return function(n){var e=d;if(m&&n%1)return"";var u=0>n||0===n&&0>1/n?(n=-n,"-"):"-"===a?"":a;if(0>p){var c=ta.formatPrefix(n,h);n=c.scale(n),e=c.symbol+d}else n*=p;n=g(n,h);var x,b,_=n.lastIndexOf(".");if(0>_){var w=y?n.lastIndexOf("e"):-1;0>w?(x=n,b=""):(x=n.substring(0,w),b=n.substring(w))}else x=n.substring(0,_),b=t+n.substring(_+1);!l&&f&&(x=i(x,1/0));var S=v.length+x.length+b.length+(M?0:u.length),k=s>S?new Array(S=s-S+1).join(r):"";return M&&(x=i(k+x,k.length?s-b.length:1/0)),u+=v,n=x+b,("<"===o?u+n+k:">"===o?k+u+n:"^"===o?k.substring(0,S>>=1)+u+n+k.substring(S):u+(M?n:k+n))+e}}}function Ut(n){return n+""}function jt(){this._=new Date(arguments.length>1?Date.UTC.apply(this,arguments):arguments[0])}function Ft(n,t,e){function r(t){var e=n(t),r=i(e,1);return r-t>t-e?e:r}function u(e){return t(e=n(new cc(e-1)),1),e}function i(n,e){return t(n=new cc(+n),e),n}function o(n,r,i){var o=u(n),a=[];if(i>1)for(;r>o;)e(o)%i||a.push(new Date(+o)),t(o,1);else for(;r>o;)a.push(new Date(+o)),t(o,1);return a}function a(n,t,e){try{cc=jt;var r=new jt;return r._=n,o(r,t,e)}finally{cc=Date}}n.floor=n,n.round=r,n.ceil=u,n.offset=i,n.range=o;var c=n.utc=Ht(n);return c.floor=c,c.round=Ht(r),c.ceil=Ht(u),c.offset=Ht(i),c.range=a,n}function Ht(n){return function(t,e){try{cc=jt;var r=new jt;return r._=t,n(r,e)._}finally{cc=Date}}}function Ot(n){function t(n){function t(t){for(var e,u,i,o=[],a=-1,c=0;++aa;){if(r>=l)return-1;if(u=t.charCodeAt(a++),37===u){if(o=t.charAt(a++),i=C[o in sc?t.charAt(a++):o],!i||(r=i(n,e,r))<0)return-1}else if(u!=e.charCodeAt(r++))return-1}return r}function r(n,t,e){_.lastIndex=0;var r=_.exec(t.slice(e));return r?(n.w=w.get(r[0].toLowerCase()),e+r[0].length):-1}function u(n,t,e){x.lastIndex=0;var r=x.exec(t.slice(e));return r?(n.w=b.get(r[0].toLowerCase()),e+r[0].length):-1}function i(n,t,e){E.lastIndex=0;var r=E.exec(t.slice(e));return r?(n.m=A.get(r[0].toLowerCase()),e+r[0].length):-1}function o(n,t,e){S.lastIndex=0;var r=S.exec(t.slice(e));return r?(n.m=k.get(r[0].toLowerCase()),e+r[0].length):-1}function a(n,t,r){return e(n,N.c.toString(),t,r)}function c(n,t,r){return e(n,N.x.toString(),t,r)}function l(n,t,r){return e(n,N.X.toString(),t,r)}function s(n,t,e){var r=M.get(t.slice(e,e+=2).toLowerCase());return null==r?-1:(n.p=r,e)}var f=n.dateTime,h=n.date,g=n.time,p=n.periods,v=n.days,d=n.shortDays,m=n.months,y=n.shortMonths;t.utc=function(n){function e(n){try{cc=jt;var t=new cc;return t._=n,r(t)}finally{cc=Date}}var r=t(n);return e.parse=function(n){try{cc=jt;var t=r.parse(n);return t&&t._}finally{cc=Date}},e.toString=r.toString,e},t.multi=t.utc.multi=ae;var M=ta.map(),x=Yt(v),b=Zt(v),_=Yt(d),w=Zt(d),S=Yt(m),k=Zt(m),E=Yt(y),A=Zt(y);p.forEach(function(n,t){M.set(n.toLowerCase(),t)});var N={a:function(n){return d[n.getDay()]},A:function(n){return v[n.getDay()]},b:function(n){return y[n.getMonth()]},B:function(n){return m[n.getMonth()]},c:t(f),d:function(n,t){return It(n.getDate(),t,2)},e:function(n,t){return It(n.getDate(),t,2)},H:function(n,t){return It(n.getHours(),t,2)},I:function(n,t){return It(n.getHours()%12||12,t,2)},j:function(n,t){return It(1+ac.dayOfYear(n),t,3)},L:function(n,t){return It(n.getMilliseconds(),t,3)},m:function(n,t){return It(n.getMonth()+1,t,2)},M:function(n,t){return It(n.getMinutes(),t,2)},p:function(n){return p[+(n.getHours()>=12)]},S:function(n,t){return It(n.getSeconds(),t,2)},U:function(n,t){return It(ac.sundayOfYear(n),t,2)},w:function(n){return n.getDay()},W:function(n,t){return It(ac.mondayOfYear(n),t,2)},x:t(h),X:t(g),y:function(n,t){return It(n.getFullYear()%100,t,2)},Y:function(n,t){return It(n.getFullYear()%1e4,t,4)},Z:ie,"%":function(){return"%"}},C={a:r,A:u,b:i,B:o,c:a,d:Qt,e:Qt,H:te,I:te,j:ne,L:ue,m:Kt,M:ee,p:s,S:re,U:Xt,w:Vt,W:$t,x:c,X:l,y:Wt,Y:Bt,Z:Jt,"%":oe};return t}function It(n,t,e){var r=0>n?"-":"",u=(r?-n:n)+"",i=u.length;return r+(e>i?new Array(e-i+1).join(t)+u:u)}function Yt(n){return new RegExp("^(?:"+n.map(ta.requote).join("|")+")","i")}function Zt(n){for(var t=new l,e=-1,r=n.length;++e68?1900:2e3)}function Kt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.m=r[0]-1,e+r[0].length):-1}function Qt(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.d=+r[0],e+r[0].length):-1}function ne(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.j=+r[0],e+r[0].length):-1}function te(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.H=+r[0],e+r[0].length):-1}function ee(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.M=+r[0],e+r[0].length):-1}function re(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+2));return r?(n.S=+r[0],e+r[0].length):-1}function ue(n,t,e){fc.lastIndex=0;var r=fc.exec(t.slice(e,e+3));return r?(n.L=+r[0],e+r[0].length):-1}function ie(n){var t=n.getTimezoneOffset(),e=t>0?"-":"+",r=ga(t)/60|0,u=ga(t)%60;return e+It(r,"0",2)+It(u,"0",2)}function oe(n,t,e){hc.lastIndex=0;var r=hc.exec(t.slice(e,e+1));return r?e+r[0].length:-1}function ae(n){for(var t=n.length,e=-1;++e=0?1:-1,a=o*e,c=Math.cos(t),l=Math.sin(t),s=i*l,f=u*c+s*Math.cos(a),h=s*o*Math.sin(a);yc.add(Math.atan2(h,f)),r=n,u=c,i=l}var t,e,r,u,i;Mc.point=function(o,a){Mc.point=n,r=(t=o)*Da,u=Math.cos(a=(e=a)*Da/2+qa/4),i=Math.sin(a)},Mc.lineEnd=function(){n(t,e)}}function pe(n){var t=n[0],e=n[1],r=Math.cos(e);return[r*Math.cos(t),r*Math.sin(t),Math.sin(e)]}function ve(n,t){return n[0]*t[0]+n[1]*t[1]+n[2]*t[2]}function de(n,t){return[n[1]*t[2]-n[2]*t[1],n[2]*t[0]-n[0]*t[2],n[0]*t[1]-n[1]*t[0]]}function me(n,t){n[0]+=t[0],n[1]+=t[1],n[2]+=t[2]}function ye(n,t){return[n[0]*t,n[1]*t,n[2]*t]}function Me(n){var t=Math.sqrt(n[0]*n[0]+n[1]*n[1]+n[2]*n[2]);n[0]/=t,n[1]/=t,n[2]/=t}function xe(n){return[Math.atan2(n[1],n[0]),tt(n[2])]}function be(n,t){return ga(n[0]-t[0])a;++a)u.point((e=n[a])[0],e[1]);return void u.lineEnd()}var c=new qe(e,n,null,!0),l=new qe(e,null,c,!1);c.o=l,i.push(c),o.push(l),c=new qe(r,n,null,!1),l=new qe(r,null,c,!0),c.o=l,i.push(c),o.push(l)}}),o.sort(t),ze(i),ze(o),i.length){for(var a=0,c=e,l=o.length;l>a;++a)o[a].e=c=!c;for(var s,f,h=i[0];;){for(var g=h,p=!0;g.v;)if((g=g.n)===h)return;s=g.z,u.lineStart();do{if(g.v=g.o.v=!0,g.e){if(p)for(var a=0,l=s.length;l>a;++a)u.point((f=s[a])[0],f[1]);else r(g.x,g.n.x,1,u);g=g.n}else{if(p){s=g.p.z;for(var a=s.length-1;a>=0;--a)u.point((f=s[a])[0],f[1])}else r(g.x,g.p.x,-1,u);g=g.p}g=g.o,s=g.z,p=!p}while(!g.v);u.lineEnd()}}}function ze(n){if(t=n.length){for(var t,e,r=0,u=n[0];++r0){for(b||(i.polygonStart(),b=!0),i.lineStart();++o1&&2&t&&e.push(e.pop().concat(e.shift())),g.push(e.filter(Te))}var g,p,v,d=t(i),m=u.invert(r[0],r[1]),y={point:o,lineStart:c,lineEnd:l,polygonStart:function(){y.point=s,y.lineStart=f,y.lineEnd=h,g=[],p=[]},polygonEnd:function(){y.point=o,y.lineStart=c,y.lineEnd=l,g=ta.merge(g);var n=Fe(m,p);g.length?(b||(i.polygonStart(),b=!0),Ce(g,De,n,e,i)):n&&(b||(i.polygonStart(),b=!0),i.lineStart(),e(null,null,1,i),i.lineEnd()),b&&(i.polygonEnd(),b=!1),g=p=null},sphere:function(){i.polygonStart(),i.lineStart(),e(null,null,1,i),i.lineEnd(),i.polygonEnd()}},M=Re(),x=t(M),b=!1;return y}}function Te(n){return n.length>1}function Re(){var n,t=[];return{lineStart:function(){t.push(n=[])},point:function(t,e){n.push([t,e])},lineEnd:b,buffer:function(){var e=t;return t=[],n=null,e},rejoin:function(){t.length>1&&t.push(t.pop().concat(t.shift()))}}}function De(n,t){return((n=n.x)[0]<0?n[1]-Ra-Ca:Ra-n[1])-((t=t.x)[0]<0?t[1]-Ra-Ca:Ra-t[1])}function Pe(n){var t,e=0/0,r=0/0,u=0/0;return{lineStart:function(){n.lineStart(),t=1},point:function(i,o){var a=i>0?qa:-qa,c=ga(i-e);ga(c-qa)0?Ra:-Ra),n.point(u,r),n.lineEnd(),n.lineStart(),n.point(a,r),n.point(i,r),t=0):u!==a&&c>=qa&&(ga(e-u)Ca?Math.atan((Math.sin(t)*(i=Math.cos(r))*Math.sin(e)-Math.sin(r)*(u=Math.cos(t))*Math.sin(n))/(u*i*o)):(t+r)/2}function je(n,t,e,r){var u;if(null==n)u=e*Ra,r.point(-qa,u),r.point(0,u),r.point(qa,u),r.point(qa,0),r.point(qa,-u),r.point(0,-u),r.point(-qa,-u),r.point(-qa,0),r.point(-qa,u);else if(ga(n[0]-t[0])>Ca){var i=n[0]a;++a){var l=t[a],s=l.length;if(s)for(var f=l[0],h=f[0],g=f[1]/2+qa/4,p=Math.sin(g),v=Math.cos(g),d=1;;){d===s&&(d=0),n=l[d];var m=n[0],y=n[1]/2+qa/4,M=Math.sin(y),x=Math.cos(y),b=m-h,_=b>=0?1:-1,w=_*b,S=w>qa,k=p*M;if(yc.add(Math.atan2(k*_*Math.sin(w),v*x+k*Math.cos(w))),i+=S?b+_*La:b,S^h>=e^m>=e){var E=de(pe(f),pe(n));Me(E);var A=de(u,E);Me(A);var N=(S^b>=0?-1:1)*tt(A[2]);(r>N||r===N&&(E[0]||E[1]))&&(o+=S^b>=0?1:-1)}if(!d++)break;h=m,p=M,v=x,f=n}}return(-Ca>i||Ca>i&&0>yc)^1&o}function He(n){function t(n,t){return Math.cos(n)*Math.cos(t)>i}function e(n){var e,i,c,l,s;return{lineStart:function(){l=c=!1,s=1},point:function(f,h){var g,p=[f,h],v=t(f,h),d=o?v?0:u(f,h):v?u(f+(0>f?qa:-qa),h):0;if(!e&&(l=c=v)&&n.lineStart(),v!==c&&(g=r(e,p),(be(e,g)||be(p,g))&&(p[0]+=Ca,p[1]+=Ca,v=t(p[0],p[1]))),v!==c)s=0,v?(n.lineStart(),g=r(p,e),n.point(g[0],g[1])):(g=r(e,p),n.point(g[0],g[1]),n.lineEnd()),e=g;else if(a&&e&&o^v){var m;d&i||!(m=r(p,e,!0))||(s=0,o?(n.lineStart(),n.point(m[0][0],m[0][1]),n.point(m[1][0],m[1][1]),n.lineEnd()):(n.point(m[1][0],m[1][1]),n.lineEnd(),n.lineStart(),n.point(m[0][0],m[0][1])))}!v||e&&be(e,p)||n.point(p[0],p[1]),e=p,c=v,i=d},lineEnd:function(){c&&n.lineEnd(),e=null},clean:function(){return s|(l&&c)<<1}}}function r(n,t,e){var r=pe(n),u=pe(t),o=[1,0,0],a=de(r,u),c=ve(a,a),l=a[0],s=c-l*l;if(!s)return!e&&n;var f=i*c/s,h=-i*l/s,g=de(o,a),p=ye(o,f),v=ye(a,h);me(p,v);var d=g,m=ve(p,d),y=ve(d,d),M=m*m-y*(ve(p,p)-1);if(!(0>M)){var x=Math.sqrt(M),b=ye(d,(-m-x)/y);if(me(b,p),b=xe(b),!e)return b;var _,w=n[0],S=t[0],k=n[1],E=t[1];w>S&&(_=w,w=S,S=_);var A=S-w,N=ga(A-qa)A;if(!N&&k>E&&(_=k,k=E,E=_),C?N?k+E>0^b[1]<(ga(b[0]-w)qa^(w<=b[0]&&b[0]<=S)){var z=ye(d,(-m+x)/y);return me(z,p),[b,xe(z)]}}}function u(t,e){var r=o?n:qa-n,u=0;return-r>t?u|=1:t>r&&(u|=2),-r>e?u|=4:e>r&&(u|=8),u}var i=Math.cos(n),o=i>0,a=ga(i)>Ca,c=gr(n,6*Da);return Le(t,e,c,o?[0,-n]:[-qa,n-qa])}function Oe(n,t,e,r){return function(u){var i,o=u.a,a=u.b,c=o.x,l=o.y,s=a.x,f=a.y,h=0,g=1,p=s-c,v=f-l;if(i=n-c,p||!(i>0)){if(i/=p,0>p){if(h>i)return;g>i&&(g=i)}else if(p>0){if(i>g)return;i>h&&(h=i)}if(i=e-c,p||!(0>i)){if(i/=p,0>p){if(i>g)return;i>h&&(h=i)}else if(p>0){if(h>i)return;g>i&&(g=i)}if(i=t-l,v||!(i>0)){if(i/=v,0>v){if(h>i)return;g>i&&(g=i)}else if(v>0){if(i>g)return;i>h&&(h=i)}if(i=r-l,v||!(0>i)){if(i/=v,0>v){if(i>g)return;i>h&&(h=i)}else if(v>0){if(h>i)return;g>i&&(g=i)}return h>0&&(u.a={x:c+h*p,y:l+h*v}),1>g&&(u.b={x:c+g*p,y:l+g*v}),u}}}}}}function Ie(n,t,e,r){function u(r,u){return ga(r[0]-n)0?0:3:ga(r[0]-e)0?2:1:ga(r[1]-t)0?1:0:u>0?3:2}function i(n,t){return o(n.x,t.x)}function o(n,t){var e=u(n,1),r=u(t,1);return e!==r?e-r:0===e?t[1]-n[1]:1===e?n[0]-t[0]:2===e?n[1]-t[1]:t[0]-n[0]}return function(a){function c(n){for(var t=0,e=d.length,r=n[1],u=0;e>u;++u)for(var i,o=1,a=d[u],c=a.length,l=a[0];c>o;++o)i=a[o],l[1]<=r?i[1]>r&&Q(l,i,n)>0&&++t:i[1]<=r&&Q(l,i,n)<0&&--t,l=i;return 0!==t}function l(i,a,c,l){var s=0,f=0;if(null==i||(s=u(i,c))!==(f=u(a,c))||o(i,a)<0^c>0){do l.point(0===s||3===s?n:e,s>1?r:t);while((s=(s+c+4)%4)!==f)}else l.point(a[0],a[1])}function s(u,i){return u>=n&&e>=u&&i>=t&&r>=i}function f(n,t){s(n,t)&&a.point(n,t)}function h(){C.point=p,d&&d.push(m=[]),S=!0,w=!1,b=_=0/0}function g(){v&&(p(y,M),x&&w&&A.rejoin(),v.push(A.buffer())),C.point=f,w&&a.lineEnd()}function p(n,t){n=Math.max(-Tc,Math.min(Tc,n)),t=Math.max(-Tc,Math.min(Tc,t));var e=s(n,t);if(d&&m.push([n,t]),S)y=n,M=t,x=e,S=!1,e&&(a.lineStart(),a.point(n,t));else if(e&&w)a.point(n,t);else{var r={a:{x:b,y:_},b:{x:n,y:t}};N(r)?(w||(a.lineStart(),a.point(r.a.x,r.a.y)),a.point(r.b.x,r.b.y),e||a.lineEnd(),k=!1):e&&(a.lineStart(),a.point(n,t),k=!1)}b=n,_=t,w=e}var v,d,m,y,M,x,b,_,w,S,k,E=a,A=Re(),N=Oe(n,t,e,r),C={point:f,lineStart:h,lineEnd:g,polygonStart:function(){a=A,v=[],d=[],k=!0},polygonEnd:function(){a=E,v=ta.merge(v);var t=c([n,r]),e=k&&t,u=v.length;(e||u)&&(a.polygonStart(),e&&(a.lineStart(),l(null,null,1,a),a.lineEnd()),u&&Ce(v,i,t,l,a),a.polygonEnd()),v=d=m=null}};return C}}function Ye(n){var t=0,e=qa/3,r=ir(n),u=r(t,e);return u.parallels=function(n){return arguments.length?r(t=n[0]*qa/180,e=n[1]*qa/180):[t/qa*180,e/qa*180]},u}function Ze(n,t){function e(n,t){var e=Math.sqrt(i-2*u*Math.sin(t))/u;return[e*Math.sin(n*=u),o-e*Math.cos(n)]}var r=Math.sin(n),u=(r+Math.sin(t))/2,i=1+r*(2*u-r),o=Math.sqrt(i)/u;return e.invert=function(n,t){var e=o-t;return[Math.atan2(n,e)/u,tt((i-(n*n+e*e)*u*u)/(2*u))]},e}function Ve(){function n(n,t){Dc+=u*n-r*t,r=n,u=t}var t,e,r,u;Hc.point=function(i,o){Hc.point=n,t=r=i,e=u=o},Hc.lineEnd=function(){n(t,e)}}function Xe(n,t){Pc>n&&(Pc=n),n>jc&&(jc=n),Uc>t&&(Uc=t),t>Fc&&(Fc=t)}function $e(){function n(n,t){o.push("M",n,",",t,i)}function t(n,t){o.push("M",n,",",t),a.point=e}function e(n,t){o.push("L",n,",",t)}function r(){a.point=n}function u(){o.push("Z")}var i=Be(4.5),o=[],a={point:n,lineStart:function(){a.point=t},lineEnd:r,polygonStart:function(){a.lineEnd=u},polygonEnd:function(){a.lineEnd=r,a.point=n},pointRadius:function(n){return i=Be(n),a},result:function(){if(o.length){var n=o.join("");return o=[],n}}};return a}function Be(n){return"m0,"+n+"a"+n+","+n+" 0 1,1 0,"+-2*n+"a"+n+","+n+" 0 1,1 0,"+2*n+"z"}function We(n,t){_c+=n,wc+=t,++Sc}function Je(){function n(n,r){var u=n-t,i=r-e,o=Math.sqrt(u*u+i*i);kc+=o*(t+n)/2,Ec+=o*(e+r)/2,Ac+=o,We(t=n,e=r)}var t,e;Ic.point=function(r,u){Ic.point=n,We(t=r,e=u)}}function Ge(){Ic.point=We}function Ke(){function n(n,t){var e=n-r,i=t-u,o=Math.sqrt(e*e+i*i);kc+=o*(r+n)/2,Ec+=o*(u+t)/2,Ac+=o,o=u*n-r*t,Nc+=o*(r+n),Cc+=o*(u+t),zc+=3*o,We(r=n,u=t)}var t,e,r,u;Ic.point=function(i,o){Ic.point=n,We(t=r=i,e=u=o)},Ic.lineEnd=function(){n(t,e)}}function Qe(n){function t(t,e){n.moveTo(t+o,e),n.arc(t,e,o,0,La)}function e(t,e){n.moveTo(t,e),a.point=r}function r(t,e){n.lineTo(t,e)}function u(){a.point=t}function i(){n.closePath()}var o=4.5,a={point:t,lineStart:function(){a.point=e},lineEnd:u,polygonStart:function(){a.lineEnd=i},polygonEnd:function(){a.lineEnd=u,a.point=t},pointRadius:function(n){return o=n,a},result:b};return a}function nr(n){function t(n){return(a?r:e)(n)}function e(t){return rr(t,function(e,r){e=n(e,r),t.point(e[0],e[1])})}function r(t){function e(e,r){e=n(e,r),t.point(e[0],e[1])}function r(){M=0/0,S.point=i,t.lineStart()}function i(e,r){var i=pe([e,r]),o=n(e,r);u(M,x,y,b,_,w,M=o[0],x=o[1],y=e,b=i[0],_=i[1],w=i[2],a,t),t.point(M,x)}function o(){S.point=e,t.lineEnd()}function c(){r(),S.point=l,S.lineEnd=s}function l(n,t){i(f=n,h=t),g=M,p=x,v=b,d=_,m=w,S.point=i}function s(){u(M,x,y,b,_,w,g,p,f,v,d,m,a,t),S.lineEnd=o,o()}var f,h,g,p,v,d,m,y,M,x,b,_,w,S={point:e,lineStart:r,lineEnd:o,polygonStart:function(){t.polygonStart(),S.lineStart=c
+},polygonEnd:function(){t.polygonEnd(),S.lineStart=r}};return S}function u(t,e,r,a,c,l,s,f,h,g,p,v,d,m){var y=s-t,M=f-e,x=y*y+M*M;if(x>4*i&&d--){var b=a+g,_=c+p,w=l+v,S=Math.sqrt(b*b+_*_+w*w),k=Math.asin(w/=S),E=ga(ga(w)-1)i||ga((y*z+M*q)/x-.5)>.3||o>a*g+c*p+l*v)&&(u(t,e,r,a,c,l,N,C,E,b/=S,_/=S,w,d,m),m.point(N,C),u(N,C,E,b,_,w,s,f,h,g,p,v,d,m))}}var i=.5,o=Math.cos(30*Da),a=16;return t.precision=function(n){return arguments.length?(a=(i=n*n)>0&&16,t):Math.sqrt(i)},t}function tr(n){var t=nr(function(t,e){return n([t*Pa,e*Pa])});return function(n){return or(t(n))}}function er(n){this.stream=n}function rr(n,t){return{point:t,sphere:function(){n.sphere()},lineStart:function(){n.lineStart()},lineEnd:function(){n.lineEnd()},polygonStart:function(){n.polygonStart()},polygonEnd:function(){n.polygonEnd()}}}function ur(n){return ir(function(){return n})()}function ir(n){function t(n){return n=a(n[0]*Da,n[1]*Da),[n[0]*h+c,l-n[1]*h]}function e(n){return n=a.invert((n[0]-c)/h,(l-n[1])/h),n&&[n[0]*Pa,n[1]*Pa]}function r(){a=Ae(o=lr(m,M,x),i);var n=i(v,d);return c=g-n[0]*h,l=p+n[1]*h,u()}function u(){return s&&(s.valid=!1,s=null),t}var i,o,a,c,l,s,f=nr(function(n,t){return n=i(n,t),[n[0]*h+c,l-n[1]*h]}),h=150,g=480,p=250,v=0,d=0,m=0,M=0,x=0,b=Lc,_=y,w=null,S=null;return t.stream=function(n){return s&&(s.valid=!1),s=or(b(o,f(_(n)))),s.valid=!0,s},t.clipAngle=function(n){return arguments.length?(b=null==n?(w=n,Lc):He((w=+n)*Da),u()):w},t.clipExtent=function(n){return arguments.length?(S=n,_=n?Ie(n[0][0],n[0][1],n[1][0],n[1][1]):y,u()):S},t.scale=function(n){return arguments.length?(h=+n,r()):h},t.translate=function(n){return arguments.length?(g=+n[0],p=+n[1],r()):[g,p]},t.center=function(n){return arguments.length?(v=n[0]%360*Da,d=n[1]%360*Da,r()):[v*Pa,d*Pa]},t.rotate=function(n){return arguments.length?(m=n[0]%360*Da,M=n[1]%360*Da,x=n.length>2?n[2]%360*Da:0,r()):[m*Pa,M*Pa,x*Pa]},ta.rebind(t,f,"precision"),function(){return i=n.apply(this,arguments),t.invert=i.invert&&e,r()}}function or(n){return rr(n,function(t,e){n.point(t*Da,e*Da)})}function ar(n,t){return[n,t]}function cr(n,t){return[n>qa?n-La:-qa>n?n+La:n,t]}function lr(n,t,e){return n?t||e?Ae(fr(n),hr(t,e)):fr(n):t||e?hr(t,e):cr}function sr(n){return function(t,e){return t+=n,[t>qa?t-La:-qa>t?t+La:t,e]}}function fr(n){var t=sr(n);return t.invert=sr(-n),t}function hr(n,t){function e(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*r+a*u;return[Math.atan2(c*i-s*o,a*r-l*u),tt(s*i+c*o)]}var r=Math.cos(n),u=Math.sin(n),i=Math.cos(t),o=Math.sin(t);return e.invert=function(n,t){var e=Math.cos(t),a=Math.cos(n)*e,c=Math.sin(n)*e,l=Math.sin(t),s=l*i-c*o;return[Math.atan2(c*i+l*o,a*r+s*u),tt(s*r-a*u)]},e}function gr(n,t){var e=Math.cos(n),r=Math.sin(n);return function(u,i,o,a){var c=o*t;null!=u?(u=pr(e,u),i=pr(e,i),(o>0?i>u:u>i)&&(u+=o*La)):(u=n+o*La,i=n-.5*c);for(var l,s=u;o>0?s>i:i>s;s-=c)a.point((l=xe([e,-r*Math.cos(s),-r*Math.sin(s)]))[0],l[1])}}function pr(n,t){var e=pe(t);e[0]-=n,Me(e);var r=nt(-e[1]);return((-e[2]<0?-r:r)+2*Math.PI-Ca)%(2*Math.PI)}function vr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[n,t]})}}function dr(n,t,e){var r=ta.range(n,t-Ca,e).concat(t);return function(n){return r.map(function(t){return[t,n]})}}function mr(n){return n.source}function yr(n){return n.target}function Mr(n,t,e,r){var u=Math.cos(t),i=Math.sin(t),o=Math.cos(r),a=Math.sin(r),c=u*Math.cos(n),l=u*Math.sin(n),s=o*Math.cos(e),f=o*Math.sin(e),h=2*Math.asin(Math.sqrt(it(r-t)+u*o*it(e-n))),g=1/Math.sin(h),p=h?function(n){var t=Math.sin(n*=h)*g,e=Math.sin(h-n)*g,r=e*c+t*s,u=e*l+t*f,o=e*i+t*a;return[Math.atan2(u,r)*Pa,Math.atan2(o,Math.sqrt(r*r+u*u))*Pa]}:function(){return[n*Pa,t*Pa]};return p.distance=h,p}function xr(){function n(n,u){var i=Math.sin(u*=Da),o=Math.cos(u),a=ga((n*=Da)-t),c=Math.cos(a);Yc+=Math.atan2(Math.sqrt((a=o*Math.sin(a))*a+(a=r*i-e*o*c)*a),e*i+r*o*c),t=n,e=i,r=o}var t,e,r;Zc.point=function(u,i){t=u*Da,e=Math.sin(i*=Da),r=Math.cos(i),Zc.point=n},Zc.lineEnd=function(){Zc.point=Zc.lineEnd=b}}function br(n,t){function e(t,e){var r=Math.cos(t),u=Math.cos(e),i=n(r*u);return[i*u*Math.sin(t),i*Math.sin(e)]}return e.invert=function(n,e){var r=Math.sqrt(n*n+e*e),u=t(r),i=Math.sin(u),o=Math.cos(u);return[Math.atan2(n*i,r*o),Math.asin(r&&e*i/r)]},e}function _r(n,t){function e(n,t){o>0?-Ra+Ca>t&&(t=-Ra+Ca):t>Ra-Ca&&(t=Ra-Ca);var e=o/Math.pow(u(t),i);return[e*Math.sin(i*n),o-e*Math.cos(i*n)]}var r=Math.cos(n),u=function(n){return Math.tan(qa/4+n/2)},i=n===t?Math.sin(n):Math.log(r/Math.cos(t))/Math.log(u(t)/u(n)),o=r*Math.pow(u(n),i)/i;return i?(e.invert=function(n,t){var e=o-t,r=K(i)*Math.sqrt(n*n+e*e);return[Math.atan2(n,e)/i,2*Math.atan(Math.pow(o/r,1/i))-Ra]},e):Sr}function wr(n,t){function e(n,t){var e=i-t;return[e*Math.sin(u*n),i-e*Math.cos(u*n)]}var r=Math.cos(n),u=n===t?Math.sin(n):(r-Math.cos(t))/(t-n),i=r/u+n;return ga(u)u;u++){for(;r>1&&Q(n[e[r-2]],n[e[r-1]],n[u])<=0;)--r;e[r++]=u}return e.slice(0,r)}function zr(n,t){return n[0]-t[0]||n[1]-t[1]}function qr(n,t,e){return(e[0]-t[0])*(n[1]-t[1])<(e[1]-t[1])*(n[0]-t[0])}function Lr(n,t,e,r){var u=n[0],i=e[0],o=t[0]-u,a=r[0]-i,c=n[1],l=e[1],s=t[1]-c,f=r[1]-l,h=(a*(c-l)-f*(u-i))/(f*o-a*s);return[u+h*o,c+h*s]}function Tr(n){var t=n[0],e=n[n.length-1];return!(t[0]-e[0]||t[1]-e[1])}function Rr(){tu(this),this.edge=this.site=this.circle=null}function Dr(n){var t=el.pop()||new Rr;return t.site=n,t}function Pr(n){Xr(n),Qc.remove(n),el.push(n),tu(n)}function Ur(n){var t=n.circle,e=t.x,r=t.cy,u={x:e,y:r},i=n.P,o=n.N,a=[n];Pr(n);for(var c=i;c.circle&&ga(e-c.circle.x)s;++s)l=a[s],c=a[s-1],Kr(l.edge,c.site,l.site,u);c=a[0],l=a[f-1],l.edge=Jr(c.site,l.site,null,u),Vr(c),Vr(l)}function jr(n){for(var t,e,r,u,i=n.x,o=n.y,a=Qc._;a;)if(r=Fr(a,o)-i,r>Ca)a=a.L;else{if(u=i-Hr(a,o),!(u>Ca)){r>-Ca?(t=a.P,e=a):u>-Ca?(t=a,e=a.N):t=e=a;break}if(!a.R){t=a;break}a=a.R}var c=Dr(n);if(Qc.insert(t,c),t||e){if(t===e)return Xr(t),e=Dr(t.site),Qc.insert(c,e),c.edge=e.edge=Jr(t.site,c.site),Vr(t),void Vr(e);if(!e)return void(c.edge=Jr(t.site,c.site));Xr(t),Xr(e);var l=t.site,s=l.x,f=l.y,h=n.x-s,g=n.y-f,p=e.site,v=p.x-s,d=p.y-f,m=2*(h*d-g*v),y=h*h+g*g,M=v*v+d*d,x={x:(d*y-g*M)/m+s,y:(h*M-v*y)/m+f};Kr(e.edge,l,p,x),c.edge=Jr(l,n,null,x),e.edge=Jr(n,p,null,x),Vr(t),Vr(e)}}function Fr(n,t){var e=n.site,r=e.x,u=e.y,i=u-t;if(!i)return r;var o=n.P;if(!o)return-1/0;e=o.site;var a=e.x,c=e.y,l=c-t;if(!l)return a;var s=a-r,f=1/i-1/l,h=s/l;return f?(-h+Math.sqrt(h*h-2*f*(s*s/(-2*l)-c+l/2+u-i/2)))/f+r:(r+a)/2}function Hr(n,t){var e=n.N;if(e)return Fr(e,t);var r=n.site;return r.y===t?r.x:1/0}function Or(n){this.site=n,this.edges=[]}function Ir(n){for(var t,e,r,u,i,o,a,c,l,s,f=n[0][0],h=n[1][0],g=n[0][1],p=n[1][1],v=Kc,d=v.length;d--;)if(i=v[d],i&&i.prepare())for(a=i.edges,c=a.length,o=0;c>o;)s=a[o].end(),r=s.x,u=s.y,l=a[++o%c].start(),t=l.x,e=l.y,(ga(r-t)>Ca||ga(u-e)>Ca)&&(a.splice(o,0,new Qr(Gr(i.site,s,ga(r-f)Ca?{x:f,y:ga(t-f)Ca?{x:ga(e-p)Ca?{x:h,y:ga(t-h)Ca?{x:ga(e-g)=-za)){var g=c*c+l*l,p=s*s+f*f,v=(f*g-l*p)/h,d=(c*p-s*g)/h,f=d+a,m=rl.pop()||new Zr;m.arc=n,m.site=u,m.x=v+o,m.y=f+Math.sqrt(v*v+d*d),m.cy=f,n.circle=m;for(var y=null,M=tl._;M;)if(m.yd||d>=a)return;if(h>p){if(i){if(i.y>=l)return}else i={x:d,y:c};e={x:d,y:l}}else{if(i){if(i.yr||r>1)if(h>p){if(i){if(i.y>=l)return}else i={x:(c-u)/r,y:c};e={x:(l-u)/r,y:l}}else{if(i){if(i.yg){if(i){if(i.x>=a)return}else i={x:o,y:r*o+u};e={x:a,y:r*a+u}}else{if(i){if(i.xi||f>o||r>h||u>g)){if(p=n.point){var p,v=t-n.x,d=e-n.y,m=v*v+d*d;if(c>m){var y=Math.sqrt(c=m);r=t-y,u=e-y,i=t+y,o=e+y,a=p}}for(var M=n.nodes,x=.5*(s+h),b=.5*(f+g),_=t>=x,w=e>=b,S=w<<1|_,k=S+4;k>S;++S)if(n=M[3&S])switch(3&S){case 0:l(n,s,f,x,b);break;case 1:l(n,x,f,h,b);break;case 2:l(n,s,b,x,g);break;case 3:l(n,x,b,h,g)}}}(n,r,u,i,o),a}function gu(n,t){n=ta.rgb(n),t=ta.rgb(t);var e=n.r,r=n.g,u=n.b,i=t.r-e,o=t.g-r,a=t.b-u;return function(n){return"#"+xt(Math.round(e+i*n))+xt(Math.round(r+o*n))+xt(Math.round(u+a*n))}}function pu(n,t){var e,r={},u={};for(e in n)e in t?r[e]=mu(n[e],t[e]):u[e]=n[e];for(e in t)e in n||(u[e]=t[e]);return function(n){for(e in r)u[e]=r[e](n);return u}}function vu(n,t){return n=+n,t=+t,function(e){return n*(1-e)+t*e}}function du(n,t){var e,r,u,i=il.lastIndex=ol.lastIndex=0,o=-1,a=[],c=[];for(n+="",t+="";(e=il.exec(n))&&(r=ol.exec(t));)(u=r.index)>i&&(u=t.slice(i,u),a[o]?a[o]+=u:a[++o]=u),(e=e[0])===(r=r[0])?a[o]?a[o]+=r:a[++o]=r:(a[++o]=null,c.push({i:o,x:vu(e,r)})),i=ol.lastIndex;return ir;++r)a[(e=c[r]).i]=e.x(n);return a.join("")})}function mu(n,t){for(var e,r=ta.interpolators.length;--r>=0&&!(e=ta.interpolators[r](n,t)););return e}function yu(n,t){var e,r=[],u=[],i=n.length,o=t.length,a=Math.min(n.length,t.length);for(e=0;a>e;++e)r.push(mu(n[e],t[e]));for(;i>e;++e)u[e]=n[e];for(;o>e;++e)u[e]=t[e];return function(n){for(e=0;a>e;++e)u[e]=r[e](n);return u}}function Mu(n){return function(t){return 0>=t?0:t>=1?1:n(t)}}function xu(n){return function(t){return 1-n(1-t)}}function bu(n){return function(t){return.5*(.5>t?n(2*t):2-n(2-2*t))}}function _u(n){return n*n}function wu(n){return n*n*n}function Su(n){if(0>=n)return 0;if(n>=1)return 1;var t=n*n,e=t*n;return 4*(.5>n?e:3*(n-t)+e-.75)}function ku(n){return function(t){return Math.pow(t,n)}}function Eu(n){return 1-Math.cos(n*Ra)}function Au(n){return Math.pow(2,10*(n-1))}function Nu(n){return 1-Math.sqrt(1-n*n)}function Cu(n,t){var e;return arguments.length<2&&(t=.45),arguments.length?e=t/La*Math.asin(1/n):(n=1,e=t/4),function(r){return 1+n*Math.pow(2,-10*r)*Math.sin((r-e)*La/t)}}function zu(n){return n||(n=1.70158),function(t){return t*t*((n+1)*t-n)}}function qu(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375}function Lu(n,t){n=ta.hcl(n),t=ta.hcl(t);var e=n.h,r=n.c,u=n.l,i=t.h-e,o=t.c-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.c:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return st(e+i*n,r+o*n,u+a*n)+""}}function Tu(n,t){n=ta.hsl(n),t=ta.hsl(t);var e=n.h,r=n.s,u=n.l,i=t.h-e,o=t.s-r,a=t.l-u;return isNaN(o)&&(o=0,r=isNaN(r)?t.s:r),isNaN(i)?(i=0,e=isNaN(e)?t.h:e):i>180?i-=360:-180>i&&(i+=360),function(n){return ct(e+i*n,r+o*n,u+a*n)+""}}function Ru(n,t){n=ta.lab(n),t=ta.lab(t);var e=n.l,r=n.a,u=n.b,i=t.l-e,o=t.a-r,a=t.b-u;return function(n){return ht(e+i*n,r+o*n,u+a*n)+""}}function Du(n,t){return t-=n,function(e){return Math.round(n+t*e)}}function Pu(n){var t=[n.a,n.b],e=[n.c,n.d],r=ju(t),u=Uu(t,e),i=ju(Fu(e,t,-u))||0;t[0]*e[1]180?s+=360:s-l>180&&(l+=360),u.push({i:r.push(r.pop()+"rotate(",null,")")-2,x:vu(l,s)})):s&&r.push(r.pop()+"rotate("+s+")"),f!=h?u.push({i:r.push(r.pop()+"skewX(",null,")")-2,x:vu(f,h)}):h&&r.push(r.pop()+"skewX("+h+")"),g[0]!=p[0]||g[1]!=p[1]?(e=r.push(r.pop()+"scale(",null,",",null,")"),u.push({i:e-4,x:vu(g[0],p[0])},{i:e-2,x:vu(g[1],p[1])})):(1!=p[0]||1!=p[1])&&r.push(r.pop()+"scale("+p+")"),e=u.length,function(n){for(var t,i=-1;++i=0;)e.push(u[r])}function Qu(n,t){for(var e=[n],r=[];null!=(n=e.pop());)if(r.push(n),(i=n.children)&&(u=i.length))for(var u,i,o=-1;++oe;++e)(t=n[e][1])>u&&(r=e,u=t);return r}function si(n){return n.reduce(fi,0)}function fi(n,t){return n+t[1]}function hi(n,t){return gi(n,Math.ceil(Math.log(t.length)/Math.LN2+1))}function gi(n,t){for(var e=-1,r=+n[0],u=(n[1]-r)/t,i=[];++e<=t;)i[e]=u*e+r;return i}function pi(n){return[ta.min(n),ta.max(n)]}function vi(n,t){return n.value-t.value}function di(n,t){var e=n._pack_next;n._pack_next=t,t._pack_prev=n,t._pack_next=e,e._pack_prev=t}function mi(n,t){n._pack_next=t,t._pack_prev=n}function yi(n,t){var e=t.x-n.x,r=t.y-n.y,u=n.r+t.r;return.999*u*u>e*e+r*r}function Mi(n){function t(n){s=Math.min(n.x-n.r,s),f=Math.max(n.x+n.r,f),h=Math.min(n.y-n.r,h),g=Math.max(n.y+n.r,g)}if((e=n.children)&&(l=e.length)){var e,r,u,i,o,a,c,l,s=1/0,f=-1/0,h=1/0,g=-1/0;if(e.forEach(xi),r=e[0],r.x=-r.r,r.y=0,t(r),l>1&&(u=e[1],u.x=u.r,u.y=0,t(u),l>2))for(i=e[2],wi(r,u,i),t(i),di(r,i),r._pack_prev=i,di(i,u),u=r._pack_next,o=3;l>o;o++){wi(r,u,i=e[o]);var p=0,v=1,d=1;for(a=u._pack_next;a!==u;a=a._pack_next,v++)if(yi(a,i)){p=1;break}if(1==p)for(c=r._pack_prev;c!==a._pack_prev&&!yi(c,i);c=c._pack_prev,d++);p?(d>v||v==d&&u.ro;o++)i=e[o],i.x-=m,i.y-=y,M=Math.max(M,i.r+Math.sqrt(i.x*i.x+i.y*i.y));n.r=M,e.forEach(bi)}}function xi(n){n._pack_next=n._pack_prev=n}function bi(n){delete n._pack_next,delete n._pack_prev}function _i(n,t,e,r){var u=n.children;if(n.x=t+=r*n.x,n.y=e+=r*n.y,n.r*=r,u)for(var i=-1,o=u.length;++i=0;)t=u[i],t.z+=e,t.m+=e,e+=t.s+(r+=t.c)}function Ci(n,t,e){return n.a.parent===t.parent?n.a:e}function zi(n){return 1+ta.max(n,function(n){return n.y})}function qi(n){return n.reduce(function(n,t){return n+t.x},0)/n.length}function Li(n){var t=n.children;return t&&t.length?Li(t[0]):n}function Ti(n){var t,e=n.children;return e&&(t=e.length)?Ti(e[t-1]):n}function Ri(n){return{x:n.x,y:n.y,dx:n.dx,dy:n.dy}}function Di(n,t){var e=n.x+t[3],r=n.y+t[0],u=n.dx-t[1]-t[3],i=n.dy-t[0]-t[2];return 0>u&&(e+=u/2,u=0),0>i&&(r+=i/2,i=0),{x:e,y:r,dx:u,dy:i}}function Pi(n){var t=n[0],e=n[n.length-1];return e>t?[t,e]:[e,t]}function Ui(n){return n.rangeExtent?n.rangeExtent():Pi(n.range())}function ji(n,t,e,r){var u=e(n[0],n[1]),i=r(t[0],t[1]);return function(n){return i(u(n))}}function Fi(n,t){var e,r=0,u=n.length-1,i=n[r],o=n[u];return i>o&&(e=r,r=u,u=e,e=i,i=o,o=e),n[r]=t.floor(i),n[u]=t.ceil(o),n}function Hi(n){return n?{floor:function(t){return Math.floor(t/n)*n},ceil:function(t){return Math.ceil(t/n)*n}}:ml}function Oi(n,t,e,r){var u=[],i=[],o=0,a=Math.min(n.length,t.length)-1;for(n[a]2?Oi:ji,c=r?Iu:Ou;return o=u(n,t,c,e),a=u(t,n,c,mu),i}function i(n){return o(n)}var o,a;return i.invert=function(n){return a(n)},i.domain=function(t){return arguments.length?(n=t.map(Number),u()):n},i.range=function(n){return arguments.length?(t=n,u()):t},i.rangeRound=function(n){return i.range(n).interpolate(Du)},i.clamp=function(n){return arguments.length?(r=n,u()):r},i.interpolate=function(n){return arguments.length?(e=n,u()):e},i.ticks=function(t){return Xi(n,t)},i.tickFormat=function(t,e){return $i(n,t,e)},i.nice=function(t){return Zi(n,t),u()},i.copy=function(){return Ii(n,t,e,r)},u()}function Yi(n,t){return ta.rebind(n,t,"range","rangeRound","interpolate","clamp")}function Zi(n,t){return Fi(n,Hi(Vi(n,t)[2]))}function Vi(n,t){null==t&&(t=10);var e=Pi(n),r=e[1]-e[0],u=Math.pow(10,Math.floor(Math.log(r/t)/Math.LN10)),i=t/r*u;return.15>=i?u*=10:.35>=i?u*=5:.75>=i&&(u*=2),e[0]=Math.ceil(e[0]/u)*u,e[1]=Math.floor(e[1]/u)*u+.5*u,e[2]=u,e}function Xi(n,t){return ta.range.apply(ta,Vi(n,t))}function $i(n,t,e){var r=Vi(n,t);if(e){var u=ic.exec(e);if(u.shift(),"s"===u[8]){var i=ta.formatPrefix(Math.max(ga(r[0]),ga(r[1])));return u[7]||(u[7]="."+Bi(i.scale(r[2]))),u[8]="f",e=ta.format(u.join("")),function(n){return e(i.scale(n))+i.symbol}}u[7]||(u[7]="."+Wi(u[8],r)),e=u.join("")}else e=",."+Bi(r[2])+"f";return ta.format(e)}function Bi(n){return-Math.floor(Math.log(n)/Math.LN10+.01)}function Wi(n,t){var e=Bi(t[2]);return n in yl?Math.abs(e-Bi(Math.max(ga(t[0]),ga(t[1]))))+ +("e"!==n):e-2*("%"===n)}function Ji(n,t,e,r){function u(n){return(e?Math.log(0>n?0:n):-Math.log(n>0?0:-n))/Math.log(t)}function i(n){return e?Math.pow(t,n):-Math.pow(t,-n)}function o(t){return n(u(t))}return o.invert=function(t){return i(n.invert(t))},o.domain=function(t){return arguments.length?(e=t[0]>=0,n.domain((r=t.map(Number)).map(u)),o):r},o.base=function(e){return arguments.length?(t=+e,n.domain(r.map(u)),o):t},o.nice=function(){var t=Fi(r.map(u),e?Math:xl);return n.domain(t),r=t.map(i),o},o.ticks=function(){var n=Pi(r),o=[],a=n[0],c=n[1],l=Math.floor(u(a)),s=Math.ceil(u(c)),f=t%1?2:t;if(isFinite(s-l)){if(e){for(;s>l;l++)for(var h=1;f>h;h++)o.push(i(l)*h);o.push(i(l))}else for(o.push(i(l));l++0;h--)o.push(i(l)*h);for(l=0;o[l]c;s--);o=o.slice(l,s)}return o},o.tickFormat=function(n,t){if(!arguments.length)return Ml;arguments.length<2?t=Ml:"function"!=typeof t&&(t=ta.format(t));var r,a=Math.max(.1,n/o.ticks().length),c=e?(r=1e-12,Math.ceil):(r=-1e-12,Math.floor);return function(n){return n/i(c(u(n)+r))<=a?t(n):""}},o.copy=function(){return Ji(n.copy(),t,e,r)},Yi(o,n)}function Gi(n,t,e){function r(t){return n(u(t))}var u=Ki(t),i=Ki(1/t);return r.invert=function(t){return i(n.invert(t))},r.domain=function(t){return arguments.length?(n.domain((e=t.map(Number)).map(u)),r):e},r.ticks=function(n){return Xi(e,n)},r.tickFormat=function(n,t){return $i(e,n,t)},r.nice=function(n){return r.domain(Zi(e,n))},r.exponent=function(o){return arguments.length?(u=Ki(t=o),i=Ki(1/t),n.domain(e.map(u)),r):t},r.copy=function(){return Gi(n.copy(),t,e)},Yi(r,n)}function Ki(n){return function(t){return 0>t?-Math.pow(-t,n):Math.pow(t,n)}}function Qi(n,t){function e(e){return i[((u.get(e)||("range"===t.t?u.set(e,n.push(e)):0/0))-1)%i.length]}function r(t,e){return ta.range(n.length).map(function(n){return t+e*n})}var u,i,o;return e.domain=function(r){if(!arguments.length)return n;n=[],u=new l;for(var i,o=-1,a=r.length;++o e?[0/0,0/0]:[e>0?a[e-1]:n[0],e t?0/0:t/i+n,[t,t+1/i]},r.copy=function(){return to(n,t,e)},u()}function eo(n,t){function e(e){return e>=e?t[ta.bisect(n,e)]:void 0}return e.domain=function(t){return arguments.length?(n=t,e):n},e.range=function(n){return arguments.length?(t=n,e):t},e.invertExtent=function(e){return e=t.indexOf(e),[n[e-1],n[e]]},e.copy=function(){return eo(n,t)},e}function ro(n){function t(n){return+n}return t.invert=t,t.domain=t.range=function(e){return arguments.length?(n=e.map(t),t):n},t.ticks=function(t){return Xi(n,t)},t.tickFormat=function(t,e){return $i(n,t,e)},t.copy=function(){return ro(n)},t}function uo(){return 0}function io(n){return n.innerRadius}function oo(n){return n.outerRadius}function ao(n){return n.startAngle}function co(n){return n.endAngle}function lo(n){return n&&n.padAngle}function so(n,t,e,r){return(n-e)*t-(t-r)*n>0?0:1}function fo(n,t,e,r,u){var i=n[0]-t[0],o=n[1]-t[1],a=(u?r:-r)/Math.sqrt(i*i+o*o),c=a*o,l=-a*i,s=n[0]+c,f=n[1]+l,h=t[0]+c,g=t[1]+l,p=(s+h)/2,v=(f+g)/2,d=h-s,m=g-f,y=d*d+m*m,M=e-r,x=s*g-h*f,b=(0>m?-1:1)*Math.sqrt(M*M*y-x*x),_=(x*m-d*b)/y,w=(-x*d-m*b)/y,S=(x*m+d*b)/y,k=(-x*d+m*b)/y,E=_-p,A=w-v,N=S-p,C=k-v;return E*E+A*A>N*N+C*C&&(_=S,w=k),[[_-c,w-l],[_*e/M,w*e/M]]}function ho(n){function t(t){function o(){l.push("M",i(n(s),a))}for(var c,l=[],s=[],f=-1,h=t.length,g=Et(e),p=Et(r);++f1&&u.push("H",r[0]),u.join("")}function mo(n){for(var t=0,e=n.length,r=n[0],u=[r[0],",",r[1]];++t1){a=t[1],i=n[c],c++,r+="C"+(u[0]+o[0])+","+(u[1]+o[1])+","+(i[0]-a[0])+","+(i[1]-a[1])+","+i[0]+","+i[1];for(var l=2;l9&&(u=3*t/Math.sqrt(u),o[a]=u*e,o[a+1]=u*r));for(a=-1;++a<=c;)u=(n[Math.min(c,a+1)][0]-n[Math.max(0,a-1)][0])/(6*(1+o[a]*o[a])),i.push([u||0,o[a]*u||0]);return i}function To(n){return n.length<3?go(n):n[0]+_o(n,Lo(n))}function Ro(n){for(var t,e,r,u=-1,i=n.length;++ur)return s();var u=i[i.active];u&&(--i.count,delete i[i.active],u.event&&u.event.interrupt.call(n,n.__data__,u.index)),i.active=r,o.event&&o.event.start.call(n,n.__data__,t),o.tween.forEach(function(e,r){(r=r.call(n,n.__data__,t))&&v.push(r)}),h=o.ease,f=o.duration,ta.timer(function(){return p.c=l(e||1)?Ne:l,1},0,a)}function l(e){if(i.active!==r)return 1;for(var u=e/f,a=h(u),c=v.length;c>0;)v[--c].call(n,a);return u>=1?(o.event&&o.event.end.call(n,n.__data__,t),s()):void 0}function s(){return--i.count?delete i[r]:delete n[e],1}var f,h,g=o.delay,p=ec,v=[];return p.t=g+a,u>=g?c(u-g):void(p.c=c)},0,a)}}function Bo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate("+(isFinite(r)?r:e(n))+",0)"})}function Wo(n,t,e){n.attr("transform",function(n){var r=t(n);return"translate(0,"+(isFinite(r)?r:e(n))+")"})}function Jo(n){return n.toISOString()}function Go(n,t,e){function r(t){return n(t)}function u(n,e){var r=n[1]-n[0],u=r/e,i=ta.bisect(Vl,u);return i==Vl.length?[t.year,Vi(n.map(function(n){return n/31536e6}),e)[2]]:i?t[u/Vl[i-1]1?{floor:function(t){for(;e(t=n.floor(t));)t=Ko(t-1);return t},ceil:function(t){for(;e(t=n.ceil(t));)t=Ko(+t+1);return t}}:n))},r.ticks=function(n,t){var e=Pi(r.domain()),i=null==n?u(e,10):"number"==typeof n?u(e,n):!n.range&&[{range:n},t];return i&&(n=i[0],t=i[1]),n.range(e[0],Ko(+e[1]+1),1>t?1:t)},r.tickFormat=function(){return e},r.copy=function(){return Go(n.copy(),t,e)},Yi(r,n)}function Ko(n){return new Date(n)}function Qo(n){return JSON.parse(n.responseText)}function na(n){var t=ua.createRange();return t.selectNode(ua.body),t.createContextualFragment(n.responseText)}var ta={version:"3.5.5"},ea=[].slice,ra=function(n){return ea.call(n)},ua=this.document;if(ua)try{ra(ua.documentElement.childNodes)[0].nodeType}catch(ia){ra=function(n){for(var t=n.length,e=new Array(t);t--;)e[t]=n[t];return e}}if(Date.now||(Date.now=function(){return+new Date}),ua)try{ua.createElement("DIV").style.setProperty("opacity",0,"")}catch(oa){var aa=this.Element.prototype,ca=aa.setAttribute,la=aa.setAttributeNS,sa=this.CSSStyleDeclaration.prototype,fa=sa.setProperty;aa.setAttribute=function(n,t){ca.call(this,n,t+"")},aa.setAttributeNS=function(n,t,e){la.call(this,n,t,e+"")},sa.setProperty=function(n,t,e){fa.call(this,n,t+"",e)}}ta.ascending=e,ta.descending=function(n,t){return n>t?-1:t>n?1:t>=n?0:0/0},ta.min=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ur&&(e=r)}else{for(;++u=r){e=r;break}for(;++ur&&(e=r)}return e},ta.max=function(n,t){var e,r,u=-1,i=n.length;if(1===arguments.length){for(;++u=r){e=r;break}for(;++ue&&(e=r)}else{for(;++u=r){e=r;break}for(;++ue&&(e=r)}return e},ta.extent=function(n,t){var e,r,u,i=-1,o=n.length;if(1===arguments.length){for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}else{for(;++i=r){e=u=r;break}for(;++ir&&(e=r),r>u&&(u=r))}return[e,u]},ta.sum=function(n,t){var e,r=0,i=n.length,o=-1;if(1===arguments.length)for(;++o1?c/(s-1):void 0},ta.deviation=function(){var n=ta.variance.apply(this,arguments);return n?Math.sqrt(n):n};var ha=i(e);ta.bisectLeft=ha.left,ta.bisect=ta.bisectRight=ha.right,ta.bisector=function(n){return i(1===n.length?function(t,r){return e(n(t),r)}:n)},ta.shuffle=function(n,t,e){(i=arguments.length)<3&&(e=n.length,2>i&&(t=0));for(var r,u,i=e-t;i;)u=Math.random()*i--|0,r=n[i+t],n[i+t]=n[u+t],n[u+t]=r;return n},ta.permute=function(n,t){for(var e=t.length,r=new Array(e);e--;)r[e]=n[t[e]];return r},ta.pairs=function(n){for(var t,e=0,r=n.length-1,u=n[0],i=new Array(0>r?0:r);r>e;)i[e]=[t=u,u=n[++e]];return i},ta.zip=function(){if(!(r=arguments.length))return[];for(var n=-1,t=ta.min(arguments,o),e=new Array(t);++n=0;)for(r=n[u],t=r.length;--t>=0;)e[--o]=r[t];return e};var ga=Math.abs;ta.range=function(n,t,e){if(arguments.length<3&&(e=1,arguments.length<2&&(t=n,n=0)),(t-n)/e===1/0)throw new Error("infinite range");var r,u=[],i=a(ga(e)),o=-1;if(n*=i,t*=i,e*=i,0>e)for(;(r=n+e*++o)>t;)u.push(r/i);else for(;(r=n+e*++o)=i.length)return r?r.call(u,o):e?o.sort(e):o;for(var c,s,f,h,g=-1,p=o.length,v=i[a++],d=new l;++g=i.length)return n;var r=[],u=o[e++];return n.forEach(function(n,u){r.push({key:n,values:t(u,e)})}),u?r.sort(function(n,t){return u(n.key,t.key)}):r}var e,r,u={},i=[],o=[];return u.map=function(t,e){return n(e,t,0)},u.entries=function(e){return t(n(ta.map,e,0),0)},u.key=function(n){return i.push(n),u},u.sortKeys=function(n){return o[i.length-1]=n,u},u.sortValues=function(n){return e=n,u},u.rollup=function(n){return r=n,u},u},ta.set=function(n){var t=new m;if(n)for(var e=0,r=n.length;r>e;++e)t.add(n[e]);return t},c(m,{has:h,add:function(n){return this._[s(n+="")]=!0,n},remove:g,values:p,size:v,empty:d,forEach:function(n){for(var t in this._)n.call(this,f(t))}}),ta.behavior={},ta.rebind=function(n,t){for(var e,r=1,u=arguments.length;++r=0&&(r=n.slice(e+1),n=n.slice(0,e)),n)return arguments.length<2?this[n].on(r):this[n].on(r,t);if(2===arguments.length){if(null==t)for(n in this)this.hasOwnProperty(n)&&this[n].on(r,null);return this}},ta.event=null,ta.requote=function(n){return n.replace(ma,"\\$&")};var ma=/[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g,ya={}.__proto__?function(n,t){n.__proto__=t}:function(n,t){for(var e in t)n[e]=t[e]},Ma=function(n,t){return t.querySelector(n)},xa=function(n,t){return t.querySelectorAll(n)},ba=function(n,t){var e=n.matches||n[x(n,"matchesSelector")];return(ba=function(n,t){return e.call(n,t)})(n,t)};"function"==typeof Sizzle&&(Ma=function(n,t){return Sizzle(n,t)[0]||null},xa=Sizzle,ba=Sizzle.matchesSelector),ta.selection=function(){return ta.select(ua.documentElement)};var _a=ta.selection.prototype=[];_a.select=function(n){var t,e,r,u,i=[];n=N(n);for(var o=-1,a=this.length;++o=0&&(e=n.slice(0,t),n=n.slice(t+1)),wa.hasOwnProperty(e)?{space:wa[e],local:n}:n}},_a.attr=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node();return n=ta.ns.qualify(n),n.local?e.getAttributeNS(n.space,n.local):e.getAttribute(n)}for(t in n)this.each(z(t,n[t]));return this}return this.each(z(n,t))},_a.classed=function(n,t){if(arguments.length<2){if("string"==typeof n){var e=this.node(),r=(n=T(n)).length,u=-1;if(t=e.classList){for(;++uu){if("string"!=typeof n){2>u&&(e="");for(r in n)this.each(P(r,n[r],e));return this}if(2>u){var i=this.node();return t(i).getComputedStyle(i,null).getPropertyValue(n)}r=""}return this.each(P(n,e,r))},_a.property=function(n,t){if(arguments.length<2){if("string"==typeof n)return this.node()[n];for(t in n)this.each(U(t,n[t]));return this}return this.each(U(n,t))},_a.text=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.textContent=null==t?"":t}:null==n?function(){this.textContent=""}:function(){this.textContent=n}):this.node().textContent},_a.html=function(n){return arguments.length?this.each("function"==typeof n?function(){var t=n.apply(this,arguments);this.innerHTML=null==t?"":t}:null==n?function(){this.innerHTML=""}:function(){this.innerHTML=n}):this.node().innerHTML},_a.append=function(n){return n=j(n),this.select(function(){return this.appendChild(n.apply(this,arguments))})},_a.insert=function(n,t){return n=j(n),t=N(t),this.select(function(){return this.insertBefore(n.apply(this,arguments),t.apply(this,arguments)||null)})},_a.remove=function(){return this.each(F)},_a.data=function(n,t){function e(n,e){var r,u,i,o=n.length,f=e.length,h=Math.min(o,f),g=new Array(f),p=new Array(f),v=new Array(o);if(t){var d,m=new l,y=new Array(o);for(r=-1;++rr;++r)p[r]=H(e[r]);for(;o>r;++r)v[r]=n[r]}p.update=g,p.parentNode=g.parentNode=v.parentNode=n.parentNode,a.push(p),c.push(g),s.push(v)}var r,u,i=-1,o=this.length;if(!arguments.length){for(n=new Array(o=(r=this[0]).length);++ii;i++){u.push(t=[]),t.parentNode=(e=this[i]).parentNode;for(var a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return A(u)},_a.order=function(){for(var n=-1,t=this.length;++n=0;)(e=r[u])&&(i&&i!==e.nextSibling&&i.parentNode.insertBefore(e,i),i=e);return this},_a.sort=function(n){n=I.apply(this,arguments);for(var t=-1,e=this.length;++tn;n++)for(var e=this[n],r=0,u=e.length;u>r;r++){var i=e[r];if(i)return i}return null},_a.size=function(){var n=0;return Y(this,function(){++n}),n};var Sa=[];ta.selection.enter=Z,ta.selection.enter.prototype=Sa,Sa.append=_a.append,Sa.empty=_a.empty,Sa.node=_a.node,Sa.call=_a.call,Sa.size=_a.size,Sa.select=function(n){for(var t,e,r,u,i,o=[],a=-1,c=this.length;++ar){if("string"!=typeof n){2>r&&(t=!1);for(e in n)this.each(X(e,n[e],t));return this}if(2>r)return(r=this.node()["__on"+n])&&r._;e=!1}return this.each(X(n,t,e))};var ka=ta.map({mouseenter:"mouseover",mouseleave:"mouseout"});ua&&ka.forEach(function(n){"on"+n in ua&&ka.remove(n)});var Ea,Aa=0;ta.mouse=function(n){return J(n,k())};var Na=this.navigator&&/WebKit/.test(this.navigator.userAgent)?-1:0;ta.touch=function(n,t,e){if(arguments.length<3&&(e=t,t=k().changedTouches),t)for(var r,u=0,i=t.length;i>u;++u)if((r=t[u]).identifier===e)return J(n,r)},ta.behavior.drag=function(){function n(){this.on("mousedown.drag",i).on("touchstart.drag",o)}function e(n,t,e,i,o){return function(){function a(){var n,e,r=t(h,v);r&&(n=r[0]-M[0],e=r[1]-M[1],p|=n|e,M=r,g({type:"drag",x:r[0]+l[0],y:r[1]+l[1],dx:n,dy:e}))}function c(){t(h,v)&&(m.on(i+d,null).on(o+d,null),y(p&&ta.event.target===f),g({type:"dragend"}))}var l,s=this,f=ta.event.target,h=s.parentNode,g=r.of(s,arguments),p=0,v=n(),d=".drag"+(null==v?"":"-"+v),m=ta.select(e(f)).on(i+d,a).on(o+d,c),y=W(f),M=t(h,v);u?(l=u.apply(s,arguments),l=[l.x-M[0],l.y-M[1]]):l=[0,0],g({type:"dragstart"})}}var r=E(n,"drag","dragstart","dragend"),u=null,i=e(b,ta.mouse,t,"mousemove","mouseup"),o=e(G,ta.touch,y,"touchmove","touchend");return n.origin=function(t){return arguments.length?(u=t,n):u},ta.rebind(n,r,"on")},ta.touches=function(n,t){return arguments.length<2&&(t=k().touches),t?ra(t).map(function(t){var e=J(n,t);return e.identifier=t.identifier,e}):[]};var Ca=1e-6,za=Ca*Ca,qa=Math.PI,La=2*qa,Ta=La-Ca,Ra=qa/2,Da=qa/180,Pa=180/qa,Ua=Math.SQRT2,ja=2,Fa=4;ta.interpolateZoom=function(n,t){function e(n){var t=n*y;if(m){var e=rt(v),o=i/(ja*h)*(e*ut(Ua*t+v)-et(v));return[r+o*l,u+o*s,i*e/rt(Ua*t+v)]}return[r+n*l,u+n*s,i*Math.exp(Ua*t)]}var r=n[0],u=n[1],i=n[2],o=t[0],a=t[1],c=t[2],l=o-r,s=a-u,f=l*l+s*s,h=Math.sqrt(f),g=(c*c-i*i+Fa*f)/(2*i*ja*h),p=(c*c-i*i-Fa*f)/(2*c*ja*h),v=Math.log(Math.sqrt(g*g+1)-g),d=Math.log(Math.sqrt(p*p+1)-p),m=d-v,y=(m||Math.log(c/i))/Ua;return e.duration=1e3*y,e},ta.behavior.zoom=function(){function n(n){n.on(q,f).on(Oa+".zoom",g).on("dblclick.zoom",p).on(R,h)}function e(n){return[(n[0]-k.x)/k.k,(n[1]-k.y)/k.k]}function r(n){return[n[0]*k.k+k.x,n[1]*k.k+k.y]}function u(n){k.k=Math.max(N[0],Math.min(N[1],n))}function i(n,t){t=r(t),k.x+=n[0]-t[0],k.y+=n[1]-t[1]}function o(t,e,r,o){t.__chart__={x:k.x,y:k.y,k:k.k},u(Math.pow(2,o)),i(d=e,r),t=ta.select(t),C>0&&(t=t.transition().duration(C)),t.call(n.event)}function a(){b&&b.domain(x.range().map(function(n){return(n-k.x)/k.k}).map(x.invert)),w&&w.domain(_.range().map(function(n){return(n-k.y)/k.k}).map(_.invert))}function c(n){z++||n({type:"zoomstart"})}function l(n){a(),n({type:"zoom",scale:k.k,translate:[k.x,k.y]})}function s(n){--z||n({type:"zoomend"}),d=null}function f(){function n(){f=1,i(ta.mouse(u),g),l(a)}function r(){h.on(L,null).on(T,null),p(f&&ta.event.target===o),s(a)}var u=this,o=ta.event.target,a=D.of(u,arguments),f=0,h=ta.select(t(u)).on(L,n).on(T,r),g=e(ta.mouse(u)),p=W(u);Dl.call(u),c(a)}function h(){function n(){var n=ta.touches(p);return g=k.k,n.forEach(function(n){n.identifier in d&&(d[n.identifier]=e(n))}),n}function t(){var t=ta.event.target;ta.select(t).on(x,r).on(b,a),_.push(t);for(var e=ta.event.changedTouches,u=0,i=e.length;i>u;++u)d[e[u].identifier]=null;var c=n(),l=Date.now();if(1===c.length){if(500>l-M){var s=c[0];o(p,s,d[s.identifier],Math.floor(Math.log(k.k)/Math.LN2)+1),S()}M=l}else if(c.length>1){var s=c[0],f=c[1],h=s[0]-f[0],g=s[1]-f[1];m=h*h+g*g}}function r(){var n,t,e,r,o=ta.touches(p);Dl.call(p);for(var a=0,c=o.length;c>a;++a,r=null)if(e=o[a],r=d[e.identifier]){if(t)break;n=e,t=r}if(r){var s=(s=e[0]-n[0])*s+(s=e[1]-n[1])*s,f=m&&Math.sqrt(s/m);n=[(n[0]+e[0])/2,(n[1]+e[1])/2],t=[(t[0]+r[0])/2,(t[1]+r[1])/2],u(f*g)}M=null,i(n,t),l(v)}function a(){if(ta.event.touches.length){for(var t=ta.event.changedTouches,e=0,r=t.length;r>e;++e)delete d[t[e].identifier];for(var u in d)return void n()}ta.selectAll(_).on(y,null),w.on(q,f).on(R,h),E(),s(v)}var g,p=this,v=D.of(p,arguments),d={},m=0,y=".zoom-"+ta.event.changedTouches[0].identifier,x="touchmove"+y,b="touchend"+y,_=[],w=ta.select(p),E=W(p);t(),c(v),w.on(q,null).on(R,t)}function g(){var n=D.of(this,arguments);y?clearTimeout(y):(v=e(d=m||ta.mouse(this)),Dl.call(this),c(n)),y=setTimeout(function(){y=null,s(n)},50),S(),u(Math.pow(2,.002*Ha())*k.k),i(d,v),l(n)}function p(){var n=ta.mouse(this),t=Math.log(k.k)/Math.LN2;o(this,n,e(n),ta.event.shiftKey?Math.ceil(t)-1:Math.floor(t)+1)}var v,d,m,y,M,x,b,_,w,k={x:0,y:0,k:1},A=[960,500],N=Ia,C=250,z=0,q="mousedown.zoom",L="mousemove.zoom",T="mouseup.zoom",R="touchstart.zoom",D=E(n,"zoomstart","zoom","zoomend");return Oa||(Oa="onwheel"in ua?(Ha=function(){return-ta.event.deltaY*(ta.event.deltaMode?120:1)},"wheel"):"onmousewheel"in ua?(Ha=function(){return ta.event.wheelDelta},"mousewheel"):(Ha=function(){return-ta.event.detail},"MozMousePixelScroll")),n.event=function(n){n.each(function(){var n=D.of(this,arguments),t=k;Tl?ta.select(this).transition().each("start.zoom",function(){k=this.__chart__||{x:0,y:0,k:1},c(n)}).tween("zoom:zoom",function(){var e=A[0],r=A[1],u=d?d[0]:e/2,i=d?d[1]:r/2,o=ta.interpolateZoom([(u-k.x)/k.k,(i-k.y)/k.k,e/k.k],[(u-t.x)/t.k,(i-t.y)/t.k,e/t.k]);return function(t){var r=o(t),a=e/r[2];this.__chart__=k={x:u-r[0]*a,y:i-r[1]*a,k:a},l(n)}}).each("interrupt.zoom",function(){s(n)}).each("end.zoom",function(){s(n)}):(this.__chart__=k,c(n),l(n),s(n))})},n.translate=function(t){return arguments.length?(k={x:+t[0],y:+t[1],k:k.k},a(),n):[k.x,k.y]},n.scale=function(t){return arguments.length?(k={x:k.x,y:k.y,k:+t},a(),n):k.k},n.scaleExtent=function(t){return arguments.length?(N=null==t?Ia:[+t[0],+t[1]],n):N},n.center=function(t){return arguments.length?(m=t&&[+t[0],+t[1]],n):m},n.size=function(t){return arguments.length?(A=t&&[+t[0],+t[1]],n):A},n.duration=function(t){return arguments.length?(C=+t,n):C},n.x=function(t){return arguments.length?(b=t,x=t.copy(),k={x:0,y:0,k:1},n):b},n.y=function(t){return arguments.length?(w=t,_=t.copy(),k={x:0,y:0,k:1},n):w},ta.rebind(n,D,"on")};var Ha,Oa,Ia=[0,1/0];ta.color=ot,ot.prototype.toString=function(){return this.rgb()+""},ta.hsl=at;var Ya=at.prototype=new ot;Ya.brighter=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,this.l/n)},Ya.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new at(this.h,this.s,n*this.l)},Ya.rgb=function(){return ct(this.h,this.s,this.l)},ta.hcl=lt;var Za=lt.prototype=new ot;Za.brighter=function(n){return new lt(this.h,this.c,Math.min(100,this.l+Va*(arguments.length?n:1)))},Za.darker=function(n){return new lt(this.h,this.c,Math.max(0,this.l-Va*(arguments.length?n:1)))},Za.rgb=function(){return st(this.h,this.c,this.l).rgb()},ta.lab=ft;var Va=18,Xa=.95047,$a=1,Ba=1.08883,Wa=ft.prototype=new ot;Wa.brighter=function(n){return new ft(Math.min(100,this.l+Va*(arguments.length?n:1)),this.a,this.b)},Wa.darker=function(n){return new ft(Math.max(0,this.l-Va*(arguments.length?n:1)),this.a,this.b)},Wa.rgb=function(){return ht(this.l,this.a,this.b)},ta.rgb=mt;var Ja=mt.prototype=new ot;Ja.brighter=function(n){n=Math.pow(.7,arguments.length?n:1);var t=this.r,e=this.g,r=this.b,u=30;return t||e||r?(t&&u>t&&(t=u),e&&u>e&&(e=u),r&&u>r&&(r=u),new mt(Math.min(255,t/n),Math.min(255,e/n),Math.min(255,r/n))):new mt(u,u,u)},Ja.darker=function(n){return n=Math.pow(.7,arguments.length?n:1),new mt(n*this.r,n*this.g,n*this.b)},Ja.hsl=function(){return _t(this.r,this.g,this.b)},Ja.toString=function(){return"#"+xt(this.r)+xt(this.g)+xt(this.b)};var Ga=ta.map({aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074});Ga.forEach(function(n,t){Ga.set(n,yt(t))}),ta.functor=Et,ta.xhr=At(y),ta.dsv=function(n,t){function e(n,e,i){arguments.length<3&&(i=e,e=null);var o=Nt(n,t,null==e?r:u(e),i);return o.row=function(n){return arguments.length?o.response(null==(e=n)?r:u(n)):e},o}function r(n){return e.parse(n.responseText)}function u(n){return function(t){return e.parse(t.responseText,n)}}function i(t){return t.map(o).join(n)}function o(n){return a.test(n)?'"'+n.replace(/\"/g,'""')+'"':n}var a=new RegExp('["'+n+"\n]"),c=n.charCodeAt(0);return e.parse=function(n,t){var r;return e.parseRows(n,function(n,e){if(r)return r(n,e-1);var u=new Function("d","return {"+n.map(function(n,t){return JSON.stringify(n)+": d["+t+"]"}).join(",")+"}");r=t?function(n,e){return t(u(n),e)}:u})},e.parseRows=function(n,t){function e(){if(s>=l)return o;if(u)return u=!1,i;var t=s;if(34===n.charCodeAt(t)){for(var e=t;e++s;){var r=n.charCodeAt(s++),a=1;if(10===r)u=!0;else if(13===r)u=!0,10===n.charCodeAt(s)&&(++s,++a);else if(r!==c)continue;return n.slice(t,s-a)}return n.slice(t)}for(var r,u,i={},o={},a=[],l=n.length,s=0,f=0;(r=e())!==o;){for(var h=[];r!==i&&r!==o;)h.push(r),r=e();t&&null==(h=t(h,f++))||a.push(h)}return a},e.format=function(t){if(Array.isArray(t[0]))return e.formatRows(t);var r=new m,u=[];return t.forEach(function(n){for(var t in n)r.has(t)||u.push(r.add(t))}),[u.map(o).join(n)].concat(t.map(function(t){return u.map(function(n){return o(t[n])}).join(n)})).join("\n")},e.formatRows=function(n){return n.map(i).join("\n")},e},ta.csv=ta.dsv(",","text/csv"),ta.tsv=ta.dsv(" ","text/tab-separated-values");var Ka,Qa,nc,tc,ec,rc=this[x(this,"requestAnimationFrame")]||function(n){setTimeout(n,17)};ta.timer=function(n,t,e){var r=arguments.length;2>r&&(t=0),3>r&&(e=Date.now());var u=e+t,i={c:n,t:u,f:!1,n:null};Qa?Qa.n=i:Ka=i,Qa=i,nc||(tc=clearTimeout(tc),nc=1,rc(qt))},ta.timer.flush=function(){Lt(),Tt()},ta.round=function(n,t){return t?Math.round(n*(t=Math.pow(10,t)))/t:Math.round(n)};var uc=["y","z","a","f","p","n","\xb5","m","","k","M","G","T","P","E","Z","Y"].map(Dt);ta.formatPrefix=function(n,t){var e=0;return n&&(0>n&&(n*=-1),t&&(n=ta.round(n,Rt(n,t))),e=1+Math.floor(1e-12+Math.log(n)/Math.LN10),e=Math.max(-24,Math.min(24,3*Math.floor((e-1)/3)))),uc[8+e/3]};var ic=/(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i,oc=ta.map({b:function(n){return n.toString(2)},c:function(n){return String.fromCharCode(n)},o:function(n){return n.toString(8)},x:function(n){return n.toString(16)},X:function(n){return n.toString(16).toUpperCase()},g:function(n,t){return n.toPrecision(t)},e:function(n,t){return n.toExponential(t)},f:function(n,t){return n.toFixed(t)},r:function(n,t){return(n=ta.round(n,Rt(n,t))).toFixed(Math.max(0,Math.min(20,Rt(n*(1+1e-15),t))))}}),ac=ta.time={},cc=Date;jt.prototype={getDate:function(){return this._.getUTCDate()},getDay:function(){return this._.getUTCDay()},getFullYear:function(){return this._.getUTCFullYear()},getHours:function(){return this._.getUTCHours()},getMilliseconds:function(){return this._.getUTCMilliseconds()},getMinutes:function(){return this._.getUTCMinutes()},getMonth:function(){return this._.getUTCMonth()},getSeconds:function(){return this._.getUTCSeconds()},getTime:function(){return this._.getTime()},getTimezoneOffset:function(){return 0},valueOf:function(){return this._.valueOf()},setDate:function(){lc.setUTCDate.apply(this._,arguments)},setDay:function(){lc.setUTCDay.apply(this._,arguments)},setFullYear:function(){lc.setUTCFullYear.apply(this._,arguments)},setHours:function(){lc.setUTCHours.apply(this._,arguments)},setMilliseconds:function(){lc.setUTCMilliseconds.apply(this._,arguments)},setMinutes:function(){lc.setUTCMinutes.apply(this._,arguments)},setMonth:function(){lc.setUTCMonth.apply(this._,arguments)},setSeconds:function(){lc.setUTCSeconds.apply(this._,arguments)},setTime:function(){lc.setTime.apply(this._,arguments)}};var lc=Date.prototype;ac.year=Ft(function(n){return n=ac.day(n),n.setMonth(0,1),n},function(n,t){n.setFullYear(n.getFullYear()+t)},function(n){return n.getFullYear()}),ac.years=ac.year.range,ac.years.utc=ac.year.utc.range,ac.day=Ft(function(n){var t=new cc(2e3,0);return t.setFullYear(n.getFullYear(),n.getMonth(),n.getDate()),t},function(n,t){n.setDate(n.getDate()+t)},function(n){return n.getDate()-1}),ac.days=ac.day.range,ac.days.utc=ac.day.utc.range,ac.dayOfYear=function(n){var t=ac.year(n);return Math.floor((n-t-6e4*(n.getTimezoneOffset()-t.getTimezoneOffset()))/864e5)},["sunday","monday","tuesday","wednesday","thursday","friday","saturday"].forEach(function(n,t){t=7-t;var e=ac[n]=Ft(function(n){return(n=ac.day(n)).setDate(n.getDate()-(n.getDay()+t)%7),n},function(n,t){n.setDate(n.getDate()+7*Math.floor(t))},function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)-(e!==t)});ac[n+"s"]=e.range,ac[n+"s"].utc=e.utc.range,ac[n+"OfYear"]=function(n){var e=ac.year(n).getDay();return Math.floor((ac.dayOfYear(n)+(e+t)%7)/7)}}),ac.week=ac.sunday,ac.weeks=ac.sunday.range,ac.weeks.utc=ac.sunday.utc.range,ac.weekOfYear=ac.sundayOfYear;var sc={"-":"",_:" ",0:"0"},fc=/^\s*\d+/,hc=/^%/;ta.locale=function(n){return{numberFormat:Pt(n),timeFormat:Ot(n)}};var gc=ta.locale({decimal:".",thousands:",",grouping:[3],currency:["$",""],dateTime:"%a %b %e %X %Y",date:"%m/%d/%Y",time:"%H:%M:%S",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});ta.format=gc.numberFormat,ta.geo={},ce.prototype={s:0,t:0,add:function(n){le(n,this.t,pc),le(pc.s,this.s,this),this.s?this.t+=pc.t:this.s=pc.t
+},reset:function(){this.s=this.t=0},valueOf:function(){return this.s}};var pc=new ce;ta.geo.stream=function(n,t){n&&vc.hasOwnProperty(n.type)?vc[n.type](n,t):se(n,t)};var vc={Feature:function(n,t){se(n.geometry,t)},FeatureCollection:function(n,t){for(var e=n.features,r=-1,u=e.length;++rn?4*qa+n:n,Mc.lineStart=Mc.lineEnd=Mc.point=b}};ta.geo.bounds=function(){function n(n,t){M.push(x=[s=n,h=n]),f>t&&(f=t),t>g&&(g=t)}function t(t,e){var r=pe([t*Da,e*Da]);if(m){var u=de(m,r),i=[u[1],-u[0],0],o=de(i,u);Me(o),o=xe(o);var c=t-p,l=c>0?1:-1,v=o[0]*Pa*l,d=ga(c)>180;if(d^(v>l*p&&l*t>v)){var y=o[1]*Pa;y>g&&(g=y)}else if(v=(v+360)%360-180,d^(v>l*p&&l*t>v)){var y=-o[1]*Pa;f>y&&(f=y)}else f>e&&(f=e),e>g&&(g=e);d?p>t?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t):h>=s?(s>t&&(s=t),t>h&&(h=t)):t>p?a(s,t)>a(s,h)&&(h=t):a(t,h)>a(s,h)&&(s=t)}else n(t,e);m=r,p=t}function e(){b.point=t}function r(){x[0]=s,x[1]=h,b.point=n,m=null}function u(n,e){if(m){var r=n-p;y+=ga(r)>180?r+(r>0?360:-360):r}else v=n,d=e;Mc.point(n,e),t(n,e)}function i(){Mc.lineStart()}function o(){u(v,d),Mc.lineEnd(),ga(y)>Ca&&(s=-(h=180)),x[0]=s,x[1]=h,m=null}function a(n,t){return(t-=n)<0?t+360:t}function c(n,t){return n[0]-t[0]}function l(n,t){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nyc?(s=-(h=180),f=-(g=90)):y>Ca?g=90:-Ca>y&&(f=-90),x[0]=s,x[1]=h}};return function(n){g=h=-(s=f=1/0),M=[],ta.geo.stream(n,b);var t=M.length;if(t){M.sort(c);for(var e,r=1,u=M[0],i=[u];t>r;++r)e=M[r],l(e[0],u)||l(e[1],u)?(a(u[0],e[1])>a(u[0],u[1])&&(u[1]=e[1]),a(e[0],u[1])>a(u[0],u[1])&&(u[0]=e[0])):i.push(u=e);for(var o,e,p=-1/0,t=i.length-1,r=0,u=i[t];t>=r;u=e,++r)e=i[r],(o=a(u[1],e[0]))>p&&(p=o,s=e[0],h=u[1])}return M=x=null,1/0===s||1/0===f?[[0/0,0/0],[0/0,0/0]]:[[s,f],[h,g]]}}(),ta.geo.centroid=function(n){xc=bc=_c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,qc);var t=Nc,e=Cc,r=zc,u=t*t+e*e+r*r;return za>u&&(t=kc,e=Ec,r=Ac,Ca>bc&&(t=_c,e=wc,r=Sc),u=t*t+e*e+r*r,za>u)?[0/0,0/0]:[Math.atan2(e,t)*Pa,tt(r/Math.sqrt(u))*Pa]};var xc,bc,_c,wc,Sc,kc,Ec,Ac,Nc,Cc,zc,qc={sphere:b,point:_e,lineStart:Se,lineEnd:ke,polygonStart:function(){qc.lineStart=Ee},polygonEnd:function(){qc.lineStart=Se}},Lc=Le(Ne,Pe,je,[-qa,-qa/2]),Tc=1e9;ta.geo.clipExtent=function(){var n,t,e,r,u,i,o={stream:function(n){return u&&(u.valid=!1),u=i(n),u.valid=!0,u},extent:function(a){return arguments.length?(i=Ie(n=+a[0][0],t=+a[0][1],e=+a[1][0],r=+a[1][1]),u&&(u.valid=!1,u=null),o):[[n,t],[e,r]]}};return o.extent([[0,0],[960,500]])},(ta.geo.conicEqualArea=function(){return Ye(Ze)}).raw=Ze,ta.geo.albers=function(){return ta.geo.conicEqualArea().rotate([96,0]).center([-.6,38.7]).parallels([29.5,45.5]).scale(1070)},ta.geo.albersUsa=function(){function n(n){var i=n[0],o=n[1];return t=null,e(i,o),t||(r(i,o),t)||u(i,o),t}var t,e,r,u,i=ta.geo.albers(),o=ta.geo.conicEqualArea().rotate([154,0]).center([-2,58.5]).parallels([55,65]),a=ta.geo.conicEqualArea().rotate([157,0]).center([-3,19.9]).parallels([8,18]),c={point:function(n,e){t=[n,e]}};return n.invert=function(n){var t=i.scale(),e=i.translate(),r=(n[0]-e[0])/t,u=(n[1]-e[1])/t;return(u>=.12&&.234>u&&r>=-.425&&-.214>r?o:u>=.166&&.234>u&&r>=-.214&&-.115>r?a:i).invert(n)},n.stream=function(n){var t=i.stream(n),e=o.stream(n),r=a.stream(n);return{point:function(n,u){t.point(n,u),e.point(n,u),r.point(n,u)},sphere:function(){t.sphere(),e.sphere(),r.sphere()},lineStart:function(){t.lineStart(),e.lineStart(),r.lineStart()},lineEnd:function(){t.lineEnd(),e.lineEnd(),r.lineEnd()},polygonStart:function(){t.polygonStart(),e.polygonStart(),r.polygonStart()},polygonEnd:function(){t.polygonEnd(),e.polygonEnd(),r.polygonEnd()}}},n.precision=function(t){return arguments.length?(i.precision(t),o.precision(t),a.precision(t),n):i.precision()},n.scale=function(t){return arguments.length?(i.scale(t),o.scale(.35*t),a.scale(t),n.translate(i.translate())):i.scale()},n.translate=function(t){if(!arguments.length)return i.translate();var l=i.scale(),s=+t[0],f=+t[1];return e=i.translate(t).clipExtent([[s-.455*l,f-.238*l],[s+.455*l,f+.238*l]]).stream(c).point,r=o.translate([s-.307*l,f+.201*l]).clipExtent([[s-.425*l+Ca,f+.12*l+Ca],[s-.214*l-Ca,f+.234*l-Ca]]).stream(c).point,u=a.translate([s-.205*l,f+.212*l]).clipExtent([[s-.214*l+Ca,f+.166*l+Ca],[s-.115*l-Ca,f+.234*l-Ca]]).stream(c).point,n},n.scale(1070)};var Rc,Dc,Pc,Uc,jc,Fc,Hc={point:b,lineStart:b,lineEnd:b,polygonStart:function(){Dc=0,Hc.lineStart=Ve},polygonEnd:function(){Hc.lineStart=Hc.lineEnd=Hc.point=b,Rc+=ga(Dc/2)}},Oc={point:Xe,lineStart:b,lineEnd:b,polygonStart:b,polygonEnd:b},Ic={point:We,lineStart:Je,lineEnd:Ge,polygonStart:function(){Ic.lineStart=Ke},polygonEnd:function(){Ic.point=We,Ic.lineStart=Je,Ic.lineEnd=Ge}};ta.geo.path=function(){function n(n){return n&&("function"==typeof a&&i.pointRadius(+a.apply(this,arguments)),o&&o.valid||(o=u(i)),ta.geo.stream(n,o)),i.result()}function t(){return o=null,n}var e,r,u,i,o,a=4.5;return n.area=function(n){return Rc=0,ta.geo.stream(n,u(Hc)),Rc},n.centroid=function(n){return _c=wc=Sc=kc=Ec=Ac=Nc=Cc=zc=0,ta.geo.stream(n,u(Ic)),zc?[Nc/zc,Cc/zc]:Ac?[kc/Ac,Ec/Ac]:Sc?[_c/Sc,wc/Sc]:[0/0,0/0]},n.bounds=function(n){return jc=Fc=-(Pc=Uc=1/0),ta.geo.stream(n,u(Oc)),[[Pc,Uc],[jc,Fc]]},n.projection=function(n){return arguments.length?(u=(e=n)?n.stream||tr(n):y,t()):e},n.context=function(n){return arguments.length?(i=null==(r=n)?new $e:new Qe(n),"function"!=typeof a&&i.pointRadius(a),t()):r},n.pointRadius=function(t){return arguments.length?(a="function"==typeof t?t:(i.pointRadius(+t),+t),n):a},n.projection(ta.geo.albersUsa()).context(null)},ta.geo.transform=function(n){return{stream:function(t){var e=new er(t);for(var r in n)e[r]=n[r];return e}}},er.prototype={point:function(n,t){this.stream.point(n,t)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}},ta.geo.projection=ur,ta.geo.projectionMutator=ir,(ta.geo.equirectangular=function(){return ur(ar)}).raw=ar.invert=ar,ta.geo.rotation=function(n){function t(t){return t=n(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t}return n=lr(n[0]%360*Da,n[1]*Da,n.length>2?n[2]*Da:0),t.invert=function(t){return t=n.invert(t[0]*Da,t[1]*Da),t[0]*=Pa,t[1]*=Pa,t},t},cr.invert=ar,ta.geo.circle=function(){function n(){var n="function"==typeof r?r.apply(this,arguments):r,t=lr(-n[0]*Da,-n[1]*Da,0).invert,u=[];return e(null,null,1,{point:function(n,e){u.push(n=t(n,e)),n[0]*=Pa,n[1]*=Pa}}),{type:"Polygon",coordinates:[u]}}var t,e,r=[0,0],u=6;return n.origin=function(t){return arguments.length?(r=t,n):r},n.angle=function(r){return arguments.length?(e=gr((t=+r)*Da,u*Da),n):t},n.precision=function(r){return arguments.length?(e=gr(t*Da,(u=+r)*Da),n):u},n.angle(90)},ta.geo.distance=function(n,t){var e,r=(t[0]-n[0])*Da,u=n[1]*Da,i=t[1]*Da,o=Math.sin(r),a=Math.cos(r),c=Math.sin(u),l=Math.cos(u),s=Math.sin(i),f=Math.cos(i);return Math.atan2(Math.sqrt((e=f*o)*e+(e=l*s-c*f*a)*e),c*s+l*f*a)},ta.geo.graticule=function(){function n(){return{type:"MultiLineString",coordinates:t()}}function t(){return ta.range(Math.ceil(i/d)*d,u,d).map(h).concat(ta.range(Math.ceil(l/m)*m,c,m).map(g)).concat(ta.range(Math.ceil(r/p)*p,e,p).filter(function(n){return ga(n%d)>Ca}).map(s)).concat(ta.range(Math.ceil(a/v)*v,o,v).filter(function(n){return ga(n%m)>Ca}).map(f))}var e,r,u,i,o,a,c,l,s,f,h,g,p=10,v=p,d=90,m=360,y=2.5;return n.lines=function(){return t().map(function(n){return{type:"LineString",coordinates:n}})},n.outline=function(){return{type:"Polygon",coordinates:[h(i).concat(g(c).slice(1),h(u).reverse().slice(1),g(l).reverse().slice(1))]}},n.extent=function(t){return arguments.length?n.majorExtent(t).minorExtent(t):n.minorExtent()},n.majorExtent=function(t){return arguments.length?(i=+t[0][0],u=+t[1][0],l=+t[0][1],c=+t[1][1],i>u&&(t=i,i=u,u=t),l>c&&(t=l,l=c,c=t),n.precision(y)):[[i,l],[u,c]]},n.minorExtent=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],a=+t[0][1],o=+t[1][1],r>e&&(t=r,r=e,e=t),a>o&&(t=a,a=o,o=t),n.precision(y)):[[r,a],[e,o]]},n.step=function(t){return arguments.length?n.majorStep(t).minorStep(t):n.minorStep()},n.majorStep=function(t){return arguments.length?(d=+t[0],m=+t[1],n):[d,m]},n.minorStep=function(t){return arguments.length?(p=+t[0],v=+t[1],n):[p,v]},n.precision=function(t){return arguments.length?(y=+t,s=vr(a,o,90),f=dr(r,e,y),h=vr(l,c,90),g=dr(i,u,y),n):y},n.majorExtent([[-180,-90+Ca],[180,90-Ca]]).minorExtent([[-180,-80-Ca],[180,80+Ca]])},ta.geo.greatArc=function(){function n(){return{type:"LineString",coordinates:[t||r.apply(this,arguments),e||u.apply(this,arguments)]}}var t,e,r=mr,u=yr;return n.distance=function(){return ta.geo.distance(t||r.apply(this,arguments),e||u.apply(this,arguments))},n.source=function(e){return arguments.length?(r=e,t="function"==typeof e?null:e,n):r},n.target=function(t){return arguments.length?(u=t,e="function"==typeof t?null:t,n):u},n.precision=function(){return arguments.length?n:0},n},ta.geo.interpolate=function(n,t){return Mr(n[0]*Da,n[1]*Da,t[0]*Da,t[1]*Da)},ta.geo.length=function(n){return Yc=0,ta.geo.stream(n,Zc),Yc};var Yc,Zc={sphere:b,point:b,lineStart:xr,lineEnd:b,polygonStart:b,polygonEnd:b},Vc=br(function(n){return Math.sqrt(2/(1+n))},function(n){return 2*Math.asin(n/2)});(ta.geo.azimuthalEqualArea=function(){return ur(Vc)}).raw=Vc;var Xc=br(function(n){var t=Math.acos(n);return t&&t/Math.sin(t)},y);(ta.geo.azimuthalEquidistant=function(){return ur(Xc)}).raw=Xc,(ta.geo.conicConformal=function(){return Ye(_r)}).raw=_r,(ta.geo.conicEquidistant=function(){return Ye(wr)}).raw=wr;var $c=br(function(n){return 1/n},Math.atan);(ta.geo.gnomonic=function(){return ur($c)}).raw=$c,Sr.invert=function(n,t){return[n,2*Math.atan(Math.exp(t))-Ra]},(ta.geo.mercator=function(){return kr(Sr)}).raw=Sr;var Bc=br(function(){return 1},Math.asin);(ta.geo.orthographic=function(){return ur(Bc)}).raw=Bc;var Wc=br(function(n){return 1/(1+n)},function(n){return 2*Math.atan(n)});(ta.geo.stereographic=function(){return ur(Wc)}).raw=Wc,Er.invert=function(n,t){return[-t,2*Math.atan(Math.exp(n))-Ra]},(ta.geo.transverseMercator=function(){var n=kr(Er),t=n.center,e=n.rotate;return n.center=function(n){return n?t([-n[1],n[0]]):(n=t(),[n[1],-n[0]])},n.rotate=function(n){return n?e([n[0],n[1],n.length>2?n[2]+90:90]):(n=e(),[n[0],n[1],n[2]-90])},e([0,0,90])}).raw=Er,ta.geom={},ta.geom.hull=function(n){function t(n){if(n.length<3)return[];var t,u=Et(e),i=Et(r),o=n.length,a=[],c=[];for(t=0;o>t;t++)a.push([+u.call(this,n[t],t),+i.call(this,n[t],t),t]);for(a.sort(zr),t=0;o>t;t++)c.push([a[t][0],-a[t][1]]);var l=Cr(a),s=Cr(c),f=s[0]===l[0],h=s[s.length-1]===l[l.length-1],g=[];for(t=l.length-1;t>=0;--t)g.push(n[a[l[t]][2]]);for(t=+f;t=r&&l.x<=i&&l.y>=u&&l.y<=o?[[r,o],[i,o],[i,u],[r,u]]:[];s.point=n[a]}),t}function e(n){return n.map(function(n,t){return{x:Math.round(i(n,t)/Ca)*Ca,y:Math.round(o(n,t)/Ca)*Ca,i:t}})}var r=Ar,u=Nr,i=r,o=u,a=ul;return n?t(n):(t.links=function(n){return iu(e(n)).edges.filter(function(n){return n.l&&n.r}).map(function(t){return{source:n[t.l.i],target:n[t.r.i]}})},t.triangles=function(n){var t=[];return iu(e(n)).cells.forEach(function(e,r){for(var u,i,o=e.site,a=e.edges.sort(Yr),c=-1,l=a.length,s=a[l-1].edge,f=s.l===o?s.r:s.l;++c=l,h=r>=s,g=h<<1|f;n.leaf=!1,n=n.nodes[g]||(n.nodes[g]=su()),f?u=l:a=l,h?o=s:c=s,i(n,t,e,r,u,o,a,c)}var s,f,h,g,p,v,d,m,y,M=Et(a),x=Et(c);if(null!=t)v=t,d=e,m=r,y=u;else if(m=y=-(v=d=1/0),f=[],h=[],p=n.length,o)for(g=0;p>g;++g)s=n[g],s.xm&&(m=s.x),s.y>y&&(y=s.y),f.push(s.x),h.push(s.y);else for(g=0;p>g;++g){var b=+M(s=n[g],g),_=+x(s,g);v>b&&(v=b),d>_&&(d=_),b>m&&(m=b),_>y&&(y=_),f.push(b),h.push(_)}var w=m-v,S=y-d;w>S?y=d+w:m=v+S;var k=su();if(k.add=function(n){i(k,n,+M(n,++g),+x(n,g),v,d,m,y)},k.visit=function(n){fu(n,k,v,d,m,y)},k.find=function(n){return hu(k,n[0],n[1],v,d,m,y)},g=-1,null==t){for(;++g=0?n.slice(0,t):n,r=t>=0?n.slice(t+1):"in";return e=cl.get(e)||al,r=ll.get(r)||y,Mu(r(e.apply(null,ea.call(arguments,1))))},ta.interpolateHcl=Lu,ta.interpolateHsl=Tu,ta.interpolateLab=Ru,ta.interpolateRound=Du,ta.transform=function(n){var t=ua.createElementNS(ta.ns.prefix.svg,"g");return(ta.transform=function(n){if(null!=n){t.setAttribute("transform",n);var e=t.transform.baseVal.consolidate()}return new Pu(e?e.matrix:sl)})(n)},Pu.prototype.toString=function(){return"translate("+this.translate+")rotate("+this.rotate+")skewX("+this.skew+")scale("+this.scale+")"};var sl={a:1,b:0,c:0,d:1,e:0,f:0};ta.interpolateTransform=Hu,ta.layout={},ta.layout.bundle=function(){return function(n){for(var t=[],e=-1,r=n.length;++ea*a/d){if(p>c){var l=t.charge/c;n.px-=i*l,n.py-=o*l}return!0}if(t.point&&c&&p>c){var l=t.pointCharge/c;n.px-=i*l,n.py-=o*l}}return!t.charge}}function t(n){n.px=ta.event.x,n.py=ta.event.y,a.resume()}var e,r,u,i,o,a={},c=ta.dispatch("start","tick","end"),l=[1,1],s=.9,f=fl,h=hl,g=-30,p=gl,v=.1,d=.64,m=[],M=[];return a.tick=function(){if((r*=.99)<.005)return c.end({type:"end",alpha:r=0}),!0;var t,e,a,f,h,p,d,y,x,b=m.length,_=M.length;for(e=0;_>e;++e)a=M[e],f=a.source,h=a.target,y=h.x-f.x,x=h.y-f.y,(p=y*y+x*x)&&(p=r*i[e]*((p=Math.sqrt(p))-u[e])/p,y*=p,x*=p,h.x-=y*(d=f.weight/(h.weight+f.weight)),h.y-=x*d,f.x+=y*(d=1-d),f.y+=x*d);if((d=r*v)&&(y=l[0]/2,x=l[1]/2,e=-1,d))for(;++e0?n:0:n>0&&(c.start({type:"start",alpha:r=n}),ta.timer(a.tick)),a):r},a.start=function(){function n(n,r){if(!e){for(e=new Array(c),a=0;c>a;++a)e[a]=[];for(a=0;s>a;++a){var u=M[a];e[u.source.index].push(u.target),e[u.target.index].push(u.source)}}for(var i,o=e[t],a=-1,l=o.length;++at;++t)(r=m[t]).index=t,r.weight=0;for(t=0;s>t;++t)r=M[t],"number"==typeof r.source&&(r.source=m[r.source]),"number"==typeof r.target&&(r.target=m[r.target]),++r.source.weight,++r.target.weight;for(t=0;c>t;++t)r=m[t],isNaN(r.x)&&(r.x=n("x",p)),isNaN(r.y)&&(r.y=n("y",v)),isNaN(r.px)&&(r.px=r.x),isNaN(r.py)&&(r.py=r.y);if(u=[],"function"==typeof f)for(t=0;s>t;++t)u[t]=+f.call(this,M[t],t);else for(t=0;s>t;++t)u[t]=f;if(i=[],"function"==typeof h)for(t=0;s>t;++t)i[t]=+h.call(this,M[t],t);else for(t=0;s>t;++t)i[t]=h;if(o=[],"function"==typeof g)for(t=0;c>t;++t)o[t]=+g.call(this,m[t],t);else for(t=0;c>t;++t)o[t]=g;return a.resume()},a.resume=function(){return a.alpha(.1)},a.stop=function(){return a.alpha(0)},a.drag=function(){return e||(e=ta.behavior.drag().origin(y).on("dragstart.force",Xu).on("drag.force",t).on("dragend.force",$u)),arguments.length?void this.on("mouseover.force",Bu).on("mouseout.force",Wu).call(e):e},ta.rebind(a,c,"on")};var fl=20,hl=1,gl=1/0;ta.layout.hierarchy=function(){function n(u){var i,o=[u],a=[];for(u.depth=0;null!=(i=o.pop());)if(a.push(i),(l=e.call(n,i,i.depth))&&(c=l.length)){for(var c,l,s;--c>=0;)o.push(s=l[c]),s.parent=i,s.depth=i.depth+1;r&&(i.value=0),i.children=l}else r&&(i.value=+r.call(n,i,i.depth)||0),delete i.children;return Qu(u,function(n){var e,u;t&&(e=n.children)&&e.sort(t),r&&(u=n.parent)&&(u.value+=n.value)}),a}var t=ei,e=ni,r=ti;return n.sort=function(e){return arguments.length?(t=e,n):t},n.children=function(t){return arguments.length?(e=t,n):e},n.value=function(t){return arguments.length?(r=t,n):r},n.revalue=function(t){return r&&(Ku(t,function(n){n.children&&(n.value=0)}),Qu(t,function(t){var e;t.children||(t.value=+r.call(n,t,t.depth)||0),(e=t.parent)&&(e.value+=t.value)})),t},n},ta.layout.partition=function(){function n(t,e,r,u){var i=t.children;if(t.x=e,t.y=t.depth*u,t.dx=r,t.dy=u,i&&(o=i.length)){var o,a,c,l=-1;for(r=t.value?r/t.value:0;++lf?-1:1),p=(f-c*g)/ta.sum(l),v=ta.range(c),d=[];return null!=e&&v.sort(e===pl?function(n,t){return l[t]-l[n]}:function(n,t){return e(o[n],o[t])}),v.forEach(function(n){d[n]={data:o[n],value:a=l[n],startAngle:s,endAngle:s+=a*p+g,padAngle:h}}),d}var t=Number,e=pl,r=0,u=La,i=0;return n.value=function(e){return arguments.length?(t=e,n):t},n.sort=function(t){return arguments.length?(e=t,n):e},n.startAngle=function(t){return arguments.length?(r=t,n):r},n.endAngle=function(t){return arguments.length?(u=t,n):u},n.padAngle=function(t){return arguments.length?(i=t,n):i},n};var pl={};ta.layout.stack=function(){function n(a,c){if(!(h=a.length))return a;var l=a.map(function(e,r){return t.call(n,e,r)}),s=l.map(function(t){return t.map(function(t,e){return[i.call(n,t,e),o.call(n,t,e)]})}),f=e.call(n,s,c);l=ta.permute(l,f),s=ta.permute(s,f);var h,g,p,v,d=r.call(n,s,c),m=l[0].length;for(p=0;m>p;++p)for(u.call(n,l[0][p],v=d[p],s[0][p][1]),g=1;h>g;++g)u.call(n,l[g][p],v+=s[g-1][p][1],s[g][p][1]);return a}var t=y,e=ai,r=ci,u=oi,i=ui,o=ii;return n.values=function(e){return arguments.length?(t=e,n):t},n.order=function(t){return arguments.length?(e="function"==typeof t?t:vl.get(t)||ai,n):e},n.offset=function(t){return arguments.length?(r="function"==typeof t?t:dl.get(t)||ci,n):r},n.x=function(t){return arguments.length?(i=t,n):i},n.y=function(t){return arguments.length?(o=t,n):o},n.out=function(t){return arguments.length?(u=t,n):u},n};var vl=ta.map({"inside-out":function(n){var t,e,r=n.length,u=n.map(li),i=n.map(si),o=ta.range(r).sort(function(n,t){return u[n]-u[t]}),a=0,c=0,l=[],s=[];for(t=0;r>t;++t)e=o[t],c>a?(a+=i[e],l.push(e)):(c+=i[e],s.push(e));return s.reverse().concat(l)},reverse:function(n){return ta.range(n.length).reverse()},"default":ai}),dl=ta.map({silhouette:function(n){var t,e,r,u=n.length,i=n[0].length,o=[],a=0,c=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];r>a&&(a=r),o.push(r)}for(e=0;i>e;++e)c[e]=(a-o[e])/2;return c},wiggle:function(n){var t,e,r,u,i,o,a,c,l,s=n.length,f=n[0],h=f.length,g=[];for(g[0]=c=l=0,e=1;h>e;++e){for(t=0,u=0;s>t;++t)u+=n[t][e][1];for(t=0,i=0,a=f[e][0]-f[e-1][0];s>t;++t){for(r=0,o=(n[t][e][1]-n[t][e-1][1])/(2*a);t>r;++r)o+=(n[r][e][1]-n[r][e-1][1])/a;i+=o*n[t][e][1]}g[e]=c-=u?i/u*a:0,l>c&&(l=c)}for(e=0;h>e;++e)g[e]-=l;return g},expand:function(n){var t,e,r,u=n.length,i=n[0].length,o=1/u,a=[];for(e=0;i>e;++e){for(t=0,r=0;u>t;t++)r+=n[t][e][1];if(r)for(t=0;u>t;t++)n[t][e][1]/=r;else for(t=0;u>t;t++)n[t][e][1]=o}for(e=0;i>e;++e)a[e]=0;return a},zero:ci});ta.layout.histogram=function(){function n(n,i){for(var o,a,c=[],l=n.map(e,this),s=r.call(this,l,i),f=u.call(this,s,l,i),i=-1,h=l.length,g=f.length-1,p=t?1:1/h;++i0)for(i=-1;++i=s[0]&&a<=s[1]&&(o=c[ta.bisect(f,a,1,g)-1],o.y+=p,o.push(n[i]));return c}var t=!0,e=Number,r=pi,u=hi;return n.value=function(t){return arguments.length?(e=t,n):e},n.range=function(t){return arguments.length?(r=Et(t),n):r},n.bins=function(t){return arguments.length?(u="number"==typeof t?function(n){return gi(n,t)}:Et(t),n):u},n.frequency=function(e){return arguments.length?(t=!!e,n):t},n},ta.layout.pack=function(){function n(n,i){var o=e.call(this,n,i),a=o[0],c=u[0],l=u[1],s=null==t?Math.sqrt:"function"==typeof t?t:function(){return t};if(a.x=a.y=0,Qu(a,function(n){n.r=+s(n.value)}),Qu(a,Mi),r){var f=r*(t?1:Math.max(2*a.r/c,2*a.r/l))/2;Qu(a,function(n){n.r+=f}),Qu(a,Mi),Qu(a,function(n){n.r-=f})}return _i(a,c/2,l/2,t?1:1/Math.max(2*a.r/c,2*a.r/l)),o}var t,e=ta.layout.hierarchy().sort(vi),r=0,u=[1,1];return n.size=function(t){return arguments.length?(u=t,n):u},n.radius=function(e){return arguments.length?(t=null==e||"function"==typeof e?e:+e,n):t},n.padding=function(t){return arguments.length?(r=+t,n):r},Gu(n,e)},ta.layout.tree=function(){function n(n,u){var s=o.call(this,n,u),f=s[0],h=t(f);if(Qu(h,e),h.parent.m=-h.z,Ku(h,r),l)Ku(f,i);else{var g=f,p=f,v=f;Ku(f,function(n){n.xp.x&&(p=n),n.depth>v.depth&&(v=n)});var d=a(g,p)/2-g.x,m=c[0]/(p.x+a(p,g)/2+d),y=c[1]/(v.depth||1);Ku(f,function(n){n.x=(n.x+d)*m,n.y=n.depth*y})}return s}function t(n){for(var t,e={A:null,children:[n]},r=[e];null!=(t=r.pop());)for(var u,i=t.children,o=0,a=i.length;a>o;++o)r.push((i[o]=u={_:i[o],parent:t,children:(u=i[o].children)&&u.slice()||[],A:null,a:null,z:0,m:0,c:0,s:0,t:null,i:o}).a=u);return e.children[0]}function e(n){var t=n.children,e=n.parent.children,r=n.i?e[n.i-1]:null;if(t.length){Ni(n);var i=(t[0].z+t[t.length-1].z)/2;r?(n.z=r.z+a(n._,r._),n.m=n.z-i):n.z=i}else r&&(n.z=r.z+a(n._,r._));n.parent.A=u(n,r,n.parent.A||e[0])}function r(n){n._.x=n.z+n.parent.m,n.m+=n.parent.m}function u(n,t,e){if(t){for(var r,u=n,i=n,o=t,c=u.parent.children[0],l=u.m,s=i.m,f=o.m,h=c.m;o=Ei(o),u=ki(u),o&&u;)c=ki(c),i=Ei(i),i.a=n,r=o.z+f-u.z-l+a(o._,u._),r>0&&(Ai(Ci(o,n,e),n,r),l+=r,s+=r),f+=o.m,l+=u.m,h+=c.m,s+=i.m;o&&!Ei(i)&&(i.t=o,i.m+=f-s),u&&!ki(c)&&(c.t=u,c.m+=l-h,e=n)}return e}function i(n){n.x*=c[0],n.y=n.depth*c[1]}var o=ta.layout.hierarchy().sort(null).value(null),a=Si,c=[1,1],l=null;return n.separation=function(t){return arguments.length?(a=t,n):a},n.size=function(t){return arguments.length?(l=null==(c=t)?i:null,n):l?null:c},n.nodeSize=function(t){return arguments.length?(l=null==(c=t)?null:i,n):l?c:null},Gu(n,o)},ta.layout.cluster=function(){function n(n,i){var o,a=t.call(this,n,i),c=a[0],l=0;Qu(c,function(n){var t=n.children;t&&t.length?(n.x=qi(t),n.y=zi(t)):(n.x=o?l+=e(n,o):0,n.y=0,o=n)});var s=Li(c),f=Ti(c),h=s.x-e(s,f)/2,g=f.x+e(f,s)/2;return Qu(c,u?function(n){n.x=(n.x-c.x)*r[0],n.y=(c.y-n.y)*r[1]}:function(n){n.x=(n.x-h)/(g-h)*r[0],n.y=(1-(c.y?n.y/c.y:1))*r[1]}),a}var t=ta.layout.hierarchy().sort(null).value(null),e=Si,r=[1,1],u=!1;return n.separation=function(t){return arguments.length?(e=t,n):e},n.size=function(t){return arguments.length?(u=null==(r=t),n):u?null:r},n.nodeSize=function(t){return arguments.length?(u=null!=(r=t),n):u?r:null},Gu(n,t)},ta.layout.treemap=function(){function n(n,t){for(var e,r,u=-1,i=n.length;++ut?0:t),e.area=isNaN(r)||0>=r?0:r}function t(e){var i=e.children;if(i&&i.length){var o,a,c,l=f(e),s=[],h=i.slice(),p=1/0,v="slice"===g?l.dx:"dice"===g?l.dy:"slice-dice"===g?1&e.depth?l.dy:l.dx:Math.min(l.dx,l.dy);for(n(h,l.dx*l.dy/e.value),s.area=0;(c=h.length)>0;)s.push(o=h[c-1]),s.area+=o.area,"squarify"!==g||(a=r(s,v))<=p?(h.pop(),p=a):(s.area-=s.pop().area,u(s,v,l,!1),v=Math.min(l.dx,l.dy),s.length=s.area=0,p=1/0);s.length&&(u(s,v,l,!0),s.length=s.area=0),i.forEach(t)}}function e(t){var r=t.children;if(r&&r.length){var i,o=f(t),a=r.slice(),c=[];for(n(a,o.dx*o.dy/t.value),c.area=0;i=a.pop();)c.push(i),c.area+=i.area,null!=i.z&&(u(c,i.z?o.dx:o.dy,o,!a.length),c.length=c.area=0);r.forEach(e)}}function r(n,t){for(var e,r=n.area,u=0,i=1/0,o=-1,a=n.length;++oe&&(i=e),e>u&&(u=e));return r*=r,t*=t,r?Math.max(t*u*p/r,r/(t*i*p)):1/0}function u(n,t,e,r){var u,i=-1,o=n.length,a=e.x,l=e.y,s=t?c(n.area/t):0;if(t==e.dx){for((r||s>e.dy)&&(s=e.dy);++ie.dx)&&(s=e.dx);++ie&&(t=1),1>e&&(n=0),function(){var e,r,u;do e=2*Math.random()-1,r=2*Math.random()-1,u=e*e+r*r;while(!u||u>1);return n+t*e*Math.sqrt(-2*Math.log(u)/u)}},logNormal:function(){var n=ta.random.normal.apply(ta,arguments);return function(){return Math.exp(n())}},bates:function(n){var t=ta.random.irwinHall(n);return function(){return t()/n}},irwinHall:function(n){return function(){for(var t=0,e=0;n>e;e++)t+=Math.random();return t}}},ta.scale={};var ml={floor:y,ceil:y};ta.scale.linear=function(){return Ii([0,1],[0,1],mu,!1)};var yl={s:1,g:1,p:1,r:1,e:1};ta.scale.log=function(){return Ji(ta.scale.linear().domain([0,1]),10,!0,[1,10])};var Ml=ta.format(".0e"),xl={floor:function(n){return-Math.ceil(-n)},ceil:function(n){return-Math.floor(-n)}};ta.scale.pow=function(){return Gi(ta.scale.linear(),1,[0,1])},ta.scale.sqrt=function(){return ta.scale.pow().exponent(.5)},ta.scale.ordinal=function(){return Qi([],{t:"range",a:[[]]})},ta.scale.category10=function(){return ta.scale.ordinal().range(bl)},ta.scale.category20=function(){return ta.scale.ordinal().range(_l)},ta.scale.category20b=function(){return ta.scale.ordinal().range(wl)},ta.scale.category20c=function(){return ta.scale.ordinal().range(Sl)};var bl=[2062260,16744206,2924588,14034728,9725885,9197131,14907330,8355711,12369186,1556175].map(Mt),_l=[2062260,11454440,16744206,16759672,2924588,10018698,14034728,16750742,9725885,12955861,9197131,12885140,14907330,16234194,8355711,13092807,12369186,14408589,1556175,10410725].map(Mt),wl=[3750777,5395619,7040719,10264286,6519097,9216594,11915115,13556636,9202993,12426809,15186514,15190932,8666169,11356490,14049643,15177372,8077683,10834324,13528509,14589654].map(Mt),Sl=[3244733,7057110,10406625,13032431,15095053,16616764,16625259,16634018,3253076,7652470,10607003,13101504,7695281,10394312,12369372,14342891,6513507,9868950,12434877,14277081].map(Mt);ta.scale.quantile=function(){return no([],[])},ta.scale.quantize=function(){return to(0,1,[0,1])},ta.scale.threshold=function(){return eo([.5],[0,1])},ta.scale.identity=function(){return ro([0,1])},ta.svg={},ta.svg.arc=function(){function n(){var n=Math.max(0,+e.apply(this,arguments)),l=Math.max(0,+r.apply(this,arguments)),s=o.apply(this,arguments)-Ra,f=a.apply(this,arguments)-Ra,h=Math.abs(f-s),g=s>f?0:1;if(n>l&&(p=l,l=n,n=p),h>=Ta)return t(l,g)+(n?t(n,1-g):"")+"Z";var p,v,d,m,y,M,x,b,_,w,S,k,E=0,A=0,N=[];if((m=(+c.apply(this,arguments)||0)/2)&&(d=i===kl?Math.sqrt(n*n+l*l):+i.apply(this,arguments),g||(A*=-1),l&&(A=tt(d/l*Math.sin(m))),n&&(E=tt(d/n*Math.sin(m)))),l){y=l*Math.cos(s+A),M=l*Math.sin(s+A),x=l*Math.cos(f-A),b=l*Math.sin(f-A);var C=Math.abs(f-s-2*A)<=qa?0:1;if(A&&so(y,M,x,b)===g^C){var z=(s+f)/2;y=l*Math.cos(z),M=l*Math.sin(z),x=b=null}}else y=M=0;if(n){_=n*Math.cos(f-E),w=n*Math.sin(f-E),S=n*Math.cos(s+E),k=n*Math.sin(s+E);var q=Math.abs(s-f+2*E)<=qa?0:1;if(E&&so(_,w,S,k)===1-g^q){var L=(s+f)/2;_=n*Math.cos(L),w=n*Math.sin(L),S=k=null}}else _=w=0;if((p=Math.min(Math.abs(l-n)/2,+u.apply(this,arguments)))>.001){v=l>n^g?0:1;var T=null==S?[_,w]:null==x?[y,M]:Lr([y,M],[S,k],[x,b],[_,w]),R=y-T[0],D=M-T[1],P=x-T[0],U=b-T[1],j=1/Math.sin(Math.acos((R*P+D*U)/(Math.sqrt(R*R+D*D)*Math.sqrt(P*P+U*U)))/2),F=Math.sqrt(T[0]*T[0]+T[1]*T[1]);if(null!=x){var H=Math.min(p,(l-F)/(j+1)),O=fo(null==S?[_,w]:[S,k],[y,M],l,H,g),I=fo([x,b],[_,w],l,H,g);p===H?N.push("M",O[0],"A",H,",",H," 0 0,",v," ",O[1],"A",l,",",l," 0 ",1-g^so(O[1][0],O[1][1],I[1][0],I[1][1]),",",g," ",I[1],"A",H,",",H," 0 0,",v," ",I[0]):N.push("M",O[0],"A",H,",",H," 0 1,",v," ",I[0])}else N.push("M",y,",",M);if(null!=S){var Y=Math.min(p,(n-F)/(j-1)),Z=fo([y,M],[S,k],n,-Y,g),V=fo([_,w],null==x?[y,M]:[x,b],n,-Y,g);p===Y?N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",V[1],"A",n,",",n," 0 ",g^so(V[1][0],V[1][1],Z[1][0],Z[1][1]),",",1-g," ",Z[1],"A",Y,",",Y," 0 0,",v," ",Z[0]):N.push("L",V[0],"A",Y,",",Y," 0 0,",v," ",Z[0])}else N.push("L",_,",",w)}else N.push("M",y,",",M),null!=x&&N.push("A",l,",",l," 0 ",C,",",g," ",x,",",b),N.push("L",_,",",w),null!=S&&N.push("A",n,",",n," 0 ",q,",",1-g," ",S,",",k);return N.push("Z"),N.join("")}function t(n,t){return"M0,"+n+"A"+n+","+n+" 0 1,"+t+" 0,"+-n+"A"+n+","+n+" 0 1,"+t+" 0,"+n}var e=io,r=oo,u=uo,i=kl,o=ao,a=co,c=lo;return n.innerRadius=function(t){return arguments.length?(e=Et(t),n):e},n.outerRadius=function(t){return arguments.length?(r=Et(t),n):r},n.cornerRadius=function(t){return arguments.length?(u=Et(t),n):u},n.padRadius=function(t){return arguments.length?(i=t==kl?kl:Et(t),n):i},n.startAngle=function(t){return arguments.length?(o=Et(t),n):o},n.endAngle=function(t){return arguments.length?(a=Et(t),n):a},n.padAngle=function(t){return arguments.length?(c=Et(t),n):c},n.centroid=function(){var n=(+e.apply(this,arguments)+ +r.apply(this,arguments))/2,t=(+o.apply(this,arguments)+ +a.apply(this,arguments))/2-Ra;return[Math.cos(t)*n,Math.sin(t)*n]},n};var kl="auto";ta.svg.line=function(){return ho(y)};var El=ta.map({linear:go,"linear-closed":po,step:vo,"step-before":mo,"step-after":yo,basis:So,"basis-open":ko,"basis-closed":Eo,bundle:Ao,cardinal:bo,"cardinal-open":Mo,"cardinal-closed":xo,monotone:To});El.forEach(function(n,t){t.key=n,t.closed=/-closed$/.test(n)});var Al=[0,2/3,1/3,0],Nl=[0,1/3,2/3,0],Cl=[0,1/6,2/3,1/6];ta.svg.line.radial=function(){var n=ho(Ro);return n.radius=n.x,delete n.x,n.angle=n.y,delete n.y,n},mo.reverse=yo,yo.reverse=mo,ta.svg.area=function(){return Do(y)},ta.svg.area.radial=function(){var n=Do(Ro);return n.radius=n.x,delete n.x,n.innerRadius=n.x0,delete n.x0,n.outerRadius=n.x1,delete n.x1,n.angle=n.y,delete n.y,n.startAngle=n.y0,delete n.y0,n.endAngle=n.y1,delete n.y1,n},ta.svg.chord=function(){function n(n,a){var c=t(this,i,n,a),l=t(this,o,n,a);return"M"+c.p0+r(c.r,c.p1,c.a1-c.a0)+(e(c,l)?u(c.r,c.p1,c.r,c.p0):u(c.r,c.p1,l.r,l.p0)+r(l.r,l.p1,l.a1-l.a0)+u(l.r,l.p1,c.r,c.p0))+"Z"}function t(n,t,e,r){var u=t.call(n,e,r),i=a.call(n,u,r),o=c.call(n,u,r)-Ra,s=l.call(n,u,r)-Ra;return{r:i,a0:o,a1:s,p0:[i*Math.cos(o),i*Math.sin(o)],p1:[i*Math.cos(s),i*Math.sin(s)]}}function e(n,t){return n.a0==t.a0&&n.a1==t.a1}function r(n,t,e){return"A"+n+","+n+" 0 "+ +(e>qa)+",1 "+t}function u(n,t,e,r){return"Q 0,0 "+r}var i=mr,o=yr,a=Po,c=ao,l=co;return n.radius=function(t){return arguments.length?(a=Et(t),n):a},n.source=function(t){return arguments.length?(i=Et(t),n):i},n.target=function(t){return arguments.length?(o=Et(t),n):o},n.startAngle=function(t){return arguments.length?(c=Et(t),n):c},n.endAngle=function(t){return arguments.length?(l=Et(t),n):l},n},ta.svg.diagonal=function(){function n(n,u){var i=t.call(this,n,u),o=e.call(this,n,u),a=(i.y+o.y)/2,c=[i,{x:i.x,y:a},{x:o.x,y:a},o];return c=c.map(r),"M"+c[0]+"C"+c[1]+" "+c[2]+" "+c[3]}var t=mr,e=yr,r=Uo;return n.source=function(e){return arguments.length?(t=Et(e),n):t},n.target=function(t){return arguments.length?(e=Et(t),n):e},n.projection=function(t){return arguments.length?(r=t,n):r},n},ta.svg.diagonal.radial=function(){var n=ta.svg.diagonal(),t=Uo,e=n.projection;return n.projection=function(n){return arguments.length?e(jo(t=n)):t},n},ta.svg.symbol=function(){function n(n,r){return(zl.get(t.call(this,n,r))||Oo)(e.call(this,n,r))}var t=Ho,e=Fo;return n.type=function(e){return arguments.length?(t=Et(e),n):t},n.size=function(t){return arguments.length?(e=Et(t),n):e},n};var zl=ta.map({circle:Oo,cross:function(n){var t=Math.sqrt(n/5)/2;return"M"+-3*t+","+-t+"H"+-t+"V"+-3*t+"H"+t+"V"+-t+"H"+3*t+"V"+t+"H"+t+"V"+3*t+"H"+-t+"V"+t+"H"+-3*t+"Z"},diamond:function(n){var t=Math.sqrt(n/(2*Ll)),e=t*Ll;return"M0,"+-t+"L"+e+",0 0,"+t+" "+-e+",0Z"},square:function(n){var t=Math.sqrt(n)/2;return"M"+-t+","+-t+"L"+t+","+-t+" "+t+","+t+" "+-t+","+t+"Z"},"triangle-down":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+e+"L"+t+","+-e+" "+-t+","+-e+"Z"},"triangle-up":function(n){var t=Math.sqrt(n/ql),e=t*ql/2;return"M0,"+-e+"L"+t+","+e+" "+-t+","+e+"Z"}});ta.svg.symbolTypes=zl.keys();var ql=Math.sqrt(3),Ll=Math.tan(30*Da);_a.transition=function(n){for(var t,e,r=Tl||++Ul,u=Xo(n),i=[],o=Rl||{time:Date.now(),ease:Su,delay:0,duration:250},a=-1,c=this.length;++ai;i++){u.push(t=[]);for(var e=this[i],a=0,c=e.length;c>a;a++)(r=e[a])&&n.call(r,r.__data__,a,i)&&t.push(r)}return Yo(u,this.namespace,this.id)},Pl.tween=function(n,t){var e=this.id,r=this.namespace;return arguments.length<2?this.node()[r][e].tween.get(n):Y(this,null==t?function(t){t[r][e].tween.remove(n)}:function(u){u[r][e].tween.set(n,t)})},Pl.attr=function(n,t){function e(){this.removeAttribute(a)}function r(){this.removeAttributeNS(a.space,a.local)}function u(n){return null==n?e:(n+="",function(){var t,e=this.getAttribute(a);return e!==n&&(t=o(e,n),function(n){this.setAttribute(a,t(n))})})}function i(n){return null==n?r:(n+="",function(){var t,e=this.getAttributeNS(a.space,a.local);return e!==n&&(t=o(e,n),function(n){this.setAttributeNS(a.space,a.local,t(n))})})}if(arguments.length<2){for(t in n)this.attr(t,n[t]);return this}var o="transform"==n?Hu:mu,a=ta.ns.qualify(n);return Zo(this,"attr."+n,t,a.local?i:u)},Pl.attrTween=function(n,t){function e(n,e){var r=t.call(this,n,e,this.getAttribute(u));return r&&function(n){this.setAttribute(u,r(n))}}function r(n,e){var r=t.call(this,n,e,this.getAttributeNS(u.space,u.local));return r&&function(n){this.setAttributeNS(u.space,u.local,r(n))}}var u=ta.ns.qualify(n);return this.tween("attr."+n,u.local?r:e)},Pl.style=function(n,e,r){function u(){this.style.removeProperty(n)}function i(e){return null==e?u:(e+="",function(){var u,i=t(this).getComputedStyle(this,null).getPropertyValue(n);return i!==e&&(u=mu(i,e),function(t){this.style.setProperty(n,u(t),r)})})}var o=arguments.length;if(3>o){if("string"!=typeof n){2>o&&(e="");for(r in n)this.style(r,n[r],e);return this}r=""}return Zo(this,"style."+n,e,i)},Pl.styleTween=function(n,e,r){function u(u,i){var o=e.call(this,u,i,t(this).getComputedStyle(this,null).getPropertyValue(n));return o&&function(t){this.style.setProperty(n,o(t),r)}}return arguments.length<3&&(r=""),this.tween("style."+n,u)},Pl.text=function(n){return Zo(this,"text",n,Vo)},Pl.remove=function(){var n=this.namespace;return this.each("end.transition",function(){var t;this[n].count<2&&(t=this.parentNode)&&t.removeChild(this)})},Pl.ease=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].ease:("function"!=typeof n&&(n=ta.ease.apply(ta,arguments)),Y(this,function(r){r[e][t].ease=n}))},Pl.delay=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].delay:Y(this,"function"==typeof n?function(r,u,i){r[e][t].delay=+n.call(r,r.__data__,u,i)}:(n=+n,function(r){r[e][t].delay=n}))},Pl.duration=function(n){var t=this.id,e=this.namespace;return arguments.length<1?this.node()[e][t].duration:Y(this,"function"==typeof n?function(r,u,i){r[e][t].duration=Math.max(1,n.call(r,r.__data__,u,i))}:(n=Math.max(1,n),function(r){r[e][t].duration=n}))},Pl.each=function(n,t){var e=this.id,r=this.namespace;if(arguments.length<2){var u=Rl,i=Tl;try{Tl=e,Y(this,function(t,u,i){Rl=t[r][e],n.call(t,t.__data__,u,i)})}finally{Rl=u,Tl=i}}else Y(this,function(u){var i=u[r][e];(i.event||(i.event=ta.dispatch("start","end","interrupt"))).on(n,t)});return this},Pl.transition=function(){for(var n,t,e,r,u=this.id,i=++Ul,o=this.namespace,a=[],c=0,l=this.length;l>c;c++){a.push(n=[]);for(var t=this[c],s=0,f=t.length;f>s;s++)(e=t[s])&&(r=e[o][u],$o(e,s,o,i,{time:r.time,ease:r.ease,delay:r.delay+r.duration,duration:r.duration})),n.push(e)}return Yo(a,o,i)},ta.svg.axis=function(){function n(n){n.each(function(){var n,l=ta.select(this),s=this.__chart__||e,f=this.__chart__=e.copy(),h=null==c?f.ticks?f.ticks.apply(f,a):f.domain():c,g=null==t?f.tickFormat?f.tickFormat.apply(f,a):y:t,p=l.selectAll(".tick").data(h,f),v=p.enter().insert("g",".domain").attr("class","tick").style("opacity",Ca),d=ta.transition(p.exit()).style("opacity",Ca).remove(),m=ta.transition(p.order()).style("opacity",1),M=Math.max(u,0)+o,x=Ui(f),b=l.selectAll(".domain").data([0]),_=(b.enter().append("path").attr("class","domain"),ta.transition(b));v.append("line"),v.append("text");var w,S,k,E,A=v.select("line"),N=m.select("line"),C=p.select("text").text(g),z=v.select("text"),q=m.select("text"),L="top"===r||"left"===r?-1:1;if("bottom"===r||"top"===r?(n=Bo,w="x",k="y",S="x2",E="y2",C.attr("dy",0>L?"0em":".71em").style("text-anchor","middle"),_.attr("d","M"+x[0]+","+L*i+"V0H"+x[1]+"V"+L*i)):(n=Wo,w="y",k="x",S="y2",E="x2",C.attr("dy",".32em").style("text-anchor",0>L?"end":"start"),_.attr("d","M"+L*i+","+x[0]+"H0V"+x[1]+"H"+L*i)),A.attr(E,L*u),z.attr(k,L*M),N.attr(S,0).attr(E,L*u),q.attr(w,0).attr(k,L*M),f.rangeBand){var T=f,R=T.rangeBand()/2;s=f=function(n){return T(n)+R}}else s.rangeBand?s=f:d.call(n,f,s);v.call(n,s,f),m.call(n,f,f)})}var t,e=ta.scale.linear(),r=jl,u=6,i=6,o=3,a=[10],c=null;return n.scale=function(t){return arguments.length?(e=t,n):e},n.orient=function(t){return arguments.length?(r=t in Fl?t+"":jl,n):r},n.ticks=function(){return arguments.length?(a=arguments,n):a},n.tickValues=function(t){return arguments.length?(c=t,n):c},n.tickFormat=function(e){return arguments.length?(t=e,n):t},n.tickSize=function(t){var e=arguments.length;return e?(u=+t,i=+arguments[e-1],n):u},n.innerTickSize=function(t){return arguments.length?(u=+t,n):u},n.outerTickSize=function(t){return arguments.length?(i=+t,n):i},n.tickPadding=function(t){return arguments.length?(o=+t,n):o},n.tickSubdivide=function(){return arguments.length&&n},n};var jl="bottom",Fl={top:1,right:1,bottom:1,left:1};ta.svg.brush=function(){function n(t){t.each(function(){var t=ta.select(this).style("pointer-events","all").style("-webkit-tap-highlight-color","rgba(0,0,0,0)").on("mousedown.brush",i).on("touchstart.brush",i),o=t.selectAll(".background").data([0]);o.enter().append("rect").attr("class","background").style("visibility","hidden").style("cursor","crosshair"),t.selectAll(".extent").data([0]).enter().append("rect").attr("class","extent").style("cursor","move");var a=t.selectAll(".resize").data(v,y);a.exit().remove(),a.enter().append("g").attr("class",function(n){return"resize "+n}).style("cursor",function(n){return Hl[n]}).append("rect").attr("x",function(n){return/[ew]$/.test(n)?-3:null}).attr("y",function(n){return/^[ns]/.test(n)?-3:null}).attr("width",6).attr("height",6).style("visibility","hidden"),a.style("display",n.empty()?"none":null);var c,f=ta.transition(t),h=ta.transition(o);l&&(c=Ui(l),h.attr("x",c[0]).attr("width",c[1]-c[0]),r(f)),s&&(c=Ui(s),h.attr("y",c[0]).attr("height",c[1]-c[0]),u(f)),e(f)})}function e(n){n.selectAll(".resize").attr("transform",function(n){return"translate("+f[+/e$/.test(n)]+","+h[+/^s/.test(n)]+")"})}function r(n){n.select(".extent").attr("x",f[0]),n.selectAll(".extent,.n>rect,.s>rect").attr("width",f[1]-f[0])}function u(n){n.select(".extent").attr("y",h[0]),n.selectAll(".extent,.e>rect,.w>rect").attr("height",h[1]-h[0])}function i(){function i(){32==ta.event.keyCode&&(C||(M=null,q[0]-=f[1],q[1]-=h[1],C=2),S())}function v(){32==ta.event.keyCode&&2==C&&(q[0]+=f[1],q[1]+=h[1],C=0,S())}function d(){var n=ta.mouse(b),t=!1;x&&(n[0]+=x[0],n[1]+=x[1]),C||(ta.event.altKey?(M||(M=[(f[0]+f[1])/2,(h[0]+h[1])/2]),q[0]=f[+(n[0]s?(u=r,r=s):u=s),v[0]!=r||v[1]!=u?(e?a=null:o=null,v[0]=r,v[1]=u,!0):void 0}function y(){d(),k.style("pointer-events","all").selectAll(".resize").style("display",n.empty()?"none":null),ta.select("body").style("cursor",null),L.on("mousemove.brush",null).on("mouseup.brush",null).on("touchmove.brush",null).on("touchend.brush",null).on("keydown.brush",null).on("keyup.brush",null),z(),w({type:"brushend"})}var M,x,b=this,_=ta.select(ta.event.target),w=c.of(b,arguments),k=ta.select(b),E=_.datum(),A=!/^(n|s)$/.test(E)&&l,N=!/^(e|w)$/.test(E)&&s,C=_.classed("extent"),z=W(b),q=ta.mouse(b),L=ta.select(t(b)).on("keydown.brush",i).on("keyup.brush",v);if(ta.event.changedTouches?L.on("touchmove.brush",d).on("touchend.brush",y):L.on("mousemove.brush",d).on("mouseup.brush",y),k.interrupt().selectAll("*").interrupt(),C)q[0]=f[0]-q[0],q[1]=h[0]-q[1];else if(E){var T=+/w$/.test(E),R=+/^n/.test(E);x=[f[1-T]-q[0],h[1-R]-q[1]],q[0]=f[T],q[1]=h[R]}else ta.event.altKey&&(M=q.slice());k.style("pointer-events","none").selectAll(".resize").style("display",null),ta.select("body").style("cursor",_.style("cursor")),w({type:"brushstart"}),d()}var o,a,c=E(n,"brushstart","brush","brushend"),l=null,s=null,f=[0,0],h=[0,0],g=!0,p=!0,v=Ol[0];return n.event=function(n){n.each(function(){var n=c.of(this,arguments),t={x:f,y:h,i:o,j:a},e=this.__chart__||t;this.__chart__=t,Tl?ta.select(this).transition().each("start.brush",function(){o=e.i,a=e.j,f=e.x,h=e.y,n({type:"brushstart"})}).tween("brush:brush",function(){var e=yu(f,t.x),r=yu(h,t.y);return o=a=null,function(u){f=t.x=e(u),h=t.y=r(u),n({type:"brush",mode:"resize"})}}).each("end.brush",function(){o=t.i,a=t.j,n({type:"brush",mode:"resize"}),n({type:"brushend"})}):(n({type:"brushstart"}),n({type:"brush",mode:"resize"}),n({type:"brushend"}))})},n.x=function(t){return arguments.length?(l=t,v=Ol[!l<<1|!s],n):l},n.y=function(t){return arguments.length?(s=t,v=Ol[!l<<1|!s],n):s},n.clamp=function(t){return arguments.length?(l&&s?(g=!!t[0],p=!!t[1]):l?g=!!t:s&&(p=!!t),n):l&&s?[g,p]:l?g:s?p:null},n.extent=function(t){var e,r,u,i,c;return arguments.length?(l&&(e=t[0],r=t[1],s&&(e=e[0],r=r[0]),o=[e,r],l.invert&&(e=l(e),r=l(r)),e>r&&(c=e,e=r,r=c),(e!=f[0]||r!=f[1])&&(f=[e,r])),s&&(u=t[0],i=t[1],l&&(u=u[1],i=i[1]),a=[u,i],s.invert&&(u=s(u),i=s(i)),u>i&&(c=u,u=i,i=c),(u!=h[0]||i!=h[1])&&(h=[u,i])),n):(l&&(o?(e=o[0],r=o[1]):(e=f[0],r=f[1],l.invert&&(e=l.invert(e),r=l.invert(r)),e>r&&(c=e,e=r,r=c))),s&&(a?(u=a[0],i=a[1]):(u=h[0],i=h[1],s.invert&&(u=s.invert(u),i=s.invert(i)),u>i&&(c=u,u=i,i=c))),l&&s?[[e,u],[r,i]]:l?[e,r]:s&&[u,i])},n.clear=function(){return n.empty()||(f=[0,0],h=[0,0],o=a=null),n},n.empty=function(){return!!l&&f[0]==f[1]||!!s&&h[0]==h[1]},ta.rebind(n,c,"on")};var Hl={n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Ol=[["n","e","s","w","nw","ne","se","sw"],["e","w"],["n","s"],[]],Il=ac.format=gc.timeFormat,Yl=Il.utc,Zl=Yl("%Y-%m-%dT%H:%M:%S.%LZ");Il.iso=Date.prototype.toISOString&&+new Date("2000-01-01T00:00:00.000Z")?Jo:Zl,Jo.parse=function(n){var t=new Date(n);return isNaN(t)?null:t},Jo.toString=Zl.toString,ac.second=Ft(function(n){return new cc(1e3*Math.floor(n/1e3))},function(n,t){n.setTime(n.getTime()+1e3*Math.floor(t))},function(n){return n.getSeconds()}),ac.seconds=ac.second.range,ac.seconds.utc=ac.second.utc.range,ac.minute=Ft(function(n){return new cc(6e4*Math.floor(n/6e4))},function(n,t){n.setTime(n.getTime()+6e4*Math.floor(t))},function(n){return n.getMinutes()}),ac.minutes=ac.minute.range,ac.minutes.utc=ac.minute.utc.range,ac.hour=Ft(function(n){var t=n.getTimezoneOffset()/60;return new cc(36e5*(Math.floor(n/36e5-t)+t))},function(n,t){n.setTime(n.getTime()+36e5*Math.floor(t))},function(n){return n.getHours()}),ac.hours=ac.hour.range,ac.hours.utc=ac.hour.utc.range,ac.month=Ft(function(n){return n=ac.day(n),n.setDate(1),n},function(n,t){n.setMonth(n.getMonth()+t)},function(n){return n.getMonth()}),ac.months=ac.month.range,ac.months.utc=ac.month.utc.range;var Vl=[1e3,5e3,15e3,3e4,6e4,3e5,9e5,18e5,36e5,108e5,216e5,432e5,864e5,1728e5,6048e5,2592e6,7776e6,31536e6],Xl=[[ac.second,1],[ac.second,5],[ac.second,15],[ac.second,30],[ac.minute,1],[ac.minute,5],[ac.minute,15],[ac.minute,30],[ac.hour,1],[ac.hour,3],[ac.hour,6],[ac.hour,12],[ac.day,1],[ac.day,2],[ac.week,1],[ac.month,1],[ac.month,3],[ac.year,1]],$l=Il.multi([[".%L",function(n){return n.getMilliseconds()}],[":%S",function(n){return n.getSeconds()}],["%I:%M",function(n){return n.getMinutes()}],["%I %p",function(n){return n.getHours()}],["%a %d",function(n){return n.getDay()&&1!=n.getDate()}],["%b %d",function(n){return 1!=n.getDate()}],["%B",function(n){return n.getMonth()}],["%Y",Ne]]),Bl={range:function(n,t,e){return ta.range(Math.ceil(n/e)*e,+t,e).map(Ko)},floor:y,ceil:y};Xl.year=ac.year,ac.scale=function(){return Go(ta.scale.linear(),Xl,$l)};var Wl=Xl.map(function(n){return[n[0].utc,n[1]]}),Jl=Yl.multi([[".%L",function(n){return n.getUTCMilliseconds()}],[":%S",function(n){return n.getUTCSeconds()}],["%I:%M",function(n){return n.getUTCMinutes()}],["%I %p",function(n){return n.getUTCHours()}],["%a %d",function(n){return n.getUTCDay()&&1!=n.getUTCDate()}],["%b %d",function(n){return 1!=n.getUTCDate()}],["%B",function(n){return n.getUTCMonth()}],["%Y",Ne]]);Wl.year=ac.year.utc,ac.scale.utc=function(){return Go(ta.scale.linear(),Wl,Jl)},ta.text=At(function(n){return n.responseText}),ta.json=function(n,t){return Nt(n,"application/json",Qo,t)},ta.html=function(n,t){return Nt(n,"text/html",na,t)},ta.xml=At(function(n){return n.responseXML}),"function"==typeof define&&define.amd?define(ta):"object"==typeof module&&module.exports&&(module.exports=ta),this.d3=ta}();!function(){var topojson={version:"1.6.20",mesh:function(topology){return object(topology,meshArcs.apply(this,arguments))},meshArcs:meshArcs,merge:function(topology){return object(topology,mergeArcs.apply(this,arguments))},mergeArcs:mergeArcs,feature:featureOrCollection,neighbors:neighbors,presimplify:presimplify};function stitchArcs(topology,arcs){var stitchedArcs={},fragmentByStart={},fragmentByEnd={},fragments=[],emptyIndex=-1;arcs.forEach(function(i,j){var arc=topology.arcs[i<0?~i:i],t;if(arc.length<3&&!arc[1][0]&&!arc[1][1]){t=arcs[++emptyIndex],arcs[emptyIndex]=i,arcs[j]=t}});arcs.forEach(function(i){var e=ends(i),start=e[0],end=e[1],f,g;if(f=fragmentByEnd[start]){delete fragmentByEnd[f.end];f.push(i);f.end=end;if(g=fragmentByStart[end]){delete fragmentByStart[g.start];var fg=g===f?f:f.concat(g);fragmentByStart[fg.start=f.start]=fragmentByEnd[fg.end=g.end]=fg}else{fragmentByStart[f.start]=fragmentByEnd[f.end]=f}}else if(f=fragmentByStart[end]){delete fragmentByStart[f.start];f.unshift(i);f.start=start;if(g=fragmentByEnd[start]){delete fragmentByEnd[g.end];var gf=g===f?f:g.concat(f);fragmentByStart[gf.start=g.start]=fragmentByEnd[gf.end=f.end]=gf}else{fragmentByStart[f.start]=fragmentByEnd[f.end]=f}}else{f=[i];fragmentByStart[f.start=start]=fragmentByEnd[f.end=end]=f}});function ends(i){var arc=topology.arcs[i<0?~i:i],p0=arc[0],p1;if(topology.transform)p1=[0,0],arc.forEach(function(dp){p1[0]+=dp[0],p1[1]+=dp[1]});else p1=arc[arc.length-1];return i<0?[p1,p0]:[p0,p1]}function flush(fragmentByEnd,fragmentByStart){for(var k in fragmentByEnd){var f=fragmentByEnd[k];delete fragmentByStart[f.start];delete f.start;delete f.end;f.forEach(function(i){stitchedArcs[i<0?~i:i]=1});fragments.push(f)}}flush(fragmentByEnd,fragmentByStart);flush(fragmentByStart,fragmentByEnd);arcs.forEach(function(i){if(!stitchedArcs[i<0?~i:i])fragments.push([i])});return fragments}function meshArcs(topology,o,filter){var arcs=[];if(arguments.length>1){var geomsByArc=[],geom;function arc(i){var j=i<0?~i:i;(geomsByArc[j]||(geomsByArc[j]=[])).push({i:i,g:geom})}function line(arcs){arcs.forEach(arc)}function polygon(arcs){arcs.forEach(line)}function geometry(o){if(o.type==="GeometryCollection")o.geometries.forEach(geometry);else if(o.type in geometryType)geom=o,geometryType[o.type](o.arcs)}var geometryType={LineString:line,MultiLineString:polygon,Polygon:polygon,MultiPolygon:function(arcs){arcs.forEach(polygon)}};geometry(o);geomsByArc.forEach(arguments.length<3?function(geoms){arcs.push(geoms[0].i)}:function(geoms){if(filter(geoms[0].g,geoms[geoms.length-1].g))arcs.push(geoms[0].i)})}else{for(var i=0,n=topology.arcs.length;i0}polygons.forEach(function(polygon){if(!polygon._){var component=[],neighbors=[polygon];polygon._=1;components.push(component);while(polygon=neighbors.pop()){component.push(polygon);polygon.forEach(function(ring){ring.forEach(function(arc){polygonsByArc[arc<0?~arc:arc].forEach(function(polygon){if(!polygon._){polygon._=1;neighbors.push(polygon)}})})})}}});polygons.forEach(function(polygon){delete polygon._});return{type:"MultiPolygon",arcs:components.map(function(polygons){var arcs=[],n;polygons.forEach(function(polygon){polygon.forEach(function(ring){ring.forEach(function(arc){if(polygonsByArc[arc<0?~arc:arc].length<2){arcs.push(arc)}})})});arcs=stitchArcs(topology,arcs);if((n=arcs.length)>1){var sgn=exterior(polygons[0][0]);for(var i=0,t;i>>1;if(a[mid]0)object=array[size],down(array[object._=0]=object,0);return removed};heap.remove=function(removed){var i=removed._,object;if(array[i]!==removed)return;if(i!==--size)object=array[size],(compareArea(object,removed)<0?up:down)(array[object._=i]=object,i);return i};function up(object,i){while(i>0){var j=(i+1>>1)-1,parent=array[j];if(compareArea(object,parent)>=0)break;array[parent._=i]=parent;array[object._=i=j]=object}}function down(object,i){while(true){var r=i+1<<1,l=r-1,j=i,child=array[j];if(l
-
-
-
-
-
-
-
-
- topojson/world-110m.json at master · mbostock/topojson
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Skip to content
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Permalink
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Sorry, something went wrong.
Reload?
-
Sorry, we cannot display this file.
-
Sorry, this file is invalid so it cannot be displayed.
-
-
-
-
-
-
-
-
-
Jump to Line
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Something went wrong with that request. Please try again.
-
-
-
-
-
-
-
-
-
-
-
-
You signed in with another tab or window. Reload to refresh your session.
-
You signed out in another tab or window. Reload to refresh your session.
-
-
-
-
-
-
-
-
+{"type":"Topology","objects":{"land":{"type":"MultiPolygon","arcs":[[[0]],[[1]],[[2]],[[3]],[[4]],[[5]],[[6]],[[7,8,9]],[[10,11,12,13]],[[14]],[[15]],[[16]],[[17]],[[18]],[[19]],[[20]],[[21,22]],[[23]],[[24]],[[25]],[[26]],[[27]],[[28]],[[29]],[[30]],[[31,32]],[[33]],[[34]],[[35]],[[36]],[[37]],[[38]],[[39]],[[40]],[[41]],[[42]],[[43]],[[44,45]],[[46]],[[47]],[[48]],[[49,50,51,52]],[[53]],[[54]],[[55]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]],[[62]],[[63]],[[64,65]],[[66]],[[67]],[[68]],[[69]],[[70]],[[71]],[[72]],[[73]],[[74]],[[75]],[[76]],[[77]],[[78,79]],[[80]],[[81]],[[82]],[[83]],[[84]],[[85]],[[86]],[[87]],[[88]],[[89]],[[90]],[[91]],[[92,93]],[[94]],[[95]],[[96,97,98,99,100,101,102,103]],[[104]],[[105]],[[106]],[[107]],[[108]],[[109]],[[110]],[[111,112]],[[113]],[[114,115]],[[116,117]],[[118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229]],[[230,231]],[[232]],[[233]],[[234]],[[235]],[[236]],[[237]],[[238,239,240,241]],[[242]],[[243]],[[244]],[[245]],[[246]],[[247]],[[248]],[[249]],[[250,251,252,253,254,255,256,257,258,259,260,261,262,263,264,265,266,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299,300,301,302,303,304,305,306,307,308,309,310,311,312,313,314,315,316,317,318,319,320,321,322,323,324,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,352,353,354,355,356,357,358,359,360,361,362,363,364,365,366,367,368,369,370,371,372,373,374,375,376,377,378,379,380,381,382,383,384,385,386,387,388,389,390,391,392,393,394,395,396,397,398,399,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,419,420,421,422,423,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,457,458,459,460,461,462,463,464,465,466,467],[468,469,470,471,472,473,474,475,476,477]],[[478]],[[479]],[[480]],[[481]],[[482]],[[483]],[[484]],[[485]],[[486]],[[487]],[[488]],[[489]],[[490]],[[491]]]},"countries":{"type":"GeometryCollection","bbox":[-179.99999999999997,-90.00000000000003,180.00000000000014,83.64513000000001],"geometries":[{"type":"Polygon","id":4,"arcs":[[492,493,494,495,496,497]]},{"type":"MultiPolygon","id":24,"arcs":[[[498,499,334,500,336,501,502]],[[339,503,504]]]},{"type":"Polygon","id":8,"arcs":[[505,506,398,507,508,509]]},{"type":"Polygon","id":784,"arcs":[[297,510,299,511,512]]},{"type":"MultiPolygon","id":32,"arcs":[[[513,13]],[[514,515,516,517,172,518,174,519,520]]]},{"type":"Polygon","id":51,"arcs":[[521,522,523,524,525]]},{"type":"MultiPolygon","id":10,"arcs":[[[0]],[[1]],[[2]],[[3]],[[4]],[[5]],[[6]],[[526,527,9]]]},{"type":"Polygon","id":260,"arcs":[[15]]},{"type":"MultiPolygon","id":36,"arcs":[[[16]],[[26]]]},{"type":"Polygon","id":40,"arcs":[[528,529,530,531,532,533,534]]},{"type":"MultiPolygon","id":31,"arcs":[[[535,-525]],[[477,536,469,537,471,538,-523,539,540]]]},{"type":"Polygon","id":108,"arcs":[[541,542,543]]},{"type":"Polygon","id":56,"arcs":[[544,545,546,547,419]]},{"type":"Polygon","id":204,"arcs":[[548,549,550,551,552]]},{"type":"Polygon","id":854,"arcs":[[553,554,555,-551,556,557]]},{"type":"Polygon","id":50,"arcs":[[558,280,559,282,560]]},{"type":"Polygon","id":100,"arcs":[[561,395,562,563,564,565]]},{"type":"MultiPolygon","id":44,"arcs":[[[73]],[[75]],[[76]]]},{"type":"Polygon","id":70,"arcs":[[566,567,568]]},{"type":"Polygon","id":112,"arcs":[[569,570,571,572,573]]},{"type":"Polygon","id":84,"arcs":[[574,148,575]]},{"type":"Polygon","id":68,"arcs":[[576,577,578,579,-521]]},{"type":"Polygon","id":76,"arcs":[[-516,580,-579,581,582,583,584,585,586,165,587,167,588,169,589]]},{"type":"Polygon","id":96,"arcs":[[50,590]]},{"type":"Polygon","id":64,"arcs":[[591,592]]},{"type":"Polygon","id":72,"arcs":[[593,594,595,596]]},{"type":"Polygon","id":140,"arcs":[[597,598,599,600,601,602,603]]},{"type":"MultiPolygon","id":124,"arcs":[[[86]],[[87]],[[88]],[[89]],[[90]],[[105]],[[106]],[[108]],[[110]],[[113]],[[604,119,605,121,606,123,607,125,608,127,609,129,610,209,611,211,612,223,613,225,614,227,615,229]],[[616,231]],[[232]],[[233]],[[234]],[[235]],[[237]],[[238,617,240,618]],[[243]],[[245]],[[246]],[[248]],[[249]],[[478]],[[479]],[[481]],[[482]],[[483]],[[489]],[[490]]]},{"type":"Polygon","id":756,"arcs":[[-532,619,620,621]]},{"type":"MultiPolygon","id":152,"arcs":[[[-514,10,622,12]],[[-520,175,623,177,624,-577]]]},{"type":"MultiPolygon","id":156,"arcs":[[[66]],[[625,269,626,271,627,628,629,630,-593,631,632,633,634,-496,635,636,637,638,639,640]]]},{"type":"Polygon","id":384,"arcs":[[348,641,642,643,-554,644]]},{"type":"Polygon","id":120,"arcs":[[645,646,343,647,648,649,650,-604,651]]},{"type":"Polygon","id":180,"arcs":[[652,653,-542,654,655,-503,338,-505,656,-602,657]]},{"type":"Polygon","id":178,"arcs":[[-504,340,658,-652,-603,-657]]},{"type":"Polygon","id":170,"arcs":[[659,180,660,661,157,662,-583,663]]},{"type":"Polygon","id":188,"arcs":[[185,664,152,665]]},{"type":"Polygon","id":192,"arcs":[[72]]},{"type":"Polygon","id":-99,"arcs":[[79,666]]},{"type":"Polygon","id":196,"arcs":[[78,-667]]},{"type":"Polygon","id":203,"arcs":[[-534,667,668,669]]},{"type":"Polygon","id":276,"arcs":[[426,670,-668,-533,-622,671,672,-546,673,421,674]]},{"type":"Polygon","id":262,"arcs":[[316,675,676,677]]},{"type":"MultiPolygon","id":208,"arcs":[[[94]],[[678,-675,422,679,424]]]},{"type":"Polygon","id":214,"arcs":[[64,680]]},{"type":"Polygon","id":12,"arcs":[[681,682,683,684,685,365,686,687]]},{"type":"Polygon","id":218,"arcs":[[179,-660,688]]},{"type":"Polygon","id":818,"arcs":[[313,689,690,371,691,373,692]]},{"type":"Polygon","id":232,"arcs":[[693,694,315,-678]]},{"type":"Polygon","id":724,"arcs":[[417,695,411,696,413,697]]},{"type":"Polygon","id":233,"arcs":[[698,434,699,700]]},{"type":"Polygon","id":231,"arcs":[[-677,701,702,703,704,705,706,-694]]},{"type":"Polygon","id":246,"arcs":[[707,436,708,709]]},{"type":"MultiPolygon","id":242,"arcs":[[[20]],[[22,21]]]},{"type":"Polygon","id":238,"arcs":[[14]]},{"type":"MultiPolygon","id":250,"arcs":[[[710,711,164,-587]],[[84]],[[712,-672,-621,713,410,-696,418,-548]]]},{"type":"Polygon","id":266,"arcs":[[341,714,-646,-659]]},{"type":"MultiPolygon","id":826,"arcs":[[[715,92]],[[716,97,717,99,718,101,719,103]]]},{"type":"Polygon","id":268,"arcs":[[385,720,-540,-522,721]]},{"type":"Polygon","id":288,"arcs":[[347,-645,-558,722]]},{"type":"Polygon","id":324,"arcs":[[723,724,351,725,726,727,-643]]},{"type":"Polygon","id":270,"arcs":[[354,728]]},{"type":"Polygon","id":624,"arcs":[[352,729,-726]]},{"type":"Polygon","id":226,"arcs":[[342,-647,-715]]},{"type":"MultiPolygon","id":300,"arcs":[[[80]],[[397,-507,730,-564,731]]]},{"type":"Polygon","id":304,"arcs":[[491]]},{"type":"Polygon","id":320,"arcs":[[189,732,-576,149,733,734]]},{"type":"Polygon","id":328,"arcs":[[162,735,-585,736]]},{"type":"Polygon","id":340,"arcs":[[187,737,-734,150,738]]},{"type":"Polygon","id":191,"arcs":[[739,-569,740,401,741,742]]},{"type":"Polygon","id":332,"arcs":[[-681,65]]},{"type":"Polygon","id":348,"arcs":[[-529,743,744,745,746,-743,747]]},{"type":"MultiPolygon","id":360,"arcs":[[[28]],[[748,32]],[[33]],[[34]],[[37]],[[38]],[[41]],[[42]],[[749,45]],[[46]],[[47]],[[750,52]],[[48]]]},{"type":"Polygon","id":356,"arcs":[[-634,751,-632,-592,-631,752,-561,283,753,285,754]]},{"type":"Polygon","id":372,"arcs":[[93,-716]]},{"type":"Polygon","id":364,"arcs":[[755,-498,756,287,757,289,758,759,760,-536,-524,-539,472]]},{"type":"Polygon","id":368,"arcs":[[761,762,763,764,765,766,-760]]},{"type":"Polygon","id":352,"arcs":[[109]]},{"type":"Polygon","id":376,"arcs":[[767,768,769,-693,374,770,376,771,772]]},{"type":"MultiPolygon","id":380,"arcs":[[[81]],[[82]],[[773,403,774,405,775,407,776,409,-714,-620,-531]]]},{"type":"Polygon","id":388,"arcs":[[63]]},{"type":"Polygon","id":400,"arcs":[[-768,777,-765,778,312,-770,779]]},{"type":"MultiPolygon","id":392,"arcs":[[[77]],[[83]],[[85]]]},{"type":"Polygon","id":398,"arcs":[[780,781,474,782,-638,783]]},{"type":"Polygon","id":404,"arcs":[[322,784,785,786,-704,787]]},{"type":"Polygon","id":417,"arcs":[[-784,-637,788,789]]},{"type":"Polygon","id":116,"arcs":[[790,791,792,273]]},{"type":"Polygon","id":410,"arcs":[[262,793,264,794]]},{"type":"Polygon","id":-99,"arcs":[[-510,795,796,797]]},{"type":"Polygon","id":414,"arcs":[[291,798,-763]]},{"type":"Polygon","id":418,"arcs":[[799,800,-629,801,-792]]},{"type":"Polygon","id":422,"arcs":[[-772,377,802]]},{"type":"Polygon","id":430,"arcs":[[349,803,-724,-642]]},{"type":"Polygon","id":434,"arcs":[[804,-688,805,368,806,370,-691,807,808]]},{"type":"Polygon","id":144,"arcs":[[54]]},{"type":"Polygon","id":426,"arcs":[[809]]},{"type":"Polygon","id":440,"arcs":[[810,811,812,-570,813]]},{"type":"Polygon","id":442,"arcs":[[-673,-713,-547]]},{"type":"Polygon","id":428,"arcs":[[432,814,-701,815,-571,-813]]},{"type":"Polygon","id":504,"arcs":[[-686,816,360,817,362,818,364]]},{"type":"Polygon","id":498,"arcs":[[819,820]]},{"type":"Polygon","id":450,"arcs":[[25]]},{"type":"Polygon","id":484,"arcs":[[821,147,-575,-733,190,822,192,823,194,824,196,825,198,826]]},{"type":"Polygon","id":807,"arcs":[[-798,827,-565,-731,-506]]},{"type":"Polygon","id":466,"arcs":[[828,-683,829,-555,-644,-728,830]]},{"type":"Polygon","id":104,"arcs":[[277,831,279,-559,-753,-630,-801,832]]},{"type":"Polygon","id":499,"arcs":[[833,400,-741,-568,834,-796,-509]]},{"type":"Polygon","id":496,"arcs":[[835,-640]]},{"type":"Polygon","id":508,"arcs":[[836,325,837,838,839,840,841,842]]},{"type":"Polygon","id":478,"arcs":[[843,356,844,358,845,-684,-829]]},{"type":"Polygon","id":454,"arcs":[[-843,846,847]]},{"type":"MultiPolygon","id":458,"arcs":[[[275,848]],[[-751,49,-591,51]]]},{"type":"Polygon","id":516,"arcs":[[333,-500,849,-595,850]]},{"type":"Polygon","id":540,"arcs":[[19]]},{"type":"Polygon","id":562,"arcs":[[-556,-830,-682,-805,851,-650,852,-552]]},{"type":"Polygon","id":566,"arcs":[[345,-553,-853,-649]]},{"type":"Polygon","id":558,"arcs":[[186,-739,151,-665]]},{"type":"Polygon","id":528,"arcs":[[-674,-545,420]]},{"type":"MultiPolygon","id":578,"arcs":[[[853,-710,854,438,855,440]],[[480]],[[485]],[[486]]]},{"type":"Polygon","id":524,"arcs":[[-752,-633]]},{"type":"MultiPolygon","id":554,"arcs":[[[17]],[[18]]]},{"type":"MultiPolygon","id":512,"arcs":[[[856,304,857,858,-512,300,859,302]],[[-511,298]]]},{"type":"Polygon","id":586,"arcs":[[-635,-755,286,-757,-497]]},{"type":"Polygon","id":591,"arcs":[[860,182,861,184,-666,153,862,155,863,-661]]},{"type":"Polygon","id":604,"arcs":[[-625,178,-689,-664,-582,-578]]},{"type":"MultiPolygon","id":608,"arcs":[[[53]],[[56]],[[57]],[[58]],[[59]],[[60]],[[61]]]},{"type":"MultiPolygon","id":598,"arcs":[[[39]],[[40]],[[-750,44]],[[43]]]},{"type":"Polygon","id":616,"arcs":[[-671,427,864,429,865,-814,-574,866,867,-669]]},{"type":"Polygon","id":630,"arcs":[[62]]},{"type":"Polygon","id":408,"arcs":[[868,261,-795,265,869,870,268,-626]]},{"type":"Polygon","id":620,"arcs":[[-698,414,871,416]]},{"type":"Polygon","id":600,"arcs":[[-580,-581,-515]]},{"type":"Polygon","id":275,"arcs":[[-780,-769]]},{"type":"Polygon","id":634,"arcs":[[295,872]]},{"type":"Polygon","id":642,"arcs":[[873,-821,874,392,875,394,-562,876,-746]]},{"type":"MultiPolygon","id":643,"arcs":[[[91]],[[-866,430,877,-811]],[[114,116,878]],[[236]],[[242]],[[244]],[[247]],[[250,879,252,880,254,881,256,882,258,883,260,-869,-641,-836,-639,-783,475,884,-541,-721,386,885,388,886,-572,-816,-700,435,-708,-854,441,887,443,888,445,889,447,890,449,891,451,892,893,894,455,895,457,896,459,897,461,898,463,899,465,900,111,901]],[[484]],[[487]],[[488]]]},{"type":"Polygon","id":646,"arcs":[[902,-543,-654,903]]},{"type":"Polygon","id":732,"arcs":[[-685,-846,359,-817]]},{"type":"Polygon","id":682,"arcs":[[904,309,905,311,-779,-764,-799,292,906,294,-873,296,-513,-859,907]]},{"type":"Polygon","id":729,"arcs":[[908,909,-599,910,-808,-690,314,-695,-707,911]]},{"type":"Polygon","id":728,"arcs":[[912,-705,-787,913,-658,-601,914,-909]]},{"type":"Polygon","id":686,"arcs":[[355,-844,-831,-727,-730,353,-729]]},{"type":"MultiPolygon","id":90,"arcs":[[[27]],[[29]],[[30]],[[35]],[[36]]]},{"type":"Polygon","id":694,"arcs":[[350,-725,-804]]},{"type":"Polygon","id":222,"arcs":[[188,-735,-738]]},{"type":"Polygon","id":-99,"arcs":[[-702,-676,317,915,916]]},{"type":"Polygon","id":706,"arcs":[[917,321,-788,-703,-917,918,319]]},{"type":"Polygon","id":688,"arcs":[[-566,-828,-797,-835,-567,-740,-747,-877]]},{"type":"Polygon","id":740,"arcs":[[163,-712,919,-586,-736]]},{"type":"Polygon","id":703,"arcs":[[-868,920,-744,-535,-670]]},{"type":"Polygon","id":705,"arcs":[[-530,-748,-742,402,-774]]},{"type":"Polygon","id":752,"arcs":[[-855,-709,437]]},{"type":"Polygon","id":748,"arcs":[[921,-839]]},{"type":"Polygon","id":760,"arcs":[[-778,-773,-803,378,922,-766]]},{"type":"Polygon","id":148,"arcs":[[-852,-809,-911,-598,-651]]},{"type":"Polygon","id":768,"arcs":[[923,-723,-557,-550]]},{"type":"Polygon","id":764,"arcs":[[274,-849,276,-833,-800,-791]]},{"type":"Polygon","id":762,"arcs":[[-789,-636,-495,924]]},{"type":"Polygon","id":795,"arcs":[[-756,473,-782,925,-493]]},{"type":"Polygon","id":626,"arcs":[[31,-749]]},{"type":"Polygon","id":780,"arcs":[[55]]},{"type":"Polygon","id":788,"arcs":[[-687,926,367,-806]]},{"type":"MultiPolygon","id":792,"arcs":[[[-722,-526,-761,-767,-923,379,927,928,382,929,384]],[[-732,-563,396]]]},{"type":"Polygon","id":158,"arcs":[[74]]},{"type":"Polygon","id":834,"arcs":[[-785,930,324,-837,-848,931,-655,-544,-903,932]]},{"type":"Polygon","id":800,"arcs":[[-904,-653,-914,-786,-933]]},{"type":"Polygon","id":804,"arcs":[[-887,389,933,391,-875,-820,-874,-745,-921,-867,-573]]},{"type":"Polygon","id":858,"arcs":[[-590,170,934,-517]]},{"type":"MultiPolygon","id":840,"arcs":[[[67]],[[68]],[[69]],[[70]],[[71]],[[130,935,132,936,134,937,136,938,138,939,940,141,941,143,942,145,-827,199,943,201,944,945,204,946,206,947,208,-611]],[[95]],[[104]],[[107]],[[-613,212,948,214,949,216,950,218,951,220,952,222]]]},{"type":"Polygon","id":860,"arcs":[[-926,-781,-790,-925,-494]]},{"type":"Polygon","id":862,"arcs":[[158,953,160,954,-737,-584,-663]]},{"type":"Polygon","id":704,"arcs":[[272,-793,-802,-628]]},{"type":"MultiPolygon","id":548,"arcs":[[[23]],[[24]]]},{"type":"Polygon","id":887,"arcs":[[305,955,307,-908,-858]]},{"type":"Polygon","id":710,"arcs":[[956,328,957,330,958,332,-851,-594,959,-840,-922,-838,326],[-810]]},{"type":"Polygon","id":894,"arcs":[[-847,-842,960,-596,-850,-499,-656,-932]]},{"type":"Polygon","id":716,"arcs":[[-960,-597,-961,-841]]}]}},"arcs":[[[33452,5736],[-82,-294],[-81,-259],[-582,79],[-621,-34],[-348,192],[0,22],[-152,170],[625,-23],[599,-56],[207,237],[147,203],[288,-237]],[[5775,6048],[-533,-79],[-364,204],[-163,203],[-11,34],[-180,158],[169,214],[517,-90],[277,-181],[212,-203],[76,-260]],[[37457,6883],[342,-248],[120,-350],[33,-248],[11,-293],[-430,-181],[-452,-146],[-522,-136],[-582,-113],[-658,34],[-365,192],[49,237],[593,158],[239,192],[174,248],[126,214],[168,203],[180,238],[0,-1],[141,0],[414,125],[419,-125]],[[16330,9501],[359,-90],[332,102],[-158,-203],[-261,-147],[-386,45],[-278,203],[60,192],[332,-102]],[[15122,9513],[425,-226],[-164,23],[-359,56],[-381,158],[202,124],[277,-135]],[[22505,10404],[305,-79],[304,68],[163,-327],[-217,45],[-337,-23],[-343,23],[-376,-34],[-283,113],[-146,237],[174,101],[353,-79],[403,-45]],[[30985,10967],[33,-259],[-49,-226],[-76,-214],[-326,-79],[-311,-113],[-364,11],[136,226],[-327,-79],[-310,-79],[-212,169],[-16,237],[305,226],[190,67],[321,-22],[82,293],[16,215],[-6,462],[158,271],[256,90],[147,-214],[65,-214],[120,-260],[92,-248],[76,-260]],[[794,3215],[78,48],[94,59],[81,51],[41,25]],[[1088,3398],[41,-1],[29,-10]],[[1158,3387],[402,-239],[352,239],[63,33],[816,102],[265,-135],[130,-68],[419,-192],[789,-147],[625,-180],[1072,-136],[800,158],[1181,-113],[669,-180],[734,169],[773,158],[60,271],[-1094,22],[-898,136],[-234,225],[-745,125],[49,259],[103,237],[104,214],[-55,237],[-462,158],[-212,204],[-430,180],[675,-34],[642,91],[402,-192],[495,169],[457,214],[223,192],[-98,237],[-359,158],[-408,169],[-571,34],[-500,79],[-539,57],[-180,214],[-359,181],[-217,203],[-87,654],[136,-56],[250,-181],[457,57],[441,79],[228,-249],[441,57],[370,124],[348,158],[315,192],[419,56],[-11,215],[-97,214],[81,203],[359,102],[163,-192],[425,113],[321,146],[397,12],[375,56],[376,136],[299,124],[337,124],[218,-34],[190,-45],[414,79],[370,-102],[381,12],[364,79],[375,-57],[414,-56],[386,22],[403,-11],[413,-11],[381,22],[283,170],[337,90],[349,-124],[331,101],[300,203],[179,-180],[98,-203],[180,-192],[288,169],[332,-214],[375,-68],[321,-158],[392,34],[354,101],[418,-22],[376,-79],[381,-102],[147,249],[-180,191],[-136,204],[-359,45],[-158,214],[-60,214],[-98,429],[213,-79],[364,-34],[359,34],[327,-90],[283,-169],[119,-203],[376,-34],[359,79],[381,113],[342,67],[283,-135],[370,45],[239,440],[224,-259],[321,-102],[348,56],[228,-225],[365,-23],[337,-68],[332,-124],[218,215],[108,203],[278,-226],[381,57],[283,-125],[190,-191],[370,56],[288,124],[283,147],[337,79],[392,68],[354,79],[272,124],[163,180],[65,249],[-32,236],[-87,226],[-98,226],[-87,226],[-71,203],[-16,225],[27,226],[130,214],[109,237],[44,226],[-55,248],[-32,226],[136,260],[152,169],[180,214],[190,181],[223,169],[109,248],[152,158],[174,147],[267,34],[174,180],[196,113],[228,68],[202,147],[157,180],[218,68],[163,-147],[-103,-192],[-283,-169],[-120,-124],[-206,90],[-229,-56],[-190,-136],[-202,-146],[-136,-170],[-38,-225],[17,-215],[130,-191],[-190,-136],[-261,-45],[-153,-192],[-163,-180],[-174,-249],[-44,-214],[98,-237],[147,-181],[229,-135],[212,-181],[114,-225],[60,-215],[82,-225],[130,-192],[82,-215],[38,-530],[81,-214],[22,-226],[87,-226],[-38,-304],[-152,-237],[-163,-192],[-370,-79],[-125,-203],[-169,-192],[-419,-215],[-370,-90],[-348,-124],[-376,-124],[-223,-237],[-446,-23],[-489,23],[-441,-45],[-468,0],[87,-226],[424,-101],[311,-158],[174,-204],[-310,-180],[-479,56],[-397,-146],[-17,-237],[-11,-226],[327,-192],[60,-214],[353,-215],[588,-90],[500,-158],[398,-180],[506,-181],[690,-90],[681,-158],[473,-170],[517,-191],[272,-271],[136,-215],[337,204],[457,169],[484,180],[577,147],[495,158],[691,11],[680,-79],[560,-135],[180,248],[386,169],[702,12],[550,124],[522,124],[577,79],[614,102],[430,146],[-196,203],[-119,203],[0,215],[-539,-23],[-571,-90],[-544,0],[-77,214],[39,429],[125,124],[397,136],[468,135],[337,169],[337,170],[251,225],[380,102],[376,79],[190,45],[430,23],[408,79],[343,112],[337,136],[305,135],[386,181],[245,192],[261,169],[82,226],[-294,135],[98,237],[185,181],[288,112],[305,136],[283,180],[217,226],[136,271],[202,158],[331,-34],[136,-192],[332,-22],[11,214],[142,226],[299,-57],[71,-214],[331,-34],[360,102],[348,67],[315,-34],[120,-237],[305,192],[283,102],[315,79],[310,79],[283,135],[310,91],[240,124],[168,203],[207,-147],[288,79],[202,-271],[157,-203],[316,113],[125,226],[283,158],[365,-34],[108,-215],[229,215],[299,68],[326,22],[294,-11],[310,-68],[300,-34],[130,-192],[180,-169],[304,102],[327,22],[315,0],[310,12],[278,79],[294,67],[245,158],[261,102],[283,56],[212,158],[152,316],[158,192],[288,-90],[109,-203],[239,-136],[289,45],[196,-203],[206,-146],[283,135],[98,248],[250,102],[289,192],[272,79],[326,112],[218,125],[228,135],[218,124],[261,-68],[250,203],[180,158],[261,-11],[229,136],[54,203],[234,158],[228,113],[278,90],[256,45],[244,-34],[262,-56],[223,-158],[27,-249],[245,-191],[168,-158],[332,-68],[185,-158],[229,-158],[266,-34],[223,113],[240,237],[261,-124],[272,-68],[261,-68],[272,-45],[277,0],[229,-598],[-11,-147],[-33,-259],[-266,-147],[-218,-214],[38,-226],[310,11],[-38,-225],[-141,-215],[-131,-237],[212,-180],[321,-57],[321,102],[153,226],[92,214],[153,181],[174,169],[70,203],[147,282],[174,57],[316,22],[277,68],[283,90],[136,226],[82,214],[190,215],[272,146],[234,113],[153,192],[157,101],[202,91],[277,-57],[250,57],[272,67],[305,-33],[201,158],[142,383],[103,-158],[131,-271],[234,-112],[266,-46],[267,68],[283,-45],[261,-11],[174,56],[234,-34],[212,-124],[250,79],[300,0],[255,79],[289,-79],[185,192],[141,192],[191,158],[348,429],[179,-79],[212,-158],[185,-203],[354,-350],[272,-12],[256,0],[299,68],[299,79],[229,158],[190,169],[310,23],[207,124],[218,-113],[141,-180],[196,-181],[305,23],[190,-147],[332,-147],[348,-56],[288,45],[218,181],[185,180],[250,45],[251,-79],[288,-56],[261,90],[250,0],[245,-56],[256,-57],[250,102],[299,90],[283,23],[316,0],[255,56],[251,45],[76,282],[11,237],[174,-158],[49,-259],[92,-237],[115,-192],[234,-102],[315,34],[365,12],[250,33],[364,0],[262,12],[364,-23],[310,-45],[196,-181],[-54,-214],[179,-169],[299,-136],[310,-146],[360,-102],[375,-90],[283,-90],[315,-12],[180,192],[245,-158],[212,-180],[245,-136],[337,-56],[321,-68],[136,-226],[316,-135],[212,-203],[310,-90],[321,11],[299,-34],[332,11],[332,-45],[310,-79],[288,-135],[289,-113],[195,-169],[-32,-226],[-147,-203],[-125,-260],[-98,-203],[-131,-237],[-364,-90],[-163,-203],[-360,-124],[-125,-226],[-190,-214],[-201,-181],[-115,-237],[-70,-214],[-28,-260],[6,-214],[158,-226],[60,-214],[130,-204],[517,-78],[109,-249],[-501,-90],[-424,-124],[-528,-23],[-234,-327],[-49,-271],[-119,-214],[-147,-215],[370,-191],[141,-237],[239,-215],[338,-192],[386,-180],[419,-181],[636,-180],[142,-282],[800,-125],[53,-44],[208,-170],[767,147],[636,-181],[-99520,-139],[16,-4],[245,335],[501,-181],[32,21]],[[31400,20215],[-92,-233],[-238,-178]],[[31070,19804],[-301,64]],[[30769,19868],[-202,174],[-291,83],[-350,322],[-283,309],[-383,645],[229,-121],[390,-384],[369,-207],[143,264],[90,394],[256,238],[198,-68]],[[30935,21517],[106,-267],[139,-432],[361,-345],[389,-144],[-125,-288],[-264,-29],[-141,203]],[[33736,22402],[222,-259],[-83,-202],[-375,-173],[-125,202],[-236,-259],[-139,259],[333,345],[236,-144],[167,231]],[[69522,23202],[-427,-37],[-7,306],[41,238],[19,118],[179,-181],[263,-72],[9,-110],[-77,-262]],[[90387,28338],[269,-199],[151,79],[217,111],[166,-39],[20,-684],[-95,-198],[-29,-463],[-97,157],[-193,-401],[-57,31],[-171,18],[-171,493],[-38,380],[-160,502],[7,264],[181,-51]],[[98060,28265],[63,-238],[198,233],[80,-243],[0,-242],[-103,-267],[-182,-424],[-142,-232],[103,-277],[-214,-7],[-238,-217],[-75,-377],[-157,-583],[-219,-257],[-138,-164],[-256,12],[-180,190],[-302,40],[-46,212],[149,427],[349,568],[179,109],[200,219],[238,301],[167,299],[123,429],[106,146],[41,321],[195,267],[61,-245]],[[98502,31008],[202,-607],[5,394],[126,-158],[41,-435],[224,-188],[188,-46],[158,220],[141,-67],[-67,-511],[-85,-336],[-212,12],[-74,-175],[26,-248],[-41,-107],[-105,-310],[-138,-395],[-214,-229],[-48,151],[-116,83],[160,474],[-91,317],[-299,230],[8,209],[201,200],[47,444],[-13,372],[-113,386],[8,102],[-133,237],[-218,510],[-117,408],[104,45],[151,-320],[216,-149],[78,-513]],[[96421,39068],[-105,-138],[-153,155],[-199,259],[-179,306],[-184,406],[-38,195],[119,-8],[156,-196],[122,-196],[89,-161],[228,-357],[144,-265]],[[99547,41844],[96,-167],[-46,-300],[-172,-79],[-153,71],[-27,253],[107,198],[126,-71],[69,95]],[[0,42295],[99822,-141],[-177,-122],[-36,215],[139,118],[88,32],[-99836,180]],[[0,42577],[57,26],[-34,-277],[-23,-31]],[[96623,42347],[-92,-76],[-93,252],[10,155],[175,-331]],[[96418,43229],[45,-464],[-75,72],[-58,-31],[-39,159],[-6,441],[133,-177]],[[63904,44023],[45,-693],[72,-269],[-28,-277],[-49,-169],[-94,338],[-53,-171],[53,-427],[-24,-244],[-77,-133],[-18,-488],[-109,-671],[-137,-793],[-172,-1092],[-106,-800],[-125,-668],[-226,-136],[-243,-244],[-160,147],[-220,206],[-77,304],[-18,510],[-98,460],[-26,414],[50,415],[128,100],[1,191],[133,437],[25,367],[-65,272],[-52,364],[-23,530],[97,322],[38,366],[138,21],[155,118],[103,104],[122,8],[158,328],[229,355],[83,289],[-38,247],[118,-70],[153,401],[6,346],[92,257],[96,-247],[74,-245],[69,-380]],[[89877,43903],[100,-452],[179,217],[92,-243],[133,-225],[-29,-255],[60,-494],[42,-288],[70,-70],[75,-492],[-27,-299],[90,-390],[301,-301],[197,-274],[186,-251],[-37,-139],[159,-361],[108,-623],[111,126],[113,-249],[68,88],[48,-610],[197,-354],[129,-220],[217,-466],[78,-463],[7,-328],[-19,-356],[132,-490],[-16,-509],[-48,-267],[-75,-514],[6,-330],[-55,-413],[-123,-524],[-205,-283],[-102,-446],[-93,-284],[-82,-497],[-107,-287],[-70,-431],[-36,-397],[14,-182],[-159,-200],[-311,-21],[-257,-236],[-127,-223],[-168,-248],[-230,255],[-170,101],[43,301],[-152,-109],[-243,-417],[-240,156],[-158,91],[-159,41],[-269,167],[-179,355],[-52,437],[-64,291],[-137,233],[-267,70],[91,279],[-67,428],[-136,-399],[-247,-106],[146,319],[42,332],[107,282],[-22,427],[-226,-491],[-174,-197],[-106,-458],[-217,237],[9,305],[-174,418],[-147,216],[52,133],[-356,349],[-195,16],[-267,280],[-498,-54],[-359,-206],[-317,-192],[-265,38],[-294,-296],[-241,-132],[-53,-302],[-103,-234],[-236,-14],[-174,-52],[-246,105],[-199,-62],[-191,-27],[-165,-307],[-81,26],[-140,-163],[-133,-183],[-203,23],[-186,0],[-295,368],[-149,109],[6,330],[138,79],[47,131],[-10,207],[34,400],[-31,341],[-147,582],[-45,329],[12,328],[-111,375],[-7,169],[-123,230],[-35,451],[-158,456],[-39,245],[122,-249],[-93,535],[137,-167],[83,-223],[-5,294],[-138,454],[-26,181],[-65,173],[31,333],[56,141],[38,289],[-29,336],[114,415],[21,-439],[118,396],[225,193],[136,245],[212,212],[126,45],[77,-71],[219,214],[168,64],[42,126],[74,53],[153,-14],[292,169],[151,256],[71,307],[163,293],[13,229],[7,314],[194,489],[117,-497],[119,115],[-99,272],[87,279],[122,-125],[34,439],[152,283],[67,227],[140,98],[4,161],[122,-67],[5,145],[122,82],[134,78],[205,-264],[155,-342],[173,-3],[177,-54],[-59,316],[133,462],[126,150],[-44,144],[121,329],[168,203],[142,-68],[234,108],[-5,294],[-204,190],[148,84],[184,-143],[148,-236],[234,-148],[79,59],[172,-177],[162,164],[105,-50],[65,111],[127,-285],[-74,-308],[-105,-233],[-96,-19],[32,-230],[-81,-288],[-99,-283],[20,-163],[221,-318],[214,-184],[143,-199],[201,-341],[78,1],[145,-148],[43,-178],[265,-195],[183,197],[55,309],[56,255],[34,316],[85,458],[-39,279],[20,167],[-32,330],[37,434],[53,117],[-43,192],[67,305],[52,317],[7,164],[104,216],[78,-282],[19,-361],[70,-70],[11,-242],[101,-293],[21,-326],[-10,-209]],[[95032,45793],[78,-198],[-194,3],[-106,355],[166,-140],[56,-20]],[[83531,45933],[-117,-11],[-368,403],[259,113],[146,-175],[97,-175],[-17,-155]],[[94680,46144],[-108,-13],[-170,58],[-58,89],[17,228],[183,-90],[91,-121],[45,-151]],[[94910,46301],[-42,-106],[-206,499],[-57,344],[94,0],[100,-461],[111,-276]],[[84713,46708],[32,136],[239,129],[194,20],[87,72],[105,-72],[-102,-156],[-289,-252],[-233,-165]],[[84746,46420],[-181,-430],[-238,-127],[-33,69],[25,196],[119,351],[275,229]],[[82749,47167],[100,-153],[172,47],[69,-245],[-321,-116],[-193,-77],[-149,4],[95,332],[153,5],[74,203]],[[84139,47168],[-41,-320],[-417,-163],[-370,71],[0,210],[220,120],[174,-173],[185,44],[249,211]],[[94409,47028],[12,-116],[-218,245],[-152,206],[-104,192],[41,59],[128,-138],[228,-265],[65,-183]],[[93760,47598],[-56,-33],[-121,131],[-114,237],[14,96],[166,-243],[111,-188]],[[80172,47926],[533,-57],[61,237],[515,-277],[101,-373],[417,-105],[341,-342],[-317,-220],[-306,232],[-251,-15],[-288,42],[-260,104],[-322,220],[-204,57],[-116,-72],[-506,237],[-48,247],[-255,43],[191,550],[337,-34],[224,-225],[115,-44],[38,-205]],[[87423,48251],[-143,-393],[-27,434],[49,207],[58,195],[63,-169],[0,-274]],[[93299,47902],[-78,-58],[-120,221],[-122,366],[-59,439],[38,55],[30,-171],[84,-130],[135,-366],[131,-195],[-39,-161]],[[92217,48675],[-146,-48],[-44,-161],[-152,-140],[-142,-135],[-148,1],[-228,167],[-158,161],[23,178],[249,-84],[152,45],[42,276],[40,14],[27,-306],[158,44],[78,197],[155,206],[-30,339],[166,11],[56,-94],[-5,-320],[-93,-351]],[[85346,49837],[-104,-191],[-192,106],[-54,248],[281,27],[69,-190]],[[86241,50048],[101,-441],[-234,238],[-232,48],[-157,-38],[-192,20],[65,317],[344,24],[305,-168]],[[92538,49238],[-87,-154],[-52,340],[-65,223],[-126,189],[-158,245],[-200,170],[77,139],[150,-162],[94,-126],[117,-139],[111,-241],[106,-185],[33,-299]],[[89166,50332],[482,-397],[513,-329],[192,-295],[154,-290],[43,-339],[462,-356],[68,-306],[-256,-62],[62,-383],[248,-378],[180,-611],[159,19],[-11,-255],[215,-98],[-84,-108],[295,-243],[-30,-166],[-184,-40],[-69,149],[-238,65],[-281,86],[-216,368],[-158,316],[-144,504],[-362,252],[-235,-164],[-170,-190],[35,-425],[-218,-198],[-155,96],[-288,25]],[[89175,46579],[-247,472],[-282,116],[-69,-164],[-352,-18],[118,469],[175,160],[-72,626],[-134,483],[-538,488],[-229,48],[-417,532],[-82,-279],[-107,-51],[-63,211],[-1,250],[-212,283],[299,207],[198,-11],[-23,153],[-407,1],[-110,343],[-248,106],[-117,285],[374,140],[142,188],[446,-237],[44,-214],[78,-931],[287,-345],[232,611],[319,347],[247,1],[238,-201],[206,-206],[298,-110]],[[84788,52647],[-223,-571],[-209,-111],[-267,113],[-463,-29],[-243,-83],[-39,-436],[248,-512],[150,261],[518,196],[-22,-265],[-121,83],[-121,-337],[-245,-223],[263,-738],[-50,-198],[249,-665],[-2,-378],[-148,-170],[-109,203],[134,471],[-273,-222],[-69,159],[36,222],[-200,338],[21,561],[-186,-175],[24,-671],[11,-824],[-176,-84],[-119,169],[79,530],[-43,556],[-117,4],[-86,395],[115,377],[40,457],[139,868],[58,238],[237,427],[217,-170],[350,-80],[319,24],[275,419],[48,-129]],[[85746,52481],[-15,-503],[-143,57],[-42,-351],[114,-304],[-78,-69],[-112,365],[-82,736],[56,460],[92,210],[20,-315],[164,-50],[26,-236]],[[79393,48459],[-308,-12],[-234,481],[-356,471],[-119,349],[-210,469],[-138,432],[-212,806],[-244,480],[-81,495],[-103,449],[-250,363],[-145,493],[-209,322],[-290,635],[-24,293],[178,-23],[430,-111],[246,-564],[215,-390],[153,-240],[263,-619],[283,-9],[233,-394],[161,-482],[211,-263],[-111,-471],[159,-200],[100,-14],[47,-402],[97,-321],[204,-51],[135,-365],[-70,-716],[-11,-891]],[[80461,52985],[204,-198],[214,108],[56,488],[119,108],[333,125],[199,456],[137,364]],[[81723,54436],[110,215],[236,316]],[[82069,54967],[214,400],[140,450],[112,2],[143,-291],[13,-251],[183,-160],[231,-173],[-20,-226],[-186,-29],[50,-281],[-205,-196]],[[82744,54212],[-158,-520],[204,-545],[-48,-265],[312,-533],[-329,-68],[-93,-393],[12,-522],[-267,-393],[-7,-574],[-107,-881],[-41,205],[-316,-259],[-110,352],[-198,33],[-139,184],[-330,-207],[-101,279],[-182,-32],[-229,67],[-43,772],[-138,160],[-134,493],[-38,504],[32,533],[165,383]],[[85104,56675],[28,-382],[16,-323],[-94,-527],[-102,587],[-130,-292],[89,-425],[-79,-270],[-327,335],[-78,416],[84,274],[-176,273],[-87,-239],[-131,22],[-205,-321],[-46,168],[109,486],[175,161],[151,217],[98,-260],[212,157],[45,257],[196,16],[-16,445],[225,-273],[23,-290],[20,-212]],[[72560,55398],[-242,-132],[-132,458],[-49,828],[126,935],[192,-320],[129,-406],[134,-599],[-42,-600],[-116,-164]],[[33073,57651],[-232,-63],[-50,52],[81,158],[-6,228],[160,75],[58,-20],[-11,-430]],[[84439,57749],[-100,-190],[-87,-363],[-87,-171],[-171,398],[57,154],[70,162],[30,357],[153,34],[-44,-388],[205,556],[-26,-549]],[[82917,57194],[-369,-546],[136,403],[200,355],[167,399],[146,572],[49,-470],[-183,-317],[-146,-396]],[[83856,58678],[166,-179],[177,1],[-5,-240],[-129,-245],[-176,-173],[-10,268],[20,293],[-43,275]],[[84861,58834],[78,-643],[-214,152],[5,-193],[68,-355],[-132,-129],[-11,405],[-84,30],[-43,348],[163,-46],[-4,218],[-169,440],[266,-13],[77,-214]],[[83757,59356],[-74,-498],[-119,288],[-142,438],[238,-21],[97,-207]],[[83700,62485],[171,-164],[85,150],[26,-146],[-46,-239],[95,-413],[-73,-478],[-164,-191],[-43,-465],[62,-458],[147,-64],[123,68],[347,-319],[-27,-313],[91,-139],[-29,-265],[-216,283],[-103,302],[-71,-211],[-177,345],[-253,-86],[-138,128],[14,238],[87,146],[-83,133],[-36,-207],[-137,331],[-41,251],[-11,551],[112,-190],[29,901],[90,522],[169,-1]],[[31780,62327],[-71,-146],[-209,4],[-163,-21],[-16,247],[40,84],[227,-3],[142,-51],[50,-114]],[[28638,62119],[-84,-96],[-156,92],[-159,210],[34,132],[116,40],[64,-19],[187,-52],[147,-138],[46,-158],[-195,-11]],[[30080,63183],[34,98],[217,-3],[165,-148],[73,14],[50,-204],[152,11],[-9,-171],[124,-21],[136,-211],[-103,-235],[-132,126],[-127,-25],[-92,28],[-50,-105],[-106,-36],[-43,140],[-92,-83],[-111,-394],[-71,92],[-14,165]],[[30081,62221],[-185,98],[-131,-40],[-169,42],[-130,-108],[-149,179],[24,186],[256,-80],[210,-46],[100,128],[-127,250],[2,220],[-175,89],[62,159],[170,-25],[241,-90]],[[80649,62586],[-240,-277],[-228,179],[-8,495],[137,261],[304,161],[159,-13],[62,-220],[-122,-254],[-64,-332]],[[6794,62819],[-41,-96],[-69,82],[8,161],[-46,210],[14,64],[48,94],[-19,113],[16,54],[21,-11],[107,-97],[49,-50],[45,-77],[71,-202],[-7,-32],[-108,-123],[-89,-90]],[[6645,63718],[-94,-41],[-47,121],[-32,47],[-3,36],[27,49],[99,-55],[73,-88],[-23,-69]],[[6456,64025],[-9,-63],[-149,17],[21,70],[137,-24]],[[6207,64108],[-15,-33],[-19,8],[-97,20],[-35,130],[-11,23],[74,80],[23,-37],[80,-191]],[[5737,64488],[-33,-57],[-93,105],[14,42],[43,57],[64,-13],[5,-134]],[[27867,64939],[110,-210],[260,65],[98,-136],[235,-356],[173,-260],[92,8],[165,-118],[-20,-162],[205,-23],[210,-236],[-33,-135],[-185,-73],[-187,-29],[-191,46],[-398,-56],[186,321],[-113,150],[-179,38],[-96,166],[-66,328],[-157,-22],[-259,154],[-83,121],[-362,89],[-97,113],[104,144],[-273,29],[-199,-299],[-115,-8],[-40,-141],[-138,-63],[-118,55],[146,178],[60,208],[126,128],[142,112],[210,55],[67,63],[240,-41],[219,-6],[261,-197]],[[28462,65512],[-68,-29],[-70,332],[-104,167],[60,365],[84,-23],[97,-478],[1,-334]],[[83659,64954],[-119,-472],[-146,486],[-32,427],[163,566],[223,436],[127,-172],[-49,-347],[-167,-924]],[[28383,67136],[-303,-92],[-19,213],[130,46],[184,-17],[8,-150]],[[28611,67142],[-48,-409],[-51,73],[4,301],[-124,228],[-1,66],[220,-259]],[[87399,71495],[35,-197],[-156,-349],[-114,185],[-143,-134],[-73,-337],[-181,164],[2,273],[154,344],[158,-67],[114,242],[204,-124]],[[59437,72019],[8,-46],[-285,-234],[-136,74],[-64,232],[132,21]],[[59092,72066],[19,3],[40,139],[200,-8],[253,172],[-188,-245],[21,-108]],[[56583,72391],[152,-194],[216,33],[207,-41],[-7,-100],[151,69],[-35,-170],[-400,-49],[3,95],[-339,112],[52,245]],[[54311,73846],[-100,-453],[41,-179],[-58,-296],[-213,217],[-141,62],[-387,293],[38,296],[325,-53],[284,63],[211,50]],[[52558,75561],[166,-408],[-39,-762],[-126,36],[-113,-192],[-105,153],[-11,694],[-64,330],[153,-29],[139,178]],[[89159,73219],[-104,-460],[48,-288],[-145,-406],[-355,-271],[-488,-36],[-396,-657],[-186,221],[-12,431],[-483,-127],[-329,-271],[-325,-11],[282,-424],[-186,-979],[-179,-242],[-135,224],[69,519],[-176,167],[-113,395],[263,177],[145,362],[280,298],[203,394],[553,171],[297,-117],[291,1024],[185,-275],[408,575],[158,224],[174,704],[-47,648],[117,364],[295,105],[152,-798],[-9,-467],[-256,-580],[4,-594]],[[52655,76104],[-92,-445],[-126,118],[-64,387],[56,214],[179,220],[47,-494]],[[89974,77268],[195,-122],[197,244],[62,-647],[-412,-157],[-244,-572],[-436,393],[-152,-630],[-308,-9],[-39,573],[138,443],[296,32],[81,797],[83,449],[326,-600],[213,-194]],[[32315,78637],[202,-78],[257,16],[-137,-236],[-102,-37],[-353,244],[-69,193],[105,177],[97,-279]],[[32831,80108],[-135,-10],[-360,180],[-258,272],[96,49],[365,-145],[284,-240],[8,-106]],[[15692,79765],[-140,-80],[-456,262],[-84,204],[-248,202],[-50,164],[-286,103],[-107,314],[24,133],[291,-125],[171,-88],[261,-61],[94,-198],[138,-274],[277,-238],[115,-318]],[[34407,81019],[-184,-504],[181,195],[187,-124],[-98,-200],[247,-158],[128,140],[277,-177],[-86,-422],[194,99],[36,-306],[86,-358],[-117,-507],[-125,-21],[-183,109],[60,471],[-77,73],[-322,-499],[-166,20],[196,270],[-267,140],[-298,-34],[-539,17],[-43,171],[173,202],[-121,157],[234,347],[287,917],[172,328],[241,198],[129,-25],[-54,-156],[-148,-363]],[[13136,82950],[267,46],[-84,-654],[242,-463],[-111,1],[-167,264],[-103,265],[-140,179],[-51,253],[16,184],[131,-75]],[[89901,81054],[280,-1020],[-411,190],[-171,-832],[271,-590],[-8,-403],[-211,347],[-182,-445],[-51,483],[31,561],[-32,621],[64,436],[13,770],[-163,566],[24,787],[257,265],[-110,267],[123,81],[73,-381],[96,-555],[-7,-567],[114,-581]],[[47896,83579],[233,23],[298,-356],[-149,-395]],[[48278,82851],[46,-412],[-210,-514],[-493,-340],[-393,87],[225,601],[-145,586],[378,451],[210,269]],[[53524,83854],[-166,-466],[-291,325],[-39,239],[408,191],[88,-289]],[[7498,84721],[-277,-219],[-142,148],[-43,270],[252,205],[148,88],[185,-39],[117,-179],[-240,-274]],[[49420,84027],[270,-740]],[[49690,83287],[190,-93],[171,-656],[79,-227],[337,-110],[-34,-368],[-142,-169],[111,-298],[-250,-302],[-371,6],[-473,-159],[-130,114],[-183,-270],[-257,65],[-195,-220],[-148,115],[407,605],[249,125]],[[49051,81445],[-436,96]],[[48615,81541],[-79,229],[291,179],[-152,310],[52,377]],[[48727,82636],[414,-52]],[[49141,82584],[40,334]],[[49181,82918],[-190,363]],[[48991,83281],[-337,101],[-66,156],[101,258],[-92,158],[-149,-272],[-17,555],[-140,294],[101,595],[216,467],[222,-45],[335,48],[-297,-623],[283,79],[304,-3],[-72,-469],[-250,-516],[287,-37]],[[4006,86330],[-171,-89],[-182,107],[-168,157],[274,98],[220,-52],[27,-221]],[[27981,87625],[-108,-302],[-123,49],[-73,171],[13,40],[107,173],[114,-13],[70,-118]],[[27250,87943],[-325,-317],[-196,13],[-61,156],[207,265],[381,-5],[-6,-112]],[[2297,88560],[171,-109],[173,59],[225,-152],[276,-77],[-23,-63],[-211,-121],[-211,125],[-106,104],[-245,-33],[-66,51],[17,216]],[[26344,89640],[51,-253],[143,89],[161,-151],[304,-198],[318,-179],[25,-274],[204,45],[199,-191],[-247,-181],[-432,138],[-156,259],[-275,-306],[-396,-298],[-95,337],[-377,-55],[242,284],[35,454],[95,527],[201,-47]],[[45969,90100],[-64,-373],[314,-392],[-361,-440],[-801,-394],[-240,-105],[-365,85],[-775,182],[273,254],[-605,282],[492,112],[-12,169],[-583,134],[188,375],[421,85],[433,-391],[422,314],[349,-163],[453,307],[461,-41]],[[28926,90499],[-312,-29],[-69,282],[118,323],[255,80],[217,-160],[3,-246],[-32,-80],[-180,-170]],[[0,91544],[681,-440],[728,-572],[-24,-358],[187,-143],[-64,418],[754,-86],[544,-539],[-276,-251],[-455,-59],[-7,-563],[-111,-120],[-260,17],[-212,201],[-369,168],[-62,250],[-283,94],[-315,-74],[-151,201],[60,214],[-333,-137],[126,-271],[-158,-244]],[[0,89250],[0,2294]],[[23431,91627],[-173,-202],[-374,175],[-226,-63],[-380,259],[245,178],[194,250],[295,-164],[166,-103],[84,-110],[169,-220]],[[99999,92620],[-305,-29],[-49,183],[-99645,240]],[[0,93014],[99999,-394]],[[0,93014],[36,24],[235,-1],[402,-165],[-24,-79],[-286,-138],[-363,-35]],[[0,92620],[0,394]],[[27392,90477],[-544,-402],[-386,-89]],[[26462,89986],[-287,173],[-83,-289],[-268,-486],[-81,-252],[-322,-389],[-397,-38],[-220,-244],[-18,-374],[-323,-72],[-340,-467],[-301,-648],[-108,-454]],[[23714,86446],[-15,-669],[408,-96]],[[24107,85681],[125,-539],[130,-437],[388,114],[517,-250],[277,-219],[199,-272],[348,-158],[294,-243],[459,-33],[302,-56],[-45,-499],[86,-578],[201,-645],[414,-547],[214,188],[150,592],[-145,909],[-196,303],[445,270],[314,404],[154,401]],[[28738,84386],[-22,385],[-189,489]],[[28527,85260],[-338,434],[328,603],[-121,522],[-93,899],[194,133],[476,-157],[286,-56],[230,152],[258,-196],[342,-333],[85,-224]],[[30174,87037],[495,-43],[-8,-484]],[[30661,86510],[92,-728],[254,-90],[201,-339],[402,319],[266,636]],[[31876,86308],[184,268],[216,-515]],[[32276,86061],[362,-734],[307,-691],[-112,-362],[370,-325],[250,-329],[442,-149],[179,-183],[110,-488],[216,-76],[112,-217],[20,-647],[-202,-217],[-199,-202],[-458,-205],[-349,-473],[-470,-93],[-594,121],[-417,4],[-287,-40],[-233,-413],[-354,-255],[-401,-762],[-320,-532],[236,95],[446,756],[583,480]],[[31513,80124],[416,58],[245,-283]],[[32174,79899],[-262,-387],[88,-620],[91,-435],[361,-287],[459,83],[278,647],[19,-417],[180,-209],[-344,-377],[-615,-343],[-276,-233],[-310,-415],[-211,43],[-11,487],[483,476],[-445,-19],[-309,-70]],[[31350,77823],[48,-189],[-296,-279],[-286,-198],[-293,-171]],[[30523,76986],[-159,-376],[-35,-95]],[[30329,76515],[-3,-306],[92,-305],[115,-14],[-29,210],[83,-128],[-22,-165],[-188,-93],[-133,11],[-205,-100],[-121,-29],[-162,-28],[-231,-167],[408,108],[82,-109],[-389,-173],[-177,-1],[8,71],[-84,-160],[82,-26],[-60,-414],[-203,-443],[-20,148],[-61,30],[-91,144],[57,-310]],[[29077,74266],[66,-103],[8,-217]],[[29151,73946],[-89,-224],[-157,-460],[-25,23],[86,392],[-142,220],[-33,478],[-53,-249],[59,-365]],[[28797,73761],[-175,86],[183,-181]],[[28805,73666],[12,-548],[79,-40],[29,-199],[39,-577],[-176,-427],[-288,-171],[-182,-338],[-139,-37],[-141,-211],[-39,-193],[-305,-374],[-157,-274],[-131,-342],[-43,-409],[50,-400],[92,-492],[124,-408],[1,-249],[132,-668],[-9,-388],[-12,-224],[-69,-352],[-83,-73],[-137,70],[-44,253]],[[27408,66595],[-106,132],[-147,496]],[[27155,67223],[-129,440]],[[27026,67663],[-42,226],[57,382]],[[27041,68271],[-77,317],[-217,481]],[[26747,69069],[-108,89],[-281,-262],[-49,29],[-135,269],[-174,142],[-314,-72],[-247,63],[-212,-39]],[[25227,69288],[-118,-81],[54,-162]],[[25163,69045],[-5,-234],[59,-113],[-53,-76],[-103,85],[-104,-109]],[[24957,68598],[-202,18],[-207,304]],[[24548,68920],[-242,-72],[-202,133],[-173,-40],[-234,-135],[-253,-427],[-276,-248],[-152,-275],[-63,-259],[-3,-397],[14,-277],[52,-196]],[[23016,66727],[1,-1],[-1,-1],[-107,-503]],[[22909,66222],[-49,-415],[-20,-771],[-27,-281],[48,-315],[86,-280],[56,-447],[184,-429],[65,-328],[109,-284],[295,-153],[114,-241],[244,161],[212,58],[208,104],[175,99],[176,235],[67,336],[22,483],[48,169],[188,151],[294,133],[246,-20],[169,49],[66,-122],[-9,-278],[-149,-342],[-66,-351],[51,-100],[-42,-249],[-69,-449],[-71,148],[-58,-10]],[[25472,62483],[1,-84],[53,-3],[-5,-157],[-45,-249],[24,-89],[-29,-206],[18,-55],[-32,-291],[-55,-153],[-50,-18],[-55,-199]],[[25297,60979],[90,-105],[24,86],[82,-73]],[[25493,60887],[29,-23],[61,101],[79,9],[26,-47],[43,28],[129,-52],[128,15],[90,64],[32,65],[89,-30],[66,-39],[73,13],[55,50],[127,-80],[44,-13],[85,-107],[80,-129],[101,-88],[73,-159]],[[26903,60465],[-24,-55],[-14,-129],[29,-210],[-64,-197],[-30,-231],[-9,-254],[15,-148],[7,-260],[-43,-56],[-26,-247],[19,-152],[-56,-147],[12,-156],[43,-94]],[[26762,58129],[70,-313],[108,-232],[130,-246]],[[27070,57338],[100,-206],[-6,-122],[111,-26],[26,47],[77,-142],[136,42],[119,145],[168,116]],[[27801,57192],[95,173],[153,-34]],[[28049,57331],[-10,-57],[155,-20],[124,-99],[90,-173]],[[28408,56982],[105,-160],[143,-18]],[[28656,56804],[209,402],[114,62],[3,190],[51,487],[159,267],[175,11],[22,120],[218,-48],[218,291],[109,128],[134,278],[98,-36],[73,-151],[-54,-194]],[[30185,58611],[-8,-136],[-163,-67],[91,-262],[-3,-301],[-123,-334],[105,-457],[120,37],[62,417],[-86,202],[-14,436],[346,234],[-38,272],[97,181],[100,-404],[195,-10],[180,-321],[11,-190]],[[31057,57908],[249,-5],[297,59]],[[31603,57962],[159,-258],[213,-71],[155,180],[4,145],[344,34],[333,8],[-236,-170],[95,-272],[222,-43],[210,-283]],[[33102,57232],[45,-461],[144,13],[109,-136]],[[33400,56648],[183,-212],[171,-375],[8,-297],[105,-13],[149,-281],[109,-201]],[[34125,55269],[333,-115],[30,104],[225,41],[298,-155]],[[35011,55144],[95,-63],[204,-136],[294,-486],[46,-236]],[[35650,54223],[95,27],[69,-318],[155,-1008],[149,-95],[7,-397],[-208,-474],[86,-174],[491,-90]],[[36494,51694],[10,-577],[211,377]],[[36715,51494],[349,-207],[462,-351],[135,-338],[-45,-319],[323,178],[540,-305],[415,23],[411,-477],[355,-645],[214,-166],[237,-23],[101,-182],[94,-733],[46,-348],[-110,-953],[-142,-376],[-391,-801],[-177,-651],[-206,-499],[-69,-11],[-78,-424],[20,-1079],[-77,-888],[-30,-379],[-88,-228],[-49,-769],[-282,-752],[-47,-595]],[[38626,39196],[-225,-249],[-65,-346]],[[38336,38601],[-302,2],[-437,-222],[-195,-256],[-311,-168],[-327,-459],[-235,-571],[-41,-430],[46,-318],[-51,-582],[-63,-281],[-195,-317],[-308,-1013],[-244,-457],[-189,-269],[-127,-548],[-183,-329]],[[35174,32383],[-121,-362],[-313,-320],[-205,115],[-151,-62],[-256,247],[-189,-18]],[[33939,31983],[-169,318],[-19,-300]],[[33751,32001],[353,-493],[-38,-397],[173,-251],[-14,-282],[-267,-738],[-412,-309],[-557,-120],[-305,58],[59,-343],[-57,-431],[51,-291],[-167,-202],[-284,-80],[-267,210],[-108,-151],[39,-572],[188,-173],[152,181],[82,-299],[-255,-179],[-223,-358],[-41,-579],[-66,-309],[-262,-1],[-218,-295],[-80,-432]],[[31227,25165],[274,-422],[265,-116]],[[31766,24627],[-96,-517],[-328,-325],[-180,-675],[-254,-227],[-113,-270],[89,-598],[185,-333],[-117,29]],[[30952,21711],[-247,4],[-134,-141],[-250,-208],[-45,-538],[-118,-14],[-313,188],[-318,401],[-346,329],[-87,365],[79,337],[-140,383],[-36,982],[119,554],[293,445],[-422,168],[265,509],[94,956],[309,-202],[145,1193],[-186,153],[-87,-719],[-175,81],[87,823],[95,1067],[127,394]],[[29661,29221],[-79,562],[-23,649]],[[29559,30432],[117,18],[170,930],[192,922],[118,858],[-64,863],[83,475],[-34,711],[163,703],[50,1114],[89,1196],[87,1287],[-20,943],[-58,811]],[[30452,41263],[-279,331],[-24,236],[-551,578],[-498,630],[-214,355],[-115,476],[46,166],[-236,755],[-274,1063],[-262,1147],[-114,262],[-87,424],[-216,376],[-198,233],[90,257],[-134,550],[86,403],[221,364]],[[27693,49869],[148,430],[-60,251],[-106,-267],[-166,252],[56,163],[-47,522],[97,87],[52,359],[105,371],[-20,235],[153,123],[190,230]],[[28095,52625],[-37,178],[103,44],[-12,288],[65,209],[138,38],[117,362],[106,302],[-102,137],[52,335],[-62,526],[59,152],[-44,487],[-112,306]],[[28366,55989],[-93,167],[-59,310]],[[28214,56466],[68,154],[-70,40],[-52,190],[-138,160],[-122,-37],[-56,-200],[-112,-145],[-61,-20],[-27,-120],[132,-312],[-75,-74],[-40,-85]],[[27661,56017],[-130,-30],[-48,344],[-36,-97]],[[27447,56234],[-92,33],[-56,232],[-114,38],[-72,68],[-119,-1],[-8,-125],[-32,87]],[[26954,56566],[-151,128],[-56,121],[32,100],[-11,127],[-77,138],[-109,113],[-95,74],[-19,168],[-73,103],[18,-167],[-55,-138],[-64,160],[-89,57],[-38,116],[2,175],[36,182],[-78,81],[64,111]],[[26191,58215],[-96,181],[-130,233],[-61,194],[-117,181],[-140,260],[31,89],[46,-87],[21,41]],[[25745,59307],[-48,180],[-84,50]],[[25613,59537],[-31,-135],[-161,8],[-100,55],[-115,115],[-154,36],[-79,123]],[[24973,59739],[-142,101],[-174,10],[-127,114],[-149,238]],[[24381,60202],[-314,620],[-144,187],[-226,150],[-156,-42],[-223,-216],[-140,-57],[-196,152],[-208,109],[-260,264],[-208,81],[-314,268],[-233,275],[-70,154],[-155,34],[-284,183],[-116,262],[-299,327],[-139,363],[-66,281],[93,56],[-29,164],[64,150],[1,199],[-93,259],[-25,229],[-94,290],[-244,573],[-280,450],[-135,359],[-238,235],[-51,140],[42,356],[-142,135],[-164,279],[-69,402],[-149,47]],[[19117,67920],[-162,304],[-130,280]],[[18825,68504],[-12,180],[-149,434],[-99,441],[5,221],[-201,229],[-93,-26],[-159,159],[-44,-234],[46,-276],[27,-433],[95,-237],[206,-397],[46,-135],[42,-41],[37,-198],[49,8],[56,-372],[85,-146],[59,-204],[174,-293],[92,-536],[83,-252],[77,-270],[15,-304],[134,-19],[112,-261],[100,-257]],[[19608,65285],[-6,-103],[-117,-212]],[[19485,64970],[-49,3],[-74,350]],[[19362,65323],[-182,328],[-200,278]],[[18980,65929],[-142,147],[9,421],[-42,312],[-132,179],[-191,257]],[[18482,67245],[-37,-74],[-70,150]],[[18375,67321],[-171,139],[-164,334],[20,44],[115,-33],[103,215],[10,260],[-214,411],[-163,159],[-102,360],[-103,377],[-129,461],[-113,518]],[[17464,70566],[-46,294],[-180,331],[-130,69],[-30,165],[-156,29],[-100,156],[-258,57]],[[16564,71667],[-70,93],[-34,316]],[[16460,72076],[-270,578],[-231,801],[10,133],[-123,190],[-215,483],[-38,469],[-148,315],[61,477],[-10,494]],[[15496,76016],[-89,442],[109,542]],[[15516,77000],[67,1045]],[[15583,78045],[-50,773]],[[15533,78818],[-88,493],[-80,267]],[[15365,79578],[33,112],[402,-195],[148,-544]],[[15948,78951],[68,152],[-44,472]],[[15972,79575],[-94,473]],[[15878,80048],[-38,1],[-537,566],[-199,248],[-503,239],[-155,510],[40,353],[-356,245]],[[14130,82210],[-48,465],[-336,418]],[[13746,83093],[-6,296]],[[13740,83389],[-153,217]],[[13587,83606],[-245,184],[-78,502]],[[13264,84292],[-358,466],[-150,543],[-267,38],[-441,14],[-326,165],[-574,598],[-266,109],[-486,206],[-385,-49],[-546,264],[-330,246],[-309,-122],[58,-400],[-154,-37],[-321,-120],[-245,-195]],[[8164,86018],[-307,-122],[-40,339]],[[7817,86235],[125,565],[295,177],[-76,145],[-354,-321],[-190,-383],[-400,-410],[203,-280],[-262,-413],[-299,-241],[-278,-176],[-69,-255],[-434,-297],[-87,-271],[-325,-246],[-191,44],[-259,-160],[-282,-196],[-231,-193],[-477,-164]],[[4226,83160],[-43,97],[304,269]],[[4487,83526],[271,177],[296,315],[345,65],[137,236],[385,345],[62,115],[205,204],[48,437],[141,340],[-320,-175],[-90,99],[-150,-209],[-181,292],[-75,-207],[-104,287],[-278,-230],[-170,0],[-24,343],[50,211],[-179,205],[-361,-110],[-235,270],[-190,138],[-1,327],[-214,245],[108,331],[226,322],[99,295],[225,42],[191,-92],[224,278],[201,-50],[212,179],[-52,263],[-155,104],[205,222],[-170,-7],[-295,-125],[-85,-127],[-219,127],[-392,-65],[-407,138],[-117,232]],[[3654,89313],[-351,335],[390,240]],[[3693,89888],[620,282],[228,0],[-38,-288],[586,22],[-225,357],[-342,219],[-197,288],[-267,246],[-381,182],[155,302]],[[3832,91498],[493,18],[350,263]],[[4675,91779],[66,280],[284,274],[271,66],[526,256],[256,-39],[427,307],[421,-121],[201,-260],[123,112],[469,-35],[-16,-132],[425,-98],[283,57],[585,-182],[534,-54],[214,-75],[370,94],[421,-173],[302,-81]],[[10837,91975],[518,-139],[438,-277],[289,-53],[244,241]],[[12326,91747],[336,180],[413,-71]],[[13075,91856],[416,253],[455,144],[191,-239],[207,134],[62,272],[192,-62],[470,-516],[369,390],[38,-437],[341,95],[105,168],[337,-33],[424,-242],[650,-211],[383,-98],[272,37]],[[17987,91511],[375,-292],[-391,-286]],[[17971,90933],[502,-123],[750,68],[236,100],[296,-345],[302,291],[-283,245],[179,197],[338,26],[223,58],[224,-138],[279,-312],[310,46],[491,-260],[431,91],[405,-13],[-32,358]],[[22622,91222],[247,101],[431,-196]],[[23300,91127],[-2,-545],[177,459],[223,-15],[126,579],[-298,355],[-324,233],[22,636],[329,418],[366,-92],[281,-255],[378,-649],[-247,-283],[517,-116],[-1,-589],[371,451],[332,-371],[-83,-427],[269,-388],[290,416],[202,497],[16,632],[394,-44],[411,-85],[373,-286],[17,-285],[-207,-307],[196,-309],[-36,-280]],[[19722,91438],[-824,-101],[-374,-39]],[[18524,91298],[-151,271],[-379,157],[-246,-64],[-343,456],[185,61],[429,99],[392,-26],[362,100],[-537,135],[-594,-46],[-394,11],[-146,213],[644,230],[-428,-8],[-485,152],[233,431],[193,229],[744,351],[284,-111],[-139,-270],[618,174],[386,-291],[314,294],[254,-188],[227,-566],[140,238],[-197,590],[244,85],[276,-93],[311,-232],[175,-561],[86,-406],[466,-285],[502,-273],[-31,-253],[-456,-47],[178,-221],[-94,-211],[-503,90],[-478,156],[-322,-35],[-522,-196]],[[20972,94111],[-244,-381],[-434,404],[95,80],[372,23],[211,-126]],[[28794,93928],[25,-159],[-296,16],[-299,13],[-304,-78],[-80,35],[-306,306],[12,207],[133,38],[636,-62],[479,-316]],[[25955,93959],[219,-359],[256,465],[704,236],[477,-596],[-42,-377],[550,168],[263,228],[616,-291],[383,-274],[36,-252],[515,131],[290,-367],[670,-228],[242,-232],[263,-539],[-510,-268],[654,-376],[441,-127],[400,-529],[437,-38],[-87,-404],[-487,-669],[-342,246],[-437,554],[-359,-72],[-35,-330],[292,-335],[377,-265],[114,-153],[181,-570],[-96,-414],[-350,156],[-697,461],[393,-496],[289,-348],[45,-201],[-753,230],[-596,334],[-337,281],[97,162],[-414,296],[-405,280],[5,-167],[-803,-92],[-235,198],[183,424],[522,10],[571,74],[-92,205],[96,287],[360,561],[-77,255],[-107,197],[-425,280],[-563,196],[178,145],[-294,358],[-245,33],[-219,196],[-149,-170],[-503,-74],[-1011,129],[-588,169],[-450,87],[-231,202],[290,263],[-394,2],[-88,583],[213,515],[286,235],[717,154],[-204,-373]],[[22123,94355],[331,-122],[496,73],[72,-167],[-259,-276],[420,-248],[-50,-518],[-455,-223],[-268,48],[-192,220],[-690,444],[5,185],[567,-72],[-306,377],[329,279]],[[89889,93991],[-421,-4],[-569,64],[-49,31],[263,227],[348,54],[394,-221],[34,-151]],[[24112,93737],[-298,-430],[-317,21],[-173,506],[4,287],[145,244],[276,157],[579,-20],[530,-140],[-415,-513],[-331,-112]],[[15808,92660],[-147,253],[-641,304]],[[15020,93217],[93,188],[218,477]],[[15331,93882],[241,378],[-272,353],[939,90],[397,-119],[709,-32],[270,-167],[298,-243],[-349,-145],[-681,-405],[-344,-403]],[[16539,93189],[0,-242],[-731,-287]],[[91869,95069],[-321,-228],[-444,52],[-516,227],[66,187],[518,-87],[697,-151]],[[23996,95009],[-151,-223],[-403,43],[-337,150],[148,259],[399,155],[243,-202],[101,-182]],[[90301,95344],[-219,-427],[-1023,16],[-461,-136],[-550,374],[149,396],[366,108],[734,-25],[1004,-306]],[[22639,96011],[212,-267],[9,-295],[-127,-429],[-458,-59],[-298,92],[5,336],[-455,-44],[-18,445],[299,-18],[419,197],[390,-34],[22,76]],[[19941,95712],[109,-205],[247,97],[291,-25],[49,-282],[-169,-274],[-940,-89],[-701,-249],[-423,-13],[-35,187],[577,255],[-1255,-69],[-389,103],[379,563],[262,161],[782,-194],[493,-341],[485,-44],[-397,551],[255,210],[286,-67],[94,-275]],[[65981,92556],[-164,-51],[-907,75],[-74,256],[-503,154],[-40,311],[284,124],[-10,314],[551,491],[-255,70],[665,506],[-75,261],[621,304],[917,370],[925,108],[475,214],[541,74],[193,-227],[-187,-179],[-984,-286],[-848,-274],[-863,-548],[-414,-563],[-435,-553],[56,-479],[531,-472]],[[23699,96229],[308,-186],[547,2],[240,-190],[-64,-216],[319,-130],[177,-137],[374,-26],[406,-48],[441,125],[566,49],[451,-40],[298,-218],[62,-238],[-174,-153],[-414,-124],[-355,70],[-797,-88],[-570,-11],[-449,71],[-738,186],[-96,316],[-34,286],[-279,251],[-574,70],[-322,179],[104,236],[573,-36]],[[17722,96544],[-38,-443],[-214,-199],[-259,-29],[-517,-246],[-444,-88],[-377,124],[472,431],[570,373],[426,-8],[381,85]],[[0,89247],[99640,-253],[-360,42],[250,-307],[166,-474],[128,-155],[32,-238],[-71,-153],[-518,126],[-777,-434],[-247,-67],[-425,-405],[-403,-353],[-102,-262]],[[97313,86314],[-397,398],[-724,-451],[-126,213]],[[96066,86474],[-268,-246],[-371,79],[-90,-379],[-333,-557],[10,-233],[316,-129],[-37,-839],[-258,-21],[-119,-482],[116,-248],[-486,-294],[-96,-657],[-415,-141],[-83,-585],[-400,-536],[-103,396],[-119,841],[-155,1279],[134,799],[234,344]],[[93543,84865],[15,269],[431,129]],[[93989,85263],[496,725],[479,592],[499,459],[223,812]],[[95686,87851],[-337,-48],[-167,-475]],[[95182,87328],[-705,-632],[-227,708],[-717,-196],[-696,-965],[230,-353],[-620,-151],[-430,-59],[20,417],[-431,87],[-344,-283],[-850,99]],[[90412,86000],[-913,-171],[-900,-1124]],[[88599,84705],[-1065,-1358]],[[87534,83347],[438,-72],[136,-361]],[[88108,82914],[270,-128],[178,288],[305,-38],[401,-633],[9,-490],[-217,-576],[-23,-687],[-126,-921],[-418,-833],[-94,-399],[-377,-670],[-374,-665],[-179,-340],[-370,-338],[-175,-8],[-175,280],[-373,-421],[-43,-192]],[[86327,76143],[-106,35],[-120,-195],[-83,-196],[10,-414],[-143,-127],[-50,-102],[-104,-170],[-185,-95],[-121,-154],[-9,-250],[-32,-63],[111,-94],[157,-253]],[[85652,74065],[240,-679],[68,-373],[3,-664],[-105,-316],[-252,-111],[-222,-239],[-250,-49]],[[85134,71634],[-31,314],[52,431],[-123,600]],[[85032,72979],[206,97],[-190,493]],[[85048,73569],[-135,109],[-34,-108],[-81,-48],[-10,109],[-72,52],[-75,92]],[[84641,73775],[77,254],[65,67]],[[84783,74096],[-25,106],[71,310]],[[84829,74512],[-18,94],[-163,63],[-131,154]],[[84517,74823],[-388,-167],[-204,-269],[-300,-157],[148,267],[-58,224],[220,387],[-147,302],[-242,-204],[-314,-400],[-171,-372],[-272,-28],[-142,-268],[147,-390],[227,-94],[9,-259],[220,-168],[311,411],[247,-224],[179,-15]],[[83987,73399],[46,-302],[-394,-161]],[[83639,72936],[-130,-311],[-270,-289],[-142,-403],[299,-316],[109,-567],[169,-527],[189,-443],[-5,-428],[-174,-157],[66,-307],[164,-179],[-43,-469],[-71,-456],[-155,-52],[-203,-623],[-225,-756],[-258,-687],[-382,-532],[-386,-484],[-313,-67],[-170,-255],[-96,186],[-157,-286],[-388,-288],[-294,-88],[-95,-609],[-154,-33],[-73,418],[66,222],[-373,185],[-131,-94]],[[80013,64241],[-371,-493],[-231,-544],[-61,-399],[212,-607],[260,-753],[252,-356],[169,-462],[127,-1066],[-37,-1013],[-232,-379],[-318,-371],[-227,-480],[-346,-536],[-101,369],[78,390],[-206,327]],[[78981,57868],[-233,84],[-112,301],[-141,594]],[[78495,58847],[-249,265],[-238,-11],[41,452],[-245,-3],[-22,-633],[-150,-841],[-90,-509],[19,-417],[181,-18],[113,-526],[50,-498],[155,-330],[168,-67],[144,-299]],[[78372,55412],[64,-54],[164,-347],[116,-386],[16,-388],[-29,-262],[27,-198],[20,-340],[98,-159],[109,-509],[-5,-195],[-197,-38],[-263,426],[-329,457],[-32,294],[-161,385],[-38,477],[-100,314],[30,419],[-61,244]],[[77801,55552],[-110,221],[-47,285],[-148,325],[-135,274],[-45,-339],[-53,320],[30,359],[82,553]],[[77375,57550],[-27,427],[86,441],[-94,341],[23,627],[-113,299],[-90,689],[-50,727],[-121,477]],[[76989,61578],[-183,-288],[-315,-411]],[[76491,60879],[-156,51],[-172,135],[96,714],[-58,539],[-218,664],[34,208],[-163,74],[-197,469]],[[75657,63733],[-79,301],[-16,293],[-53,277],[-116,335],[-256,23],[25,-237]],[[75162,64725],[-87,-320],[-118,116]],[[74957,64521],[-41,-105],[-78,63],[-108,52]],[[74730,64531],[-39,-210],[-189,7],[-343,-120],[16,-433],[-148,-341],[-400,-387],[-311,-678],[-209,-363],[-276,-377],[-1,-265],[-138,-142]],[[72692,61222],[-250,-206],[-130,-31]],[[72312,60985],[-84,-439],[58,-749],[15,-478],[-118,-547],[-1,-978],[-144,-28],[-126,-439],[84,-190],[-253,-163],[-93,-392],[-112,-165],[-263,537],[-128,807],[-107,581],[-97,272],[-148,553],[-69,720],[-48,360],[-253,791],[-115,1116],[-83,737],[1,698],[-54,539],[-404,-345],[-196,69],[-362,698],[133,208],[-82,226],[-326,489]],[[68937,65473],[-203,146],[-83,414],[-215,438],[-512,-108],[-451,-11],[-391,-81]],[[67082,66271],[-523,174]],[[66559,66445],[-302,133],[-314,74]],[[65943,66652],[-118,707],[-133,102],[-214,-103],[-280,-279],[-339,191],[-281,443],[-267,164],[-186,546],[-205,768],[-149,-93],[-177,190]],[[63594,69288],[-103,-224],[-165,28]],[[63326,69092],[58,-254],[-25,-132],[89,-434]],[[63448,68272],[109,-497],[137,-131]],[[63694,67644],[47,-202],[190,-243]],[[63931,67199],[16,-237],[-27,-192],[35,-193],[80,-162],[37,-189],[41,-141]],[[64113,66085],[-18,419],[75,302],[76,62],[84,-180],[5,-337],[-61,-339]],[[64274,66012],[53,-220]],[[64327,65792],[49,28],[11,-158],[217,91],[230,-15],[168,-17],[190,389],[207,369],[176,355]],[[65575,66834],[80,196],[35,-50],[-26,-238],[-37,-104]],[[65627,66638],[38,-455]],[[65665,66183],[125,-393],[155,-209]],[[65945,65581],[204,-76],[164,-105]],[[66313,65400],[125,-330],[75,-191],[100,-73],[-1,-128],[-101,-344],[-44,-161],[-117,-184],[-104,-395],[-126,30],[-58,-137],[-44,-292],[34,-385],[-26,-71],[-128,2],[-174,-215],[-27,-281],[-63,-121]],[[65634,62124],[-173,5],[-109,-146]],[[65352,61983],[1,-232],[-134,-160],[-153,54],[-186,-194],[-128,-33]],[[64752,61418],[-201,-154],[-54,-256],[-6,-196],[-277,-244],[-444,-268],[-249,-406],[-122,-32],[-83,34],[-163,-239],[-177,-111],[-233,-30],[-70,-33],[-61,-152],[-73,-42]],[[62539,59289],[-42,-146],[-138,13],[-89,-79]],[[62270,59077],[-192,30],[-72,336],[8,315],[-46,170],[-54,426],[-80,236],[56,28],[-29,264],[34,111],[-12,251]],[[61883,61244],[-36,246],[-84,173]],[[61763,61663],[-22,230],[-143,206],[-148,483],[-79,469],[-192,397],[-124,94],[-184,549],[-32,400],[12,342],[-159,638],[-130,225],[-150,119],[-92,330],[15,130],[-77,299],[-81,128],[-108,429],[-170,464]],[[59899,67595],[-141,396],[-139,-3]],[[59619,67988],[44,316],[12,201],[34,230]],[[59709,68735],[-9,84]],[[59700,68819],[-78,-232],[-60,-435],[-75,-300],[-65,-100],[-93,186],[-125,257],[-198,825],[-29,-52],[115,-608],[171,-579],[210,-897],[102,-313],[90,-325],[249,-638],[-55,-100],[9,-374],[323,-517],[49,-118]],[[60240,64499],[90,-565],[-61,-105],[40,-593],[102,-687],[106,-142],[152,-213]],[[60669,62194],[161,-666],[77,-529],[152,-281],[379,-544],[154,-328],[151,-332],[87,-198],[136,-173]],[[61966,59143],[66,-178],[-9,-240],[-158,-137],[119,-158]],[[61984,58430],[91,-106],[54,-238],[125,-241],[138,-2],[262,147],[302,68],[245,179],[138,38],[99,105]],[[63438,58380],[158,21],[89,11]],[[63685,58412],[128,85],[147,58],[132,198],[105,1],[6,-159],[-25,-335],[1,-303],[-59,-208],[-78,-622],[-134,-644],[-172,-735],[-238,-844],[-237,-645],[-327,-785]],[[62934,53474],[-278,-466],[-415,-572]],[[62241,52436],[-259,-438],[-304,-698],[-64,-304],[-63,-136]],[[61551,50860],[-195,-230],[-68,-240],[-104,-42],[-40,-406],[-89,-233],[-54,-383],[-112,-190]],[[60889,49136],[-128,-709],[16,-327]],[[60777,48100],[178,-210],[8,-149],[-76,-348],[16,-175],[-18,-275],[97,-361],[115,-568],[101,-126]],[[61198,45888],[45,-258],[-11,-574],[34,-505],[11,-900],[49,-282],[-83,-412],[-108,-400],[-177,-357],[-254,-219],[-313,-279],[-313,-618],[-107,-106],[-194,-409],[-115,-133],[-23,-411],[132,-436],[54,-337],[4,-173],[49,29],[-8,-565],[-45,-267],[65,-99],[-41,-239],[-116,-205],[-229,-195],[-334,-312],[-122,-213],[24,-242],[71,-39],[-24,-303]],[[59119,36429],[-70,-419],[-32,-479],[-72,-260],[-190,-290],[-54,-84],[-118,-292],[-77,-296],[-158,-413],[-314,-594],[-196,-345]],[[57838,32957],[-209,-262],[-291,-224]],[[57338,32471],[-141,-30],[-36,-160],[-169,85],[-138,-109],[-301,111]],[[56553,32368],[-168,-70],[-115,30]],[[56270,32328],[-286,-228],[-238,-91],[-171,-218],[-127,-13],[-117,205]],[[55331,31983],[-94,11],[-120,257]],[[55117,32251],[-13,-80],[-37,155],[2,337],[-90,386],[89,105],[-7,442],[-182,539],[-139,488],[-1,1],[-199,749]],[[54540,35373],[-207,435],[-108,420],[-62,561],[-68,417],[-93,887],[-7,689],[-35,314],[-108,237],[-144,476],[-146,691],[-60,361],[-226,563],[-17,441]],[[53259,41865],[-26,363],[38,506],[96,527],[15,247],[90,519],[66,236],[159,377]],[[53697,44640],[90,257],[29,426]],[[53816,45323],[-15,326],[-83,206],[-74,350],[-68,345],[15,120],[85,228],[-84,557],[-57,385]],[[53535,47840],[-139,365],[26,111]],[[53422,48316],[-39,179]],[[53383,48495],[-74,433]],[[53309,48928],[-228,610]],[[53081,49538],[-285,581],[-184,475],[-169,595],[9,192],[61,184],[67,419],[56,427]],[[52636,52411],[-52,87],[96,647]],[[52680,53145],[40,454],[-108,381],[-127,98],[-56,258]],[[52429,54336],[-71,82],[3,159]],[[52361,54577],[-289,-207],[-105,30],[-107,-129],[-222,13],[-149,360],[-91,417],[-197,379],[-209,-7],[-245,1]],[[50747,55434],[-229,-67],[-224,-123]],[[50294,55244],[-436,-337],[-154,-198],[-250,-167],[-248,164]],[[49206,54706],[-126,-6],[-194,112],[-178,-6],[-329,-101],[-193,-166],[-275,-211],[-54,15]],[[47857,54343],[-73,-5],[-286,274],[-252,439],[-237,315],[-187,371]],[[46822,55737],[-75,43],[-200,232],[-144,308],[-49,211],[-34,425]],[[46320,56956],[-122,341],[-108,226],[-71,74],[-69,115],[-32,254],[-41,127],[-80,94]],[[45797,58187],[-149,241],[-117,38],[-63,162],[1,88],[-84,122],[-18,124]],[[45367,58962],[-46,441]],[[45321,59403],[36,255]],[[45357,59658],[-115,449],[-138,205],[122,109],[134,404],[66,296]],[[45426,61121],[-24,311]],[[45402,61432],[78,284],[34,542]],[[45514,62258],[-30,569],[-34,286],[28,287],[-72,274],[-146,249]],[[45260,63923],[12,243]],[[45272,64166],[13,267],[106,157],[91,300],[-18,195],[96,406],[155,366],[93,93],[74,336],[6,307],[100,356],[185,210],[177,588]],[[46350,67747],[144,229]],[[46494,67976],[259,64]],[[46753,68040],[219,393],[139,154]],[[47111,68587],[232,481],[-70,716],[106,495],[37,304],[179,389],[278,263],[206,238],[186,596],[87,354],[205,-3],[167,-244],[264,39],[288,-127],[121,-6]],[[49397,72082],[267,315],[300,100],[175,238],[268,175],[471,102],[459,47],[140,-85],[262,227],[297,4],[113,-134],[190,35]],[[52339,73106],[302,233],[195,-70]],[[52836,73269],[-9,-291],[236,212],[20,-111],[-139,-282],[-2,-266],[96,-143],[-36,-499],[-183,-289],[53,-314],[143,-10],[70,-274],[106,-90]],[[53191,70912],[326,-198],[117,50],[232,-96],[368,-258],[130,-512],[250,-111],[391,-242],[296,-286],[136,150]],[[55437,69409],[133,265],[-65,441]],[[55505,70115],[87,280],[200,270],[192,78],[375,-118],[95,-257],[104,-3],[88,-98],[276,-67],[68,-191]],[[56990,70009],[369,10],[268,-152]],[[57627,69867],[275,-170],[129,-90]],[[58031,69607],[214,182],[114,165],[245,48],[198,-73],[75,-286],[65,189],[222,-136],[217,-33],[137,145]],[[59518,69808],[80,190],[-19,32]],[[59579,70030],[74,269],[56,435]],[[59709,70734],[40,146],[8,6]],[[59757,70886],[99,469],[138,406],[5,20]],[[59999,71781],[-26,440],[68,237]],[[60041,72458],[-102,261],[105,217],[-169,-49],[-233,132],[-191,-331],[-421,-65],[-225,309],[-300,19],[-64,-238],[-192,-69],[-268,307],[-303,-11],[-165,573],[-203,320]],[[57310,73833],[135,448],[-176,275]],[[57269,74556],[308,551],[428,23],[117,437]],[[58122,75567],[529,-76]],[[58651,75491],[334,374],[324,162]],[[59309,76027],[459,13],[485,-406],[399,-223],[323,89],[239,-52],[328,301]],[[61542,75749],[42,246],[-70,393],[-160,212],[-154,66],[-102,177]],[[61098,76843],[-354,486],[-317,218],[-240,338],[202,92],[231,482]],[[60620,78459],[-156,228],[410,234],[-8,126]],[[60866,79047],[-249,-92]],[[60617,78955],[-222,-46],[-185,-187],[-260,-30],[-239,-215],[16,-358],[136,-139],[284,35],[-55,-206]],[[60092,77809],[-304,-99],[-377,-334]],[[59411,77376],[-154,117],[61,271],[-304,169],[50,110],[265,191],[-80,132],[-432,146],[-19,215],[-257,-71],[-103,-317],[-215,-426]],[[58223,77913],[6,-149]],[[58229,77764],[-135,-123],[-84,53]],[[58010,77694],[-78,-694]],[[57932,77000],[-144,-239],[-101,-412],[89,-328]],[[57776,76021],[33,-222],[243,-186],[-51,-141],[-330,-32],[-118,-178],[-232,-310],[-87,268],[3,119]],[[57237,75339],[-169,17],[-145,54],[-336,-150],[192,-323],[-141,-94],[-154,-1],[-147,297],[-52,-127],[62,-344],[139,-270],[-105,-126],[155,-265],[137,-167],[4,-326],[-257,153],[82,-294],[-176,-60],[105,-509],[-184,-7],[-228,251],[-104,460],[-49,384],[-108,264],[-143,329],[-18,164]],[[55597,74649],[-48,40],[-5,127],[-154,193],[-24,274],[23,393],[38,179]],[[55427,75855],[-46,91],[-59,44]],[[55322,75990],[-78,188],[-120,115]],[[55124,76293],[-261,213],[-161,207],[-254,171],[-233,424],[56,43],[-127,242],[-5,195],[-179,91],[-85,-249],[-82,193],[6,200],[10,9]],[[53809,78032],[62,52]],[[53871,78084],[-221,84],[-226,-204],[15,-286],[-34,-164],[91,-293],[261,-290],[140,-476]],[[53897,76455],[309,-465],[217,4]],[[54423,75994],[68,-127],[-78,-115],[249,-208],[204,-174],[238,-301],[29,-107],[-52,-206]],[[55081,74756],[-154,269],[-242,94]],[[54685,75119],[-116,-372],[200,-214],[-33,-300],[-116,-34],[-148,-494],[-116,-45],[1,176],[57,309],[60,123],[-108,334],[-85,290],[-115,72],[-82,249]],[[54084,75213],[-179,105],[-120,231]],[[53785,75549],[-206,37],[-217,260],[-254,375],[-189,332],[-86,569],[-138,67],[-226,190],[-128,-78],[-161,-267],[-115,-42]],[[52065,76992],[-252,-326],[-548,156],[-404,-186],[-32,-347]],[[50829,76289],[15,-335],[-263,-383],[-356,-122],[-25,-194],[-171,-319],[-107,-469],[108,-329],[-160,-257],[-60,-374],[-210,-115],[-197,-443]],[[49403,72949],[-352,-9],[-265,11]],[[48786,72951],[-174,-203],[-106,-218],[-136,48],[-103,195],[-79,331],[-259,89]],[[47929,73193],[-112,-149],[-146,81],[-143,-64],[42,451],[-26,354],[-124,53]],[[47420,73919],[-67,219],[22,376]],[[47375,74514],[111,210],[20,232],[58,347],[-6,244],[-56,206],[-12,195]],[[47490,75948],[14,410],[-114,250],[393,415],[340,-104],[373,4],[296,-98],[230,30],[449,-19]],[[49471,76836],[144,345],[53,1147],[-287,605],[-205,291],[-424,222],[-28,420],[360,125],[466,-148],[-88,652],[263,-247],[646,449],[84,472],[243,116]],[[50698,81285],[222,113]],[[50920,81398],[143,159],[244,847],[380,241],[231,-16]],[[51918,82629],[54,122],[232,31],[52,-127],[188,284],[-63,216],[-13,326]],[[52368,83481],[-113,320],[-8,589],[46,155],[80,173],[244,36]],[[52617,84754],[98,158],[223,163]],[[52938,85075],[-9,-296],[-82,-188],[33,-161],[151,-87],[-68,-217],[-83,62]],[[52880,84188],[-200,-414],[76,-281]],[[52756,83493],[4,-222],[281,-135],[-3,-204],[283,108],[156,158],[313,-228],[132,-183]],[[53922,82787],[189,169]],[[54111,82956],[434,267],[350,194]],[[54895,83417],[277,-97],[21,-140],[268,-8]],[[55461,83172],[63,254]],[[55524,83426],[383,187],[-59,484]],[[55848,84097],[10,433],[136,362],[262,196],[221,-430],[223,11]],[[56700,84669],[53,443],[32,339]],[[56785,85451],[-102,-72],[-176,204],[-24,331],[351,161],[350,83],[301,-95],[287,17]],[[57772,86080],[316,318],[-291,274]],[[57797,86672],[-504,-46],[-489,-211],[-452,-121],[-161,314],[-269,189],[62,567],[-135,520],[133,335],[252,362],[635,624],[185,121],[-28,243],[-387,272]],[[56639,89841],[-478,-163],[-269,-401],[43,-353],[-441,-463],[-537,-495],[-202,-811],[198,-406],[265,-320],[-255,-649],[-289,-135],[-106,-967],[-157,-539],[-337,55],[-158,-456],[-321,-27],[-89,545],[-232,653],[-211,814]],[[53063,85723],[-187,354],[-548,-666]],[[52328,85411],[-370,-135],[-385,293]],[[51573,85569],[-99,619],[-88,1329],[256,371],[733,483],[549,595],[508,802],[668,1112],[465,434],[763,722],[610,252],[457,-31],[423,477],[506,-25],[499,115],[869,-422],[-358,-154],[305,-361]],[[58639,91887],[286,200],[456,-348],[761,-137],[1050,-652],[213,-273],[18,-384],[-308,-302],[-454,-154],[-1240,438],[-204,-73],[453,-422]],[[59670,89780],[36,-856]],[[59706,88924],[358,-175],[217,-150],[36,279]],[[60317,88878],[-174,257],[183,209]],[[60326,89344],[672,-358]],[[60998,88986],[234,140],[-187,422]],[[61045,89548],[647,564],[256,-33],[260,-202],[161,396],[-231,343],[136,345],[-204,357],[777,-185],[158,-322],[-351,-71]],[[62654,90740],[2,-321],[218,-197]],[[62874,90222],[429,125],[68,367]],[[63371,90714],[581,275],[969,494]],[[64921,91483],[209,-28],[-273,-350],[344,-60],[199,197],[521,16],[412,239],[317,-347],[315,381],[-291,334],[145,190],[820,-175],[385,-180],[1006,-658],[186,302],[-282,304],[-8,122],[-335,57],[92,273],[-149,449],[-8,185],[512,521]],[[69038,93255],[182,524],[207,113]],[[69427,93892],[735,-152],[58,-320]],[[70220,93420],[-263,-467],[173,-184]],[[70130,92769],[89,-403],[-63,-789],[307,-353],[-120,-384],[-544,-818],[318,-85],[110,207],[306,148],[74,285],[240,274],[-162,328],[130,380],[-304,47],[-67,321],[222,578],[-361,469],[497,389],[-64,409],[139,13],[145,-319],[-109,-556],[297,-105],[-127,415],[465,227],[577,30],[513,-328],[-247,479],[-28,614]],[[72363,94242],[484,116],[668,-25]],[[73515,94333],[602,75],[-226,301]],[[73891,94709],[321,379],[319,15]],[[74531,95103],[540,286],[734,77]],[[75805,95466],[93,158],[729,53]],[[76627,95677],[227,-129],[624,306],[510,-10],[77,249],[265,245],[656,236],[476,-186],[-378,-142],[629,-89],[75,-284],[254,140],[812,-8],[626,-281],[223,-215],[-69,-300],[-307,-170],[-730,-320],[-209,-171],[345,-80],[410,-146]],[[81143,94322],[250,109],[142,-369]],[[81535,94062],[122,149],[444,91],[892,-95],[67,-269],[1162,-86],[15,440],[590,-101],[443,3],[449,-303],[128,-369],[-165,-241],[349,-453],[437,-234],[268,605],[446,-260],[473,155],[538,-177],[204,162],[455,-81]],[[88852,92998],[-201,535],[367,249]],[[89018,93782],[2509,-374],[236,-342],[727,-440],[1122,109],[553,-95],[231,-238],[-33,-421],[342,-164],[372,118],[492,15],[525,-113],[526,64],[484,-512],[344,184],[-224,368],[123,256],[886,-161],[578,34]],[[98811,92070],[799,-274],[-99610,-252]],[[0,91544],[0,-2297]],[[63641,75603],[141,-409],[130,-26]],[[63912,75168],[85,-156],[-228,-46],[-49,-447]],[[63720,74519],[-47,-202],[-102,-135]],[[63571,74182],[7,-285]],[[63578,73897],[88,-424],[263,-120],[193,-289],[395,-100],[434,153],[27,134]],[[64978,73251],[-52,408],[40,602],[-216,195],[71,394],[-184,34],[61,485],[262,-141],[244,184],[-202,346],[-80,329],[-224,-147],[-28,-422],[-87,374]],[[64583,75892],[-15,140],[68,240],[-53,201],[-322,196],[-125,517],[-154,146],[-9,187],[270,-54],[11,421],[236,93],[243,-86],[50,562],[-50,356],[-278,-28],[-236,141],[-321,-253],[-259,-121]],[[63639,78550],[-127,-342],[-269,-95],[-276,-594],[252,-547]],[[63219,76972],[-27,-387],[303,-679]],[[63495,75906],[146,-303]],[[23933,96472],[-126,-17],[-521,37],[-74,161],[559,-9],[195,-107],[-33,-65]],[[19392,96574],[-518,-166],[-411,186],[224,183],[406,59],[392,-90],[-93,-172]],[[56867,96664],[-620,-236],[-490,134],[191,149],[-167,184],[575,115],[110,-216],[401,-130]],[[19538,97095],[-339,-113],[-461,1],[5,82],[285,173],[149,-27],[361,-116]],[[23380,96781],[-411,-119],[-226,134],[-119,216],[-22,238],[360,-23],[162,-38],[332,-200],[-76,-208]],[[22205,96935],[108,-240],[-453,64],[-457,187],[-619,21],[268,171],[-335,139],[-21,221],[546,-79],[751,-210],[212,-274]],[[79187,96925],[-1566,-222],[507,756],[229,64],[208,-37],[704,-327],[-82,-234]],[[55069,97728],[915,-429],[-699,-227],[-155,-424],[-243,-108],[-132,-478],[-335,-22],[-598,351],[252,205],[-416,166],[-541,487],[-216,451],[757,206],[152,-202],[396,8],[105,197],[408,20],[350,-201]],[[57068,98134],[545,-202],[-412,-310],[-806,-68],[-819,96],[-50,159],[-398,10],[-304,264],[858,161],[403,-138],[281,172],[702,-144]],[[64204,98215],[-373,-76],[-250,-44],[-39,-94],[-324,-95],[-301,136],[158,180],[-618,17],[542,105],[422,7],[57,-155],[159,138],[262,95],[412,-126],[-107,-88]],[[77760,97255],[-606,-71],[-773,166],[-462,220],[-213,413],[-379,113],[722,394],[600,130],[540,-290],[640,-557],[-69,-518]],[[25828,97704],[334,-186],[-381,-171],[-513,-434],[-492,-41],[-575,74],[-299,235],[4,208],[220,154],[-508,-5],[-306,192],[-176,261],[193,256],[192,175],[285,41],[-122,132],[646,29],[355,-308],[468,-123],[455,-109],[220,-380]],[[30972,99689],[742,-45],[597,-74],[508,-156],[-12,-154],[-678,-250],[-672,-117],[-251,-129],[605,3],[-656,-349],[-452,-163],[-476,-470],[-573,-96],[-177,-117],[-841,-62],[383,-72],[-192,-103],[230,-284],[-264,-198],[-429,-163],[-132,-225],[-388,-172],[39,-130],[475,22],[6,-141],[-742,-345],[-726,159],[-816,-89],[-414,69],[-525,30],[-35,277],[514,130],[-137,415],[170,41],[742,-249],[-379,370],[-450,110],[225,223],[492,137],[79,201],[-392,225],[-118,297],[759,-25],[220,-63],[433,210],[-625,67],[-972,-37],[-491,196],[-232,232],[-324,169],[-61,197],[413,110],[324,18],[545,94],[409,214],[344,-30],[300,-161],[211,311],[367,92],[498,64],[849,24],[148,-63],[802,98],[601,-37],[602,-36]],[[42472,99927],[1737,-457],[-513,-222],[-1062,-25],[-1496,-56],[140,-103],[984,63],[836,-198],[540,176],[231,-206],[-305,-335],[707,214],[1348,223],[833,-111],[156,-246],[-1132,-410],[-157,-133],[-888,-99],[643,-28],[-324,-420],[-224,-373],[9,-641],[333,-376],[-434,-24],[-457,-182],[513,-305],[65,-490],[-297,-53],[360,-495],[-617,-42],[322,-234],[-91,-203],[-391,-89],[-388,-2],[348,-390],[4,-256],[-549,238],[-143,-154],[375,-144],[364,-352],[105,-464],[-495,-111],[-214,222],[-344,331],[95,-391],[-322,-303],[732,-24],[383,-31],[-745,-502],[-755,-454],[-813,-199],[-306,-2],[-288,-222],[-386,-608],[-597,-404],[-192,-23],[-370,-142],[-399,-134],[-238,-357],[-4,-403],[-141,-378],[-453,-461],[112,-450],[-125,-476],[-142,-563],[-391,-35],[-410,471],[-556,3],[-269,315],[-186,563],[-481,716],[-141,375],[-38,517],[-384,532],[100,424],[-186,203],[275,673],[418,214],[110,241],[58,450],[-318,-204],[-151,-85],[-249,-83],[-341,188],[-19,392],[109,306],[258,8],[567,-153],[-478,366],[-249,197],[-276,-81],[-232,143],[310,536],[-169,215],[-220,398],[-335,611],[-353,223],[3,241],[-745,337],[-590,42],[-743,-23],[-677,-42],[-323,183],[-482,362],[729,181],[559,31],[-1188,149],[-627,236],[39,223],[1051,277],[1018,277],[107,210],[-750,206],[243,230],[961,402],[404,62],[-115,258],[658,152],[854,90],[853,6],[303,-180],[737,317],[663,-215],[390,-45],[577,-188],[-660,311],[38,246],[932,344],[975,-26],[354,213],[982,55],[2219,-72]],[[67002,72360],[284,-219],[209,77],[58,261],[219,87],[157,175],[55,460],[234,112],[44,205],[131,-154],[84,-18]],[[68477,73346],[154,-4],[210,-122]],[[68841,73220],[85,-70],[201,185],[93,-111],[90,264],[166,-12],[43,84],[29,233],[120,200],[150,-131],[-30,-176],[84,-27],[-26,-484],[110,-189],[97,121],[123,57],[173,258],[192,-42],[286,-1]],[[70827,73379],[50,-165]],[[70877,73214],[-162,-65],[-141,-106],[-319,-67],[-298,-121],[-163,-251],[66,-244],[32,-287],[-139,-242],[12,-221],[-76,-207],[-265,18],[110,-381],[-177,-146],[-118,-347],[15,-346],[-108,-162],[-103,53],[-212,-75],[-31,-161],[-207,1],[-154,-326],[-10,-490],[-361,-239],[-194,50],[-56,-126],[-166,74],[-278,-87],[-465,294]],[[66909,69007],[252,523],[-23,370],[-210,97],[-22,366],[-91,460],[119,315],[-121,85],[76,419],[113,718]],[[56642,45537],[29,-179],[-32,-279],[49,-270],[-41,-216],[24,-199],[-579,7],[-13,-1832],[188,-471],[181,-360]],[[56448,41738],[-510,-235],[-673,82],[-192,276],[-1126,-25],[-42,-40],[-166,260],[-180,17],[-166,-98],[-134,-110]],[[53697,44640],[90,256],[29,427]],[[53535,47840],[-139,364],[26,112]],[[53422,48316],[115,78],[80,-11],[98,69],[820,-7],[68,-430],[80,-345],[64,-186],[106,-301],[184,46],[91,81],[154,-81],[42,144],[69,336],[172,22],[15,100],[142,2],[-24,-207],[337,5],[5,-363],[56,-222],[-41,-347],[21,-354],[93,-214],[-15,-685],[68,53],[121,-15],[172,87],[127,-34]],[[53309,48928],[112,249],[84,97],[104,-198]],[[53609,49076],[-101,-121],[-45,-148],[-9,-251],[-71,-61]],[[55719,75933],[-35,-196],[39,-247],[115,-140]],[[55838,75350],[-5,-151],[-91,-84],[-16,-187],[-129,-279]],[[55427,75855],[-47,91]],[[55380,75946],[-18,183],[120,284],[18,-109],[75,51]],[[55575,76355],[59,-154],[66,-59],[19,-209]],[[65575,66834],[52,-196]],[[65665,66183],[-142,-2],[-23,-375],[50,-80],[-126,-114],[-1,-235],[-81,-238],[-7,-232]],[[65335,64907],[-56,-122],[-835,290],[-106,584],[-11,133]],[[31400,20215],[-168,16],[-297,0],[0,1286]],[[32587,39017],[511,-940],[227,-88],[339,-425],[286,-225],[40,-254],[-273,-876],[280,-156],[312,-88],[220,92],[252,441],[45,509]],[[34826,37007],[138,110],[139,-332],[-6,-460],[-234,-318],[-186,-234],[-314,-559],[-370,-786]],[[33993,34428],[-70,-461],[-74,-592],[3,-573],[-61,-128],[-21,-372]],[[33770,32302],[-19,-301]],[[31227,25165],[273,-422],[266,-116]],[[30952,21711],[-257,90],[-672,77],[-115,336],[6,431],[-185,-37],[-98,209],[-24,611],[213,253],[88,365],[-33,292],[148,491],[101,763],[-30,338],[122,109],[-30,217],[-129,115],[92,242],[-126,218],[-65,665],[112,117],[-47,702],[65,590],[75,513],[166,209],[-84,563],[-1,529],[210,376],[-7,481],[159,562],[1,530],[-72,105],[-128,994],[171,592],[-27,558],[100,523],[182,540],[196,358],[-83,226],[58,186],[-9,960],[302,284],[96,598],[-34,144]],[[31359,38736],[231,521],[364,-141],[163,-416],[109,464],[316,-24],[45,-123]],[[62106,75494],[386,89]],[[62492,75583],[57,-151],[106,-100],[-56,-144],[148,-198],[-78,-183],[118,-157],[124,-94],[7,-399]],[[62918,74157],[-101,-17]],[[62817,74140],[-113,333],[1,89],[-123,-2],[-82,155],[-58,-16]],[[62442,74699],[-109,168],[-207,144],[27,280],[-47,203]],[[794,3215],[294,183]],[[1088,3398],[38,-6],[32,-5]],[[54716,79543],[-21,-236],[-156,-1],[53,-125],[-92,-370]],[[54500,78811],[-53,-97],[-243,-15],[-140,-130],[-229,44]],[[53835,78613],[-398,149],[-62,200],[-274,-100],[-32,-109],[-169,81]],[[52900,78834],[-142,16],[-125,105],[42,141],[-10,102]],[[52665,79198],[83,32],[141,-160],[39,152],[245,-25],[199,104],[133,-18],[87,-118],[26,98],[-40,375],[100,73],[98,266]],[[53776,79977],[206,-186],[157,236],[98,43],[215,-176],[131,30],[128,-109]],[[54711,79815],[-23,-73],[28,-199]],[[62817,74140],[-190,76],[-141,266],[-44,217]],[[63641,75603],[141,-408],[130,-27]],[[63720,74519],[-48,-202],[-101,-135]],[[63578,73897],[-69,-28],[-173,301],[95,285],[-82,169],[-104,-43],[-327,-424]],[[62492,75583],[68,94],[207,-165],[149,-34],[38,67],[-136,312],[72,79]],[[62890,75936],[78,-19],[191,-350],[122,-39],[48,146],[166,232]],[[58149,49238],[-17,694],[-70,262]],[[58062,50194],[169,-45],[85,328],[147,-38]],[[58463,50439],[16,-227],[60,-130],[3,-187],[-69,-121],[-108,-300],[-101,-209],[-115,-27]],[[50920,81398],[204,-45],[257,120],[176,-252],[153,-135]],[[51710,81086],[-32,-389]],[[51678,80697],[-72,-22],[-30,-323]],[[51576,80352],[-243,263],[-143,-45],[-194,272],[-129,231],[-129,9],[-40,203]],[[50747,55434],[-229,-68]],[[50518,55366],[-69,398],[13,1322],[-56,119],[-11,283],[-96,201],[-85,170],[35,303]],[[50249,58162],[96,66],[56,251],[136,54],[61,172]],[[50598,58705],[93,169],[100,2],[212,-332]],[[51003,58544],[-11,-191],[62,-342],[-54,-232],[29,-154],[-135,-357],[-86,-176],[-52,-364],[7,-366],[-16,-928]],[[49214,57382],[-190,149],[-130,-22],[-97,-145],[-125,122],[-49,190],[-125,126]],[[48498,57802],[-18,334],[76,244],[-7,195],[221,477],[41,395],[76,141],[134,-78],[116,117],[38,148],[216,259],[53,180],[259,238],[153,82],[70,-110],[178,3]],[[50104,60427],[-22,-280],[37,-262],[156,-376],[9,-279],[320,-130],[-6,-395]],[[50249,58162],[-243,13]],[[50006,58175],[-128,46],[-90,-93],[-123,42],[-482,-27],[-7,-327],[38,-434]],[[75742,64522],[-6,-413],[-97,88],[18,-464]],[[75162,64725],[-87,-321],[-118,117]],[[74730,64531],[-43,474],[-96,433],[47,347],[-171,154],[62,210],[173,215],[-200,305],[98,390],[220,-248],[133,-29],[24,-400],[265,-79],[257,8],[160,-98],[-128,-487],[-124,-34],[-86,-327],[152,-299],[46,368],[76,2],[147,-914]],[[56293,77303],[80,-236],[108,42],[213,-90],[408,-30],[138,147],[327,133],[202,-209],[163,-60]],[[57776,76021],[-239,77],[-283,-181]],[[57254,75917],[-3,-287],[-252,-55],[-196,202],[-222,-159],[-206,17]],[[56375,75635],[-20,381],[-139,185]],[[56216,76201],[46,81],[-30,69],[47,183],[105,180],[-135,248],[-24,211],[68,130]],[[55279,77663],[100,2],[-69,-253],[134,-222],[-41,-271],[-65,-25]],[[55338,76894],[-52,-53],[-90,-134],[-41,-316]],[[55155,76391],[-246,218],[-105,240],[-106,128],[-127,215],[-61,178],[-136,270],[59,239],[99,-133],[60,120],[130,13],[239,-96],[192,8],[126,-128]],[[56523,82877],[268,-4],[302,217],[64,325],[228,184],[-26,258]],[[57359,83857],[169,97],[298,222]],[[57826,84176],[293,-144],[39,-143],[146,68],[272,-137],[27,-270],[-60,-156],[174,-377],[113,-105],[-16,-104],[187,-101],[80,-154],[-108,-126],[-224,20],[-54,-53],[66,-192],[68,-368]],[[58829,81834],[-239,-34],[-85,-127],[-18,-290],[-111,56],[-250,-28],[-73,135],[-104,-100],[-105,83],[-218,11],[-310,139],[-281,45],[-215,-13],[-152,-156],[-133,-23]],[[56535,81532],[-6,257],[-85,267],[166,117],[2,230],[-77,219],[-12,255]],[[25238,62085],[-2,85],[33,26],[51,-68],[99,348],[53,7]],[[25297,60979],[-83,-1],[22,650],[2,457]],[[31359,38736],[-200,-79],[-109,794],[-150,646],[88,557],[-146,244],[-37,416],[-136,391]],[[30669,41705],[175,622],[-119,484],[63,194],[-49,213],[108,288],[6,490],[13,405],[60,195],[-240,926]],[[30686,45522],[206,-48],[143,12],[62,174],[243,234],[147,216],[363,98],[-29,-432],[34,-221],[-23,-386],[302,-516],[311,-95],[109,-216],[188,-114],[115,-167],[175,6],[161,-171],[12,-333],[55,-168],[3,-248],[-81,-10],[107,-671],[533,-23],[-41,-333],[30,-227],[151,-162],[66,-358],[-49,-453],[-77,-253],[27,-328],[-87,-119]],[[33842,40210],[-4,177],[-259,295],[-258,8],[-484,-167],[-133,-507],[-7,-310],[-110,-689]],[[34826,37007],[54,332],[38,340],[0,317],[-100,105],[-104,-94],[-103,26],[-33,222],[-26,527],[-52,172],[-187,156],[-114,-113],[-293,111],[18,782],[-82,320]],[[30686,45522],[-157,-99],[-126,66],[18,875],[-228,-339],[-245,15],[-105,307],[-184,33],[59,247],[-155,351],[-115,518],[73,106],[0,243],[168,166],[-28,312],[71,200],[20,269],[318,392],[227,111],[37,86],[251,-27]],[[30585,49354],[125,1579],[6,250],[-43,330],[-123,210],[1,418],[156,95],[56,-60],[9,221],[-162,60],[-4,360],[541,-13],[92,198],[77,-182],[55,-340],[52,71]],[[31423,52551],[153,-304],[216,37],[54,176],[206,135],[115,94],[32,244],[198,164],[-15,121],[-235,49],[-39,363],[12,386],[-125,149],[52,53],[206,-73],[221,-144],[80,136],[200,89],[310,216],[102,220],[-37,162]],[[33129,54824],[145,26],[64,-133],[-36,-253],[96,-87],[63,-268],[-77,-203],[-44,-490],[71,-291],[20,-267],[171,-270],[137,-28],[30,112],[88,25],[126,101],[90,153],[154,-48],[67,20]],[[34294,52923],[151,-47],[25,118],[-46,114],[28,167],[112,-51],[131,59],[159,-122]],[[34854,53161],[121,-119],[86,156],[62,-24],[38,-162],[133,41],[107,219],[85,424],[164,527]],[[36494,51694],[10,-578],[211,378]],[[38626,39196],[-225,-250],[-65,-345]],[[35174,32383],[-77,326],[122,273],[-160,392],[-218,318],[-286,369],[-103,-17],[-279,446],[-180,-62]],[[82069,54967],[-13,-284],[-16,-368],[-133,18],[-58,-196],[-126,299]],[[75471,67823],[113,-184],[-20,-354],[-227,-17],[-234,39],[-175,-90],[-252,218],[-6,115]],[[74670,67550],[184,429],[150,146],[198,-134],[147,-14],[122,-154]],[[58175,39107],[-393,-424],[-249,-430],[-93,-383],[-83,-217],[-152,-46],[-48,-275],[-28,-180],[-178,-134],[-226,28],[-133,162],[-117,70],[-135,-134],[-68,-276],[-132,-173],[-139,-257],[-199,-59],[-62,202],[26,351],[-165,548],[-75,86]],[[55526,37566],[0,1681],[274,20],[8,2051],[207,19],[428,202],[106,-238],[177,226],[85,1],[156,130]],[[56967,41658],[50,-43]],[[57017,41615],[107,-460],[56,-103],[87,-333],[315,-633],[119,-62],[0,-203],[82,-365],[215,-88],[177,-261]],[[54244,56103],[229,44],[52,148],[46,-11],[69,-131],[350,221],[118,224],[145,202],[-28,202],[78,53],[269,-35],[261,266],[201,629],[141,233],[176,98]],[[56351,58246],[31,-246],[160,-360],[1,-235],[-45,-240],[18,-179],[96,-166]],[[56612,56820],[212,-252]],[[56824,56568],[152,-232],[2,-188],[187,-299],[116,-250],[70,-345],[208,-228],[44,-183]],[[57603,54843],[-91,-61],[-178,14],[-209,60],[-104,-49],[-41,-140],[-90,-17],[-110,121],[-309,-287],[-127,58],[-38,-45],[-83,-347],[-207,112],[-203,57],[-177,212],[-229,196],[-149,-186],[-108,-292],[-25,-402]],[[55125,53847],[-178,33],[-188,96],[-166,-305],[-146,-536]],[[54447,53135],[-29,167],[-12,263],[-127,185],[-103,297],[-23,207],[-132,301],[23,171],[-28,243],[21,446],[67,105],[140,583]],[[27392,90477],[-544,-403],[-386,-88]],[[23714,86446],[-16,-669],[409,-96]],[[28738,84386],[-23,385],[-188,489]],[[30174,87037],[495,-44],[-8,-483]],[[31876,86308],[184,267],[216,-514]],[[31513,80124],[415,58],[246,-283]],[[31350,77823],[-181,326],[0,785],[-123,166],[-187,-98],[-92,152],[-212,-435],[-84,-448],[-99,-262],[-118,-89],[-89,-29],[-28,-142],[-512,-1],[-422,-4],[-125,-106],[-294,-414],[-34,-45],[-89,-225],[-255,0],[-273,-2],[-125,-91],[44,-113],[25,-176],[-5,-58],[-363,-287],[-286,-90],[-323,-308],[-70,0],[-94,91],[-31,82],[6,60],[61,202],[131,317],[81,340],[-56,500],[-59,523],[-290,270],[35,103],[-41,70],[-76,0],[-56,91],[-14,137],[-54,-60],[-75,18],[17,57],[-65,57],[-27,151],[-216,185],[-224,191],[-272,223],[-261,209],[-248,-163],[-91,-6],[-342,150],[-225,-75],[-269,179],[-284,91],[-194,36],[-86,97],[-49,317],[-94,-3],[-1,-221],[-575,0],[-951,0],[-944,-1],[-833,1],[-834,0],[-819,0],[-847,0],[-273,0],[-825,0],[-788,0]],[[14130,82210],[-48,464],[-336,419]],[[13740,83389],[154,278],[-7,363],[-473,367],[-284,657],[-173,413],[-255,259],[-187,236],[-147,298],[-279,-187],[-270,-321],[-247,378],[-194,252],[-271,160],[-273,17],[1,3279],[2,2137]],[[12326,91747],[336,179],[413,-70]],[[17987,91511],[374,-292],[-390,-286]],[[22622,91222],[247,100],[431,-195]],[[19722,91438],[-704,-86],[-494,-54]],[[15020,93217],[119,244],[192,421]],[[16539,93189],[0,-251],[-731,-278]],[[52900,78834],[-22,-236],[-122,-97],[-206,72],[-60,-232],[-132,-18],[-48,91],[-156,-195],[-134,-28],[-120,124]],[[51900,78315],[-95,252],[-133,-90],[5,261],[203,323],[-9,147],[126,-53],[77,98]],[[52074,79253],[236,-4],[57,125],[298,-176]],[[31070,19804],[-137,18],[-164,46]],[[29661,29221],[-80,562],[-22,649]],[[30452,41263],[143,147],[74,295]],[[86288,76244],[-179,340],[-111,-323],[-429,-248],[44,-304],[-241,21],[-131,181],[-191,-409],[-306,-309],[-227,-370]],[[83987,73399],[45,-302],[-393,-161]],[[80013,64241],[-280,149],[-132,234],[44,332],[-254,105],[-134,216],[-236,-307],[-271,-66],[-221,3],[-149,-141]],[[78380,64766],[-144,-84],[42,-659],[-148,16],[-25,135]],[[78105,64174],[-9,238],[-203,-167],[-121,106],[-206,216],[81,478],[-176,112],[-66,530],[-293,-96],[33,684],[263,480],[11,475],[-8,441],[-121,137],[-93,339],[-162,-42]],[[77035,68105],[-300,86],[94,242],[-130,358],[-198,-243],[-233,142],[-321,-367],[-252,-428],[-224,-72]],[[74670,67550],[-23,454],[-170,-121]],[[74477,67883],[-324,56],[-314,132],[-225,253],[-216,114],[-93,276],[-157,83],[-280,375],[-223,177],[-115,-138]],[[72530,69211],[-386,403],[-273,365],[-78,635],[200,-78],[9,294],[-111,295],[28,470],[-298,675]],[[71621,72270],[-457,233],[-82,442],[-205,269]],[[70827,73379],[-42,328],[10,224],[-169,131],[-91,-58],[-70,533]],[[70465,74537],[79,132],[-39,135],[266,272],[192,112],[294,-77],[105,368],[356,68],[99,229],[438,312],[39,130]],[[72294,76218],[-22,328],[190,150],[-250,1000],[550,231],[143,128],[200,1031],[551,-190],[155,261],[13,577],[230,54],[212,383]],[[74266,80171],[109,48]],[[74375,80219],[73,-402],[233,-306],[396,-216],[192,-464],[-107,-673],[100,-249],[330,-99],[374,-80],[336,-359],[171,-64],[127,-531],[163,-342],[306,14],[574,-129],[369,80],[274,-86],[411,-350],[336,1],[123,-179],[324,309],[448,200],[417,21],[324,203],[200,309],[194,193],[-45,190],[-89,222],[146,371],[156,-52],[286,-117],[277,306],[423,223],[204,380],[195,164],[404,77],[219,-65],[30,204],[-251,403],[-223,184],[-214,-212],[-274,89],[-157,-73],[-72,236],[197,575],[135,434]],[[82410,80559],[333,-217],[392,364],[-3,253],[251,611],[155,184],[-4,318],[-152,137],[229,287],[345,104],[369,15],[415,-171],[244,-212],[172,-581],[104,-248],[97,-354],[103,-564],[483,-184],[329,-409],[112,-541],[423,-1],[240,227],[459,170],[-146,-518],[-107,-211],[-96,-631],[-186,-560],[-338,102],[-238,-203],[73,-494],[-40,-680],[-142,-16],[2,-292]],[[47857,54343],[22,474],[26,72],[-8,227],[-118,241],[-88,39],[-81,158],[60,256],[-28,278],[13,168]],[[47655,56256],[44,0],[17,251],[-22,112],[27,80],[103,69],[-69,461],[-64,238],[23,195],[55,45]],[[47769,57707],[36,52],[77,-86],[215,-5],[51,168],[48,-11],[80,65],[43,-246],[65,72],[114,86]],[[49214,57382],[74,-819],[-117,-484],[-73,-650],[121,-496],[-13,-227]],[[53632,53135],[-35,31],[-164,-74],[-169,77],[-132,-38]],[[53132,53131],[-452,14]],[[52429,54336],[-72,82],[4,159]],[[52361,54577],[71,408],[132,556],[81,5],[165,337],[105,9],[156,-236],[191,194],[26,239],[63,232],[43,291],[148,238],[56,403],[59,128],[39,299],[74,368],[234,446],[14,191],[31,104],[-110,229]],[[53939,59018],[9,184],[78,33]],[[54026,59235],[111,-369],[18,-382],[-10,-383],[151,-523],[-155,6],[-78,-41],[-127,57],[-60,-271],[164,-336],[121,-98],[39,-239],[87,-397],[-43,-156]],[[54447,53135],[-20,-311],[-220,136],[-225,152],[-350,23]],[[58564,53850],[-16,-673],[111,-78],[-89,-205],[-107,-153],[-106,-300],[-59,-268],[-15,-462],[-65,-220],[-2,-434]],[[58216,51057],[-80,-161],[-10,-342],[-38,-45],[-26,-315]],[[58149,49238],[50,-530],[-27,-299],[55,-334],[161,-323],[150,-726]],[[58538,47026],[-109,59],[-373,-97],[-75,-69],[-79,-368],[62,-254],[-49,-681],[-34,-578],[75,-103],[194,-224],[76,105],[23,-621],[-212,4],[-114,317],[-103,246],[-213,80],[-62,302],[-170,-182],[-222,81],[-93,261],[-176,53],[-131,-14],[-15,179],[-96,15]],[[53609,49076],[73,-59],[95,221],[152,-6],[17,-163],[104,-102],[164,361],[161,281],[71,185],[-10,473],[121,560],[127,296],[183,278],[32,184],[7,211],[45,200],[-14,326],[34,510],[55,360],[83,308],[16,347]],[[57603,54843],[169,-475],[124,-70],[75,97],[128,-38],[155,122],[66,-246],[244,-383]],[[53081,49538],[212,318],[-105,381],[95,144],[187,71],[23,255],[148,-276],[245,-25],[85,273],[36,382],[-31,450],[-131,341],[120,667],[-69,114],[-207,-47],[-78,298],[21,251]],[[29063,51742],[-119,136],[-137,191],[-79,-92],[-235,80],[-68,248],[-52,-9],[-278,329]],[[28366,55989],[36,280],[89,-41],[52,171],[-64,339],[34,85]],[[28513,56823],[143,-19]],[[30185,58611],[-178,-96],[-71,-288],[-107,-165],[-81,-215],[-34,-410],[-77,-337],[144,-39],[35,-265],[62,-126],[21,-232],[-33,-213],[10,-120],[69,-48],[66,-201],[357,55],[161,-73],[196,-496],[112,62],[200,-31],[158,66],[99,-99],[-50,-311],[-62,-193],[-22,-413],[56,-383],[79,-171],[9,-129],[-140,-286],[100,-127],[74,-202],[85,-574]],[[30585,49354],[-139,306],[-83,14],[179,586],[-213,270],[-166,-50],[-101,100],[-153,-152],[-207,72],[-163,603],[-129,149],[-89,272],[-184,272],[-74,-54]],[[26191,58215],[42,74],[183,-152],[63,75],[89,-48],[46,-119],[82,-38],[66,122]],[[27070,57338],[-107,-51],[1,-232],[58,-86],[-41,-68],[10,-104],[-23,-117],[-14,-114]],[[59437,72019],[-30,20],[-53,-44],[-42,12],[-14,-22],[-5,59],[-20,35],[-54,6],[-75,-49],[-52,30]],[[53776,79977],[-157,247],[-141,139],[-30,243],[-49,171],[202,125],[103,144],[200,111],[70,110],[73,-66],[124,60]],[[54171,81261],[132,-186],[207,-50],[-17,-158],[151,-119],[41,148],[191,-64],[26,-180],[207,-35],[127,-284]],[[55236,80333],[-82,0],[-43,-104],[-64,-25],[-18,-131],[-54,-28],[-7,-53],[-95,-60],[-123,10],[-39,-127]],[[53922,82787],[64,-293],[-77,-154],[101,-205],[69,-308],[-22,-199],[114,-367]],[[52074,79253],[35,410],[140,395],[-400,106],[-131,151]],[[51718,80315],[16,252],[-56,130]],[[51710,81086],[-47,604],[167,0],[70,217],[69,527],[-51,195]],[[52368,83481],[210,-76],[178,88]],[[61984,58430],[-102,-308]],[[61882,58122],[-62,103],[-67,-41],[-155,9],[-4,176],[-22,159],[94,269],[98,255]],[[61764,59052],[119,-50],[83,141]],[[52880,84188],[-200,-415],[76,-280]],[[52617,84754],[98,159],[223,162]],[[30081,62221],[5,157],[-71,172],[68,97],[21,222],[-24,314]],[[53333,65346],[-952,-1097],[-804,-1132],[-392,-257]],[[51185,62860],[-308,-56],[-3,366],[-129,94],[-173,165],[-66,270],[-937,1256],[-937,1257]],[[48632,66212],[-1045,1394]],[[47587,67606],[6,112],[-1,38]],[[47592,67756],[-2,682],[449,425],[277,88],[227,155],[107,288],[324,228],[12,427],[161,50],[126,213],[363,97],[51,224],[-73,122],[-96,608],[-17,350],[-104,369]],[[52339,73106],[-57,-295],[44,-549],[-65,-475],[-171,-322],[24,-433],[227,-344],[3,-139],[171,-232],[118,-1034]],[[52633,69283],[90,-509],[15,-267],[-49,-470],[21,-263],[-36,-315],[24,-362],[-110,-240],[164,-420],[11,-247],[99,-321],[130,105],[219,-267],[122,-361]],[[29063,51742],[38,-438],[-86,-374],[-303,-603],[-334,-227],[-170,-501],[-53,-389],[-157,-237],[-116,291],[-113,62],[-114,-45],[-8,211],[79,137],[-33,240]],[[60240,64499],[-1102,0],[-1077,0],[-1117,0]],[[56944,64499],[0,2120],[0,2048],[-83,464],[71,356],[-43,246],[101,276]],[[57627,69867],[275,-171],[129,-89]],[[59518,69808],[182,-989]],[[61764,59052],[-95,187],[-114,337],[-124,185],[-71,199],[-242,231],[-191,7],[-67,120],[-163,-135],[-168,261],[-87,-430],[-323,121]],[[60119,60135],[-30,230],[120,847],[27,382],[88,177],[204,95],[141,328]],[[49471,76836],[111,-224],[511,-262],[101,125],[313,-261],[322,75]],[[49403,72949],[-352,-8],[-265,10]],[[47929,73193],[-23,191],[103,216],[38,156],[-96,172],[77,378],[-111,345],[120,48],[11,272],[45,84],[3,449],[129,156],[-78,289],[-162,20],[-47,-72],[-164,-1],[-70,282],[-113,-84],[-101,-146]],[[56753,85111],[32,340]],[[57772,86080],[42,-100],[-198,-332],[83,-537],[-120,-183]],[[57579,84928],[-229,1],[-239,214],[-121,70],[-237,-102]],[[61882,58122],[-61,-204],[103,-317],[102,-277],[106,-206],[909,-683],[233,3]],[[63274,56438],[-785,-1728],[-362,-26],[-247,-406],[-178,-10],[-76,-182]],[[61626,54086],[-190,0],[-112,195],[-254,-241],[-82,-240],[-185,45],[-62,67],[-65,-16],[-87,6],[-352,489],[-193,0],[-95,189],[0,324],[-145,96]],[[59804,55000],[-164,627],[-127,133],[-48,231],[-141,280],[-171,42],[95,328],[147,14],[42,176]],[[59437,56831],[-4,517]],[[59433,57348],[82,603],[132,161],[28,236],[119,440],[168,285],[112,567],[45,495]],[[57942,91602],[-41,-403],[425,-383],[-256,-435],[323,-655],[-187,-494],[250,-429],[-113,-375],[411,-394],[-105,-294],[-258,-333],[-594,-735]],[[56639,89841],[-93,225],[-8,886],[-433,392],[-371,282]],[[55734,91626],[167,152],[309,-304],[362,29],[298,-140],[265,255],[137,422],[431,196],[356,-229],[-117,-405]],[[34854,53161],[70,246],[24,262],[48,246],[-107,340]],[[34889,54255],[-22,394],[144,495]],[[51576,80352],[62,-50],[80,13]],[[51900,78315],[-11,-163],[82,-216],[-97,-176],[72,-445],[151,-73],[-32,-250]],[[52636,52411],[94,33],[404,-6],[-2,693]],[[48278,82851],[-210,118],[-172,-8],[57,309],[-57,309]],[[49420,84027],[22,-61],[248,-679]],[[49051,81445],[-2,0],[-434,96]],[[48727,82636],[413,-52],[1,0]],[[49181,82918],[-186,355],[-4,8]],[[61098,76843],[34,68],[235,-99],[409,-93],[378,-276],[48,-107],[169,90],[259,-120],[85,-236],[175,-134]],[[62106,75494],[-268,282],[-296,-27]],[[50006,58175],[-20,-180],[116,-297],[-1,-418],[27,-454],[69,-210],[-61,-518],[22,-287],[74,-365],[62,-202]],[[47655,56256],[-78,14],[-57,-232],[-78,3],[-55,123],[19,231],[-116,353],[-73,-65],[-59,-13]],[[47158,56670],[-77,-33],[3,211],[-44,151],[9,168],[-60,242],[-78,206],[-222,0],[-65,-108],[-76,-13],[-48,-125],[-32,-159],[-148,-254]],[[45797,58187],[123,281],[84,-11],[73,97],[61,1],[44,76],[-24,191],[31,60],[5,195]],[[46194,59077],[134,-5],[200,-141],[61,13],[21,64],[151,-45],[40,32]],[[46801,58995],[16,-211],[44,1],[73,77],[46,-20],[77,-146],[119,-46],[76,125],[90,77],[67,80],[55,-15],[62,-126],[33,-159],[114,-241],[-57,-149],[-11,-187],[59,57],[35,-67],[-15,-172],[85,-166]],[[45357,59658],[302,17],[63,136],[88,10],[110,-142],[86,-3],[92,97],[56,-166],[-120,-130],[-121,11],[-119,121],[-103,-133],[-50,-5],[-67,-80],[-253,12]],[[45367,58962],[147,93],[92,-18],[75,65],[513,-25]],[[55838,75350],[182,51],[106,126],[150,-11],[46,100],[53,19]],[[57254,75917],[135,-153],[-86,-360],[-66,-65]],[[24381,60202],[7,168],[32,135],[-39,107],[133,470],[357,1],[7,197],[-45,35],[-31,124],[-103,133],[-103,193],[125,1],[1,324],[259,1],[257,-6]],[[25493,60887],[-127,-220],[-131,-161],[-20,-111],[22,-113],[-58,-146]],[[25179,60136],[-65,-36],[15,-67],[-52,-64],[-95,-145],[-9,-85]],[[34125,55269],[-44,-518],[-169,-150],[15,-136],[-51,-297],[123,-418],[89,-1],[37,-325],[169,-501]],[[33129,54824],[-188,437],[75,159],[-5,265],[171,93],[69,108],[-95,213],[24,210],[220,339]],[[25613,59537],[19,231],[-38,62],[-57,41],[-122,-68],[-10,77],[-84,93],[-60,114],[-82,49]],[[26903,60465],[-95,12],[-38,-79],[-97,-75],[-70,0],[-61,-73],[-56,26],[-47,88],[-29,-17],[-36,-138],[-27,5],[-4,-118],[-97,-159],[-51,-68],[-29,-72],[-82,117],[-60,-154],[-58,4],[-65,-14],[6,-283],[-41,-5],[-35,-131],[-86,-24]],[[55230,78267],[67,-223],[89,-164],[-107,-217]],[[55155,76391],[-31,-98]],[[53809,78032],[194,-20],[51,98],[94,-94],[109,-12],[-1,161],[97,59],[27,233],[221,153]],[[54601,78610],[88,-71],[208,-247],[229,-111],[104,86]],[[54716,79543],[141,-148],[103,-62],[233,70],[22,116],[111,17],[135,89],[30,-37],[130,72],[66,136],[91,35],[297,-175],[59,59]],[[56134,79715],[155,-157],[19,-154]],[[56308,79404],[-170,-121],[-131,-391],[-168,-390],[-223,-109]],[[55616,78393],[-173,26],[-213,-152]],[[54601,78610],[-54,194],[-47,7]],[[84713,46708],[28,-113],[5,-175]],[[89166,50332],[5,-1877],[4,-1876]],[[80461,52985],[47,-385],[190,-325],[179,117],[177,-42],[162,291],[133,51],[263,-162],[226,123],[143,801],[107,200],[96,655],[319,0],[241,-97]],[[72530,69211],[-176,-261],[-108,-538],[269,-218],[262,-283],[362,-323],[381,-75],[160,-293],[215,-54],[334,-135],[231,10],[32,228],[-36,366],[21,248]],[[77035,68105],[20,-219],[-97,-105],[23,-355],[-199,104],[-359,-397],[8,-330],[-153,-483],[-14,-281],[-124,-474],[-217,131],[-11,-596],[-63,-196],[30,-245],[-137,-137]],[[72692,61222],[-251,-206],[-129,-31]],[[68937,65473],[185,384],[612,-1],[-56,494],[-156,292],[-31,444],[-182,258],[306,604],[323,-44],[290,604],[174,584],[270,578],[-4,411],[236,333],[-224,284],[-96,390],[-99,504],[137,249],[421,-141],[310,86],[268,484]],[[64978,73251],[244,112],[197,329],[186,-17],[122,108],[197,-53],[308,-292],[221,-63],[318,-510],[207,-21],[24,-484]],[[66909,69007],[137,-302],[112,-348],[266,-253],[7,-508],[133,-93],[23,-265],[-400,-298],[-105,-669]],[[66559,66445],[-303,133],[-313,74]],[[63594,69288],[-104,-224]],[[63490,69064],[-153,302],[-3,307],[-89,0],[46,417],[-143,438],[-340,315],[-193,548],[65,449],[139,199],[-21,336],[-182,173],[-180,687]],[[62436,73235],[-152,461],[55,179],[-87,660],[190,164]],[[63490,69064],[-164,28]],[[63326,69092],[-187,48],[-204,-553]],[[62935,68587],[-516,46],[-784,1158],[-413,403],[-335,156]],[[60887,70350],[-112,701]],[[60775,71051],[615,600],[105,696],[-26,421],[152,142],[142,359]],[[61763,73269],[119,90],[324,-75],[97,-146],[133,97]],[[59922,70666],[-49,-182]],[[59873,70484],[-100,80],[-58,-383],[69,-65],[-71,-79],[-12,-152],[131,78]],[[59832,69963],[7,-224],[-139,-920]],[[59579,70030],[74,270],[56,434]],[[59757,70886],[93,-1],[25,101],[75,7]],[[59950,70993],[4,-236],[-38,-87],[6,-4]],[[53835,78613],[-31,-283],[67,-246]],[[53897,76455],[309,-464],[217,3]],[[55081,74756],[-154,268],[-242,95]],[[54084,75213],[-179,104],[-120,232]],[[59922,70666],[309,-228],[544,613]],[[60887,70350],[-53,-87],[-556,-289],[277,-575],[-92,-98],[-46,-193],[-212,-80],[-66,-207],[-120,-177],[-310,91]],[[59832,69963],[41,169],[0,352]],[[69711,76170],[-159,-107],[-367,-401],[-121,-412],[-104,-4],[-76,273],[-353,18],[-57,472],[-135,4],[21,578],[-333,421],[-476,-45],[-326,-84],[-265,519],[-227,218],[-431,412],[-52,50],[-715,-340],[11,-2124]],[[65546,75618],[-142,-28],[-195,452],[-188,161],[-315,-120],[-123,-191]],[[63639,78550],[-142,93],[29,296],[-177,385],[-207,-16],[-235,391],[160,436],[-81,118],[222,632],[285,-334],[35,421],[573,626],[434,15],[612,-399],[329,-233],[295,243],[440,12],[356,-298],[80,170],[391,-24],[69,272],[-450,396],[267,281],[-52,157],[266,150],[-200,394],[127,197],[1039,200],[136,142],[695,213],[250,239],[499,-124],[88,-597],[290,140],[356,-197],[-23,-314],[267,33],[696,543],[-102,-180],[355,-445],[620,-1463],[148,302],[383,-332],[399,148],[154,-104],[133,-332],[194,-112],[119,-244],[358,77],[147,-353]],[[72294,76218],[-171,84],[-140,207],[-412,61],[-461,15],[-100,-63],[-396,242],[-158,-119],[-43,-340],[-457,198],[-183,-81],[-62,-252]],[[60889,49136],[-399,576],[-19,334],[-1007,1173],[-47,63]],[[59417,51282],[-3,611],[80,233],[137,381],[101,420],[-123,661],[-32,289],[-132,400]],[[59445,54277],[171,344],[188,379]],[[61626,54086],[-243,-653],[3,-2098],[165,-475]],[[70465,74537],[-526,-87],[-343,187],[-301,-45],[26,332],[303,-96],[101,177]],[[69725,75005],[212,-56],[355,414],[-329,304],[-198,-144],[-205,217],[234,373],[-83,57]],[[78495,58847],[-66,696],[178,479],[359,110],[261,-83]],[[79227,60049],[229,-226],[126,397],[246,-212]],[[79828,60008],[64,-384],[-34,-690],[-467,-443],[122,-349],[-292,-42],[-240,-232]],[[85134,71634],[-31,313],[51,432],[-122,600]],[[85048,73569],[17,52],[124,-21],[108,260],[197,28],[118,38],[40,139]],[[55575,76355],[52,129]],[[55627,76484],[66,42],[38,191],[50,32],[40,-81],[52,-36],[36,-92],[46,-27],[54,-107],[39,3],[-31,-140],[-33,-68],[9,-43]],[[55993,76158],[-62,-23],[-164,-89],[-13,-118],[-35,5]],[[63448,68272],[-196,-15],[-69,274],[-248,56]],[[79227,60049],[90,260],[12,487],[-224,502],[-18,568],[-211,468],[-210,40],[-56,-201],[-163,-17],[-83,102],[-293,-344],[-6,517],[68,606],[-188,27],[-16,346],[-120,178]],[[77809,63588],[59,212],[237,374]],[[78380,64766],[162,-454],[125,-524],[342,-4],[108,-502],[-178,-151],[-80,-207],[333,-345],[231,-680],[175,-508],[210,-400],[70,-407],[-50,-576]],[[59999,71781],[125,-30],[45,-226],[-151,-217],[-68,-315]],[[46822,55737],[66,184],[15,168],[126,313],[129,268]],[[54125,64996],[-197,-214],[-156,316],[-439,248]],[[52633,69283],[136,133],[24,244],[-30,238],[191,222],[86,185],[135,165],[16,442]],[[55437,69409],[133,264],[-65,442]],[[56944,64499],[0,-1150],[-320,-2],[-3,-242]],[[56621,63105],[-1108,1103],[-1108,1103],[-280,-315]],[[58049,35154],[96,-173],[-85,-281],[-47,-187],[-155,-90],[-51,-184],[-99,-58],[-209,443],[148,365],[151,225],[130,118],[121,-178]],[[56314,83116],[-23,147],[30,157],[-123,92],[-291,100]],[[55907,83612],[-59,485]],[[55848,84097],[318,176],[466,-37],[273,57],[39,-120],[148,-37],[267,-279]],[[56523,82877],[-67,177],[-142,62]],[[56700,84669],[53,442]],[[57579,84928],[134,-133],[24,-279],[89,-340]],[[47592,67756],[-42,0],[7,-308],[-172,-19],[-90,-131],[-126,0],[-100,75],[-234,-62],[-91,-449],[-86,-42],[-131,-726],[-386,-621],[-92,-796],[-114,-258],[-33,-208],[-625,-46],[-5,1]],[[46350,67747],[5,8],[139,221]],[[46753,68040],[218,393],[140,154]],[[57394,79599],[66,85],[185,57],[204,-180],[115,-21],[125,-155],[-20,-195],[101,-95],[40,-240],[97,-147],[-19,-86],[52,-58],[-74,-43],[-164,17],[-27,80],[-58,-46],[20,-103],[-76,-184],[-49,-197],[-70,-63]],[[57842,78025],[-50,263],[30,246],[-9,253],[-160,342],[-89,243],[-86,171],[-84,56]],[[23016,66727],[-107,-505]],[[19117,67920],[-162,303],[-130,281]],[[19608,65285],[-6,-104],[-117,-211]],[[19362,65323],[-181,328],[-201,278]],[[18482,67245],[-37,-75],[-70,151]],[[17464,70566],[316,44],[353,63],[-26,-113],[419,-280],[634,-406],[552,5],[221,0],[0,237],[481,0],[102,-204],[142,-182],[165,-253],[92,-301],[69,-317],[144,-174],[230,-172],[175,455],[227,11],[196,-230],[139,-394],[96,-338],[164,-328],[61,-403],[78,-271],[217,-178],[197,-127],[108,17]],[[55993,76158],[95,33],[128,10]],[[46619,60247],[93,105],[47,339],[88,13],[194,-160],[157,114],[107,-38],[42,128],[1114,8],[62,404],[-48,71],[-134,2485],[-134,2485],[425,11]],[[51185,62860],[1,-1326],[-152,-384],[-24,-355],[-247,-92],[-379,-49],[-102,-205],[-178,-22]],[[46801,58995],[13,179],[-24,223],[-104,162],[-54,330],[-13,358]],[[76989,61578],[-183,-289],[-315,-410]],[[77809,63588],[-159,-134],[-162,-249],[-196,-26],[-127,-623],[-117,-104],[134,-506],[177,-420],[113,-380],[-101,-501],[-96,-106],[66,-289],[185,-458],[32,-321],[-4,-268],[108,-525],[-152,-537],[-135,-591]],[[55380,75946],[-58,44]],[[55338,76894],[74,-99],[40,-80],[91,-62],[106,-119],[-22,-50]],[[74375,80219],[292,99],[530,496],[423,271],[242,-176],[289,-9],[186,-269],[277,-21],[402,-144],[270,401],[-113,339],[288,596],[311,-238],[252,-67],[327,-148],[53,-432],[394,-242],[263,107],[351,75],[279,-76],[272,-276],[168,-295],[258,6],[350,-94],[255,143],[366,96],[407,405],[166,-62],[146,-193],[331,48]],[[59599,45195],[209,47],[334,-163],[73,73],[193,15],[99,173],[167,-10],[303,224],[221,334]],[[59119,36429],[-211,5]],[[58908,36434],[-24,254],[-41,259]],[[58843,36947],[-23,206],[49,642],[-72,410],[-133,810]],[[58664,39015],[292,654],[74,415],[42,52],[31,339],[-45,171],[12,430],[54,400],[0,728],[-145,185],[-132,42],[-60,143],[-128,121],[-232,-11],[-18,215]],[[58409,42899],[-26,410],[843,474]],[[59226,43783],[159,-276],[77,53],[110,-146],[16,-231],[-59,-268],[21,-405],[181,-356],[85,399],[120,122],[-24,740],[-116,417],[-100,185],[-97,-8],[-77,748],[77,438]],[[46619,60247],[-184,395],[-168,424],[-184,153],[-133,169],[-155,-6],[-135,-126],[-138,50],[-96,-185]],[[45402,61432],[78,283],[34,543]],[[45260,63923],[60,192],[1088,-4],[-53,832],[68,296],[261,51],[-9,1474],[911,-30],[1,872]],[[59226,43783],[-147,149],[85,535],[87,201],[-53,477],[56,467],[47,156],[-71,489],[-131,257]],[[59099,46514],[273,-108],[55,-159],[95,-269],[77,-783]],[[77801,55552],[48,103],[227,-252],[22,-296],[183,69],[91,236]],[[56448,41738],[228,131],[180,-33],[109,-130],[2,-48]],[[55526,37566],[0,-2127],[-248,-294],[-149,-42],[-175,108],[-125,42],[-47,247],[-109,157],[-133,-284]],[[54125,64996],[68,-895],[104,-150],[4,-183],[116,-198],[-60,-248],[-107,-1168],[-15,-749],[-354,-543],[-120,-759],[115,-213],[0,-371],[178,-13],[-28,-271]],[[53939,59018],[-52,-12],[-188,630],[-65,23],[-217,-322],[-215,168],[-150,34],[-80,-81],[-163,17],[-164,-245],[-141,-14],[-337,298],[-131,-142],[-142,10],[-104,218],[-279,214],[-298,-68],[-72,-124],[-39,-331],[-80,-233],[-19,-514]],[[58639,91887],[-473,-231],[-224,-54]],[[55734,91626],[-172,-23],[-41,-379],[-523,92],[-74,-321],[-267,2],[-183,-409],[-278,-639],[-431,-810],[101,-197],[-97,-228],[-275,10],[-180,-540],[17,-765],[177,-292],[-92,-677],[-231,-395],[-122,-332]],[[52328,85411],[-371,-135],[-384,293]],[[65634,62124],[-173,4],[-109,-145]],[[64752,61418],[-91,403],[-217,950]],[[64444,62771],[833,576],[185,1152],[-127,408]],[[65945,65581],[203,-76],[165,-105]],[[28366,55989],[-93,166],[-59,311]],[[27661,56017],[-130,-29],[-48,344],[-36,-98]],[[27801,57192],[95,172],[153,-33]],[[28408,56982],[105,-159]],[[54111,82956],[434,266],[350,195]],[[55461,83172],[342,-65],[511,9]],[[56535,81532],[139,-502],[-29,-162],[-138,-67],[-252,-479],[71,-259],[-60,34]],[[56266,80097],[-264,221],[-200,-81],[-131,59],[-165,-123],[-140,204],[-114,-78],[-16,34]],[[86288,76244],[39,-101]],[[84641,73775],[76,254],[66,67]],[[84783,74096],[-25,105],[71,311]],[[47420,73919],[-67,218],[22,377]],[[64274,66012],[-77,-41],[-84,114]],[[56308,79404],[120,123],[172,-64],[178,-2],[129,-141],[95,89],[205,55],[69,135],[118,0]],[[57842,78025],[124,-106],[131,93],[126,-99]],[[58229,77764],[-135,-124],[-84,54]],[[56293,77303],[-51,101],[65,97],[-69,72],[-87,-129],[-162,167],[-22,237],[-169,136],[-31,183],[-151,226]],[[55524,83426],[383,186]],[[0,92620],[99999,0]],[[97313,86314],[-397,399],[-724,-453],[-126,214]],[[93543,84865],[14,269],[432,129]],[[95686,87851],[-337,-49],[-167,-474]],[[90412,86000],[-914,-171],[-899,-1124]],[[87534,83347],[438,-73],[136,-360]],[[63219,76972],[-27,-388],[303,-678]],[[60620,78459],[-156,227],[410,236],[-8,125]],[[60617,78955],[9,255],[143,161],[269,42],[44,192],[-62,318],[113,302],[-3,169],[-410,187],[-162,-6],[-172,270],[-213,-92],[-352,203],[6,113],[-99,250],[-222,28],[-23,178],[70,117],[-178,326],[-288,-56],[-84,29],[-70,-131],[-104,24]],[[59670,89780],[18,-267],[18,-589]],[[60317,88878],[-168,248],[177,218]],[[60998,88986],[233,140],[-186,422]],[[62654,90740],[1,-321],[219,-197]],[[63371,90714],[580,274],[970,495]],[[69038,93255],[183,523],[206,114]],[[69427,93892],[736,-152],[57,-320]],[[70220,93420],[-263,-468],[173,-183]],[[72363,94242],[483,116],[669,-25]],[[73891,94709],[321,378],[319,16]],[[75805,95466],[93,157],[729,54]],[[81143,94322],[251,109],[141,-369]],[[88852,92998],[-201,534],[367,250]],[[98811,92070],[799,-275],[-99610,-251]],[[0,89250],[0,-3]],[[58449,51176],[110,-325],[-16,-339],[-80,-73]],[[58216,51057],[67,-59],[166,178]],[[61883,61244],[-37,246],[-83,173]],[[59899,67595],[-141,395],[-139,-2]],[[63694,67644],[47,-203],[190,-242]],[[64444,62771],[-801,-221],[-259,-259],[-199,-604],[-130,-96],[-70,191],[-106,-28],[-269,57],[-50,58],[-321,-13],[-75,-52],[-114,149],[-74,-283],[28,-243],[-121,-183]],[[59434,57280],[-39,11],[5,287],[-33,197],[-143,228],[-34,415],[34,425],[-129,40],[-19,-129],[-167,-29],[67,-169],[23,-346],[-152,-316],[-138,-415],[-144,-59],[-233,336],[-105,-119],[-29,-168],[-143,-109],[-9,-118],[-277,0],[-38,118],[-200,20],[-100,-99],[-77,50],[-143,336],[-48,158],[-200,-79],[-76,-267],[-72,-514],[-95,-109],[-85,-63]],[[56635,56793],[-23,27]],[[56351,58246],[3,140],[-102,169],[-3,335],[-58,222],[-98,-33],[28,211],[72,240],[-32,239],[92,176],[-58,135],[73,355],[127,425],[240,-41],[-14,2286]],[[59433,57348],[1,-68]],[[59434,57280],[3,-449]],[[59445,54277],[-171,-265],[-195,1],[-224,-135],[-176,129],[-115,-157]],[[56824,56568],[-189,225]],[[63438,58380],[158,20]],[[63596,58400],[-2,-9],[-1,-237],[0,-581],[0,-301],[-125,-353],[-194,-481]],[[62934,53474],[-278,-467],[-415,-571]],[[63596,58400],[89,12]],[[34889,54255],[109,-341],[-49,-248],[-24,-263],[-71,-242]],[[56266,80097],[-77,-150],[-55,-232]],[[58908,36434],[-56,-256],[-163,-62],[-166,312],[-2,199],[76,216],[26,168],[80,41],[140,-105]],[[60041,72458],[74,126],[75,127],[15,321],[91,-112],[306,160],[147,-108],[229,1],[320,217],[149,-10],[316,89]],[[50518,55366],[-224,-122]],[[68841,73220],[156,583],[-60,429],[-204,137],[72,254],[232,-27],[132,318],[89,370],[371,134],[-58,-267],[40,-161],[114,15]],[[65546,75618],[313,8],[-45,290],[237,199],[234,334],[374,-304],[30,-460],[106,-118],[301,27],[93,-105],[137,-593],[317,-398],[181,-271],[291,-282],[369,-247],[-7,-352]],[[52339,73106],[302,232],[195,-69]],[[57310,73833],[135,447],[-176,276]],[[57269,74556],[308,550],[428,23],[117,438]],[[58651,75491],[334,373],[324,163]],[[60889,49136],[-128,-710],[16,-326]],[[59099,46514],[-157,172],[-177,97],[-111,97],[-116,146]],[[58449,51176],[98,69],[304,-7],[566,44]],[[60092,77809],[-304,-100],[-377,-333]],[[33939,31983],[-169,319]],[[30523,76986],[-147,-342],[-47,-129]],[[29077,74266],[69,-103],[5,-217]],[[28797,73761],[-183,90],[191,-185]],[[27408,66595],[-105,132],[-148,496]],[[27026,67663],[-42,225],[57,383]],[[27041,68271],[-77,316],[-217,482]],[[25227,69288],[-114,-90],[50,-153]],[[24957,68598],[-202,17],[-207,305]],[[16564,71667],[-71,93],[-33,316]],[[15496,76016],[-89,441],[109,543]],[[15516,77000],[34,523],[33,522]],[[15533,78818],[-88,492],[-80,268]],[[15948,78951],[69,152],[-45,472]],[[13587,83606],[-245,183],[-78,503]],[[8164,86018],[-308,-122],[-39,339]],[[4226,83160],[-43,96],[304,270]],[[3654,89313],[-351,334],[390,241]],[[3832,91498],[493,19],[350,262]],[[31057,57908],[249,-6],[297,60]],[[33102,57232],[45,-462],[144,13],[109,-135]],[[62539,59289],[-43,-146],[-137,12],[-89,-78]],[[57838,32957],[-210,-262],[-290,-224]],[[56553,32368],[-168,-71],[-115,31]],[[55331,31983],[-94,10],[-120,258]],[[58175,39107],[113,-6],[134,-97],[94,69],[148,-58]],[[58409,42899],[-210,-79],[-159,-230],[-33,-199],[-100,-46],[-241,-473],[-154,-373],[-94,-13],[-90,66],[-311,63]]],"transform":{"scale":[0.0036000360003600037,0.001736468664686647],"translate":[-180,-90]}}
\ No newline at end of file