f1d02f8944b8
red-tape: Update site.
author | Steve Losh <steve@stevelosh.com> |
---|---|
date | Tue, 13 Aug 2013 10:24:27 -0400 |
parents | 2aa508b4fa14 |
children | 98dd2a7bf1d6 |
branches/tags | (none) |
files | red-tape/_dmedia/bootstrap.css red-tape/_dmedia/less.js red-tape/_dmedia/style.less red-tape/_dmedia/tango.css red-tape/basics/index.html red-tape/changelog/index.html red-tape/cleaners/index.html red-tape/form-arguments/index.html red-tape/index.html red-tape/initial-data/index.html red-tape/input/index.html red-tape/installation/index.html red-tape/reference/index.html red-tape/rendering/index.html red-tape/result-maps/index.html |
Changes
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/_dmedia/bootstrap.css Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,356 @@ +html,body{margin:0;padding:0;} +h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,cite,code,del,dfn,em,img,q,s,samp,small,strike,strong,sub,sup,tt,var,dd,dl,dt,li,ol,ul,fieldset,form,label,legend,button,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;font-weight:normal;font-style:normal;font-size:100%;line-height:1;font-family:inherit;} +table{border-collapse:collapse;border-spacing:0;} +ol,ul{list-style:none;} +q:before,q:after,blockquote:before,blockquote:after{content:"";} +html{overflow-y:scroll;font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;} +a:focus{outline:thin dotted;} +a:hover,a:active{outline:0;} +article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block;} +audio,canvas,video{display:inline-block;*display:inline;*zoom:1;} +audio:not([controls]){display:none;} +sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;} +sup{top:-0.5em;} +sub{bottom:-0.25em;} +img{border:0;-ms-interpolation-mode:bicubic;} +button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle;} +button,input{line-height:normal;*overflow:visible;} +button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;} +button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;} +input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;} +input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;} +textarea{overflow:auto;vertical-align:top;} +body{background-color:#ffffff;margin:0;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:18px;color:#404040;} +.container{width:940px;margin-left:auto;margin-right:auto;zoom:1;}.container:before,.container:after{display:table;content:"";zoom:1;} +.container:after{clear:both;} +.container-fluid{position:relative;min-width:940px;padding-left:20px;padding-right:20px;zoom:1;}.container-fluid:before,.container-fluid:after{display:table;content:"";zoom:1;} +.container-fluid:after{clear:both;} +.container-fluid>.sidebar{position:absolute;top:0;left:20px;width:220px;} +.container-fluid>.content{margin-left:240px;} +a{color:#0069d6;text-decoration:none;line-height:inherit;font-weight:inherit;}a:hover{color:#00438a;text-decoration:underline;} +.pull-right{float:right;} +.pull-left{float:left;} +.hide{display:none;} +.show{display:block;} +.row{zoom:1;margin-left:-20px;}.row:before,.row:after{display:table;content:"";zoom:1;} +.row:after{clear:both;} +.row>[class*="span"]{display:inline;float:left;margin-left:20px;} +.span1{width:40px;} +.span2{width:100px;} +.span3{width:160px;} +.span4{width:220px;} +.span5{width:280px;} +.span6{width:340px;} +.span7{width:400px;} +.span8{width:460px;} +.span9{width:520px;} +.span10{width:580px;} +.span11{width:640px;} +.span12{width:700px;} +.span13{width:760px;} +.span14{width:820px;} +.span15{width:880px;} +.span16{width:940px;} +.span17{width:1000px;} +.span18{width:1060px;} +.span19{width:1120px;} +.span20{width:1180px;} +.span21{width:1240px;} +.span22{width:1300px;} +.span23{width:1360px;} +.span24{width:1420px;} +.row>.offset1{margin-left:80px;} +.row>.offset2{margin-left:140px;} +.row>.offset3{margin-left:200px;} +.row>.offset4{margin-left:260px;} +.row>.offset5{margin-left:320px;} +.row>.offset6{margin-left:380px;} +.row>.offset7{margin-left:440px;} +.row>.offset8{margin-left:500px;} +.row>.offset9{margin-left:560px;} +.row>.offset10{margin-left:620px;} +.row>.offset11{margin-left:680px;} +.row>.offset12{margin-left:740px;} +.span-one-third{width:300px;} +.span-two-thirds{width:620px;} +.row>.offset-one-third{margin-left:340px;} +.row>.offset-two-thirds{margin-left:660px;} +p{font-size:13px;font-weight:normal;line-height:18px;margin-bottom:9px;}p small{font-size:11px;color:#bfbfbf;} +h1,h2,h3,h4,h5,h6{font-weight:bold;color:#404040;}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{color:#bfbfbf;} +h1{margin-bottom:18px;font-size:30px;line-height:36px;}h1 small{font-size:18px;} +h2{font-size:24px;line-height:36px;}h2 small{font-size:14px;} +h3,h4,h5,h6{line-height:36px;} +h3{font-size:18px;}h3 small{font-size:14px;} +h4{font-size:16px;}h4 small{font-size:12px;} +h5{font-size:14px;} +h6{font-size:13px;color:#bfbfbf;text-transform:uppercase;} +ul,ol{margin:0 0 18px 25px;} +ul ul,ul ol,ol ol,ol ul{margin-bottom:0;} +ul{list-style:disc;} +ol{list-style:decimal;} +li{line-height:18px;color:#808080;} +ul.unstyled{list-style:none;margin-left:0;} +dl{margin-bottom:18px;}dl dt,dl dd{line-height:18px;} +dl dt{font-weight:bold;} +dl dd{margin-left:9px;} +hr{margin:20px 0 19px;border:0;border-bottom:1px solid #eee;} +strong{font-style:inherit;font-weight:bold;} +em{font-style:italic;font-weight:inherit;line-height:inherit;} +.muted{color:#bfbfbf;} +blockquote{margin-bottom:18px;border-left:5px solid #eee;padding-left:15px;}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;} +blockquote small{display:block;font-size:12px;font-weight:300;line-height:18px;color:#bfbfbf;}blockquote small:before{content:'\2014 \00A0';} +address{display:block;line-height:18px;margin-bottom:18px;} +code,pre{padding:0 3px 2px;font-family:Monaco, Andale Mono, Courier New, monospace;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +code{background-color:#fee9cc;color:rgba(0, 0, 0, 0.75);padding:1px 3px;} +pre{background-color:#f5f5f5;display:block;padding:8.5px;margin:0 0 18px;line-height:18px;font-size:12px;border:1px solid #ccc;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;white-space:pre;white-space:pre-wrap;word-wrap:break-word;} +form{margin-bottom:18px;} +fieldset{margin-bottom:18px;padding-top:18px;}fieldset legend{display:block;padding-left:150px;font-size:19.5px;line-height:1;color:#404040;*padding:0 0 5px 145px;*line-height:1.5;} +form .clearfix{margin-bottom:18px;zoom:1;}form .clearfix:before,form .clearfix:after{display:table;content:"";zoom:1;} +form .clearfix:after{clear:both;} +label,input,select,textarea{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:normal;} +label{padding-top:6px;font-size:13px;line-height:18px;float:left;width:130px;text-align:right;color:#404040;} +form .input{margin-left:150px;} +input[type=checkbox],input[type=radio]{cursor:pointer;} +input,textarea,select,.uneditable-input{display:inline-block;width:210px;height:18px;padding:4px;font-size:13px;line-height:18px;color:#808080;border:1px solid #ccc;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;} +select{padding:initial;} +input[type=checkbox],input[type=radio]{width:auto;height:auto;padding:0;margin:3px 0;*margin-top:0;line-height:normal;border:none;} +input[type=file]{background-color:#ffffff;padding:initial;border:initial;line-height:initial;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +input[type=button],input[type=reset],input[type=submit]{width:auto;height:auto;} +select,input[type=file]{height:27px;*height:auto;line-height:27px;*margin-top:4px;} +select[multiple]{height:inherit;background-color:#ffffff;} +textarea{height:auto;} +.uneditable-input{background-color:#ffffff;display:block;border-color:#eee;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.025);cursor:not-allowed;} +:-moz-placeholder{color:#bfbfbf;} +::-webkit-input-placeholder{color:#bfbfbf;} +input,textarea{-webkit-transition:border linear 0.2s,box-shadow linear 0.2s;-moz-transition:border linear 0.2s,box-shadow linear 0.2s;-ms-transition:border linear 0.2s,box-shadow linear 0.2s;-o-transition:border linear 0.2s,box-shadow linear 0.2s;transition:border linear 0.2s,box-shadow linear 0.2s;-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1);} +input:focus,textarea:focus{outline:0;border-color:rgba(82, 168, 236, 0.8);-webkit-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);box-shadow:inset 0 1px 3px rgba(0, 0, 0, 0.1),0 0 8px rgba(82, 168, 236, 0.6);} +input[type=file]:focus,input[type=checkbox]:focus,select:focus{-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;outline:1px dotted #666;} +form .clearfix.error>label,form .clearfix.error .help-block,form .clearfix.error .help-inline{color:#b94a48;} +form .clearfix.error input,form .clearfix.error textarea{color:#b94a48;border-color:#ee5f5b;}form .clearfix.error input:focus,form .clearfix.error textarea:focus{border-color:#e9322d;-webkit-box-shadow:0 0 6px #f8b9b7;-moz-box-shadow:0 0 6px #f8b9b7;box-shadow:0 0 6px #f8b9b7;} +form .clearfix.error .input-prepend .add-on,form .clearfix.error .input-append .add-on{color:#b94a48;background-color:#fce6e6;border-color:#b94a48;} +form .clearfix.warning>label,form .clearfix.warning .help-block,form .clearfix.warning .help-inline{color:#c09853;} +form .clearfix.warning input,form .clearfix.warning textarea{color:#c09853;border-color:#ccae64;}form .clearfix.warning input:focus,form .clearfix.warning textarea:focus{border-color:#be9a3f;-webkit-box-shadow:0 0 6px #e5d6b1;-moz-box-shadow:0 0 6px #e5d6b1;box-shadow:0 0 6px #e5d6b1;} +form .clearfix.warning .input-prepend .add-on,form .clearfix.warning .input-append .add-on{color:#c09853;background-color:#d2b877;border-color:#c09853;} +form .clearfix.success>label,form .clearfix.success .help-block,form .clearfix.success .help-inline{color:#468847;} +form .clearfix.success input,form .clearfix.success textarea{color:#468847;border-color:#57a957;}form .clearfix.success input:focus,form .clearfix.success textarea:focus{border-color:#458845;-webkit-box-shadow:0 0 6px #9acc9a;-moz-box-shadow:0 0 6px #9acc9a;box-shadow:0 0 6px #9acc9a;} +form .clearfix.success .input-prepend .add-on,form .clearfix.success .input-append .add-on{color:#468847;background-color:#bcddbc;border-color:#468847;} +.input-mini,input.mini,textarea.mini,select.mini{width:60px;} +.input-small,input.small,textarea.small,select.small{width:90px;} +.input-medium,input.medium,textarea.medium,select.medium{width:150px;} +.input-large,input.large,textarea.large,select.large{width:210px;} +.input-xlarge,input.xlarge,textarea.xlarge,select.xlarge{width:270px;} +.input-xxlarge,input.xxlarge,textarea.xxlarge,select.xxlarge{width:530px;} +textarea.xxlarge{overflow-y:auto;} +input.span1,textarea.span1{display:inline-block;float:none;width:30px;margin-left:0;} +input.span2,textarea.span2{display:inline-block;float:none;width:90px;margin-left:0;} +input.span3,textarea.span3{display:inline-block;float:none;width:150px;margin-left:0;} +input.span4,textarea.span4{display:inline-block;float:none;width:210px;margin-left:0;} +input.span5,textarea.span5{display:inline-block;float:none;width:270px;margin-left:0;} +input.span6,textarea.span6{display:inline-block;float:none;width:330px;margin-left:0;} +input.span7,textarea.span7{display:inline-block;float:none;width:390px;margin-left:0;} +input.span8,textarea.span8{display:inline-block;float:none;width:450px;margin-left:0;} +input.span9,textarea.span9{display:inline-block;float:none;width:510px;margin-left:0;} +input.span10,textarea.span10{display:inline-block;float:none;width:570px;margin-left:0;} +input.span11,textarea.span11{display:inline-block;float:none;width:630px;margin-left:0;} +input.span12,textarea.span12{display:inline-block;float:none;width:690px;margin-left:0;} +input.span13,textarea.span13{display:inline-block;float:none;width:750px;margin-left:0;} +input.span14,textarea.span14{display:inline-block;float:none;width:810px;margin-left:0;} +input.span15,textarea.span15{display:inline-block;float:none;width:870px;margin-left:0;} +input.span16,textarea.span16{display:inline-block;float:none;width:930px;margin-left:0;} +input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{background-color:#f5f5f5;border-color:#ddd;cursor:not-allowed;} +.actions{background:#f5f5f5;margin-top:18px;margin-bottom:18px;padding:17px 20px 18px 150px;border-top:1px solid #ddd;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;}.actions .secondary-action{float:right;}.actions .secondary-action a{line-height:30px;}.actions .secondary-action a:hover{text-decoration:underline;} +.help-inline,.help-block{font-size:13px;line-height:18px;color:#bfbfbf;} +.help-inline{padding-left:5px;*position:relative;*top:-5px;} +.help-block{display:block;max-width:600px;} +.inline-inputs{color:#808080;}.inline-inputs span{padding:0 2px 0 1px;} +.input-prepend input,.input-append input{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;} +.input-prepend .add-on,.input-append .add-on{position:relative;background:#f5f5f5;border:1px solid #ccc;z-index:2;float:left;display:block;width:auto;min-width:16px;height:18px;padding:4px 4px 4px 5px;margin-right:-1px;font-weight:normal;line-height:18px;color:#bfbfbf;text-align:center;text-shadow:0 1px 0 #ffffff;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-prepend .active,.input-append .active{background:#a9dba9;border-color:#46a546;} +.input-prepend .add-on{*margin-top:1px;} +.input-append input{float:left;-webkit-border-radius:3px 0 0 3px;-moz-border-radius:3px 0 0 3px;border-radius:3px 0 0 3px;} +.input-append .add-on{-webkit-border-radius:0 3px 3px 0;-moz-border-radius:0 3px 3px 0;border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px;} +.inputs-list{margin:0 0 5px;width:100%;}.inputs-list li{display:block;padding:0;width:100%;} +.inputs-list label{display:block;float:none;width:auto;padding:0;margin-left:20px;line-height:18px;text-align:left;white-space:normal;}.inputs-list label strong{color:#808080;} +.inputs-list label small{font-size:11px;font-weight:normal;} +.inputs-list .inputs-list{margin-left:25px;margin-bottom:10px;padding-top:0;} +.inputs-list:first-child{padding-top:6px;} +.inputs-list li+li{padding-top:2px;} +.inputs-list input[type=radio],.inputs-list input[type=checkbox]{margin-bottom:0;margin-left:-20px;float:left;} +.form-stacked{padding-left:20px;}.form-stacked fieldset{padding-top:9px;} +.form-stacked legend{padding-left:0;} +.form-stacked label{display:block;float:none;width:auto;font-weight:bold;text-align:left;line-height:20px;padding-top:0;} +.form-stacked .clearfix{margin-bottom:9px;}.form-stacked .clearfix div.input{margin-left:0;} +.form-stacked .inputs-list{margin-bottom:0;}.form-stacked .inputs-list li{padding-top:0;}.form-stacked .inputs-list li label{font-weight:normal;padding-top:0;} +.form-stacked div.clearfix.error{padding-top:10px;padding-bottom:10px;padding-left:10px;margin-top:0;margin-left:-10px;} +.form-stacked .actions{margin-left:-20px;padding-left:20px;} +table{width:100%;margin-bottom:18px;padding:0;font-size:13px;border-collapse:collapse;}table th,table td{padding:10px 10px 9px;line-height:18px;text-align:left;} +table th{padding-top:9px;font-weight:bold;vertical-align:middle;} +table td{vertical-align:top;border-top:1px solid #ddd;} +table tbody th{border-top:1px solid #ddd;vertical-align:top;} +.condensed-table th,.condensed-table td{padding:5px 5px 4px;} +.bordered-table{border:1px solid #ddd;border-collapse:separate;*border-collapse:collapse;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;}.bordered-table th+th,.bordered-table td+td,.bordered-table th+td{border-left:1px solid #ddd;} +.bordered-table thead tr:first-child th:first-child,.bordered-table tbody tr:first-child td:first-child{-webkit-border-radius:4px 0 0 0;-moz-border-radius:4px 0 0 0;border-radius:4px 0 0 0;} +.bordered-table thead tr:first-child th:last-child,.bordered-table tbody tr:first-child td:last-child{-webkit-border-radius:0 4px 0 0;-moz-border-radius:0 4px 0 0;border-radius:0 4px 0 0;} +.bordered-table tbody tr:last-child td:first-child{-webkit-border-radius:0 0 0 4px;-moz-border-radius:0 0 0 4px;border-radius:0 0 0 4px;} +.bordered-table tbody tr:last-child td:last-child{-webkit-border-radius:0 0 4px 0;-moz-border-radius:0 0 4px 0;border-radius:0 0 4px 0;} +table .span1{width:20px;} +table .span2{width:60px;} +table .span3{width:100px;} +table .span4{width:140px;} +table .span5{width:180px;} +table .span6{width:220px;} +table .span7{width:260px;} +table .span8{width:300px;} +table .span9{width:340px;} +table .span10{width:380px;} +table .span11{width:420px;} +table .span12{width:460px;} +table .span13{width:500px;} +table .span14{width:540px;} +table .span15{width:580px;} +table .span16{width:620px;} +.zebra-striped tbody tr:nth-child(odd) td,.zebra-striped tbody tr:nth-child(odd) th{background-color:#f9f9f9;} +.zebra-striped tbody tr:hover td,.zebra-striped tbody tr:hover th{background-color:#f5f5f5;} +table .header{cursor:pointer;}table .header:after{content:"";float:right;margin-top:7px;border-width:0 4px 4px;border-style:solid;border-color:#000 transparent;visibility:hidden;} +table .headerSortUp,table .headerSortDown{background-color:rgba(141, 192, 219, 0.25);text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);} +table .header:hover:after{visibility:visible;} +table .headerSortDown:after,table .headerSortDown:hover:after{visibility:visible;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +table .headerSortUp:after{border-bottom:none;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #000;visibility:visible;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;filter:alpha(opacity=60);-khtml-opacity:0.6;-moz-opacity:0.6;opacity:0.6;} +table .blue{color:#049cdb;border-bottom-color:#049cdb;} +table .headerSortUp.blue,table .headerSortDown.blue{background-color:#ade6fe;} +table .green{color:#46a546;border-bottom-color:#46a546;} +table .headerSortUp.green,table .headerSortDown.green{background-color:#cdeacd;} +table .red{color:#9d261d;border-bottom-color:#9d261d;} +table .headerSortUp.red,table .headerSortDown.red{background-color:#f4c8c5;} +table .yellow{color:#ffc40d;border-bottom-color:#ffc40d;} +table .headerSortUp.yellow,table .headerSortDown.yellow{background-color:#fff6d9;} +table .orange{color:#f89406;border-bottom-color:#f89406;} +table .headerSortUp.orange,table .headerSortDown.orange{background-color:#fee9cc;} +table .purple{color:#7a43b6;border-bottom-color:#7a43b6;} +table .headerSortUp.purple,table .headerSortDown.purple{background-color:#e2d5f0;} +.topbar{height:40px;position:fixed;top:0;left:0;right:0;z-index:10000;overflow:visible;}.topbar a{color:#bfbfbf;text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);} +.topbar h3 a:hover,.topbar .brand:hover,.topbar ul .active>a{background-color:#333;background-color:rgba(255, 255, 255, 0.05);color:#ffffff;text-decoration:none;} +.topbar h3{position:relative;} +.topbar h3 a,.topbar .brand{float:left;display:block;padding:8px 20px 12px;margin-left:-20px;color:#ffffff;font-size:20px;font-weight:200;line-height:1;} +.topbar p{margin:0;line-height:40px;}.topbar p a:hover{background-color:transparent;color:#ffffff;} +.topbar form{float:left;margin:5px 0 0 0;position:relative;filter:alpha(opacity=100);-khtml-opacity:1;-moz-opacity:1;opacity:1;} +.topbar form.pull-right{float:right;} +.topbar input{background-color:#444;background-color:rgba(255, 255, 255, 0.3);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:normal;font-weight:13px;line-height:1;padding:4px 9px;color:#ffffff;color:rgba(255, 255, 255, 0.75);border:1px solid #111;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.1),0 1px 0px rgba(255, 255, 255, 0.25);-webkit-transition:none;-moz-transition:none;-ms-transition:none;-o-transition:none;transition:none;}.topbar input:-moz-placeholder{color:#e6e6e6;} +.topbar input::-webkit-input-placeholder{color:#e6e6e6;} +.topbar input:hover{background-color:#bfbfbf;background-color:rgba(255, 255, 255, 0.5);color:#ffffff;} +.topbar input:focus,.topbar input.focused{outline:0;background-color:#ffffff;color:#404040;text-shadow:0 1px 0 #ffffff;border:0;padding:5px 10px;-webkit-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);-moz-box-shadow:0 0 3px rgba(0, 0, 0, 0.15);box-shadow:0 0 3px rgba(0, 0, 0, 0.15);} +.topbar-inner,.topbar .fill{background-color:#222;background-color:#222222;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#333333), to(#222222));background-image:-moz-linear-gradient(top, #333333, #222222);background-image:-ms-linear-gradient(top, #333333, #222222);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #333333), color-stop(100%, #222222));background-image:-webkit-linear-gradient(top, #333333, #222222);background-image:-o-linear-gradient(top, #333333, #222222);background-image:linear-gradient(top, #333333, #222222);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#333333', endColorstr='#222222', GradientType=0);-webkit-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);-moz-box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);box-shadow:0 1px 3px rgba(0, 0, 0, 0.25),inset 0 -1px 0 rgba(0, 0, 0, 0.1);} +.topbar div>ul,.nav{display:block;float:left;margin:0 10px 0 0;position:relative;left:0;}.topbar div>ul>li,.nav>li{display:block;float:left;} +.topbar div>ul a,.nav a{display:block;float:none;padding:10px 10px 11px;line-height:19px;text-decoration:none;}.topbar div>ul a:hover,.nav a:hover{color:#ffffff;text-decoration:none;} +.topbar div>ul .active>a,.nav .active>a{background-color:#222;background-color:rgba(0, 0, 0, 0.5);} +.topbar div>ul.secondary-nav,.nav.secondary-nav{float:right;margin-left:10px;margin-right:0;}.topbar div>ul.secondary-nav .menu-dropdown,.nav.secondary-nav .menu-dropdown,.topbar div>ul.secondary-nav .dropdown-menu,.nav.secondary-nav .dropdown-menu{right:0;border:0;} +.topbar div>ul a.menu:hover,.nav a.menu:hover,.topbar div>ul li.open .menu,.nav li.open .menu,.topbar div>ul .dropdown-toggle:hover,.nav .dropdown-toggle:hover,.topbar div>ul .dropdown.open .dropdown-toggle,.nav .dropdown.open .dropdown-toggle{background:#444;background:rgba(255, 255, 255, 0.05);} +.topbar div>ul .menu-dropdown,.nav .menu-dropdown,.topbar div>ul .dropdown-menu,.nav .dropdown-menu{background-color:#333;}.topbar div>ul .menu-dropdown a.menu,.nav .menu-dropdown a.menu,.topbar div>ul .dropdown-menu a.menu,.nav .dropdown-menu a.menu,.topbar div>ul .menu-dropdown .dropdown-toggle,.nav .menu-dropdown .dropdown-toggle,.topbar div>ul .dropdown-menu .dropdown-toggle,.nav .dropdown-menu .dropdown-toggle{color:#ffffff;}.topbar div>ul .menu-dropdown a.menu.open,.nav .menu-dropdown a.menu.open,.topbar div>ul .dropdown-menu a.menu.open,.nav .dropdown-menu a.menu.open,.topbar div>ul .menu-dropdown .dropdown-toggle.open,.nav .menu-dropdown .dropdown-toggle.open,.topbar div>ul .dropdown-menu .dropdown-toggle.open,.nav .dropdown-menu .dropdown-toggle.open{background:#444;background:rgba(255, 255, 255, 0.05);} +.topbar div>ul .menu-dropdown li a,.nav .menu-dropdown li a,.topbar div>ul .dropdown-menu li a,.nav .dropdown-menu li a{color:#999;text-shadow:0 1px 0 rgba(0, 0, 0, 0.5);}.topbar div>ul .menu-dropdown li a:hover,.nav .menu-dropdown li a:hover,.topbar div>ul .dropdown-menu li a:hover,.nav .dropdown-menu li a:hover{background-color:#191919;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#292929), to(#191919));background-image:-moz-linear-gradient(top, #292929, #191919);background-image:-ms-linear-gradient(top, #292929, #191919);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #292929), color-stop(100%, #191919));background-image:-webkit-linear-gradient(top, #292929, #191919);background-image:-o-linear-gradient(top, #292929, #191919);background-image:linear-gradient(top, #292929, #191919);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#292929', endColorstr='#191919', GradientType=0);color:#ffffff;} +.topbar div>ul .menu-dropdown .active a,.nav .menu-dropdown .active a,.topbar div>ul .dropdown-menu .active a,.nav .dropdown-menu .active a{color:#ffffff;} +.topbar div>ul .menu-dropdown .divider,.nav .menu-dropdown .divider,.topbar div>ul .dropdown-menu .divider,.nav .dropdown-menu .divider{background-color:#222;border-color:#444;} +.topbar ul .menu-dropdown li a,.topbar ul .dropdown-menu li a{padding:4px 15px;} +li.menu,.dropdown{position:relative;} +a.menu:after,.dropdown-toggle:after{width:0;height:0;display:inline-block;content:"↓";text-indent:-99999px;vertical-align:top;margin-top:8px;margin-left:4px;border-left:4px solid transparent;border-right:4px solid transparent;border-top:4px solid #ffffff;filter:alpha(opacity=50);-khtml-opacity:0.5;-moz-opacity:0.5;opacity:0.5;} +.menu-dropdown,.dropdown-menu{background-color:#ffffff;float:left;display:none;position:absolute;top:40px;z-index:900;min-width:160px;max-width:220px;_width:160px;margin-left:0;margin-right:0;padding:6px 0;zoom:1;border-color:#999;border-color:rgba(0, 0, 0, 0.2);border-style:solid;border-width:0 1px 1px;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-moz-box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.menu-dropdown li,.dropdown-menu li{float:none;display:block;background-color:none;} +.menu-dropdown .divider,.dropdown-menu .divider{height:1px;margin:5px 0;overflow:hidden;background-color:#eee;border-bottom:1px solid #ffffff;} +.topbar .dropdown-menu a,.dropdown-menu a{display:block;padding:4px 15px;clear:both;font-weight:normal;line-height:18px;color:#808080;text-shadow:0 1px 0 #ffffff;}.topbar .dropdown-menu a:hover,.dropdown-menu a:hover,.topbar .dropdown-menu a.hover,.dropdown-menu a.hover{background-color:#dddddd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#eeeeee), to(#dddddd));background-image:-moz-linear-gradient(top, #eeeeee, #dddddd);background-image:-ms-linear-gradient(top, #eeeeee, #dddddd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #eeeeee), color-stop(100%, #dddddd));background-image:-webkit-linear-gradient(top, #eeeeee, #dddddd);background-image:-o-linear-gradient(top, #eeeeee, #dddddd);background-image:linear-gradient(top, #eeeeee, #dddddd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#dddddd', GradientType=0);color:#404040;text-decoration:none;-webkit-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);-moz-box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);box-shadow:inset 0 1px 0 rgba(0, 0, 0, 0.025),inset 0 -1px rgba(0, 0, 0, 0.025);} +.open .menu,.dropdown.open .menu,.open .dropdown-toggle,.dropdown.open .dropdown-toggle{color:#ffffff;background:#ccc;background:rgba(0, 0, 0, 0.3);} +.open .menu-dropdown,.dropdown.open .menu-dropdown,.open .dropdown-menu,.dropdown.open .dropdown-menu{display:block;} +.tabs,.pills{margin:0 0 18px;padding:0;list-style:none;zoom:1;}.tabs:before,.pills:before,.tabs:after,.pills:after{display:table;content:"";zoom:1;} +.tabs:after,.pills:after{clear:both;} +.tabs>li,.pills>li{float:left;}.tabs>li>a,.pills>li>a{display:block;} +.tabs{border-color:#ddd;border-style:solid;border-width:0 0 1px;}.tabs>li{position:relative;margin-bottom:-1px;}.tabs>li>a{padding:0 15px;margin-right:2px;line-height:34px;border:1px solid transparent;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0;border-radius:4px 4px 0 0;}.tabs>li>a:hover{text-decoration:none;background-color:#eee;border-color:#eee #eee #ddd;} +.tabs .active>a,.tabs .active>a:hover{color:#808080;background-color:#ffffff;border:1px solid #ddd;border-bottom-color:transparent;cursor:default;} +.tabs .menu-dropdown,.tabs .dropdown-menu{top:35px;border-width:1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;} +.tabs a.menu:after,.tabs .dropdown-toggle:after{border-top-color:#999;margin-top:15px;margin-left:5px;} +.tabs li.open.menu .menu,.tabs .open.dropdown .dropdown-toggle{border-color:#999;} +.tabs li.open a.menu:after,.tabs .dropdown.open .dropdown-toggle:after{border-top-color:#555;} +.pills a{margin:5px 3px 5px 0;padding:0 15px;line-height:30px;text-shadow:0 1px 1px #ffffff;-webkit-border-radius:15px;-moz-border-radius:15px;border-radius:15px;}.pills a:hover{color:#ffffff;text-decoration:none;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#00438a;} +.pills .active a{color:#ffffff;text-shadow:0 1px 1px rgba(0, 0, 0, 0.25);background-color:#0069d6;} +.pills-vertical>li{float:none;} +.tab-content>.tab-pane,.pill-content>.pill-pane,.tab-content>div,.pill-content>div{display:none;} +.tab-content>.active,.pill-content>.active{display:block;} +.breadcrumb{padding:7px 14px;margin:0 0 18px;background-color:#f5f5f5;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ffffff), to(#f5f5f5));background-image:-moz-linear-gradient(top, #ffffff, #f5f5f5);background-image:-ms-linear-gradient(top, #ffffff, #f5f5f5);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ffffff), color-stop(100%, #f5f5f5));background-image:-webkit-linear-gradient(top, #ffffff, #f5f5f5);background-image:-o-linear-gradient(top, #ffffff, #f5f5f5);background-image:linear-gradient(top, #ffffff, #f5f5f5);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#f5f5f5', GradientType=0);border:1px solid #ddd;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;}.breadcrumb li{display:inline;text-shadow:0 1px 0 #ffffff;} +.breadcrumb .divider{padding:0 5px;color:#bfbfbf;} +.breadcrumb .active a{color:#404040;} +.hero-unit{background-color:#f5f5f5;margin-bottom:30px;padding:60px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;} +.hero-unit p{font-size:18px;font-weight:200;line-height:27px;} +footer{margin-top:17px;padding-top:17px;border-top:1px solid #eee;} +.page-header{margin-bottom:17px;border-bottom:1px solid #ddd;-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);box-shadow:0 1px 0 rgba(255, 255, 255, 0.5);}.page-header h1{margin-bottom:8px;} +.btn.danger,.alert-message.danger,.btn.danger:hover,.alert-message.danger:hover,.btn.error,.alert-message.error,.btn.error:hover,.alert-message.error:hover,.btn.success,.alert-message.success,.btn.success:hover,.alert-message.success:hover,.btn.info,.alert-message.info,.btn.info:hover,.alert-message.info:hover{color:#ffffff;} +.btn .close,.alert-message .close{font-family:Arial,sans-serif;line-height:18px;} +.btn.danger,.alert-message.danger,.btn.error,.alert-message.error{background-color:#c43c35;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#ee5f5b), to(#c43c35));background-image:-moz-linear-gradient(top, #ee5f5b, #c43c35);background-image:-ms-linear-gradient(top, #ee5f5b, #c43c35);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #ee5f5b), color-stop(100%, #c43c35));background-image:-webkit-linear-gradient(top, #ee5f5b, #c43c35);background-image:-o-linear-gradient(top, #ee5f5b, #c43c35);background-image:linear-gradient(top, #ee5f5b, #c43c35);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ee5f5b', endColorstr='#c43c35', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#c43c35 #c43c35 #882a25;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.success,.alert-message.success{background-color:#57a957;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#62c462), to(#57a957));background-image:-moz-linear-gradient(top, #62c462, #57a957);background-image:-ms-linear-gradient(top, #62c462, #57a957);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #62c462), color-stop(100%, #57a957));background-image:-webkit-linear-gradient(top, #62c462, #57a957);background-image:-o-linear-gradient(top, #62c462, #57a957);background-image:linear-gradient(top, #62c462, #57a957);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#62c462', endColorstr='#57a957', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#57a957 #57a957 #3d773d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.info,.alert-message.info{background-color:#339bb9;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#5bc0de), to(#339bb9));background-image:-moz-linear-gradient(top, #5bc0de, #339bb9);background-image:-ms-linear-gradient(top, #5bc0de, #339bb9);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bc0de), color-stop(100%, #339bb9));background-image:-webkit-linear-gradient(top, #5bc0de, #339bb9);background-image:-o-linear-gradient(top, #5bc0de, #339bb9);background-image:linear-gradient(top, #5bc0de, #339bb9);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5bc0de', endColorstr='#339bb9', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#339bb9 #339bb9 #22697d;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn{cursor:pointer;display:inline-block;background-color:#e6e6e6;background-repeat:no-repeat;background-image:-webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), color-stop(25%, #ffffff), to(#e6e6e6));background-image:-webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-moz-linear-gradient(top, #ffffff, #ffffff 25%, #e6e6e6);background-image:-ms-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:-o-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);background-image:linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e6e6e6', GradientType=0);padding:5px 14px 6px;text-shadow:0 1px 1px rgba(255, 255, 255, 0.75);color:#333;font-size:13px;line-height:normal;border:1px solid #ccc;border-bottom-color:#bbb;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2),0 1px 2px rgba(0, 0, 0, 0.05);-webkit-transition:0.1s linear all;-moz-transition:0.1s linear all;-ms-transition:0.1s linear all;-o-transition:0.1s linear all;transition:0.1s linear all;}.btn:hover{background-position:0 -15px;color:#333;text-decoration:none;} +.btn:focus{outline:1px dotted #666;} +.btn.primary{color:#ffffff;background-color:#0064cd;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#049cdb), to(#0064cd));background-image:-moz-linear-gradient(top, #049cdb, #0064cd);background-image:-ms-linear-gradient(top, #049cdb, #0064cd);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #049cdb), color-stop(100%, #0064cd));background-image:-webkit-linear-gradient(top, #049cdb, #0064cd);background-image:-o-linear-gradient(top, #049cdb, #0064cd);background-image:linear-gradient(top, #049cdb, #0064cd);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#049cdb', endColorstr='#0064cd', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#0064cd #0064cd #003f81;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);} +.btn.active,.btn:active{-webkit-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:inset 0 2px 4px rgba(0, 0, 0, 0.25),0 1px 2px rgba(0, 0, 0, 0.05);} +.btn.disabled{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn[disabled]{cursor:default;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);filter:alpha(opacity=65);-khtml-opacity:0.65;-moz-opacity:0.65;opacity:0.65;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;} +.btn.large{font-size:15px;line-height:normal;padding:9px 14px 9px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;} +.btn.small{padding:7px 9px 7px;font-size:11px;} +:root .alert-message,:root .btn{border-radius:0 \0;} +button.btn::-moz-focus-inner,input[type=submit].btn::-moz-focus-inner{padding:0;border:0;} +.close{float:right;color:#000000;font-size:20px;font-weight:bold;line-height:13.5px;text-shadow:0 1px 0 #ffffff;filter:alpha(opacity=25);-khtml-opacity:0.25;-moz-opacity:0.25;opacity:0.25;}.close:hover{color:#000000;text-decoration:none;filter:alpha(opacity=40);-khtml-opacity:0.4;-moz-opacity:0.4;opacity:0.4;} +.alert-message{position:relative;padding:7px 15px;margin-bottom:18px;color:#404040;background-color:#eedc94;background-repeat:repeat-x;background-image:-khtml-gradient(linear, left top, left bottom, from(#fceec1), to(#eedc94));background-image:-moz-linear-gradient(top, #fceec1, #eedc94);background-image:-ms-linear-gradient(top, #fceec1, #eedc94);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0%, #fceec1), color-stop(100%, #eedc94));background-image:-webkit-linear-gradient(top, #fceec1, #eedc94);background-image:-o-linear-gradient(top, #fceec1, #eedc94);background-image:linear-gradient(top, #fceec1, #eedc94);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fceec1', endColorstr='#eedc94', GradientType=0);text-shadow:0 -1px 0 rgba(0, 0, 0, 0.25);border-color:#eedc94 #eedc94 #e4c652;border-color:rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);border-width:1px;border-style:solid;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.25);}.alert-message .close{margin-top:1px;*margin-top:0;} +.alert-message a{font-weight:bold;color:#404040;} +.alert-message.danger p a,.alert-message.error p a,.alert-message.success p a,.alert-message.info p a{color:#ffffff;} +.alert-message h5{line-height:18px;} +.alert-message p{margin-bottom:0;} +.alert-message div{margin-top:5px;margin-bottom:2px;line-height:28px;} +.alert-message .btn{-webkit-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);-moz-box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);box-shadow:0 1px 0 rgba(255, 255, 255, 0.25);} +.alert-message.block-message{background-image:none;background-color:#fdf5d9;filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);padding:14px;border-color:#fceec1;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;}.alert-message.block-message ul,.alert-message.block-message p{margin-right:30px;} +.alert-message.block-message ul{margin-bottom:0;} +.alert-message.block-message li{color:#404040;} +.alert-message.block-message .alert-actions{margin-top:5px;} +.alert-message.block-message.error,.alert-message.block-message.success,.alert-message.block-message.info{color:#404040;text-shadow:0 1px 0 rgba(255, 255, 255, 0.5);} +.alert-message.block-message.error{background-color:#fddfde;border-color:#fbc7c6;} +.alert-message.block-message.success{background-color:#d1eed1;border-color:#bfe7bf;} +.alert-message.block-message.info{background-color:#ddf4fb;border-color:#c6edf9;} +.alert-message.block-message.danger p a,.alert-message.block-message.error p a,.alert-message.block-message.success p a,.alert-message.block-message.info p a{color:#404040;} +.pagination{height:36px;margin:18px 0;}.pagination ul{float:left;margin:0;border:1px solid #ddd;border:1px solid rgba(0, 0, 0, 0.15);-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);-moz-box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);box-shadow:0 1px 2px rgba(0, 0, 0, 0.05);} +.pagination li{display:inline;} +.pagination a{float:left;padding:0 14px;line-height:34px;border-right:1px solid;border-right-color:#ddd;border-right-color:rgba(0, 0, 0, 0.15);*border-right-color:#ddd;text-decoration:none;} +.pagination a:hover,.pagination .active a{background-color:#c7eefe;} +.pagination .disabled a,.pagination .disabled a:hover{background-color:transparent;color:#bfbfbf;} +.pagination .next a{border:0;} +.well{background-color:#f5f5f5;margin-bottom:20px;padding:19px;min-height:20px;border:1px solid #eee;border:1px solid rgba(0, 0, 0, 0.05);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);-moz-box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);box-shadow:inset 0 1px 1px rgba(0, 0, 0, 0.05);}.well blockquote{border-color:#ddd;border-color:rgba(0, 0, 0, 0.15);} +.modal-backdrop{background-color:#000000;position:fixed;top:0;left:0;right:0;bottom:0;z-index:10000;}.modal-backdrop.fade{opacity:0;} +.modal-backdrop,.modal-backdrop.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} +.modal{position:fixed;top:50%;left:50%;z-index:11000;max-height:500px;overflow:auto;width:560px;margin:-250px 0 0 -280px;background-color:#ffffff;border:1px solid #999;border:1px solid rgba(0, 0, 0, 0.3);*border:1px solid #999;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.modal .close{margin-top:7px;} +.modal.fade{-webkit-transition:opacity .3s linear, top .3s ease-out;-moz-transition:opacity .3s linear, top .3s ease-out;-ms-transition:opacity .3s linear, top .3s ease-out;-o-transition:opacity .3s linear, top .3s ease-out;transition:opacity .3s linear, top .3s ease-out;top:-25%;} +.modal.fade.in{top:50%;} +.modal-header{border-bottom:1px solid #eee;padding:5px 15px;} +.modal-body{padding:15px;} +.modal-body form{margin-bottom:0;} +.modal-footer{background-color:#f5f5f5;padding:14px 15px 15px;border-top:1px solid #ddd;-webkit-border-radius:0 0 6px 6px;-moz-border-radius:0 0 6px 6px;border-radius:0 0 6px 6px;-webkit-box-shadow:inset 0 1px 0 #ffffff;-moz-box-shadow:inset 0 1px 0 #ffffff;box-shadow:inset 0 1px 0 #ffffff;zoom:1;margin-bottom:0;}.modal-footer:before,.modal-footer:after{display:table;content:"";zoom:1;} +.modal-footer:after{clear:both;} +.modal-footer .btn{float:right;margin-left:5px;} +.modal .popover,.modal .twipsy{z-index:12000;} +.twipsy{display:block;position:absolute;visibility:visible;padding:5px;font-size:11px;z-index:1000;filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;}.twipsy.fade.in{filter:alpha(opacity=80);-khtml-opacity:0.8;-moz-opacity:0.8;opacity:0.8;} +.twipsy.above .twipsy-arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.twipsy.left .twipsy-arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.twipsy.below .twipsy-arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.twipsy.right .twipsy-arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.twipsy-inner{padding:3px 8px;background-color:#000000;color:white;text-align:center;max-width:200px;text-decoration:none;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;} +.twipsy-arrow{position:absolute;width:0;height:0;} +.popover{position:absolute;top:0;left:0;z-index:1000;padding:5px;display:none;}.popover.above .arrow{bottom:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-top:5px solid #000000;} +.popover.right .arrow{top:50%;left:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:5px solid #000000;} +.popover.below .arrow{top:0;left:50%;margin-left:-5px;border-left:5px solid transparent;border-right:5px solid transparent;border-bottom:5px solid #000000;} +.popover.left .arrow{top:50%;right:0;margin-top:-5px;border-top:5px solid transparent;border-bottom:5px solid transparent;border-left:5px solid #000000;} +.popover .arrow{position:absolute;width:0;height:0;} +.popover .inner{background:#000000;background:rgba(0, 0, 0, 0.8);padding:3px;overflow:hidden;width:280px;-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px;-webkit-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);-moz-box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);box-shadow:0 3px 7px rgba(0, 0, 0, 0.3);} +.popover .title{background-color:#f5f5f5;padding:9px 15px;line-height:1;-webkit-border-radius:3px 3px 0 0;-moz-border-radius:3px 3px 0 0;border-radius:3px 3px 0 0;border-bottom:1px solid #eee;} +.popover .content{background-color:#ffffff;padding:14px;-webkit-border-radius:0 0 3px 3px;-moz-border-radius:0 0 3px 3px;border-radius:0 0 3px 3px;-webkit-background-clip:padding-box;-moz-background-clip:padding-box;background-clip:padding-box;}.popover .content p,.popover .content ul,.popover .content ol{margin-bottom:0;} +.fade{-webkit-transition:opacity 0.15s linear;-moz-transition:opacity 0.15s linear;-ms-transition:opacity 0.15s linear;-o-transition:opacity 0.15s linear;transition:opacity 0.15s linear;opacity:0;}.fade.in{opacity:1;} +.label{padding:1px 3px 2px;font-size:9.75px;font-weight:bold;color:#ffffff;text-transform:uppercase;white-space:nowrap;background-color:#bfbfbf;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;text-shadow:none;}.label.important{background-color:#c43c35;} +.label.warning{background-color:#f89406;} +.label.success{background-color:#46a546;} +.label.notice{background-color:#62cffc;} +.media-grid{margin-left:-20px;margin-bottom:0;zoom:1;}.media-grid:before,.media-grid:after{display:table;content:"";zoom:1;} +.media-grid:after{clear:both;} +.media-grid li{display:inline;} +.media-grid a{float:left;padding:4px;margin:0 0 18px 20px;border:1px solid #ddd;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);box-shadow:0 1px 1px rgba(0, 0, 0, 0.075);}.media-grid a img{display:block;} +.media-grid a:hover{border-color:#0069d6;-webkit-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);-moz-box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);box-shadow:0 1px 4px rgba(0, 105, 214, 0.25);}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/_dmedia/less.js Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,9 @@ +// +// LESS - Leaner CSS v1.2.1 +// http://lesscss.org +// +// Copyright (c) 2009-2011, Alexis Sellier +// Licensed under the Apache 2.0 License. +// +(function(a,b){function c(b){return a.less[b.split("/")[1]]}function m(){var a=document.getElementsByTagName("style");for(var b=0;b<a.length;b++)a[b].type.match(k)&&(new d.Parser).parse(a[b].innerHTML||"",function(c,d){var e=d.toCSS(),f=a[b];f.type="text/css",f.styleSheet?f.styleSheet.cssText=e:f.innerHTML=e})}function n(a,b){for(var c=0;c<d.sheets.length;c++)o(d.sheets[c],a,b,d.sheets.length-(c+1))}function o(b,c,e,f){var g=a.location.href.replace(/[#?].*$/,""),i=b.href.replace(/\?.*$/,""),j=h&&h.getItem(i),k=h&&h.getItem(i+":timestamp"),l={css:j,timestamp:k};/^(https?|file):/.test(i)||(i.charAt(0)=="/"?i=a.location.protocol+"//"+a.location.host+i:i=g.slice(0,g.lastIndexOf("/")+1)+i);var m=i.match(/([^\/]+)$/)[1];s(b.href,b.type,function(a,g){if(!e&&l&&g&&(new Date(g)).valueOf()===(new Date(l.timestamp)).valueOf())r(l.css,b),c(null,b,{local:!0,remaining:f});else try{(new d.Parser({optimization:d.optimization,paths:[i.replace(/[\w\.-]+$/,"")],mime:b.type,filename:m})).parse(a,function(d,e){if(d)return w(d,i);try{c(d,e,a,b,{local:!1,lastModified:g,remaining:f}),u(document.getElementById("less-error-message:"+q(i)))}catch(d){w(d,i)}})}catch(h){w(h,i)}},function(a,b){throw new Error("Couldn't load "+b+" ("+a+")")})}function q(a){return a.replace(/^[a-z]+:\/\/?[^\/]+/,"").replace(/^\//,"").replace(/\?.*$/,"").replace(/\.[^\.\/]+$/,"").replace(/[^\.\w-]+/g,"-").replace(/\./g,":")}function r(a,b,c){var d,e=b.href?b.href.replace(/\?.*$/,""):"",f="less:"+(b.title||q(e));(d=document.getElementById(f))===null&&(d=document.createElement("style"),d.type="text/css",d.media=b.media||"screen",d.id=f,document.getElementsByTagName("head")[0].appendChild(d));if(d.styleSheet)try{d.styleSheet.cssText=a}catch(g){throw new Error("Couldn't reassign styleSheet.cssText.")}else(function(a){d.childNodes.length>0?d.firstChild.nodeValue!==a.nodeValue&&d.replaceChild(a,d.firstChild):d.appendChild(a)})(document.createTextNode(a));c&&h&&(v("saving "+e+" to cache."),h.setItem(e,a),h.setItem(e+":timestamp",c))}function s(a,b,c,e){function i(b,c,d){b.status>=200&&b.status<300?c(b.responseText,b.getResponseHeader("Last-Modified")):typeof d=="function"&&d(b.status,a)}var f=t(),h=g?!1:d.async;typeof f.overrideMimeType=="function"&&f.overrideMimeType("text/css"),f.open("GET",a,h),f.setRequestHeader("Accept",b||"text/x-less, text/css; q=0.9, */*; q=0.5"),f.send(null),g?f.status===0||f.status>=200&&f.status<300?c(f.responseText):e(f.status,a):h?f.onreadystatechange=function(){f.readyState==4&&i(f,c,e)}:i(f,c,e)}function t(){if(a.XMLHttpRequest)return new XMLHttpRequest;try{return new ActiveXObject("MSXML2.XMLHTTP.3.0")}catch(b){return v("browser doesn't support AJAX."),null}}function u(a){return a&&a.parentNode.removeChild(a)}function v(a){d.env=="development"&&typeof console!="undefined"&&console.log("less: "+a)}function w(a,b){var c="less-error-message:"+q(b),e='<li><label>{line}</label><pre class="{class}">{content}</pre></li>',f=document.createElement("div"),g,h,i=[],j=a.filename||b;f.id=c,f.className="less-error-message",h="<h3>"+(a.message||"There is an error in your .less file")+"</h3>"+'<p>in <a href="'+j+'">'+j+"</a> ";var k=function(a,b,c){a.extract[b]&&i.push(e.replace(/\{line\}/,parseInt(a.line)+(b-1)).replace(/\{class\}/,c).replace(/\{content\}/,a.extract[b]))};a.stack?h+="<br/>"+a.stack.split("\n").slice(1).join("<br/>"):a.extract&&(k(a,0,""),k(a,1,"line"),k(a,2,""),h+="on line "+a.line+", column "+(a.column+1)+":</p>"+"<ul>"+i.join("")+"</ul>"),f.innerHTML=h,r([".less-error-message ul, .less-error-message li {","list-style-type: none;","margin-right: 15px;","padding: 4px 0;","margin: 0;","}",".less-error-message label {","font-size: 12px;","margin-right: 15px;","padding: 4px 0;","color: #cc7777;","}",".less-error-message pre {","color: #dd6666;","padding: 4px 0;","margin: 0;","display: inline-block;","}",".less-error-message pre.line {","color: #ff0000;","}",".less-error-message h3 {","font-size: 20px;","font-weight: bold;","padding: 15px 0 5px 0;","margin: 0;","}",".less-error-message a {","color: #10a","}",".less-error-message .error {","color: red;","font-weight: bold;","padding-bottom: 2px;","border-bottom: 1px dashed red;","}"].join("\n"),{title:"error-message"}),f.style.cssText=["font-family: Arial, sans-serif","border: 1px solid #e00","background-color: #eee","border-radius: 5px","-webkit-border-radius: 5px","-moz-border-radius: 5px","color: #e00","padding: 15px","margin-bottom: 15px"].join(";"),d.env=="development"&&(g=setInterval(function(){document.body&&(document.getElementById(c)?document.body.replaceChild(f,document.getElementById(c)):document.body.insertBefore(f,document.body.firstChild),clearInterval(g))},10))}Array.isArray||(Array.isArray=function(a){return Object.prototype.toString.call(a)==="[object Array]"||a instanceof Array}),Array.prototype.forEach||(Array.prototype.forEach=function(a,b){var c=this.length>>>0;for(var d=0;d<c;d++)d in this&&a.call(b,this[d],d,this)}),Array.prototype.map||(Array.prototype.map=function(a){var b=this.length>>>0,c=new Array(b),d=arguments[1];for(var e=0;e<b;e++)e in this&&(c[e]=a.call(d,this[e],e,this));return c}),Array.prototype.filter||(Array.prototype.filter=function(a){var b=[],c=arguments[1];for(var d=0;d<this.length;d++)a.call(c,this[d])&&b.push(this[d]);return b}),Array.prototype.reduce||(Array.prototype.reduce=function(a){var b=this.length>>>0,c=0;if(b===0&&arguments.length===1)throw new TypeError;if(arguments.length>=2)var d=arguments[1];else do{if(c in this){d=this[c++];break}if(++c>=b)throw new TypeError}while(!0);for(;c<b;c++)c in this&&(d=a.call(null,d,this[c],c,this));return d}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){var b=this.length,c=arguments[1]||0;if(!b)return-1;if(c>=b)return-1;c<0&&(c+=b);for(;c<b;c++){if(!Object.prototype.hasOwnProperty.call(this,c))continue;if(a===this[c])return c}return-1}),Object.keys||(Object.keys=function(a){var b=[];for(var c in a)Object.prototype.hasOwnProperty.call(a,c)&&b.push(c);return b}),String.prototype.trim||(String.prototype.trim=function(){return String(this).replace(/^\s\s*/,"").replace(/\s\s*$/,"")});var d,f;typeof environment=="object"&&{}.toString.call(environment)==="[object Environment]"?(typeof a=="undefined"?d={}:d=a.less={},f=d.tree={},d.mode="rhino"):typeof a=="undefined"?(d=exports,f=c("./tree"),d.mode="node"):(typeof a.less=="undefined"&&(a.less={}),d=a.less,f=a.less.tree={},d.mode="browser"),d.Parser=function(b){function t(){j=m[i],k=h,n=h}function u(){m[i]=j,h=k,n=h}function v(){h>n&&(m[i]=m[i].slice(h-n),n=h)}function w(a){var b,c,d,e,f,j,k,l;if(a instanceof Function)return a.call(o.parsers);if(typeof a=="string")b=g.charAt(h)===a?a:null,d=1,v();else{v();if(!(b=a.exec(m[i])))return null;d=b[0].length}if(b){l=h+=d,j=h+m[i].length-d;while(h<j){e=g.charCodeAt(h);if(e!==32&&e!==10&&e!==9)break;h++}return m[i]=m[i].slice(d+(h-l)),n=h,m[i].length===0&&i<m.length-1&&i++,typeof b=="string"?b:b.length===1?b[0]:b}}function x(a,b){var c=w(a);if(!!c)return c;y(b||(typeof a=="string"?"expected '"+a+"' got '"+g.charAt(h)+"'":"unexpected token"))}function y(a,b){throw{index:h,type:b||"Syntax",message:a}}function z(a){return typeof a=="string"?g.charAt(h)===a:a.test(m[i])?!0:!1}function A(a,b){return a.filename&&b.filename&&a.filename!==b.filename?o.imports.contents[a.filename]:g}function B(a,b){for(var c=a,d=-1;c>=0&&b.charAt(c)!=="\n";c--)d++;return{line:typeof a=="number"?(b.slice(0,a).match(/\n/g)||"").length:null,column:d}}function C(a,b){var c=A(a,b),d=B(a.index,c),e=d.line,f=d.column,g=c.split("\n");this.type=a.type||"Syntax",this.message=a.message,this.filename=a.filename||b.filename,this.index=a.index,this.line=typeof e=="number"?e+1:null,this.callLine=a.call&&B(a.call,c)+1,this.callExtract=g[B(a.call,c)],this.stack=a.stack,this.column=f,this.extract=[g[e-1],g[e],g[e+1]]}var g,h,i,j,k,l,m,n,o,q=this,r=function(){},s=this.imports={paths:b&&b.paths||[],queue:[],files:{},contents:{},mime:b&&b.mime,error:null,push:function(a,c){var e=this;this.queue.push(a),d.Parser.importer(a,this.paths,function(b,d,f){e.queue.splice(e.queue.indexOf(a),1),e.files[a]=d,e.contents[a]=f,b&&!e.error&&(e.error=b),c(b,d),e.queue.length===0&&r()},b)}};return this.env=b=b||{},this.optimization="optimization"in this.env?this.env.optimization:1,this.env.filename=this.env.filename||null,o={imports:s,parse:function(a,e){var j,k,p,q,s,t,u=[],v,x=null;h=i=n=l=0,m=[],g=a.replace(/\r\n/g,"\n"),m=function(a){var c=0,d=/[^"'`\{\}\/\(\)]+/g,e=/\/\*(?:[^*]|\*+[^\/*])*\*+\/|\/\/.*/g,f=0,h,i=a[0],j,k;for(var l=0,m,n;l<g.length;l++){d.lastIndex=l,(h=d.exec(g))&&h.index===l&&(l+=h[0].length,i.push(h[0])),m=g.charAt(l),e.lastIndex=l,!k&&!j&&m==="/"&&(n=g.charAt(l+1),(n==="/"||n==="*")&&(h=e.exec(g))&&h.index===l&&(l+=h[0].length,i.push(h[0]),m=g.charAt(l)));if(m==="{"&&!k&&!j)f++,i.push(m);else if(m==="}"&&!k&&!j)f--,i.push(m),a[++c]=i=[];else if(m==="("&&!k&&!j)i.push(m),j=!0;else if(m===")"&&!k&&j)i.push(m),j=!1;else{if(m==='"'||m==="'"||m==="`")k?k=k===m?!1:k:k=m;i.push(m)}}if(f>0)throw{type:"Syntax",message:"Missing closing `}`",filename:b.filename};return a.map(function(a){return a.join("")})}([[]]);try{j=new f.Ruleset([],w(this.parsers.primary)),j.root=!0}catch(y){return e(new C(y,b))}j.toCSS=function(a){var e,g,h;return function(e,g){var h=[],i;e=e||{},typeof g=="object"&&!Array.isArray(g)&&(g=Object.keys(g).map(function(a){var b=g[a];return b instanceof f.Value||(b instanceof f.Expression||(b=new f.Expression([b])),b=new f.Value([b])),new f.Rule("@"+a,b,!1,0)}),h=[new f.Ruleset(null,g)]);try{var j=a.call(this,{frames:h}).toCSS([],{compress:e.compress||!1})}catch(k){throw new C(k,b)}if(i=o.imports.error)throw i instanceof C?i:new C(i,b);return e.yuicompress&&d.mode==="node"?c("./cssmin").compressor.cssmin(j):e.compress?j.replace(/(\s)+/g,"$1"):j}}(j.eval);if(h<g.length-1){h=l,t=g.split("\n"),s=(g.slice(0,h).match(/\n/g)||"").length+1;for(var z=h,A=-1;z>=0&&g.charAt(z)!=="\n";z--)A++;x={type:"Parse",message:"Syntax Error on line "+s,index:h,filename:b.filename,line:s,column:A,extract:[t[s-2],t[s-1],t[s]]}}this.imports.queue.length>0?r=function(){e(x,j)}:e(x,j)},parsers:{primary:function(){var a,b=[];while((a=w(this.mixin.definition)||w(this.rule)||w(this.ruleset)||w(this.mixin.call)||w(this.comment)||w(this.directive))||w(/^[\s\n]+/))a&&b.push(a);return b},comment:function(){var a;if(g.charAt(h)!=="/")return;if(g.charAt(h+1)==="/")return new f.Comment(w(/^\/\/.*/),!0);if(a=w(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/))return new f.Comment(a)},entities:{quoted:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=='"'&&g.charAt(b)!=="'")return;c&&w("~");if(a=w(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/))return new f.Quoted(a[0],a[1]||a[2],c)},keyword:function(){var a;if(a=w(/^[_A-Za-z-][_A-Za-z0-9-]*/))return f.colors.hasOwnProperty(a)?new f.Color(f.colors[a].slice(1)):new f.Keyword(a)},call:function(){var a,c,d=h;if(!(a=/^([\w-]+|%|progid:[\w\.]+)\(/.exec(m[i])))return;a=a[1].toLowerCase();if(a==="url")return null;h+=a.length;if(a==="alpha")return w(this.alpha);w("("),c=w(this.entities.arguments);if(!w(")"))return;if(a)return new f.Call(a,c,d,b.filename)},arguments:function(){var a=[],b;while(b=w(this.entities.assignment)||w(this.expression)){a.push(b);if(!w(","))break}return a},literal:function(){return w(this.entities.dimension)||w(this.entities.color)||w(this.entities.quoted)},assignment:function(){var a,b;if((a=w(/^\w+(?=\s?=)/i))&&w("=")&&(b=w(this.entity)))return new f.Assignment(a,b)},url:function(){var a;if(g.charAt(h)!=="u"||!w(/^url\(/))return;return a=w(this.entities.quoted)||w(this.entities.variable)||w(this.entities.dataURI)||w(/^[-\w%@$\/.&=:;#+?~]+/)||"",x(")"),new f.URL(a.value||a.data||a instanceof f.Variable?a:new f.Anonymous(a),s.paths)},dataURI:function(){var a;if(w(/^data:/)){a={},a.mime=w(/^[^\/]+\/[^,;)]+/)||"",a.charset=w(/^;\s*charset=[^,;)]+/)||"",a.base64=w(/^;\s*base64/)||"",a.data=w(/^,\s*[^)]+/);if(a.data)return a}},variable:function(){var a,c=h;if(g.charAt(h)==="@"&&(a=w(/^@@?[\w-]+/)))return new f.Variable(a,c,b.filename)},color:function(){var a;if(g.charAt(h)==="#"&&(a=w(/^#([a-fA-F0-9]{6}|[a-fA-F0-9]{3})/)))return new f.Color(a[1])},dimension:function(){var a,b=g.charCodeAt(h);if(b>57||b<45||b===47)return;if(a=w(/^(-?\d*\.?\d+)(px|%|em|rem|pc|ex|in|deg|s|ms|pt|cm|mm|rad|grad|turn)?/))return new f.Dimension(a[1],a[2])},javascript:function(){var a,b=h,c;g.charAt(b)==="~"&&(b++,c=!0);if(g.charAt(b)!=="`")return;c&&w("~");if(a=w(/^`([^`]*)`/))return new f.JavaScript(a[1],h,c)}},variable:function(){var a;if(g.charAt(h)==="@"&&(a=w(/^(@[\w-]+)\s*:/)))return a[1]},shorthand:function(){var a,b;if(!z(/^[@\w.%-]+\/[@\w.-]+/))return;if((a=w(this.entity))&&w("/")&&(b=w(this.entity)))return new f.Shorthand(a,b)},mixin:{call:function(){var a=[],c,d,e,i=h,j=g.charAt(h),k=!1;if(j!=="."&&j!=="#")return;while(c=w(/^[#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/))a.push(new f.Element(d,c,h)),d=w(">");w("(")&&(e=w(this.entities.arguments))&&w(")"),w(this.important)&&(k=!0);if(a.length>0&&(w(";")||z("}")))return new f.mixin.Call(a,e,i,b.filename,k)},definition:function(){var a,b=[],c,d,e,i,j;if(g.charAt(h)!=="."&&g.charAt(h)!=="#"||z(/^[^{]*(;|})/))return;t();if(c=w(/^([#.](?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+)\s*\(/)){a=c[1];while(e=w(this.entities.variable)||w(this.entities.literal)||w(this.entities.keyword)){e instanceof f.Variable?w(":")?(i=x(this.expression,"expected expression"),b.push({name:e.name,value:i})):b.push({name:e.name}):b.push({value:e});if(!w(","))break}x(")"),w(/^when/)&&(j=x(this.conditions,"expected condition")),d=w(this.block);if(d)return new f.mixin.Definition(a,b,d,j);u()}}},entity:function(){return w(this.entities.literal)||w(this.entities.variable)||w(this.entities.url)||w(this.entities.call)||w(this.entities.keyword)||w(this.entities.javascript)||w(this.comment)},end:function(){return w(";")||z("}")},alpha:function(){var a;if(!w(/^\(opacity=/i))return;if(a=w(/^\d+/)||w(this.entities.variable))return x(")"),new f.Alpha(a)},element:function(){var a,b,c,d;c=w(this.combinator),a=w(/^(?:\d+\.\d+|\d+)%/)||w(/^(?:[.#]?|:*)(?:[\w-]|\\(?:[a-fA-F0-9]{1,6} ?|[^a-fA-F0-9]))+/)||w("*")||w(this.attribute)||w(/^\([^)@]+\)/),a||w("(")&&(d=w(this.entities.variable))&&w(")")&&(a=new f.Paren(d));if(a)return new f.Element(c,a,h);if(c.value&&c.value.charAt(0)==="&")return new f.Element(c,null,h)},combinator:function(){var a,b=g.charAt(h);if(b===">"||b==="+"||b==="~"){h++;while(g.charAt(h)===" ")h++;return new f.Combinator(b)}if(b==="&"){a="&",h++,g.charAt(h)===" "&&(a="& ");while(g.charAt(h)===" ")h++;return new f.Combinator(a)}if(b===":"&&g.charAt(h+1)===":"){h+=2;while(g.charAt(h)===" ")h++;return new f.Combinator("::")}return g.charAt(h-1)===" "?new f.Combinator(" "):new f.Combinator(null)},selector:function(){var a,b,c=[],d,e;while(b=w(this.element)){d=g.charAt(h),c.push(b);if(d==="{"||d==="}"||d===";"||d===",")break}if(c.length>0)return new f.Selector(c)},tag:function(){return w(/^[a-zA-Z][a-zA-Z-]*[0-9]?/)||w("*")},attribute:function(){var a="",b,c,d;if(!w("["))return;if(b=w(/^[a-zA-Z-]+/)||w(this.entities.quoted))(d=w(/^[|~*$^]?=/))&&(c=w(this.entities.quoted)||w(/^[\w-]+/))?a=[b,d,c.toCSS?c.toCSS():c].join(""):a=b;if(!w("]"))return;if(a)return"["+a+"]"},block:function(){var a;if(w("{")&&(a=w(this.primary))&&w("}"))return a},ruleset:function(){var a=[],b,c,d;t();while(b=w(this.selector)){a.push(b),w(this.comment);if(!w(","))break;w(this.comment)}if(a.length>0&&(c=w(this.block)))return new f.Ruleset(a,c);l=h,u()},rule:function(){var a,b,c=g.charAt(h),d,e;t();if(c==="."||c==="#"||c==="&")return;if(a=w(this.variable)||w(this.property)){a.charAt(0)!="@"&&(e=/^([^@+\/'"*`(;{}-]*);/.exec(m[i]))?(h+=e[0].length-1,b=new f.Anonymous(e[1])):a==="font"?b=w(this.font):b=w(this.value),d=w(this.important);if(b&&w(this.end))return new f.Rule(a,b,d,k);l=h,u()}},"import":function(){var a,b,c=h;if(w(/^@import\s+/)&&(a=w(this.entities.quoted)||w(this.entities.url))){b=w(this.mediaFeatures);if(w(";"))return new f.Import(a,s,b,c)}},mediaFeature:function(){var a=[];do if(e=w(this.entities.keyword))a.push(e);else if(w("(")){p=w(this.property),e=w(this.entity);if(!w(")"))return null;if(p&&e)a.push(new f.Paren(new f.Rule(p,e,null,h,!0)));else{if(!e)return null;a.push(new f.Paren(e))}}while(e);if(a.length>0)return new f.Expression(a)},mediaFeatures:function(){var a,b=[];while(a=w(this.mediaFeature)){b.push(a);if(!w(","))break}return b.length>0?b:null},media:function(){var a;if(w(/^@media/)){a=w(this.mediaFeatures);if(rules=w(this.block))return new f.Directive("@media",rules,a)}},directive:function(){var a,b,c,d,e,i;if(g.charAt(h)!=="@")return;if(b=w(this["import"])||w(this.media))return b;if(a=w(/^@page|@keyframes/)||w(/^@(?:-webkit-|-moz-|-o-|-ms-)[a-z0-9-]+/)){d=(w(/^[^{]+/)||"").trim();if(c=w(this.block))return new f.Directive(a+" "+d,c)}else if(a=w(/^@[-a-z]+/))if(a==="@font-face"){if(c=w(this.block))return new f.Directive(a,c)}else if((b=w(this.entity))&&w(";"))return new f.Directive(a,b)},font:function(){var a=[],b=[],c,d,e,g;while(g=w(this.shorthand)||w(this.entity))b.push(g);a.push(new f.Expression(b));if(w(","))while(g=w(this.expression)){a.push(g);if(!w(","))break}return new f.Value(a)},value:function(){var a,b=[],c;while(a=w(this.expression)){b.push(a);if(!w(","))break}if(b.length>0)return new f.Value(b)},important:function(){if(g.charAt(h)==="!")return w(/^! *important/)},sub:function(){var a;if(w("(")&&(a=w(this.expression))&&w(")"))return a},multiplication:function(){var a,b,c,d;if(a=w(this.operand)){while(!z(/^\/\*/)&&(c=w("/")||w("*"))&&(b=w(this.operand)))d=new f.Operation(c,[d||a,b]);return d||a}},addition:function(){var a,b,c,d;if(a=w(this.multiplication)){while((c=w(/^[-+]\s+/)||g.charAt(h-1)!=" "&&(w("+")||w("-")))&&(b=w(this.multiplication)))d=new f.Operation(c,[d||a,b]);return d||a}},conditions:function(){var a,b,c=h,d;if(a=w(this.condition)){while(w(",")&&(b=w(this.condition)))d=new f.Condition("or",d||a,b,c);return d||a}},condition:function(){var a,b,c,d,e=h,g=!1;w(/^not/)&&(g=!0),x("(");if(a=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))return(d=w(/^(?:>=|=<|[<=>])/))?(b=w(this.addition)||w(this.entities.keyword)||w(this.entities.quoted))?c=new f.Condition(d,a,b,e,g):y("expected expression"):c=new f.Condition("=",a,new f.Keyword("true"),e,g),x(")"),w(/^and/)?new f.Condition("and",c,w(this.condition)):c},operand:function(){var a,b=g.charAt(h+1);g.charAt(h)==="-"&&(b==="@"||b==="(")&&(a=w("-"));var c=w(this.sub)||w(this.entities.dimension)||w(this.entities.color)||w(this.entities.variable)||w(this.entities.call);return a?new f.Operation("*",[new f.Dimension(-1),c]):c},expression:function(){var a,b,c=[],d;while(a=w(this.addition)||w(this.entity))c.push(a);if(c.length>0)return new f.Expression(c)},property:function(){var a;if(a=w(/^(\*?-?[-a-z_0-9]+)\s*:/))return a[1]}}}};if(d.mode==="browser"||d.mode==="rhino")d.Parser.importer=function(a,b,c,d){a.charAt(0)!=="/"&&b.length>0&&(a=b[0]+a),o({href:a,title:a,type:d.mime},c,!0)};(function(a){function b(b){return a.functions.hsla(b.h,b.s,b.l,b.a)}function c(b){if(b instanceof a.Dimension)return parseFloat(b.unit=="%"?b.value/100:b.value);if(typeof b=="number")return b;throw{error:"RuntimeError",message:"color functions take numbers as parameters"}}function d(a){return Math.min(1,Math.max(0,a))}a.functions={rgb:function(a,b,c){return this.rgba(a,b,c,1)},rgba:function(b,d,e,f){var g=[b,d,e].map(function(a){return c(a)}),f=c(f);return new a.Color(g,f)},hsl:function(a,b,c){return this.hsla(a,b,c,1)},hsla:function(a,b,d,e){function h(a){return a=a<0?a+1:a>1?a-1:a,a*6<1?g+(f-g)*a*6:a*2<1?f:a*3<2?g+(f-g)*(2/3-a)*6:g}a=c(a)%360/360,b=c(b),d=c(d),e=c(e);var f=d<=.5?d*(b+1):d+b-d*b,g=d*2-f;return this.rgba(h(a+1/3)*255,h(a)*255,h(a-1/3)*255,e)},hue:function(b){return new a.Dimension(Math.round(b.toHSL().h))},saturation:function(b){return new a.Dimension(Math.round(b.toHSL().s*100),"%")},lightness:function(b){return new a.Dimension(Math.round(b.toHSL().l*100),"%")},alpha:function(b){return new a.Dimension(b.toHSL().a)},saturate:function(a,c){var e=a.toHSL();return e.s+=c.value/100,e.s=d(e.s),b(e)},desaturate:function(a,c){var e=a.toHSL();return e.s-=c.value/100,e.s=d(e.s),b(e)},lighten:function(a,c){var e=a.toHSL();return e.l+=c.value/100,e.l=d(e.l),b(e)},darken:function(a,c){var e=a.toHSL();return e.l-=c.value/100,e.l=d(e.l),b(e)},fadein:function(a,c){var e=a.toHSL();return e.a+=c.value/100,e.a=d(e.a),b(e)},fadeout:function(a,c){var e=a.toHSL();return e.a-=c.value/100,e.a=d(e.a),b(e)},fade:function(a,c){var e=a.toHSL();return e.a=c.value/100,e.a=d(e.a),b(e)},spin:function(a,c){var d=a.toHSL(),e=(d.h+c.value)%360;return d.h=e<0?360+e:e,b(d)},mix:function(b,c,d){var e=d.value/100,f=e*2-1,g=b.toHSL().a-c.toHSL().a,h=((f*g==-1?f:(f+g)/(1+f*g))+1)/2,i=1-h,j=[b.rgb[0]*h+c.rgb[0]*i,b.rgb[1]*h+c.rgb[1]*i,b.rgb[2]*h+c.rgb[2]*i],k=b.alpha*e+c.alpha*(1-e);return new a.Color(j,k)},greyscale:function(b){return this.desaturate(b,new a.Dimension(100))},e:function(b){return new a.Anonymous(b instanceof a.JavaScript?b.evaluated:b)},escape:function(b){return new a.Anonymous(encodeURI(b.value).replace(/=/g,"%3D").replace(/:/g,"%3A").replace(/#/g,"%23").replace(/;/g,"%3B").replace(/\(/g,"%28").replace(/\)/g,"%29"))},"%":function(b){var c=Array.prototype.slice.call(arguments,1),d=b.value;for(var e=0;e<c.length;e++)d=d.replace(/%[sda]/i,function(a){var b=a.match(/s/i)?c[e].value:c[e].toCSS();return a.match(/[A-Z]$/)?encodeURIComponent(b):b});return d=d.replace(/%%/g,"%"),new a.Quoted('"'+d+'"',d)},round:function(a){return this._math("round",a)},ceil:function(a){return this._math("ceil",a)},floor:function(a){return this._math("floor",a)},_math:function(b,d){if(d instanceof a.Dimension)return new a.Dimension(Math[b](c(d)),d.unit);if(typeof d=="number")return Math[b](d);throw{type:"Argument",message:"argument must be a number"}},argb:function(b){return new a.Anonymous(b.toARGB())},percentage:function(b){return new a.Dimension(b.value*100,"%")},color:function(b){if(b instanceof a.Quoted)return new a.Color(b.value.slice(1));throw{type:"Argument",message:"argument must be a string"}},iscolor:function(b){return this._isa(b,a.Color)},isnumber:function(b){return this._isa(b,a.Dimension)},isstring:function(b){return this._isa(b,a.Quoted)},iskeyword:function(b){return this._isa(b,a.Keyword)},isurl:function(b){return this._isa(b,a.URL)},ispixel:function(b){return b instanceof a.Dimension&&b.unit==="px"?a.True:a.False},ispercentage:function(b){return b instanceof a.Dimension&&b.unit==="%"?a.True:a.False},isem:function(b){return b instanceof a.Dimension&&b.unit==="em"?a.True:a.False},_isa:function(b,c){return b instanceof c?a.True:a.False}}})(c("./tree")),function(a){a.colors={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"}}(c("./tree")),function(a){a.Alpha=function(a){this.value=a},a.Alpha.prototype={toCSS:function(){return"alpha(opacity="+(this.value.toCSS?this.value.toCSS():this.value)+")"},eval:function(a){return this.value.eval&&(this.value=this.value.eval(a)),this}}}(c("../tree")),function(a){a.Anonymous=function(a){this.value=a.value||a},a.Anonymous.prototype={toCSS:function(){return this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Assignment=function(a,b){this.key=a,this.value=b},a.Assignment.prototype={toCSS:function(){return this.key+"="+(this.value.toCSS?this.value.toCSS():this.value)},eval:function(a){return this.value.eval&&(this.value=this.value.eval(a)),this}}}(c("../tree")),function(a){a.Call=function(a,b,c,d){this.name=a,this.args=b,this.index=c,this.filename=d},a.Call.prototype={eval:function(b){var c=this.args.map(function(a){return a.eval(b)});if(!(this.name in a.functions))return new a.Anonymous(this.name+"("+c.map(function(a){return a.toCSS()}).join(", ")+")");try{return a.functions[this.name].apply(a.functions,c)}catch(d){throw{type:d.type||"Runtime",message:"error evaluating function `"+this.name+"`"+(d.message?": "+d.message:""),index:this.index,filename:this.filename}}},toCSS:function(a){return this.eval(a).toCSS()}}}(c("../tree")),function(a){a.Color=function(a,b){Array.isArray(a)?this.rgb=a:a.length==6?this.rgb=a.match(/.{2}/g).map(function(a){return parseInt(a,16)}):this.rgb=a.split("").map(function(a){return parseInt(a+a,16)}),this.alpha=typeof b=="number"?b:1},a.Color.prototype={eval:function(){return this},toCSS:function(){return this.alpha<1?"rgba("+this.rgb.map(function(a){return Math.round(a)}).concat(this.alpha).join(", ")+")":"#"+this.rgb.map(function(a){return a=Math.round(a),a=(a>255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")},operate:function(b,c){var d=[];c instanceof a.Color||(c=c.toColor());for(var e=0;e<3;e++)d[e]=a.operate(b,this.rgb[e],c.rgb[e]);return new a.Color(d,this.alpha+c.alpha)},toHSL:function(){var a=this.rgb[0]/255,b=this.rgb[1]/255,c=this.rgb[2]/255,d=this.alpha,e=Math.max(a,b,c),f=Math.min(a,b,c),g,h,i=(e+f)/2,j=e-f;if(e===f)g=h=0;else{h=i>.5?j/(2-e-f):j/(e+f);switch(e){case a:g=(b-c)/j+(b<c?6:0);break;case b:g=(c-a)/j+2;break;case c:g=(a-b)/j+4}g/=6}return{h:g*360,s:h,l:i,a:d}},toARGB:function(){var a=[Math.round(this.alpha*255)].concat(this.rgb);return"#"+a.map(function(a){return a=Math.round(a),a=(a>255?255:a<0?0:a).toString(16),a.length===1?"0"+a:a}).join("")}}}(c("../tree")),function(a){a.Comment=function(a,b){this.value=a,this.silent=!!b},a.Comment.prototype={toCSS:function(a){return a.compress?"":this.value},eval:function(){return this}}}(c("../tree")),function(a){a.Condition=function(a,b,c,d,e){this.op=a.trim(),this.lvalue=b,this.rvalue=c,this.index=d,this.negate=e},a.Condition.prototype.eval=function(a){var b=this.lvalue.eval(a),c=this.rvalue.eval(a),d=this.index,e,e=function(a){switch(a){case"and":return b&&c;case"or":return b||c;default:if(b.compare)e=b.compare(c);else{if(!c.compare)throw{type:"Type",message:"Unable to perform comparison",index:d};e=c.compare(b)}switch(e){case-1:return a==="<"||a==="=<";case 0:return a==="="||a===">="||a==="=<";case 1:return a===">"||a===">="}}}(this.op);return this.negate?!e:e}}(c("../tree")),function(a){a.Dimension=function(a,b){this.value=parseFloat(a),this.unit=b||null},a.Dimension.prototype={eval:function(){return this},toColor:function(){return new a.Color([this.value,this.value,this.value])},toCSS:function(){var a=this.value+this.unit;return a},operate:function(b,c){return new a.Dimension(a.operate(b,this.value,c.value),this.unit||c.unit)},compare:function(b){return b instanceof a.Dimension?b.value>this.value?-1:b.value<this.value?1:0:-1}}}(c("../tree")),function(a){a.Directive=function(b,c,d){this.name=b,this.features=d&&new a.Value(d),Array.isArray(c)?(this.ruleset=new a.Ruleset([],c),this.ruleset.allowImports=!0):this.value=c},a.Directive.prototype={toCSS:function(a,b){var c=this.features?" "+this.features.toCSS(b):"";return this.ruleset?(this.ruleset.root=!0,this.name+c+(b.compress?"{":" {\n ")+this.ruleset.toCSS(a,b).trim().replace(/\n/g,"\n ")+(b.compress?"}":"\n}\n")):this.name+" "+this.value.toCSS()+";\n"},eval:function(a){return this.features=this.features&&this.features.eval(a),a.frames.unshift(this),this.ruleset=this.ruleset&&this.ruleset.eval(a),a.frames.shift(),this},variable:function(b){return a.Ruleset.prototype.variable.call(this.ruleset,b)},find:function(){return a.Ruleset.prototype.find.apply(this.ruleset,arguments)},rulesets:function(){return a.Ruleset.prototype.rulesets.apply(this.ruleset)}}}(c("../tree")),function(a){a.Element=function(b,c,d){this.combinator=b instanceof a.Combinator?b:new a.Combinator(b),typeof c=="string"?this.value=c.trim():c?this.value=c:this.value="",this.index=d},a.Element.prototype.eval=function(b){return new a.Element(this.combinator,this.value.eval?this.value.eval(b):this.value,this.index)},a.Element.prototype.toCSS=function(a){return this.combinator.toCSS(a||{})+(this.value.toCSS?this.value.toCSS(a):this.value)},a.Combinator=function(a){a===" "?this.value=" ":a==="& "?this.value="& ":this.value=a?a.trim():""},a.Combinator.prototype.toCSS=function(a){return{"":""," ":" ","&":"","& ":" ",":":" :","::":"::","+":a.compress?"+":" + ","~":a.compress?"~":" ~ ",">":a.compress?">":" > "}[this.value]}}(c("../tree")),function(a){a.Expression=function(a){this.value=a},a.Expression.prototype={eval:function(b){return this.value.length>1?new a.Expression(this.value.map(function(a){return a.eval(b)})):this.value.length===1?this.value[0].eval(b):this},toCSS:function(a){return this.value.map(function(b){return b.toCSS?b.toCSS(a):""}).join(" ")}}}(c("../tree")),function(a){a.Import=function(b,c,d,e){var f=this;this.index=e,this._path=b,this.features=d&&new a.Value(d),b instanceof a.Quoted?this.path=/\.(le?|c)ss(\?.*)?$/.test(b.value)?b.value:b.value+".less":this.path=b.value.value||b.value,this.css=/css(\?.*)?$/.test(this.path),this.css||c.push(this.path,function(b,c){b&&(b.index=e),f.root=c||new a.Ruleset([],[])})},a.Import.prototype={toCSS:function(a){var b=this.features?" "+this.features.toCSS(a):"";return this.css?"@import "+this._path.toCSS()+b+";\n":""},eval:function(b){var c,d=this.features&&this.features.eval(b);if(this.css)return this;c=new a.Ruleset([],this.root.rules.slice(0));for(var e=0;e<c.rules.length;e++)c.rules[e]instanceof a.Import&&Array.prototype.splice.apply(c.rules,[e,1].concat(c.rules[e].eval(b)));return this.features?new a.Directive("@media",c.rules,this.features.value):c.rules}}}(c("../tree")),function(a){a.JavaScript=function(a,b,c){this.escaped=c,this.expression=a,this.index=b},a.JavaScript.prototype={eval:function(b){var c,d=this,e={},f=this.expression.replace(/@\{([\w-]+)\}/g,function(c,e){return a.jsify((new a.Variable("@"+e,d.index)).eval(b))});try{f=new Function("return ("+f+")")}catch(g){throw{message:"JavaScript evaluation error: `"+ +f+"`",index:this.index}}for(var h in b.frames[0].variables())e[h.slice(1)]={value:b.frames[0].variables()[h].value,toJS:function(){return this.value.eval(b).toCSS()}};try{c=f.call(e)}catch(g){throw{message:"JavaScript evaluation error: '"+g.name+": "+g.message+"'",index:this.index}}return typeof c=="string"?new a.Quoted('"'+c+'"',c,this.escaped,this.index):Array.isArray(c)?new a.Anonymous(c.join(", ")):new a.Anonymous(c)}}}(c("../tree")),function(a){a.Keyword=function(a){this.value=a},a.Keyword.prototype={eval:function(){return this},toCSS:function(){return this.value},compare:function(b){return b instanceof a.Keyword?b.value===this.value?0:1:-1}},a.True=new a.Keyword("true"),a.False=new a.Keyword("false")}(c("../tree")),function(a){a.mixin={},a.mixin.Call=function(b,c,d,e,f){this.selector=new a.Selector(b),this.arguments=c,this.index=d,this.filename=e,this.important=f},a.mixin.Call.prototype={eval:function(a){var b,c,d=[],e=!1;for(var f=0;f<a.frames.length;f++)if((b=a.frames[f].find(this.selector)).length>0){c=this.arguments&&this.arguments.map(function(b){return b.eval(a)});for(var g=0;g<b.length;g++)if(b[g].match(c,a))try{Array.prototype.push.apply(d,b[g].eval(a,this.arguments,this.important).rules),e=!0}catch(h){throw{message:h.message,index:h.index,filename:this.filename,stack:h.stack,call:this.index}}if(e)return d;throw{type:"Runtime",message:"No matching definition was found for `"+this.selector.toCSS().trim()+"("+this.arguments.map(function(a){return a.toCSS()}).join(", ")+")`",index:this.index,filename:this.filename}}throw{type:"Name",message:this.selector.toCSS().trim()+" is undefined",index:this.index,filename:this.filename}}},a.mixin.Definition=function(b,c,d,e){this.name=b,this.selectors=[new a.Selector([new a.Element(null,b)])],this.params=c,this.condition=e,this.arity=c.length,this.rules=d,this._lookups={},this.required=c.reduce(function(a,b){return!b.name||b.name&&!b.value?a+1:a},0),this.parent=a.Ruleset.prototype,this.frames=[]},a.mixin.Definition.prototype={toCSS:function(){return""},variable:function(a){return this.parent.variable.call(this,a)},variables:function(){return this.parent.variables.call(this)},find:function(){return this.parent.find.apply(this,arguments)},rulesets:function(){return this.parent.rulesets.apply(this)},evalParams:function(b,c){var d=new a.Ruleset(null,[]);for(var e=0,f;e<this.params.length;e++)if(this.params[e].name){if(!(f=c&&c[e]||this.params[e].value))throw{type:"Runtime",message:"wrong number of arguments for "+this.name+" ("+c.length+" for "+this.arity+")"};d.rules.unshift(new a.Rule(this.params[e].name,f.eval(b)))}return d},eval:function(b,c,d){var e=this.evalParams(b,c),f,g=[],h;for(var i=0;i<Math.max(this.params.length,c&&c.length);i++)g.push(c[i]||this.params[i].value);return e.rules.unshift(new a.Rule("@arguments",(new a.Expression(g)).eval(b))),h=d?this.rules.map(function(b){return new a.Rule(b.name,b.value,"!important",b.index)}):this.rules.slice(0),(new a.Ruleset(null,h)).eval({frames:[this,e].concat(this.frames,b.frames)})},match:function(a,b){var c=a&&a.length||0,d,e;if(c<this.required)return!1;if(this.required>0&&c>this.params.length)return!1;if(this.condition&&!this.condition.eval({frames:[this.evalParams(b,a)].concat(b.frames)}))return!1;d=Math.min(c,this.arity);for(var f=0;f<d;f++)if(!this.params[f].name&&a[f].eval(b).toCSS()!=this.params[f].value.eval(b).toCSS())return!1;return!0}}}(c("../tree")),function(a){a.Operation=function(a,b){this.op=a.trim(),this.operands=b},a.Operation.prototype.eval=function(b){var c=this.operands[0].eval(b),d=this.operands[1].eval(b),e;if(c instanceof a.Dimension&&d instanceof a.Color){if(this.op!=="*"&&this.op!=="+")throw{name:"OperationError",message:"Can't substract or divide a color from a number"};e=d,d=c,c=e}return c.operate(this.op,d)},a.operate=function(a,b,c){switch(a){case"+":return b+c;case"-":return b-c;case"*":return b*c;case"/":return b/c}}}(c("../tree")),function(a){a.Paren=function(a){this.value=a},a.Paren.prototype={toCSS:function(a){return"("+this.value.toCSS(a)+")"},eval:function(b){return new a.Paren(this.value.eval(b))}}}(c("../tree")),function(a){a.Quoted=function(a,b,c,d){this.escaped=c,this.value=b||"",this.quote=a.charAt(0),this.index=d},a.Quoted.prototype={toCSS:function(){return this.escaped?this.value:this.quote+this.value+this.quote},eval:function(b){var c=this,d=this.value.replace(/`([^`]+)`/g,function(d,e){return(new a.JavaScript(e,c.index,!0)).eval(b).value}).replace(/@\{([\w-]+)\}/g,function(d,e){var f=(new a.Variable("@"+e,c.index)).eval(b);return"value"in f?f.value:f.toCSS()});return new a.Quoted(this.quote+d+this.quote,d,this.escaped,this.index)}}}(c("../tree")),function(a){a.Rule=function(b,c,d,e,f){this.name=b,this.value=c instanceof a.Value?c:new a.Value([c]),this.important=d?" "+d.trim():"",this.index=e,this.inline=f||!1,b.charAt(0)==="@"?this.variable=!0:this.variable=!1},a.Rule.prototype.toCSS=function(a){return this.variable?"":this.name+(a.compress?":":": ")+this.value.toCSS(a)+this.important+(this.inline?"":";")},a.Rule.prototype.eval=function(b){return new a.Rule(this.name,this.value.eval(b),this.important,this.index,this.inline)},a.Shorthand=function(a,b){this.a=a,this.b=b},a.Shorthand.prototype={toCSS:function(a){return this.a.toCSS(a)+"/"+this.b.toCSS(a)},eval:function(){return this}}}(c("../tree")),function(a){a.Ruleset=function(a,b){this.selectors=a,this.rules=b,this._lookups={}},a.Ruleset.prototype={eval:function(b){var c=this.selectors&&this.selectors.map(function(a){return a.eval(b)}),d=new a.Ruleset(c,this.rules.slice(0));d.root=this.root,d.allowImports=this.allowImports,b.frames.unshift(d);if(d.root||d.allowImports)for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.Import&&Array.prototype.splice.apply(d.rules,[e,1].concat(d.rules[e].eval(b)));for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.mixin.Definition&&(d.rules[e].frames=b.frames.slice(0));for(var e=0;e<d.rules.length;e++)d.rules[e]instanceof a.mixin.Call&&Array.prototype.splice.apply(d.rules,[e,1].concat(d.rules[e].eval(b)));for(var e=0,f;e<d.rules.length;e++)f=d.rules[e],f instanceof a.mixin.Definition||(d.rules[e]=f.eval?f.eval(b):f);return b.frames.shift(),d},match:function(a){return!a||a.length===0},variables:function(){return this._variables?this._variables:this._variables=this.rules.reduce(function(b,c){return c instanceof a.Rule&&c.variable===!0&&(b[c.name]=c),b},{})},variable:function(a){return this.variables()[a]},rulesets:function(){return this._rulesets?this._rulesets:this._rulesets=this.rules.filter(function(b){return b instanceof a.Ruleset||b instanceof a.mixin.Definition})},find:function(b,c){c=c||this;var d=[],e,f,g=b.toCSS();return g in this._lookups?this._lookups[g]:(this.rulesets().forEach(function(e){if(e!==c)for(var g=0;g<e.selectors.length;g++)if(f=b.match(e.selectors[g])){b.elements.length>e.selectors[g].elements.length?Array.prototype.push.apply(d,e.find(new a.Selector(b.elements.slice(1)),c)):d.push(e);break}}),this._lookups[g]=d)},toCSS:function(b,c){var d=[],e=[],f=[],g=[],h,i;this.root||(b.length===0?g=this.selectors.map(function(a){return[a]}):this.joinSelectors(g,b,this.selectors));for(var j=0;j<this.rules.length;j++)i=this.rules[j],i.rules||i instanceof a.Directive?f.push(i.toCSS(g,c)):i instanceof a.Comment?i.silent||(this.root?f.push(i.toCSS(c)):e.push(i.toCSS(c))):i.toCSS&&!i.variable?e.push(i.toCSS(c)):i.value&&!i.variable&&e.push(i.value.toString());return f=f.join(""),this.root?d.push(e.join(c.compress?"":"\n")):e.length>0&&(h=g.map(function(a){return a.map(function(a){return a.toCSS(c)}).join("").trim()}).join(c.compress?",":g.length>3?",\n":", "),d.push(h,(c.compress?"{":" {\n ")+e.join(c.compress?"":"\n ")+(c.compress?"}":"\n}\n"))),d.push(f),d.join("")+(c.compress?"\n":"")},joinSelectors:function(a,b,c){for(var d=0;d<c.length;d++)this.joinSelector(a,b,c[d])},joinSelector:function(b,c,d){var e=[],f=[],g=[],h=[],i=!1,j;for(var k=0;k<d.elements.length;k++)j=d.elements[k],j.combinator.value.charAt(0)==="&"&&(i=!0),i?h.push(j):g.push(j);i||(h=g,g=[]),g.length>0&&e.push(new a.Selector(g)),h.length>0&&f.push(new a.Selector(h));for(var l=0;l<c.length;l++)b.push(e.concat(c[l]).concat(f))}}}(c("../tree")),function(a){a.Selector=function(a){this.elements=a,this.elements[0].combinator.value===""&&(this.elements[0].combinator.value=" ")},a.Selector.prototype.match=function(a){var b=this.elements.length,c=a.elements.length,d=Math.min(b,c);if(b<c)return!1;for(var e=0;e<d;e++)if(this.elements[e].value!==a.elements[e].value)return!1;return!0},a.Selector.prototype.eval=function(b){return new a.Selector(this.elements.map(function(a){return a.eval(b)}))},a.Selector.prototype.toCSS=function(a){return this._css?this._css:this._css=this.elements.map(function(b){return typeof b=="string"?" "+b.trim():b.toCSS(a)}).join("")}}(c("../tree")),function(b){b.URL=function(b,c){b.data?this.attrs=b:(typeof a!="undefined"&&!/^(?:https?:\/\/|file:\/\/|data:|\/)/.test(b.value)&&c.length>0&&(b.value=c[0]+(b.value.charAt(0)==="/"?b.value.slice(1):b.value)),this.value=b,this.paths=c)},b.URL.prototype={toCSS:function(){return"url("+(this.attrs?"data:"+this.attrs.mime+this.attrs.charset+this.attrs.base64+this.attrs.data:this.value.toCSS())+")"},eval:function(a){return this.attrs?this:new b.URL(this.value.eval(a),this.paths)}}}(c("../tree")),function(a){a.Value=function(a){this.value=a,this.is="value"},a.Value.prototype={eval:function(b){return this.value.length===1?this.value[0].eval(b):new a.Value(this.value.map(function(a){return a.eval(b)}))},toCSS:function(a){return this.value.map(function(b){return b.toCSS(a)}).join(a.compress?",":", ")}}}(c("../tree")),function(a){a.Variable=function(a,b,c){this.name=a,this.index=b,this.file=c},a.Variable.prototype={eval:function(b){var c,d,e=this.name;e.indexOf("@@")==0&&(e="@"+(new a.Variable(e.slice(1))).eval(b).value);if(c=a.find(b.frames,function(a){if(d=a.variable(e))return d.value.eval(b)}))return c;throw{type:"Name",message:"variable "+e+" is undefined",filename:this.file,index:this.index}}}}(c("../tree")),function(a){a.find=function(a,b){for(var c=0,d;c<a.length;c++)if(d=b.call(a,a[c]))return d;return null},a.jsify=function(a){return Array.isArray(a.value)&&a.value.length>1?"["+a.value.map(function(a){return a.toCSS(!1)}).join(", ")+"]":a.toCSS(!1)}}(c("./tree"));var g=location.protocol==="file:"||location.protocol==="chrome:"||location.protocol==="chrome-extension:"||location.protocol==="resource:";d.env=d.env||(location.hostname=="127.0.0.1"||location.hostname=="0.0.0.0"||location.hostname=="localhost"||location.port.length>0||g?"development":"production"),d.async=!1,d.poll=d.poll||(g?1e3:1500),d.watch=function(){return this.watchMode=!0},d.unwatch=function(){return this.watchMode=!1},d.env==="development"?(d.optimization=0,/!watch/.test(location.hash)&&d.watch(),d.watchTimer=setInterval(function(){d.watchMode&&n(function(a,b,c,d,e){b&&r(b.toCSS(),d,e.lastModified)})},d.poll)):d.optimization=3;var h;try{h=typeof a.localStorage=="undefined"?null:a.localStorage}catch(i){h=null}var j=document.getElementsByTagName("link"),k=/^text\/(x-)?less$/;d.sheets=[];for(var l=0;l<j.length;l++)(j[l].rel==="stylesheet/less"||j[l].rel.match(/stylesheet/)&&j[l].type.match(k))&&d.sheets.push(j[l]);d.refresh=function(a){var b,c;b=c=new Date,n(function(a,d,e,f,g){g.local?v("loading "+f.href+" from cache."):(v("parsed "+f.href+" successfully."),r(d.toCSS(),f,g.lastModified)),v("css for "+f.href+" generated in "+(new Date-c)+"ms"),g.remaining===0&&v("css generated in "+(new Date-b)+"ms"),c=new Date},a),m()},d.refreshStyles=m,d.refresh(d.env==="development")})(window); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/_dmedia/style.less Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,88 @@ +html { + overflow-y: scroll; +} +.wrap { + width: 500px; + margin: 40px auto 180px; +} +h1, h2, h3 { + a { + color: #404040; + + &:hover { + color: #0069d6; + text-decoration: none; + } + } +} +h2 { + font-size: 18px; +} +h3 { + font-size: 16px; +} +h4 { + font-size: 14px; +} +h5 { + font-size: 12px; +} +pre { + line-height: 16px; + white-space: pre; + overflow-x: auto; +} +ol, ul { + margin-bottom: 9px; + + li { + color: #404040; + } +} +footer { + text-align: center; +} +body.index { + header { + text-align: center; + + h1 { + font-size: 56px/1; + margin-bottom: 40px; + } + } + ol.toc { + list-style-type: none; + margin-left: 0; + + li { + font-size: 16px; + line-height: 1.25; + + } + } +} +body.content { + header { + float: right; + position: relative; + min-width: 150px; + + h1 { + text-align: right; + font-size: 24px; + line-height: 1; + margin-top: 10px; + } + } + .toc { + ul { + list-style-type: none; + margin-left: 0px; + + ul { + margin-left: 20px; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/_dmedia/tango.css Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,71 @@ +.codehilite .hll { background-color: #ffffcc } +.codehilite .c { color: #8f5902; font-style: italic } /* Comment */ +.codehilite .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.codehilite .g { color: #000000 } /* Generic */ +.codehilite .k { color: #204a87; font-weight: bold } /* Keyword */ +.codehilite .l { color: #000000 } /* Literal */ +.codehilite .n { color: #000000 } /* Name */ +.codehilite .o { color: #ce5c00; font-weight: bold } /* Operator */ +.codehilite .x { color: #000000 } /* Other */ +.codehilite .p { color: #000000; font-weight: bold } /* Punctuation */ +.codehilite .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ +.codehilite .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #a40000 } /* Generic.Deleted */ +.codehilite .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.codehilite .gr { color: #ef2929 } /* Generic.Error */ +.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.codehilite .gi { color: #00A000 } /* Generic.Inserted */ +.codehilite .go { color: #000000; font-style: italic } /* Generic.Output */ +.codehilite .gp { color: #8f5902 } /* Generic.Prompt */ +.codehilite .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.codehilite .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.codehilite .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ +.codehilite .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ +.codehilite .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ +.codehilite .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ +.codehilite .ld { color: #000000 } /* Literal.Date */ +.codehilite .m { color: #0000cf; font-weight: bold } /* Literal.Number */ +.codehilite .s { color: #4e9a06 } /* Literal.String */ +.codehilite .na { color: #c4a000 } /* Name.Attribute */ +.codehilite .nb { color: #204a87 } /* Name.Builtin */ +.codehilite .nc { color: #000000 } /* Name.Class */ +.codehilite .no { color: #000000 } /* Name.Constant */ +.codehilite .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ +.codehilite .ni { color: #ce5c00 } /* Name.Entity */ +.codehilite .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #000000 } /* Name.Function */ +.codehilite .nl { color: #f57900 } /* Name.Label */ +.codehilite .nn { color: #000000 } /* Name.Namespace */ +.codehilite .nx { color: #000000 } /* Name.Other */ +.codehilite .py { color: #000000 } /* Name.Property */ +.codehilite .nt { color: #204a87; font-weight: bold } /* Name.Tag */ +.codehilite .nv { color: #000000 } /* Name.Variable */ +.codehilite .ow { color: #204a87; font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ +.codehilite .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ +.codehilite .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ +.codehilite .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ +.codehilite .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ +.codehilite .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.codehilite .sc { color: #4e9a06 } /* Literal.String.Char */ +.codehilite .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.codehilite .s2 { color: #4e9a06 } /* Literal.String.Double */ +.codehilite .se { color: #4e9a06 } /* Literal.String.Escape */ +.codehilite .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.codehilite .si { color: #4e9a06 } /* Literal.String.Interpol */ +.codehilite .sx { color: #4e9a06 } /* Literal.String.Other */ +.codehilite .sr { color: #4e9a06 } /* Literal.String.Regex */ +.codehilite .s1 { color: #4e9a06 } /* Literal.String.Single */ +.codehilite .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.codehilite .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.codehilite .vc { color: #000000 } /* Name.Variable.Class */ +.codehilite .vg { color: #000000 } /* Name.Variable.Global */ +.codehilite .vi { color: #000000 } /* Name.Variable.Instance */ +.codehilite .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/basics/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,343 @@ +<html><head><title>Basics / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="basics"><a href="">Basics</a></h1><p>Red Tape is a fairly simple library. It's designed to take raw form data +(strings), validate it, and turn it into useful data structures.</p> +<p>Red Tape does <em>not</em> handle rendering form fields into HTML. That's the job of +your templating library, and you always need to customize <code><input></code> tags anyway.</p> +<p>It's designed with Ring, Compojure, and friends in mind (though it's not limited +to them) so let's take a look at a really simple application to see it in +action.</p> +<div class="toc"> +<ul><li><a href="#scaffolding">Scaffolding</a></li> +<li><a href="#defining-the-form">Defining the Form</a></li> +<li><a href="#using-the-form">Using the Form</a></li> +<li><a href="#cleaners">Cleaners</a></li> +<li><a href="#validation">Validation</a></li> +<li><a href="#built-in-cleaners">Built-In Cleaners</a></li> +<li><a href="#putting-it-all-together">Putting it All Together</a></li> +<li><a href="#summary">Summary</a></li> +</ul></div> +<h2 id="scaffolding">Scaffolding</h2> +<p>For this tutorial we'll create a Compojure app that allows people to submit +comments. Let's sketch out the normal structure of that now:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">feedback</span> + <span class="p">(</span><span class="nf">require</span> <span class="p">[</span><span class="nv">compojure.core</span> <span class="ss">:refer</span> <span class="ss">:all</span><span class="p">]</span> + <span class="p">[</span><span class="nv">compojure.route</span> <span class="ss">:as</span> <span class="nv">route</span><span class="p">]</span> + <span class="p">[</span><span class="nv">hiccup.page</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">html5</span><span class="p">]]</span> + <span class="p">[</span><span class="nv">ring.adapter.jetty</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">run-jetty</span><span class="p">]]</span> + <span class="p">[</span><span class="nv">ring.middleware.params</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">wrap-params</span><span class="p">]]))</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">page</span> <span class="p">[]</span> + <span class="p">(</span><span class="nf">html5</span> + <span class="p">[</span><span class="ss">:body</span> + <span class="p">[</span><span class="ss">:form</span> <span class="p">{</span><span class="ss">:method</span> <span class="s">"POST"</span> <span class="ss">:action</span> <span class="s">"/"</span><span class="p">}</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"Who are you?"</span><span class="p">]</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">"text"</span> <span class="ss">:name</span> <span class="s">"name"</span><span class="p">}]</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"What do you want to say?"</span><span class="p">]</span> + <span class="p">[</span><span class="ss">:textarea</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"comment"</span><span class="p">}]]]))</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">save-feedback</span> <span class="p">[</span><span class="nv">from</span> <span class="nv">comment</span><span class="p">]</span> + <span class="c1">; In a real app this would save the string to a database, email</span> + <span class="c1">; it to someone, etc.</span> + <span class="p">(</span><span class="nb">println </span><span class="nv">from</span> <span class="s">"said:"</span> <span class="nv">comment</span><span class="p">))</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">handle-get</span> <span class="p">[]</span> + <span class="c1">; ...</span> +<span class="p">)</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">handle-post</span> <span class="p">[]</span> + <span class="c1">; ...</span> +<span class="p">)</span> + +<span class="p">(</span><span class="nf">defroutes</span> <span class="nv">app</span> + <span class="p">(</span><span class="nf">GET</span> <span class="s">"/"</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span><span class="p">))</span> + <span class="p">(</span><span class="nf">POST</span> <span class="s">"/"</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">handle-post</span> <span class="nv">request</span><span class="p">)))</span> + +<span class="p">(</span><span class="k">def </span><span class="nv">handler</span> <span class="p">(</span><span class="nb">-> </span><span class="nv">app</span> + <span class="nv">wrap-params</span><span class="p">))</span> + +<span class="p">(</span><span class="kd">defonce </span><span class="nv">server</span> + <span class="p">(</span><span class="nf">run-jetty</span> <span class="o">#</span><span class="ss">'handler</span> <span class="p">{</span><span class="ss">:port</span> <span class="mi">3000</span><span class="p">}))</span> +</pre></div> + + +<p>That's about it for the boilerplate. The next step is to fill in the bodies of +<code>handle-get</code> and <code>handle-post</code>. We'll need to do a few things:</p> +<ul><li>Render the initial comment form when the user first GETs the page.</li> +<li>Validate incoming POST data to make sure it's sane.</li> +<li>If the data isn't valid, inform the user and re-render the form nicely so they + can fix it.</li> +<li>Once we've got valid data, clean it up and send it off to be saved.</li> +</ul><p>This is where Red Tape comes in.</p> +<h2 id="defining-the-form">Defining the Form</h2> +<p>The main part of Red Tape is the <code>defform</code> macro. Let's define a simple +feedback form:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">feedback</span> + <span class="c1">; ...</span> + <span class="p">(</span><span class="nf">require</span> <span class="p">[</span><span class="nv">red-tape.core</span> <span class="ss">:refer</span> <span class="p">[</span><span class="nv">defform</span><span class="p">]]))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">feedback-form</span> <span class="p">{}</span> + <span class="ss">:name</span> <span class="p">[]</span> + <span class="ss">:comment</span> <span class="p">[])</span> +</pre></div> + + +<p><code>defform</code> takes a name, a map of form options, and a sequence of keywords and +vectors representing fields. We'll look at each of those parts in more detail +later, but for now let's actually <em>use</em> the form we've defined.</p> +<h2 id="using-the-form">Using the Form</h2> +<p>Defining a form results in a simple function that can be called with or without +data. Let's sketch out how our handler functions will look:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-get</span> + <span class="p">([</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">feedback-form</span><span class="p">)))</span> + <span class="p">([</span><span class="nv">request</span> <span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">page</span><span class="p">)))</span> +</pre></div> + + +<p>There are a couple of things going on here.</p> +<p>We've split the definition of <code>handle-get</code> into two pieces. The first piece +takes a request, builds the default feedback form and forwards those along to +the second piece, which actually renders the page. You'll see why we split it +up like that shortly.</p> +<p>Calling <code>(feedback-form)</code> without data returns a "result map" representing a fresh +form. It will look like this:</p> +<div class="codehilite"><pre><span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span> + <span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:arguments</span> <span class="p">{}</span> + <span class="ss">:data</span> <span class="p">{}</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="nv">nil</span><span class="p">}</span> +</pre></div> + + +<p>We'll see how to use this later. Let's move on to <code>handle-post</code>:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-post</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">data</span> <span class="p">(</span><span class="ss">:params</span> <span class="nv">request</span><span class="p">)</span> + <span class="nv">form</span> <span class="p">(</span><span class="nf">feedback-form</span> <span class="nv">data</span><span class="p">)]</span> + <span class="c1">; ...))</span> +</pre></div> + + +<p><code>handle-post</code> takes the raw HTTP POST data (from <code>(:params request)</code>) and passes +it through the feedback form. Once again this results in a map. Assuming the +user entered the name "Steve" and the comment "Hello!", the resulting map will +look like this:</p> +<div class="codehilite"><pre><span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:arguments</span> <span class="p">{}</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"Steve"</span> + <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"Steve"</span> + <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="ss">:errors</span> <span class="nv">nil</span><span class="p">}</span> +</pre></div> + + +<p>In a nutshell, this is all Red Tape does. You define form functions using +<code>defform</code>, and those functions take in data and turn it into a result map like +this.</p> +<p>Let's add a bit of data cleaning to the form to get something more useful.</p> +<h2 id="cleaners">Cleaners</h2> +<p>Every field you define in a <code>defform</code> also gets a vector of "cleaners" +associated with it. A cleaner is a vanilla Clojure function that takes one +argument (the incoming value) and returns a new value (the outgoing result).</p> +<p>Let's see this in action by modifying our form to strip leading and trailing +whitespace from the user's name automatically:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">feedback-form</span> <span class="p">{}</span> + <span class="ss">:name</span> <span class="p">[</span><span class="nv">clojure.string/trim</span><span class="p">]</span> + <span class="ss">:comment</span> <span class="p">[])</span> +</pre></div> + + +<p><code>clojure.string/trim</code> is just a normal Clojure function that trims off +whitespace. Let's imagine that the user entered <code>Steve</code> as their name +this time. Calling <code>(feedback-form data)</code> now results in the following map:</p> +<div class="codehilite"><pre><span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:arguments</span> <span class="p">{}</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">" Steve "</span> <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"Steve"</span> <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="ss">:errors</span> <span class="nv">nil</span><span class="p">}</span> +</pre></div> + + +<p>The <code>:data</code> in the result map still contains the raw data the user entered, but +the <code>:results</code> have had their values passed through their cleaners first.</p> +<p>You can define as many cleaners as you want for each field. The data will be +threaded through them in order, much like the <code>-></code> macro. This lets you define +simple cleaning functions and combine them as needed. For example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">feedback-form</span> <span class="p">{}</span> + <span class="ss">:name</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> + <span class="nv">clojure.string/lower-case</span><span class="p">]</span> + <span class="ss">:comment</span> <span class="p">[</span><span class="nv">clojure.string/trim</span><span class="p">])</span> + +<span class="p">(</span><span class="nf">feedback-form</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">" Steve "</span> <span class="ss">:comment</span> <span class="s">" Hello! "</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">" Steve "</span> <span class="ss">:comment</span> <span class="s">" Hello! "</span><span class="p">}</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"steve"</span> <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="c1">; ...</span> + <span class="p">}</span> +</pre></div> + + +<p>Here we're trimming the name and then lowercasing it, and trimming the comment +as well (but not lowercasing that).</p> +<h2 id="validation">Validation</h2> +<p>Cleaners also serve another purpose. If a cleaner function throws an Exception, +the value won't progress any further, and the result map will be marked as +invalid.</p> +<p>Let's look at an example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">age-form</span> <span class="p">{}</span> + <span class="ss">:age</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> + <span class="o">#</span><span class="p">(</span><span class="nf">Long.</span> <span class="nv">%</span><span class="p">)])</span> +</pre></div> + + +<p>If we call this form with a number, everything is fine:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">age-form</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"27"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"27"</span><span class="p">}</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:age</span> <span class="mi">27</span><span class="p">}</span> + <span class="ss">:errors</span> <span class="nv">nil</span><span class="p">}</span> +</pre></div> + + +<p>But if we try to feed it garbage:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">age-form</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"cats"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"cats"</span><span class="p">}</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:age</span> <span class="nv"><NumberFormatException</span><span class="err">:</span> <span class="nv">...></span><span class="p">}}</span> +</pre></div> + + +<p>There are a few things to see here. If any cleaner function throws an +Exception, the resulting map will have <code>:valid</code> set to <code>false</code>.</p> +<p>There will also be no <code>:results</code> entry in an invalid result. You only get +<code>:results</code> if your <em>entire</em> form is valid. This is to help prevent you from +accidentally using the results of a form with invalid data.</p> +<p>The <code>:errors</code> map will map field names to the exception their cleaners threw. +This happens on a per-field basis, so you can have separate errors for each +field.</p> +<p>Red Tape uses Slingshot's <code>try+</code> to catch exceptions, so if you want to you can +use <code>throw+</code> to throw errors in an easier-to-manage way and they'll be caught +just fine.</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">ensure-not-immortal</span> <span class="p">[</span><span class="nv">age</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">> </span><span class="nv">age</span> <span class="mi">150</span><span class="p">)</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"I think you're lying!"</span><span class="p">)</span> + <span class="nv">age</span><span class="p">))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">age-form</span> <span class="p">{}</span> + <span class="ss">:age</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> + <span class="o">#</span><span class="p">(</span><span class="nf">Long.</span> <span class="nv">%</span><span class="p">)</span> + <span class="nv">ensure-not-immortal</span><span class="p">])</span> + +<span class="p">(</span><span class="nf">age-form</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"1000"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"1000"</span><span class="p">}</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:age</span> <span class="s">"I think you're lying!"</span><span class="p">}}</span> +</pre></div> + + +<p>Notice how <code>ensure-not-immortal</code> expected a number and not a String. This is +fine because we still kept the <code>#(Long. %)</code> cleaner to handle that conversion.</p> +<p>Finally, also notice that the <code>:data</code> entry in the result map is present and +contains the data the user entered, even though it turned out to be invalid. +We'll use this later when we want to rerender the form, so the user doesn't have +to type all the data again.</p> +<h2 id="built-in-cleaners">Built-In Cleaners</h2> +<p>Red Tape contains a number of useful cleaner functions pre-defined in the +<code>red-tape.cleaners</code> namespace.</p> +<p>We'll use <code>red-tape.cleaners/non-blank</code> in this tutorial. <code>non-blank</code> is +a simple cleaner that throws an exception if it receives an empty string, or +otherwise passes through the data unchanged.</p> +<p>Let's change the form to make sure that users don't try to submit an empty +comment (but we'll still allow an empty name, in case someone wants to comment +anonymously):</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">feedback</span> + <span class="c1">; ...</span> + <span class="p">(</span><span class="nf">require</span> <span class="p">[</span><span class="nv">red-tape.cleaners</span> <span class="ss">:as</span> <span class="nv">cleaners</span><span class="p">]))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">feedback-form</span> <span class="p">{}</span> + <span class="ss">:name</span> <span class="p">[</span><span class="nv">clojure.string/trim</span><span class="p">]</span> + <span class="ss">:comment</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> <span class="nv">cleaners/non-blank</span><span class="p">])</span> +</pre></div> + + +<p>Notice that we trim whitespace <em>before</em> checking for a non-blank string, so +a comment of all whitespace would result in an error.</p> +<h2 id="putting-it-all-together">Putting it All Together</h2> +<p>Now that we've seen how to clean and validate, we can finally connect the +missing pieces to our feedback form.</p> +<p>First we'll redefine our little HTML page to take the form as an argument, so we +can pre-fill the inputs with any initial data:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">page</span> <span class="p">[</span><span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">html5</span> + <span class="p">[</span><span class="ss">:body</span> + <span class="p">[</span><span class="ss">:form</span> <span class="p">{</span><span class="ss">:method</span> <span class="s">"POST"</span> <span class="ss">:action</span> <span class="s">"/"</span><span class="p">}</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"Who are you?"</span><span class="p">]</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">"text"</span> <span class="ss">:name</span> <span class="s">"name"</span> + <span class="ss">:value</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">form</span> <span class="p">[</span><span class="ss">:data</span> <span class="ss">:name</span><span class="p">])}]</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"What do you want to say?"</span><span class="p">]</span> + <span class="p">[</span><span class="ss">:textarea</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"comment"</span><span class="p">}</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">form</span> <span class="p">[</span><span class="ss">:data</span> <span class="ss">:comment</span><span class="p">])]]]))</span> +</pre></div> + + +<p>Notice how we pull the values of each field out of the <code>:data</code> entry in the form +result map.</p> +<p>Now we can write the final GET handler:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-get</span> + <span class="p">([</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">feedback-form</span><span class="p">)))</span> + <span class="p">([</span><span class="nv">request</span> <span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">page</span> <span class="nv">form</span><span class="p">)))</span> +</pre></div> + + +<p>And the POST handler:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-post</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">data</span> <span class="p">(</span><span class="ss">:params</span> <span class="nv">request</span><span class="p">)</span> + <span class="nv">form</span> <span class="p">(</span><span class="nf">feedback-form</span> <span class="nv">data</span><span class="p">)]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="ss">:valid</span> <span class="nv">form</span><span class="p">)</span> + <span class="p">(</span><span class="k">let </span><span class="p">[{</span><span class="ss">:keys</span> <span class="p">[</span><span class="nb">name </span><span class="nv">comment</span><span class="p">]}</span> <span class="p">(</span><span class="ss">:results</span> <span class="nv">form</span><span class="p">)]</span> + <span class="p">(</span><span class="nf">save-feedback</span> <span class="nb">name </span><span class="nv">comment</span><span class="p">)</span> + <span class="p">(</span><span class="nf">redirect</span> <span class="s">"/"</span><span class="p">))</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="nv">form</span><span class="p">))))</span> +</pre></div> + + +<p>We use the form to process the raw data, and then examine the result. If it is +valid, we save the feedback by using the cleaned <code>:results</code> and we're done.</p> +<p>If it's <em>not</em> valid, we use the GET handler to re-render the form without +redirecting. We pass along our <em>invalid</em> form as we do that, so that when the +GET handler calls the <code>page</code> and uses the <code>:data</code> it will fill in the fields +correctly so the user doesn't have to retype everything.</p> +<h2 id="summary">Summary</h2> +<p>That was a lot to cover, but now you've seen the basic Red Tape workflow! Most +of the time you'll be doing what we just finished:</p> +<ul><li>Defining the form.</li> +<li>Defining a GET handler that creates a blank form.</li> +<li>Defining a GET handler that takes a form (either blank or invalid) and renders + it to HTML.</li> +<li>Defining a POST handler that runs data through the form, examines the result, + and does the appropriate thing depending on whether it's valid or not.</li> +</ul><p>Now that you've got the general idea, it's time to look at a few topics in more +detail. Start with the <a href="../input/">form input</a> guide.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/changelog/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,15 @@ +<html><head><title>Changelog / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="changelog"><a href="">Changelog</a></h1><p>Red Tape uses <a href="http://semver.org/">semantic versioning</a> to number versions.</p> +<div class="toc"> +<ul><li><a href="#v100">v1.0.0</a></li> +</ul></div> +<h2 id="v100">v1.0.0</h2> +<p>Initial stable release.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/cleaners/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,308 @@ +<html><head><title>Cleaners / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="cleaners"><a href="">Cleaners</a></h1><p>Cleaners are the workhorses of Red Tape. They massage your form's data into the +shape you want, and detect bad data so you can bail out if necessary.</p> +<div class="toc"> +<ul><li><a href="#cleaners-are-functions">Cleaners are Functions</a></li> +<li><a href="#validation-errors">Validation Errors</a></li> +<li><a href="#optional-fields">Optional Fields</a></li> +<li><a href="#form-level-cleaners">Form-Level Cleaners</a></li> +<li><a href="#built-in-cleaners">Built-In Cleaners</a></li> +<li><a href="#results">Results</a></li> +</ul></div> +<h2 id="cleaners-are-functions">Cleaners are Functions</h2> +<p>Cleaners are plain old Clojure functions -- there's nothing special about them. +They take one argument (the data to clean) and return a result.</p> +<p>Let's look at a few examples. First we have a cleaner function that takes +a value and turns it into a Long:</p> +<div class="codehilite"><pre><span class="c1">; A cleaner to turn the raw string into a Long.</span> +<span class="p">(</span><span class="kd">defn </span><span class="nv">to-long</span> <span class="p">[</span><span class="nv">v</span><span class="p">]</span> + <span class="p">(</span><span class="nf">Long.</span> <span class="nv">v</span><span class="p">))</span> +</pre></div> + + +<p>The next cleaner function takes a user ID, looks up the user in a "database", +and returns the user. We'll talk about the <code>throw+</code> in the next section.</p> +<div class="codehilite"><pre><span class="c1">; A cleaner to take a Long user ID and look up the</span> +<span class="c1">; user in a database.</span> +<span class="p">(</span><span class="k">def </span><span class="nv">users</span> <span class="p">{</span><span class="mi">1</span> <span class="s">"Steve"</span><span class="p">})</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">to-user</span> <span class="p">[</span><span class="nv">id</span><span class="p">]</span> + <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">user</span> <span class="p">(</span><span class="nb">get </span><span class="nv">users</span> <span class="nv">id</span><span class="p">)]</span> + <span class="p">(</span><span class="k">if </span><span class="nv">user</span> + <span class="nv">user</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"Invalid user ID!"</span><span class="p">))))</span> +</pre></div> + + +<p>Now we can use these two cleaners in a simple form:</p> +<div class="codehilite"><pre><span class="c1">; Use both of these cleaners to first turn the input</span> +<span class="c1">; string into a long, then into a user.</span> +<span class="p">(</span><span class="nf">defform</span> <span class="nv">user-form</span> <span class="p">{}</span> + <span class="ss">:user</span> <span class="p">[</span><span class="nv">to-long</span> <span class="nv">to-user</span><span class="p">])</span> + +<span class="c1">; Now we'll call the form with some data.</span> +<span class="p">(</span><span class="nf">user-form</span> <span class="p">{</span><span class="s">"user"</span> <span class="s">"1"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:user</span> <span class="s">"Steve"</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<h2 id="validation-errors">Validation Errors</h2> +<p>Cleaners can report a validation error by throwing an exception, or by using +Slingshot's <code>throw+</code> to throw <em>anything</em>.</p> +<p>If a cleaner throws something, the result map's <code>:valid</code> entry will be <code>false</code> +and the <code>:errors</code> entry will contain whatever was thrown. Continuing the +example above:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">user-form</span> <span class="p">{</span><span class="s">"user"</span> <span class="s">"400"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:user</span> <span class="s">"Invalid user ID!"</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>What happened here?</p> +<p>First, the string <code>"400"</code> was given to the first cleaner, which turned it into +the long <code>400</code>.</p> +<p>Then that long was given to the second cleaner, which tried to look it up in the +database. Since it wasn't found, the cleaner used <code>throw+</code> to throw a string as +an error, so the form was marked as invalid.</p> +<h2 id="optional-fields">Optional Fields</h2> +<p>Sometimes you want to be have a field that is optional, but when it's given you +want to transform it.</p> +<p>You <em>could</em> do this by adapting your cleaner functions like this:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">user-profile</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:username</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:bio</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nb">when-let </span><span class="p">[</span><span class="nv">bio</span> <span class="nv">%</span><span class="p">]</span> <span class="nv">bio</span><span class="p">)</span> + <span class="o">#</span><span class="p">(</span><span class="nb">when-let </span><span class="p">[</span><span class="nv">bio</span> <span class="nv">%</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="p">(</span><span class="nf">length</span> <span class="nv">bio</span><span class="p">)</span> <span class="mi">10</span><span class="p">)</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"If given, must be at least 10 characters."</span><span class="p">)</span> + <span class="nv">bio</span><span class="p">))</span> + <span class="o">#</span><span class="p">(</span><span class="nb">when-let </span><span class="p">[</span><span class="nv">bio</span> <span class="nv">%</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">> </span><span class="p">(</span><span class="nf">length</span> <span class="nv">bio</span><span class="p">)</span> <span class="mi">2000</span><span class="p">)</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"Must be under 2000 characters."</span><span class="p">)</span> + <span class="nv">bio</span><span class="p">))])</span> +</pre></div> + + +<p>This will certainly work, but it means you have to convert the empty string to +<code>nil</code> manually and then do a lot of <code>when-let</code>ing in your cleaners to pass the +<code>nil</code> through to the end.</p> +<p>You can avoid this by marking the cleaners as <code>:red-tape/optional</code>:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">user-profile</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:username</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:bio</span> <span class="o">^</span><span class="ss">:red-tape/optional</span> <span class="p">[</span> + <span class="o">#</span><span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">< </span><span class="p">(</span><span class="nf">length</span> <span class="nv">%</span><span class="p">)</span> <span class="mi">10</span><span class="p">)</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"If given, must be at least 10 characters."</span><span class="p">)</span> + <span class="nv">%</span><span class="p">)</span> + <span class="o">#</span><span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">> </span><span class="p">(</span><span class="nf">length</span> <span class="nv">%</span><span class="p">)</span> <span class="mi">2000</span><span class="p">)</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"Must be under 2000 characters."</span><span class="p">)</span> + <span class="nv">%</span><span class="p">)])</span> +</pre></div> + + +<h2 id="form-level-cleaners">Form-Level Cleaners</h2> +<p>Sometimes you need to clean or validate based on more than one field in your +form. For that you need to use form-level cleaners.</p> +<p>Form-level cleaners are similar to field cleaners: they're vanilla Clojure +functions that take and return a single value. That value is a map of all the +fields, <em>after</em> the field-level cleaners have been run.</p> +<p>Note that if any individual fields had errors, the form-level cleaners will +<em>not</em> be run. It doesn't make sense to run them on garbage input.</p> +<p>They can throw errors just like field-level cleaners too.</p> +<p>Let's look at how to use form-level cleaners with a simple example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">new-passwords-match</span> <span class="p">[</span><span class="nv">form-data</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">not= </span><span class="p">(</span><span class="ss">:new-password-1</span> <span class="nv">form-data</span><span class="p">)</span> + <span class="p">(</span><span class="ss">:new-password-2</span> <span class="nv">form-data</span><span class="p">))</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"New passwords do not match!"</span><span class="p">)</span> + <span class="nv">form-data</span><span class="p">))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">change-password-form</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[]</span> + <span class="ss">:old-password</span> <span class="p">[]</span> + <span class="ss">:new-password-1</span> <span class="p">[]</span> + <span class="ss">:new-password-2</span> <span class="p">[]</span> + <span class="ss">:red-tape/form</span> <span class="nv">new-passwords-match</span><span class="p">)</span> + +<span class="p">(</span><span class="nf">change-password-form</span> <span class="p">{</span><span class="ss">:user-id</span> <span class="s">"101"</span> + <span class="ss">:old-password</span> <span class="s">"foo"</span> + <span class="ss">:new-password-1</span> <span class="s">"a"</span> + <span class="ss">:new-password-2</span> <span class="s">"b"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:red-tape/form</span> <span class="o">#</span><span class="p">{</span><span class="s">"New passwords do not match!"</span><span class="p">}}}</span> +</pre></div> + + +<p>There's a lot to see here. First, we defined a function that takes a map of +form data (after any field cleaners have been run).</p> +<p>If the new password fields match, the function returns the map of data. In this +case it doesn't modify it at all, but it could if we wanted to.</p> +<p>If the new passwords don't match, an error is thrown with Slingshot's <code>throw+</code>.</p> +<p>Next we define the form. The form-level cleaners are specified by attaching +them to the special <code>:red-tape/form</code> "field".</p> +<p>Notice how the form-level cleaner in the example is given on its own, not as +a vector. There are actually three ways to specify form-level cleaners, +depending on how they need to interact.</p> +<p>The first way is to give a single function like we did in the example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">foo</span> <span class="p">{}</span> + <span class="nv">...</span> + <span class="ss">:red-tape/form</span> <span class="nv">my-cleaner</span><span class="p">)</span> +</pre></div> + + +<p>If you only have one form-level cleaner this is the simplest way to go.</p> +<p>The second option is to give a vector of functions, just like field cleaners:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">foo</span> <span class="p">{}</span> + <span class="nv">...</span> + <span class="ss">:red-tape/form</span> <span class="p">[</span><span class="nv">my-cleaner-1</span> <span class="nv">my-cleaner-2</span><span class="p">])</span> +</pre></div> + + +<p>These will be run in sequence, with the output of each feeding into the next. +This allows you to split up your form-level cleaners just like your field-level +ones.</p> +<p>Finally, you can give a set containing zero or more entries of either of the +first two types: </p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">foo</span> <span class="p">{}</span> + <span class="nv">...</span> + <span class="ss">:red-tape/form</span> <span class="o">#</span><span class="p">{</span><span class="nv">my-standalone-cleaner</span> + <span class="p">[</span><span class="nv">my-cleaner-part-1</span> <span class="nv">my-cleaner-part-2</span><span class="p">]})</span> +</pre></div> + + +<p>Each entry in the set will be evaluated according to the rules above, and its +output fed into the other entries.</p> +<p>This happens in an unspecified order, so you should only use a set to define +form-level cleaners that explicitly do <em>not</em> depend on each other. If one +cleaner depends on another one adjusting the data first, you need to use +a vector to make sure they run in the correct order.</p> +<p>The last thing to notice here is that the form-level errors are returned as +a <em>set</em> in the result map. This is because Red Tape will return <em>all</em> the +errors for each entry in the set of cleaners at once. For example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">new-passwords-match</span> <span class="p">[</span><span class="nv">form-data</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nb">not= </span><span class="p">(</span><span class="ss">:new-password-1</span> <span class="nv">form-data</span><span class="p">)</span> + <span class="p">(</span><span class="ss">:new-password-2</span> <span class="nv">form-data</span><span class="p">))</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"New passwords do not match!"</span><span class="p">)</span> + <span class="nv">form-data</span><span class="p">))</span> + +<span class="p">(</span><span class="kd">defn </span><span class="nv">old-password-is-correct</span> <span class="p">[</span><span class="nv">form-data</span><span class="p">]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="nf">check-password</span> <span class="p">(</span><span class="ss">:user-id</span> <span class="nv">form</span><span class="p">)</span> <span class="p">(</span><span class="ss">:old-password</span> <span class="nv">form</span><span class="p">)))</span> + <span class="nv">form-data</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"Current password is not correct!"</span><span class="p">))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">change-password-form</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[]</span> + <span class="ss">:old-password</span> <span class="p">[]</span> + <span class="ss">:new-password-1</span> <span class="p">[]</span> + <span class="ss">:new-password-2</span> <span class="p">[]</span> + <span class="ss">:red-tape/form</span> <span class="o">#</span><span class="p">{</span><span class="nv">old-password-is-correct</span> <span class="nv">new-passwords-match</span><span class="p">})</span> + +<span class="p">(</span><span class="nf">change-password-form</span> <span class="p">{</span><span class="ss">:user-id</span> <span class="s">"101"</span> + <span class="ss">:old-password</span> <span class="s">"wrong"</span> + <span class="ss">:new-password-1</span> <span class="s">"a"</span> + <span class="ss">:new-password-2</span> <span class="s">"b"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:red-tape/form</span> <span class="p">[</span><span class="s">"Current password is not correct!"</span> + <span class="s">"New passwords do not match!"</span><span class="p">]}}</span> +</pre></div> + + +<p>Since the form-level cleaners were both specified in a set, Red Tape knows that +one doesn't depend on the other. Even though one of them failed, Red Tape will +still run the others and return <em>all</em> the errors so you can show them all to the +user at once. Otherwise the user would have to tediously fix one error at +a time and submit to see if there were any other problems.</p> +<p>One last thing: form-level cleaners can change the values in the map they return +as much as they like, but they should <strong>not</strong> add or remove entries from it. +It's <em>probably</em> okay to add entries as long as they won't conflict with anything +else (i.e.: use a namespaced keyword) but the author makes no guarantees about +that.</p> +<h2 id="built-in-cleaners">Built-In Cleaners</h2> +<p>Red Tape contains a number of common cleaners in <code>red-tape.cleaners</code>. There are +also some handy macros for making your own cleaners.</p> +<p><code>ensure-is</code> is a macro that takes a value, a predicate, and an error message. +If the value satisfies the predicate, that value is passed straight through. +Otherwise the error is thrown:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">user-profile</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[</span><span class="nv">...</span> + <span class="o">#</span><span class="p">(</span><span class="nf">ensure-is</span> <span class="nv">%</span> <span class="nb">pos? </span><span class="s">"Invalid ID."</span><span class="p">)</span> + <span class="nv">...</span><span class="p">]</span> + <span class="ss">:username</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:bio</span> <span class="o">^</span><span class="ss">:red-tape/optional</span> <span class="p">[</span><span class="nv">...</span><span class="p">])</span> +</pre></div> + + +<p><code>ensure-not</code> passes the value through if it does <em>not</em> satisfy the predicate, +and throws the error if it <em>does</em>.</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">user-profile</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:username</span> <span class="p">[</span><span class="nv">...</span> + <span class="o">#</span><span class="p">(</span><span class="nf">ensure-not</span> <span class="nv">%</span> <span class="o">#</span><span class="p">{</span><span class="s">"admin"</span> <span class="s">"administrator"</span><span class="p">}</span> + <span class="s">"That username is reserved, sorry."</span><span class="p">)</span> + <span class="nv">...</span><span class="p">]</span> + <span class="ss">:bio</span> <span class="o">^</span><span class="ss">:red-tape/optional</span> <span class="p">[</span><span class="nv">...</span><span class="p">])</span> +</pre></div> + + +<p><code>red-tape.cleaners</code> also contains some pre-made cleaners that you'll probably +find useful:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">...</span> + <span class="p">(</span><span class="ss">:require</span> <span class="p">[</span><span class="nv">red-tape.cleaners</span> <span class="ss">:as</span> <span class="nv">cleaners</span><span class="p">]))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">user-profile</span> <span class="p">{}</span> + <span class="ss">:user-id</span> <span class="p">[</span><span class="nv">cleaners/to-long</span> + <span class="nv">cleaners/positive</span> + <span class="nv">...</span><span class="p">]</span> + <span class="ss">:username</span> <span class="p">[</span><span class="nv">cleaners/non-blank</span> + <span class="o">#</span><span class="p">(</span><span class="nf">cleaners/length</span> <span class="mi">3</span> <span class="mi">20</span> <span class="nv">%</span><span class="p">)</span> + <span class="nv">...</span><span class="p">]</span> + <span class="ss">:bio</span> <span class="o">^</span><span class="ss">:red-tape/optional</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/max-length</span> <span class="mi">2000</span> <span class="nv">%</span><span class="p">)]</span> + <span class="ss">:state</span> <span class="p">[</span><span class="nv">cleaners/non-blank</span> + <span class="nv">clojure.string/upper-case</span> + <span class="o">#</span><span class="p">(</span><span class="nf">cleaners/choices</span> <span class="o">#</span><span class="p">{</span><span class="s">"NY"</span> <span class="s">"PA"</span> <span class="s">"OR"</span> <span class="nv">...</span><span class="p">})])</span> +</pre></div> + + +<p>Most of the built-in cleaners take an extra argument that lets you provide +a custom error message when they fail:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">signup-form</span> <span class="p">{}</span> + <span class="ss">:username</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">"[a-zA-Z0-9]+"</span> <span class="nv">%</span><span class="p">)])</span> + +<span class="p">(</span><span class="nf">signup-form</span> <span class="p">{</span><span class="ss">:username</span> <span class="s">"cats and dogs!"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:username</span> <span class="s">"Invalid format."</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">better-signup-form</span> <span class="p">{}</span> + <span class="ss">:username</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">"[a-zA-Z0-9]+"</span> <span class="nv">%</span> + <span class="s">"Username may contain only letters and numbers."</span><span class="p">)])</span> + +<span class="p">(</span><span class="nf">signup-form</span> <span class="p">{</span><span class="ss">:username</span> <span class="s">"cats and dogs!"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:username</span> <span class="s">"Username may contain only letters and numbers."</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>See the <a href="../reference">Reference</a> section for the full list of built-in +cleaners.</p> +<h2 id="results">Results</h2> +<p>Once all cleaners have been run on the data, the results (or errors) will be +returned as a result map. Read the <a href="../result-maps/">result maps</a> guide for +more information.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/form-arguments/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,114 @@ +<html><head><title>Form Arguments / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="form-arguments"><a href="">Form Arguments</a></h1><p>Sometimes a simple form isn't enough. When you need dynamic functionality in +your forms you can use Red Tape's form arguments.</p> +<p>Once you've gone through this document, read the <a href="../rendering/">rendering</a> +guide.</p> +<div class="toc"> +<ul><li><a href="#the-problem">The Problem</a></li> +<li><a href="#defining-form-arguments">Defining Form Arguments</a></li> +<li><a href="#using-form-arguments">Using Form Arguments</a></li> +<li><a href="#initial-data">Initial Data</a></li> +</ul></div> +<h2 id="the-problem">The Problem</h2> +<p>Let's use a simple example to demonstrate why form arguments are necessary and +how they work. Suppose you have a site that hosts videos. You'd like to have +a "delete video" form to let users delete their videos. A first crack at it +might look like this:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">video-exists</span> <span class="p">[</span><span class="nv">video-id</span><span class="p">]</span> + <span class="c1">; Checks that the given video id exists...</span> +<span class="p">)</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{}</span> + <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span><span class="p">])</span> +</pre></div> + + +<p>Nothing new here. But this form will let <em>anyone</em> delete <em>any</em> video. In real +life you probably only want users to be able to delete their <em>own</em> videos.</p> +<h2 id="defining-form-arguments">Defining Form Arguments</h2> +<p>To define form arguments you can use the <code>:arguments</code> entry in the form options +map, passing a vector of symbols. Let's change our <code>delete-video-form</code> form:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]}</span> + <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span><span class="p">])</span> +</pre></div> + + +<p>The form will now take a <code>user</code> argument. So calling the form <em>without</em> data +would be done like this:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> + <span class="nv">fresh-delete-form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span><span class="p">)])</span> +</pre></div> + + +<p>And calling it <em>with</em> data would look like this:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> + <span class="nv">post-data</span> <span class="nv">...</span> + <span class="nv">form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span> <span class="nv">post-data</span><span class="p">)])</span> +</pre></div> + + +<h2 id="using-form-arguments">Using Form Arguments</h2> +<p>Using form arguments relies on one key idea: your cleaner definitions are +evaluated in a context where the form arguments are bound to the symbols you +specified.</p> +<p>Here's how we could use our <code>user</code> argument:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">video-owned-by</span> <span class="p">[</span><span class="nv">user</span> <span class="nv">video-id</span><span class="p">]</span> + <span class="c1">; Check that the given user owns the given video id.</span> +<span class="p">)</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">delete-video-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]}</span> + <span class="ss">:video-id</span> <span class="p">[</span><span class="nv">video-exists</span> + <span class="o">#</span><span class="p">(</span><span class="nf">video-owned-by</span> <span class="nv">user</span> <span class="nv">%</span><span class="p">)])</span> + +<span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">current-user</span> <span class="nv">...</span> + <span class="nv">post-data</span> <span class="nv">...</span> + <span class="nv">form</span> <span class="p">(</span><span class="nf">delete-video-form</span> <span class="nv">current-user</span> <span class="nv">post-data</span><span class="p">)]</span> + <span class="nv">...</span><span class="p">)</span> +</pre></div> + + +<p>Notice how the second cleaner in the <code>defform</code> is defined as <code>#(video-owned-by +user %)</code>. <code>user</code> here is the form argument, so the anonymouse function will be +created with the appropriate user each time <code>delete-form</code> is called.</p> +<h2 id="initial-data">Initial Data</h2> +<p>The initial data map is also evaluated in a context where the form arguments are +bound. You can use this to define default values based on the form arguments.</p> +<p>For example: suppose you have a profile page where users can change their email +address and profile. A form for this might look like:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">profile-form</span> <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]</span> + <span class="ss">:initial</span> <span class="p">{</span><span class="ss">:email</span> <span class="p">(</span><span class="ss">:email</span> <span class="nv">user</span><span class="p">)</span> + <span class="ss">:bio</span> <span class="p">(</span><span class="ss">:bio</span> <span class="nv">user</span><span class="p">)}}</span> + <span class="ss">:email</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">"\S+@\S+"</span> <span class="nv">%</span> + <span class="s">"Please enter a valid email address."</span><span class="p">)]</span> + <span class="ss">:bio</span> <span class="p">[])</span> + +<span class="p">(</span><span class="nf">profile-form</span> <span class="nv">some-user</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="nv">nil</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span> + <span class="ss">:bio</span> <span class="s">"Computers are terrible."</span><span class="p">}}</span> + +<span class="p">(</span><span class="nf">profile-form</span> <span class="nv">some-user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"this is not an email"</span> + <span class="ss">:bio</span> <span class="s">"I like cats."</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"Please enter a valid email address"</span><span class="p">}</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"this is not an email"</span> + <span class="ss">:bio</span> <span class="s">"I like cats."</span><span class="p">}}</span> +</pre></div> + + +<p>Notice how in the fresh form the <code>:data</code> is prepopulated with the user's +information that was pulled from the form argument. Once the user actually +enters some data, the initial data is ignored.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,127 @@ +<!DOCTYPE html> +<html> + <head> + <title>Red Tape</title> + <link rel="stylesheet" href="./_dmedia/bootstrap.css"/> + <link rel="stylesheet" href="./_dmedia/tango.css"/> + <link rel="stylesheet/less" type="text/css" href="./_dmedia/style.less"> + <script src="./_dmedia/less.js" type="text/javascript"> + </script> + </head> + <body class="index"> + <div class="wrap"> + <header><h1><a href="">Red Tape</a></h1></header> + <div class="markdown"> +<p>Red Tape is a Clojure library for processing user-submitted data. It's heavily +inspired by Django's forms (the good parts).</p> +<p><strong>License:</strong> MIT/X11<br /> +<strong>Documentation:</strong> <a href="http://sjl.bitbucket.org/red-tape/">http://sjl.bitbucket.org/red-tape/</a><br /> +<strong>Changelog:</strong> <a href="http://sjl.bitbucket.org/red-tape/changelog/">http://sjl.bitbucket.org/red-tape/changelog/</a><br /> +<strong>Issues:</strong> <a href="http://github.com/sjl/red-tape/issues/">http://github.com/sjl/red-tape/issues/</a><br /> +<strong>Mercurial:</strong> <a href="http://bitbucket.org/sjl/red-tape/">http://bitbucket.org/sjl/red-tape/</a><br /> +<strong>Git:</strong> <a href="http://github.com/sjl/red-tape/">http://github.com/sjl/red-tape/</a></p> +<h2 id="what-does-it-look-like">What Does it Look Like?</h2> +<p>You'll need to read the docs to really understand what's going on, but here's +a quick example so you can see the shape of the code.</p> +<p>First you'll define a form:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">ns </span><span class="nv">my-web-app</span> + <span class="p">(</span><span class="ss">:require</span> <span class="p">[</span><span class="nv">red-tape.core</span> <span class="ss">:refer</span> <span class="nv">defform</span><span class="p">]</span> + <span class="p">[</span><span class="nv">red-tape.cleaners</span> <span class="ss">:as</span> <span class="nv">cleaners</span><span class="p">]))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">comment-form</span> + <span class="p">{</span><span class="ss">:arguments</span> <span class="p">[</span><span class="nv">user</span><span class="p">]</span> + <span class="ss">:initial</span> <span class="p">{</span><span class="ss">:email</span> <span class="p">(</span><span class="ss">:email-address</span> <span class="nv">user</span><span class="p">)}}</span> + + <span class="ss">:email</span> <span class="o">^</span><span class="ss">:red-tape/optional</span> + <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">cleaners/matches</span> <span class="o">#</span><span class="s">"\S+@\S+"</span> <span class="nv">%</span> + <span class="s">"Enter a valid email (or leave it blank)."</span><span class="p">)]</span> + <span class="ss">:comment</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> + <span class="nv">cleaners/non-blank</span> + <span class="o">#</span><span class="p">(</span><span class="nf">cleaners/max-length</span> <span class="mi">2000</span> <span class="nv">%</span><span class="p">)])</span> +</pre></div> + + +<p>A form can have some arguments, initial data, as well as some fields. Fields +have functions for validating data and massaging it into what you need.</p> +<p>Now we can use the form to create an initial form (which we'll display to the +user so they can fill it in):</p> +<div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">steve</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span><span class="p">})</span> +<span class="p">(</span><span class="k">def </span><span class="nv">anon</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span><span class="p">})</span> + +<span class="p">(</span><span class="nf">comment-form</span> <span class="nv">steve</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span> + <span class="ss">:valid</span> <span class="nv">nil</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="nv">nil</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span> <span class="ss">:comment</span> <span class="s">""</span><span class="p">}</span> + <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span><span class="p">}}}</span> + +<span class="p">(</span><span class="nf">comment-form</span> <span class="nv">anon</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span> + <span class="ss">:valid</span> <span class="nv">nil</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="nv">nil</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span> <span class="ss">:comment</span> <span class="s">""</span><span class="p">}</span> + <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span><span class="p">}}}</span> +</pre></div> + + +<p>And once they submit it we use the form once again to validate and transform the +data:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">comment-form</span> <span class="nv">steve</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve+nospam@stevelosh.com"</span> + <span class="ss">:comment</span> <span class="s">" Hello!"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">true</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve+nospam@stevelosh.com"</span> + <span class="ss">:comment</span> <span class="s">"Hello!"</span><span class="p">}</span> + <span class="ss">:errors</span> <span class="nv">nil</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve+nospam@stevelosh.com"</span> + <span class="ss">:comment</span> <span class="s">" Hello!"</span><span class="p">}</span> + <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"steve@stevelosh.com"</span><span class="p">}}}</span> +</pre></div> + + +<p>If there are errors, we know the data was bad and we need to show the form to +the user again so they can fix it:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">comment-form</span> <span class="nv">anon</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span> + <span class="ss">:comment</span> <span class="s">""</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:valid</span> <span class="nv">false</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:errors</span> <span class="p">{</span><span class="ss">:comment</span> <span class="s">"This field is required."</span><span class="p">}</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span> + <span class="ss">:comment</span> <span class="s">""</span><span class="p">}</span> + <span class="ss">:arguments</span> <span class="p">{</span><span class="ss">:user</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">""</span><span class="p">}}}</span> +</pre></div> + + +<p>But you'll really need to read the docs to understand what's happening here.</p> +<h2 id="why-red-tape">Why Red Tape?</h2> +<p>There are a lot of other Clojure "validation" libraries out there. I wasn't +happy with any of them for a few reasons:</p> +<ul> +<li>Some try to be too general and validate <em>anything</em>. Red Tape is designed to + work with user-submitted web form data with very little friction.</li> +<li>Some don't do enough. Validating data is only the first step. When working + with forms you want to transform data, handle initial data, and so on. Red + Tape knows what real-world web forms needs and gives it to you.</li> +<li>Some are too complicated. Red Tape uses just a little bit of macro magic + combined with vanilla Clojure functions and Slingshot to do everything. This + makes it very easy to extend and work with.</li> +</ul> +<p>In a nutshell, Red Tape was made by someone who used Django's forms in client +sites for years and knows all the <a href="https://github.com/dwaiter/django-goodfields">rough edges</a> that needed sanding.</p> +<h2 id="get-started">Get Started</h2> +<p>Get started by <a href="./installation/">installing</a> Red Tape, then move on to <a href="./basics/">the +basics</a>.</p><h2>Table of Contents</h2><ol class="toc"><li><a href="installation/">Installation</a></li><li><a href="basics/">Basics</a></li><li><a href="input/">Form Input</a></li><li><a href="cleaners/">Cleaners</a></li><li><a href="result-maps/">Result Maps</a></li><li><a href="initial-data/">Initial Data</a></li><li><a href="form-arguments/">Form Arguments</a></li><li><a href="rendering/">Rendering</a></li><li><a href="reference/">Reference</a></li><li><a href="changelog/">Changelog</a></li></ol> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id='rochester-made' href='http://rochestermade.com' title='Rochester Made'><img src='http://rochestermade.com/media/images/rochester-made-dark-on-light.png' alt='Rochester Made' title='Rochester Made' /></a></p></footer> + </div> + </body> +</html>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/initial-data/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,49 @@ +<html><head><title>Initial Data / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="initial-data"><a href="">Initial Data</a></h1><p>Sometimes you want to provide sane defaults for fields in a form. To do this +you can use the <code>:initial</code> entry in the <code>defform</code> option map. For example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">sample-form</span> <span class="p">{</span><span class="ss">:initial</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"user@example.com"</span><span class="p">}}</span> + <span class="ss">:email</span> <span class="p">[]</span> + <span class="ss">:message</span> <span class="p">[])</span> +</pre></div> + + +<p>When creating a fresh form, this initial data will populate the <code>:data</code> map in +the result instead of the default empty string:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">sample-form</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">true</span> + <span class="ss">:results</span> <span class="nv">nil</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"user@example.com"</span> + <span class="ss">:message</span> <span class="s">""</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>When you run some data through the form, the initial data is ignored completely:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">sample-form</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"foo@bar.com"</span> + <span class="ss">:message</span> <span class="s">"hi"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:fresh</span> <span class="nv">false</span> + <span class="ss">:results</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"foo@bar.com"</span> + <span class="ss">:message</span> <span class="s">"hi"</span><span class="p">}</span> + <span class="ss">:data</span> <span class="p">{</span><span class="ss">:email</span> <span class="s">"foo@bar.com"</span> + <span class="ss">:message</span> <span class="s">"hi"</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>This means when you're rendering a form field to HTML you can simply pull the +field's entry from the <code>:data</code> map and fill the field with its contents. It +doesn't matter whether it happened to be a fresh form or a form that had some +errors.</p> +<p>Note that initial data should always be specified as a string, since the <code>:data</code> +map will always contain strings once you've run data through it.</p> +<p>Move on to the <a href="../form-arguments/">form arguments</a> guide next.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/input/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,50 @@ +<html><head><title>Form Input / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="form-input"><a href="">Form Input</a></h1><p>Red Tape forms take a map of data as input. Red Tape is designed with web-based +forms in mind, but since it operates on plain old maps you could certainly use +it for other things as well.</p> +<div class="toc"> +<ul><li><a href="#keys">Keys</a></li> +<li><a href="#values">Values</a></li> +</ul></div> +<h2 id="keys">Keys</h2> +<p>The input map should contain keys that match the field definitions of a form. +For example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defform</span> <span class="nv">change-password-form</span> <span class="p">{}</span> + <span class="ss">:old-password</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:new-password-1</span> <span class="p">[</span><span class="nv">...</span><span class="p">]</span> + <span class="ss">:new-password-2</span> <span class="p">[</span><span class="nv">...</span><span class="p">])</span> + +<span class="p">(</span><span class="nf">change-password-form</span> <span class="p">{</span><span class="ss">:old-password</span> <span class="s">"foo"</span> + <span class="ss">:new-password-1</span> <span class="s">"bar"</span> + <span class="ss">:new-password-2</span> <span class="s">"bar"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> <span class="nv">result</span> <span class="nb">map </span><span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>For convenience, the keys in the input map can be strings and everything will +still work:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">change-password-form</span> <span class="p">{</span><span class="s">"old-password"</span> <span class="s">"foo"</span> + <span class="s">"new-password-1"</span> <span class="s">"bar"</span> + <span class="s">"new-password-2"</span> <span class="s">"bar"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="nv">...</span> <span class="nv">result</span> <span class="nb">map </span><span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>If the input map contains both a string and a keyword key for a field, the +results are undefined. Don't do that.</p> +<h2 id="values">Values</h2> +<p>All values in the input map should be strings.</p> +<p>Using other types of data may <em>appear</em> to work, but Red Tape's behavior may +change in the future so you really should just stick to string input and use +cleaners to convert them to whatever kind of data you want.</p> +<p>Move on to the <a href="../cleaners/">cleaners</a> guide to learn more about this.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/installation/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,17 @@ +<html><head><title>Installation / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="installation"><a href="">Installation</a></h1><p>Red Tape requires Leiningen 2. Add the following to your <code>project.clj</code>:</p> +<div class="codehilite"><pre><span class="p">[</span><span class="n">red</span><span class="o">-</span><span class="n">tape</span> "0<span class="p">.</span>0<span class="p">.</span>1"<span class="p">]</span> +</pre></div> + + +<p>That's all there is to it. Red Tape depends on one other library (Slingshot) +but Leiningen should handle that for you.</p> +<p>Once you've got it installed, move on to the <a href="../basics/">basics guide</a>.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/reference/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,224 @@ +<html><head><title>Reference / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="reference"><a href="">Reference</a></h1><p>The following is a list of all user-facing parts of Red Tape.</p> +<p>If there are any backwards-incompatible changes to anything listed here, they +will be noted in the changelog and the author will feel bad.</p> +<p>Anything not listed here is subject to change at any time with no warning, so +don't touch it.</p> +<div class="toc"> +<ul><li><a href="#red-tapecore">red-tape.core</a><ul><li><a href="#defform">defform</a></li> +<li><a href="#form">form</a></li> +</ul></li> +<li><a href="#red-tapecleaners">red-tape.cleaners</a><ul><li><a href="#utilities">Utilities</a><ul><li><a href="#ensure-is">ensure-is</a></li> +<li><a href="#ensure-not">ensure-not</a></li> +</ul></li> +<li><a href="#string-cleaners">String Cleaners</a><ul><li><a href="#non-blank">non-blank</a></li> +<li><a href="#min-length">min-length</a></li> +<li><a href="#max-length">max-length</a></li> +<li><a href="#length">length</a></li> +<li><a href="#matches">matches</a></li> +</ul></li> +<li><a href="#numeric-cleaners">Numeric Cleaners</a><ul><li><a href="#to-long">to-long</a></li> +<li><a href="#to-double">to-double</a></li> +<li><a href="#positive">positive</a></li> +<li><a href="#negative">negative</a></li> +</ul></li> +<li><a href="#other-cleaners">Other Cleaners</a><ul><li><a href="#choices">choices</a></li> +<li><a href="#to-boolean">to-boolean</a></li> +</ul></li> +</ul></li> +</ul></div> +<h2 id="red-tapecore">red-tape.core</h2> +<p><code>red-tape.core</code> contains the <code>defform</code> and <code>form</code> macros you use to build forms. +Everything else in the namespace is internal. Don't poke at it.</p> +<h3 id="defform">defform</h3> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.core/defform</span> <span class="nv">form-name</span> <span class="nv">options</span> <span class="o">&</span> <span class="nv">fields</span><span class="p">)</span> +</pre></div> + + +<p>Define a form.</p> +<p>The first argument is the name the form will be bound to.</p> +<p>The <code>options</code> map can contain some or all of the following entries:</p> +<ul><li><code>:arguments</code>: a vector of symbols that the form function will take as + arguments. See the <a href="../form-arguments/">form arguments</a> guide for more + information.</li> +<li><code>:initial</code>: a map of field names to expressions that will be evaluated to get + initial data. See the <a href="../initial-data/">initial data</a> guide for more + information.</li> +</ul><p>More options may be added in the future.</p> +<p>Finally the fields are defined as pairs of <code>:field [... cleaners ...]</code>. The +field name <code>:red-tape/form</code> is used to define <a href="../cleaners/#form-level-cleaners">form-level +cleaners</a>.</p> +<h3 id="form">form</h3> +<p><code>form</code> is like <code>defform</code> except that you don't specify a name, and it creates an +anonymous form. In a nutshell: <code>form</code> is to <code>defform</code> as <code>fn</code> is to <code>defn</code>.</p> +<h2 id="red-tapecleaners">red-tape.cleaners</h2> +<p>Red Tape contains a number of built-in cleaners and utility functions in the +<code>red-tape.cleaners</code> namespace.</p> +<p>Most built-in cleaners take an optional <code>error-message</code> argument that you can +use to provide a custom error message if they fail (instead of the generic one +they each provide).</p> +<p>Also, remember that cleaners are just vanilla Clojure functions, which means +you'll find a lot of useful cleaners in the <code>clojure.string</code> namespace.</p> +<h3 id="utilities">Utilities</h3> +<p>The <code>ensure-is</code> and <code>ensure-not</code> macros are useful when writing your own +reusable cleaner functions.</p> +<h4 id="ensure-is">ensure-is</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/ensure-is</span> <span class="nv">value</span> <span class="nv">predicate</span> <span class="nv">message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the given value returns <code>true</code> for the given predicate.</p> +<p>If the value satisfies the predicate, it is returned unchanged.</p> +<p>Otherwise, the given message is thrown with Slingshot's <code>throw+</code>.</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">ensure-is</span> <span class="mi">10</span> <span class="nb">pos? </span><span class="s">"Enter a positive number."</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="mi">10</span> + +<span class="p">(</span><span class="nf">ensure-is</span> <span class="mi">-10</span> <span class="nb">pos? </span><span class="s">"Enter a positive number."</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="c1">; throws "Enter a positive number."</span> +</pre></div> + + +<h4 id="ensure-not">ensure-not</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/ensure-not</span> <span class="nv">value</span> <span class="nv">predicate</span> <span class="nv">message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the given value returns <code>false</code> for the given predicate.</p> +<p>If the value satisfies the predicate, it is returned unchanged.</p> +<p>Otherwise, the given message is thrown with Slingshot's <code>throw+</code>.</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">ensure-not</span> <span class="mi">255</span> <span class="o">#</span><span class="p">(</span><span class="nb">< </span><span class="nv">%</span> <span class="mi">10</span><span class="p">)</span> <span class="s">"Cannot be less than 10."</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="mi">255</span> + +<span class="p">(</span><span class="nf">ensure-not</span> <span class="mi">1</span> <span class="o">#</span><span class="p">(</span><span class="nb">< </span><span class="nv">%</span> <span class="mi">10</span><span class="p">)</span> <span class="s">"Cannot be less than 10."</span><span class="p">)</span> +<span class="c1">; =></span> +<span class="c1">; throws "Cannot be less than 10."</span> +</pre></div> + + +<h3 id="string-cleaners">String Cleaners</h3> +<p>There are a number of built-in cleaners designed to be used with String data.</p> +<h4 id="non-blank">non-blank</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/non-blank</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/non-blank</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the given string is not empty.</p> +<p>Whitespace is treated like any other character -- use <code>clojure.string/trim</code> to +remove it first if you want to.</p> +<h4 id="min-length">min-length</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/min-length</span> <span class="nv">n</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/min-length</span> <span class="nv">n</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure that the given string is at least <code>n</code> characters long.</p> +<h4 id="max-length">max-length</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/max-length</span> <span class="nv">n</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/max-length</span> <span class="nv">n</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure that the given string is at most <code>n</code> characters long.</p> +<h4 id="length">length</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/length</span> <span class="nb">min max </span><span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/length</span> <span class="nb">min max </span><span class="nv">s</span> <span class="nv">min-error</span> <span class="nv">max-error</span><span class="p">)</span> +</pre></div> + + +<p>Ensure that the given string is at least <code>min</code> and at most <code>max</code> characters +long.</p> +<p><code>length</code> takes two optional error message arguments, one to throw if the string +is too short and one if it's too long.</p> +<h4 id="matches">matches</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/matches</span> <span class="nv">regex</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/matches</span> <span class="nv">regex</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure that the string (fully) matches the given regular expression.</p> +<p>The regex must match the <em>entire</em> string, so <code>(matches #"foo" "foo bar")</code> will +throw an error.</p> +<h3 id="numeric-cleaners">Numeric Cleaners</h3> +<p>Red Tape has a few built-in cleaners for working with numbers too.</p> +<h4 id="to-long">to-long</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/to-long</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/to-long</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Convert a String to a Long.</p> +<h4 id="to-double">to-double</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/to-double</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/to-double</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Convert a String to a Double.</p> +<h4 id="positive">positive</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/positive</span> <span class="nv">n</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/positive</span> <span class="nv">n</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the given number is positive.</p> +<p>This function expects a number, so use <code>to-long</code> or <code>to-double</code> first.</p> +<h4 id="negative">negative</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/negative</span> <span class="nv">n</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/negative</span> <span class="nv">n</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the given number is negative.</p> +<p>This function expects a number, so use <code>to-long</code> or <code>to-double</code> first.</p> +<h3 id="other-cleaners">Other Cleaners</h3> +<p>Finally, there are some other cleaners that don't fit into one of the other +categories.</p> +<h4 id="choices">choices</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/choices</span> <span class="nv">cs</span> <span class="nv">value</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/choices</span> <span class="nv">cs</span> <span class="nv">value</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Ensure the value is contained in the given choices.</p> +<p>Useful when you have a list of valid things for the user to choose from:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="k">def </span><span class="nv">us-states</span> <span class="o">#</span><span class="p">{</span><span class="s">"NY"</span> <span class="s">"PA"</span> <span class="s">"OR"</span> <span class="nv">...</span><span class="p">})</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">address-form</span> <span class="p">{}</span> + <span class="nv">...</span> + <span class="ss">:state</span> <span class="p">[</span><span class="o">#</span><span class="p">(</span><span class="nf">choices</span> <span class="nv">us-states</span> <span class="nv">%</span> + <span class="s">"Please enter a valid US state."</span><span class="p">)])</span> +</pre></div> + + +<p>Not limited to strings, so you can parse the input into something else and then +check for those with choices if you want.</p> +<p>Note that this uses <code>clojure.core/contains?</code> which is stupid about vectors, so +be careful! Use a set if possible.</p> +<h4 id="to-boolean">to-boolean</h4> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">red-tape.cleaners/to-boolean</span> <span class="nv">s</span><span class="p">)</span> +<span class="p">(</span><span class="nf">red-tape.cleaners/to-boolean</span> <span class="nv">s</span> <span class="nv">error-message</span><span class="p">)</span> +</pre></div> + + +<p>Convert a string to a boolean.</p> +<p>The following strings are considered true (case-insensitive):</p> +<ul><li><code>"1"</code></li> +<li><code>"true"</code></li> +<li><code>"t"</code></li> +</ul><p>The following strings are considered false (case-insensitive):</p> +<ul><li><code>"0"</code></li> +<li><code>"false"</code></li> +<li><code>"f"</code></li> +</ul><p>Anything else is an error.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/rendering/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,115 @@ +<html><head><title>Rendering / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="rendering"><a href="">Rendering</a></h1><p>Red Tape does not provide any built-in functionality for rendering HTML. It +leaves that up to you, so you can choose your preferred templating library. +However, it's built with a few key ideas in mind that should make it easier for +you do create forms that are usable and friendly for your users.</p> +<p>Let's look at a full example that shows all the things you should think about +when rendering a form. This example will use the Hiccup templating library for +brevity, but as mentioned you can use any templating library you like.</p> +<p>Once you've read this section you're all set! Dig in and start making some +forms. The <a href="../reference/">reference guide</a> will be there when you need it.</p> +<div class="toc"> +<ul><li><a href="#basic-structure">Basic Structure</a></li> +<li><a href="#rendering-a-fresh-form">Rendering a Fresh Form</a></li> +<li><a href="#rendering-errors">Rendering Errors</a></li> +</ul></div> +<h2 id="basic-structure">Basic Structure</h2> +<p>Recall the structure of the GET handler from the <a href="../basics/">basics</a> guide:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-get</span> + <span class="p">([</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">your-form</span><span class="p">)))</span> + <span class="p">([</span><span class="nv">request</span> <span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">...</span> <span class="nv">render</span> <span class="nv">the</span> <span class="nv">page</span>, <span class="nv">passing</span> <span class="nv">in</span> <span class="nv">the</span> <span class="nv">form</span> <span class="nv">...</span><span class="p">)))</span> +</pre></div> + + +<p>And also recall the POST handler:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-post</span> <span class="p">[</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="k">let </span><span class="p">[</span><span class="nv">data</span> <span class="p">(</span><span class="ss">:params</span> <span class="nv">request</span><span class="p">)</span> + <span class="nv">form</span> <span class="p">(</span><span class="nf">your-form</span> <span class="nv">data</span><span class="p">)]</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="ss">:valid</span> <span class="nv">form</span><span class="p">)</span> + <span class="p">(</span><span class="nf">...</span> <span class="k">do </span><span class="nv">whatever</span> <span class="nv">you</span> <span class="nv">need</span>, <span class="nv">then</span> <span class="nv">redirect</span> <span class="nv">somewhere</span> <span class="nv">else</span> <span class="nv">...</span><span class="p">)</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="nv">form</span><span class="p">))))</span> +</pre></div> + + +<p>As you can see, there's only one place where the form gets rendered in this +code. This means your rendering code will all be in a single place, and should +handle result maps from fresh forms as well as ones that contain errors.</p> +<h2 id="rendering-a-fresh-form">Rendering a Fresh Form</h2> +<p>Let's talk about the fresh form first. We'll use a simple form as an example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">require</span> <span class="o">'</span><span class="p">[</span><span class="nv">red-tape.cleaners</span> <span class="ss">:as</span> <span class="nv">cleaners</span><span class="p">])</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">simple-form</span> <span class="p">{</span><span class="ss">:initial</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"Steve"</span><span class="p">}}</span> + <span class="ss">:name</span> <span class="p">[</span><span class="nv">clojure.string/trim</span> + <span class="nv">cleaners/non-blank</span><span class="p">])</span> +</pre></div> + + +<p>Now we'll create the get handler for this:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">handle-get</span> + <span class="p">([</span><span class="nv">request</span><span class="p">]</span> + <span class="p">(</span><span class="nf">handle-get</span> <span class="nv">request</span> <span class="p">(</span><span class="nf">simple-form</span><span class="p">)))</span> + <span class="p">([</span><span class="nv">request</span> <span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">simple-page</span> <span class="nv">form</span><span class="p">)))</span> +</pre></div> + + +<p>Now we'll use Hiccup to create <code>simple-page</code>:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defpage</span> <span class="nv">simple-page</span> <span class="p">[</span><span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">html</span> + <span class="p">[</span><span class="ss">:body</span> + <span class="p">[</span><span class="ss">:form</span> <span class="p">{</span><span class="ss">:method</span> <span class="s">"POST"</span><span class="p">}</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"What is your name?"</span><span class="p">]</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"name"</span> + <span class="ss">:value</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">form</span> <span class="p">[</span><span class="ss">:data</span> <span class="ss">:name</span><span class="p">])}]</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">"submit"</span><span class="p">}]]]))</span> +</pre></div> + + +<p>Notice how the <code>value</code> for the name input is pulled out of the <code>:data</code> entry of +the result map. This ensures that the field will be filled in with the correct +initial data ("Steve" in this example).</p> +<h2 id="rendering-errors">Rendering Errors</h2> +<p>Suppose the user backspaces out the initial name and submits the form. The +<code>non-blank</code> cleaner in the form means that a blank name is considered invalid.</p> +<p>The usual POST handler for a Red Tape form will use the GET handler to re-render +the result map of the invalid form, so our template should take this into +account and display errors properly. Let's see how we might do that:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="nf">defpage</span> <span class="nv">simple-page</span> <span class="p">[</span><span class="nv">form</span><span class="p">]</span> + <span class="p">(</span><span class="nf">html</span> + <span class="p">[</span><span class="ss">:body</span> + <span class="p">(</span><span class="k">if </span><span class="p">(</span><span class="ss">:errors</span> <span class="nv">form</span><span class="p">)</span> + <span class="p">[</span><span class="ss">:p</span> <span class="s">"There was a problem. Please try again."</span><span class="p">])</span> + <span class="p">[</span><span class="ss">:form</span> <span class="p">{</span><span class="ss">:method</span> <span class="s">"POST"</span><span class="p">}</span> + <span class="p">[</span><span class="ss">:label</span> <span class="s">"What is your name?"</span><span class="p">]</span> + <span class="p">(</span><span class="nb">when-let </span><span class="p">[</span><span class="nv">error</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">form</span> <span class="p">[</span><span class="ss">:errors</span> <span class="ss">:name</span><span class="p">])]</span> + <span class="p">[</span><span class="ss">:p</span> <span class="nv">error</span><span class="p">])</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:name</span> <span class="s">"name"</span> + <span class="ss">:value</span> <span class="p">(</span><span class="nf">get-in</span> <span class="nv">form</span> <span class="p">[</span><span class="ss">:data</span> <span class="ss">:name</span><span class="p">])}]</span> + <span class="p">[</span><span class="ss">:input</span> <span class="p">{</span><span class="ss">:type</span> <span class="s">"submit"</span><span class="p">}]]]))</span> +</pre></div> + + +<p>There are two additions here. First we check if there were any errors at the +top of the form. If there were, we display a message at the top of the form.</p> +<p>This is especially important when you have more than a single field, because the +field with the problem may be further down the page and the user needs to know +that they need to scroll down to see it.</p> +<p>We also check if there were any errors for the specific field, and if so we +display it. In this simple example there's only one field, but in most forms +you'll have many fields, each of which may or may not have errors.</p> +<p>This form didn't have any form-level cleaners, but if it did we would have +wanted to display those too (probably at the top of the form).</p> +<p>Also remember that the <code>:data</code> in the form that we're using to prepopulate the +field will now contain whatever the user submitted. That way we won't blow away +the things the user typed if there happens to be an error. They can fix the +error and just hit submit.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/red-tape/result-maps/index.html Tue Aug 13 10:24:27 2013 -0400 @@ -0,0 +1,75 @@ +<html><head><title>Result Maps / Red Tape</title><link rel="stylesheet" href="../_dmedia/bootstrap.css"/><link rel="stylesheet" href="../_dmedia/tango.css"/><link rel="stylesheet/less" type="text/css" href="../_dmedia/style.less"/><script src="../_dmedia/less.js" type="text/javascript"> + </script></head><body class="content"> + <div class="wrap"> + <header><h1><a href="..">Red Tape</a></h1></header><div class="markdown"> +<h1 id="result-maps"><a href="">Result Maps</a></h1><p>After you pass data through a form (or call a form without data) you'll get +a result map back.</p> +<p>This map contains a number of entries that describe the results of running the +cleaners on that data (or some placeholders when you run a form without data). +Let's look at each of the entries in detail.</p> +<p>Once you're done here, move on to the <a href="../initial-data/">initial data</a> guide.</p> +<div class="toc"> +<ul><li><a href="#fresh">:fresh</a></li> +<li><a href="#valid">:valid</a></li> +<li><a href="#errors">:errors</a></li> +<li><a href="#data">:data</a></li> +<li><a href="#results">:results</a></li> +<li><a href="#arguments">:arguments</a></li> +</ul></div> +<h2 id="fresh">:fresh</h2> +<p>This will be <code>true</code> if this is a fresh form (i.e.: it was called without data), +and <code>false</code> otherwise.</p> +<h2 id="valid">:valid</h2> +<p>If the form was called without data, this will be <code>nil</code>.</p> +<p>Otherwise, if the form was called with data, and the data passed through all +cleaners without anything being thrown, this will be <code>true</code>.</p> +<p>Otherwise it will be <code>false.</code></p> +<p>This should be used to determine whether to trust the data from the form, or +whether to re-render it for the user (and display the errors).</p> +<h2 id="errors">:errors</h2> +<p>If the form was called with data and a cleaner threw an exception (or anything +else with Slingshot's <code>throw+</code>) then <code>:errors</code> will be a map of field keys to +whatever was thrown. For example:</p> +<div class="codehilite"><pre><span class="p">(</span><span class="kd">defn </span><span class="nv">always-fail</span> <span class="p">[</span><span class="nv">x</span><span class="p">]</span> + <span class="p">(</span><span class="nf">throw+</span> <span class="s">"Error!"</span><span class="p">))</span> + +<span class="p">(</span><span class="nf">defform</span> <span class="nv">failing-form</span> <span class="p">{}</span> + <span class="ss">:foo</span> <span class="p">[</span><span class="nv">always-fail</span><span class="p">]</span> + <span class="ss">:bar</span> <span class="p">[])</span> + +<span class="p">(</span><span class="nf">failing-form</span> <span class="p">{</span><span class="s">"foo"</span> <span class="s">"aaa"</span> + <span class="s">"bar"</span> <span class="s">"bbb"</span><span class="p">})</span> +<span class="c1">; =></span> +<span class="p">{</span><span class="ss">:errors</span> <span class="p">{</span><span class="ss">:foo</span> <span class="s">"Error!"</span><span class="p">}</span> + <span class="nv">...</span><span class="p">}</span> +</pre></div> + + +<p>Only fields that actually threw errors will have entries in the <code>:errors</code> map.</p> +<p>If any form-level cleaners threw an error, <code>:errors</code> will contain an entry for +<code>:red-tape/form</code>. This will be a set of form-level errors (for the reasons +described in the previous section).</p> +<p>If no errors were thrown anywhere, <code>:errors</code> will be <code>nil</code>.</p> +<h2 id="data">:data</h2> +<p>The <code>:data</code> entry in the result map will contain the raw data that was passed +into the form, <em>before</em> it was run through the cleaners.</p> +<p>This is useful for re-rendering the form when there are errors.</p> +<p>TODO: give a full example here.</p> +<p>If this is the result map of a fresh form (a form that has been called without +form data), every entry in <code>:data</code> will be an empty string (or initial data, +which we'll discuss in the next chapter).</p> +<h2 id="results">:results</h2> +<p>The <code>:results</code> entry is a map of field keys to the values <em>after</em> they've been +passed through all the cleaners. After you've checked that the form is valid by +looking at <code>:valid</code>, you should use the data in <code>:results</code> to do whatever you +need to do.</p> +<p>If this is a fresh form, or if any cleaners threw errors, <code>:results</code> will be +<code>nil</code>.</p> +<h2 id="arguments">:arguments</h2> +<p>The <code>:arguments</code> entry is a mapping of keywords to form arguments, which will be +discussed in a later chapter.</p> + </div> + <footer><p>Made and <a href="http://sjl.bitbucket.org/d/">documented</a> with love by <a href="http://stevelosh.com">Steve +Losh</a>.</p> +<p><br/><a id="rochester-made" href="http://rochestermade.com" title="Rochester Made"><img src="http://rochestermade.com/media/images/rochester-made-dark-on-light.png" alt="Rochester Made" title="Rochester Made"/></a></p></footer></div> + </body></html> \ No newline at end of file