diff --git a/OurUmbraco.Site/umbraco/assets/css/installer.css b/OurUmbraco.Site/umbraco/assets/css/installer.css
index 47cf1ac1..0ecf4fa0 100644
--- a/OurUmbraco.Site/umbraco/assets/css/installer.css
+++ b/OurUmbraco.Site/umbraco/assets/css/installer.css
@@ -1 +1 @@
-@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url('../fonts/opensans/OpenSans-Regular-webfont.eot');src:local('Open Sans'),local('OpenSans'),url('../fonts/opensans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Regular-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url('../fonts/opensans/OpenSans-Semibold-webfont.eot');src:local('Open Sans Semibold'),local('OpenSans-Semibold'),url('../fonts/opensans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Semibold-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg')}.red{color:#9d261d}.blue{color:#2e8aea}.color-red,.color-red i{color:#d90416!important}.color-blue,.color-blue i{color:#04bfbf!important}.color-orange,.color-orange i{color:#d9631e!important}.color-green,.color-green i{color:#04bf67!important}.color-yellow,.color-yellow i{color:#f28729!important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;color:#000;text-align:center;vertical-align:middle;cursor:pointer;background:#f2f2f2;border:1px solid #ccc;box-shadow:none}.btn:hover,.btn:focus{color:#343434;text-decoration:none;background:#e4e4e4;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled],.btn:disabled:hover{cursor:default;border-color:#ccc;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-group>.btn+.dropdown-toggle{-webkit-box-shadow:none;box-shadow:none}.btn-group>.btn:first-child,.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-radius:0}.btn-large{padding:11px 19px;font-size:17.5px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-round{z-index:6666;display:inline-block;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:32px;color:#555;text-align:center;background:#fff;-moz-border-radius:15px;border-radius:15px}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active,.btn-neutral.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:-moz-linear-gradient(top,#53a93f,#53a93f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#53a93f),to(#53a93f));background-image:-webkit-linear-gradient(top,#53a93f,#53a93f);background-image:-o-linear-gradient(top,#53a93f,#53a93f);background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active{background-color:#4fa23c}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;*background-color:#4a9638}.btn-primary:active,.btn-primary.active{background-color:#418431 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#df7f48;*background-color:#df7f48;background-image:-moz-linear-gradient(top,#df7f48,#df7f48);background-image:-webkit-gradient(linear,0 0,0 100%,from(#df7f48),to(#df7f48));background-image:-webkit-linear-gradient(top,#df7f48,#df7f48);background-image:-o-linear-gradient(top,#df7f48,#df7f48);background-image:linear-gradient(to bottom,#df7f48,#df7f48);background-repeat:repeat-x;border-color:#df7f48 #df7f48 #ba5821;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdf7f48',endColorstr='#ffdf7f48',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active{background-color:#dd793f}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;*background-color:#db7032}.btn-warning:active,.btn-warning.active{background-color:#d06324 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ee5f5b;*background-color:#ee5f5b;background-image:-moz-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#ee5f5b));background-image:-webkit-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:-o-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:linear-gradient(to bottom,#ee5f5b,#ee5f5b);background-repeat:repeat-x;border-color:#ee5f5b #ee5f5b #e51d18;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffee5f5b',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active{background-color:#ed5652}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;*background-color:#ec4844}.btn-danger:active,.btn-danger.active{background-color:#e9322d \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:-moz-linear-gradient(top,#53a93f,#53a93f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#53a93f),to(#53a93f));background-image:-webkit-linear-gradient(top,#53a93f,#53a93f);background-image:-o-linear-gradient(top,#53a93f,#53a93f);background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active{background-color:#4fa23c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;*background-color:#4a9638}.btn-success:active,.btn-success.active{background-color:#418431 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active{background-color:#53bddc}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active{background-color:#3f3f3f}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}.btn-neutral{color:#fff;color:#656565;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#f5f5f5;*background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:-o-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:linear-gradient(to bottom,#f5f5f5,#f5f5f5);background-repeat:repeat-x;border-color:#f5f5f5 #f5f5f5 #cfcfcf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff5f5f5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active{background-color:#f0f0f0}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active,.btn-neutral.active,.btn-neutral.disabled,.btn-neutral[disabled]{color:#fff;*background-color:#e8e8e8}.btn-neutral:active,.btn-neutral.active{background-color:#dcdcdc \9}.btn-neutral:hover,.btn-neutral:focus{color:#656565}.btn-neutral.disabled,.btn-neutral[disabled]{color:#656565;opacity:.65;filter:alpha(opacity=65)}.btn-install{display:block;padding:15px 50px;margin:40px auto;font-size:16px;color:white;background:#2e8aea;border:0}.btn-install:hover{background:#2b8ee3}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#000;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#000;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#343434;text-decoration:none}.btn-link-reverse{text-decoration:underline}.btn-link-reverse:hover,.btn-link-reverse:focus{text-decoration:none}.btn-link.-underline{display:inline-block;text-decoration:underline}.btn-link.-underline:hover{text-decoration:none}small.umb-detail,label small,.guiDialogTiny{display:block;font-size:11px;font-weight:normal;color:#b3b3b3!important;text-decoration:none}label.control-label{padding:0 10px 0 0!important;font-weight:bold}.umb-status-label{color:#555!important}.controls-row label{padding:0 10px 0 10px;vertical-align:middle}.breadcrumb{display:block;height:30px;margin-top:10px}.breadcrumb li{height:30px;vertical-align:middle}.breadcrumb li a{height:30px;vertical-align:middle}.breadcrumb input{font-size:11px!Important}.form-search{position:relative;padding:0}.form-search a{text-decoration:none;cursor:pointer}.form-search a:hover{color:#555}.form-search h4{color:#555}.form-search small{color:#d9d9d9}.form-search .icon,.form-search .icon-search{position:absolute;top:6px;left:4px;color:#d9d9d9}.form-search .icon-search{pointer-events:none}.form-search input{width:90%;padding:4px 0 4px 16px;padding-left:25px!Important;font-size:17.5px;font-weight:400;line-height:22px;background:#fff;border:1px solid #d9d9d9}.form-search .search-input{font-weight:bold;border-color:#d9d9d9}.form-search .search-input:hover,.form-search .search-input:focus,.form-search .search-input:focus:hover{border-color:#ccc}.form-search .search-input:-moz-placeholder{font-weight:normal}.form-search .search-input:-ms-input-placeholder{font-weight:normal}.form-search .search-input::-webkit-input-placeholder{font-weight:normal}form{margin:0 0 20px}form.-no-margin-bottom{margin-bottom:0}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#343434;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#d9d9d9}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}input.-full-width-input{width:100%;height:30px;padding:4px 6px;box-sizing:border-box}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:none;outline:0;outline:none \9}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#d9d9d9;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-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)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#d9d9d9}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#d9d9d9}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#d9d9d9}.radio,.checkbox{min-height:20px;padding-left:20px}.radio.no-indent,.checkbox.no-indent{padding-left:0}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-right:5px;margin-left:0}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}.bigInput,.input-large-type{font-size:20px!important}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:706px}input.span11,textarea.span11,.uneditable-input.span11{width:646px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:526px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:406px}input.span6,textarea.span6,.uneditable-input.span6{width:346px}input.span5,textarea.span5,.uneditable-input.span5{width:286px}input.span4,textarea.span4,.uneditable-input.span4{width:226px}input.span3,textarea.span3,.uneditable-input.span3{width:166px}input.span2,textarea.span2,.uneditable-input.span2{width:106px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#f8f8f8}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.show-validation.ng-invalid .control-group.error .control-label,.show-validation.ng-invalid .umb-panel-header-content-wrapper .control-label,.show-validation.ng-invalid .control-group.error .help-block,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-block,.show-validation.ng-invalid .control-group.error .help-inline,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-inline{color:#9d261d}.show-validation.ng-invalid .control-group.error .checkbox.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .checkbox.ng-invalid,.show-validation.ng-invalid .control-group.error .radio.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .radio.ng-invalid,.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{color:#9d261d}.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{border-color:#9d261d}.show-validation.ng-invalid .control-group.error .input-prepend .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-prepend .add-on,.show-validation.ng-invalid .control-group.error .input-append .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-append .add-on{color:#9d261d;background-color:#f8dcda;border-color:#9d261d}.highlight-error{color:#9d261d!important;border-color:#ee5f5b!important}.show-validation .umb-headline-editor-wrapper input:focus:invalid,.show-validation .umb-headline-editor-wrapper textarea:focus:invalid,.show-validation .umb-headline-editor-wrapper select:focus:invalid{color:inherit;border:0;border-color:inherit;-webkit-box-shadow:inherit;-moz-box-shadow:inherit;box-shadow:inherit}.ng-invalid>.umb-headline-editor-wrapper h1{color:#9d261d;cursor:pointer;border-bottom:1px dashed #9d261d}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#5a5a5a}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#f8f8f8;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:top;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}.control-group.-no-margin{margin-bottom:0}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}.umb-panel-buttons .umb-btn-toolbar .btn{position:relative;z-index:1000}@media(max-width:767px){.form-horizontal .control-label{width:100%}.form-horizontal .controls{margin-left:0}}body{margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}a{color:#000;text-decoration:none}a:hover,a:focus{color:#000;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:0;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:0}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.span12{width:720px}.span11{width:660px}.span10{width:600px}.span9{width:540px}.span8{width:480px}.span7{width:420px}.span6{width:360px}.span5{width:300px}.span4{width:240px}.span3{width:180px}.span2{width:120px}.span1{width:60px}.offset12{margin-left:720px}.offset11{margin-left:660px}.offset10{margin-left:600px}.offset9{margin-left:540px}.offset8{margin-left:480px}.offset7{margin-left:420px}.offset6{margin-left:360px}.offset5{margin-left:300px}.offset4{margin-left:240px}.offset3{margin-left:180px}.offset2{margin-left:120px}.offset1{margin-left:60px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:0;*margin-left:-0.06944444444444445%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:0}.row-fluid .span12{width:99.99999999999999%;*width:99.93055555555554%}.row-fluid .span11{width:91.66666666666666%;*width:91.59722222222221%}.row-fluid .span10{width:83.33333333333331%;*width:83.26388888888887%}.row-fluid .span9{width:74.99999999999999%;*width:74.93055555555554%}.row-fluid .span8{width:66.66666666666666%;*width:66.59722222222221%}.row-fluid .span7{width:58.33333333333333%;*width:58.263888888888886%}.row-fluid .span6{width:49.99999999999999%;*width:49.93055555555555%}.row-fluid .span5{width:41.66666666666666%;*width:41.597222222222214%}.row-fluid .span4{width:33.33333333333333%;*width:33.263888888888886%}.row-fluid .span3{width:24.999999999999996%;*width:24.930555555555554%}.row-fluid .span2{width:16.666666666666664%;*width:16.59722222222222%}.row-fluid .span1{width:8.333333333333332%;*width:8.263888888888888%}.row-fluid .offset12{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset12:first-child{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset11{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset11:first-child{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset10{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset10:first-child{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset9{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset9:first-child{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset8{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset8:first-child{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset7{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset7:first-child{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset6{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset6:first-child{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset5{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset5:first-child{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset4{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset4:first-child{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset3{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset3:first-child{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset2{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset2:first-child{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset1{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}.row-fluid .offset1:first-child{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:0;padding-left:0;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}.thumbnails{margin-left:0;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:0}.thumbnails>li a:hover{text-decoration:none}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#000;-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)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none!important}html{background:url('../img/installer.jpg') no-repeat center center fixed;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover}body{width:100%;height:100%;padding:0;margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;text-align:center;vertical-align:middle}#logo{position:absolute;top:20px;left:20px;z-index:777;opacity:.8}#installer{z-index:667;width:750px;height:600px;padding:30px;margin:auto;overflow:hidden;text-align:left;background:white}#overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:666;background:#1d1d1d}.loading #overlay{display:block!important;opacity:.5}#fact{z-index:667;width:750px;height:600px;font-size:25px;line-height:35px;color:#fff;text-align:left;text-shadow:0 0 4px black}#fact h2{padding-bottom:10px;margin-bottom:20px;font-size:35px;color:white;border-bottom:1px solid white}#fact a{color:white}#feedback{position:absolute;right:0;bottom:20px;left:0;z-index:667;height:25px;font-size:14px;line-height:20px;color:#fff;text-align:center;text-shadow:0 0 4px black}h1{padding-bottom:10px;color:#555;border-bottom:1px solid #f8f8f8}.error h1,.error .message,span.error{color:#9d261d}legend{font-size:14px;font-weight:bold}input.ng-dirty.ng-invalid{color:#b94a48;border-color:#b94a48}.disabled{opacity:.6}.controls{text-align:left}.controls small{display:block;color:#555}.absolute-center{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto}.fade-hide,.fade-show{-webkit-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;-moz-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;-o-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s}.fade-hide{opacity:1}.fade-hide.fade-hide-active{opacity:0}.fade-show{opacity:0}.fade-show.fade-show-active{opacity:1}.umb-loader{width:100%;height:5px;margin-top:0;margin-left:-100%;background-color:white;-webkit-animation-duration:1s;-moz-animation-duration:1s;-ms-animation-duration:1s;-o-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;-ms-animation-iteration-count:infinite;-o-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;-moz-animation-name:bounce_loadingProgressG;-ms-animation-name:bounce_loadingProgressG;-o-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;-moz-animation-timing-function:linear;-ms-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}@-moz-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-webkit-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-ms-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-o-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}.umb-loader-container,.umb-loader-done{position:absolute;bottom:0;left:0;z-index:777;width:0;height:3px;overflow:hidden}.umb-loader-done{right:0;background:white}.permissions-report{display:block;height:320px;padding:0;margin:0;overflow:auto}.permissions-report>li{list-style:none}.permissions-report h4{margin:7px}.upgrade-report{display:block;height:280px;overflow:auto}select{width:320px}#ysod{width:100%;height:500px;overflow:auto;border:0}#starterKits .thumbnails{min-height:128px}#starterKits a.thumbnail{position:relative;height:98px}#starterKits a.thumbnail small{position:absolute;top:10px;left:10px;z-index:50}#starterKits a.thumbnail img{position:relative;z-index:100}
\ No newline at end of file
+@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url('../fonts/opensans/OpenSans-Regular-webfont.eot');src:local('Open Sans'),local('OpenSans'),url('../fonts/opensans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Regular-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url('../fonts/opensans/OpenSans-Semibold-webfont.eot');src:local('Open Sans Semibold'),local('OpenSans-Semibold'),url('../fonts/opensans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Semibold-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg')}.red{color:#9d261d}.blue{color:#2e8aea}.color-red,.color-red i{color:#d90416!important}.color-blue,.color-blue i{color:#04bfbf!important}.color-orange,.color-orange i{color:#d9631e!important}.color-green,.color-green i{color:#04bf67!important}.color-yellow,.color-yellow i{color:#f28729!important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;color:#000;text-align:center;vertical-align:middle;cursor:pointer;background:#f2f2f2;border:1px solid #ccc;box-shadow:none}.btn:hover,.btn:focus{color:#343434;text-decoration:none;background:#e4e4e4;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled],.btn:disabled:hover{cursor:default;border-color:#ccc;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-group>.btn+.dropdown-toggle{-webkit-box-shadow:none;box-shadow:none}.btn-group>.btn:first-child,.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-radius:0}.btn-large{padding:11px 19px;font-size:17.5px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-small{padding:2px 10px;font-size:11.9px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-round{z-index:6666;display:inline-block;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:32px;color:#555;text-align:center;background:#fff;-moz-border-radius:15px;border-radius:15px}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active,.btn-neutral.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:-moz-linear-gradient(top,#53a93f,#53a93f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#53a93f),to(#53a93f));background-image:-webkit-linear-gradient(top,#53a93f,#53a93f);background-image:-o-linear-gradient(top,#53a93f,#53a93f);background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active{background-color:#4fa23c}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;*background-color:#4a9638}.btn-primary:active,.btn-primary.active{background-color:#418431 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#df7f48;*background-color:#df7f48;background-image:-moz-linear-gradient(top,#df7f48,#df7f48);background-image:-webkit-gradient(linear,0 0,0 100%,from(#df7f48),to(#df7f48));background-image:-webkit-linear-gradient(top,#df7f48,#df7f48);background-image:-o-linear-gradient(top,#df7f48,#df7f48);background-image:linear-gradient(to bottom,#df7f48,#df7f48);background-repeat:repeat-x;border-color:#df7f48 #df7f48 #ba5821;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdf7f48',endColorstr='#ffdf7f48',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active{background-color:#dd793f}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;*background-color:#db7032}.btn-warning:active,.btn-warning.active{background-color:#d06324 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ee5f5b;*background-color:#ee5f5b;background-image:-moz-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:-webkit-gradient(linear,0 0,0 100%,from(#ee5f5b),to(#ee5f5b));background-image:-webkit-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:-o-linear-gradient(top,#ee5f5b,#ee5f5b);background-image:linear-gradient(to bottom,#ee5f5b,#ee5f5b);background-repeat:repeat-x;border-color:#ee5f5b #ee5f5b #e51d18;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffee5f5b',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active{background-color:#ed5652}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;*background-color:#ec4844}.btn-danger:active,.btn-danger.active{background-color:#e9322d \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:-moz-linear-gradient(top,#53a93f,#53a93f);background-image:-webkit-gradient(linear,0 0,0 100%,from(#53a93f),to(#53a93f));background-image:-webkit-linear-gradient(top,#53a93f,#53a93f);background-image:-o-linear-gradient(top,#53a93f,#53a93f);background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active{background-color:#4fa23c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;*background-color:#4a9638}.btn-success:active,.btn-success.active{background-color:#418431 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:-moz-linear-gradient(top,#5bc0de,#2f96b4);background-image:-webkit-gradient(linear,0 0,0 100%,from(#5bc0de),to(#2f96b4));background-image:-webkit-linear-gradient(top,#5bc0de,#2f96b4);background-image:-o-linear-gradient(top,#5bc0de,#2f96b4);background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active{background-color:#53bddc}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:-moz-linear-gradient(top,#444,#222);background-image:-webkit-gradient(linear,0 0,0 100%,from(#444),to(#222));background-image:-webkit-linear-gradient(top,#444,#222);background-image:-o-linear-gradient(top,#444,#222);background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active{background-color:#3f3f3f}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}.btn-neutral{color:#fff;color:#656565;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#f5f5f5;*background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f5f5f5));background-image:-webkit-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:-o-linear-gradient(top,#f5f5f5,#f5f5f5);background-image:linear-gradient(to bottom,#f5f5f5,#f5f5f5);background-repeat:repeat-x;border-color:#f5f5f5 #f5f5f5 #cfcfcf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff5f5f5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active{background-color:#f0f0f0}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active,.btn-neutral.active,.btn-neutral.disabled,.btn-neutral[disabled]{color:#fff;*background-color:#e8e8e8}.btn-neutral:active,.btn-neutral.active{background-color:#dcdcdc \9}.btn-neutral:hover,.btn-neutral:focus{color:#656565}.btn-neutral.disabled,.btn-neutral[disabled]{color:#656565;opacity:.65;filter:alpha(opacity=65)}.btn-install{display:block;padding:15px 50px;margin:40px auto;font-size:16px;color:white;background:#2e8aea;border:0}.btn-install:hover{background:#2b8ee3}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.btn-link{color:#000;cursor:pointer;border-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.btn-link:hover,.btn-link:focus{color:#000;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#343434;text-decoration:none}.btn-link-reverse{text-decoration:underline}.btn-link-reverse:hover,.btn-link-reverse:focus{text-decoration:none}.btn-link.-underline{display:inline-block;text-decoration:underline}.btn-link.-underline:hover{text-decoration:none}small.umb-detail,label small,.guiDialogTiny{display:block;font-size:11px;font-weight:normal;color:#b3b3b3!important;text-decoration:none}label.control-label,.control-label{padding:0 10px 0 0!important;font-weight:bold}.umb-status-label{color:#555!important}.controls-row label{padding:0 10px 0 10px;vertical-align:middle}.breadcrumb{display:block;height:30px;margin-top:10px}.breadcrumb li{height:30px;vertical-align:middle}.breadcrumb li a{height:30px;vertical-align:middle}.breadcrumb input{font-size:11px!Important}.form-search{position:relative;padding:0}.form-search a{text-decoration:none;cursor:pointer}.form-search a:hover{color:#555}.form-search h4{color:#555}.form-search small{color:#d9d9d9}.form-search .icon,.form-search .icon-search{position:absolute;top:6px;left:4px;color:#d9d9d9}.form-search .icon-search{pointer-events:none}.form-search input{width:90%;padding:4px 0 4px 16px;padding-left:25px!Important;font-size:17.5px;font-weight:400;line-height:22px;background:#fff;border:1px solid #d9d9d9}.form-search .search-input{font-weight:bold;border-color:#d9d9d9}.form-search .search-input:hover,.form-search .search-input:focus,.form-search .search-input:focus:hover{border-color:#ccc}.form-search .search-input:-moz-placeholder{font-weight:normal}.form-search .search-input:-ms-input-placeholder{font-weight:normal}.form-search .search-input::-webkit-input-placeholder{font-weight:normal}form{margin:0 0 20px}form.-no-margin-bottom{margin-bottom:0}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#343434;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#d9d9d9}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}label{display:inline-block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}input.-full-width-input{width:100%;height:30px;padding:4px 6px;box-sizing:border-box}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;-webkit-transition:border linear .2s,box-shadow linear .2s;-moz-transition:border linear .2s,box-shadow linear .2s;-o-transition:border linear .2s,box-shadow linear .2s;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:none;outline:0;outline:none \9}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#d9d9d9;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;-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)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#d9d9d9}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#d9d9d9}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#d9d9d9}.radio,.checkbox{min-height:20px;padding-left:20px}.radio.no-indent,.checkbox.no-indent{padding-left:0}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-right:5px;margin-left:0}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}.bigInput,.input-large-type{font-size:20px!important}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:706px}input.span11,textarea.span11,.uneditable-input.span11{width:646px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:526px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:406px}input.span6,textarea.span6,.uneditable-input.span6{width:346px}input.span5,textarea.span5,.uneditable-input.span5{width:286px}input.span4,textarea.span4,.uneditable-input.span4{width:226px}input.span3,textarea.span3,.uneditable-input.span3{width:166px}input.span2,textarea.span2,.uneditable-input.span2{width:106px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#f8f8f8}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.show-validation.ng-invalid .control-group.error .control-label,.show-validation.ng-invalid .umb-panel-header-content-wrapper .control-label,.show-validation.ng-invalid .control-group.error .help-block,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-block,.show-validation.ng-invalid .control-group.error .help-inline,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-inline{color:#9d261d}.show-validation.ng-invalid .control-group.error .checkbox.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .checkbox.ng-invalid,.show-validation.ng-invalid .control-group.error .radio.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .radio.ng-invalid,.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{color:#9d261d}.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{border-color:#9d261d}.show-validation.ng-invalid .control-group.error .input-prepend .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-prepend .add-on,.show-validation.ng-invalid .control-group.error .input-append .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-append .add-on{color:#9d261d;background-color:#f8dcda;border-color:#9d261d}.highlight-error{color:#9d261d!important;border-color:#ee5f5b!important}.show-validation .umb-headline-editor-wrapper input:focus:invalid,.show-validation .umb-headline-editor-wrapper textarea:focus:invalid,.show-validation .umb-headline-editor-wrapper select:focus:invalid{color:inherit;border:0;border-color:inherit;-webkit-box-shadow:inherit;-moz-box-shadow:inherit;box-shadow:inherit}.ng-invalid>.umb-headline-editor-wrapper h1{color:#9d261d;cursor:pointer;border-bottom:1px dashed #9d261d}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#5a5a5a}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#f8f8f8;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query,.form-search .input-prepend .search-query{-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.form-search .input-append .search-query{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search .input-append .btn{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{-webkit-border-radius:0 14px 14px 0;-moz-border-radius:0 14px 14px 0;border-radius:0 14px 14px 0}.form-search .input-prepend .btn{-webkit-border-radius:14px 0 0 14px;-moz-border-radius:14px 0 0 14px;border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:top;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}.control-group.-no-margin{margin-bottom:0}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}.umb-panel-buttons .umb-btn-toolbar .btn{position:relative;z-index:1000}@media(max-width:767px){.form-horizontal .control-label{width:100%}.form-horizontal .controls{margin-left:0}}body{margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}a{color:#000;text-decoration:none}a:hover,a:focus{color:#000;text-decoration:underline}.img-rounded{-webkit-border-radius:6px;-moz-border-radius:6px;border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{-webkit-border-radius:500px;-moz-border-radius:500px;border-radius:500px}.row{margin-left:0;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:0}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.span12{width:720px}.span11{width:660px}.span10{width:600px}.span9{width:540px}.span8{width:480px}.span7{width:420px}.span6{width:360px}.span5{width:300px}.span4{width:240px}.span3{width:180px}.span2{width:120px}.span1{width:60px}.offset12{margin-left:720px}.offset11{margin-left:660px}.offset10{margin-left:600px}.offset9{margin-left:540px}.offset8{margin-left:480px}.offset7{margin-left:420px}.offset6{margin-left:360px}.offset5{margin-left:300px}.offset4{margin-left:240px}.offset3{margin-left:180px}.offset2{margin-left:120px}.offset1{margin-left:60px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:0;*margin-left:-0.06944444444444445%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:0}.row-fluid .span12{width:99.99999999999999%;*width:99.93055555555554%}.row-fluid .span11{width:91.66666666666666%;*width:91.59722222222221%}.row-fluid .span10{width:83.33333333333331%;*width:83.26388888888887%}.row-fluid .span9{width:74.99999999999999%;*width:74.93055555555554%}.row-fluid .span8{width:66.66666666666666%;*width:66.59722222222221%}.row-fluid .span7{width:58.33333333333333%;*width:58.263888888888886%}.row-fluid .span6{width:49.99999999999999%;*width:49.93055555555555%}.row-fluid .span5{width:41.66666666666666%;*width:41.597222222222214%}.row-fluid .span4{width:33.33333333333333%;*width:33.263888888888886%}.row-fluid .span3{width:24.999999999999996%;*width:24.930555555555554%}.row-fluid .span2{width:16.666666666666664%;*width:16.59722222222222%}.row-fluid .span1{width:8.333333333333332%;*width:8.263888888888888%}.row-fluid .offset12{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset12:first-child{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset11{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset11:first-child{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset10{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset10:first-child{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset9{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset9:first-child{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset8{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset8:first-child{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset7{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset7:first-child{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset6{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset6:first-child{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset5{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset5:first-child{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset4{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset4:first-child{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset3{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset3:first-child{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset2{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset2:first-child{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset1{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}.row-fluid .offset1:first-child{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:0;padding-left:0;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}.thumbnails{margin-left:0;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:0}.thumbnails>li a:hover{text-decoration:none}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.055);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.055);box-shadow:0 1px 3px rgba(0,0,0,0.055);-webkit-transition:all .2s ease-in-out;-moz-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#000;-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)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none!important}html{background:url('../img/installer.jpg') no-repeat center center fixed;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover}body{width:100%;height:100%;padding:0;margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;text-align:center;vertical-align:middle}#logo{position:absolute;top:20px;left:20px;z-index:777;opacity:.8}#installer{z-index:667;width:750px;height:600px;padding:30px;margin:auto;overflow:hidden;text-align:left;background:white}#overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:666;background:#1d1d1d}.loading #overlay{display:block!important;opacity:.5}#fact{z-index:667;width:750px;height:600px;font-size:25px;line-height:35px;color:#fff;text-align:left;text-shadow:0 0 4px black}#fact h2{padding-bottom:10px;margin-bottom:20px;font-size:35px;color:white;border-bottom:1px solid white}#fact a{color:white}#feedback{position:absolute;right:0;bottom:20px;left:0;z-index:667;height:25px;font-size:14px;line-height:20px;color:#fff;text-align:center;text-shadow:0 0 4px black}h1{padding-bottom:10px;color:#555;border-bottom:1px solid #f8f8f8}.error h1,.error .message,span.error{color:#9d261d}legend{font-size:14px;font-weight:bold}input.ng-dirty.ng-invalid{color:#b94a48;border-color:#b94a48}.disabled{opacity:.6}.controls{text-align:left}.controls small{display:block;color:#555}.absolute-center{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto}.fade-hide,.fade-show{-webkit-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;-moz-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;-o-transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s;transition:all cubic-bezier(0.25,0.46,0.45,0.94) .5s}.fade-hide{opacity:1}.fade-hide.fade-hide-active{opacity:0}.fade-show{opacity:0}.fade-show.fade-show-active{opacity:1}.umb-loader{width:100%;height:5px;margin-top:0;margin-left:-100%;background-color:white;-webkit-animation-duration:1s;-moz-animation-duration:1s;-ms-animation-duration:1s;-o-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;-moz-animation-iteration-count:infinite;-ms-animation-iteration-count:infinite;-o-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;-moz-animation-name:bounce_loadingProgressG;-ms-animation-name:bounce_loadingProgressG;-o-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;-moz-animation-timing-function:linear;-ms-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}@-moz-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-webkit-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-ms-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@-o-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}.umb-loader-container,.umb-loader-done{position:absolute;bottom:0;left:0;z-index:777;width:0;height:3px;overflow:hidden}.umb-loader-done{right:0;background:white}.permissions-report{display:block;height:320px;padding:0;margin:0;overflow:auto}.permissions-report>li{list-style:none}.permissions-report h4{margin:7px}.upgrade-report{display:block;height:280px;overflow:auto}select{width:320px}#ysod{width:100%;height:500px;overflow:auto;border:0}#starterKits .thumbnails{min-height:128px}#starterKits a.thumbnail{position:relative;height:98px}#starterKits a.thumbnail small{position:absolute;top:10px;left:10px;z-index:50}#starterKits a.thumbnail img{position:relative;z-index:100}
\ No newline at end of file
diff --git a/OurUmbraco.Site/umbraco/assets/css/umbraco.css b/OurUmbraco.Site/umbraco/assets/css/umbraco.css
index bb71eec7..2fdf1bf2 100644
--- a/OurUmbraco.Site/umbraco/assets/css/umbraco.css
+++ b/OurUmbraco.Site/umbraco/assets/css/umbraco.css
@@ -1 +1 @@
-@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url('../fonts/opensans/OpenSans-Regular-webfont.eot');src:local('Open Sans'),local('OpenSans'),url('../fonts/opensans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Regular-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url('../fonts/opensans/OpenSans-Semibold-webfont.eot');src:local('Open Sans Semibold'),local('OpenSans-Semibold'),url('../fonts/opensans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Semibold-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg')}.red{color:#9d261d}.blue{color:#2e8aea}.color-red,.color-red i{color:#d90416!important}.color-blue,.color-blue i{color:#04bfbf!important}.color-orange,.color-orange i{color:#d9631e!important}.color-green,.color-green i{color:#04bf67!important}.color-yellow,.color-yellow i{color:#f28729!important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;box-sizing:border-box}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}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}a{color:#000;text-decoration:none}a:hover,a:focus{color:#000;text-decoration:underline}.img-rounded{border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{border-radius:500px}.row{margin-left:0;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:0}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.span12{width:720px}.span11{width:660px}.span10{width:600px}.span9{width:540px}.span8{width:480px}.span7{width:420px}.span6{width:360px}.span5{width:300px}.span4{width:240px}.span3{width:180px}.span2{width:120px}.span1{width:60px}.offset12{margin-left:720px}.offset11{margin-left:660px}.offset10{margin-left:600px}.offset9{margin-left:540px}.offset8{margin-left:480px}.offset7{margin-left:420px}.offset6{margin-left:360px}.offset5{margin-left:300px}.offset4{margin-left:240px}.offset3{margin-left:180px}.offset2{margin-left:120px}.offset1{margin-left:60px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:0;*margin-left:-0.06944444444444445%;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:0}.row-fluid .span12{width:99.99999999999999%;*width:99.93055555555554%}.row-fluid .span11{width:91.66666666666666%;*width:91.59722222222221%}.row-fluid .span10{width:83.33333333333331%;*width:83.26388888888887%}.row-fluid .span9{width:74.99999999999999%;*width:74.93055555555554%}.row-fluid .span8{width:66.66666666666666%;*width:66.59722222222221%}.row-fluid .span7{width:58.33333333333333%;*width:58.263888888888886%}.row-fluid .span6{width:49.99999999999999%;*width:49.93055555555555%}.row-fluid .span5{width:41.66666666666666%;*width:41.597222222222214%}.row-fluid .span4{width:33.33333333333333%;*width:33.263888888888886%}.row-fluid .span3{width:24.999999999999996%;*width:24.930555555555554%}.row-fluid .span2{width:16.666666666666664%;*width:16.59722222222222%}.row-fluid .span1{width:8.333333333333332%;*width:8.263888888888888%}.row-fluid .offset12{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset12:first-child{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset11{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset11:first-child{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset10{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset10:first-child{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset9{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset9:first-child{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset8{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset8:first-child{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset7{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset7:first-child{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset6{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset6:first-child{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset5{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset5:first-child{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset4{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset4:first-child{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset3{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset3:first-child{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset2{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset2:first-child{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset1{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}.row-fluid .offset1:first-child{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:0;padding-left:0;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#d9d9d9}a.muted:hover,a.muted:focus{color:#c0c0c0}.text-warning{color:#fff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-error{color:#fff}a.text-error:hover,a.text-error:focus{color:#e6e6e6}.text-info{color:#fff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-success{color:#fff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:500;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#d9d9d9}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #f8f8f8}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #f8f8f8;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #d9d9d9}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #f8f8f8}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#d9d9d9}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #f8f8f8;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre.code{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#343434;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre.code{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:0}pre.code.prettyprint{margin-bottom:20px}pre.code code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dbdbdb}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #dbdbdb}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #dbdbdb;border-collapse:separate;*border-collapse:collapse;border-left:0;border-radius:0}.table-bordered th,.table-bordered td{border-left:1px solid #dbdbdb}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{border-top-left-radius:0;-moz-border-radius-topleft:0}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{border-top-right-radius:0;-moz-border-radius-topright:0}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{border-top-left-radius:0;-moz-border-radius-topleft:0}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{border-top-right-radius:0;-moz-border-radius-topright:0}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:104px;margin-left:0}.table td.span3,.table th.span3{float:none;width:164px;margin-left:0}.table td.span4,.table th.span4{float:none;width:224px;margin-left:0}.table td.span5,.table th.span5{float:none;width:284px;margin-left:0}.table td.span6,.table th.span6{float:none;width:344px;margin-left:0}.table td.span7,.table th.span7{float:none;width:404px;margin-left:0}.table td.span8,.table th.span8{float:none;width:464px;margin-left:0}.table td.span9,.table th.span9{float:none;width:524px;margin-left:0}.table td.span10,.table th.span10{float:none;width:584px;margin-left:0}.table td.span11,.table th.span11{float:none;width:644px;margin-left:0}.table td.span12,.table th.span12{float:none;width:704px;margin-left:0}.table tbody tr.success>td{background-color:#46a546}.table tbody tr.error>td{background-color:#9d261d}.table tbody tr.warning>td{background-color:#df7f48}.table tbody tr.info>td{background-color:#2e8aea}.table-hover tbody tr.success:hover>td{background-color:#3e933e}.table-hover tbody tr.error:hover>td{background-color:#872119}.table-hover tbody tr.warning:hover>td{background-color:#db7032}.table-hover tbody tr.info:hover>td{background-color:#177de7}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#343434;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#000;background-image:linear-gradient(to bottom,#000,#000);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000',endColorstr='#ff000000',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#000;background-image:linear-gradient(to bottom,#000,#000);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000',endColorstr='#ff000000',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#d9d9d9}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;border-radius:0}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:0;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)}.well-large{padding:24px;border-radius:0}.well-small{padding:9px;border-radius:0}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.btn-group>.btn.large:first-child{margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e4e4e4}.btn-group.open .btn-primary.dropdown-toggle{background-color:#53a93f}.btn-group.open .btn-warning.dropdown-toggle{background-color:#df7f48}.btn-group.open .btn-danger.dropdown-toggle{background-color:#ee5f5b}.btn-group.open .btn-success.dropdown-toggle{background-color:#53a93f}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{border-radius:0}.btn-group-vertical>.btn:last-child{border-radius:0}.btn-group-vertical>.btn-large:first-child{border-radius:0}.btn-group-vertical>.btn-large:last-child{border-radius:0}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;background-color:#df7f48;border:1px solid transparent;border-radius:0}.alert,.alert h4,.alert a{color:#fff}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.close.-align-right{right:0}.alert-success{color:#fff;background-color:#46a546;border-color:transparent}.alert-success h4{color:#fff}.alert-danger,.alert-error{color:#fff;background-color:#9d261d;border-color:transparent}.alert-danger h4,.alert-error h4{color:#fff}.alert-info{color:#fff;background-color:#2e8aea;border-color:transparent}.alert-info h4{color:#fff}.alert-form{color:#555;background-color:#ececec;border:1px solid #555!important}.alert-form.-no-border{border:none!important}.alert-form h4{color:#555}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.list-icons li{max-width:300px;padding-left:35px}.list-icons li>i.icon{padding-right:7px;margin-left:-25px}.icon.handle{color:#d9d9d9}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#f8f8f8}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#d9d9d9;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#000}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:8px;padding-left:8px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:5px;padding-bottom:4px;line-height:20px;color:#555;border:1px solid transparent}.nav-tabs>li>a:hover{color:#000}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#f8f8f8 #f8f8f8 #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#000;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.show-validation .tab-error a,.show-validation .tab-error a:hover,.show-validation .tab-error a:focus{color:#fff!important;background-color:#9d261d!important;border-color:transparent}.show-validation .tab-error a:before{margin-right:5px;font-family:'icomoon';vertical-align:top;content:"\e25d"}.umb-nav-tabs{margin:-8px 0 0 0}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#000}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{border-top-right-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{border-bottom-right-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.dropdown-menu{border-radius:0}.dropdown-menu>li>a{padding:8px 20px}.nav-tabs .dropdown-menu{border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#000;border-bottom-color:#000}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#000;border-bottom-color:#000}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{background-color:#d9d9d9;border-color:#d9d9d9}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#d9d9d9}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#f8f8f8 #ddd #f8f8f8 #f8f8f8}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#f8f8f8 #f8f8f8 #f8f8f8 #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#d9d9d9}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;border-radius:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#343434}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#343434;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active{background-color:#ededed}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;border-radius:1px;box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#343434;border-bottom-color:#343434}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#d9d9d9;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#d9d9d9}.navbar-inverse .navbar-text{color:#d9d9d9}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#d9d9d9}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#d9d9d9;border-bottom-color:#d9d9d9}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#343434;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active{background-color:#101010}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;border-radius:0}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#d9d9d9}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;border-radius:0;*zoom:1;box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#d9d9d9;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#d9d9d9;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#d9d9d9;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;border-radius:6px;outline:0;box-shadow:0 3px 7px rgba(0,0,0,0.3);background-clip:padding-box}.modal.fade{top:-25%;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;border-radius:0 0 6px 6px;*zoom:1;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:0}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.tipmenu .tooltip{position:absolute;right:0;left:0;margin-right:auto;margin-left:auto}.tipmenu:hover .tooltip{opacity:1}.tipmenu:hover .tooltip a{color:white}.tipmenu .tooltip-inner{margin-right:auto;margin-left:auto}.thumbnails{margin-left:0;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:0}.thumbnails>li a:hover{text-decoration:none}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;border-radius:0;box-shadow:0 1px 3px rgba(0,0,0,0.055);transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#000;box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#d9d9d9}.label{border-radius:3px}.badge{padding-right:9px;padding-left:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#fff}.label-important[href],.badge-important[href]{background-color:#e6e6e6}.label-warning,.badge-warning{background-color:#df7f48}.label-warning[href],.badge-warning[href]{background-color:#d06324}.label-success,.badge-success{background-color:#fff}.label-success[href],.badge-success[href]{background-color:#e6e6e6}.label-info,.badge-info{background-color:#fff}.label-info[href],.badge-info[href]{background-color:#e6e6e6}.label-inverse,.badge-inverse{background-color:#343434}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;border-radius:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-sizing:border-box;transition:width .6s ease}.progress .bar+.bar{box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#e69a6f;background-image:linear-gradient(to bottom,#eaad89,#df7f48);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeaad89',endColorstr='#ffdf7f48',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#eaad89;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:0}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#343434;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#f8f8f8;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}html,body{height:100%;overflow:hidden}body{width:100%;height:100%;padding:0;margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}.padded{padding:20px}#layout{position:relative;z-index:1;height:100%;padding:0}#mainwrapper{width:100%;height:100%;margin:0}#contentwrapper,#contentcolumn{position:absolute;top:0;right:0;bottom:0;left:80px;z-index:10;margin:0}#umb-notifications-wrapper{left:80px}#contentcolumn{left:0}#contentcolumn iframe#right{position:relative;display:block;width:100%;height:100%;border:0}#leftcolumn{position:absolute;z-index:20;float:left;width:80px;height:100%}#applications{position:absolute;top:0;bottom:0;left:0;z-index:1000;height:100%;text-align:center}#applications-tray{position:absolute;top:0;bottom:0;left:80px;z-index:900;height:100%;text-align:center}#search-form{position:absolute;top:0;right:0;left:0;z-index:100;display:block;margin:0;overflow:hidden}#search-form .form-search{display:-ms-flexbox;display:-webkit-flex;display:flex;margin-top:17px}#search-form .form-search .umb-search-field{width:auto;min-width:160px;-webkit-flex:1;-ms-flex:1;flex:1}#navigation{position:absolute;top:0;bottom:0;left:80px;z-index:100;height:100%;background:#fff}.navigation-inner-container{position:absolute;top:0;right:0;bottom:0;left:0;z-index:100;padding-top:100px;border-right:1px solid #d9d9d9}#dialog{position:absolute;top:0;left:100%;z-index:50;display:inline-block;min-width:500px}#tree{z-index:100!important;padding:0;overflow:auto}#tree .umb-tree{padding:0 0 20px 0}#search-results{z-index:200}#contextMenu{position:absolute;top:0;left:100%;z-index:50;min-width:250px}#speechbubble{position:absolute;right:0;bottom:100px;left:0;z-index:1060;padding:0;margin:auto;background:0;border:0;border-bottom:0;border-radius:0}.ui-resizable-e{position:absolute;top:0;right:-5px;bottom:0;z-index:9999!important;width:4px;cursor:e-resize;background-color:#f8f8f8;border:solid 1px #d9d9d9;border-top:0;border-bottom:0}@media(min-width:1101px){#contentwrapper,#umb-notifications-wrapper{left:440px}#speechbubble{left:360px}}.emptySection #contentwrapper,.emptySection #umb-notifications-wrapper{left:80px}.emptySection #speechbubble{left:0}.emptySection #navigation{display:none}.login-only #speechbubble{left:0!important;z-index:10000}.login-only #speechbubble ul{padding-left:20px}@media(max-width:767px){#leftcolumn{width:60px}#applications ul.sections{width:60px}ul.sections.sections-tray{width:60px}#applications-tray{left:60px}#navigation{left:60px}#contentwrapper,#contentcolumn,#umb-notifications-wrapper{left:30px}#umbracoMainPageBody .umb-modal-left.fade.in{margin-left:60px}}@media(max-width:500px){#leftcolumn{width:40px}#applications ul.sections{width:40px}ul.sections li [class^="icon-"]:before{font-size:25px!important}#applications ul.sections li.avatar a img{width:25px}ul.sections a span{display:none!important}#applications ul.sections-tray{width:40px}ul.sections.sections-tray{width:40px}#applications-tray{left:40px}#navigation{left:40px}#contentwrapper,#contentcolumn,#umb-notifications-wrapper{left:20px}#umbracoMainPageBody .umb-modal-left.fade.in{width:85%!important;margin-left:40px}}.shadow-depth-1{box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.shadow-depth-2{box-shadow:0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23)}.shadow-depth-3{box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23)}.shadow-depth-4{box-shadow:0 14px 28px rgba(0,0,0,0.25),0 10px 10px rgba(0,0,0,0.22)}.shadow-depth-5{box-shadow:0 19px 38px rgba(0,0,0,0.3),0 15px 12px rgba(0,0,0,0.22)}.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,0.75,1);transform:scale3d(1.25,0.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,0.85,1);transform:scale3d(1.15,0.85,1)}65%{-webkit-transform:scale3d(0.95,1.05,1);transform:scale3d(0.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,0.95,1);transform:scale3d(1.05,0.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,0.75,1);transform:scale3d(1.25,0.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,0.85,1);transform:scale3d(1.15,0.85,1)}65%{-webkit-transform:scale3d(0.95,1.05,1);transform:scale3d(0.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,0.95,1);transform:scale3d(1.05,0.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-animation-name:swing;animation-name:swing;-webkit-transform-origin:top center;transform-origin:top center}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg);transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg);transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(0.97,0.97,0.97);transform:scale3d(0.97,0.97,0.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(0.97,0.97,0.97);transform:scale3d(0.97,0.97,0.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}}.bounceOut{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(0.95,0.95,0.95);transform:perspective(400px) scale3d(0.95,0.95,0.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(0.95,0.95,0.95);transform:perspective(400px) scale3d(0.95,0.95,0.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-animation-name:flip;animation-name:flip;-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible}@-webkit-keyframes flipInX{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-animation-name:flipInX;animation-name:flipInX;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipInY{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-animation-name:flipInY;animation-name:flipInY;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg)}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg)}}.flipOutX{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg)}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg)}}.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes lightSpeedIn{0%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg)}60%{opacity:1;-webkit-transform:skewX(20deg);transform:skewX(20deg)}80%{opacity:1;-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes lightSpeedIn{0%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg)}60%{opacity:1;-webkit-transform:skewX(20deg);transform:skewX(20deg)}80%{opacity:1;-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}100%{opacity:1;-webkit-transform:none;transform:none}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg)}}@keyframes lightSpeedOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg)}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);-webkit-transform-origin:center;transform-origin:center}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:center;transform-origin:center}}@keyframes rotateIn{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);-webkit-transform-origin:center;transform-origin:center}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:center;transform-origin:center}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateInDownLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateInDownRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateInUpLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateInUpRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{opacity:1;-webkit-transform-origin:center;transform-origin:center}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);-webkit-transform-origin:center;transform-origin:center}}@keyframes rotateOut{0%{opacity:1;-webkit-transform-origin:center;transform-origin:center}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);-webkit-transform-origin:center;transform-origin:center}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateOutDownLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateOutDownRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateOutUpLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateOutUpRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}40%,80%{opacity:1;-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}100%{opacity:0;-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0)}}@keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}40%,80%{opacity:1;-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}100%{opacity:0;-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0)}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(-2000px,0,0);transform:scale(0.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(-2000px,0,0);transform:scale(0.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(2000px,0,0);transform:scale(0.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(2000px,0,0);transform:scale(0.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}.animated.-half-second{-webkit-animation-duration:.5s;animation-duration:.5s}.slide-in-left.ng-hide-remove{-webkit-animation:fadeInLeft .6s;animation:fadeInLeft .6s}.slide-in-left.ng-hide-add{display:block!important;-webkit-animation:fadeOutLeft .6s;animation:fadeOutLeft .6s}.slide-in-right.ng-hide-remove{-webkit-animation:fadeInRight .6s;animation:fadeInRight .6s}.slide-in-right.ng-hide-add{display:block!important;-webkit-animation:fadeOutRight .6s;animation:fadeOutRight .6s}.slide-in-up.ng-hide-remove{-webkit-animation:fadeInUp .6s;animation:fadeInUp .6s}.slide-in-up.ng-hide-add{display:block!important;-webkit-animation:fadeOutDown .6s;animation:fadeOutDown .6s}.tree-node-delete-leave{position:relative;display:block;-webkit-animation:leave 600ms cubic-bezier(0.445,0.05,0.55,0.95);animation:leave 600ms cubic-bezier(0.445,0.05,0.55,0.95)}@-webkit-keyframes leave{to{bottom:-70px;height:0;opacity:0}25%{bottom:15px}from{bottom:0;height:30px;opacity:1}}@keyframes leave{to{bottom:-70px;height:0;opacity:0}25%{bottom:15px}from{bottom:0;height:30px;opacity:1}}.tree-node-delete-leave *{color:#9d261d!important}.tree-node-slide-up{top:0;opacity:1;transition:700ms cubic-bezier(0,0,0.58,1) all}.tree-node-slide-up *{font-size:100%;transition:font-size 700ms}.tree-node-slide-up.tree-node-slide-up-hide-active{top:-100px;opacity:0}.tree-node-slide-up.tree-node-slide-up-hide-active *{font-size:120%}.tree-fade-out-hide,.tree-fade-out-show,.tree-fade-out-hide div:not(.tree-node-slide-up-hide-active),.tree-fade-out-show div:not(.tree-node-slide-up-hide-active){transition:700ms cubic-bezier(0.075,0.82,0.165,1) all}.tree-fade-out-show.tree-fade-out-show-active div:not(.tree-node-slide-up-hide-active){opacity:1}.tree-fade-out-hide.tree-fade-out-hide-active div:not(.tree-node-slide-up-hide-active){opacity:0}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;color:#000;text-align:center;vertical-align:middle;cursor:pointer;background:#f2f2f2;border:1px solid #ccc;box-shadow:none}.btn:hover,.btn:focus{color:#343434;text-decoration:none;background:#e4e4e4;background-position:0 -15px;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled],.btn:disabled:hover{cursor:default;border-color:#ccc;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.btn-group>.btn+.dropdown-toggle{box-shadow:none}.btn-group>.btn:first-child,.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-radius:0}.btn-large{padding:11px 19px;font-size:17.5px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;border-radius:0}.btn-small{padding:2px 10px;font-size:11.9px;border-radius:0}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;border-radius:0}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-round{z-index:6666;display:inline-block;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:32px;color:#555;text-align:center;background:#fff;border-radius:15px}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active,.btn-neutral.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active{background-color:#4fa23c}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;*background-color:#4a9638}.btn-primary:active,.btn-primary.active{background-color:#418431 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#df7f48;*background-color:#df7f48;background-image:linear-gradient(to bottom,#df7f48,#df7f48);background-repeat:repeat-x;border-color:#df7f48 #df7f48 #ba5821;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdf7f48',endColorstr='#ffdf7f48',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active{background-color:#dd793f}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;*background-color:#db7032}.btn-warning:active,.btn-warning.active{background-color:#d06324 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ee5f5b;*background-color:#ee5f5b;background-image:linear-gradient(to bottom,#ee5f5b,#ee5f5b);background-repeat:repeat-x;border-color:#ee5f5b #ee5f5b #e51d18;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffee5f5b',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active{background-color:#ed5652}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;*background-color:#ec4844}.btn-danger:active,.btn-danger.active{background-color:#e9322d \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active{background-color:#4fa23c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;*background-color:#4a9638}.btn-success:active,.btn-success.active{background-color:#418431 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active{background-color:#53bddc}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active{background-color:#3f3f3f}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}.btn-neutral{color:#fff;color:#656565;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#f5f5f5;*background-color:#f5f5f5;background-image:linear-gradient(to bottom,#f5f5f5,#f5f5f5);background-repeat:repeat-x;border-color:#f5f5f5 #f5f5f5 #cfcfcf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff5f5f5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active{background-color:#f0f0f0}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active,.btn-neutral.active,.btn-neutral.disabled,.btn-neutral[disabled]{color:#fff;*background-color:#e8e8e8}.btn-neutral:active,.btn-neutral.active{background-color:#dcdcdc \9}.btn-neutral:hover,.btn-neutral:focus{color:#656565}.btn-neutral.disabled,.btn-neutral[disabled]{color:#656565;opacity:.65;filter:alpha(opacity=65)}.btn-install{display:block;padding:15px 50px;margin:40px auto;font-size:16px;color:white;background:#2e8aea;border:0}.btn-install:hover{background:#2b8ee3}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;box-shadow:none}.btn-link{color:#000;cursor:pointer;border-color:transparent;border-radius:0}.btn-link:hover,.btn-link:focus{color:#000;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#343434;text-decoration:none}.btn-link-reverse{text-decoration:underline}.btn-link-reverse:hover,.btn-link-reverse:focus{text-decoration:none}.btn-link.-underline{display:inline-block;text-decoration:underline}.btn-link.-underline:hover{text-decoration:none}small.umb-detail,label small,.guiDialogTiny{display:block;font-size:11px;font-weight:normal;color:#b3b3b3!important;text-decoration:none}label.control-label{padding:0 10px 0 0!important;font-weight:bold}.umb-status-label{color:#555!important}.controls-row label{padding:0 10px 0 10px;vertical-align:middle}.breadcrumb{display:block;height:30px;margin-top:10px}.breadcrumb li{height:30px;vertical-align:middle}.breadcrumb li a{height:30px;vertical-align:middle}.breadcrumb input{font-size:11px!Important}.form-search{position:relative;padding:0}.form-search a{text-decoration:none;cursor:pointer}.form-search a:hover{color:#555}.form-search h4{color:#555}.form-search small{color:#d9d9d9}.form-search .icon,.form-search .icon-search{position:absolute;top:6px;left:4px;color:#d9d9d9}.form-search .icon-search{pointer-events:none}.form-search input{width:90%;padding:4px 0 4px 16px;padding-left:25px!Important;font-size:17.5px;font-weight:400;line-height:22px;background:#fff;border:1px solid #d9d9d9}.form-search .search-input{font-weight:bold;border-color:#d9d9d9}.form-search .search-input:hover,.form-search .search-input:focus,.form-search .search-input:focus:hover{border-color:#ccc}.form-search .search-input:-moz-placeholder{font-weight:normal}.form-search .search-input:-ms-input-placeholder{font-weight:normal}.form-search .search-input::-webkit-input-placeholder{font-weight:normal}form{margin:0 0 20px}form.-no-margin-bottom{margin-bottom:0}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#343434;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#d9d9d9}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}label{display:block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;border-radius:0}input.-full-width-input{width:100%;height:30px;padding:4px 6px;box-sizing:border-box}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:none;outline:0;outline:none \9}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#d9d9d9;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#d9d9d9}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#d9d9d9}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#d9d9d9}.radio,.checkbox{min-height:20px;padding-left:20px}.radio.no-indent,.checkbox.no-indent{padding-left:0}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-right:5px;margin-left:0}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}.bigInput,.input-large-type{font-size:20px!important}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:706px}input.span11,textarea.span11,.uneditable-input.span11{width:646px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:526px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:406px}input.span6,textarea.span6,.uneditable-input.span6{width:346px}input.span5,textarea.span5,.uneditable-input.span5{width:286px}input.span4,textarea.span4,.uneditable-input.span4{width:226px}input.span3,textarea.span3,.uneditable-input.span3{width:166px}input.span2,textarea.span2,.uneditable-input.span2{width:106px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#f8f8f8}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.show-validation.ng-invalid .control-group.error .control-label,.show-validation.ng-invalid .umb-panel-header-content-wrapper .control-label,.show-validation.ng-invalid .control-group.error .help-block,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-block,.show-validation.ng-invalid .control-group.error .help-inline,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-inline{color:#9d261d}.show-validation.ng-invalid .control-group.error .checkbox.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .checkbox.ng-invalid,.show-validation.ng-invalid .control-group.error .radio.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .radio.ng-invalid,.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{color:#9d261d}.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{border-color:#9d261d}.show-validation.ng-invalid .control-group.error .input-prepend .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-prepend .add-on,.show-validation.ng-invalid .control-group.error .input-append .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-append .add-on{color:#9d261d;background-color:#f8dcda;border-color:#9d261d}.highlight-error{color:#9d261d!important;border-color:#ee5f5b!important}.show-validation .umb-headline-editor-wrapper input:focus:invalid,.show-validation .umb-headline-editor-wrapper textarea:focus:invalid,.show-validation .umb-headline-editor-wrapper select:focus:invalid{color:inherit;border:0;border-color:inherit;box-shadow:inherit}.ng-invalid>.umb-headline-editor-wrapper h1{color:#9d261d;cursor:pointer;border-bottom:1px dashed #9d261d}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#5a5a5a}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#f8f8f8;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;border-radius:0}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;border-radius:0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin:0;border-radius:0}.form-search .input-append .search-query,.form-search .input-prepend .search-query{border-radius:0}.form-search .input-append .search-query{border-radius:14px 0 0 14px}.form-search .input-append .btn{border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{border-radius:0 14px 14px 0}.form-search .input-prepend .btn{border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:top;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}.control-group.-no-margin{margin-bottom:0}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}.umb-panel-buttons .umb-btn-toolbar .btn{position:relative;z-index:1000}@media(max-width:767px){.form-horizontal .control-label{width:100%}.form-horizontal .controls{margin-left:0}}.umb-modalcolumn{background:white}.umb-modalcolumn-header{height:94px;padding:5px 20px 0 20px;white-space:nowrap;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-modalcolumn-header h1{padding-top:10px!important;margin:0;font-size:17.5px;font-weight:400;white-space:nowrap}.umb-modalcolumn-body{position:absolute;top:100px;right:0;bottom:0;left:0;padding:0;overflow:auto;background:#fff}.no-padding .umb-modalcolumn-body{padding:0}.umb-modalcolumn .umb-modalcolumn-header .btn{position:absolute;top:13px;right:15px}.umb-modalcolumn iframe.auto-expand,.umb-modal iframe.auto-expand{position:absolute;top:0;right:0;bottom:0;left:0;padding:0;margin:0;border:0}.umb-modal .umb-loader-wrapper,.umb-modalcolumn .umb-loader-wrapper,.umb-dialog .umb-loader-wrapper{position:relative;margin:20px -20px}.umb-modal-left .umb-panel-header .umb-headline,.umb-modal-left .umb-panel-header h1{width:auto;padding-left:0}.umb-dialog{position:absolute;top:0;right:0;bottom:0;left:0;padding:0;outline:0}.umb-dialog .umb-btn-toolbar{padding:15px 20px 10px 20px;margin-top:30px;clear:both;text-align:right;background:#fff}.umb-dialog .umb-btn-toolbar .umb-control-group{padding:none;border:0}.umb-dialog-body{position:absolute;top:0;right:0;bottom:90px;left:0;overflow:auto}.umb-dialog-body .umb-pane{margin-top:15px}.umb-dialog-footer{position:absolute;right:0;bottom:0;left:0;height:60px;overflow:auto;text-align:right;border-top:#efefef 1px solid}.umbracoDialog{width:auto!Important;height:auto!Important;padding:20px}.umbracoDialog .umb-pane{margin-top:0;margin-right:0;margin-left:0}.umbracoDialog .umb-dialog-body .umb-pane{margin-top:20px;margin-right:20px;margin-left:20px}.umbracoDialog form{height:100%}.umbracoDialog .controls-row,.umb-modal .controls-row{margin-left:0!important}.modal.fade.in{border:none!important;border-radius:none!important}.umb-modal.fade{top:0!important;left:-100%!important;width:0!important;height:100%!important;outline:0;transition:opacity .3s linear,top .3s ease-out}.umb-modal.fade.in{top:0!important;left:100%!important;display:block;width:440px!important;height:100%!important;margin-left:-440px}.umb-modal-left.fade{top:0!important;left:-100%!important;width:0!important;height:100%!important;transition:opacity .3s linear,top .3s ease-out}.umb-modal-left.fade.in{top:0!important;left:0!important;display:block;width:440px!important;height:100%!important;margin-left:80px}.umb-modal .umb-panel-header{height:auto;padding:20px;background:#fff;border:0}.umb-modal .umb-panel-body{padding:0 20px 0 20px}.umb-modal.fade.in.wide{width:640px!important;margin-left:-640px}.umb-modal i{font-size:14px}.umb-modal .breadcrumb{padding:0;background:0}.umb-modal .breadcrumb input{height:12px}.umb-modal.ysod{z-index:10000}.umb-panel{position:absolute;top:0;right:0;bottom:0;left:0;background:white}.umb-panel-nobody{padding-top:100px;overflow:auto}.umb-panel-header{position:absolute;top:0;right:0;left:0;height:99px;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-panel-body{position:absolute;top:101px;right:0;bottom:0;left:0;overflow:auto;clear:both}.umb-panel-body.no-header{top:20px}.umb-panel-body.with-footer{bottom:90px}.umb-mediapicker-upload{display:-ms-flexbox;display:-webkit-flex;display:flex}.umb-mediapicker-upload .form-search{-webkit-flex:1;-ms-flex:1;flex:1}.umb-mediapicker-upload .upload-button{margin-left:16px}.umb-panel.editor-breadcrumb .umb-panel-body,.umb-panel.editor-breadcrumb .umb-bottom-bar{bottom:31px!important}.umb-panel-header .umb-headline,.umb-panel-header h1{width:100%;height:auto;padding:3px 5px;margin:15px 0 0 20px;font-size:16px;line-height:1.4;background:0;border:0;border:1px solid #f8f8f8}.umb-panel-header .umb-headline:focus,.umb-panel-header .umb-headline:active{background-color:#fff;border:1px solid #d9d9d9}.umb-headline-editor-wrapper{position:relative}.umb-headline-editor-wrapper .help-inline{position:absolute;top:25px;right:0;font-size:10px;color:#9d261d}.umb-panel-header p{margin:0 20px}.umb-btn-toolbar .dimmed,.umb-dimmed{opacity:.6}.umb-headline-editor-wrapper input{padding:0 0 2px 0;margin:-6px 0 0 0;line-height:normal;letter-spacing:-0.01em;color:#000;background:0;border:0;border:1px solid transparent;border-radius:0}.umb-headline-editor-wrapper input.ng-invalid{color:#9d261d}.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder,.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder,.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder{line-height:22px;color:#9d261d}.umb-panel-header-meta{height:50px}.umb-panel-header .umb-btn-toolbar{float:right;padding:5px 20px 0 0}.umb-panel-footer{position:absolute;right:0;bottom:0;left:0;z-index:999;padding:20px;margin:0}.umb-btn-toolbar .dropdown-menu{right:0;left:auto;z-index:6020;padding:0;border-radius:0;box-shadow:none}.umb-btn-toolbar .dropdown-menu small{display:block;padding:10px 20px;background:#fef9db}.umb-btn-toolbar .dropdown-menu .btn{width:80px;margin:20px 29px}.umb-bottom-bar{position:fixed;right:40px;bottom:0;left:100px;z-index:6010;padding:10px 0 10px 0;background:white;border-top:1px solid #f8f8f8;box-shadow:0 -18px 20px #fff}@media(min-width:1101px){.umb-bottom-bar{left:460px}}@media(max-width:767px){.umb-bottom-bar{left:80px}}@media(max-width:500px){.umb-bottom-bar{left:60px}}.umb-tab-buttons{padding-left:0}.umb-tab-buttons>.btn-group:not([style*="display:none"]):not([style*="display: none"]){margin-left:0}@media(min-width:768px){.umb-tab-buttons{padding-left:180px}}.umb-tab-pane{padding-bottom:90px}.tab-content{overflow:visible}.umb-panel-footer-nav{position:absolute;right:0;bottom:0;left:0;display:block;height:30px;margin:0;overflow:hidden;background:#f8f8f8;border-top:#d9d9d9 1px solid}.umb-panel-footer-nav li a{position:relative;display:block;float:left;height:30px;padding:8px 0 8px 30px;margin:0 1px 0 0;font-size:11px;color:#555;text-align:center;text-decoration:none;background:#f8f8f8;border-radius:0}.umb-panel-footer-nav li a:after{position:absolute;top:0;right:-16px;z-index:1;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid #f8f8f8;content:""}.umb-panel-footer-nav li a:before{position:absolute;top:0;left:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid #d9d9d9;content:""}.umb-panel-footer-nav li:first-child a{padding-left:20px}.umb-panel-footer-nav li:first-child a:before{display:none}.umb-panel-footer-nav li:last-child a:after{display:none}.umb-dialog .muted,.umb-panel .muted{color:#d9d9d9}.umb-dialog a.muted:hover,.umb-dialog a.muted:focus,.umb-panel a.muted:hover,.umb-panel a.muted:focus{color:#c0c0c0}.umb-dialog .text-warning,.umb-panel .text-warning{color:#df7f48}.umb-dialog a.text-warning:hover,.umb-dialog a.text-warning:focus,.umb-panel a.text-warning:hover,.umb-panel a.text-warning:focus{color:#d06324}.umb-dialog .text-error,.umb-panel .text-error{color:#9d261d}.umb-dialog a.text-error:hover,.umb-dialog a.text-error:focus,.umb-panel a.text-error:hover,.umb-panel a.text-error:focus{color:#721c15}.umb-dialog .text-info,.umb-panel .text-info{color:#2e8aea}.umb-dialog a.text-info:hover,.umb-dialog a.text-info:focus,.umb-panel a.text-info:hover,.umb-panel a.text-info:focus{color:#1571d0}.umb-dialog .text-success,.umb-panel .text-success{color:#46a546}.umb-dialog a.text-success:hover,.umb-dialog a.text-success:focus,.umb-panel a.text-success:hover,.umb-panel a.text-success:focus{color:#378137}.external-logins form{margin:0}.external-logins button{margin:5px}.umb-panel-header-content-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;height:100px;padding:0 20px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-panel-header-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:1;-ms-flex:1;flex:1}.umb-panel-header-content.-top-position{position:relative;top:-12px}.umb-panel-header-left-side{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-panel-header-icon{display:-webkit-flex;display:-ms-flexbox;display:flex;height:55px;margin-right:5px;cursor:pointer;background:#fff;border:1px solid #ececec;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:0 0 55px;-ms-flex:0 0 55px;flex:0 0 55px}.umb-panel-header-title-wrapper{position:relative;width:80%}.umb-panel-header-alias{position:absolute;top:5px;right:10px}.umb-panel-header-alias .umb-locked-field{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-panel-header-alias .umb-locked-field,.umb-panel-header-alias .umb-locked-field .umb-locked-field__wrapper{margin-bottom:0}.umb-panel-header-alias .umb-validation-label:after{visibility:hidden}.umb-panel-header-alias .umb-locked-field:after{display:none}.umb-panel-header-icon.-placeholder{border:1px dashed #d9d9d9}.umb-panel-header-icon .icon{font-size:35px;color:#d9d9d9}.umb-panel-header-icon-text{font-size:10px;font-weight:bold;color:#2e8aea}.umb-panel-header .umb-nav-tabs{bottom:-1px}input.umb-panel-header-name-input{width:100%;height:30px;margin-bottom:0;font-size:15px;font-weight:bold;line-height:30px;color:#000;border-color:#ececec;box-sizing:border-box}input.umb-panel-header-name-input:hover{background:#fff;border:1px solid #ccc}input.umb-panel-header-name-input.name-is-empty{background:#fff;border:1px dashed #d9d9d9}.umb-panel-header-name{font-size:16px;font-weight:bold}input.umb-panel-header-description{width:100%;height:25px;margin-bottom:0;font-size:12px;line-height:25px;background:transparent;border-color:transparent;box-sizing:border-box}input.umb-panel-header-description:hover{background:#fff;border-color:#ccc}.umb-editor-drawer-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-editor-drawer-content__right-side{margin-left:auto}.umb-editor-drawer-content__left-side{margin-right:auto}ul.sections{display:block;width:80px;height:100%;margin:0;background:#1d1d1d;border-right:1px solid #343434}ul.sections li{display:block;border-left:4px #1d1d1d solid;transition:all .3s linear}ul.sections li [class^="icon-"]:before,ul.sections li [class*=" icon-"]:before{margin:1px 0 0 0;font-size:30px;opacity:.4;transition:all .3s linear}ul.sections:hover li [class^="icon-"]:before,ul.sections:hover li [class*=" icon-"]:before{opacity:1}ul.sections li a{display:block;width:100%;height:100%;padding:20px 4px 4px 0;margin:0 0 0 -4px;color:#d9d9d9;text-align:center;text-decoration:none;cursor:pointer;border-bottom:1px solid #343434}ul.sections a span{display:block;font-size:10px;line-height:1.4em;opacity:0;transition:all .3s linear}ul.sections:hover a span{opacity:1}ul.sections li.avatar{height:75px;padding:22px 0 2px 0;margin:0 0 0 -4px;text-align:center;border-bottom:1px solid #343434}ul.sections li.avatar a{width:40px;height:40px;padding:0;margin:0 auto;border:0}ul.sections li.avatar a img{width:30px;border-radius:50%}.faded ul.sections li{opacity:.4}ul.sections li.current,ul.sections li:hover{border-left:4px #df7f48 solid}ul.sections li.avatar.current,ul.sections li.avatar:hover{border-left:4px #1d1d1d solid}ul.sections li.current a{padding-left:0}ul.sections li.help{position:absolute;bottom:0;left:0;display:block;width:100%;margin:0}ul.sections li.help a{border-bottom:0}li.expand a,li.expand{border:none!Important}li.expand.open>a>i.icon{transition:all 1s!important}li.expand.open>a>i.icon:before{-webkit-transform:rotate(180deg)!important;transform:rotate(180deg)!important}ul.sections-tray{width:80px;padding-top:99px}ul.sections-tray>li:first-child>a{border-top:1px solid #343434}ul.sections-tray>li{border-left-width:5px}ul.sections-tray>li.current,ul.sections-tray>li:hover{border-left-width:5px}@font-face{font-family:'icomoon';font-style:normal;font-weight:normal;src:url('../fonts/helveticons/helveticons.eot');src:url('../fonts/helveticons/helveticons.eot?#iefix') format('embedded-opentype'),url('../fonts/helveticons/helveticons.ttf') format('truetype'),url('../fonts/helveticons/helveticons.svg#icomoon') format('svg')}[class^="icon-"],[class*=" icon-"]{*margin-right:.3em;font-family:icomoon;-webkit-font-smoothing:antialiased;font-style:normal;font-weight:normal;text-decoration:inherit}[class^="icon-"]:before,[class*=" icon-"]:before{display:inline-block;text-decoration:inherit;speak:none}i.large{font-size:32px}i.medium{font-size:24px}i.small{font-size:14px}.icon-zoom-out:before{content:"\e000"}.icon-truck:before{content:"\e001"}.icon-zoom-in:before{content:"\e002"}.icon-zip:before{content:"\e003"}.icon-axis-rotation:before{content:"\e004"}.icon-yen-bag:before{content:"\e005"}.icon-axis-rotation-2:before{content:"\e006"}.icon-axis-rotation-3:before{content:"\e007"}.icon-wrench:before{content:"\e008"}.icon-wine-glass:before{content:"\e009"}.icon-wrong:before{content:"\e00a"}.icon-windows:before{content:"\e00b"}.icon-window-sizes:before{content:"\e00c"}.icon-window-popin:before{content:"\e00d"}.icon-wifi:before{content:"\e00e"}.icon-width:before{content:"\e00f"}.icon-weight:before{content:"\e010"}.icon-war:before{content:"\e011"}.icon-wand:before{content:"\e012"}.icon-wallet:before{content:"\e013"}.icon-wall-plug:before{content:"\e014"}.icon-voice:before{content:"\e016"}.icon-video:before{content:"\e017"}.icon-vcard:before{content:"\e018"}.icon-utilities:before{content:"\e019"}.icon-users:before{content:"\e01a"}.icon-users-alt:before{content:"\e01b"}.icon-user:before{content:"\e01c"}.icon-user-glasses:before{content:"\e01d"}.icon-user-females:before{content:"\e01e"}.icon-user-females-alt:before{content:"\e01f"}.icon-user-female:before{content:"\e020"}.icon-usb:before{content:"\e021"}.icon-usb-connector:before{content:"\e022"}.icon-unlocked:before{content:"\e023"}.icon-universal:before{content:"\e024"}.icon-undo:before{content:"\e025"}.icon-umbrella:before{content:"\e026"}.icon-umb-deploy:before{content:"\e027"}.icon-umb-contour:before,.traycontour:before{content:"\e028"}.icon-umb-settings:before,.traysettings:before{content:"\e029"}.icon-umb-users:before,.trayuser:before,.trayusers:before{content:"\e02a"}.icon-umb-media:before,.traymedia:before{content:"\e02b"}.icon-umb-content:before,.traycontent:before{content:"\e02c"}.icon-umb-developer:before,.traydeveloper:before{content:"\e02d"}.icon-umb-members:before,.traymember:before{content:"\e015"}.icon-umb-translation:before,.traytranslation:before{content:"\e1fd"}.icon-tv:before{content:"\e02e"}.icon-tv-old:before{content:"\e02f"}.icon-trophy:before{content:"\e030"}.icon-tree:before{content:"\e031"}.icon-trash:before{content:"\e032"}.icon-trash-alt:before{content:"\e033"}.icon-trash-alt-2:before{content:"\e034"}.icon-train:before{content:"\e035"}.icon-trafic:before{content:"\e036"}.icon-traffic-alt:before{content:"\e037"}.icon-top:before{content:"\e038"}.icon-tools:before{content:"\e039"}.icon-timer:before{content:"\e03a"}.icon-time:before{content:"\e03b"}.icon-t-shirt:before{content:"\e03c"}.icon-tab-key:before{content:"\e03d"}.icon-tab:before{content:"\e03e"}.icon-tactics:before{content:"\e03f"}.icon-tag:before{content:"\e040"}.icon-tags:before{content:"\e041"}.icon-takeaway-cup:before{content:"\e042"}.icon-target:before{content:"\e043"}.icon-temperatrure-alt:before{content:"\e044"}.icon-temperature:before{content:"\e045"}.icon-terminal:before{content:"\e046"}.icon-theater:before{content:"\e047"}.icon-theif:before{content:"\e048"}.icon-thought-bubble:before{content:"\e049"}.icon-thumb-down:before{content:"\e04a"}.icon-thumb-up:before{content:"\e04b"}.icon-thumbnail-list:before{content:"\e04c"}.icon-thumbnails-small:before{content:"\e04d"}.icon-thumbnails:before{content:"\e04e"}.icon-ticket:before{content:"\e04f"}.icon-sync:before{content:"\e050"}.icon-sweatshirt:before{content:"\e051"}.icon-sunny:before{content:"\e052"}.icon-stream:before{content:"\e053"}.icon-store:before{content:"\e054"}.icon-stop:before{content:"\e055"}.icon-stop-hand:before{content:"\e056"}.icon-stop-alt:before{content:"\e057"}.icon-stamp:before{content:"\e058"}.icon-stacked-disks:before{content:"\e059"}.icon-ssd:before{content:"\e05a"}.icon-squiggly-line:before{content:"\e05b"}.icon-sprout:before{content:"\e05c"}.icon-split:before{content:"\e05d"}.icon-split-alt:before{content:"\e05e"}.icon-speed-gauge:before{content:"\e05f"}.icon-speaker:before{content:"\e060"}.icon-sound:before{content:"\e061"}.icon-spades:before{content:"\e062"}.icon-sound-waves:before{content:"\e063"}.icon-shipping-box:before{content:"\e064"}.icon-shipping:before{content:"\e065"}.icon-shoe:before{content:"\e066"}.icon-shopping-basket-alt-2:before{content:"\e067"}.icon-shopping-basket:before{content:"\e068"}.icon-shopping-basket-alt:before{content:"\e069"}.icon-shorts:before{content:"\e06a"}.icon-shuffle:before{content:"\e06b"}.icon-sience:before{content:"\e06c"}.icon-simcard:before{content:"\e06d"}.icon-single-note:before{content:"\e06e"}.icon-sitemap:before{content:"\e06f"}.icon-sleep:before{content:"\e070"}.icon-slideshow:before{content:"\e071"}.icon-smiley-inverted:before{content:"\e072"}.icon-smiley:before{content:"\e073"}.icon-snow:before{content:"\e074"}.icon-sound-low:before{content:"\e075"}.icon-sound-medium:before{content:"\e076"}.icon-sound-off:before{content:"\e077"}.icon-shift:before{content:"\e078"}.icon-shield:before{content:"\e079"}.icon-sharing-iphone:before{content:"\e07a"}.icon-share:before{content:"\e07b"}.icon-share-alt:before{content:"\e07c"}.icon-share-alt-2:before{content:"\e07d"}.icon-settings:before{content:"\e07e"}.icon-settings-alt:before{content:"\e07f"}.icon-settings-alt-2:before{content:"\e080"}.icon-server:before{content:"\e081"}.icon-server-alt:before{content:"\e082"}.icon-sensor:before{content:"\e083"}.icon-security-camera:before{content:"\e084"}.icon-search:before{content:"\e085"}.icon-scull:before{content:"\e086"}.icon-script:before{content:"\e087"}.icon-script-alt:before{content:"\e088"}.icon-screensharing:before{content:"\e089"}.icon-school:before{content:"\e08a"}.icon-scan:before{content:"\e08b"}.icon-refresh:before{content:"\e08c"}.icon-remote:before{content:"\e08d"}.icon-remove:before{content:"\e08e"}.icon-repeat-one:before{content:"\e08f"}.icon-repeat:before{content:"\e090"}.icon-resize:before{content:"\e091"}.icon-reply-arrow:before{content:"\e092"}.icon-return-to-top:before{content:"\e093"}.icon-right-double-arrow:before{content:"\e094"}.icon-road:before{content:"\e095"}.icon-roadsign:before{content:"\e096"}.icon-rocket:before{content:"\e097"}.icon-rss:before{content:"\e098"}.icon-ruler-alt:before{content:"\e099"}.icon-ruler:before{content:"\e09a"}.icon-sandbox-toys:before{content:"\e09b"}.icon-satellite-dish:before{content:"\e09c"}.icon-save:before{content:"\e09d"}.icon-safedial:before{content:"\e09e"}.icon-safe:before{content:"\e09f"}.icon-redo:before{content:"\e0a0"}.icon-printer-alt:before{content:"\e0a1"}.icon-planet:before{content:"\e0a2"}.icon-paste-in:before{content:"\e0a3"}.icon-os-x:before{content:"\e0a4"}.icon-navigation-left:before{content:"\e0a5"}.icon-message:before{content:"\e0a6"}.icon-lock:before{content:"\e0a7"}.icon-layers-alt:before{content:"\e0a8"}.icon-record:before{content:"\e0a9"}.icon-print:before{content:"\e0aa"}.icon-plane:before{content:"\e0ab"}.icon-partly-cloudy:before{content:"\e0ac"}.icon-ordered-list:before{content:"\e0ad"}.icon-navigation-last:before{content:"\e0ae"}.icon-message-unopened:before{content:"\e0af"}.icon-location-nearby:before{content:"\e0b0"}.icon-laptop:before{content:"\e0b1"}.icon-reception:before{content:"\e0b2"}.icon-price-yen:before{content:"\e0b3"}.icon-piracy:before{content:"\e0b4"}.icon-parental-control:before{content:"\e0b5"}.icon-operator:before{content:"\e0b6"}.icon-navigation-horizontal:before{content:"\e0b7"}.icon-message-open:before{content:"\e0b8"}.icon-lab:before{content:"\e0b9"}.icon-location-near-me:before{content:"\e0ba"}.icon-receipt-yen:before{content:"\e0bb"}.icon-price-pound:before{content:"\e0bc"}.icon-pin-location:before{content:"\e0bd"}.icon-parachute-drop:before{content:"\e0be"}.icon-old-phone:before{content:"\e0bf"}.icon-merge:before{content:"\e0c0"}.icon-navigation-first:before{content:"\e0c1"}.icon-locate:before{content:"\e0c2"}.icon-keyhole:before{content:"\e0c3"}.icon-receipt-pound:before{content:"\e0c4"}.icon-price-euro:before{content:"\e0c5"}.icon-piggy-bank:before{content:"\e0c6"}.icon-paper-plane:before{content:"\e0c7"}.icon-old-key:before{content:"\e0c8"}.icon-navigation-down:before{content:"\e0c9"}.icon-megaphone:before{content:"\e0ca"}.icon-loading:before{content:"\e0cb"}.icon-keychain:before{content:"\e0cc"}.icon-receipt-euro:before{content:"\e0cd"}.icon-price-dollar:before{content:"\e0ce"}.icon-pie-chart:before{content:"\e0cf"}.icon-paper-plane-alt:before{content:"\e0d0"}.icon-notepad:before{content:"\e0d1"}.icon-navigation-bottom:before{content:"\e0d2"}.icon-meeting:before{content:"\e0d3"}.icon-keyboard:before{content:"\e0d4"}.icon-load:before{content:"\e0d5"}.icon-receipt-dollar:before{content:"\e0d6"}.icon-previous:before{content:"\e0d7"}.icon-pictures:before{content:"\e0d8"}.icon-notepad-alt:before{content:"\e0d9"}.icon-paper-bag:before{content:"\e0da"}.icon-name-badge:before{content:"\e0db"}.icon-medicine:before{content:"\e0dc"}.icon-list:before{content:"\e0dd"}.icon-key:before{content:"\e0de"}.icon-receipt-alt:before{content:"\e0df"}.icon-previous-media:before{content:"\e0e0"}.icon-pictures-alt:before{content:"\e0e1"}.icon-pants:before{content:"\e0e2"}.icon-nodes:before{content:"\e0e3"}.icon-music:before{content:"\e0e4"}.icon-readonly:before{content:"\e0e5"}.icon-presentation:before{content:"\e0e6"}.icon-pictures-alt-2:before{content:"\e0e7"}.icon-pannel-close:before{content:"\e0e8"}.icon-next:before{content:"\e0e9"}.icon-multiple-windows:before{content:"\e0ea"}.icon-medical-emergency:before{content:"\e0eb"}.icon-medal:before{content:"\e0ec"}.icon-link:before{content:"\e0ed"}.icon-linux-tux:before{content:"\e0ee"}.icon-junk:before{content:"\e0ef"}.icon-item-arrangement:before{content:"\e0f0"}.icon-iphone:before{content:"\e0f1"}.icon-lightning:before{content:"\e0f2"}.icon-map:before{content:"\e0f3"}.icon-multiple-credit-cards:before{content:"\e0f4"}.icon-next-media:before{content:"\e0f5"}.icon-panel-show:before{content:"\e0f6"}.icon-picture:before{content:"\e0f7"}.icon-power:before{content:"\e0f8"}.icon-re-post:before{content:"\e0f9"}.icon-rate:before{content:"\e0fa"}.icon-rain:before{content:"\e0fb"}.icon-radio:before{content:"\e0fc"}.icon-radio-receiver:before{content:"\e0fd"}.icon-radio-alt:before{content:"\e0fe"}.icon-quote:before{content:"\e0ff"}.icon-qr-code:before{content:"\e100"}.icon-pushpin:before{content:"\e101"}.icon-pulse:before{content:"\e102"}.icon-projector:before{content:"\e103"}.icon-play:before{content:"\e104"}.icon-playing-cards:before{content:"\e105"}.icon-playlist:before{content:"\e106"}.icon-plugin:before{content:"\e107"}.icon-podcast:before{content:"\e108"}.icon-poker-chip:before{content:"\e109"}.icon-poll:before{content:"\e10a"}.icon-post-it:before{content:"\e10b"}.icon-pound-bag:before{content:"\e10c"}.icon-power-outlet:before{content:"\e10d"}.icon-photo-album:before{content:"\e10e"}.icon-phone:before{content:"\e10f"}.icon-phone-ring:before{content:"\e110"}.icon-people:before{content:"\e111"}.icon-people-female:before{content:"\e112"}.icon-people-alt:before{content:"\e113"}.icon-people-alt-2:before{content:"\e114"}.icon-pc:before{content:"\e115"}.icon-pause:before{content:"\e116"}.icon-path:before{content:"\e117"}.icon-out:before{content:"\e118"}.icon-outbox:before{content:"\e119"}.icon-outdent:before{content:"\e11a"}.icon-page-add:before{content:"\e11b"}.icon-page-down:before{content:"\e11c"}.icon-page-remove:before{content:"\e11d"}.icon-page-restricted:before{content:"\e11e"}.icon-page-up:before{content:"\e11f"}.icon-paint-roller:before{content:"\e120"}.icon-palette:before{content:"\e121"}.icon-newspaper:before{content:"\e122"}.icon-newspaper-alt:before{content:"\e123"}.icon-network-alt:before{content:"\e124"}.icon-navigational-arrow:before{content:"\e125"}.icon-navigation:before{content:"\e126"}.icon-navigation-vertical:before{content:"\e127"}.icon-navigation-up:before{content:"\e128"}.icon-navigation-top:before{content:"\e129"}.icon-navigation-road:before{content:"\e12a"}.icon-navigation-right:before{content:"\e12b"}.icon-microscope:before{content:"\e12c"}.icon-mindmap:before{content:"\e12d"}.icon-molecular-network:before{content:"\e12e"}.icon-molecular:before{content:"\e12f"}.icon-mountain:before{content:"\e130"}.icon-mouse-cursor:before{content:"\e131"}.icon-mouse:before{content:"\e132"}.icon-movie-alt:before{content:"\e133"}.icon-map-marker:before{content:"\e134"}.icon-movie:before{content:"\e135"}.icon-map-location:before{content:"\e136"}.icon-map-alt:before{content:"\e137"}.icon-male-symbol:before{content:"\e138"}.icon-male-and-female:before{content:"\e139"}.icon-mailbox:before{content:"\e13a"}.icon-magnet:before{content:"\e13b"}.icon-loupe:before{content:"\e13c"}.icon-mobile:before{content:"\e13d"}.icon-logout:before{content:"\e13e"}.icon-log-out:before{content:"\e13f"}.icon-layers:before{content:"\e140"}.icon-left-double-arrow:before{content:"\e141"}.icon-layout:before{content:"\e142"}.icon-legal:before{content:"\e143"}.icon-lense:before{content:"\e144"}.icon-library:before{content:"\e145"}.icon-light-down:before{content:"\e146"}.icon-light-up:before{content:"\e147"}.icon-lightbulb-active:before{content:"\e148"}.icon-lightbulb:before{content:"\e149"}.icon-ipad:before{content:"\e14a"}.icon-invoice:before{content:"\e14b"}.icon-info:before{content:"\e14c"}.icon-infinity:before{content:"\e14d"}.icon-indent:before{content:"\e14e"}.icon-inbox:before{content:"\e14f"}.icon-inbox-full:before{content:"\e150"}.icon-inactive-line:before{content:"\e151"}.icon-imac:before{content:"\e152"}.icon-hourglass:before{content:"\e153"}.icon-home:before{content:"\e154"}.icon-grid:before{content:"\e155"}.icon-food:before{content:"\e156"}.icon-favorite:before{content:"\e157"}.icon-door-open-alt:before{content:"\e158"}.icon-diagnostics:before{content:"\e159"}.icon-contrast:before{content:"\e15a"}.icon-coins-dollar-alt:before{content:"\e15b"}.icon-circle-dotted-active:before{content:"\e15c"}.icon-cinema:before{content:"\e15d"}.icon-chip:before{content:"\e15e"}.icon-chip-alt:before{content:"\e15f"}.icon-chess:before{content:"\e160"}.icon-checkbox:before{content:"\e161"}.icon-checkbox-empty:before{content:"\e162"}.icon-checkbox-dotted:before{content:"\e163"}.icon-checkbox-dotted-active:before{content:"\e164"}.icon-check:before{content:"\e165"}.icon-chat:before{content:"\e166"}.icon-chat-active:before{content:"\e167"}.icon-chart:before{content:"\e168"}.icon-chart-curve:before{content:"\e169"}.icon-certificate:before{content:"\e16a"}.icon-categories:before{content:"\e16b"}.icon-cash-register:before{content:"\e16c"}.icon-car:before{content:"\e16d"}.icon-caps-lock:before{content:"\e16e"}.icon-candy:before{content:"\e16f"}.icon-circle-dotted:before{content:"\e170"}.icon-circuits:before{content:"\e171"}.icon-circus:before{content:"\e172"}.icon-client:before{content:"\e173"}.icon-clothes-hanger:before{content:"\e174"}.icon-cloud-drive:before{content:"\e175"}.icon-cloud-upload:before{content:"\e176"}.icon-cloud:before{content:"\e177"}.icon-cloudy:before{content:"\e178"}.icon-clubs:before{content:"\e179"}.icon-cocktail:before{content:"\e17a"}.icon-code:before{content:"\e17b"}.icon-coffee:before{content:"\e17c"}.icon-coin-dollar:before{content:"\e17d"}.icon-coin-pound:before{content:"\e17e"}.icon-coin-yen:before{content:"\e17f"}.icon-coin:before{content:"\e180"}.icon-coins-alt:before{content:"\e181"}.icon-console:before{content:"\e182"}.icon-connection:before{content:"\e183"}.icon-compress:before{content:"\e184"}.icon-company:before{content:"\e185"}.icon-command:before{content:"\e186"}.icon-coin-euro:before{content:"\e187"}.icon-combination-lock:before{content:"\e188"}.icon-combination-lock-open:before{content:"\e189"}.icon-comb:before{content:"\e18a"}.icon-columns:before{content:"\e18b"}.icon-colorpicker:before{content:"\e18c"}.icon-color-bucket:before{content:"\e18d"}.icon-coins:before{content:"\e18e"}.icon-coins-yen:before{content:"\e18f"}.icon-coins-yen-alt:before{content:"\e190"}.icon-coins-pound:before{content:"\e191"}.icon-coins-pound-alt:before{content:"\e192"}.icon-coins-euro:before{content:"\e193"}.icon-coins-euro-alt:before{content:"\e194"}.icon-coins-dollar:before{content:"\e195"}.icon-conversation-alt:before{content:"\e196"}.icon-conversation:before{content:"\e197"}.icon-coverflow:before{content:"\e198"}.icon-credit-card-alt:before{content:"\e199"}.icon-credit-card:before{content:"\e19a"}.icon-crop:before{content:"\e19b"}.icon-crosshair:before{content:"\e19c"}.icon-crown-alt:before{content:"\e19d"}.icon-crown:before{content:"\e19e"}.icon-cupcake:before{content:"\e19f"}.icon-curve:before{content:"\e1a0"}.icon-cut:before{content:"\e1a1"}.icon-dashboard:before{content:"\e1a2"}.icon-defrag:before{content:"\e1a3"}.icon-delete:before{content:"\e1a4"}.icon-delete-key:before{content:"\e1a5"}.icon-departure:before{content:"\e1a6"}.icon-desk:before{content:"\e1a7"}.icon-desktop:before{content:"\e1a8"}.icon-donate:before{content:"\e1a9"}.icon-dollar-bag:before{content:"\e1aa"}.icon-documents:before{content:"\e1ab"}.icon-document:before{content:"\e1ac"}.icon-document-dashed-line:before{content:"\e1ad"}.icon-dock-connector:before{content:"\e1ae"}.icon-dna:before{content:"\e1af"}.icon-display:before{content:"\e1b0"}.icon-disk-image:before{content:"\e1b1"}.icon-disc:before{content:"\e1b2"}.icon-directions:before{content:"\e1b3"}.icon-directions-alt:before{content:"\e1b4"}.icon-diploma:before{content:"\e1b5"}.icon-diploma-alt:before{content:"\e1b6"}.icon-dice:before{content:"\e1b7"}.icon-diamonds:before{content:"\e1b8"}.icon-diamond:before{content:"\e1b9"}.icon-diagonal-arrow:before{content:"\e1ba"}.icon-diagonal-arrow-alt:before{content:"\e1bb"}.icon-door-open:before{content:"\e1bc"}.icon-download-alt:before{content:"\e1bd"}.icon-download:before{content:"\e1be"}.icon-drop:before{content:"\e1bf"}.icon-eco:before{content:"\e1c0"}.icon-economy:before{content:"\e1c1"}.icon-edit:before{content:"\e1c2"}.icon-eject:before{content:"\e1c3"}.icon-employee:before{content:"\e1c4"}.icon-energy-saving-bulb:before{content:"\e1c5"}.icon-enter:before{content:"\e1c6"}.icon-equalizer:before{content:"\e1c7"}.icon-escape:before{content:"\e1c8"}.icon-ethernet:before{content:"\e1c9"}.icon-euro-bag:before{content:"\e1ca"}.icon-exit-fullscreen:before{content:"\e1cb"}.icon-eye:before{content:"\e1cc"}.icon-facebook-like:before{content:"\e1cd"}.icon-factory:before{content:"\e1ce"}.icon-font:before{content:"\e1cf"}.icon-folders:before{content:"\e1d0"}.icon-folder:before,.icon-folder-close:before{content:"\e1d1"}.icon-folder-outline:before{content:"\e1d2"}.icon-folder-open:before{content:"\e1d3"}.icon-flowerpot:before{content:"\e1d4"}.icon-flashlight:before{content:"\e1d5"}.icon-flash:before{content:"\e1d6"}.icon-flag:before{content:"\e1d7"}.icon-flag-alt:before{content:"\e1d8"}.icon-firewire:before{content:"\e1d9"}.icon-firewall:before{content:"\e1da"}.icon-fire:before{content:"\e1db"}.icon-fingerprint:before{content:"\e1dc"}.icon-filter:before{content:"\e1dd"}.icon-filter-arrows:before{content:"\e1de"}.icon-files:before{content:"\e1df"}.icon-file-cabinet:before{content:"\e1e0"}.icon-female-symbol:before{content:"\e1e1"}.icon-footprints:before{content:"\e1e2"}.icon-hammer:before{content:"\e1e3"}.icon-hand-active-alt:before{content:"\e1e4"}.icon-forking:before{content:"\e1e5"}.icon-hand-active:before{content:"\e1e6"}.icon-hand-pointer-alt:before{content:"\e1e7"}.icon-hand-pointer:before{content:"\e1e8"}.icon-handprint:before{content:"\e1e9"}.icon-handshake:before{content:"\e1ea"}.icon-handtool:before{content:"\e1eb"}.icon-hard-drive:before{content:"\e1ec"}.icon-help:before{content:"\e1ed"}.icon-graduate:before{content:"\e1ee"}.icon-gps:before{content:"\e1ef"}.icon-help-alt:before{content:"\e1f0"}.icon-height:before{content:"\e1f1"}.icon-globe:before{content:"\e1f2"}.icon-hearts:before{content:"\e1f3"}.icon-globe-inverted-europe-africa:before{content:"\e1f4"}.icon-headset:before{content:"\e1f5"}.icon-globe-inverted-asia:before{content:"\e1f6"}.icon-headphones:before{content:"\e1f7"}.icon-globe-inverted-america:before{content:"\e1f8"}.icon-hd:before{content:"\e1f9"}.icon-globe-europe---africa:before{content:"\e1fa"}.icon-hat:before{content:"\e1fb"}.icon-globe-asia:before{content:"\e1fc"}.icon-globe-alt:before{content:"\e1fd"}.icon-hard-drive-alt:before{content:"\e1fe"}.icon-glasses:before{content:"\e1ff"}.icon-gift:before{content:"\e200"}.icon-handtool-alt:before{content:"\e201"}.icon-geometry:before{content:"\e202"}.icon-game:before{content:"\e203"}.icon-fullscreen:before{content:"\e204"}.icon-fullscreen-alt:before{content:"\e205"}.icon-frame:before{content:"\e206"}.icon-frame-alt:before{content:"\e207"}.icon-camera-roll:before{content:"\e208"}.icon-bookmark:before{content:"\e209"}.icon-bill:before{content:"\e20a"}.icon-baby-stroller:before{content:"\e20b"}.icon-alarm-clock:before{content:"\e20c"}.icon-adressbook:before{content:"\e20d"}.icon-add:before{content:"\e20e"}.icon-activity:before{content:"\e20f"}.icon-untitled:before{content:"\e210"}.icon-glasses:before{content:"\e211"}.icon-camcorder:before{content:"\e212"}.icon-calendar:before{content:"\e213"}.icon-calendar-alt:before{content:"\e214"}.icon-calculator:before{content:"\e215"}.icon-bus:before{content:"\e216"}.icon-burn:before{content:"\e217"}.icon-bulleted-list:before{content:"\e218"}.icon-bug:before{content:"\e219"}.icon-brush:before{content:"\e21a"}.icon-brush-alt:before{content:"\e21b"}.icon-brush-alt-2:before{content:"\e21c"}.icon-browser-window:before{content:"\e21d"}.icon-briefcase:before{content:"\e21e"}.icon-brick:before{content:"\e21f"}.icon-brackets:before{content:"\e220"}.icon-box:before{content:"\e221"}.icon-box-open:before{content:"\e222"}.icon-box-alt:before{content:"\e223"}.icon-books:before{content:"\e224"}.icon-billboard:before{content:"\e225"}.icon-bills-dollar:before{content:"\e226"}.icon-bills-euro:before{content:"\e227"}.icon-bills-pound:before{content:"\e228"}.icon-bills-yen:before{content:"\e229"}.icon-bills:before{content:"\e22a"}.icon-binarycode:before{content:"\e22b"}.icon-binoculars:before{content:"\e22c"}.icon-bird:before{content:"\e22d"}.icon-birthday-cake:before{content:"\e22e"}.icon-blueprint:before{content:"\e22f"}.icon-block:before{content:"\e230"}.icon-bluetooth:before{content:"\e231"}.icon-boat-shipping:before{content:"\e232"}.icon-bomb:before{content:"\e233"}.icon-book-alt-2:before{content:"\e234"}.icon-bones:before{content:"\e235"}.icon-book-alt:before{content:"\e236"}.icon-book:before{content:"\e237"}.icon-bill-yen:before{content:"\e238"}.icon-award:before{content:"\e239"}.icon-bill-pound:before{content:"\e23a"}.icon-autofill:before{content:"\e23b"}.icon-bill-euro:before{content:"\e23c"}.icon-auction-hammer:before{content:"\e23d"}.icon-bill-dollar:before{content:"\e23e"}.icon-attachment:before{content:"\e23f"}.icon-bell:before{content:"\e240"}.icon-article:before{content:"\e241"}.icon-bell-off:before{content:"\e242"}.icon-art-easel:before{content:"\e243"}.icon-beer-glass:before{content:"\e244"}.icon-arrow-up:before{content:"\e245"}.icon-battery-low:before{content:"\e246"}.icon-arrow-right:before{content:"\e247"}.icon-battery-full:before{content:"\e248"}.icon-arrow-left:before{content:"\e249"}.icon-bars:before{content:"\e24a"}.icon-arrow-down:before{content:"\e24b"}.icon-barcode:before{content:"\e24c"}.icon-arrivals:before{content:"\e24d"}.icon-bar-chart:before{content:"\e24e"}.icon-application-window:before{content:"\e24f"}.icon-band-aid:before{content:"\e250"}.icon-application-window-alt:before{content:"\e251"}.icon-ball:before{content:"\e252"}.icon-application-error:before{content:"\e253"}.icon-badge-restricted:before{content:"\e254"}.icon-app:before{content:"\e255"}.icon-badge-remove:before{content:"\e256"}.icon-anchor:before{content:"\e257"}.icon-badge-count:before{content:"\e258"}.icon-alt:before{content:"\e259"}.icon-badge-add:before{content:"\e25a"}.icon-alert:before{content:"\e25b"}.icon-backspace:before{content:"\e25c"}.icon-alert-alt:before{content:"\e25d"}.fill{height:100%;min-height:100%}.shadow{box-shadow:3px 0 7px #dbdbdb}.umb-scrollable,.umb-auto-overflow{overflow:auto}.umb-abstract{display:block;margin-top:0;margin-bottom:15px;font-size:14px;color:#b3b3b3}h5{margin-top:15px;font-size:13px;font-weight:bold;color:#b3b3b3;text-transform:uppercase}h5.-border-bottom{padding-bottom:5px;border-bottom:1px solid #ececec}h5.-black{color:#000}.umb-form-actions{background:0;border:0}.datepicker td.active,.datepicker td span.active{background:#f3762c!important}.umb-datetime-picker div.info{vertical-align:middle}.umb-userwidget img{float:left;margin-right:15px}.umb-userwidget small{display:block}.popover-title{display:none}.thumbnails>li.umb-plus-btn{margin:0 10px 10px 0}.umb-plus-btn a{display:block;width:136px;height:136px;font-size:50px;line-height:136px;color:#d9d9d9;text-align:center;text-decoration:none;border:2px dashed #d9d9d9;transition:all .3s ease-in-out}.umb-plus-btn a:hover{color:#000;border:2px dashed #000}.umb-plus-btn i{margin:auto;vertical-align:middle}.umb-pane{margin:30px 20px}.umb-control-group{padding-bottom:20px;margin-bottom:15px!important;border-bottom:1px solid #f8f8f8}.umb-control-group.-no-border{border:0}.compact .umb-pane{margin:0 0 15px 0}.compact .umb-control-group{padding-bottom:10px;margin-bottom:5px!important;border-bottom:1px solid #f8f8f8}.compact label.control-label{padding-top:0!important;margin-bottom:0}.compact .controls-row{padding-top:0}.umb-pane>.umb-control-group:last-child{padding-bottom:0!important;border:0}.umb-control-group .umb-el-wrap{padding:0}.umb-control-group label.control-label{text-align:left}.umb-control-group label.control-label>div>label{padding-left:0}.umb-control-group label .help-block,.umb-control-group label small{padding-top:5px;font-size:11px;line-height:13px;color:#a0a0a0}.umb-nolabel .controls{margin-left:0}.controls-row{padding-top:5px;margin-left:240px}.umb-user-panel .controls-row{margin-left:0}.controls-row label{display:inline-block}.controls-row>div>label{padding-left:0}.block-form .controls-row{padding-top:0;margin-left:0}.hidelabel>div>.controls-row,.hidelabel>.controls-row,.hidelabel>div>.controls{padding:0;margin:0!important;border:0}.controls-row>.vertical-align-items{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.controls-row>.vertical-align-items>input.umb-editor-tiny{margin-right:5px;margin-left:5px}.controls-row>.vertical-align-items>input.umb-editor-tiny:first-child{margin-left:0}.thumbnails .selected{background:#000;border-color:#000}.umb-version{position:absolute;right:20px;bottom:5px;color:#bbb}.dashboardHideLink{display:none}.dashboardWrapper{position:relative}.dashboardWrapper h2{padding:0 0 0 45px}.dashboardWrapper h3{font-size:14px;font-weight:bold}.dashboardIcon{position:absolute;top:2px;left:2px}.umb-dashboard-control iframe{position:absolute;display:block;width:99%;height:99%;overflow:auto!important}.umb-table{word-wrap:break-word;table-layout:fixed}.umb-no-border{border:none!important}table thead a{color:#333}.umb-table tbody.ui-sortable tr{cursor:pointer}.umb-table tbody.ui-sortable tr.ui-sortable-helper{background-color:rgba(4,156,219,0.5);border:0}.umb-table tbody.ui-sortable tr.ui-sortable-helper td{border:0}.umb-table tbody.ui-sortable tr.ui-sortable-placeholder{background-color:#2e8aea;border:0}.umb-table tbody.ui-sortable tr.ui-sortable-placeholder td{height:5px;padding:0;line-height:0}.thumbnails>li.umb-thumbnail{position:relative;margin:0 10px 10px 0}.thumbnails>li.umb-thumbnail .umb-icons{position:absolute;top:0;left:0;z-index:1000;width:100%;padding:17px 0;background:#222}.thumbnails>li.umb-thumbnail .icon-crop{position:absolute;top:10px;left:10px;font-size:14px;color:#fff}.thumbnails>li.umb-thumbnail .icon-remove{position:absolute;top:10px;right:10px;font-size:14px;color:#fff}.umb-image-crop{float:left;width:400px;padding:0 0 30px 0;margin:0 30px 25px 0}.umb-image-mask{position:relative;width:399px;height:300px;margin:0 30px 0 0}.umb-image-mask:after{position:absolute;top:0;left:0;z-index:5999;width:100%;height:100%;content:"";opacity:.6;filter:alpha(opacity=6);box-shadow:inset 0 0 0 40px #fff,inset 0 0 0 41px rgba(0,0,0,0.2),inset 0 0 20px 41px rgba(0,0,0,0.2)}.umb-image-mask .icon-screenshot{position:absolute;top:125px;left:110px;z-index:1000;font-size:30px;color:#fff}.umb-image-mask .icon-circle{position:absolute;top:130px;left:115px;z-index:1;font-size:20px;color:#4285f4}.umb-crop-preview{float:left;width:400px}.umb-image-controls{width:400px}.umb-image-controls .icon-minus{float:left;padding:14px 10px 0 10px;color:#ccc}.umb-image-controls .icon-plus{float:right;padding:14px 10px 0 10px;color:#ccc;text-align:left}.umb-image-crop .range{display:block;float:left;width:82%;height:1px;margin:20px 0 0 0;background:#ccc;-webkit-appearance:none}.umb-image-crop .range::-webkit-slider-thumb{width:14px;height:14px;margin-top:-1px;background:#f2f2f2;border:1px solid #ccc;border-radius:2px;content:"1";-webkit-appearance:none}.umb-search-group li>div{padding-left:20px}.umb-search-group li>div a>i{height:100%}#search-form .form-search div{margin-top:3px}#search-form .form-search div .btn{position:relative;top:0;left:0}#dictionaryItems tr{border-top:solid 1px #d9d9d9}#dictionaryItems thead tr{font-weight:bold;border-top:0}#dictionaryItems th{font-weight:normal;text-align:left}#dictionaryItems td{text-align:center}#dictionaryItems thead td:first-of-type{text-align:left}#dictioanryItems i{font-size:18px}#dictionaryItems .icon-alert{color:#9d261d}#dictionaryItems .icon-check{color:#46a546}.umb-loader{width:100%;height:1px;margin-top:0;margin-left:-100%;background-color:#2e8aea;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}@-webkit-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}.umb-loader-wrapper{position:absolute;right:0;left:0;margin:10px 0;overflow:hidden}.umb-loader-wrapper.-bottom{bottom:0}.strong{font-weight:bold}.inline{display:inline}.input-label--title{margin-bottom:3px;font-weight:bold;color:#000}.input-label--small{display:inline;font-size:12px;font-weight:bold;color:rgba(0,0,0,0.7)}.input-label--small:hover{color:#000}input[type=checkbox]:checked+.input-label--small{color:#2e8aea}.panel-dialog--header{padding-bottom:10px;margin:10px 0;font-size:17.5px;font-weight:bold;line-height:20px;border-bottom:1px solid #555}.bootstrap-datetimepicker-widget,.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th,.bootstrap-datetimepicker-widget td span{border-radius:0!important}.umb-item-list{display:block;width:auto;margin:0}.umb-item-list li{display:block;width:auto}.umb-tree{width:auto;min-width:100%;margin:0}.umb-tree li{display:block;width:auto;min-width:100%}.umb-tree li.current>div,.umb-tree div.selected{background:#2e8aea}.umb-tree li.current>div a.umb-options i,.umb-tree div.selected i{background:#fff;border-color:#2e8aea;transition:opacity 120ms ease}.umb-tree li.current>div a.umb-options:hover i,.umb-tree div.selected i{opacity:.7}.umb-tree li.current>div a,.umb-tree li.current>div i.icon,.umb-tree li.current>div ins{color:white!important;background:#2e8aea;border-color:#2e8aea}.umb-tree li.root>div{padding:0}.umb-tree li.root>div h5{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin:0;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-tree li.root>div h5>a,.umb-tree-header{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:20px 0 20px 20px;box-sizing:border-box}.umb-tree *{white-space:nowrap}.umb-tree ul{width:100%;min-width:100%;padding:0;margin:0}.umb-tree ul.collapsed{display:none}.umb-tree a{text-decoration:none;cursor:pointer;outline:0}.umb-tree a:hover{text-decoration:none}.umb-tree div{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 0 5px 0;overflow:hidden;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-tree a.noSpr{background-position:0 0}.umb-tree div>a.umb-options{visibility:hidden}.umb-tree div:hover>a.umb-options{visibility:visible}.umb-tree li.root>div a,.umb-tree li.root h5,.umb-tree-header{font-size:13px;font-weight:bold;color:#b3b3b3;text-transform:uppercase}.umb-tree ins{width:16px;height:16px;margin:-4px 0 0 -16px;font-size:12px;text-decoration:none;visibility:hidden;transition:opacity 120ms ease}.umb-tree ins:hover{opacity:.7}.umb-tree li:hover ins{cursor:pointer;visibility:visible}.umb-tree li div{padding:0}.umb-tree li>div a:not(.umb-options){display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;padding:6px 0}.umb-tree .icon{margin:0 13px 0 0;font-size:20px;color:#21201c;vertical-align:middle}.umb-tree-icon{cursor:pointer}.umb-tree i.noSpr{display:inline-block;width:16px;height:16px;margin-top:1px;line-height:16px}.umb-tree div:hover{background:#f8f8f8}.umb-tree small.search-subtitle{display:block;padding-left:35px;color:#d9d9d9}.umb-tree .umb-tree-node-search{cursor:pointer}.umb-tree .umb-search-group-item{padding-left:20px}.umb-tree .umb-search-group-item-link{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.icon-check:before{content:"\e165"}.umb-tree .umb-tree-node-checked i{color:#2e8aea!important}.umb-tree .umb-tree-node-checked i:before{content:"\e165"!important}a.umb-options{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:9px 5px;margin-right:10px;text-align:center;cursor:pointer;visibility:hidden;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}a.umb-options i{display:inline-block;width:5px!important;height:5px!important;margin:0 2px 0 0;background:#000;border-radius:20px}a.umb-options i:last-child{margin:0}a.umb-options:hover{background:#e4e4e4;border-radius:0}li.root>div>a.umb-options{top:18px;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 5px}.hide-options a.umb-options{display:none!important}.hide-header h5{display:none!important}.umb-icon-item{position:relative;display:block;padding:2px;padding-left:55px}.umb-icon-item:hover{background:#f8f8f8}.umb-icon-item i.icon{position:absolute;top:8px;left:19px}.umb-icon-item a:hover div{text-decoration:underline}.umb-icon-item a{height:15px;padding-top:3px;font-size:12px;color:#525252;text-decoration:none}.umb-icon-item small{display:block;font-size:10px;color:#999}.umb-icon-item:hover a.umb-options{visibility:visible}.umb-icon-item .umb-spr{float:left}div.not-published>i.icon,div.not-published>a{opacity:.6}div.protected:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#9d261d;content:"\e256"}div.has-unpublished-version:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#46a546;content:"\e25a"}div.not-allowed>i.icon,div.not-allowed>a{cursor:not-allowed}.umb-tree li.current>div:before{color:#add8e6}div.is-container:before{position:absolute;bottom:0;padding-top:8px;padding-left:13px;font-family:'icomoon';font-size:8px;color:#04bfbf;pointer-events:none;content:"\e04e"}div.locked:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#9d261d;content:"\e0a7"}.umb-actions{padding:0;margin:12px 0 0 0;list-style:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.umb-actions li.sep{display:block;border-top:1px solid #efefef}.umb-actions li.sep:first-child{border-top:0}.umb-actions a{display:block;padding:8px 25px 8px 20px;font-size:14px;color:#000;text-decoration:none;white-space:nowrap;cursor:pointer}.umb-actions a:hover,.umb-actions a:focus,.umb-actions li.selected{color:#000!important;background:#f8f8f8!important}.umb-actions .menu-label{display:inline-block;padding-top:2px;padding-left:15px;vertical-align:middle}.umb-actions i{margin-top:2px;font-size:18px;color:#999;color:#414141;vertical-align:middle}.umb-actions-child{display:block;margin:0;list-style:none}.umb-actions-child li{display:block}.umb-actions-child a{display:block;padding-left:10px;clear:both;text-decoration:none}.umb-actions-child li .menu-label{margin-left:20px;font-size:12px;color:#000}.umb-actions-child li .menu-label small{display:block;clear:right;font-size:12px;line-height:14px;color:#ccc;white-space:normal}.umb-actions-child li a:hover .menuLabel small{text-decoration:none!important}.umb-actions-child i{font-size:32px}.umb-actions-child li.add{padding-top:20px;margin-top:20px;border-top:1px solid #e9e9e9}.umb-actions-child li.add i{opacity:.4}.umb-tree i.icon.blue{color:#2e8aea}.umb-tree i.icon.green{color:#46a546}.umb-tree i.icon.purple{color:#7a43b6}.umb-tree i.icon.orange{color:#df7f48}.umb-tree i.icon.red{color:#9d261d}.umb-tree li div.l{position:absolute;bottom:0;left:0;width:100%;height:1px;overflow:hidden}.umb-tree li div.l div{width:100%;height:1px;margin-top:0;margin-left:-100%;background-color:#2e8aea;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}.umb-tree .umb-loader{height:10px;margin:10px 10px 10px 10px}body.touch .umb-tree ins{padding:7px;font-size:14px;visibility:visible}body.touch .umb-tree li div{padding-top:8px;padding-bottom:8px;font-size:110%}body.touch .umb-actions a{padding:7px 25px 7px 20px;font-size:110%}body.touch a.umb-options i{margin-top:20px}.umb-listview{width:auto!important}.umb-listview .dropdown-menu{border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:0;box-shadow:0 5px 10px rgba(0,0,0,0)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#000;background:#f8f8f8}.umb-listview table{border:1px solid #d9d9d9}.umb-listview table caption{text-align:left;vertical-align:middle;background:#fff}.umb-sub-header{padding:0 0 20px 0}.umb-sub-header .header-content-right{float:right}.form-search .inner-addon{position:relative}.form-search .inner-addon [class^="icon-"],.form-search .inner-addon [class*=" icon-"]{position:absolute;top:0;padding:5px 8px;pointer-events:none}.form-search .inner-addon input[type="text"]{width:190px}.form-search .left-addon [class^="icon-"],.form-search .left-addon [class*=" icon-"]{right:inherit;left:0}.form-search .right-addon [class^="icon-"],.form-search .right-addon [class*=" icon-"]{right:0;left:inherit}.form-search .left-addon input[type="text"]{padding-right:6px;padding-left:30px!important}.form-search .right-addon input[type="text"]{padding-right:30px;padding-left:6px!important}.umb-listview table form{position:relative;margin:0}.umb-listview table input[type="text"]{width:60px;padding:4px 0 4px 20px;background:0;border:1px solid #d9d9d9;transition:all .5s}.umb-listview table input::-webkit-input-placeholder{color:#555}.umb-listview table input:-moz-placeholder{color:#555}.umb-listview table input::-moz-placeholder{color:#555}.umb-listview table input:-ms-input-placeholder{color:#555}.umb-listview table input[type="text"]:focus{width:200px;color:#000;background:#fff;border:1px solid #d9d9d9}.umb-listview table thead a{cursor:default}.umb-listview table thead a:hover{text-decoration:none}.umb-listview table thead a.sortable{cursor:pointer}.umb-listview table thead a.sortable span:hover{text-decoration:underline}.umb-listview .icon-star{color:#d9d9d9}.umb-listview .selected i.icon,.umb-listview tbody tr:hover i.icon{display:none}.umb-listview .selected input[type="checkbox"],.umb-listview tr:hover input[type="checkbox"]{display:inline-block!important}.umb-listview .inactive{color:#d9d9d9}.umb-listview .selected td{font-weight:bold}.umb-listview table thead{font-size:11px;font-weight:600;text-transform:uppercase;background-color:#fff}.umb-listview table tfoot{background:#f8f8f8}.umb-listview table tfoot td:last-child{border-left:none}.umb-listview table tfoot th{padding:0 20px}.umb-listview .label{font-size:11px;font-weight:400;color:#000;text-shadow:none;background:#f8f8f8;border:1px solid #d9d9d9}.umb-listview .table-striped tbody>tr:nth-child(even)>td,.umb-listview .table-striped tbody>tr:nth-child(even)>th{background-color:#f8f8f8}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background:0}.umb-listview .pagination{margin:0}.umb-listview .table th{font-weight:normal}.umb-listview .showing{padding:8px 4px 2px 4px;font-size:11px;color:#b0b0b0;background:0}.umb-listview .pagination{text-align:center}.umb-listview .pagination ul{background:0;border-radius:0;box-shadow:0 1px 2px rgba(0,0,0,0)}.umb-listview .pagination ul>li>a,.pagination ul>li>span{padding:8px 4px 2px 4px;font-size:12px;color:#b0b0b0;background:0;border:0}.umb-listview .pagination ul>li.active>a,.umb-listview .pagination ul>li>a:hover{color:#000}.umb-listview .pagination ul>li.disabled>a,.umb-listview .pagination ul>li.disabled>a:hover{color:#d9d9d9}.umb-listview .table-striped tbody td{position:relative}.umb-listview .table-striped thead input[type="checkbox"]{margin-left:7px}.umb-listview .table-striped tbody input[type="checkbox"]{z-index:5;display:none;margin-left:7px}.umb-listview .table-striped tbody i{top:10px;left:6px;z-index:6;display:block;width:20px;height:20px;padding:0 0 0 4px;background:#fff}.umb-listview .table-striped tbody>tr:nth-child(even)>td i,.umb-listview .table-striped tbody>tr:nth-child(even)>th i{background-color:#f8f8f8}.table-striped tbody i:hover{display:none!important}.list-view-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 15px;margin-bottom:1px;background:#f8f8f8;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.list-view-layout__sort-handle{margin-right:15px;font-size:14px;color:#d9d9d9}.list-view-layout__name{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-right:15px;font-weight:bold;line-height:1.2em;-webkit-flex:5;-ms-flex:5;flex:5;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.list-view-layout__name-text{margin-right:3px}.list-view-layout__system{font-size:10px;font-weight:normal}.list-view-layout__path{margin-right:15px;-webkit-flex:10;-ms-flex:10;flex:10}.list-view-layout__icon{display:block;padding:6px 8px;margin-right:10px;font-size:18px;vertical-align:middle;background:#fff;border:1px solid #d9d9d9}.list-view-layout__icon:hover,.list-view-layout__icon:focus,.list-view-layout__icon:active{text-decoration:none}.list-view-layout__remove{position:relative;cursor:pointer}.list-view-add-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 0;margin-top:10px;color:#2e8aea;border:1px dashed #d9d9d9;box-sizing:border-box;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.list-view-add-layout:hover{text-decoration:none}.mceContentBody{overflow-y:hidden!important}.usky-grid IFRAME{overflow:hidden}.usky-grid .ui-sortable-helper{position:absolute!important;width:160px!important;height:80px!important;padding:5px;overflow:hidden;background:#ccc;border:dashed 1px #000!important;border-radius:5px;opacity:.4;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.usky-grid .ui-sortable-helper *{padding:0!important;margin:0!important;color:#999!important;background:none!important;border:none!important}.usky-grid .ui-sortable-helper .cell-tools{display:none!important}.usky-grid .ui-sortable-placeholder{padding:20px;font-family:icomoon;font-size:85px;line-height:65px;color:#555;text-align:center;vertical-align:middle;background-color:#f8f8f8;border:2px dashed #d9d9d9}.usky-grid .ui-sortable-placeholder:hover{border-color:#555}.usky-grid .ui-sortable-placeholder:before{content:"\e1bd"}.usky-grid-width{width:100%;margin:20px auto}.usky-grid .right{float:right}.usky-grid .tb{width:100%}.usky-grid .td{display:inline-block;width:100%;vertical-align:top;border-right:1px dashed rgba(182,182,182,0);box-sizing:border-box}.usky-grid .tb:hover .td{border-right:1px dashed rgba(182,182,182,0.3)}.usky-grid .td.last{border-right:1px dashed rgba(182,182,182,0)!important}.usky-grid .middle{text-align:center}.usky-grid .mainTb{border-collapse:separate}.usky-grid .mainTd{position:relative}.usky-grid .usky-row{position:relative;border:1px dashed rgba(0,0,0,0)}.umb-grid .tb:hover .usky-row{border-bottom:1px dashed rgba(182,182,182,0)!important}.usky-grid .usky-cell{position:relative;min-height:127px;border:1px dashed rgba(0,0,0,0)}.usky-grid .cell-tools{position:absolute;top:0;right:0;bottom:0;z-index:50;width:50px;opacity:.3;transition:all .2s ease-in-out}.umb-grid .cell-tools{width:50px!important}.usky-grid .cell-tools.with-prompt{width:200px}.usky-grid .cell-tools:hover{opacity:1}.usky-grid .cell-tools-add{position:absolute;right:0;bottom:0;left:0;margin:0 45px 1px 0;text-align:center}.usky-grid .cell-tools-add.emptyArea{margin:0 0 1px 0}.usky-grid .usky-control:hover .cell-tools-add{opacity:1}.usky-grid .cell-tools-remove{position:absolute;top:0;right:5px;z-index:500;display:inline-block;text-align:right}.usky-grid .cell-tools-remove .iconBox:hover,.usky-grid .cell-tools-remove .iconBox:hover *{background:#9d261d!important;border-color:#9d261d!important}.usky-grid .cell-tools-move{position:absolute;top:33px;right:5px;z-index:500;display:inline-block;cursor:move}.usky-grid .cell-tools-edit{position:absolute;top:66px;right:5px}.usky-grid .usky-control{position:relative;display:block;margin:10px 0 0 0;background-clip:padding-box}.usky-grid .warnhighlight,.usky-grid .td.last.warnhighlight{border:1px dashed #9d261d!important}.usky-grid .infohighlight,.usky-grid .td.last.infohighlight{border:1px dashed #2e8aea!important}.usky-grid .warnhighlight>ins.item-label{color:#9d261d;border-color:#9d261d}.usky-grid .infohighlight>ins.item-label{color:#2e8aea;border-color:#2e8aea}.usky-grid ins.item-label{position:absolute;top:-22px;left:-1px;display:none;height:20px;padding:0 7px;overflow:hidden;font-size:.8em;color:#d9d9d9;text-decoration:none;background-color:white;border:1px dashed #d9d9d9;border-bottom:1px solid white!important}.usky-grid .usky-row-inner>ins.item-label{top:-20px;left:0}.usky-grid .usky-control-inner.selectedControl,.usky-grid .usky-row-inner.selectedRow{border:1px dashed #d9d9d9}.usky-grid .usky-control-inner.selectedControl>ins.item-label,.usky-grid .usky-row-inner.selectedRow>ins.item-label{z-index:100000;display:block}.usky-grid .usky-control-placeholder{position:relative;min-height:20px;text-align:center;text-align:-moz-center;cursor:text}.usky-grid .usky-control-placeholder .placeholder{height:20px;padding:5px;font-size:14px;text-align:left;border:1px solid rgba(182,182,182,0.3);opacity:.7}.usky-grid .usky-control-placeholder:hover .placeholder{border:1px solid rgba(182,182,182,0.8)}.usky-grid .usky-editor-placeholder{position:relative;min-height:65px;padding:20px;padding-bottom:30px;text-align:center;text-align:-moz-center;background-color:white;border:4px dashed #f8f8f8}.usky-grid .usky-editor-placeholder i{display:block;margin-bottom:10px;font-size:85px;line-height:85px;color:#f8f8f8}.usky-grid textarea.textstring{display:block;overflow:hidden;color:#555;background:#fff;border:0;outline:0;resize:none}.usky-grid .usky-cell-rte textarea{display:none!important}.usky-grid .usky-cell-media .caption{display:block;width:98%;overflow:hidden;font-style:italic;background:#fff;border:0;outline:0;resize:none}.usky-grid .cellPanelRte{min-height:60px}.usky-grid .usky-cell-embed iframe{width:100%}.usky-grid .iconBox{display:inline-block;padding:4px 6px 4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.usky-grid .iconBox span.prompt{display:block;text-align:center;white-space:nowrap}.usky-grid .iconBox span.prompt>a{text-decoration:underline}.usky-grid .iconBox:hover,.usky-grid .iconBox:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.usky-grid .iconBox a:hover{color:white!important;text-decoration:none}.usky-grid .iconBox.selected{background:transparent;background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-radius:3px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-webkit-appearance:none}.usky-grid .iconBox i{display:block;font-size:16px!important;color:#5f5f5f}.usky-grid ul{display:inline-block;padding:0;margin:10px 0 0 0;text-align:center;list-style:none}.usky-grid .help-text{display:inline-block;padding:10px 20px 10px 20px;clear:both;font-size:14px;color:#555;background:#f8f8f8;border-radius:15px}.usky-grid ul li{display:inline-block;width:120px;margin:8px 8px 0 8px}.usky-grid .mce-panel{clear:both;border:none!important}.usky-grid .mce-btn button{padding:8px 6px;line-height:inherit}.usky-grid .mce-toolbar{position:absolute;z-index:100;z-index:9999999;display:inline-block;float:left;padding:-1px;margin:-1px -1px 0 -1px;background-color:#fafafa;border:1px solid rgba(207,207,207,0.7);box-shadow:2px 2px 10px 0 rgba(50,50,50,0.14)}.mce-flow-layout-item{margin:0}.usky-grid .mce-panel{background:transparent!important}.usky-grid .mce-floatpanel{background-color:#f7f7f7!important}.usky-cell-rte{border:1px solid #f8f8f8}.usky-grid .fullSizeImage{width:100%}.usky-grid .boxWidth{margin-bottom:10px;text-align:right}.usky-grid .boxWidth input{width:40px;text-align:center}.usky-grid .boxWidth label{padding:0;margin:5px 5px 0 0;font-size:10px;color:#808080}.usky-grid .usky-cell{padding-top:5px;padding-bottom:15px}.usky-grid .usky-control{padding:5px;margin:10px 0 0 0;border:1px dashed transparent}.usky-grid .usky-templates-columns{margin-top:30px}.usky-grid .usky-row-inner{margin-right:45px;border:1px dashed transparent}.usky-grid .usky-control-inner{position:relative;min-height:60px;padding:5px;margin-right:45px;margin-bottom:15px;border:1px dashed transparent}.usky-grid .uSky-templates{width:100%;overflow:hidden;text-align:center}.usky-grid .uSky-templates-template{display:inline-block;width:100px;padding-right:30px;margin:20px}.usky-grid .uSky-templates-template a.tb:hover{border:5px solid #2e8aea}.usky-grid .uSky-templates-template .tb{position:relative;width:100%;height:150px;padding:10px;cursor:pointer;background-color:#f8f8f8;border:5px solid #d9d9d9}.usky-grid .uSky-templates-template .tr{position:relative;height:100%}.usky-grid .uSky-templates-template .tb .uSky-templates-column{height:100%;border:1px dashed #d9d9d9;border-right:0}.usky-grid .uSky-templates-template .tb .uSky-templates-column.last{border-right:1px dashed #d9d9d9!important}.usky-grid a.uSky-templates-column:hover,.usky-grid a.uSky-templates-column.selected{background-color:#2e8aea}.usky-grid .templates-preview{display:inline-block;width:100%;text-align:center}.usky-grid .templates-preview small{position:absolute;bottom:-25px;left:0;width:100%;padding-top:15px}.usky-grid .templates-preview .help-text{margin:35px 35px 0 0}.usky-grid .preview-rows{position:relative;display:inline-block;width:125px;margin:35px 40px 15px 0;border:2px solid #d9d9d9;box-sizing:border-box;transition:border 200ms linear}.usky-grid .preview-rows.prevalues-rows{float:left;width:80px;margin:0 20px 20px 0}.usky-grid .preview-rows.prevalues-templates{float:left;margin:0 20px 20px 0}.usky-grid .preview-rows:hover{cursor:pointer;border-color:#2e8aea}.usky-grid .preview-rows .preview-row{display:inline-block;width:100%;vertical-align:bottom}.usky-grid .preview-rows.layout{padding:2px}.usky-grid .preview-rows.layout .preview-row{height:100%}.usky-grid .preview-rows.layout .preview-col{height:180px;border:dashed 1px #d9d9d9}.usky-grid .preview-rows.layout .preview-cell{background-color:#f8f8f8}.usky-grid .preview-rows.layout .preview-overlay{display:none}.usky-grid .preview-rows.columns{min-height:18px;padding:1px;line-height:11px}.usky-grid .preview-rows.columns.prevalues-rows{min-height:30px}.usky-grid .preview-rows .preview-col{display:block;float:left;width:33.3%;height:10px;margin:0;border:1px solid #fff;box-sizing:border-box}.usky-grid .preview-rows .preview-col .preview-cell{display:block;width:100%;height:100%;margin:0 1px 1px 0;background-color:#d9d9d9}.usky-grid .preview-rows.prevalues-templates .preview-col{height:80px}.usky-grid .preview-overlay{position:absolute;top:0;left:0;display:block;width:100%;height:100%;border:3px solid white;box-sizing:border-box}.usky-grid .cell-tools-menu{position:absolute;top:0;left:50%;z-index:6660;width:360px;height:380px;padding:7px;margin-top:-270px;margin-left:-150px;overflow:auto;background:white;border:1px solid #ccc;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.usky-grid .cell-tools-menu h5{padding:10px;margin-top:0;color:#999;border-bottom:1px solid #d9d9d9}.usky-grid .elements{display:block;padding:0;margin:0}.usky-grid .elements li{display:inline-block;width:90px;height:80px;padding:5px;margin:5px;overflow:hidden;font-size:11px}.usky-grid .elements li:hover,.usky-grid .elements li:hover *{color:white;background:#2e8aea}.usky-grid .elements a{color:#222;text-decoration:none}.usky-grid .elements i{display:block;font-size:30px;line-height:50px;color:#999}.usky-grid-configuration .uSky-templates{text-align:left}.usky-grid-configuration ul{display:block}.usky-grid-configuration ul li{display:block;width:auto;text-align:left}.usky-grid-configuration .uSky-templates .uSky-templates-template .tb{max-height:50px;padding:0;overflow:hidden;border-spacing:2px;border-width:2px!important}.usky-grid-configuration .uSky-templates .uSky-templates-template span{display:inline-block;background:#d9d9d9}.usky-grid-configuration .uSky-templates .uSky-templates-template .tb:hover{border-width:2px!important}.usky-grid-configuration .uSky-templates-column{display:block;float:left;margin-left:-1px;background:#d9d9d9;border:1px white solid!important}.usky-grid-configuration .uSky-templates-column.last{margin-right:-1px}.usky-grid-configuration .uSky-templates-column.add{font-size:20px;line-height:70px;color:#ccc;text-align:center;text-decoration:none;background:white}.usky-grid-configuration .mainTdpt{height:initial;border:0}.usky-grid-configuration .uSky-templates-rows .uSky-templates-row{width:60px;margin:0 50px 20px 0}.usky-grid-configuration .uSky-templates-rows .uSky-templates-row .tb{padding:0;border-spacing:2px;border-width:2px!important}.usky-grid-configuration .uSky-templates-rows .mainTdpt{height:10px!important}.usky-grid-configuration a.uSky-templates-column{height:70px!important}body.dragging,body.dragging *{cursor:move!important}li.dragged{position:absolute;z-index:2000;opacity:.5}.umb-sort li{display:block;padding:5px;margin:5px;background:#f8f8f8;border:1px solid #CCC}.umb-sort .placeholder{position:relative;padding:0;margin:0;border:0}.umb-sort .placeholder:before{position:absolute;top:-4px;left:-5px;width:0;height:0;margin-top:-5px;border:5px solid transparent;border-right:0;border-left-color:red;content:""}.umb-dashboards-forms-install{position:relative;top:-30px;padding-top:30px;background:url('../img/forms/installer-background.png');background-repeat:repeat-x;border-radius:0 0 200px 200px;box-shadow:inset 0 -40px 30px 25px #fff}.umb-dashboards-forms-install small{font-size:14px;opacity:.5}.umb-dashboards-forms-install .umb-loader{width:640px;height:4px}.umb-dashboards-forms-install .video_player video{width:100%;max-width:640px;border:1px solid #ededed;border-bottom:0;border-left:none;box-sizing:border-box}.umb-dashboards-forms-install .video_player input[type="range"]{position:relative;z-index:999;width:100%;max-width:640px;margin:0 auto;cursor:pointer;opacity:0}.umb-dashboards-forms-install .video_player input[type="range"]::-ms-fill-lower,.umb-dashboards-forms-install .video_player input[type="range"]::-ms-fill-upper{background:transparent}.umb-dashboards-forms-install .video_player input[type="range"]::-ms-tooltip{display:none}.umb-dashboards-forms-install .video_player .video-controls{position:relative;height:20px;max-width:640px;margin:-13px auto 40px;opacity:.8}.umb-dashboards-forms-install .video_player .loader{display:block;width:100%;height:3px;margin-top:-13px;background-color:whitesmoke}.umb-dashboards-forms-install .video_player .progress-bar{display:block;width:200px;height:100%;max-width:100%;background:#7c7e80;box-sizing:border-box}.umb-dashboards-forms-install .video_player .video-controls,.umb-dashboards-forms-install .video_player .loader,.umb-dashboards-forms-install .video_player .progress-bar{transition:all 150ms ease-in-out}.umb-dashboards-forms-install .video_player .progress-bar{transition-property:background}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls,.umb-dashboards-forms-install .video_player .video-controls:hover{margin-top:-19px;margin-bottom:46px;opacity:1}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls .loader,.umb-dashboards-forms-install .video_player .video-controls:hover .loader{height:8px}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls .progress-bar,.umb-dashboards-forms-install .video_player .video-controls:hover .progress-bar{background:#2e99f4}.umb-dashboards-forms-install .installer-top{position:relative;height:80px;margin:0 auto 30px;overflow:hidden;line-height:80px;text-align:center}.umb-dashboards-forms-install .installer-top i,.umb-dashboards-forms-install .installer-top h3{display:inline-block}.umb-dashboards-forms-install .installer-top i{position:absolute;top:50%;margin-top:-40px}.umb-dashboards-forms-install .installer-top h3{margin:0;margin-left:100px;font-size:36px;font-weight:700;line-height:80px;letter-spacing:-1px}.umb-validation-label{position:relative;padding:1px 5px;font-size:10px;line-height:1.5em;color:white;background:#9d261d}.umb-validation-label:after{position:absolute;bottom:100%;left:10px;width:0;height:0;margin-left:-4px;pointer-events:none;border:solid transparent;border-color:rgba(255,255,255,0);border-bottom-color:#9d261d;border-width:4px;content:" "}.umb-validation-label.-arrow-left{margin-left:10px}.umb-validation-label.-arrow-left:after{position:absolute;top:50%;right:100%;bottom:auto;left:auto;width:0;height:0;margin-top:-4px;pointer-events:none;border:solid transparent;border-color:rgba(255,255,255,0);border-right-color:#9d261d;border-width:4px;content:" "}.umb-editor-wrapper{position:absolute;top:0;right:0;bottom:0;left:0;background:white}.umb-editor-header{position:absolute;top:0;right:0;left:0;height:99px;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-editor-container{position:absolute;top:101px;right:0;bottom:52px;left:0;overflow:auto;clear:both}.umb-editor-wrapper.-no-footer .umb-editor-container{bottom:0}.umb-editor-container.-stop-scrolling{overflow:hidden}.umb-editor-drawer{position:absolute;right:0;bottom:0;left:0;z-index:999;height:31px;padding:10px 20px;margin:0;background:#f8f8f8;border-top:1px solid #d9d9d9}.umb-editor-actions{padding:0;margin:0;list-style:none}.umb-editor-actions li{display:inline-block}.umb-overlay{position:fixed;z-index:996660;overflow:hidden;background:white;box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23);-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-overlay__form{display:-webkit-flex;display:-ms-flexbox;display:flex;height:100%;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-overlay .umb-overlay-header{padding:10px;margin-top:0;background:#f8f8f8;border-bottom:1px solid #d9d9d9;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.umb-overlay .umb-overlay__title{margin:7px 0;font-size:17.5px;font-weight:bold;color:#000}.umb-overlay .umb-overlay__subtitle{font-size:11.9px;color:#555}.umb-overlay .umb-overlay-container{position:relative;height:auto;overflow-x:hidden;overflow-y:auto;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-webkit-flex-shrink:1;-ms-flex-negative:1;flex-shrink:1;-webkit-flex-basis:auto;-ms-flex-preferred-size:auto;flex-basis:auto}.umb-overlay .umb-overlay-drawer{padding:10px 20px;margin:0;background:#f8f8f8;border-top:1px solid #d9d9d9;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;-webkit-flex-basis:31px;-ms-flex-preferred-size:31px;flex-basis:31px}.umb-overlay .umb-overlay-drawer.-auto-height{-webkit-flex-basis:auto;-ms-flex-preferred-size:auto;flex-basis:auto}.umb-overlay .umb-overlay-drawer .umb-overlay-drawer__align-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.umb-overlay .umb-overlay-drawer .umb-overlay-drawer-content .dropdown-menu{right:0;left:auto}.umb-overlay.umb-overlay-center{position:absolute;top:10px;left:50%;width:600px;height:500px;-webkit-transform:translate(-50%,0);transform:translate(-50%,0)}.umb-overlay.umb-overlay-center .umb-overlay-header{text-align:center}.umb-overlay.umb-overlay-center .umb-overlay-container{padding:20px}.umb-overlay.umb-overlay-target{width:400px;height:400px;box-sizing:border-box}.umb-overlay.umb-overlay-target .umb-overlay-header{text-align:center}.umb-overlay.umb-overlay-target .umb-overlay-container{padding:10px}.umb-overlay.umb-overlay-right{top:0;right:0;bottom:0;width:500px;border:0;box-shadow:0 0 20px rgba(0,0,0,0.19),0 0 6px rgba(0,0,0,0.23)}.umb-overlay.umb-overlay-right .umb-overlay-header{padding:20px;box-sizing:border-box;-webkit-flex-basis:100px;-ms-flex-preferred-size:100px;flex-basis:100px}.umb-overlay.umb-overlay-right .umb-overlay-container{padding:20px}.umb-overlay.umb-overlay-left{top:0;bottom:0;left:0;width:500px;margin-left:80px;border:0;box-shadow:0 0 20px rgba(0,0,0,0.19),0 0 6px rgba(0,0,0,0.23)}.umb-overlay.umb-overlay-left .umb-overlay-header{padding:20px;box-sizing:border-box;-webkit-flex-basis:100px;-ms-flex-preferred-size:100px;flex-basis:100px}.umb-overlay.umb-overlay-left .umb-overlay-container{padding:20px}@media(max-width:767px){.umb-overlay.umb-overlay-left{margin-left:60px}}.umb-overlay__item-details{position:absolute;bottom:51px;left:0;width:100%;padding:20px;background:#f8f8f8;border-bottom:1px solid #d9d9d9;box-sizing:border-box}.umb-overlay__item-details-title-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-overlay__item-details-icon{margin-right:10px;font-size:16px;color:#999;vertical-align:middle}.umb-overlay__item-details-title{margin-top:0;margin-bottom:0}.umb-overlay__item-details-description{margin-top:10px;font-size:12px}.umb-overlay .control-label,.umb-overlay .form-horizontal .control-label,.form-horizontal .umb-overlay .control-label{display:block;float:none;width:100%;margin-bottom:10px;box-sizing:border-box}.umb-overlay .controls-row,.umb-overlay .form-horizontal .controls,.form-horizontal .umb-overlay .controls{margin-left:0!important}.umb-card{position:relative;padding:5px 10px 5px 10px;background:white}.umb-card .title{padding:12px;margin:0 -10px 10px -10px;font-size:16px;font-weight:400;color:#555;text-transform:none;border-bottom:1px solid #d9d9d9}.umb-card-thumb{text-align:center}.umb-card-thumb i{display:block;padding-top:5px;font-size:20px;line-height:40px;color:#2e8aea;text-align:center}.umb-card-content .item-title{margin-bottom:3px;font-size:16px;font-weight:400;color:#1d1d1d;text-transform:none;border:0}.umb-card-content p{margin-bottom:1px;color:#555}.umb-card-actions{padding-top:10px;clear:both;border-top:#f8f8f8 1px solid}.umb-card-icons{display:block;padding:0;margin:0;text-align:center;vertical-align:middle;list-style:none}.umb-card-icons.vertical{position:absolute;top:7px;right:7px;width:1px;text-align:right}.umb-card-icons li{display:inline-block;margin:0 2px 0 2px}.umb-card-icons.vertical li{display:block;float:right;margin-bottom:3px}.umb-card-list{display:block;padding:0;margin:0}.umb-card-list li{display:block;padding-bottom:3px;border-bottom:#f8f8f8 1px solid}.umb-card-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0;margin:0 auto;list-style:none;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.umb-card-grid li{width:100px;height:105px;padding:5px;overflow:hidden;font-size:11px;text-align:center;box-sizing:border-box}.umb-card-grid li.-four-in-row{-webkit-flex:0 0 25%;-ms-flex:0 0 25%;flex:0 0 25%}.umb-card-grid li.-three-in-row{-webkit-flex:0 0 33.33%;-ms-flex:0 0 33.33%;flex:0 0 33.33%}.umb-card-grid .umb-card-grid-item{display:block;width:100%;height:100%}.umb-card-grid .umb-card-grid-item:hover,.umb-card-grid .umb-card-grid-item:focus,.umb-card-grid .umb-card-grid-item:hover>*,.umb-card-grid .umb-card-grid-item:focus>*{color:white;cursor:pointer;background:#2e8aea;outline:0}.umb-card-grid a{color:#222;text-decoration:none}.umb-card-grid i{display:block;font-size:30px;line-height:50px;color:#343434}.umb-btn-round{display:inline-block;padding:4px 6px 4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.umb-btn-round:hover,.umb-btn-round:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.umb-btn-round a:hover{color:white!important;text-decoration:none}.umb-btn-round i{display:block;font-size:16px!important;color:#grayLight}.umb-btn-round.alert:hover,.umb-btn-round.alert:hover *{color:white!important;text-decoration:none;background:#9d261d!important;border-color:#9d261d!important}.umb-btn-round.no-border{background:none!important;border:none!important}.umb-sub-views .umb-sub-views-action-bar{margin-bottom:40px}.umb-sub-views .umb-sub-views-action-bar .btn-link{padding-right:0;padding-left:0}.umb-sub-views .umb-sub-views-action-bar .btn-link:focus{text-decoration:none;outline:0}.umb-sub-views .umb-sub-views-nav{float:right;margin:0}.umb-sub-views .umb-sub-views-nav .umb-sub-views-nav-item{display:inline-block;margin-left:15px}.umb-sub-views .umb-sub-views-nav .umb-sub-views-nav-item.is-active .btn-link{color:#2e8aea!important}.umb-sub-views .umb-sub-views-tools{float:left;margin:0}.umb-sub-views .umb-sub-views-tools .umb-sub-views-tool{display:inline-block;margin-right:15px}.umb-sub-views-nav{display:-webkit-flex;display:-ms-flexbox;display:flex;margin:0;list-style:none}.umb-sub-views-nav-item{display:block;margin-left:20px;text-align:center;cursor:pointer}.umb-sub-views-nav-item:focus{outline:0}.umb-sub-views-nav-item:hover,.umb-sub-views-nav-item:focus{text-decoration:none}.umb-sub-views-nav-item.is-active{color:#2e8aea}.show-validation .umb-sub-views-nav-item.-has-error{color:#9d261d}.umb-sub-views-nav-item .icon{display:block;font-size:24px;text-align:center}.umb-sub-views-nav-item-text{font-size:11px}.sub-view-columns{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:40px}.sub-view-columns h5{margin-top:0}.sub-view-column-left{margin-right:70px;-webkit-flex:0 0 250px;-ms-flex:0 0 250px;flex:0 0 250px}.sub-view-column-right{-webkit-flex:1;-ms-flex:1;flex:1}.sub-view-column-section{margin-bottom:20px}.umb-editor-sub-header{position:relative;top:0;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:15px 0;margin-top:-30px;margin-bottom:30px;background:#fff;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-editor-sub-header.-umb-sticky-bar{top:100px;-webkit-transform:translate(0,50%);transform:translate(0,50%);box-shadow:0 5px 0 rgba(0,0,0,0.08),0 1px 0 rgba(0,0,0,0.16);transition:box-shadow 1s}.umb-group-builder__property-preview .umb-editor-sub-header{display:none}.umb-editor-sub-header__content-left{margin-right:auto}.umb-editor-sub-header__content-right{margin-left:auto}.umb-editor-sub-header__content-left,.umb-editor-sub-header__content-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.umb-editor-sub-header__section{display:-webkit-flex;display:-ms-flexbox;display:flex;padding-right:20px;padding-left:20px;border-left:1px solid #d9d9d9;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-editor-sub-header__content-left .umb-editor-sub-header__section:first-child{padding-left:0;border-left:none}.umb-editor-sub-header__content-right .umb-editor-sub-header__section{border-right:1px solid #d9d9d9;border-left:none}.umb-editor-sub-header__content-right .umb-editor-sub-header__section:last-child{padding-right:0;border-right:0}.umb-grid-selector__items{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-grid-selector__item{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;width:125px;height:150px;padding:20px;margin:0 20px 20px 0;text-align:center;background:#f8f8f8;border:1px solid #ccc;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-grid-selector__item.-default{border-width:2px}.umb-grid-selector__item.-placeholder{cursor:pointer;background:0;border:1px dashed #d9d9d9}.umb-grid-selector__item-content{margin-top:10px}.umb-grid-selector__item-icon{display:block;margin-bottom:5px;font-size:50px;line-height:50px;color:#d9d9d9}.umb-grid-selector__item-label{font-size:13px;font-weight:bold}.umb-grid-selector__item-label.-blue{color:#2e8aea}.umb-grid-selector__item-remove{position:absolute;top:5px;right:5px;cursor:pointer}.umb-grid-selector__item-default-label{position:relative;top:-3px;font-size:10px;font-weight:bold;color:#555}.umb-grid-selector__item-default-label.-blue{color:#2e8aea}.umb-grid-selector__item-add{color:#2e8aea}.umb-child-selector__child{display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:300px;padding:5px 15px;margin-bottom:5px;background:#f8f8f8;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-child-selector__child.-parent{padding-top:10px;padding-bottom:10px;background:#f1f1f1}.umb-child-selector__child.-placeholder{text-align:center;cursor:pointer;background:0;border:1px dashed #d9d9d9;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-child-selector__children-container{margin-left:30px}.umb-child-selector__child-description{-webkit-flex:1;-ms-flex:1;flex:1}.umb-child-selector__child-icon-holder{display:inline-block;width:20px;margin-right:5px;text-align:center}.umb-child-selector__child-icon{font-size:16px;vertical-align:middle}.umb-child-selector__child-name{font-size:13px}.umb-child-selector__child-name.-blue{color:#2e8aea}.umb-child-selector__child-actions{text-align:right;-webkit-flex:0 0 50px;-ms-flex:0 0 50px;flex:0 0 50px}.umb-child-selector__child-remove{cursor:pointer}.umb-group-builder__groups{padding:0;margin:0;list-style:none}.umb-group-builder__group{position:relative;min-height:86px;padding:10px 10px 5px 10px;margin:50px 0 0 0;border:2px solid #ccc;border-radius:0 10px 10px 10px;box-sizing:border-box}.umb-group-builder__group.-active{border-color:#2e8aea}.umb-group-builder__group.-inherited{border-color:#f2f2f2;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__group.-placeholder{display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:86px;font-weight:bold;color:#2e8aea;cursor:pointer;border:1px dashed #d9d9d9;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group.-sortable{min-height:initial;cursor:move}.umb-group-builder__group-actions{position:absolute;top:5px;right:5px;z-index:10;opacity:0;visibility:hidden}.umb-group-builder__group-action{display:inline-block}.umb-group-builder__group-remove{position:absolute;top:-30px;right:20px;font-size:18px}.umb-group-builder__group-remove:hover{color:#0064cd;cursor:pointer}.umb-group-builder__group-title-wrapper{position:absolute;top:-45px;left:-2px;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group-title-wrapper.-placeholder{top:-44px;left:-1px}.umb-group-builder__group-title{display:-webkit-flex;display:-ms-flexbox;display:flex;height:38px;padding:5px 9px 0 9px;font-weight:bold;background:white;border:2px solid #ccc;border-bottom:0;border-radius:10px 10px 0 0;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group-title-icon{margin-left:5px}.umb-group-builder__group-title.-active{border-color:#2e8aea}.umb-group-builder__group-title.-placeholder{width:70px;border:1px dashed #d9d9d9;border-bottom:0}.umb-group-builder__group-title.-inherited{border-color:#f2f2f2}input.umb-group-builder__group-title-input{margin-bottom:0;font-weight:bold;color:#515151;background:transparent;border-color:transparent}.umb-group-builder__group-title-input:hover{border-color:#ccc}.umb-group-builder__group-title-input.-placeholder{border:1px dashed #979797}.umb-group-builder__group-inherited-label{position:relative;top:2px;display:inline-block;margin-left:10px;font-size:13px;color:#d9d9d9}.umb-group-builder__group-sort-order{position:relative;top:2px;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-left:10px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}input.umb-group-builder__group-sort-value{width:40px;padding:0 0 0 5px;margin-bottom:0;font-size:11px;border-color:#d9d9d9}.umb-group-builder__properties{min-height:35px;padding:0;margin:0;list-style:none}.umb-group-builder__property{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;margin-bottom:5px;border:1px solid transparent;border-radius:5px;box-sizing:border-box;-webkit-flex-flow:row;-ms-flex-flow:row;flex-flow:row}.umb-group-builder__property:hover{border:1px dashed #ccc}.umb-group-builder__property.-placeholder{cursor:pointer;background:#fff;border:1px dashed #d9d9d9;border-radius:5px;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__property.-inherited{border:transparent;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__property.-inherited:hover{border:transparent}.umb-group-builder__property.-locked{border:transparent;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__property.-locked:hover{border:transparent}.umb-group-builder__property.-sortable,.umb-group-builder__property.-sortable-locked{min-height:35px;border:0;border-radius:5px;-webkit-animation:none;animation:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__property.-sortable{color:#222;cursor:move;background:#e9e9e9}.umb-group-builder__property.-sortable-locked{padding-left:30px;background:#f8f8f8}.umb-group-builder__property-meta{margin-right:20px;-webkit-flex:0 0 175px;-ms-flex:0 0 175px;flex:0 0 175px}.umb-group-builder__property-meta.-full-width{-webkit-flex:1;-ms-flex:1;flex:1}.umb-group-builder__property-meta-alias{margin-bottom:5px;font-size:10px;line-height:1.5;color:#555;word-break:break-word}.umb-group-builder__property-meta-label textarea{width:100%;min-height:25px;padding:0;margin-bottom:0;overflow:hidden;font-size:14px;font-weight:bold;color:#222;background:transparent;border-color:transparent;box-sizing:border-box;resize:none}.umb-group-builder__property-meta-label textarea.ng-invalid{border:0}.umb-group-builder__property-meta-description textarea{width:100%;min-height:25px;padding:0;margin-bottom:0;overflow:hidden;font-size:12px;line-height:1.5;color:#343434;background:transparent;border-color:transparent;box-sizing:border-box;resize:none}.umb-group-builder__property-preview{position:relative;height:30px;padding:35px 10px 25px 10px;overflow:hidden;background:url("../img/checkered-background-20.png");-webkit-flex:1;-ms-flex:1;flex:1}.umb-group-builder__property-preview:hover{cursor:pointer}.umb-group-builder__property-preview:focus{outline:0}.umb-group-builder__property-preview.-not-clickable:hover{cursor:auto}.umb-group-builder__property-preview .help-inline{display:none!important}.umb-group-builder__property-preview-label{position:absolute;top:0;left:0;z-index:15;padding:3px;font-size:11px;line-height:12px;text-transform:uppercase;background:#f8f8f8;opacity:.8}.umb-group-builder__property-actions{margin:15px 0 0 15px;text-align:center;-webkit-flex:0 0 40px;-ms-flex:0 0 40px;flex:0 0 40px}.umb-group-builder__property-action{position:relative;display:block;margin:0 0 10px 0;font-size:18px;cursor:pointer}.umb-group-builder__property-action:hover{color:#0064cd}.umb-group-builder__property-tags{position:absolute;top:0;left:0;z-index:20;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-group-builder__property-tags.-right{right:0;left:auto}.umb-group-builder__property-tag{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0 4px;margin-left:10px;font-size:11px;background-color:#d9d9d9}.umb-group-builder__property-tag:first-child{margin-left:0}.umb-group-builder__property-tag.-white{background-color:#fff}.umb-group-builder__property-tag-icon{margin-right:3px}.umb-group-builder__placeholder-box{display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:12px;font-weight:bold;color:#2e8aea;background:#e9e9e9;box-sizing:border-box;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-group-builder__placeholder-box.-input{height:10px;margin-bottom:5px}.umb-group-builder__placeholder-box.-input-small{width:25%;height:5px;margin-bottom:5px}.umb-group-builder__placeholder-box.-text-full-width{height:3px;margin-bottom:3px}.umb-group-builder__placeholder-box.-text-short{width:75%;height:3px;margin-bottom:3px}.umb-group-builder__group-sortable-placeholder{margin:0 0 70px 0;background:transparent;border:1px dashed #d9d9d9;border-radius:10px;border-radius:5px}.umb-group-builder__property_sortable-placeholder{margin-bottom:5px;background:transparent;border:1px dashed #d9d9d9;border-radius:5px}.umb-group-builder__no-data-text{padding-top:50px;font-size:16px;line-height:1.8em;color:#ccc;text-align:center}.umb-overlay .show-validation .ng-invalid-val-required-component .editor-placeholder{color:#9d261d;border-color:#9d261d}.content-type-editor-dialog.edit-property-settings .validation-wrapper{position:relative}.content-type-editor-dialog.edit-property-settings .validation-label{position:absolute;top:50%;right:0;font-size:12px;color:#9d261d;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.content-type-editor-dialog.edit-property-settings textarea.editor-label{width:100%;padding-left:0;margin-bottom:0;font-size:16px;font-weight:bold;line-height:1.5em;border:0;border-color:transparent;box-shadow:none;box-sizing:border-box;resize:none}.content-type-editor-dialog.edit-property-settings textarea.editor-label:focus{outline:0;box-shadow:none!important}.content-type-editor-dialog.edit-property-settings .editor-placeholder{display:block;width:100%;height:80px;font-size:13px;font-weight:bold;line-height:80px;color:#555;color:#2e8aea;text-align:center;border:1px dashed #d9d9d9;border-radius:5px}.content-type-editor-dialog.edit-property-settings .editor-placeholder:hover{text-decoration:none}.content-type-editor-dialog.edit-property-settings .editor{margin-bottom:10px}.content-type-editor-dialog.edit-property-settings .editor .editor-icon-wrapper{float:left;width:60px;height:60px;margin-right:20px;line-height:60px;text-align:center;border:1px solid #d9d9d9;border-radius:5px}.content-type-editor-dialog.edit-property-settings .editor .editor-icon-wrapper .icon{font-size:26px}.content-type-editor-dialog.edit-property-settings .editor .editor-details{float:left;margin-top:10px}.content-type-editor-dialog.edit-property-settings .editor .editor-details .editor-name{display:block;font-weight:bold}.content-type-editor-dialog.edit-property-settings .editor .editor-details .editor-editor{display:block;font-size:12px}.content-type-editor-dialog.edit-property-settings .editor .editor-settings-icon{margin-top:8px;font-size:18px}.content-type-editor-dialog.edit-property-settings .checkbox{margin-bottom:20px}.content-type-editor-dialog.edit-property-settings .editor-description,.content-type-editor-dialog.edit-property-settings .editor-validation-pattern{min-width:100%;min-height:25px;overflow:hidden;border:0;box-sizing:border-box;resize:none}.content-type-editor-dialog.edit-property-settings .umb-dropdown{width:100%}.umb-list-view-settings__box{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:15px;background:#f8f8f8;border:1px solid #ccc;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-list-view-settings__trigger{margin-bottom:20px}.umb-list-view-settings__box.-open{border-bottom:transparent}.umb-list-view-settings__content{display:-webkit-flex;display:-ms-flexbox;display:flex}.umb-list-view-settings__list-view-icon{margin-right:10px;font-size:20px;color:#d9d9d9}.umb-list-view-settings__name{float:left;margin-right:5px;font-size:14px;font-weight:bold}.umb-list-view-settings__create-new{font-size:12px;color:#2e8aea}.umb-list-view-settings__remove-new{font-size:12px;color:#9d261d}.umb-list-view-settings__settings{padding:20px;border:1px dashed #d9d9d9;border-top:0}.umb-table{display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:640px;border:1px solid #d9d9d9;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-table a{text-decoration:none;cursor:pointer}.umb-table a:focus{text-decoration:none;outline:0}input.umb-table__input{margin:0 auto}.umb-table-head{font-size:11px;font-weight:600;text-transform:uppercase;background-color:#f8f8f8}.umb-table-head__link{position:relative;color:rgba(85,85,85,0.75);text-decoration:none;cursor:default}.umb-table-head__link:hover{color:rgba(85,85,85,0.75);text-decoration:none}.umb-table-head__link .sortable:hover{color:#000;text-decoration:none;cursor:pointer}.umb-table-thead__icon{position:absolute;padding-top:1px;padding-left:3px;font-size:13px;cursor:default}.umb-table-thead .sortable:hover{text-decoration:none;cursor:pointer}.umb-table-body .umb-table-row{font-size:13px;color:rgba(85,85,85,0.75);border-top:1px solid #d9d9d9}.umb-table-body .umb-table-row:hover{background-color:rgba(248,248,248,0.9)}.umb-table-body__link{text-decoration:none}.umb-table-body__link:hover{text-decoration:underline}.umb-table-body__icon{margin:0 auto;font-size:22px;color:#0064cd}.umb-table-body__checkicon{display:none;font-size:16px}.umb-table-body .umb-table__name{font-size:14px;font-weight:bold;color:#000}.umb-table-body__empty{height:100%;padding:20px 0;font-size:16px;color:#555;text-align:center}.-selected .umb-table-body__fileicon{display:none}.-selected .umb-table-body__checkicon{display:inline-block}.-content .-unpublished .umb-table__name>*{opacity:.4}.-content .-unpublished .umb-table-body__icon{opacity:.4}.-selected.-unpublished .umb-table-body__icon{opacity:1}.umb-table-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-table-row.-selected,.umb-table-row.-selected:hover{background-color:rgba(0,100,205,0.04)}.umb-table-cell{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:6px 2px;margin:auto 14px;text-align:left;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-flex:1 1 1%;-ms-flex:1 1 1%;flex:1 1 1%}.umb-table-cell>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default}.umb-table-cell:first-of-type{padding:15px 0;margin:0 0 0 15px;-webkit-flex:0 0 25px;-ms-flex:0 0 25px;flex:0 0 25px}.umb-table__name{max-width:25%;-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%}.umb_confirm-action__overlay{position:absolute;z-index:999999;display:-webkit-flex;display:-ms-flexbox;display:flex}.umb_confirm-action__overlay.-top{top:-50px;right:auto;bottom:auto;left:0;-webkit-animation:fadeInUp .2s;animation:fadeInUp .2s;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action{margin-bottom:5px}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action.-confirm{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action.-cancel{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-right{top:0;right:-50px;bottom:auto;left:auto;-webkit-animation:fadeInLeft .2s;animation:fadeInLeft .2s;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action{margin-left:5px}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action.-confirm{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action.-cancel{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-bottom{top:auto;right:auto;bottom:-50px;left:0;-webkit-animation:fadeInDown .2s;animation:fadeInDown .2s;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action{margin-top:5px}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action.-confirm{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action.-cancel{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-left{top:0;right:auto;bottom:auto;left:-50px;-webkit-animation:fadeInRight .2s;animation:fadeInRight .2s;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action{margin-right:5px}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action.-confirm{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action.-cancel{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay-action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:20px;height:20px;font-size:18px;color:#fff;border-radius:40px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb_confirm-action__overlay-action:hover{color:#fff;text-decoration:none}.umb_confirm-action__overlay-action.-confirm{color:#46a546!important;background:#fff}.umb_confirm-action__overlay-action.-confirm:hover{color:#46a546!important}.umb_confirm-action__overlay-action.-cancel{color:#9d261d!important;background:#fff}.umb_confirm-action__overlay-action.-cancel:hover{color:#9d261d!important}.umb-keyboard-shortcuts-overview__overlay{position:fixed;top:0;left:0;z-index:1000;width:100%;height:100%;padding-left:440px;background:#fff;box-sizing:border-box;-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-keyboard-shortcuts-overview__overlay-close{position:absolute;top:10px;right:10px}.umb-keyboard-shortcuts-overview__overlay-content{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:100%;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-shortcuts-overview__keyboard-shortcuts-group{width:50%;margin-bottom:10px}.umb-keyboard-shortcuts-overview__keyboard-shortcuts-group-name{margin-bottom:0}.umb-keyboard-shortcuts-overview__keyboard-shortcut{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:7px 0;border-bottom:1px solid #d9d9d9;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-shortcuts-overview__keyboard-shortcut.-no-air{padding:0}.umb-keyboard-shortcuts-overview__keyboard-shortcut.-no-stroke{border-bottom:0}.umb-keyboard-shortcuts-overview__description{margin-right:10px;font-size:13px;font-weight:bold}.umb-keyboard-keys{display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:12px;list-style:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-key-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-right:5px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-key{padding:1px 7px;margin-right:5px;color:#555;background:#fff;border:1px solid #d9d9d9;border-radius:5px}.umb-checkbox-list{margin-left:0;list-style:none}.umb-checkbox-list__item{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:2px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-checkbox-list__item:last-child{border-bottom:0}.umb-checkbox-list__item:hover{background-color:#f8f8f8}.umb-checkbox-list__item.-selected,.umb-checkbox-list__item.-disabled{font-weight:bold;background-color:rgba(0,100,205,0.04)}.umb-checkbox-list__item-checkbox{display:-webkit-flex;display:-ms-flexbox;display:flex;height:30px;margin-right:10px;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:0 0 30px;-ms-flex:0 0 30px;flex:0 0 30px}.umb-checkbox-list__item-checkbox.-selected{background:#2e8aea}.umb-checkbox-list__item-icon{margin-right:5px}.umb-checkbox-list__item-text{margin-bottom:0;font-size:13px;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}.umb-checkbox-list__item-text.-faded{opacity:.5}.umb-checkbox-list__item.-disabled .umb-checkbox-list__item-text{cursor:not-allowed}.umb-checkbox-list__item-caption{margin-left:2px;font-size:11px}.umb-checkbox-list__no-data{padding-top:50px;font-size:16px;line-height:1.8em;color:#ccc;text-align:center}.umb-locked-field{position:relative;display:block;font-size:13px;color:#ccc}.umb-locked-field__wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:5px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-locked-field__toggle{margin-right:3px}.umb-locked-field__toggle:hover,.umb-locked-field__toggle:focus{text-decoration:none}.umb-locked-field__lock-icon{color:#ccc;transition:color .25s}.umb-locked-field__lock-icon.-unlocked{color:#515151}input.umb-locked-field__input{padding:0;margin-bottom:0;font-size:13px;color:#ccc;background:rgba(255,255,255,0);border-color:transparent!important;transition:color .25s}input.umb-locked-field__input:focus{box-shadow:none!important}input.umb-locked-field__input.-unlocked{color:#515151}.umb-nav-tabs{position:absolute;z-index:10}.umb-nav-tabs.-padding-left{padding-left:20px}.umb-tab-content{position:relative;top:22px;padding-top:20px;border-top:1px solid #d9d9d9}.umb-load-indicator{position:absolute;top:50%;left:50%;padding:0;margin:0;font-size:0;list-style:none;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-load-indicator__bubble{position:absolute;top:50%;left:0;width:0;width:6px;height:0;height:6px;margin:0;border:2px solid #2e8aea;border-radius:100%;-webkit-transform:transformZ(0);transform:transformZ(0);-webkit-animation:umbLoadIndicatorAnimation 1.4s infinite;animation:umbLoadIndicatorAnimation 1.4s infinite}.umb-load-indicator__bubble:nth-child(1n){left:-16px;-webkit-animation-delay:0s;animation-delay:0s}.umb-load-indicator__bubble:nth-child(2n){left:0;-webkit-animation-delay:.15s;animation-delay:.15s}.umb-load-indicator__bubble:nth-child(3n){left:16px;-webkit-animation-delay:.30s;animation-delay:.30s}@-webkit-keyframes umbLoadIndicatorAnimation{0%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}50%{background:white;-webkit-transform:scale(1);transform:scale(1)}100%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}}@keyframes umbLoadIndicatorAnimation{0%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}50%{background:white;-webkit-transform:scale(1);transform:scale(1)}100%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}}.umb-breadcrumbs{margin-bottom:0;margin-left:0;list-style:none}.umb-breadcrumbs__ancestor{display:inline-block}.umb-breadcrumbs__ancestor-link,.umb-breadcrumbs__ancestor-text{font-size:11px;color:#555}.umb-breadcrumbs__ancestor-link{text-decoration:underline}.umb-breadcrumbs__ancestor-link:hover{color:#000}.umb-breadcrumbs__seperator{position:relative;top:1px;margin-right:5px;margin-left:5px;color:#ccc}.umb-media-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin-bottom:30px;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-media-grid__item{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:10px;overflow:hidden;cursor:pointer;box-shadow:0 1px 1px 0 rgba(0,0,0,0.2);transition:box-shadow 100ms;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.umb-media-grid__item.-file{background-color:#f8f8f8}.umb-media-grid__item.-selected{box-shadow:0 2px 8px 0 rgba(0,0,0,0.35)}.umb-media-grid__item:hover{text-decoration:none}.umb-media-grid__item-image{position:relative;height:auto;max-width:100%!important}.umb-media-grid__item-image-placeholder{position:relative;width:100%;height:auto;max-width:100%}.umb-media-grid__image-background{position:absolute;top:0;right:0;bottom:0;left:0;background:url("../img/checkered-background.png");background-repeat:repeat;content:"";opacity:.5}.umb-media-grid__item-overlay{position:absolute;right:0;bottom:0;left:0;z-index:100;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 10px;overflow:hidden;font-size:11px;color:white;white-space:nowrap;background:#2e8aea;opacity:0;box-sizing:border-box;transition:opacity 150ms}.umb-media-grid__item.-file .umb-media-grid__item-overlay{color:#555;background:#f8f8f8;opacity:1}.umb-media-grid__item.-file:hover .umb-media-grid__item-overlay,.umb-media-grid__item.-file.-selected .umb-media-grid__item-overlay{color:#fff;background:#2e8aea}.umb-media-grid__info{margin-right:5px}.umb-media-grid__item-overlay.-locked{opacity:1}.umb-media-grid__item:hover .umb-media-grid__item-overlay{opacity:1}.umb-media-grid__item-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.umb-media-grid__item-icon{position:absolute;top:45%;left:50%;font-size:40px!important;color:#555;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-media-grid__checkmark{position:absolute;top:10px;right:10px;z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:7px;color:#fff;background:#2e8aea;border:1px solid #fff;border-radius:50px;box-sizing:border-box;transition:background 100ms;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin-bottom:30px;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-folder-grid__folder{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 20px;margin:5px;cursor:pointer;background:#f8f8f8;border:1px solid transparent;box-sizing:border-box;transition:border .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:1 1 200px;-ms-flex:1 1 200px;flex:1 1 200px;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-folder-grid__folder:focus,.umb-folder-grid__folder:active{text-decoration:none}.umb-folder-grid__folder:hover{text-decoration:none;background-color:#ebebeb}.umb-folder-grid__folder-description{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid__folder-icon{margin-right:15px;font-size:20px;color:#000}.umb-folder-grid__folder-name{font-size:13px;font-weight:bold;color:#000}.umb-folder-grid__folder-name:hover{text-decoration:underline}.umb-folder-grid__action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:7px;color:#fff;cursor:pointer;background:rgba(0,0,0,0.4);border:1px solid #fff;border-radius:50px;opacity:0;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid__action.-selected{background:#2e8aea;opacity:1}.umb-content-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-content-grid__item{position:relative;margin:10px;cursor:pointer;background:#f8f8f8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-flex:0 1 200px;-ms-flex:0 1 200px;flex:0 1 200px}.umb-content-grid__item.-selected{box-shadow:0 2px 8px 0 rgba(0,0,0,0.35)}.umb-content-grid__icon-container{display:-webkit-flex;display:-ms-flexbox;display:flex;height:75px;background:#ebebeb;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-content-grid__icon{font-size:40px;color:#888}.umb-content-grid__icon.-light{color:#d9d9d9}.umb-content-grid__content{padding:15px;box-sizing:border-box}.umb-content-grid__item-name{display:inline-block;padding-bottom:10px;margin-bottom:10px;font-weight:bold;line-height:1.4em;color:black;border-bottom:1px solid #d9d9d9}.umb-content-grid__item-name:hover{text-decoration:underline}.umb-content-grid__item-name.-light{color:#d9d9d9}.umb-content-grid__details-list{margin-bottom:0;margin-left:0;font-size:11px;list-style:none}.umb-content-grid__details-list.-light{color:#d9d9d9}.umb-content-grid__details-label{display:inline-block;font-weight:bold}.umb-content-grid__details-value{display:inline-block}.umb-content-grid__checkmark{position:absolute;top:10px;right:10px;display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;color:#fff;cursor:pointer;background:#2e8aea;border:1px solid #fff;border-radius:50px;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-content-grid__item:hover .umb-content-grid__action:not(.-selected){opacity:1;-webkit-animation:fadeIn;animation:fadeIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}.umb-content-grid__no-items{padding-top:50px;padding-bottom:50px;font-size:16px;font-weight:bold;color:#d9d9d9}.umb-layout-selector{position:relative;display:inline-block}.umb-layout-selector__active-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;width:30px;height:30px;font-size:20px;cursor:pointer;border:1px solid transparent;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-layout-selector__active-layout:hover{border-color:#d9d9d9}.umb-layout-selector__dropdown{position:absolute;left:50%;z-index:999;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;background:#333;background:#fff;-webkit-transform:translate(-50%,0);transform:translate(-50%,0);-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-layout-selector__dropdown-item{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;margin:3px 5px;cursor:pointer;border:1px solid transparent;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-layout-selector__dropdown-item:hover{border:1px solid #d9d9d9}.umb-layout-selector__dropdown-item.-active{border:1px solid #2e8aea}.umb-layout-selector__dropdown-item-icon{font-size:20px;color:#555;text-align:center}.umb-tooltip{position:fixed;z-index:1000;display:-webkit-flex;display:-ms-flexbox;display:flex;max-width:200px;padding:10px;margin-top:15px;font-size:12px;background:#fff;-webkit-animation:fadeIn;animation:fadeIn;-webkit-animation-duration:.1s;animation-duration:.1s;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}.umb-tooltip-list{padding:10px;margin-bottom:0;margin-left:0;list-style:none}.umb-tooltip-list__item{margin-bottom:5px}.umb-tooltip-list__item:last-child{margin-bottom:0}.umb-tooltip-list__item-label{margin-bottom:-3px;font-weight:bold}.umb-overlay-backdrop{position:fixed;top:0;left:0;z-index:2000;width:100%;height:100%;background-color:rgba(255,255,255,0.5);-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-grid IFRAME{overflow:hidden}.umb-grid .ui-sortable-helper{position:absolute!important;width:42px!important;height:42px!important;padding:5px;overflow:hidden;font-family:"icomoon";text-align:center;background-color:#2e8aea!important;border-radius:2px;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.umb-grid .ui-sortable-helper:after{font-size:22px;line-height:42px;color:#fff;content:"\e126"}.umb-grid .ui-sortable-helper *{display:none}.umb-grid .ui-sortable-helper .umb-row-title-bar,.umb-grid .ui-sortable-helper .cell-tools-add{display:none!important}.umb-grid .ui-sortable-placeholder{position:absolute;right:0;left:0;height:2px;margin-bottom:20px;background-color:#2e8aea}.umb-grid .ui-sortable-placeholder:before,.umb-grid .ui-sortable-placeholder:after{position:absolute;top:-9px;font-family:"icomoon";font-size:18px;color:#2e8aea}.umb-grid .ui-sortable-placeholder:before{left:-5px;content:"\e0e9"}.umb-grid .ui-sortable-placeholder:after{right:-5px;content:"\e0d7"}.umb-grid .umb-cell .ui-sortable-placeholder{right:10px;left:10px}.umb-grid-width{width:100%;margin:20px auto}.umb-grid .right{float:right}.umb-grid .tb{width:100%}.umb-grid .td{display:inline-block;width:100%;vertical-align:top;box-sizing:border-box}.umb-grid .middle{text-align:center}.umb-grid .mainTd{position:relative}.umb-grid .umb-column{position:relative}.umb-grid .umb-row{position:relative;padding-top:10px;margin-bottom:40px}.umb-grid .row-tools a{text-decoration:none}.umb-grid .umb-cell{position:relative}.umb-grid .umb-cell-content{position:relative;display:block;margin:10px;border:1px solid transparent;box-sizing:border-box}.umb-grid .umb-row .umb-cell-placeholder{min-height:130px;background-color:#f8f8f8;border-color:#d9d9d9;border-style:dashed;border-width:2px;transition:border-color 100ms linear}.umb-grid .umb-row .umb-cell-placeholder:hover{cursor:pointer;border-color:#2e8aea}.umb-grid .umb-cell-content.-has-editors{padding-top:38px;background-color:#fff;border-color:#d9d9d9;border-style:solid;border-width:1px}.umb-grid .umb-cell-content.-has-editors:hover{cursor:auto}.umb-grid .umb-cell-content.-has-editors.-collapsed{padding-top:10px}.umb-grid .cell-tools{position:absolute;top:10px;right:10px;font-size:16px;color:#555}.umb-grid .cell-tool{float:right;cursor:pointer}.umb-grid .cell-tool:hover{color:#0064cd}.umb-grid .cell-tools-add{color:#2e8aea}.umb-grid .cell-tools-add:focus,.umb-grid .cell-tools-add:hover{text-decoration:none}.umb-grid .cell-tools-add.-center{position:absolute;top:50%;left:50%;color:#2e8aea;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-grid .cell-tools-add.-bar{display:block;padding:5px;margin:10px;text-align:center;background:#f8f8f8;border:1px dashed #ccc}.umb-grid .cell-tools-remove{position:relative;display:inline-block}.umb-grid .cell-tools-remove .iconBox:hover,.umb-grid .cell-tools-remove .iconBox:hover *{background:#9d261d!important;border-color:#9d261d!important}.umb-grid .cell-tools-move{display:inline-block}.umb-grid .cell-tools-edit{display:inline-block;color:#fff}.umb-grid .drop-overlay{position:absolute;top:0;left:0;z-index:10;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:100%;font-size:14px;font-weight:bold;line-height:1.3em;text-align:center;background:#fff;opacity:.9;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.drop-overlay.-disable{color:#9d261d}.drop-overlay.-allow{color:#46a546}.umb-grid .drop-overlay .drop-icon{margin-bottom:20px;font-size:40px}.umb-grid .umb-control{position:relative;display:block;margin-right:10px;margin-bottom:10px;margin-left:10px;overflow:hidden}.umb-control-collapsed{padding:5px 10px;cursor:move;background-color:#f8f8f8;border-color:transparent;border-style:solid;border-width:1px}.umb-control-collapsed:hover{border-color:#2e8aea}.umb-grid .umb-control-click-overlay{position:absolute;top:0;left:0;z-index:5;width:100%;height:100%;cursor:pointer;opacity:0}.umb-grid .umb-control-click-overlay:hover{background-color:#2e8aea;opacity:.1;transition:opacity .1s}.umb-grid .umb-row-title-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;padding-right:10px;padding-left:10px}.umb-grid .umb-row-title{display:inline-block;margin-right:6px;font-size:15px;font-weight:bold;color:#000;cursor:pointer}.umb-grid .row-tools{display:inline-block;margin-left:10px;font-size:18px;color:#555}.umb-grid .row-tool{cursor:pointer}.umb-grid .umb-add-row{text-align:center}.umb-grid .umb-control-placeholder{position:relative;min-height:20px;text-align:center;text-align:-moz-center;cursor:text}.umb-grid .umb-control-placeholder .placeholder{height:20px;padding:5px;font-size:14px;text-align:left;border:1px solid rgba(182,182,182,0.3);opacity:.7}.umb-grid .umb-control-placeholder:hover .placeholder{border:1px solid rgba(182,182,182,0.8)}.umb-grid .umb-editor-placeholder{position:relative;min-height:65px;padding:20px;padding-bottom:30px;text-align:center;text-align:-moz-center;cursor:pointer;background-color:#fff;border:4px dashed #d9d9d9}.umb-grid .umb-editor-placeholder i{display:block;margin-bottom:10px;font-size:85px;line-height:85px;color:#d9d9d9}.umb-grid .umb-row.-active{background-color:#2e8aea}.umb-grid .umb-row.-active .umb-row-title-bar{cursor:move}.umb-grid .umb-row.-active .row-tool,.umb-grid .umb-row.-active .umb-row-title{color:#fff}.umb-grid .umb-row.-active .umb-grid-has-config{color:rgba(255,255,255,0.66)}.umb-grid .umb-row.-active .umb-cell .umb-grid-has-config{color:rgba(0,0,0,0.44)}.umb-grid .umb-row.-active .umb-cell .umb-cell-content{border-color:transparent}.umb-grid .umb-row.-active-child{background-color:#f8f8f8}.umb-grid .umb-row.-active-child .umb-row-title-bar{cursor:default}.umb-grid .umb-row.-active-child .umb-row-title{color:#555}.umb-grid .umb-row.-active-child .row-tool{color:rgba(0,0,0,0.23)}.umb-grid .umb-row.-active-child .umb-grid-has-config{color:rgba(0,0,0,0.44)}.umb-grid .umb-row.-active-child .umb-cell-content.-placeholder{border-color:#d9d9d9}.umb-grid .umb-row.-active-child .umb-cell-content.-placeholder:hover{border-color:rgba(85,85,85,0.44)}.umb-grid .umb-row .umb-cell.-active{border-color:#d9d9d9}.umb-grid .umb-row .umb-cell.-active .umb-cell-content.-has-editors{border-color:#2e8aea;box-shadow:3px 3px 6px rgba(0,0,0,0.07)}.umb-grid .umb-row .umb-cell.-active-child .cell-tool{color:rgba(0,0,0,0.23)}.umb-grid .umb-row .umb-cell.-active-child .umb-cell-content.-has-editors{border-color:rgba(113,136,160,0.44)}.umb-grid .umb-row-title-bar{display:-webkit-flex;display:-ms-flexbox;display:flex}.umb-grid .umb-grid-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-grid .umb-tools{margin-left:auto}.umb-grid-add-more-content{text-align:center}.umb-grid .newbtn{display:inline-block;width:auto;padding:6px 15px;margin:10px auto 20px;font-size:12px;font-weight:bold;border-color:#e2e2e2;border-style:solid}.umb-grid .newbtn:hover{cursor:pointer;opacity:.77}.umb-grid textarea.textstring{display:block;overflow:hidden;color:#555;background:#fff;border:0;outline:0;resize:none}.umb-grid .umb-cell-rte textarea{display:none!important}.umb-grid .umb-cell-media .caption{display:block;width:98%;overflow:hidden;font-style:italic;background:#fff;border:0;outline:0;resize:none}.umb-grid .cellPanelRte{min-height:60px}.umb-grid .umb-cell-embed iframe{width:100%}.umb-grid .umb-cell-rte{border-color:transparent}.umb-grid .iconBox{display:inline-block;padding:4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.umb-grid .iconBox:hover,.umb-grid .iconBox:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.umb-grid .iconBox span.prompt{display:block;text-align:center;white-space:nowrap}.umb-grid .iconBox span.prompt>a{text-decoration:underline}.umb-grid .iconBox a:hover{color:white!important;text-decoration:none}.umb-grid .iconBox.selected{background:transparent;background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-radius:3px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-webkit-appearance:none}.umb-grid .iconBox i{display:block;font-size:16px!important;color:#5f5f5f}.umb-grid .help-text{display:inline-block;clear:both;font-size:14px;font-weight:bold;color:#000}.umb-grid .mce-panel{clear:both;background:transparent!important;border:none!important}.umb-grid .mce-btn button{padding:8px 6px;line-height:inherit}.umb-grid .mce-toolbar{display:none;background-color:#fafafa;border-bottom:1px solid rgba(207,207,207,0.7)}.umb-grid .umb-control.-active .mce-toolbar{display:block}.umb-grid .mce-flow-layout-item{margin:0}.umb-grid .mceContentBody{overflow-y:hidden!important}.umb-grid .fullSizeImage{width:100%}.umb-grid .boxWidth{margin-bottom:10px;text-align:right}.umb-grid .boxWidth input{width:40px;text-align:center}.umb-grid .boxWidth label{padding:0;margin:5px 5px 0 0;font-size:10px;color:#808080}.umb-grid .umb-control{border-color:transparent;border-style:solid;border-width:1px}.umb-grid .umb-control.-active{border-color:#2e8aea}.umb-grid .umb-templates-columns{margin-top:30px}.umb-grid .umb-control-inner{position:relative}.umb-grid .umb-control-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;padding:2px 5px;font-size:10px;color:#fff;background:#2e8aea;opacity:0;transition:height 80ms linear,opacity 80ms linear;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-grid .umb-control-title{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-grid .umb-control.-active .umb-control-bar{height:25px;cursor:move;opacity:1}.umb-grid .umb-control-tools{display:inline-block;margin-left:10px}.umb-grid .umb-control-tool{position:relative;display:inline-block;margin-right:5px;font-size:16px;cursor:pointer}.umb-grid .umb-templates{width:100%;overflow:hidden;text-align:center}.umb-grid .umb-templates-template{display:inline-block;width:100px;padding-right:30px;margin:20px}.umb-grid .umb-templates-template a.tb:hover{border:5px solid #2e8aea}.umb-grid .umb-templates-template .tb{position:relative;width:100%;height:150px;padding:10px;cursor:pointer;background-color:#f8f8f8;border:5px solid #d9d9d9}.umb-grid .umb-templates-template .tr{position:relative;height:100%}.umb-grid .umb-templates-template .tb .umb-templates-column{height:100%;border:1px dashed #d9d9d9;border-right:0}.umb-grid .umb-templates-template .tb .umb-templates-column.last{border-right:1px dashed #d9d9d9!important}.umb-grid a.umb-templates-column:hover,.umb-grid a.umb-templates-column.selected{background-color:#2e8aea}.umb-grid .templates-preview{display:inline-block;width:100%;text-align:center}.umb-grid .templates-preview small{position:absolute;bottom:-25px;left:0;width:100%;padding-top:15px}.umb-grid .templates-preview .help-text{margin:35px 35px 0 0}.umb-grid .preview-rows{position:relative;display:inline-block;width:125px;margin:15px;border:3px solid #d9d9d9;box-sizing:border-box;transition:border 100ms linear}.umb-grid .preview-rows.prevalues-rows{float:left;width:80px;margin:0 20px 20px 0}.umb-grid .preview-rows.prevalues-templates{float:left;margin:0 20px 20px 0}.umb-grid .preview-rows:hover{cursor:pointer;border-color:#2e8aea}.umb-grid .preview-rows .preview-row{display:inline-block;width:100%;vertical-align:bottom}.umb-grid .preview-rows.layout{padding:2px}.umb-grid .preview-rows.layout .preview-row{height:100%}.umb-grid .preview-rows.layout .preview-col{height:180px}.umb-grid .preview-rows.layout .preview-cell{background-color:#f8f8f8}.umb-grid .preview-rows.layout .preview-overlay{display:none}.umb-grid .preview-rows.columns{min-height:16px;padding:1px;line-height:11px}.umb-grid .preview-rows.columns.prevalues-rows{min-height:30px}.umb-grid .preview-rows .preview-col{display:block;float:left;width:33.3%;height:10px;margin:0;border:1px solid #fff;box-sizing:border-box}.umb-grid .preview-rows .preview-col .preview-cell{display:block;width:100%;height:100%;margin:0 1px 1px 0;background-color:#d9d9d9}.umb-grid .preview-rows.prevalues-templates .preview-col{height:80px}.umb-grid .preview-overlay{position:absolute;top:0;left:0;display:block;width:100%;height:100%;border:3px solid white;box-sizing:border-box}.umb-grid .umb-grid-has-config{display:inline;font-size:12px;color:rgba(0,0,0,0.44)}.umb-grid .umb-cell .umb-grid-has-config{position:absolute;top:10px;left:10px}.umb-grid .cell-tools-menu{position:absolute;top:0;left:50%;z-index:6660;width:360px;height:380px;padding:7px;margin-top:-270px;margin-left:-150px;overflow:auto;background:#fff;border:1px solid #ccc;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.umb-grid .cell-tools-menu h5{padding:10px;margin-top:0;color:#999;border-bottom:1px solid #d9d9d9}.umb-grid .elements{display:block;padding:0;margin:0}.umb-grid .elements li{display:inline-block;width:90px;height:80px;padding:5px;margin:5px;overflow:hidden;font-size:11px}.umb-grid .elements li:hover,.umb-grid .elements li:hover *{color:#fff;background:#2e8aea}.umb-grid .elements a{color:#222;text-decoration:none}.umb-grid .elements i{display:block;font-size:30px;line-height:50px;color:#999}.umb-grid-configuration .umb-templates{text-align:left}.umb-grid-configuration ul{display:block}.umb-grid-configuration ul li{display:block;width:auto;text-align:left}.umb-grid-configuration .umb-templates .umb-templates-template .tb{max-height:50px;padding:0;overflow:hidden;border-spacing:2px;border-width:2px!important}.umb-grid-configuration .umb-templates .umb-templates-template span{display:inline-block;background:#d9d9d9}.umb-grid-configuration .umb-templates .umb-templates-template .tb:hover{border-width:2px!important}.umb-grid-configuration .umb-templates-column{display:block;float:left;margin-left:-1px;background:#d9d9d9;border:1px white solid!important}.umb-grid-configuration .umb-templates-column.last{margin-right:-1px}.umb-grid-configuration .umb-templates-column.add{font-size:20px;line-height:70px;color:#ccc;text-align:center;text-decoration:none;background:#fff}.umb-grid-configuration .mainTdpt{height:initial;border:0}.umb-grid-configuration .umb-templates-rows .umb-templates-row{width:60px;margin:0 50px 20px 0}.umb-grid-configuration .umb-templates-rows .umb-templates-row .tb{padding:0;border-spacing:2px;border-width:2px!important}.umb-grid-configuration .umb-templates-rows .mainTdpt{height:10px!important}.umb-grid-configuration a.umb-templates-column{height:70px!important}.umb-empty-state{font-size:16.099999999999998px;line-height:1.8em;color:#999;text-align:center}.umb-empty-state.-small{font-size:11.9px}.umb-empty-state.-large{font-size:17.5px}.umb-empty-state.-center{position:absolute;top:50%;left:50%;width:80%;max-width:400px;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-property-editor.-not-clickable{pointer-events:none}.umb-button{position:relative;overflow:hidden}.umb-button__button:focus{outline:0}.umb-button__content{display:-webkit-flex;display:-ms-flexbox;display:flex;opacity:1;transition:opacity .25s ease;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-button__icon{margin-right:5px}.umb-button__content.-hidden{opacity:0}.umb-button__progress{position:absolute;top:50%;left:50%;z-index:100;width:14px;height:14px;margin-top:-9px;margin-left:-9px;border:2px solid #ccc;border-left-color:#46a546;border-radius:40px;opacity:1;-webkit-animation:rotating .4s linear infinite;animation:rotating .4s linear infinite;transition:opacity .25s ease}.umb-button__progress.-hidden{z-index:0;opacity:0}.umb-button__progress.-white{border-color:rgba(255,255,255,0.4);border-left-color:#fff}.umb-button__success,.umb-button__error{position:absolute;top:50%;left:50%;z-index:10;font-size:20px;color:#46a546;opacity:1;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);transition:opacity .25s ease}.umb-button__success{color:#46a546}.umb-button__error{color:#9d261d}.umb-button__success.-hidden,.umb-button__error.-hidden{z-index:0;opacity:0}.umb-button__success.-white,.umb-button__error.-white{color:#fff}.umb-button__overlay{position:absolute;z-index:10;width:100%;height:100%;background:#fff;opacity:0}@-webkit-keyframes rotating{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.umb-button-group__toggle{padding-right:8px;padding-left:8px;margin-left:-1px}.umb-button-group__sub-buttons.-align-right{right:0;left:auto}.umb-notifications{position:absolute;right:0;bottom:52px;left:0;z-index:1000;padding:0;margin:auto;background:0;border:0;border-bottom:0;border-radius:0}.umb-notifications__notifications{position:relative;margin:0;list-style:none}.umb-notifications__notification{position:relative;padding:5px 20px;margin-bottom:0;font-size:12px;text-shadow:none;border:0;border-radius:0}.umb-notifications__notification.-extra-padding{padding-top:20px;padding-bottom:20px}.umb-file-dropzone-directive .dropzone{position:relative;width:100%;height:400px;padding:50px 0;margin:0 0 20px 0;color:#555;text-align:center;border:1px dashed #d9d9d9;transition:height .8s}.umb-file-dropzone-directive .dropzone .illustration{width:300px}.umb-file-dropzone-directive .dropzone.is-small{height:100px}.umb-file-dropzone-directive .dropzone.is-small .illustration{width:200px}.umb-file-dropzone-directive .dropzone.drag-over{border:1px dashed #343434}.umb-file-dropzone-directive .content{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-file-dropzone-directive .file-select{margin-top:10px;font-size:15px;color:#2e8aea;cursor:pointer}.umb-file-dropzone-directive .file-select:hover{color:#0064cd;text-decoration:underline}.umb-file-dropzone-directive .file-list{padding:10px 20px;margin:0 0 30px 0;list-style:none;background:#f8f8f8}.umb-file-dropzone-directive .file-list .file{position:relative;display:block;width:100%;padding:5px 0;border-top:1px solid #d9d9d9}.umb-file-dropzone-directive .file-list .file:first-child{border-top:0}.umb-file-dropzone-directive .file-list .file.ng-enter{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-list .file.ng-leave{-webkit-animation:fadeOut 2s;animation:fadeOut 2s}.umb-file-dropzone-directive .file-list .file .file-description{display:block;width:100%;font-size:12px;color:#222}.umb-file-dropzone-directive .file-list .file .file-upload-progress{display:block;width:100%}.umb-file-dropzone-directive .file-list .file .file-icon{position:absolute;right:0;bottom:0}.umb-file-dropzone-directive .file-list .file .file-icon .icon{font-size:20px}.umb-file-dropzone-directive .file-list .file .file-icon .icon.ng-enter{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-list .file .file-icon .icon.ng-leave{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-progress{position:relative;height:4px;padding:2px}.umb-file-dropzone-directive .file-progress .file-progress-indicator{position:relative;display:block;width:0;height:100%;overflow:hidden;background-color:#2e8aea;border-radius:20px}.login-overlay{position:absolute;top:0;left:0;z-index:10000;width:100%;height:100%;padding:0;margin:0!Important;color:#fff;background:#1d1d1d url(../img/application/logo.png) no-repeat 25px 30px fixed!important;background-size:30px 30px!important;border-radius:0}.login-overlay .umb-modalcolumn{background:0;border:0}.login-overlay .form{position:fixed;top:100px;left:165px;display:block;width:370px;text-align:right}.login-overlay h1{display:block;font-size:18px;font-weight:normal;color:#fff;text-align:right}.login-overlay .alert.alert-error{display:inline-block;padding-right:6px;padding-left:6px;margin-top:10px;text-align:center}@media(max-width:565px){.login-overlay .form{right:25px;left:inherit}}#hrOr{position:relative;height:30px;padding-top:20px;text-align:center}#hrOr hr{height:1px;margin:0;background-color:#555;border:0}#hrOr div{position:relative;top:-16px;width:20px;height:20px;padding:4px;margin:auto;color:#d9d9d9;background-color:black;border:1px solid #555;border-radius:50%}.umb-editor{min-width:66.6%}.umb-editor-tiny{width:50px}.umb-editor-small{width:90px}.umb-modal .umb-editor{width:95%}.umb-dialog .umb-editor{width:95%}.umb-dialog .umb-control-group .help-block{width:95%}.umb-codeeditor{width:99%}.umb-contentpicker li a:hover .hover-hide,.umb-contentpicker li a .hover-show{display:none}.umb-contentpicker li a:hover .hover-show{display:inline-block}.umb-contentpicker-popover .search-holder{padding:10px}.umb-contentpicker small:not(:last-child){padding-right:3px;border-right:1px solid #999}.umb-contentpicker small a{color:#555}div.umb-codeeditor{border:1px solid #e8e8e8}div.umb-codeeditor .umb-el-wrap{padding:0}div.umb-codeeditor .umb-btn-toolbar{padding:0;margin:0;background:#f7f7f7;border-bottom:#e8e8e8 1px solid}.mce-tinymce{border:1px solid #d9d9d9!important;border-radius:0!important}.mce-panel{background:#f8f8f8!important;border-color:#d9d9d9!important}.mce-btn-group,.mce-btn{background:none!important;border:none!important}.mce-ico{font-size:12px!important;color:#1d1d1d!important}.mce-ico.mce-i-custom[class^="icon-"],.mce-ico.mce-i-custom[class*=" icon-"]{font-family:icomoon;font-size:16px!important}ul.color-picker li{width:60px;padding:2px;margin:3px;border:2px solid transparent}ul.color-picker li.active{border:2px dashed #d9d9d9}ul.color-picker li a{display:block;height:50px;cursor:pointer}.control-group.color-picker-preval .thumbnail{width:30px;border:0}.control-group.color-picker-preval pre{display:inline;margin-right:20px;margin-left:10px}.control-group.color-picker-preval label{padding:6px;border:solid white 1px}.umb-mediapicker .add-link{display:inline-block;width:120px;height:120px;line-height:120px;color:#d9d9d9;text-align:center;text-decoration:none;border:2px #d9d9d9 dashed;transition:all 150ms ease-in-out}.umb-mediapicker .add-link:hover{color:#2e8aea;border-color:#2e8aea}.umb-mediapicker .picked-image{position:absolute;right:10px;bottom:10px;display:none;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:36px;color:red;text-align:center;text-decoration:none;background:white;border-radius:15px;opacity:.5}.umb-thumbnails{position:relative}.umb-thumbnails i{margin:auto}.umb-thumbnails a{border:none!important;outline:0;box-shadow:none!important}.umb-sortable-thumbnails{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0;margin:0;list-style-type:none;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-sortable-thumbnails li{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:2px;margin:5px;background:white;border:1px solid #f8f8f8;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-sortable-thumbnails li:hover a{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-sortable-thumbnails li img{display:block;max-width:100%;max-height:100%;margin:auto;background-image:url(../img/checkered-background.png)}.umb-sortable-thumbnails li img.noScale{max-width:none!important;max-height:none!important}.umb-sortable-thumbnails .icon-holder{text-align:center}.umb-sortable-thumbnails .icon-holder .icon{display:block;font-size:40px;line-height:50px;color:#555}.umb-sortable-thumbnails .umb-sortable-thumbnails__actions{position:absolute;right:10px;bottom:10px;display:-webkit-flex;display:-ms-flexbox;display:flex;text-decoration:none;opacity:0;visibility:hidden;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions{opacity:1;visibility:visible}.umb-sortable-thumbnails .umb-sortable-thumbnails__action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:5px;font-size:16px;color:#222;background:white;border-radius:15px;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-sortable-thumbnails .umb-sortable-thumbnails__action.-red{color:red}.umb-sortable-thumbnails .umb-sortable-thumbnails__action:hover{text-decoration:none}.umb-cropper{position:relative;padding:10px}.umb-cropper img,.umb-cropper-gravity img{position:absolute;top:0;left:0}.umb-cropper img{max-width:initial}.umb-cropper .overlay,.umb-cropper-gravity .overlay{top:0;left:0;z-index:6001;cursor:move}.umb-cropper .viewport{position:relative;margin:auto;overflow:hidden}.umb-cropper-gravity .viewport{position:relative;width:400px;height:300px;overflow:hidden}.umb-cropper .viewport:after{position:absolute;top:0;left:0;z-index:5999;width:100%;height:100%;content:"";-moz-opacity:.75;opacity:.75;filter:alpha(opacity=7);box-shadow:inset 0 0 0 20px #fff,inset 0 0 0 21px rgba(0,0,0,0.1),inset 0 0 20px 21px rgba(0,0,0,0.2)}.umb-cropper-gravity .overlay{width:14px;height:14px;text-align:center;background:#2e8aea;border:3px solid white;border-radius:20px;opacity:.8}.umb-cropper-gravity .overlay i{font-size:26px;line-height:26px;opacity:.8!important}.umb-cropper .crop-container{text-align:center}.umb-cropper .crop-slider{padding:10px 50px 10px 50px;margin-top:10px;vertical-align:middle;border-top:1px solid #f8f8f8}.umb-cropper .crop-slider i{color:#555}.umb-cropper .crop-slider input{width:320px;margin-top:7px}.umb-cropper-gravity .viewport,.umb-cropper-gravity,.umb-cropper-imageholder{display:inline-block}.umb-cropper-imageholder{float:left}.cropList{position:relative;display:inline-block;vertical-align:top}.gravity-container .viewport{width:494px}.imagecropper .umb-sortable-thumbnails{float:left;width:100px;padding-left:2px;margin-left:4px;border-left:2px solid #f8f8f8}.imagecropper .umb-sortable-thumbnails li{display:block}.imagecropper .umb-sortable-thumbnails li.current a,.imagecropper .umb-sortable-thumbnails li.current a:hover{text-decoration:none;background:#eee}.imagecropper .umb-sortable-thumbnails li:first-of-type{padding-top:0;margin-top:0}.imagecropper .umb-sortable-thumbnails li .crop-name,.imagecropper .umb-sortable-thumbnails li .crop-size{display:block;font-size:11px;text-align:left}.imagecropper .umb-sortable-thumbnails li .crop-size{font-size:10px;font-style:italic;color:#666}.imagecropper .umb-sortable-thumbnails li a{display:block;padding:5px}.imagecropper .umb-sortable-thumbnails li a:hover{text-decoration:none;background:#f8f8f8}.imagecropper .umb-sortable-thumbnails li a:hover .crop-text{text-decoration:none}.btn-crop-delete{display:block;text-align:left}.imagecropper .umb-sortable-thumbnails.many{width:210px}.imagecropper .umb-sortable-thumbnails.many li{float:left;width:85px}.imagecropper .umb-sortable-thumbnails.many li:nth-child(2){padding-top:0;margin-top:0}.umb-folderbrowser .add-link{display:inline-block;width:120px;height:120px;line-height:120px;text-align:center;border:1px #d9d9d9 dashed}.umb-upload-drop-zone{margin-bottom:5px}.umb-upload-drop-zone .info,.umb-upload-button-big{display:block;padding:20px;font-size:14px;color:#d9d9d9;text-align:center;background:0;border:1px dashed #d9d9d9;opacity:1}.umb-upload-button-big:hover{color:#d9d9d9}.umb-upload-drop-zone .info i.icon,.umb-upload-button-big i.icon{font-size:55px;line-height:70px}.umb-upload-button-big{display:block}.umb-upload-button-big input{bottom:0;left:0;width:100%;height:100%}.umb-photo-folder .picrow{position:relative;overflow-y:hidden}.umb-photo-folder .picrow div,.umb-photo-preview{position:relative;display:inline-block;padding:0;margin:0;vertical-align:top;border:0}.umb-photo-folder .picrow div a:first-child{width:100%;height:100%}.umb-photo-folder .picrow div.umb-photo{width:100%;height:100%;background-color:#f8f8f8}.umb-photo-folder a:hover{text-decoration:none}.umb-photo-folder .umb-non-thumbnail{font-size:12px;color:#000;text-align:center;text-decoration:none;vertical-align:middle;background:#f8f8f8}.umb-photo-folder .selector-overlay{display:none}.umb-photo-folder .pic:hover .selector-overlay{position:absolute;right:0;bottom:0;left:0;z-index:100;display:block;padding:5px;overflow:hidden;color:white;text-align:center;text-decoration:none;white-space:nowrap;background:black;opacity:.4;text-overlow:ellipsis}.umb-photo-folder .umb-non-thumbnail i{position:relative;top:50%;display:block;margin:auto;font-size:50px;line-height:60px;color:#d9d9d9;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.umb-photo-folder .umb-non-thumbnail span{position:absolute;top:20px;display:block;width:100%;margin:auto}.umb-photo-folder .selected{position:relative}.umb-photo-folder .selected:before{position:absolute;right:10px;bottom:10px;z-index:100;display:block;width:32px;height:32px;overflow:hidden;font-family:Icomoon;font-size:24px;line-height:36px;color:black;text-align:center;background:white;border-radius:15px;content:"\e165";opacity:.5}.umb-fileupload .preview{float:left;padding:3px;margin-right:30px;margin-bottom:30px;background:#efefef;border:1px solid #a0a0a0;border-radius:5px}.umb-fileupload ul{margin-bottom:0;vertical-align:middle;list-style:none}.umb-fileupload label{padding-left:7px;font-weight:normal;vertical-align:middle}.umb-fileupload .preview-file{width:55px;height:45px;padding-top:27px;font-size:10px;color:#666;text-align:center;text-transform:uppercase}.umb-fileupload input{font-size:12px;line-height:1}.umb-member-group-box{width:45%}.umb-member-group-box:nth-child(1){float:left}.umb-member-group-box:nth-child(2){float:right}.umb-tags{padding:10px;font-size:13px;text-shadow:none;border:#f8f8f8 solid 1px}.umb-tags .tag{padding:7px;margin:7px;cursor:pointer;background:#2e8aea}.umb-tags .tag i{padding:2px}.umb-tags input{background:#fff;border:0}.bootstrap-datetimepicker-widget .btn{padding:0}.bootstrap-datetimepicker-widget .picker-switch .btn{background:0;border:0}.umb-datepicker .input-append .add-on{cursor:pointer}.umb-datepicker p{margin-top:10px}.umb-datepicker p a{color:#555}.CodeMirror,.CodeMirror-scroll{height:100%;min-height:200px}.umb-nested-boolean label{float:left;width:320px;margin-bottom:8px}.umb-nested-boolean label span{float:left;width:80%}.umb-nested-boolean label input[type='checkbox']{float:left;margin-right:10px}.typeahead,.tt-query,.tt-hint{z-index:auto!important;width:396px;height:30px;padding:8px 12px;margin-top:-4px!important;font-size:24px;line-height:30px;border:2px solid #d9d9d9;border-radius:2px!important;outline:0}.typeahead{background-color:#fff}.tt-query{box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.tt-hint{color:#d9d9d9!important}.tt-dropdown-menu{width:422px;padding:8px 0;margin-top:12px;background-color:#fff;border:1px solid #d9d9d9;border:1px solid rgba(0,0,0,0.2);border-radius:8px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.tt-suggestion{padding:3px 20px;font-size:14px;line-height:20px;cursor:pointer}.tt-suggestion.tt-cursor{color:#fff;background-color:#2e8aea}.tt-suggestion p{margin:0}*{-ms-touch-action:none}.ace_editor{height:200px}.nounderline{text-decoration:none!important}.nounderline:hover{text-decoration:underline!important}.nounderline *{text-decoration:none!important;border:0}.ui-sortable-placeholder{margin-left:0!important}.controls-row img{max-width:none}.thumbnail{border-radius:0}.thumbnail img{width:100%;max-width:100%!important}#mapCanvas img{max-width:none!important}.btn-group .dropdown-backdrop{display:none}iframe,.content-column-body{background:center center url(../img/loader.gif) no-repeat;border:0}.fileinput-button{position:relative;margin-bottom:5px;overflow:hidden}.fileinput-button input{position:absolute;top:0;right:0;margin:0;font-size:23px;cursor:pointer;opacity:0;filter:alpha(opacity=0);-webkit-transform:translate(-300px,0) scale(4);transform:translate(-300px,0) scale(4);direction:ltr}.legacy-custom-file{display:inline-block;width:16px;height:16px;margin-right:11px;background-position:center center}.icon-chevron-up:before{content:"\e128"}.icon-chevron-down:before{content:"\e0c9"}.pa-umb-overlay{-webkit-font-smoothing:antialiased;font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.pa-umb-overlay+.pa-umb-overlay{padding-top:30px;border-top:1px solid #d9d9d9}.pa-select-type{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-top:15px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start}.pa-select-type label{padding:0 20px}.pa-access-header{padding-bottom:0;margin:0 0 3px 0;font-weight:bold}.pa-access-description{margin:0;color:#b3b3b3}.pa-validation-message{display:inline-block;padding:6px 12px!important;margin:5px 0 0 0!important}.pa-select-pages label{margin:0;font-size:15px}.pa-select-pages label+.controls-row{padding-top:0}.pa-select-pages .umb-detail{margin:2px 0 5px;font-size:13px}.pa-choose-page a{font-size:15px;color:#2e8aea}.pa-choose-page a:hover,.pa-choose-page a:active,.pa-choose-page a:focus{color:#0064cd;text-decoration:none}.pa-choose-page a:before{margin-right:3px;font-weight:bold;content:"+"}.pa-choose-page .treePickerTitle{padding:3px 5px;font-size:13px;font-style:italic;font-weight:bold;color:grey;background:whitesmoke;border-bottom:0}.pa-form+.pa-form{margin-top:10px}
\ No newline at end of file
+@font-face{font-family:'Open Sans';font-style:normal;font-weight:400;src:url('../fonts/opensans/OpenSans-Regular-webfont.eot');src:local('Open Sans'),local('OpenSans'),url('../fonts/opensans/OpenSans-Regular-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Regular-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Regular-webfont.svg#open_sansregular') format('svg')}@font-face{font-family:'Open Sans';font-style:normal;font-weight:600;src:url('../fonts/opensans/OpenSans-Semibold-webfont.eot');src:local('Open Sans Semibold'),local('OpenSans-Semibold'),url('../fonts/opensans/OpenSans-Semibold-webfont.eot?#iefix') format('embedded-opentype'),url('../fonts/opensans/OpenSans-Semibold-webfont.ttf') format('truetype'),url('../fonts/opensans/OpenSans-Semibold-webfont.svg#open_sanssemibold') format('svg')}.red{color:#9d261d}.blue{color:#2e8aea}.color-red,.color-red i{color:#d90416!important}.color-blue,.color-blue i{color:#04bfbf!important}.color-orange,.color-orange i{color:#d9631e!important}.color-green,.color-green i{color:#04bf67!important}.color-yellow,.color-yellow i{color:#f28729!important}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;box-sizing:border-box}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}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}a:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:hover,a:active{outline:0}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{width:auto\9;height:auto;max-width:100%;vertical-align:middle;border:0;-ms-interpolation-mode:bicubic}#map_canvas img,.google-maps img{max-width:none}button,input,select,textarea{margin:0;font-size:100%;vertical-align:middle}button,input{*overflow:visible;line-height:normal}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}button,html input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button}label,select,button,input[type="button"],input[type="reset"],input[type="submit"],input[type="radio"],input[type="checkbox"]{cursor:pointer}input[type="search"]{box-sizing:content-box;-webkit-appearance:textfield}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}textarea{overflow:auto;vertical-align:top}@media print{*{color:#000!important;text-shadow:none!important;background:transparent!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}body{margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}a{color:#000;text-decoration:none}a:hover,a:focus{color:#000;text-decoration:underline}.img-rounded{border-radius:6px}.img-polaroid{padding:4px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.img-circle{border-radius:500px}.row{margin-left:0;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:0}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.span12{width:720px}.span11{width:660px}.span10{width:600px}.span9{width:540px}.span8{width:480px}.span7{width:420px}.span6{width:360px}.span5{width:300px}.span4{width:240px}.span3{width:180px}.span2{width:120px}.span1{width:60px}.offset12{margin-left:720px}.offset11{margin-left:660px}.offset10{margin-left:600px}.offset9{margin-left:540px}.offset8{margin-left:480px}.offset7{margin-left:420px}.offset6{margin-left:360px}.offset5{margin-left:300px}.offset4{margin-left:240px}.offset3{margin-left:180px}.offset2{margin-left:120px}.offset1{margin-left:60px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:0;*margin-left:-0.06944444444444445%;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:0}.row-fluid .span12{width:99.99999999999999%;*width:99.93055555555554%}.row-fluid .span11{width:91.66666666666666%;*width:91.59722222222221%}.row-fluid .span10{width:83.33333333333331%;*width:83.26388888888887%}.row-fluid .span9{width:74.99999999999999%;*width:74.93055555555554%}.row-fluid .span8{width:66.66666666666666%;*width:66.59722222222221%}.row-fluid .span7{width:58.33333333333333%;*width:58.263888888888886%}.row-fluid .span6{width:49.99999999999999%;*width:49.93055555555555%}.row-fluid .span5{width:41.66666666666666%;*width:41.597222222222214%}.row-fluid .span4{width:33.33333333333333%;*width:33.263888888888886%}.row-fluid .span3{width:24.999999999999996%;*width:24.930555555555554%}.row-fluid .span2{width:16.666666666666664%;*width:16.59722222222222%}.row-fluid .span1{width:8.333333333333332%;*width:8.263888888888888%}.row-fluid .offset12{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset12:first-child{margin-left:99.99999999999999%;*margin-left:99.8611111111111%}.row-fluid .offset11{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset11:first-child{margin-left:91.66666666666666%;*margin-left:91.52777777777777%}.row-fluid .offset10{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset10:first-child{margin-left:83.33333333333331%;*margin-left:83.19444444444443%}.row-fluid .offset9{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset9:first-child{margin-left:74.99999999999999%;*margin-left:74.8611111111111%}.row-fluid .offset8{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset8:first-child{margin-left:66.66666666666666%;*margin-left:66.52777777777777%}.row-fluid .offset7{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset7:first-child{margin-left:58.33333333333333%;*margin-left:58.19444444444444%}.row-fluid .offset6{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset6:first-child{margin-left:49.99999999999999%;*margin-left:49.86111111111111%}.row-fluid .offset5{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset5:first-child{margin-left:41.66666666666666%;*margin-left:41.52777777777777%}.row-fluid .offset4{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset4:first-child{margin-left:33.33333333333333%;*margin-left:33.19444444444444%}.row-fluid .offset3{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset3:first-child{margin-left:24.999999999999996%;*margin-left:24.86111111111111%}.row-fluid .offset2{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset2:first-child{margin-left:16.666666666666664%;*margin-left:16.52777777777778%}.row-fluid .offset1{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}.row-fluid .offset1:first-child{margin-left:8.333333333333332%;*margin-left:8.194444444444443%}[class*="span"].hide,.row-fluid [class*="span"].hide{display:none}[class*="span"].pull-right,.row-fluid [class*="span"].pull-right{float:right}.container{margin-right:auto;margin-left:auto;*zoom:1}.container:before,.container:after{display:table;line-height:0;content:""}.container:after{clear:both}.container-fluid{padding-right:0;padding-left:0;*zoom:1}.container-fluid:before,.container-fluid:after{display:table;line-height:0;content:""}.container-fluid:after{clear:both}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:21px;font-weight:200;line-height:30px}small{font-size:85%}strong{font-weight:bold}em{font-style:italic}cite{font-style:normal}.muted{color:#d9d9d9}a.muted:hover,a.muted:focus{color:#c0c0c0}.text-warning{color:#fff}a.text-warning:hover,a.text-warning:focus{color:#e6e6e6}.text-error{color:#fff}a.text-error:hover,a.text-error:focus{color:#e6e6e6}.text-info{color:#fff}a.text-info:hover,a.text-info:focus{color:#e6e6e6}.text-success{color:#fff}a.text-success:hover,a.text-success:focus{color:#e6e6e6}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}h1,h2,h3,h4,h5,h6{margin:10px 0;font-family:inherit;font-weight:500;line-height:20px;color:inherit;text-rendering:optimizelegibility}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small{font-weight:normal;line-height:1;color:#d9d9d9}h1,h2,h3{line-height:40px}h1{font-size:38.5px}h2{font-size:31.5px}h3{font-size:24.5px}h4{font-size:17.5px}h5{font-size:14px}h6{font-size:11.9px}h1 small{font-size:24.5px}h2 small{font-size:17.5px}h3 small{font-size:14px}h4 small{font-size:14px}.page-header{padding-bottom:9px;margin:20px 0 30px;border-bottom:1px solid #f8f8f8}ul,ol{padding:0;margin:0 0 10px 25px}ul ul,ul ol,ol ol,ol ul{margin-bottom:0}li{line-height:20px}ul.unstyled,ol.unstyled{margin-left:0;list-style:none}ul.inline,ol.inline{margin-left:0;list-style:none}ul.inline>li,ol.inline>li{display:inline-block;*display:inline;padding-right:5px;padding-left:5px;*zoom:1}dl{margin-bottom:20px}dt,dd{line-height:20px}dt{font-weight:bold}dd{margin-left:10px}.dl-horizontal{*zoom:1}.dl-horizontal:before,.dl-horizontal:after{display:table;line-height:0;content:""}.dl-horizontal:after{clear:both}.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}hr{margin:20px 0;border:0;border-top:1px solid #f8f8f8;border-bottom:1px solid #fff}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #d9d9d9}abbr.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:0 0 0 15px;margin:0 0 20px;border-left:5px solid #f8f8f8}blockquote p{margin-bottom:0;font-size:17.5px;font-weight:300;line-height:1.25}blockquote small{display:block;line-height:20px;color:#d9d9d9}blockquote small:before{content:'\2014 \00A0'}blockquote.pull-right{float:right;padding-right:15px;padding-left:0;border-right:5px solid #f8f8f8;border-left:0}blockquote.pull-right p,blockquote.pull-right small{text-align:right}blockquote.pull-right small:before{content:''}blockquote.pull-right small:after{content:'\00A0 \2014'}q:before,q:after,blockquote:before,blockquote:after{content:""}address{display:block;margin-bottom:20px;font-style:normal;line-height:20px}code,pre.code{padding:0 3px 2px;font-family:Monaco,Menlo,Consolas,"Courier New",monospace;font-size:12px;color:#343434;border-radius:3px}code{padding:2px 4px;color:#d14;white-space:nowrap;background-color:#f7f7f9;border:1px solid #e1e1e8}pre.code{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:20px;word-break:break-all;word-wrap:break-word;white-space:pre;white-space:pre-wrap;background-color:#f5f5f5;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.15);border-radius:0}pre.code.prettyprint{margin-bottom:20px}pre.code code{padding:0;color:inherit;white-space:pre;white-space:pre-wrap;background-color:transparent;border:0}.pre-scrollable{max-height:340px;overflow-y:scroll}table{max-width:100%;background-color:transparent;border-collapse:collapse;border-spacing:0}.table{width:100%;margin-bottom:20px}.table th,.table td{padding:8px;line-height:20px;text-align:left;vertical-align:top;border-top:1px solid #dbdbdb}.table th{font-weight:bold}.table thead th{vertical-align:bottom}.table caption+thead tr:first-child th,.table caption+thead tr:first-child td,.table colgroup+thead tr:first-child th,.table colgroup+thead tr:first-child td,.table thead:first-child tr:first-child th,.table thead:first-child tr:first-child td{border-top:0}.table tbody+tbody{border-top:2px solid #dbdbdb}.table .table{background-color:#fff}.table-condensed th,.table-condensed td{padding:4px 5px}.table-bordered{border:1px solid #dbdbdb;border-collapse:separate;*border-collapse:collapse;border-left:0;border-radius:0}.table-bordered th,.table-bordered td{border-left:1px solid #dbdbdb}.table-bordered caption+thead tr:first-child th,.table-bordered caption+tbody tr:first-child th,.table-bordered caption+tbody tr:first-child td,.table-bordered colgroup+thead tr:first-child th,.table-bordered colgroup+tbody tr:first-child th,.table-bordered colgroup+tbody tr:first-child td,.table-bordered thead:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child th,.table-bordered tbody:first-child tr:first-child td{border-top:0}.table-bordered thead:first-child tr:first-child>th:first-child,.table-bordered tbody:first-child tr:first-child>td:first-child,.table-bordered tbody:first-child tr:first-child>th:first-child{border-top-left-radius:0;-moz-border-radius-topleft:0}.table-bordered thead:first-child tr:first-child>th:last-child,.table-bordered tbody:first-child tr:first-child>td:last-child,.table-bordered tbody:first-child tr:first-child>th:last-child{border-top-right-radius:0;-moz-border-radius-topright:0}.table-bordered thead:last-child tr:last-child>th:first-child,.table-bordered tbody:last-child tr:last-child>td:first-child,.table-bordered tbody:last-child tr:last-child>th:first-child,.table-bordered tfoot:last-child tr:last-child>td:first-child,.table-bordered tfoot:last-child tr:last-child>th:first-child{border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered thead:last-child tr:last-child>th:last-child,.table-bordered tbody:last-child tr:last-child>td:last-child,.table-bordered tbody:last-child tr:last-child>th:last-child,.table-bordered tfoot:last-child tr:last-child>td:last-child,.table-bordered tfoot:last-child tr:last-child>th:last-child{border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered tfoot+tbody:last-child tr:last-child td:first-child{border-bottom-left-radius:0;-moz-border-radius-bottomleft:0}.table-bordered tfoot+tbody:last-child tr:last-child td:last-child{border-bottom-right-radius:0;-moz-border-radius-bottomright:0}.table-bordered caption+thead tr:first-child th:first-child,.table-bordered caption+tbody tr:first-child td:first-child,.table-bordered colgroup+thead tr:first-child th:first-child,.table-bordered colgroup+tbody tr:first-child td:first-child{border-top-left-radius:0;-moz-border-radius-topleft:0}.table-bordered caption+thead tr:first-child th:last-child,.table-bordered caption+tbody tr:first-child td:last-child,.table-bordered colgroup+thead tr:first-child th:last-child,.table-bordered colgroup+tbody tr:first-child td:last-child{border-top-right-radius:0;-moz-border-radius-topright:0}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background-color:#f9f9f9}.table-hover tbody tr:hover>td,.table-hover tbody tr:hover>th{background-color:#f5f5f5}table td[class*="span"],table th[class*="span"],.row-fluid table td[class*="span"],.row-fluid table th[class*="span"]{display:table-cell;float:none;margin-left:0}.table td.span1,.table th.span1{float:none;width:44px;margin-left:0}.table td.span2,.table th.span2{float:none;width:104px;margin-left:0}.table td.span3,.table th.span3{float:none;width:164px;margin-left:0}.table td.span4,.table th.span4{float:none;width:224px;margin-left:0}.table td.span5,.table th.span5{float:none;width:284px;margin-left:0}.table td.span6,.table th.span6{float:none;width:344px;margin-left:0}.table td.span7,.table th.span7{float:none;width:404px;margin-left:0}.table td.span8,.table th.span8{float:none;width:464px;margin-left:0}.table td.span9,.table th.span9{float:none;width:524px;margin-left:0}.table td.span10,.table th.span10{float:none;width:584px;margin-left:0}.table td.span11,.table th.span11{float:none;width:644px;margin-left:0}.table td.span12,.table th.span12{float:none;width:704px;margin-left:0}.table tbody tr.success>td{background-color:#46a546}.table tbody tr.error>td{background-color:#9d261d}.table tbody tr.warning>td{background-color:#df7f48}.table tbody tr.info>td{background-color:#2e8aea}.table-hover tbody tr.success:hover>td{background-color:#3e933e}.table-hover tbody tr.error:hover>td{background-color:#872119}.table-hover tbody tr.warning:hover>td{background-color:#db7032}.table-hover tbody tr.info:hover>td{background-color:#177de7}.dropup,.dropdown{position:relative}.dropdown-toggle{*margin-bottom:-3px}.dropdown-toggle:active,.open .dropdown-toggle{outline:0}.caret{display:inline-block;width:0;height:0;vertical-align:top;border-top:4px solid #000;border-right:4px solid transparent;border-left:4px solid transparent;content:""}.dropdown .caret{margin-top:8px;margin-left:2px}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;list-style:none;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:normal;line-height:20px;color:#343434;white-space:nowrap}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#fff;text-decoration:none;background-color:#000;background-image:linear-gradient(to bottom,#000,#000);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000',endColorstr='#ff000000',GradientType=0)}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{color:#fff;text-decoration:none;background-color:#000;background-image:linear-gradient(to bottom,#000,#000);background-repeat:repeat-x;outline:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000',endColorstr='#ff000000',GradientType=0)}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{color:#d9d9d9}.dropdown-menu>.disabled>a:hover,.dropdown-menu>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open{*z-index:1000}.open>.dropdown-menu{display:block}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{border-top:0;border-bottom:4px solid #000;content:""}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:1px}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;border-radius:0 6px 6px 6px}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;border-radius:5px 5px 5px 0}.dropdown-submenu>a:after{display:block;float:right;width:0;height:0;margin-top:5px;margin-right:-10px;border-color:transparent;border-left-color:#ccc;border-style:solid;border-width:5px 0 5px 5px;content:" "}.dropdown-submenu:hover>a:after{border-left-color:#fff}.dropdown-submenu.pull-left{float:none}.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:10px;border-radius:6px 0 6px 6px}.dropdown .dropdown-menu .nav-header{padding-right:20px;padding-left:20px}.typeahead{z-index:1051;margin-top:2px;border-radius:0}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:0;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)}.well-large{padding:24px;border-radius:0}.well-small{padding:9px;border-radius:0}.fade{opacity:0;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{position:relative;height:0;overflow:hidden;transition:height .35s ease}.collapse.in{height:auto}.close{float:right;font-size:20px;font-weight:bold;line-height:20px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20)}.close:hover,.close:focus{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40)}button.close{padding:0;cursor:pointer;background:transparent;border:0;-webkit-appearance:none}.btn-group{position:relative;display:inline-block;*display:inline;*margin-left:.3em;font-size:0;white-space:nowrap;vertical-align:middle;*zoom:1}.btn-group:first-child{*margin-left:0}.btn-group+.btn-group{margin-left:5px}.btn-toolbar{margin-top:10px;margin-bottom:10px;font-size:0}.btn-toolbar>.btn+.btn,.btn-toolbar>.btn-group+.btn,.btn-toolbar>.btn+.btn-group{margin-left:5px}.btn-group>.btn{position:relative;border-radius:0}.btn-group>.btn+.btn{margin-left:-1px}.btn-group>.btn,.btn-group>.dropdown-menu,.btn-group>.popover{font-size:14px}.btn-group>.btn-mini{font-size:10.5px}.btn-group>.btn-small{font-size:11.9px}.btn-group>.btn-large{font-size:17.5px}.btn-group>.btn:first-child{margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.btn-group>.btn.large:first-child{margin-left:0;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.btn-group>.btn.large:last-child,.btn-group>.large.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.btn-group>.btn:hover,.btn-group>.btn:focus,.btn-group>.btn:active,.btn-group>.btn.active{z-index:2}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{*padding-top:5px;padding-right:8px;*padding-bottom:5px;padding-left:8px;box-shadow:inset 1px 0 0 rgba(255,255,255,0.125),inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.btn-group>.btn-mini+.dropdown-toggle{*padding-top:2px;padding-right:5px;*padding-bottom:2px;padding-left:5px}.btn-group>.btn-small+.dropdown-toggle{*padding-top:5px;*padding-bottom:4px}.btn-group>.btn-large+.dropdown-toggle{*padding-top:7px;padding-right:12px;*padding-bottom:7px;padding-left:12px}.btn-group.open .dropdown-toggle{background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn-group.open .btn.dropdown-toggle{background-color:#e4e4e4}.btn-group.open .btn-primary.dropdown-toggle{background-color:#53a93f}.btn-group.open .btn-warning.dropdown-toggle{background-color:#df7f48}.btn-group.open .btn-danger.dropdown-toggle{background-color:#ee5f5b}.btn-group.open .btn-success.dropdown-toggle{background-color:#53a93f}.btn-group.open .btn-info.dropdown-toggle{background-color:#2f96b4}.btn-group.open .btn-inverse.dropdown-toggle{background-color:#222}.btn .caret{margin-top:8px;margin-left:0}.btn-large .caret{margin-top:6px}.btn-large .caret{border-top-width:5px;border-right-width:5px;border-left-width:5px}.btn-mini .caret,.btn-small .caret{margin-top:8px}.dropup .btn-large .caret{border-bottom-width:5px}.btn-primary .caret,.btn-warning .caret,.btn-danger .caret,.btn-info .caret,.btn-success .caret,.btn-inverse .caret{border-top-color:#fff;border-bottom-color:#fff}.btn-group-vertical{display:inline-block;*display:inline;*zoom:1}.btn-group-vertical>.btn{display:block;float:none;max-width:100%;border-radius:0}.btn-group-vertical>.btn+.btn{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:first-child{border-radius:0}.btn-group-vertical>.btn:last-child{border-radius:0}.btn-group-vertical>.btn-large:first-child{border-radius:0}.btn-group-vertical>.btn-large:last-child{border-radius:0}.alert{padding:8px 35px 8px 14px;margin-bottom:20px;background-color:#df7f48;border:1px solid transparent;border-radius:0}.alert,.alert h4,.alert a{color:#fff}.alert h4{margin:0}.alert .close{position:relative;top:-2px;right:-21px;line-height:20px}.close.-align-right{right:0}.alert-success{color:#fff;background-color:#46a546;border-color:transparent}.alert-success h4{color:#fff}.alert-danger,.alert-error{color:#fff;background-color:#9d261d;border-color:transparent}.alert-danger h4,.alert-error h4{color:#fff}.alert-info{color:#fff;background-color:#2e8aea;border-color:transparent}.alert-info h4{color:#fff}.alert-form{color:#555;background-color:#ececec;border:1px solid #555!important}.alert-form.-no-border{border:none!important}.alert-form h4{color:#555}.alert-block{padding-top:14px;padding-bottom:14px}.alert-block>p,.alert-block>ul{margin-bottom:0}.alert-block p+p{margin-top:5px}.list-icons li{max-width:300px;padding-left:35px}.list-icons li>i.icon{padding-right:7px;margin-left:-25px}.icon.handle{color:#d9d9d9}.nav{margin-bottom:20px;margin-left:0;list-style:none}.nav>li>a{display:block}.nav>li>a:hover,.nav>li>a:focus{text-decoration:none;background-color:#f8f8f8}.nav>li>a>img{max-width:none}.nav>.pull-right{float:right}.nav-header{display:block;padding:3px 15px;font-size:11px;font-weight:bold;line-height:20px;color:#d9d9d9;text-shadow:0 1px 0 rgba(255,255,255,0.5);text-transform:uppercase}.nav li+.nav-header{margin-top:9px}.nav-list{padding-right:15px;padding-left:15px;margin-bottom:0}.nav-list>li>a,.nav-list .nav-header{margin-right:-15px;margin-left:-15px;text-shadow:0 1px 0 rgba(255,255,255,0.5)}.nav-list>li>a{padding:3px 15px}.nav-list>.active>a,.nav-list>.active>a:hover,.nav-list>.active>a:focus{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.2);background-color:#000}.nav-list [class^="icon-"],.nav-list [class*=" icon-"]{margin-right:2px}.nav-list .divider{*width:100%;height:1px;margin:9px 1px;*margin:-5px 0 5px;overflow:hidden;background-color:#e5e5e5;border-bottom:1px solid #fff}.nav-tabs,.nav-pills{*zoom:1}.nav-tabs:before,.nav-pills:before,.nav-tabs:after,.nav-pills:after{display:table;line-height:0;content:""}.nav-tabs:after,.nav-pills:after{clear:both}.nav-tabs>li,.nav-pills>li{float:left}.nav-tabs>li>a,.nav-pills>li>a{padding-right:8px;padding-left:8px;margin-right:2px;line-height:14px}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{margin-bottom:-1px}.nav-tabs>li>a{padding-top:5px;padding-bottom:4px;line-height:20px;color:#555;border:1px solid transparent}.nav-tabs>li>a:hover{color:#000}.nav-tabs>li>a:hover,.nav-tabs>li>a:focus{border-color:#f8f8f8 #f8f8f8 #ddd}.nav-tabs>.active>a,.nav-tabs>.active>a:hover,.nav-tabs>.active>a:focus{color:#000;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.show-validation .tab-error a,.show-validation .tab-error a:hover,.show-validation .tab-error a:focus{color:#fff!important;background-color:#9d261d!important;border-color:transparent}.show-validation .tab-error a:before{margin-right:5px;font-family:'icomoon';vertical-align:top;content:"\e25d"}.umb-nav-tabs{margin:-8px 0 0 0}.nav-pills>li>a{padding-top:8px;padding-bottom:8px;margin-top:2px;margin-bottom:2px;border-radius:5px}.nav-pills>.active>a,.nav-pills>.active>a:hover,.nav-pills>.active>a:focus{color:#fff;background-color:#000}.nav-stacked>li{float:none}.nav-stacked>li>a{margin-right:0}.nav-tabs.nav-stacked{border-bottom:0}.nav-tabs.nav-stacked>li>a{border:1px solid #ddd;border-radius:0}.nav-tabs.nav-stacked>li:first-child>a{border-top-right-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-moz-border-radius-topleft:4px}.nav-tabs.nav-stacked>li:last-child>a{border-bottom-right-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-moz-border-radius-bottomleft:4px}.nav-tabs.nav-stacked>li>a:hover,.nav-tabs.nav-stacked>li>a:focus{z-index:2;border-color:#ddd}.nav-pills.nav-stacked>li>a{margin-bottom:3px}.nav-pills.nav-stacked>li:last-child>a{margin-bottom:1px}.dropdown-menu{border-radius:0}.dropdown-menu>li>a{padding:8px 20px}.nav-tabs .dropdown-menu{border-radius:0 0 6px 6px}.nav-pills .dropdown-menu{border-radius:6px}.nav .dropdown-toggle .caret{margin-top:6px;border-top-color:#000;border-bottom-color:#000}.nav .dropdown-toggle:hover .caret,.nav .dropdown-toggle:focus .caret{border-top-color:#000;border-bottom-color:#000}.nav-tabs .dropdown-toggle .caret{margin-top:8px}.nav .active .dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.nav-tabs .active .dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.nav>.dropdown.active>a:hover,.nav>.dropdown.active>a:focus{cursor:pointer}.nav-tabs .open .dropdown-toggle,.nav-pills .open .dropdown-toggle,.nav>li.dropdown.open.active>a:hover,.nav>li.dropdown.open.active>a:focus{background-color:#d9d9d9;border-color:#d9d9d9}.nav li.dropdown.open .caret,.nav li.dropdown.open.active .caret,.nav li.dropdown.open a:hover .caret,.nav li.dropdown.open a:focus .caret{border-top-color:#fff;border-bottom-color:#fff;opacity:1;filter:alpha(opacity=100)}.tabs-stacked .open>a:hover,.tabs-stacked .open>a:focus{border-color:#d9d9d9}.tabbable{*zoom:1}.tabbable:before,.tabbable:after{display:table;line-height:0;content:""}.tabbable:after{clear:both}.tab-content{overflow:auto}.tabs-below>.nav-tabs,.tabs-right>.nav-tabs,.tabs-left>.nav-tabs{border-bottom:0}.tab-content>.tab-pane,.pill-content>.pill-pane{display:none}.tab-content>.active,.pill-content>.active{display:block}.tabs-below>.nav-tabs{border-top:1px solid #ddd}.tabs-below>.nav-tabs>li{margin-top:-1px;margin-bottom:0}.tabs-below>.nav-tabs>li>a{border-radius:0 0 4px 4px}.tabs-below>.nav-tabs>li>a:hover,.tabs-below>.nav-tabs>li>a:focus{border-top-color:#ddd;border-bottom-color:transparent}.tabs-below>.nav-tabs>.active>a,.tabs-below>.nav-tabs>.active>a:hover,.tabs-below>.nav-tabs>.active>a:focus{border-color:transparent #ddd #ddd #ddd}.tabs-left>.nav-tabs>li,.tabs-right>.nav-tabs>li{float:none}.tabs-left>.nav-tabs>li>a,.tabs-right>.nav-tabs>li>a{min-width:74px;margin-right:0;margin-bottom:3px}.tabs-left>.nav-tabs{float:left;margin-right:19px;border-right:1px solid #ddd}.tabs-left>.nav-tabs>li>a{margin-right:-1px;border-radius:4px 0 0 4px}.tabs-left>.nav-tabs>li>a:hover,.tabs-left>.nav-tabs>li>a:focus{border-color:#f8f8f8 #ddd #f8f8f8 #f8f8f8}.tabs-left>.nav-tabs .active>a,.tabs-left>.nav-tabs .active>a:hover,.tabs-left>.nav-tabs .active>a:focus{border-color:#ddd transparent #ddd #ddd;*border-right-color:#fff}.tabs-right>.nav-tabs{float:right;margin-left:19px;border-left:1px solid #ddd}.tabs-right>.nav-tabs>li>a{margin-left:-1px;border-radius:0 4px 4px 0}.tabs-right>.nav-tabs>li>a:hover,.tabs-right>.nav-tabs>li>a:focus{border-color:#f8f8f8 #f8f8f8 #f8f8f8 #ddd}.tabs-right>.nav-tabs .active>a,.tabs-right>.nav-tabs .active>a:hover,.tabs-right>.nav-tabs .active>a:focus{border-color:#ddd #ddd #ddd transparent;*border-left-color:#fff}.nav>.disabled>a{color:#d9d9d9}.nav>.disabled>a:hover,.nav>.disabled>a:focus{text-decoration:none;cursor:default;background-color:transparent}.navbar{*position:relative;*z-index:2;margin-bottom:20px;overflow:visible}.navbar-inner{min-height:40px;padding-right:20px;padding-left:20px;background-color:#fafafa;background-image:linear-gradient(to bottom,#fff,#f2f2f2);background-repeat:repeat-x;border:1px solid #d4d4d4;border-radius:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#fff2f2f2',GradientType=0);*zoom:1;box-shadow:0 1px 4px rgba(0,0,0,0.065)}.navbar-inner:before,.navbar-inner:after{display:table;line-height:0;content:""}.navbar-inner:after{clear:both}.navbar .container{width:auto}.nav-collapse.collapse{height:auto;overflow:visible}.navbar .brand{display:block;float:left;padding:10px 20px 10px;margin-left:-20px;font-size:20px;font-weight:200;color:#777;text-shadow:0 1px 0 #fff}.navbar .brand:hover,.navbar .brand:focus{text-decoration:none}.navbar-text{margin-bottom:0;line-height:40px;color:#777}.navbar-link{color:#777}.navbar-link:hover,.navbar-link:focus{color:#343434}.navbar .divider-vertical{height:40px;margin:0 9px;border-right:1px solid #fff;border-left:1px solid #f2f2f2}.navbar .btn,.navbar .btn-group{margin-top:5px}.navbar .btn-group .btn,.navbar .input-prepend .btn,.navbar .input-append .btn,.navbar .input-prepend .btn-group,.navbar .input-append .btn-group{margin-top:0}.navbar-form{margin-bottom:0;*zoom:1}.navbar-form:before,.navbar-form:after{display:table;line-height:0;content:""}.navbar-form:after{clear:both}.navbar-form input,.navbar-form select,.navbar-form .radio,.navbar-form .checkbox{margin-top:5px}.navbar-form input,.navbar-form select,.navbar-form .btn{display:inline-block;margin-bottom:0}.navbar-form input[type="image"],.navbar-form input[type="checkbox"],.navbar-form input[type="radio"]{margin-top:3px}.navbar-form .input-append,.navbar-form .input-prepend{margin-top:5px;white-space:nowrap}.navbar-form .input-append input,.navbar-form .input-prepend input{margin-top:0}.navbar-search{position:relative;float:left;margin-top:5px;margin-bottom:0}.navbar-search .search-query{padding:4px 14px;margin-bottom:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:13px;font-weight:normal;line-height:1;border-radius:15px}.navbar-static-top{position:static;margin-bottom:0}.navbar-static-top .navbar-inner{border-radius:0}.navbar-fixed-top,.navbar-fixed-bottom{position:fixed;right:0;left:0;z-index:1030;margin-bottom:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{border-width:0 0 1px}.navbar-fixed-bottom .navbar-inner{border-width:1px 0 0}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding-right:0;padding-left:0;border-radius:0}.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:720px}.navbar-fixed-top{top:0}.navbar-fixed-top .navbar-inner,.navbar-static-top .navbar-inner{box-shadow:0 1px 10px rgba(0,0,0,0.1)}.navbar-fixed-bottom{bottom:0}.navbar-fixed-bottom .navbar-inner{box-shadow:0 -1px 10px rgba(0,0,0,0.1)}.navbar .nav{position:relative;left:0;display:block;float:left;margin:0 10px 0 0}.navbar .nav.pull-right{float:right;margin-right:0}.navbar .nav>li{float:left}.navbar .nav>li>a{float:none;padding:10px 15px 10px;color:#777;text-decoration:none;text-shadow:0 1px 0 #fff}.navbar .nav .dropdown-toggle .caret{margin-top:8px}.navbar .nav>li>a:focus,.navbar .nav>li>a:hover{color:#343434;text-decoration:none;background-color:transparent}.navbar .nav>.active>a,.navbar .nav>.active>a:hover,.navbar .nav>.active>a:focus{color:#555;text-decoration:none;background-color:#e5e5e5;box-shadow:inset 0 3px 8px rgba(0,0,0,0.125)}.navbar .btn-navbar{display:none;float:right;padding:7px 10px;margin-right:5px;margin-left:5px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ededed;*background-color:#e5e5e5;background-image:linear-gradient(to bottom,#f2f2f2,#e5e5e5);background-repeat:repeat-x;border-color:#e5e5e5 #e5e5e5 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2f2f2',endColorstr='#ffe5e5e5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.075)}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active{background-color:#ededed}.navbar .btn-navbar:hover,.navbar .btn-navbar:focus,.navbar .btn-navbar:active,.navbar .btn-navbar.active,.navbar .btn-navbar.disabled,.navbar .btn-navbar[disabled]{color:#fff;*background-color:#d9d9d9}.navbar .btn-navbar:active,.navbar .btn-navbar.active{background-color:#ccc \9}.navbar .btn-navbar .icon-bar{display:block;width:18px;height:2px;background-color:#f5f5f5;border-radius:1px;box-shadow:0 1px 0 rgba(0,0,0,0.25)}.btn-navbar .icon-bar+.icon-bar{margin-top:3px}.navbar .nav>li>.dropdown-menu:before{position:absolute;top:-7px;left:9px;display:inline-block;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-left:7px solid transparent;border-bottom-color:rgba(0,0,0,0.2);content:''}.navbar .nav>li>.dropdown-menu:after{position:absolute;top:-6px;left:10px;display:inline-block;border-right:6px solid transparent;border-bottom:6px solid #fff;border-left:6px solid transparent;content:''}.navbar-fixed-bottom .nav>li>.dropdown-menu:before{top:auto;bottom:-7px;border-top:7px solid #ccc;border-bottom:0;border-top-color:rgba(0,0,0,0.2)}.navbar-fixed-bottom .nav>li>.dropdown-menu:after{top:auto;bottom:-6px;border-top:6px solid #fff;border-bottom:0}.navbar .nav li.dropdown>a:hover .caret,.navbar .nav li.dropdown>a:focus .caret{border-top-color:#343434;border-bottom-color:#343434}.navbar .nav li.dropdown.open>.dropdown-toggle,.navbar .nav li.dropdown.active>.dropdown-toggle,.navbar .nav li.dropdown.open.active>.dropdown-toggle{color:#555;background-color:#e5e5e5}.navbar .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#777;border-bottom-color:#777}.navbar .nav li.dropdown.open>.dropdown-toggle .caret,.navbar .nav li.dropdown.active>.dropdown-toggle .caret,.navbar .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#555;border-bottom-color:#555}.navbar .pull-right>li>.dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right{right:0;left:auto}.navbar .pull-right>li>.dropdown-menu:before,.navbar .nav>li>.dropdown-menu.pull-right:before{right:12px;left:auto}.navbar .pull-right>li>.dropdown-menu:after,.navbar .nav>li>.dropdown-menu.pull-right:after{right:13px;left:auto}.navbar .pull-right>li>.dropdown-menu .dropdown-menu,.navbar .nav>li>.dropdown-menu.pull-right .dropdown-menu{right:100%;left:auto;margin-right:-1px;margin-left:0;border-radius:6px 0 6px 6px}.navbar-inverse .navbar-inner{background-color:#1b1b1b;background-image:linear-gradient(to bottom,#222,#111);background-repeat:repeat-x;border-color:#252525;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222',endColorstr='#ff111111',GradientType=0)}.navbar-inverse .brand,.navbar-inverse .nav>li>a{color:#d9d9d9;text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-inverse .brand:hover,.navbar-inverse .nav>li>a:hover,.navbar-inverse .brand:focus,.navbar-inverse .nav>li>a:focus{color:#fff}.navbar-inverse .brand{color:#d9d9d9}.navbar-inverse .navbar-text{color:#d9d9d9}.navbar-inverse .nav>li>a:focus,.navbar-inverse .nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .nav .active>a,.navbar-inverse .nav .active>a:hover,.navbar-inverse .nav .active>a:focus{color:#fff;background-color:#111}.navbar-inverse .navbar-link{color:#d9d9d9}.navbar-inverse .navbar-link:hover,.navbar-inverse .navbar-link:focus{color:#fff}.navbar-inverse .divider-vertical{border-right-color:#222;border-left-color:#111}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle{color:#fff;background-color:#111}.navbar-inverse .nav li.dropdown>a:hover .caret,.navbar-inverse .nav li.dropdown>a:focus .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .nav li.dropdown>.dropdown-toggle .caret{border-top-color:#d9d9d9;border-bottom-color:#d9d9d9}.navbar-inverse .nav li.dropdown.open>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.active>.dropdown-toggle .caret,.navbar-inverse .nav li.dropdown.open.active>.dropdown-toggle .caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-inverse .navbar-search .search-query{color:#fff;background-color:#515151;border-color:#111;box-shadow:inset 0 1px 2px rgba(0,0,0,0.1),0 1px 0 rgba(255,255,255,0.15);transition:none}.navbar-inverse .navbar-search .search-query:-moz-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:-ms-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder{color:#ccc}.navbar-inverse .navbar-search .search-query:focus,.navbar-inverse .navbar-search .search-query.focused{padding:5px 15px;color:#343434;text-shadow:0 1px 0 #fff;background-color:#fff;border:0;outline:0;box-shadow:0 0 3px rgba(0,0,0,0.15)}.navbar-inverse .btn-navbar{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e0e0e;*background-color:#040404;background-image:linear-gradient(to bottom,#151515,#040404);background-repeat:repeat-x;border-color:#040404 #040404 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff151515',endColorstr='#ff040404',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active{background-color:#101010}.navbar-inverse .btn-navbar:hover,.navbar-inverse .btn-navbar:focus,.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active,.navbar-inverse .btn-navbar.disabled,.navbar-inverse .btn-navbar[disabled]{color:#fff;*background-color:#000}.navbar-inverse .btn-navbar:active,.navbar-inverse .btn-navbar.active{background-color:#000 \9}.breadcrumb{padding:8px 15px;margin:0 0 20px;list-style:none;background-color:#f5f5f5;border-radius:0}.breadcrumb>li{display:inline-block;*display:inline;text-shadow:0 1px 0 #fff;*zoom:1}.breadcrumb>li>.divider{padding:0 5px;color:#ccc}.breadcrumb>.active{color:#d9d9d9}.pagination{margin:20px 0}.pagination ul{display:inline-block;*display:inline;margin-bottom:0;margin-left:0;border-radius:0;*zoom:1;box-shadow:0 1px 2px rgba(0,0,0,0.05)}.pagination ul>li{display:inline}.pagination ul>li>a,.pagination ul>li>span{float:left;padding:4px 12px;line-height:20px;text-decoration:none;background-color:#fff;border:1px solid #ddd;border-left-width:0}.pagination ul>li>a:hover,.pagination ul>li>a:focus,.pagination ul>.active>a,.pagination ul>.active>span{background-color:#f5f5f5}.pagination ul>.active>a,.pagination ul>.active>span{color:#d9d9d9;cursor:default}.pagination ul>.disabled>span,.pagination ul>.disabled>a,.pagination ul>.disabled>a:hover,.pagination ul>.disabled>a:focus{color:#d9d9d9;cursor:default;background-color:transparent}.pagination ul>li:first-child>a,.pagination ul>li:first-child>span{border-left-width:1px;border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination ul>li:last-child>a,.pagination ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-centered{text-align:center}.pagination-right{text-align:right}.pagination-large ul>li>a,.pagination-large ul>li>span{padding:11px 19px;font-size:17.5px}.pagination-large ul>li:first-child>a,.pagination-large ul>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination-large ul>li:last-child>a,.pagination-large ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-mini ul>li:first-child>a,.pagination-small ul>li:first-child>a,.pagination-mini ul>li:first-child>span,.pagination-small ul>li:first-child>span{border-bottom-left-radius:0;border-top-left-radius:0;-moz-border-radius-bottomleft:0;-moz-border-radius-topleft:0}.pagination-mini ul>li:last-child>a,.pagination-small ul>li:last-child>a,.pagination-mini ul>li:last-child>span,.pagination-small ul>li:last-child>span{border-top-right-radius:0;border-bottom-right-radius:0;-moz-border-radius-topright:0;-moz-border-radius-bottomright:0}.pagination-small ul>li>a,.pagination-small ul>li>span{padding:2px 10px;font-size:11.9px}.pagination-mini ul>li>a,.pagination-mini ul>li>span{padding:0 6px;font-size:10.5px}.pager{margin:20px 0;text-align:center;list-style:none;*zoom:1}.pager:before,.pager:after{display:table;line-height:0;content:""}.pager:after{clear:both}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:hover,.pager li>a:focus{text-decoration:none;background-color:#f5f5f5}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:hover,.pager .disabled>a:focus,.pager .disabled>span{color:#d9d9d9;cursor:default;background-color:#fff}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop,.modal-backdrop.fade.in{opacity:.8;filter:alpha(opacity=80)}.modal{position:fixed;top:10%;left:50%;z-index:1050;width:560px;margin-left:-280px;background-color:#fff;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);*border:1px solid #999;border-radius:6px;outline:0;box-shadow:0 3px 7px rgba(0,0,0,0.3);background-clip:padding-box}.modal.fade{top:-25%;transition:opacity .3s linear,top .3s ease-out}.modal.fade.in{top:10%}.modal-header{padding:9px 15px;border-bottom:1px solid #eee}.modal-header .close{margin-top:2px}.modal-header h3{margin:0;line-height:30px}.modal-body{position:relative;max-height:400px;padding:15px;overflow-y:auto}.modal-form{margin-bottom:0}.modal-footer{padding:14px 15px 15px;margin-bottom:0;text-align:right;background-color:#f5f5f5;border-top:1px solid #ddd;border-radius:0 0 6px 6px;*zoom:1;box-shadow:inset 0 1px 0 #fff}.modal-footer:before,.modal-footer:after{display:table;line-height:0;content:""}.modal-footer:after{clear:both}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.tooltip{position:absolute;z-index:1030;display:block;font-size:11px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.tooltip.in{opacity:.8;filter:alpha(opacity=80)}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:0}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-top-color:#000;border-width:5px 5px 0}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#000;border-width:5px 5px 5px 0}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-left-color:#000;border-width:5px 0 5px 5px}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-bottom-color:#000;border-width:0 5px 5px}.popover{position:absolute;top:0;left:0;z-index:1010;display:none;max-width:276px;padding:1px;text-align:left;white-space:normal;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:6px;box-shadow:0 5px 10px rgba(0,0,0,0.2);background-clip:padding-box}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;font-weight:normal;line-height:18px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-title:empty{display:none}.popover-content{padding:9px 14px}.popover .arrow,.popover .arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover .arrow{border-width:11px}.popover .arrow:after{border-width:10px;content:""}.popover.top .arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,0.25);border-bottom-width:0}.popover.top .arrow:after{bottom:1px;margin-left:-10px;border-top-color:#fff;border-bottom-width:0}.popover.right .arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,0.25);border-left-width:0}.popover.right .arrow:after{bottom:-10px;left:1px;border-right-color:#fff;border-left-width:0}.popover.bottom .arrow{top:-11px;left:50%;margin-left:-11px;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,0.25);border-top-width:0}.popover.bottom .arrow:after{top:1px;margin-left:-10px;border-bottom-color:#fff;border-top-width:0}.popover.left .arrow{top:50%;right:-11px;margin-top:-11px;border-left-color:#999;border-left-color:rgba(0,0,0,0.25);border-right-width:0}.popover.left .arrow:after{right:1px;bottom:-10px;border-left-color:#fff;border-right-width:0}.tipmenu .tooltip{position:absolute;right:0;left:0;margin-right:auto;margin-left:auto}.tipmenu:hover .tooltip{opacity:1}.tipmenu:hover .tooltip a{color:white}.tipmenu .tooltip-inner{margin-right:auto;margin-left:auto}.thumbnails{margin-left:0;list-style:none;*zoom:1}.thumbnails:before,.thumbnails:after{display:table;line-height:0;content:""}.thumbnails:after{clear:both}.row-fluid .thumbnails{margin-left:0}.thumbnails>li{float:left;margin-bottom:20px;margin-left:0}.thumbnails>li a:hover{text-decoration:none}.thumbnail{display:block;padding:4px;line-height:20px;border:1px solid #ddd;border-radius:0;box-shadow:0 1px 3px rgba(0,0,0,0.055);transition:all .2s ease-in-out}a.thumbnail:hover,a.thumbnail:focus{border-color:#000;box-shadow:0 1px 4px rgba(0,105,214,0.25)}.thumbnail>img{display:block;max-width:100%;margin-right:auto;margin-left:auto}.thumbnail .caption{padding:9px;color:#555}.media,.media-body{overflow:hidden;*overflow:visible;zoom:1}.media,.media .media{margin-top:15px}.media:first-child{margin-top:0}.media-object{display:block}.media-heading{margin:0 0 5px}.media>.pull-left{margin-right:10px}.media>.pull-right{margin-left:10px}.media-list{margin-left:0;list-style:none}.label,.badge{display:inline-block;padding:2px 4px;font-size:11.844px;font-weight:bold;line-height:14px;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);white-space:nowrap;vertical-align:baseline;background-color:#d9d9d9}.label{border-radius:3px}.badge{padding-right:9px;padding-left:9px;border-radius:9px}.label:empty,.badge:empty{display:none}a.label:hover,a.label:focus,a.badge:hover,a.badge:focus{color:#fff;text-decoration:none;cursor:pointer}.label-important,.badge-important{background-color:#fff}.label-important[href],.badge-important[href]{background-color:#e6e6e6}.label-warning,.badge-warning{background-color:#df7f48}.label-warning[href],.badge-warning[href]{background-color:#d06324}.label-success,.badge-success{background-color:#fff}.label-success[href],.badge-success[href]{background-color:#e6e6e6}.label-info,.badge-info{background-color:#fff}.label-info[href],.badge-info[href]{background-color:#e6e6e6}.label-inverse,.badge-inverse{background-color:#343434}.label-inverse[href],.badge-inverse[href]{background-color:#1a1a1a}.btn .label,.btn .badge{position:relative;top:-1px}.btn-mini .label,.btn-mini .badge{top:0}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f7f7f7;background-image:linear-gradient(to bottom,#f5f5f5,#f9f9f9);background-repeat:repeat-x;border-radius:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff9f9f9',GradientType=0);box-shadow:inset 0 1px 2px rgba(0,0,0,0.1)}.progress .bar{float:left;width:0;height:100%;font-size:12px;color:#fff;text-align:center;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#0e90d2;background-image:linear-gradient(to bottom,#149bdf,#0480be);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf',endColorstr='#ff0480be',GradientType=0);box-shadow:inset 0 -1px 0 rgba(0,0,0,0.15);box-sizing:border-box;transition:width .6s ease}.progress .bar+.bar{box-shadow:inset 1px 0 0 rgba(0,0,0,0.15),inset 0 -1px 0 rgba(0,0,0,0.15)}.progress-striped .bar{background-color:#149bdf;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent);background-size:40px 40px}.progress.active .bar{-webkit-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-danger .bar,.progress .bar-danger{background-color:#dd514c;background-image:linear-gradient(to bottom,#ee5f5b,#c43c35);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffc43c35',GradientType=0)}.progress-danger.progress-striped .bar,.progress-striped .bar-danger{background-color:#ee5f5b;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-success .bar,.progress .bar-success{background-color:#5eb95e;background-image:linear-gradient(to bottom,#62c462,#57a957);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462',endColorstr='#ff57a957',GradientType=0)}.progress-success.progress-striped .bar,.progress-striped .bar-success{background-color:#62c462;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-info .bar,.progress .bar-info{background-color:#4bb1cf;background-image:linear-gradient(to bottom,#5bc0de,#339bb9);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff339bb9',GradientType=0)}.progress-info.progress-striped .bar,.progress-striped .bar-info{background-color:#5bc0de;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.progress-warning .bar,.progress .bar-warning{background-color:#e69a6f;background-image:linear-gradient(to bottom,#eaad89,#df7f48);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeaad89',endColorstr='#ffdf7f48',GradientType=0)}.progress-warning.progress-striped .bar,.progress-striped .bar-warning{background-color:#eaad89;background-image:linear-gradient(45deg,rgba(255,255,255,0.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,0.15) 50%,rgba(255,255,255,0.15) 75%,transparent 75%,transparent)}.accordion{margin-bottom:20px}.accordion-group{margin-bottom:2px;border:1px solid #e5e5e5;border-radius:0}.accordion-heading{border-bottom:0}.accordion-heading .accordion-toggle{display:block;padding:8px 15px}.accordion-toggle{cursor:pointer}.accordion-inner{padding:9px 15px;border-top:1px solid #e5e5e5}.carousel{position:relative;margin-bottom:20px;line-height:1}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;transition:.6s ease-in-out left}.carousel-inner>.item>img,.carousel-inner>.item>a>img{display:block;line-height:1}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:40%;left:15px;width:40px;height:40px;margin-top:-20px;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-align:center;background:#222;border:3px solid #fff;border-radius:23px;opacity:.5;filter:alpha(opacity=50)}.carousel-control.right{right:15px;left:auto}.carousel-control:hover,.carousel-control:focus{color:#fff;text-decoration:none;opacity:.9;filter:alpha(opacity=90)}.carousel-indicators{position:absolute;top:15px;right:15px;z-index:5;margin:0;list-style:none}.carousel-indicators li{display:block;float:left;width:10px;height:10px;margin-left:5px;text-indent:-999px;background-color:#ccc;background-color:rgba(255,255,255,0.25);border-radius:5px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:0;bottom:0;left:0;padding:15px;background:#343434;background:rgba(0,0,0,0.75)}.carousel-caption h4,.carousel-caption p{line-height:20px;color:#fff}.carousel-caption h4{margin:0 0 5px}.carousel-caption p{margin-bottom:0}.hero-unit{padding:60px;margin-bottom:30px;font-size:18px;font-weight:200;line-height:30px;color:inherit;background-color:#f8f8f8;border-radius:6px}.hero-unit h1{margin-bottom:0;font-size:60px;line-height:1;letter-spacing:-1px;color:inherit}.hero-unit li{line-height:30px}.pull-right{float:right}.pull-left{float:left}.hide{display:none}.show{display:block}.invisible{visibility:hidden}.affix{position:fixed}html,body{height:100%;overflow:hidden}body{width:100%;height:100%;padding:0;margin:0;font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;color:#343434;background-color:#fff}.padded{padding:20px}#layout{position:relative;z-index:1;height:100%;padding:0}#mainwrapper{width:100%;height:100%;margin:0}#contentwrapper,#contentcolumn{position:absolute;top:0;right:0;bottom:0;left:80px;z-index:10;margin:0}#umb-notifications-wrapper{left:80px}#contentcolumn{left:0}#contentcolumn iframe#right{position:relative;display:block;width:100%;height:100%;border:0}#leftcolumn{position:absolute;z-index:20;float:left;width:80px;height:100%}#applications{position:absolute;top:0;bottom:0;left:0;z-index:1000;height:100%;text-align:center}#applications-tray{position:absolute;top:0;bottom:0;left:80px;z-index:900;height:100%;text-align:center}#search-form{position:absolute;top:0;right:0;left:0;z-index:100;display:block;margin:0;overflow:hidden}#search-form .form-search{display:-ms-flexbox;display:-webkit-flex;display:flex;margin-top:17px}#search-form .form-search .umb-search-field{width:auto;min-width:160px;-webkit-flex:1;-ms-flex:1;flex:1}#navigation{position:absolute;top:0;bottom:0;left:80px;z-index:100;height:100%;background:#fff}.navigation-inner-container{position:absolute;top:0;right:0;bottom:0;left:0;z-index:100;padding-top:100px;border-right:1px solid #d9d9d9}#dialog{position:absolute;top:0;left:100%;z-index:50;display:inline-block;min-width:500px}#tree{z-index:100!important;padding:0;overflow:auto}#tree .umb-tree{padding:0 0 20px 0}#search-results{z-index:200}#contextMenu{position:absolute;top:0;left:100%;z-index:50;min-width:250px}#speechbubble{position:absolute;right:0;bottom:100px;left:0;z-index:1060;padding:0;margin:auto;background:0;border:0;border-bottom:0;border-radius:0}.ui-resizable-e{position:absolute;top:0;right:-5px;bottom:0;z-index:9999!important;width:4px;cursor:e-resize;background-color:#f8f8f8;border:solid 1px #d9d9d9;border-top:0;border-bottom:0}@media(min-width:1101px){#contentwrapper,#umb-notifications-wrapper{left:440px}#speechbubble{left:360px}}.emptySection #contentwrapper,.emptySection #umb-notifications-wrapper{left:80px}.emptySection #speechbubble{left:0}.emptySection #navigation{display:none}.login-only #speechbubble{left:0!important;z-index:10000}.login-only #speechbubble ul{padding-left:20px}@media(max-width:767px){#leftcolumn{width:60px}#applications ul.sections{width:60px}ul.sections.sections-tray{width:60px}#applications-tray{left:60px}#navigation{left:60px}#contentwrapper,#contentcolumn,#umb-notifications-wrapper{left:30px}#umbracoMainPageBody .umb-modal-left.fade.in{margin-left:60px}}@media(max-width:500px){#leftcolumn{width:40px}#applications ul.sections{width:40px}ul.sections li [class^="icon-"]:before{font-size:25px!important}#applications ul.sections li.avatar a img{width:25px}ul.sections a span{display:none!important}#applications ul.sections-tray{width:40px}ul.sections.sections-tray{width:40px}#applications-tray{left:40px}#navigation{left:40px}#contentwrapper,#contentcolumn,#umb-notifications-wrapper{left:20px}#umbracoMainPageBody .umb-modal-left.fade.in{width:85%!important;margin-left:40px}}.shadow-depth-1{box-shadow:0 1px 3px rgba(0,0,0,0.12),0 1px 2px rgba(0,0,0,0.24)}.shadow-depth-2{box-shadow:0 3px 6px rgba(0,0,0,0.16),0 3px 6px rgba(0,0,0,0.23)}.shadow-depth-3{box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23)}.shadow-depth-4{box-shadow:0 14px 28px rgba(0,0,0,0.25),0 10px 10px rgba(0,0,0,0.22)}.shadow-depth-5{box-shadow:0 19px 38px rgba(0,0,0,0.3),0 15px 12px rgba(0,0,0,0.22)}.animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.animated.infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animated.hinge{-webkit-animation-duration:2s;animation-duration:2s}@-webkit-keyframes bounce{0%,100%,20%,53%,80%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}@keyframes bounce{0%,100%,20%,53%,80%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}40%,43%{-webkit-transform:translate3d(0,-30px,0);transform:translate3d(0,-30px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}70%{-webkit-transform:translate3d(0,-15px,0);transform:translate3d(0,-15px,0);transition-timing-function:cubic-bezier(0.755,0.05,0.855,0.06)}90%{-webkit-transform:translate3d(0,-4px,0);transform:translate3d(0,-4px,0)}}.bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,100%,50%{opacity:1}25%,75%{opacity:0}}.flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes pulse{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.pulse{-webkit-animation-name:pulse;animation-name:pulse}@-webkit-keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,0.75,1);transform:scale3d(1.25,0.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,0.85,1);transform:scale3d(1.15,0.85,1)}65%{-webkit-transform:scale3d(0.95,1.05,1);transform:scale3d(0.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,0.95,1);transform:scale3d(1.05,0.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes rubberBand{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}30%{-webkit-transform:scale3d(1.25,0.75,1);transform:scale3d(1.25,0.75,1)}40%{-webkit-transform:scale3d(0.75,1.25,1);transform:scale3d(0.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,0.85,1);transform:scale3d(1.15,0.85,1)}65%{-webkit-transform:scale3d(0.95,1.05,1);transform:scale3d(0.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,0.95,1);transform:scale3d(1.05,0.95,1)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shake{0%,100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.shake{-webkit-animation-name:shake;animation-name:shake}@-webkit-keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}@keyframes swing{20%{-webkit-transform:rotate3d(0,0,1,15deg);transform:rotate3d(0,0,1,15deg)}40%{-webkit-transform:rotate3d(0,0,1,-10deg);transform:rotate3d(0,0,1,-10deg)}60%{-webkit-transform:rotate3d(0,0,1,5deg);transform:rotate3d(0,0,1,5deg)}80%{-webkit-transform:rotate3d(0,0,1,-5deg);transform:rotate3d(0,0,1,-5deg)}100%{-webkit-transform:rotate3d(0,0,1,0deg);transform:rotate3d(0,0,1,0deg)}}.swing{-webkit-animation-name:swing;animation-name:swing;-webkit-transform-origin:top center;transform-origin:top center}@-webkit-keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg);transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes tada{0%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}10%,20%{-webkit-transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg);transform:scale3d(0.9,0.9,0.9) rotate3d(0,0,1,-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg);transform:scale3d(1.1,1.1,1.1) rotate3d(0,0,1,-3deg)}100%{-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}@keyframes wobble{0%{-webkit-transform:none;transform:none}15%{-webkit-transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg);transform:translate3d(-25%,0,0) rotate3d(0,0,1,-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg);transform:translate3d(20%,0,0) rotate3d(0,0,1,3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg);transform:translate3d(-15%,0,0) rotate3d(0,0,1,-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg);transform:translate3d(10%,0,0) rotate3d(0,0,1,2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg);transform:translate3d(-5%,0,0) rotate3d(0,0,1,-1deg)}100%{-webkit-transform:none;transform:none}}.wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes bounceIn{0%,100%,20%,40%,60%,80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(0.97,0.97,0.97);transform:scale3d(0.97,0.97,0.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}@keyframes bounceIn{0%,100%,20%,40%,60%,80%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(0.97,0.97,0.97);transform:scale3d(0.97,0.97,0.97)}100%{opacity:1;-webkit-transform:scale3d(1,1,1);transform:scale3d(1,1,1)}}.bounceIn{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInDown{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0);transform:translate3d(0,-3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0);transform:translate3d(0,25px,0)}75%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}90%{-webkit-transform:translate3d(0,5px,0);transform:translate3d(0,5px,0)}100%{-webkit-transform:none;transform:none}}.bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInLeft{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0);transform:translate3d(-3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0);transform:translate3d(25px,0,0)}75%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}90%{-webkit-transform:translate3d(5px,0,0);transform:translate3d(5px,0,0)}100%{-webkit-transform:none;transform:none}}.bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}@keyframes bounceInRight{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0);transform:translate3d(3000px,0,0)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0);transform:translate3d(-25px,0,0)}75%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}90%{-webkit-transform:translate3d(-5px,0,0);transform:translate3d(-5px,0,0)}100%{-webkit-transform:none;transform:none}}.bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}@keyframes bounceInUp{0%,100%,60%,75%,90%{transition-timing-function:cubic-bezier(0.215,0.61,0.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0);transform:translate3d(0,3000px,0)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}75%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}90%{-webkit-transform:translate3d(0,-5px,0);transform:translate3d(0,-5px,0)}100%{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(0.9,0.9,0.9);transform:scale3d(0.9,0.9,0.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}100%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}}.bounceOut{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0);transform:translate3d(0,-20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0);transform:translate3d(20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0);transform:translate3d(-20px,0,0)}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0);transform:translate3d(0,20px,0)}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}100%{opacity:1}}@keyframes fadeIn{0%{opacity:0}100%{opacity:1}}.fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}100%{opacity:1;-webkit-transform:none;transform:none}}.fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeOut{0%{opacity:1}100%{opacity:0}}@keyframes fadeOut{0%{opacity:1}100%{opacity:0}}.fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(0.95,0.95,0.95);transform:perspective(400px) scale3d(0.95,0.95,0.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-360deg);transform:perspective(400px) rotate3d(0,1,0,-360deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);transform:perspective(400px) translate3d(0,0,150px) rotate3d(0,1,0,-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(0.95,0.95,0.95);transform:perspective(400px) scale3d(0.95,0.95,0.95);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}100%{-webkit-transform:perspective(400px);transform:perspective(400px);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animated.flip{-webkit-animation-name:flip;animation-name:flip;-webkit-backface-visibility:visible;-ms-backface-visibility:visible;backface-visibility:visible}@-webkit-keyframes flipInX{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,10deg);transform:perspective(400px) rotate3d(1,0,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(1,0,0,-5deg);transform:perspective(400px) rotate3d(1,0,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInX{-webkit-animation-name:flipInX;animation-name:flipInX;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipInY{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg);transition-timing-function:ease-in}40%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-20deg);transform:perspective(400px) rotate3d(0,1,0,-20deg);transition-timing-function:ease-in}60%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,10deg);transform:perspective(400px) rotate3d(0,1,0,10deg)}80%{-webkit-transform:perspective(400px) rotate3d(0,1,0,-5deg);transform:perspective(400px) rotate3d(0,1,0,-5deg)}100%{-webkit-transform:perspective(400px);transform:perspective(400px)}}.flipInY{-webkit-animation-name:flipInY;animation-name:flipInY;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg)}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(1,0,0,-20deg);transform:perspective(400px) rotate3d(1,0,0,-20deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(1,0,0,90deg);transform:perspective(400px) rotate3d(1,0,0,90deg)}}.flipOutX{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg)}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{opacity:1;-webkit-transform:perspective(400px) rotate3d(0,1,0,-15deg);transform:perspective(400px) rotate3d(0,1,0,-15deg)}100%{opacity:0;-webkit-transform:perspective(400px) rotate3d(0,1,0,90deg);transform:perspective(400px) rotate3d(0,1,0,90deg)}}.flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-name:flipOutY;animation-name:flipOutY;-webkit-backface-visibility:visible!important;-ms-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes lightSpeedIn{0%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg)}60%{opacity:1;-webkit-transform:skewX(20deg);transform:skewX(20deg)}80%{opacity:1;-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes lightSpeedIn{0%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg)}60%{opacity:1;-webkit-transform:skewX(20deg);transform:skewX(20deg)}80%{opacity:1;-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}100%{opacity:1;-webkit-transform:none;transform:none}}.lightSpeedIn{-webkit-animation-name:lightSpeedIn;animation-name:lightSpeedIn;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg)}}@keyframes lightSpeedOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg)}}.lightSpeedOut{-webkit-animation-name:lightSpeedOut;animation-name:lightSpeedOut;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);-webkit-transform-origin:center;transform-origin:center}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:center;transform-origin:center}}@keyframes rotateIn{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-200deg);transform:rotate3d(0,0,1,-200deg);-webkit-transform-origin:center;transform-origin:center}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:center;transform-origin:center}}.rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn}@-webkit-keyframes rotateInDownLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateInDownLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft}@-webkit-keyframes rotateInDownRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateInDownRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight}@-webkit-keyframes rotateInUpLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateInUpLeft{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,45deg);transform:rotate3d(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft}@-webkit-keyframes rotateInUpRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateInUpRight{0%{opacity:0;-webkit-transform:rotate3d(0,0,1,-90deg);transform:rotate3d(0,0,1,-90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:1;-webkit-transform:none;transform:none;-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight}@-webkit-keyframes rotateOut{0%{opacity:1;-webkit-transform-origin:center;transform-origin:center}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);-webkit-transform-origin:center;transform-origin:center}}@keyframes rotateOut{0%{opacity:1;-webkit-transform-origin:center;transform-origin:center}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,200deg);transform:rotate3d(0,0,1,200deg);-webkit-transform-origin:center;transform-origin:center}}.rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut}@-webkit-keyframes rotateOutDownLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateOutDownLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate(0,0,1,45deg);transform:rotate(0,0,1,45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft}@-webkit-keyframes rotateOutDownRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateOutDownRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight}@-webkit-keyframes rotateOutUpLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}@keyframes rotateOutUpLeft{0%{opacity:1;-webkit-transform-origin:left bottom;transform-origin:left bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,-45deg);transform:rotate3d(0,0,1,-45deg);-webkit-transform-origin:left bottom;transform-origin:left bottom}}.rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft}@-webkit-keyframes rotateOutUpRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}@keyframes rotateOutUpRight{0%{opacity:1;-webkit-transform-origin:right bottom;transform-origin:right bottom}100%{opacity:0;-webkit-transform:rotate3d(0,0,1,90deg);transform:rotate3d(0,0,1,90deg);-webkit-transform-origin:right bottom;transform-origin:right bottom}}.rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight}@-webkit-keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}40%,80%{opacity:1;-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}100%{opacity:0;-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0)}}@keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}20%,60%{-webkit-transform:rotate3d(0,0,1,80deg);transform:rotate3d(0,0,1,80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}40%,80%{opacity:1;-webkit-transform:rotate3d(0,0,1,60deg);transform:rotate3d(0,0,1,60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-transform-origin:top left;transform-origin:top left}100%{opacity:0;-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0)}}.hinge{-webkit-animation-name:hinge;animation-name:hinge}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg);transform:translate3d(-100%,0,0) rotate3d(0,0,1,-120deg)}100%{opacity:1;-webkit-transform:none;transform:none}}.rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}@keyframes rollOut{0%{opacity:1}100%{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg);transform:translate3d(100%,0,0) rotate3d(0,0,1,120deg)}}.rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}50%{opacity:1}}.zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);transform:scale3d(0.1,0.1,0.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}60%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1)}}.zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}100%{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(0.3,0.3,0.3);transform:scale3d(0.3,0.3,0.3)}100%{opacity:0}}.zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}.zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(-2000px,0,0);transform:scale(0.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(-2000px,0,0);transform:scale(0.1) translate3d(-2000px,0,0);-webkit-transform-origin:left center;transform-origin:left center}}.zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(2000px,0,0);transform:scale(0.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0);transform:scale3d(0.475,0.475,0.475) translate3d(-42px,0,0)}100%{opacity:0;-webkit-transform:scale(0.1) translate3d(2000px,0,0);transform:scale(0.1) translate3d(2000px,0,0);-webkit-transform-origin:right center;transform-origin:right center}}.zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);transform:scale3d(0.475,0.475,0.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19);animation-timing-function:cubic-bezier(0.55,0.055,0.675,0.19)}100%{opacity:0;-webkit-transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);transform:scale3d(0.1,0.1,0.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);animation-timing-function:cubic-bezier(0.175,0.885,0.32,1);-webkit-transform-origin:center bottom;transform-origin:center bottom}}.zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp}.animated.-half-second{-webkit-animation-duration:.5s;animation-duration:.5s}.slide-in-left.ng-hide-remove{-webkit-animation:fadeInLeft .6s;animation:fadeInLeft .6s}.slide-in-left.ng-hide-add{display:block!important;-webkit-animation:fadeOutLeft .6s;animation:fadeOutLeft .6s}.slide-in-right.ng-hide-remove{-webkit-animation:fadeInRight .6s;animation:fadeInRight .6s}.slide-in-right.ng-hide-add{display:block!important;-webkit-animation:fadeOutRight .6s;animation:fadeOutRight .6s}.slide-in-up.ng-hide-remove{-webkit-animation:fadeInUp .6s;animation:fadeInUp .6s}.slide-in-up.ng-hide-add{display:block!important;-webkit-animation:fadeOutDown .6s;animation:fadeOutDown .6s}.tree-node-delete-leave{position:relative;display:block;-webkit-animation:leave 600ms cubic-bezier(0.445,0.05,0.55,0.95);animation:leave 600ms cubic-bezier(0.445,0.05,0.55,0.95)}@-webkit-keyframes leave{to{bottom:-70px;height:0;opacity:0}25%{bottom:15px}from{bottom:0;height:30px;opacity:1}}@keyframes leave{to{bottom:-70px;height:0;opacity:0}25%{bottom:15px}from{bottom:0;height:30px;opacity:1}}.tree-node-delete-leave *{color:#9d261d!important}.tree-node-slide-up{top:0;opacity:1;transition:700ms cubic-bezier(0,0,0.58,1) all}.tree-node-slide-up *{font-size:100%;transition:font-size 700ms}.tree-node-slide-up.tree-node-slide-up-hide-active{top:-100px;opacity:0}.tree-node-slide-up.tree-node-slide-up-hide-active *{font-size:120%}.tree-fade-out-hide,.tree-fade-out-show,.tree-fade-out-hide div:not(.tree-node-slide-up-hide-active),.tree-fade-out-show div:not(.tree-node-slide-up-hide-active){transition:700ms cubic-bezier(0.075,0.82,0.165,1) all}.tree-fade-out-show.tree-fade-out-show-active div:not(.tree-node-slide-up-hide-active){opacity:1}.tree-fade-out-hide.tree-fade-out-hide-active div:not(.tree-node-slide-up-hide-active){opacity:0}.btn{display:inline-block;padding:4px 12px;margin-bottom:0;font-size:14px;line-height:20px;color:#000;text-align:center;vertical-align:middle;cursor:pointer;background:#f2f2f2;border:1px solid #ccc;box-shadow:none}.btn:hover,.btn:focus{color:#343434;text-decoration:none;background:#e4e4e4;background-position:0 -15px;transition:background-position .1s linear}.btn:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.active,.btn:active{background-image:none;outline:0;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.btn.disabled,.btn[disabled],.btn:disabled:hover{cursor:default;border-color:#ccc;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.btn-group>.btn+.dropdown-toggle{box-shadow:none}.btn-group>.btn:first-child,.btn-group>.btn:last-child,.btn-group>.dropdown-toggle{border-radius:0}.btn-large{padding:11px 19px;font-size:17.5px}.btn-large [class^="icon-"],.btn-large [class*=" icon-"]{margin-top:4px;border-radius:0}.btn-small{padding:2px 10px;font-size:11.9px;border-radius:0}.btn-small [class^="icon-"],.btn-small [class*=" icon-"]{margin-top:0}.btn-mini [class^="icon-"],.btn-mini [class*=" icon-"]{margin-top:-1px}.btn-mini{padding:0 6px;font-size:10.5px;border-radius:0}.btn-block{display:block;width:100%;padding-right:0;padding-left:0;box-sizing:border-box}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.btn-round{z-index:6666;display:inline-block;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:32px;color:#555;text-align:center;background:#fff;border-radius:15px}.btn-primary.active,.btn-warning.active,.btn-danger.active,.btn-success.active,.btn-info.active,.btn-inverse.active,.btn-neutral.active{color:rgba(255,255,255,0.75)}.btn-primary{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-primary:hover,.btn-primary:focus,.btn-primary:active{background-color:#4fa23c}.btn-primary:hover,.btn-primary:focus,.btn-primary:active,.btn-primary.active,.btn-primary.disabled,.btn-primary[disabled]{color:#fff;*background-color:#4a9638}.btn-primary:active,.btn-primary.active{background-color:#418431 \9}.btn-warning{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#df7f48;*background-color:#df7f48;background-image:linear-gradient(to bottom,#df7f48,#df7f48);background-repeat:repeat-x;border-color:#df7f48 #df7f48 #ba5821;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdf7f48',endColorstr='#ffdf7f48',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-warning:hover,.btn-warning:focus,.btn-warning:active{background-color:#dd793f}.btn-warning:hover,.btn-warning:focus,.btn-warning:active,.btn-warning.active,.btn-warning.disabled,.btn-warning[disabled]{color:#fff;*background-color:#db7032}.btn-warning:active,.btn-warning.active{background-color:#d06324 \9}.btn-danger{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#ee5f5b;*background-color:#ee5f5b;background-image:linear-gradient(to bottom,#ee5f5b,#ee5f5b);background-repeat:repeat-x;border-color:#ee5f5b #ee5f5b #e51d18;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b',endColorstr='#ffee5f5b',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-danger:hover,.btn-danger:focus,.btn-danger:active{background-color:#ed5652}.btn-danger:hover,.btn-danger:focus,.btn-danger:active,.btn-danger.active,.btn-danger.disabled,.btn-danger[disabled]{color:#fff;*background-color:#ec4844}.btn-danger:active,.btn-danger.active{background-color:#e9322d \9}.btn-success{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#53a93f;*background-color:#53a93f;background-image:linear-gradient(to bottom,#53a93f,#53a93f);background-repeat:repeat-x;border-color:#53a93f #53a93f #38712a;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff53a93f',endColorstr='#ff53a93f',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-success:hover,.btn-success:focus,.btn-success:active{background-color:#4fa23c}.btn-success:hover,.btn-success:focus,.btn-success:active,.btn-success.active,.btn-success.disabled,.btn-success[disabled]{color:#fff;*background-color:#4a9638}.btn-success:active,.btn-success.active{background-color:#418431 \9}.btn-info{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#49afcd;*background-color:#2f96b4;background-image:linear-gradient(to bottom,#5bc0de,#2f96b4);background-repeat:repeat-x;border-color:#2f96b4 #2f96b4 #1f6377;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de',endColorstr='#ff2f96b4',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-info:hover,.btn-info:focus,.btn-info:active{background-color:#53bddc}.btn-info:hover,.btn-info:focus,.btn-info:active,.btn-info.active,.btn-info.disabled,.btn-info[disabled]{color:#fff;*background-color:#2a85a0}.btn-info:active,.btn-info.active{background-color:#24748c \9}.btn-inverse{color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#363636;*background-color:#222;background-image:linear-gradient(to bottom,#444,#222);background-repeat:repeat-x;border-color:#222 #222 #000;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff444444',endColorstr='#ff222222',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active{background-color:#3f3f3f}.btn-inverse:hover,.btn-inverse:focus,.btn-inverse:active,.btn-inverse.active,.btn-inverse.disabled,.btn-inverse[disabled]{color:#fff;*background-color:#151515}.btn-inverse:active,.btn-inverse.active{background-color:#080808 \9}.btn-neutral{color:#fff;color:#656565;text-shadow:0 -1px 0 rgba(0,0,0,0.25);background-color:#f5f5f5;*background-color:#f5f5f5;background-image:linear-gradient(to bottom,#f5f5f5,#f5f5f5);background-repeat:repeat-x;border-color:#f5f5f5 #f5f5f5 #cfcfcf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5',endColorstr='#fff5f5f5',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active{background-color:#f0f0f0}.btn-neutral:hover,.btn-neutral:focus,.btn-neutral:active,.btn-neutral.active,.btn-neutral.disabled,.btn-neutral[disabled]{color:#fff;*background-color:#e8e8e8}.btn-neutral:active,.btn-neutral.active{background-color:#dcdcdc \9}.btn-neutral:hover,.btn-neutral:focus{color:#656565}.btn-neutral.disabled,.btn-neutral[disabled]{color:#656565;opacity:.65;filter:alpha(opacity=65)}.btn-install{display:block;padding:15px 50px;margin:40px auto;font-size:16px;color:white;background:#2e8aea;border:0}.btn-install:hover{background:#2b8ee3}button.btn,input[type="submit"].btn{*padding-top:3px;*padding-bottom:3px}button.btn::-moz-focus-inner,input[type="submit"].btn::-moz-focus-inner{padding:0;border:0}button.btn.btn-large,input[type="submit"].btn.btn-large{*padding-top:7px;*padding-bottom:7px}button.btn.btn-small,input[type="submit"].btn.btn-small{*padding-top:3px;*padding-bottom:3px}button.btn.btn-mini,input[type="submit"].btn.btn-mini{*padding-top:1px;*padding-bottom:1px}.btn-link,.btn-link:active,.btn-link[disabled]{background-color:transparent;background-image:none;box-shadow:none}.btn-link{color:#000;cursor:pointer;border-color:transparent;border-radius:0}.btn-link:hover,.btn-link:focus{color:#000;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,.btn-link[disabled]:focus{color:#343434;text-decoration:none}.btn-link-reverse{text-decoration:underline}.btn-link-reverse:hover,.btn-link-reverse:focus{text-decoration:none}.btn-link.-underline{display:inline-block;text-decoration:underline}.btn-link.-underline:hover{text-decoration:none}small.umb-detail,label small,.guiDialogTiny{display:block;font-size:11px;font-weight:normal;color:#b3b3b3!important;text-decoration:none}label.control-label,.control-label{padding:0 10px 0 0!important;font-weight:bold}.umb-status-label{color:#555!important}.controls-row label{padding:0 10px 0 10px;vertical-align:middle}.breadcrumb{display:block;height:30px;margin-top:10px}.breadcrumb li{height:30px;vertical-align:middle}.breadcrumb li a{height:30px;vertical-align:middle}.breadcrumb input{font-size:11px!Important}.form-search{position:relative;padding:0}.form-search a{text-decoration:none;cursor:pointer}.form-search a:hover{color:#555}.form-search h4{color:#555}.form-search small{color:#d9d9d9}.form-search .icon,.form-search .icon-search{position:absolute;top:6px;left:4px;color:#d9d9d9}.form-search .icon-search{pointer-events:none}.form-search input{width:90%;padding:4px 0 4px 16px;padding-left:25px!Important;font-size:17.5px;font-weight:400;line-height:22px;background:#fff;border:1px solid #d9d9d9}.form-search .search-input{font-weight:bold;border-color:#d9d9d9}.form-search .search-input:hover,.form-search .search-input:focus,.form-search .search-input:focus:hover{border-color:#ccc}.form-search .search-input:-moz-placeholder{font-weight:normal}.form-search .search-input:-ms-input-placeholder{font-weight:normal}.form-search .search-input::-webkit-input-placeholder{font-weight:normal}form{margin:0 0 20px}form.-no-margin-bottom{margin-bottom:0}fieldset{padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:40px;color:#343434;border:0;border-bottom:1px solid #e5e5e5}legend small{font-size:15px;color:#d9d9d9}label,input,button,select,textarea{font-size:14px;font-weight:normal;line-height:20px}input,button,select,textarea{font-family:"Open Sans","Helvetica Neue",Helvetica,Arial,sans-serif}label{display:inline-block;margin-bottom:5px}select,textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{display:inline-block;height:20px;padding:4px 6px;margin-bottom:10px;font-size:14px;line-height:20px;color:#555;vertical-align:middle;border-radius:0}input.-full-width-input{width:100%;height:30px;padding:4px 6px;box-sizing:border-box}input,textarea,.uneditable-input{width:206px}textarea{height:auto}textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"],.uneditable-input{background-color:#fff;border:1px solid #ccc;transition:border linear .2s,box-shadow linear .2s}textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus,.uneditable-input:focus{border-color:none;outline:0;outline:none \9}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;*margin-top:0;line-height:normal}input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"]{width:auto}select,input[type="file"]{height:30px;*margin-top:4px;line-height:30px}select{width:220px;background-color:#fff;border:1px solid #ccc}select[multiple],select[size]{height:auto}select:focus,input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted #333;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.uneditable-input,.uneditable-textarea{color:#d9d9d9;cursor:not-allowed;background-color:#fcfcfc;border-color:#ccc;box-shadow:inset 0 1px 2px rgba(0,0,0,0.025)}.uneditable-input{overflow:hidden;white-space:nowrap}.uneditable-textarea{width:auto;height:auto}input:-moz-placeholder,textarea:-moz-placeholder{color:#d9d9d9}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#d9d9d9}input::-webkit-input-placeholder,textarea::-webkit-input-placeholder{color:#d9d9d9}.radio,.checkbox{min-height:20px;padding-left:20px}.radio.no-indent,.checkbox.no-indent{padding-left:0}.radio input[type="radio"],.checkbox input[type="checkbox"]{float:left;margin-right:5px;margin-left:0}.controls>.radio:first-child,.controls>.checkbox:first-child{padding-top:5px}.radio.inline,.checkbox.inline{display:inline-block;padding-top:5px;margin-bottom:0;vertical-align:middle}.radio.inline+.radio.inline,.checkbox.inline+.checkbox.inline{margin-left:10px}.input-mini{width:60px}.input-small{width:90px}.input-medium{width:150px}.input-large{width:210px}.input-xlarge{width:270px}.input-xxlarge{width:530px}input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"]{float:none;margin-left:0}.input-append input[class*="span"],.input-append .uneditable-input[class*="span"],.input-prepend input[class*="span"],.input-prepend .uneditable-input[class*="span"],.row-fluid input[class*="span"],.row-fluid select[class*="span"],.row-fluid textarea[class*="span"],.row-fluid .uneditable-input[class*="span"],.row-fluid .input-prepend [class*="span"],.row-fluid .input-append [class*="span"]{display:inline-block}.bigInput,.input-large-type{font-size:20px!important}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:0}input.span12,textarea.span12,.uneditable-input.span12{width:706px}input.span11,textarea.span11,.uneditable-input.span11{width:646px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:526px}input.span8,textarea.span8,.uneditable-input.span8{width:466px}input.span7,textarea.span7,.uneditable-input.span7{width:406px}input.span6,textarea.span6,.uneditable-input.span6{width:346px}input.span5,textarea.span5,.uneditable-input.span5{width:286px}input.span4,textarea.span4,.uneditable-input.span4{width:226px}input.span3,textarea.span3,.uneditable-input.span3{width:166px}input.span2,textarea.span2,.uneditable-input.span2{width:106px}input.span1,textarea.span1,.uneditable-input.span1{width:46px}.controls-row{*zoom:1}.controls-row:before,.controls-row:after{display:table;line-height:0;content:""}.controls-row:after{clear:both}.controls-row [class*="span"],.row-fluid .controls-row [class*="span"]{float:left}.controls-row .checkbox[class*="span"],.controls-row .radio[class*="span"]{padding-top:5px}input[disabled],select[disabled],textarea[disabled],input[readonly],select[readonly],textarea[readonly]{cursor:not-allowed;background-color:#f8f8f8}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"][readonly],input[type="checkbox"][readonly]{background-color:transparent}.show-validation.ng-invalid .control-group.error .control-label,.show-validation.ng-invalid .umb-panel-header-content-wrapper .control-label,.show-validation.ng-invalid .control-group.error .help-block,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-block,.show-validation.ng-invalid .control-group.error .help-inline,.show-validation.ng-invalid .umb-panel-header-content-wrapper .help-inline{color:#9d261d}.show-validation.ng-invalid .control-group.error .checkbox.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .checkbox.ng-invalid,.show-validation.ng-invalid .control-group.error .radio.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper .radio.ng-invalid,.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{color:#9d261d}.show-validation.ng-invalid .control-group.error input.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper input.ng-invalid,.show-validation.ng-invalid .control-group.error select.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper select.ng-invalid,.show-validation.ng-invalid .control-group.error textarea.ng-invalid,.show-validation.ng-invalid .umb-panel-header-content-wrapper textarea.ng-invalid{border-color:#9d261d}.show-validation.ng-invalid .control-group.error .input-prepend .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-prepend .add-on,.show-validation.ng-invalid .control-group.error .input-append .add-on,.show-validation.ng-invalid .umb-panel-header-content-wrapper .input-append .add-on{color:#9d261d;background-color:#f8dcda;border-color:#9d261d}.highlight-error{color:#9d261d!important;border-color:#ee5f5b!important}.show-validation .umb-headline-editor-wrapper input:focus:invalid,.show-validation .umb-headline-editor-wrapper textarea:focus:invalid,.show-validation .umb-headline-editor-wrapper select:focus:invalid{color:inherit;border:0;border-color:inherit;box-shadow:inherit}.ng-invalid>.umb-headline-editor-wrapper h1{color:#9d261d;cursor:pointer;border-bottom:1px dashed #9d261d}.form-actions{padding:19px 20px 20px;margin-top:20px;margin-bottom:20px;background-color:#f5f5f5;border-top:1px solid #e5e5e5;*zoom:1}.form-actions:before,.form-actions:after{display:table;line-height:0;content:""}.form-actions:after{clear:both}.help-block,.help-inline{color:#5a5a5a}.help-block{display:block;margin-bottom:10px}.help-inline{display:inline-block;*display:inline;padding-left:5px;vertical-align:middle;*zoom:1}.input-append,.input-prepend{display:inline-block;margin-bottom:10px;font-size:0;white-space:nowrap;vertical-align:middle}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input,.input-append .dropdown-menu,.input-prepend .dropdown-menu,.input-append .popover,.input-prepend .popover{font-size:14px}.input-append input,.input-prepend input,.input-append select,.input-prepend select,.input-append .uneditable-input,.input-prepend .uneditable-input{position:relative;margin-bottom:0;*margin-left:0;vertical-align:top}.input-append input:focus,.input-prepend input:focus,.input-append select:focus,.input-prepend select:focus,.input-append .uneditable-input:focus,.input-prepend .uneditable-input:focus{z-index:2}.input-append .add-on,.input-prepend .add-on{display:inline-block;width:auto;height:20px;min-width:16px;padding:4px 5px;font-size:14px;font-weight:normal;line-height:20px;text-align:center;text-shadow:0 1px 0 #fff;background-color:#f8f8f8;border:1px solid #ccc}.input-append .add-on,.input-prepend .add-on,.input-append .btn,.input-prepend .btn,.input-append .btn-group>.dropdown-toggle,.input-prepend .btn-group>.dropdown-toggle{vertical-align:top;border-radius:0}.input-append .active,.input-prepend .active{background-color:#a9dba9;border-color:#46a546}.input-prepend .add-on,.input-prepend .btn{margin-right:-1px}.input-append .add-on,.input-append .btn,.input-append .btn-group{margin-left:-1px}.input-prepend.input-append input,.input-prepend.input-append select,.input-prepend.input-append .uneditable-input{border-radius:0}.input-prepend.input-append input+.btn-group .btn,.input-prepend.input-append select+.btn-group .btn,.input-prepend.input-append .uneditable-input+.btn-group .btn{border-radius:0}.input-prepend.input-append .add-on:first-child,.input-prepend.input-append .btn:first-child{margin-right:-1px;border-radius:0}.input-prepend.input-append .add-on:last-child,.input-prepend.input-append .btn:last-child{margin-left:-1px;border-radius:0}.input-prepend.input-append .btn-group:first-child{margin-left:0}input.search-query{padding-right:4px \9;padding-left:14px;padding-left:4px \9;margin:0;border-radius:0}.form-search .input-append .search-query,.form-search .input-prepend .search-query{border-radius:0}.form-search .input-append .search-query{border-radius:14px 0 0 14px}.form-search .input-append .btn{border-radius:0 14px 14px 0}.form-search .input-prepend .search-query{border-radius:0 14px 14px 0}.form-search .input-prepend .btn{border-radius:14px 0 0 14px}.form-search input,.form-inline input,.form-horizontal input,.form-search textarea,.form-inline textarea,.form-horizontal textarea,.form-search select,.form-inline select,.form-horizontal select,.form-search .help-inline,.form-inline .help-inline,.form-horizontal .help-inline,.form-search .uneditable-input,.form-inline .uneditable-input,.form-horizontal .uneditable-input,.form-search .input-prepend,.form-inline .input-prepend,.form-horizontal .input-prepend,.form-search .input-append,.form-inline .input-append,.form-horizontal .input-append{display:inline-block;*display:inline;margin-bottom:0;vertical-align:top;*zoom:1}.form-search .hide,.form-inline .hide,.form-horizontal .hide{display:none}.form-search label,.form-inline label,.form-search .btn-group,.form-inline .btn-group{display:inline-block}.form-search .input-append,.form-inline .input-append,.form-search .input-prepend,.form-inline .input-prepend{margin-bottom:0}.form-search .radio,.form-search .checkbox,.form-inline .radio,.form-inline .checkbox{padding-left:0;margin-bottom:0;vertical-align:middle}.form-search .radio input[type="radio"],.form-search .checkbox input[type="checkbox"],.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{float:left;margin-right:3px;margin-left:0}.control-group{margin-bottom:10px}.control-group.-no-margin{margin-bottom:0}legend+.control-group{margin-top:20px;-webkit-margin-top-collapse:separate}.form-horizontal .control-group{margin-bottom:20px;*zoom:1}.form-horizontal .control-group:before,.form-horizontal .control-group:after{display:table;line-height:0;content:""}.form-horizontal .control-group:after{clear:both}.form-horizontal .control-label{float:left;width:160px;padding-top:5px;text-align:right}.form-horizontal .controls{*display:inline-block;*padding-left:20px;margin-left:180px;*margin-left:0}.form-horizontal .controls:first-child{*padding-left:180px}.form-horizontal .help-block{margin-bottom:0}.form-horizontal input+.help-block,.form-horizontal select+.help-block,.form-horizontal textarea+.help-block,.form-horizontal .uneditable-input+.help-block,.form-horizontal .input-prepend+.help-block,.form-horizontal .input-append+.help-block{margin-top:10px}.form-horizontal .form-actions{padding-left:180px}.umb-panel-buttons .umb-btn-toolbar .btn{position:relative;z-index:1000}@media(max-width:767px){.form-horizontal .control-label{width:100%}.form-horizontal .controls{margin-left:0}}.umb-modalcolumn{background:white}.umb-modalcolumn-header{height:94px;padding:5px 20px 0 20px;white-space:nowrap;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-modalcolumn-header h1{padding-top:10px!important;margin:0;font-size:17.5px;font-weight:400;white-space:nowrap}.umb-modalcolumn-body{position:absolute;top:100px;right:0;bottom:0;left:0;padding:0;overflow:auto;background:#fff}.no-padding .umb-modalcolumn-body{padding:0}.umb-modalcolumn .umb-modalcolumn-header .btn{position:absolute;top:13px;right:15px}.umb-modalcolumn iframe.auto-expand,.umb-modal iframe.auto-expand{position:absolute;top:0;right:0;bottom:0;left:0;padding:0;margin:0;border:0}.umb-modal .umb-loader-wrapper,.umb-modalcolumn .umb-loader-wrapper,.umb-dialog .umb-loader-wrapper{position:relative;margin:20px -20px}.umb-modal-left .umb-panel-header .umb-headline,.umb-modal-left .umb-panel-header h1{width:auto;padding-left:0}.umb-dialog{position:absolute;top:0;right:0;bottom:0;left:0;padding:0;outline:0}.umb-dialog .umb-btn-toolbar{padding:15px 20px 10px 20px;margin-top:30px;clear:both;text-align:right;background:#fff}.umb-dialog .umb-btn-toolbar .umb-control-group{padding:none;border:0}.umb-dialog-body{position:absolute;top:0;right:0;bottom:90px;left:0;overflow:auto}.umb-dialog-body .umb-pane{margin-top:15px}.umb-dialog-footer{position:absolute;right:0;bottom:0;left:0;height:60px;overflow:auto;text-align:right;border-top:#efefef 1px solid}.umbracoDialog{width:auto!Important;height:auto!Important;padding:20px}.umbracoDialog .umb-pane{margin-top:0;margin-right:0;margin-left:0}.umbracoDialog .umb-dialog-body .umb-pane{margin-top:20px;margin-right:20px;margin-left:20px}.umbracoDialog form{height:100%}.umbracoDialog .controls-row,.umb-modal .controls-row{margin-left:0!important}.modal.fade.in{border:none!important;border-radius:none!important}.umb-modal.fade{top:0!important;left:-100%!important;width:0!important;height:100%!important;outline:0;transition:opacity .3s linear,top .3s ease-out}.umb-modal.fade.in{top:0!important;left:100%!important;display:block;width:440px!important;height:100%!important;margin-left:-440px}.umb-modal-left.fade{top:0!important;left:-100%!important;width:0!important;height:100%!important;transition:opacity .3s linear,top .3s ease-out}.umb-modal-left.fade.in{top:0!important;left:0!important;display:block;width:440px!important;height:100%!important;margin-left:80px}.umb-modal .umb-panel-header{height:auto;padding:20px;background:#fff;border:0}.umb-modal .umb-panel-body{padding:0 20px 0 20px}.umb-modal.fade.in.wide{width:640px!important;margin-left:-640px}.umb-modal i{font-size:14px}.umb-modal .breadcrumb{padding:0;background:0}.umb-modal .breadcrumb input{height:12px}.umb-modal.ysod{z-index:10000}.umb-panel{position:absolute;top:0;right:0;bottom:0;left:0;background:white}.umb-panel-nobody{padding-top:100px;overflow:auto}.umb-panel-header{position:absolute;top:0;right:0;left:0;height:99px;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-panel-body{position:absolute;top:101px;right:0;bottom:0;left:0;overflow:auto;clear:both}.umb-panel-body.no-header{top:20px}.umb-panel-body.with-footer{bottom:90px}.umb-mediapicker-upload{display:-ms-flexbox;display:-webkit-flex;display:flex}.umb-mediapicker-upload .form-search{-webkit-flex:1;-ms-flex:1;flex:1}.umb-mediapicker-upload .upload-button{margin-left:16px}.umb-panel.editor-breadcrumb .umb-panel-body,.umb-panel.editor-breadcrumb .umb-bottom-bar{bottom:31px!important}.umb-panel-header .umb-headline,.umb-panel-header h1{width:100%;height:auto;padding:3px 5px;margin:15px 0 0 20px;font-size:16px;line-height:1.4;background:0;border:0;border:1px solid #f8f8f8}.umb-panel-header .umb-headline:focus,.umb-panel-header .umb-headline:active{background-color:#fff;border:1px solid #d9d9d9}.umb-headline-editor-wrapper{position:relative}.umb-headline-editor-wrapper .help-inline{position:absolute;top:25px;right:0;font-size:10px;color:#9d261d}.umb-panel-header p{margin:0 20px}.umb-btn-toolbar .dimmed,.umb-dimmed{opacity:.6}.umb-headline-editor-wrapper input{padding:0 0 2px 0;margin:-6px 0 0 0;line-height:normal;letter-spacing:-0.01em;color:#000;background:0;border:0;border:1px solid transparent;border-radius:0}.umb-headline-editor-wrapper input.ng-invalid{color:#9d261d}.umb-headline-editor-wrapper input.ng-invalid::-moz-placeholder,.umb-headline-editor-wrapper input.ng-invalid:-ms-input-placeholder,.umb-headline-editor-wrapper input.ng-invalid::-webkit-input-placeholder{line-height:22px;color:#9d261d}.umb-panel-header-meta{height:50px}.umb-panel-header .umb-btn-toolbar{float:right;padding:5px 20px 0 0}.umb-panel-footer{position:absolute;right:0;bottom:0;left:0;z-index:999;padding:20px;margin:0}.umb-btn-toolbar .dropdown-menu{right:0;left:auto;z-index:6020;padding:0;border-radius:0;box-shadow:none}.umb-btn-toolbar .dropdown-menu small{display:block;padding:10px 20px;background:#fef9db}.umb-btn-toolbar .dropdown-menu .btn{width:80px;margin:20px 29px}.umb-bottom-bar{position:fixed;right:40px;bottom:0;left:100px;z-index:6010;padding:10px 0 10px 0;background:white;border-top:1px solid #f8f8f8;box-shadow:0 -18px 20px #fff}@media(min-width:1101px){.umb-bottom-bar{left:460px}}@media(max-width:767px){.umb-bottom-bar{left:80px}}@media(max-width:500px){.umb-bottom-bar{left:60px}}.umb-tab-buttons{padding-left:0}.umb-tab-buttons>.btn-group:not([style*="display:none"]):not([style*="display: none"]){margin-left:0}@media(min-width:768px){.umb-tab-buttons{padding-left:180px}}.umb-tab-pane{padding-bottom:90px}.tab-content{overflow:visible}.umb-panel-footer-nav{position:absolute;right:0;bottom:0;left:0;display:block;height:30px;margin:0;overflow:hidden;background:#f8f8f8;border-top:#d9d9d9 1px solid}.umb-panel-footer-nav li a{position:relative;display:block;float:left;height:30px;padding:8px 0 8px 30px;margin:0 1px 0 0;font-size:11px;color:#555;text-align:center;text-decoration:none;background:#f8f8f8;border-radius:0}.umb-panel-footer-nav li a:after{position:absolute;top:0;right:-16px;z-index:1;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid #f8f8f8;content:""}.umb-panel-footer-nav li a:before{position:absolute;top:0;left:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid #d9d9d9;content:""}.umb-panel-footer-nav li:first-child a{padding-left:20px}.umb-panel-footer-nav li:first-child a:before{display:none}.umb-panel-footer-nav li:last-child a:after{display:none}.umb-dialog .muted,.umb-panel .muted{color:#d9d9d9}.umb-dialog a.muted:hover,.umb-dialog a.muted:focus,.umb-panel a.muted:hover,.umb-panel a.muted:focus{color:#c0c0c0}.umb-dialog .text-warning,.umb-panel .text-warning{color:#df7f48}.umb-dialog a.text-warning:hover,.umb-dialog a.text-warning:focus,.umb-panel a.text-warning:hover,.umb-panel a.text-warning:focus{color:#d06324}.umb-dialog .text-error,.umb-panel .text-error{color:#9d261d}.umb-dialog a.text-error:hover,.umb-dialog a.text-error:focus,.umb-panel a.text-error:hover,.umb-panel a.text-error:focus{color:#721c15}.umb-dialog .text-info,.umb-panel .text-info{color:#2e8aea}.umb-dialog a.text-info:hover,.umb-dialog a.text-info:focus,.umb-panel a.text-info:hover,.umb-panel a.text-info:focus{color:#1571d0}.umb-dialog .text-success,.umb-panel .text-success{color:#46a546}.umb-dialog a.text-success:hover,.umb-dialog a.text-success:focus,.umb-panel a.text-success:hover,.umb-panel a.text-success:focus{color:#378137}.external-logins form{margin:0}.external-logins button{margin:5px}.umb-panel-header-content-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;height:100px;padding:0 20px;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-panel-header-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:1;-ms-flex:1;flex:1}.umb-panel-header-content.-top-position{position:relative;top:-12px}.umb-panel-header-left-side{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex:1;-ms-flex:1;flex:1;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-panel-header-icon{display:-webkit-flex;display:-ms-flexbox;display:flex;height:55px;margin-right:5px;cursor:pointer;background:#fff;border:1px solid #ececec;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:0 0 55px;-ms-flex:0 0 55px;flex:0 0 55px}.umb-panel-header-title-wrapper{position:relative;width:80%}.umb-panel-header-alias{position:absolute;top:5px;right:10px}.umb-panel-header-alias .umb-locked-field{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-panel-header-alias .umb-locked-field,.umb-panel-header-alias .umb-locked-field .umb-locked-field__wrapper{margin-bottom:0}.umb-panel-header-alias .umb-validation-label:after{visibility:hidden}.umb-panel-header-alias .umb-locked-field:after{display:none}.umb-panel-header-icon.-placeholder{border:1px dashed #d9d9d9}.umb-panel-header-icon .icon{font-size:35px;color:#d9d9d9}.umb-panel-header-icon-text{font-size:10px;font-weight:bold;color:#2e8aea}.umb-panel-header .umb-nav-tabs{bottom:-1px}input.umb-panel-header-name-input{width:100%;height:30px;margin-bottom:0;font-size:15px;font-weight:bold;line-height:30px;color:#000;border-color:#ececec;box-sizing:border-box}input.umb-panel-header-name-input:hover{background:#fff;border:1px solid #ccc}input.umb-panel-header-name-input.name-is-empty{background:#fff;border:1px dashed #d9d9d9}.umb-panel-header-name{font-size:16px;font-weight:bold}input.umb-panel-header-description{width:100%;height:25px;margin-bottom:0;font-size:12px;line-height:25px;background:transparent;border-color:transparent;box-sizing:border-box}input.umb-panel-header-description:hover{background:#fff;border-color:#ccc}.umb-editor-drawer-content{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-editor-drawer-content__right-side{margin-left:auto}.umb-editor-drawer-content__left-side{margin-right:auto}ul.sections{display:block;width:80px;height:100%;margin:0;background:#1d1d1d;border-right:1px solid #343434}ul.sections li{display:block;border-left:4px #1d1d1d solid;transition:all .3s linear}ul.sections li [class^="icon-"]:before,ul.sections li [class*=" icon-"]:before,ul.sections li img.icon-section{margin:1px 0 0 0;font-size:30px;opacity:.4;transition:all .3s linear}ul.sections:hover li [class^="icon-"]:before,ul.sections:hover li [class*=" icon-"]:before,ul.sections:hover li img.icon-section{opacity:1}ul.sections li a{display:block;width:100%;height:100%;padding:20px 4px 4px 0;margin:0 0 0 -4px;color:#d9d9d9;text-align:center;text-decoration:none;cursor:pointer;border-bottom:1px solid #343434}ul.sections a span{display:block;font-size:10px;line-height:1.4em;opacity:0;transition:all .3s linear}ul.sections:hover a span{opacity:1}ul.sections li.avatar{height:75px;padding:22px 0 2px 0;margin:0 0 0 -4px;text-align:center;border-bottom:1px solid #343434}ul.sections li.avatar a{width:40px;height:40px;padding:0;margin:0 auto;border:0}ul.sections li.avatar a img{width:30px;border-radius:50%}.faded ul.sections li{opacity:.4}ul.sections li.current,ul.sections li:hover{border-left:4px #df7f48 solid}ul.sections li.avatar.current,ul.sections li.avatar:hover{border-left:4px #1d1d1d solid}ul.sections li.current a{padding-left:0}ul.sections li.help{position:absolute;bottom:0;left:0;display:block;width:100%;margin:0}ul.sections li.help a{border-bottom:0}li.expand a,li.expand{border:none!Important}li.expand.open>a>i.icon{transition:all 1s!important}li.expand.open>a>i.icon:before{-webkit-transform:rotate(180deg)!important;transform:rotate(180deg)!important}ul.sections-tray{width:80px;padding-top:99px}ul.sections-tray>li:first-child>a{border-top:1px solid #343434}ul.sections-tray>li{border-left-width:5px}ul.sections-tray>li.current,ul.sections-tray>li:hover{border-left-width:5px}@font-face{font-family:'icomoon';font-style:normal;font-weight:normal;src:url('../fonts/helveticons/helveticons.eot');src:url('../fonts/helveticons/helveticons.eot?#iefix') format('embedded-opentype'),url('../fonts/helveticons/helveticons.ttf') format('truetype'),url('../fonts/helveticons/helveticons.svg#icomoon') format('svg')}[class^="icon-"],[class*=" icon-"]{*margin-right:.3em;font-family:icomoon;-webkit-font-smoothing:antialiased;font-style:normal;font-weight:normal;text-decoration:inherit}[class^="icon-"]:before,[class*=" icon-"]:before{display:inline-block;text-decoration:inherit;speak:none}i.large{font-size:32px}i.medium{font-size:24px}i.small{font-size:14px}.icon-zoom-out:before{content:"\e000"}.icon-truck:before{content:"\e001"}.icon-zoom-in:before{content:"\e002"}.icon-zip:before{content:"\e003"}.icon-axis-rotation:before{content:"\e004"}.icon-yen-bag:before{content:"\e005"}.icon-axis-rotation-2:before{content:"\e006"}.icon-axis-rotation-3:before{content:"\e007"}.icon-wrench:before{content:"\e008"}.icon-wine-glass:before{content:"\e009"}.icon-wrong:before{content:"\e00a"}.icon-windows:before{content:"\e00b"}.icon-window-sizes:before{content:"\e00c"}.icon-window-popin:before{content:"\e00d"}.icon-wifi:before{content:"\e00e"}.icon-width:before{content:"\e00f"}.icon-weight:before{content:"\e010"}.icon-war:before{content:"\e011"}.icon-wand:before{content:"\e012"}.icon-wallet:before{content:"\e013"}.icon-wall-plug:before{content:"\e014"}.icon-voice:before{content:"\e016"}.icon-video:before{content:"\e017"}.icon-vcard:before{content:"\e018"}.icon-utilities:before{content:"\e019"}.icon-users:before{content:"\e01a"}.icon-users-alt:before{content:"\e01b"}.icon-user:before{content:"\e01c"}.icon-user-glasses:before{content:"\e01d"}.icon-user-females:before{content:"\e01e"}.icon-user-females-alt:before{content:"\e01f"}.icon-user-female:before{content:"\e020"}.icon-usb:before{content:"\e021"}.icon-usb-connector:before{content:"\e022"}.icon-unlocked:before{content:"\e023"}.icon-universal:before{content:"\e024"}.icon-undo:before{content:"\e025"}.icon-umbrella:before{content:"\e026"}.icon-umb-deploy:before{content:"\e027"}.icon-umb-contour:before,.traycontour:before{content:"\e028"}.icon-umb-settings:before,.traysettings:before{content:"\e029"}.icon-umb-users:before,.trayuser:before,.trayusers:before{content:"\e02a"}.icon-umb-media:before,.traymedia:before{content:"\e02b"}.icon-umb-content:before,.traycontent:before{content:"\e02c"}.icon-umb-developer:before,.traydeveloper:before{content:"\e02d"}.icon-umb-members:before,.traymember:before{content:"\e015"}.icon-umb-translation:before,.traytranslation:before{content:"\e1fd"}.icon-tv:before{content:"\e02e"}.icon-tv-old:before{content:"\e02f"}.icon-trophy:before{content:"\e030"}.icon-tree:before{content:"\e031"}.icon-trash:before{content:"\e032"}.icon-trash-alt:before{content:"\e033"}.icon-trash-alt-2:before{content:"\e034"}.icon-train:before{content:"\e035"}.icon-trafic:before{content:"\e036"}.icon-traffic-alt:before{content:"\e037"}.icon-top:before{content:"\e038"}.icon-tools:before{content:"\e039"}.icon-timer:before{content:"\e03a"}.icon-time:before{content:"\e03b"}.icon-t-shirt:before{content:"\e03c"}.icon-tab-key:before{content:"\e03d"}.icon-tab:before{content:"\e03e"}.icon-tactics:before{content:"\e03f"}.icon-tag:before{content:"\e040"}.icon-tags:before{content:"\e041"}.icon-takeaway-cup:before{content:"\e042"}.icon-target:before{content:"\e043"}.icon-temperatrure-alt:before{content:"\e044"}.icon-temperature:before{content:"\e045"}.icon-terminal:before{content:"\e046"}.icon-theater:before{content:"\e047"}.icon-theif:before{content:"\e048"}.icon-thought-bubble:before{content:"\e049"}.icon-thumb-down:before{content:"\e04a"}.icon-thumb-up:before{content:"\e04b"}.icon-thumbnail-list:before{content:"\e04c"}.icon-thumbnails-small:before{content:"\e04d"}.icon-thumbnails:before{content:"\e04e"}.icon-ticket:before{content:"\e04f"}.icon-sync:before{content:"\e050"}.icon-sweatshirt:before{content:"\e051"}.icon-sunny:before{content:"\e052"}.icon-stream:before{content:"\e053"}.icon-store:before{content:"\e054"}.icon-stop:before{content:"\e055"}.icon-stop-hand:before{content:"\e056"}.icon-stop-alt:before{content:"\e057"}.icon-stamp:before{content:"\e058"}.icon-stacked-disks:before{content:"\e059"}.icon-ssd:before{content:"\e05a"}.icon-squiggly-line:before{content:"\e05b"}.icon-sprout:before{content:"\e05c"}.icon-split:before{content:"\e05d"}.icon-split-alt:before{content:"\e05e"}.icon-speed-gauge:before{content:"\e05f"}.icon-speaker:before{content:"\e060"}.icon-sound:before{content:"\e061"}.icon-spades:before{content:"\e062"}.icon-sound-waves:before{content:"\e063"}.icon-shipping-box:before{content:"\e064"}.icon-shipping:before{content:"\e065"}.icon-shoe:before{content:"\e066"}.icon-shopping-basket-alt-2:before{content:"\e067"}.icon-shopping-basket:before{content:"\e068"}.icon-shopping-basket-alt:before{content:"\e069"}.icon-shorts:before{content:"\e06a"}.icon-shuffle:before{content:"\e06b"}.icon-sience:before{content:"\e06c"}.icon-simcard:before{content:"\e06d"}.icon-single-note:before{content:"\e06e"}.icon-sitemap:before{content:"\e06f"}.icon-sleep:before{content:"\e070"}.icon-slideshow:before{content:"\e071"}.icon-smiley-inverted:before{content:"\e072"}.icon-smiley:before{content:"\e073"}.icon-snow:before{content:"\e074"}.icon-sound-low:before{content:"\e075"}.icon-sound-medium:before{content:"\e076"}.icon-sound-off:before{content:"\e077"}.icon-shift:before{content:"\e078"}.icon-shield:before{content:"\e079"}.icon-sharing-iphone:before{content:"\e07a"}.icon-share:before{content:"\e07b"}.icon-share-alt:before{content:"\e07c"}.icon-share-alt-2:before{content:"\e07d"}.icon-settings:before{content:"\e07e"}.icon-settings-alt:before{content:"\e07f"}.icon-settings-alt-2:before{content:"\e080"}.icon-server:before{content:"\e081"}.icon-server-alt:before{content:"\e082"}.icon-sensor:before{content:"\e083"}.icon-security-camera:before{content:"\e084"}.icon-search:before{content:"\e085"}.icon-scull:before{content:"\e086"}.icon-script:before{content:"\e087"}.icon-script-alt:before{content:"\e088"}.icon-screensharing:before{content:"\e089"}.icon-school:before{content:"\e08a"}.icon-scan:before{content:"\e08b"}.icon-refresh:before{content:"\e08c"}.icon-remote:before{content:"\e08d"}.icon-remove:before{content:"\e08e"}.icon-repeat-one:before{content:"\e08f"}.icon-repeat:before{content:"\e090"}.icon-resize:before{content:"\e091"}.icon-reply-arrow:before{content:"\e092"}.icon-return-to-top:before{content:"\e093"}.icon-right-double-arrow:before{content:"\e094"}.icon-road:before{content:"\e095"}.icon-roadsign:before{content:"\e096"}.icon-rocket:before{content:"\e097"}.icon-rss:before{content:"\e098"}.icon-ruler-alt:before{content:"\e099"}.icon-ruler:before{content:"\e09a"}.icon-sandbox-toys:before{content:"\e09b"}.icon-satellite-dish:before{content:"\e09c"}.icon-save:before{content:"\e09d"}.icon-safedial:before{content:"\e09e"}.icon-safe:before{content:"\e09f"}.icon-redo:before{content:"\e0a0"}.icon-printer-alt:before{content:"\e0a1"}.icon-planet:before{content:"\e0a2"}.icon-paste-in:before{content:"\e0a3"}.icon-os-x:before{content:"\e0a4"}.icon-navigation-left:before{content:"\e0a5"}.icon-message:before{content:"\e0a6"}.icon-lock:before{content:"\e0a7"}.icon-layers-alt:before{content:"\e0a8"}.icon-record:before{content:"\e0a9"}.icon-print:before{content:"\e0aa"}.icon-plane:before{content:"\e0ab"}.icon-partly-cloudy:before{content:"\e0ac"}.icon-ordered-list:before{content:"\e0ad"}.icon-navigation-last:before{content:"\e0ae"}.icon-message-unopened:before{content:"\e0af"}.icon-location-nearby:before{content:"\e0b0"}.icon-laptop:before{content:"\e0b1"}.icon-reception:before{content:"\e0b2"}.icon-price-yen:before{content:"\e0b3"}.icon-piracy:before{content:"\e0b4"}.icon-parental-control:before{content:"\e0b5"}.icon-operator:before{content:"\e0b6"}.icon-navigation-horizontal:before{content:"\e0b7"}.icon-message-open:before{content:"\e0b8"}.icon-lab:before{content:"\e0b9"}.icon-location-near-me:before{content:"\e0ba"}.icon-receipt-yen:before{content:"\e0bb"}.icon-price-pound:before{content:"\e0bc"}.icon-pin-location:before{content:"\e0bd"}.icon-parachute-drop:before{content:"\e0be"}.icon-old-phone:before{content:"\e0bf"}.icon-merge:before{content:"\e0c0"}.icon-navigation-first:before{content:"\e0c1"}.icon-locate:before{content:"\e0c2"}.icon-keyhole:before{content:"\e0c3"}.icon-receipt-pound:before{content:"\e0c4"}.icon-price-euro:before{content:"\e0c5"}.icon-piggy-bank:before{content:"\e0c6"}.icon-paper-plane:before{content:"\e0c7"}.icon-old-key:before{content:"\e0c8"}.icon-navigation-down:before{content:"\e0c9"}.icon-megaphone:before{content:"\e0ca"}.icon-loading:before{content:"\e0cb"}.icon-keychain:before{content:"\e0cc"}.icon-receipt-euro:before{content:"\e0cd"}.icon-price-dollar:before{content:"\e0ce"}.icon-pie-chart:before{content:"\e0cf"}.icon-paper-plane-alt:before{content:"\e0d0"}.icon-notepad:before{content:"\e0d1"}.icon-navigation-bottom:before{content:"\e0d2"}.icon-meeting:before{content:"\e0d3"}.icon-keyboard:before{content:"\e0d4"}.icon-load:before{content:"\e0d5"}.icon-receipt-dollar:before{content:"\e0d6"}.icon-previous:before{content:"\e0d7"}.icon-pictures:before{content:"\e0d8"}.icon-notepad-alt:before{content:"\e0d9"}.icon-paper-bag:before{content:"\e0da"}.icon-name-badge:before{content:"\e0db"}.icon-medicine:before{content:"\e0dc"}.icon-list:before{content:"\e0dd"}.icon-key:before{content:"\e0de"}.icon-receipt-alt:before{content:"\e0df"}.icon-previous-media:before{content:"\e0e0"}.icon-pictures-alt:before{content:"\e0e1"}.icon-pants:before{content:"\e0e2"}.icon-nodes:before{content:"\e0e3"}.icon-music:before{content:"\e0e4"}.icon-readonly:before{content:"\e0e5"}.icon-presentation:before{content:"\e0e6"}.icon-pictures-alt-2:before{content:"\e0e7"}.icon-pannel-close:before{content:"\e0e8"}.icon-next:before{content:"\e0e9"}.icon-multiple-windows:before{content:"\e0ea"}.icon-medical-emergency:before{content:"\e0eb"}.icon-medal:before{content:"\e0ec"}.icon-link:before{content:"\e0ed"}.icon-linux-tux:before{content:"\e0ee"}.icon-junk:before{content:"\e0ef"}.icon-item-arrangement:before{content:"\e0f0"}.icon-iphone:before{content:"\e0f1"}.icon-lightning:before{content:"\e0f2"}.icon-map:before{content:"\e0f3"}.icon-multiple-credit-cards:before{content:"\e0f4"}.icon-next-media:before{content:"\e0f5"}.icon-panel-show:before{content:"\e0f6"}.icon-picture:before{content:"\e0f7"}.icon-power:before{content:"\e0f8"}.icon-re-post:before{content:"\e0f9"}.icon-rate:before{content:"\e0fa"}.icon-rain:before{content:"\e0fb"}.icon-radio:before{content:"\e0fc"}.icon-radio-receiver:before{content:"\e0fd"}.icon-radio-alt:before{content:"\e0fe"}.icon-quote:before{content:"\e0ff"}.icon-qr-code:before{content:"\e100"}.icon-pushpin:before{content:"\e101"}.icon-pulse:before{content:"\e102"}.icon-projector:before{content:"\e103"}.icon-play:before{content:"\e104"}.icon-playing-cards:before{content:"\e105"}.icon-playlist:before{content:"\e106"}.icon-plugin:before{content:"\e107"}.icon-podcast:before{content:"\e108"}.icon-poker-chip:before{content:"\e109"}.icon-poll:before{content:"\e10a"}.icon-post-it:before{content:"\e10b"}.icon-pound-bag:before{content:"\e10c"}.icon-power-outlet:before{content:"\e10d"}.icon-photo-album:before{content:"\e10e"}.icon-phone:before{content:"\e10f"}.icon-phone-ring:before{content:"\e110"}.icon-people:before{content:"\e111"}.icon-people-female:before{content:"\e112"}.icon-people-alt:before{content:"\e113"}.icon-people-alt-2:before{content:"\e114"}.icon-pc:before{content:"\e115"}.icon-pause:before{content:"\e116"}.icon-path:before{content:"\e117"}.icon-out:before{content:"\e118"}.icon-outbox:before{content:"\e119"}.icon-outdent:before{content:"\e11a"}.icon-page-add:before{content:"\e11b"}.icon-page-down:before{content:"\e11c"}.icon-page-remove:before{content:"\e11d"}.icon-page-restricted:before{content:"\e11e"}.icon-page-up:before{content:"\e11f"}.icon-paint-roller:before{content:"\e120"}.icon-palette:before{content:"\e121"}.icon-newspaper:before{content:"\e122"}.icon-newspaper-alt:before{content:"\e123"}.icon-network-alt:before{content:"\e124"}.icon-navigational-arrow:before{content:"\e125"}.icon-navigation:before{content:"\e126"}.icon-navigation-vertical:before{content:"\e127"}.icon-navigation-up:before{content:"\e128"}.icon-navigation-top:before{content:"\e129"}.icon-navigation-road:before{content:"\e12a"}.icon-navigation-right:before{content:"\e12b"}.icon-microscope:before{content:"\e12c"}.icon-mindmap:before{content:"\e12d"}.icon-molecular-network:before{content:"\e12e"}.icon-molecular:before{content:"\e12f"}.icon-mountain:before{content:"\e130"}.icon-mouse-cursor:before{content:"\e131"}.icon-mouse:before{content:"\e132"}.icon-movie-alt:before{content:"\e133"}.icon-map-marker:before{content:"\e134"}.icon-movie:before{content:"\e135"}.icon-map-location:before{content:"\e136"}.icon-map-alt:before{content:"\e137"}.icon-male-symbol:before{content:"\e138"}.icon-male-and-female:before{content:"\e139"}.icon-mailbox:before{content:"\e13a"}.icon-magnet:before{content:"\e13b"}.icon-loupe:before{content:"\e13c"}.icon-mobile:before{content:"\e13d"}.icon-logout:before{content:"\e13e"}.icon-log-out:before{content:"\e13f"}.icon-layers:before{content:"\e140"}.icon-left-double-arrow:before{content:"\e141"}.icon-layout:before{content:"\e142"}.icon-legal:before{content:"\e143"}.icon-lense:before{content:"\e144"}.icon-library:before{content:"\e145"}.icon-light-down:before{content:"\e146"}.icon-light-up:before{content:"\e147"}.icon-lightbulb-active:before{content:"\e148"}.icon-lightbulb:before{content:"\e149"}.icon-ipad:before{content:"\e14a"}.icon-invoice:before{content:"\e14b"}.icon-info:before{content:"\e14c"}.icon-infinity:before{content:"\e14d"}.icon-indent:before{content:"\e14e"}.icon-inbox:before{content:"\e14f"}.icon-inbox-full:before{content:"\e150"}.icon-inactive-line:before{content:"\e151"}.icon-imac:before{content:"\e152"}.icon-hourglass:before{content:"\e153"}.icon-home:before{content:"\e154"}.icon-grid:before{content:"\e155"}.icon-food:before{content:"\e156"}.icon-favorite:before{content:"\e157"}.icon-door-open-alt:before{content:"\e158"}.icon-diagnostics:before{content:"\e159"}.icon-contrast:before{content:"\e15a"}.icon-coins-dollar-alt:before{content:"\e15b"}.icon-circle-dotted-active:before{content:"\e15c"}.icon-cinema:before{content:"\e15d"}.icon-chip:before{content:"\e15e"}.icon-chip-alt:before{content:"\e15f"}.icon-chess:before{content:"\e160"}.icon-checkbox:before{content:"\e161"}.icon-checkbox-empty:before{content:"\e162"}.icon-checkbox-dotted:before{content:"\e163"}.icon-checkbox-dotted-active:before{content:"\e164"}.icon-check:before{content:"\e165"}.icon-chat:before{content:"\e166"}.icon-chat-active:before{content:"\e167"}.icon-chart:before{content:"\e168"}.icon-chart-curve:before{content:"\e169"}.icon-certificate:before{content:"\e16a"}.icon-categories:before{content:"\e16b"}.icon-cash-register:before{content:"\e16c"}.icon-car:before{content:"\e16d"}.icon-caps-lock:before{content:"\e16e"}.icon-candy:before{content:"\e16f"}.icon-circle-dotted:before{content:"\e170"}.icon-circuits:before{content:"\e171"}.icon-circus:before{content:"\e172"}.icon-client:before{content:"\e173"}.icon-clothes-hanger:before{content:"\e174"}.icon-cloud-drive:before{content:"\e175"}.icon-cloud-upload:before{content:"\e176"}.icon-cloud:before{content:"\e177"}.icon-cloudy:before{content:"\e178"}.icon-clubs:before{content:"\e179"}.icon-cocktail:before{content:"\e17a"}.icon-code:before{content:"\e17b"}.icon-coffee:before{content:"\e17c"}.icon-coin-dollar:before{content:"\e17d"}.icon-coin-pound:before{content:"\e17e"}.icon-coin-yen:before{content:"\e17f"}.icon-coin:before{content:"\e180"}.icon-coins-alt:before{content:"\e181"}.icon-console:before{content:"\e182"}.icon-connection:before{content:"\e183"}.icon-compress:before{content:"\e184"}.icon-company:before{content:"\e185"}.icon-command:before{content:"\e186"}.icon-coin-euro:before{content:"\e187"}.icon-combination-lock:before{content:"\e188"}.icon-combination-lock-open:before{content:"\e189"}.icon-comb:before{content:"\e18a"}.icon-columns:before{content:"\e18b"}.icon-colorpicker:before{content:"\e18c"}.icon-color-bucket:before{content:"\e18d"}.icon-coins:before{content:"\e18e"}.icon-coins-yen:before{content:"\e18f"}.icon-coins-yen-alt:before{content:"\e190"}.icon-coins-pound:before{content:"\e191"}.icon-coins-pound-alt:before{content:"\e192"}.icon-coins-euro:before{content:"\e193"}.icon-coins-euro-alt:before{content:"\e194"}.icon-coins-dollar:before{content:"\e195"}.icon-conversation-alt:before{content:"\e196"}.icon-conversation:before{content:"\e197"}.icon-coverflow:before{content:"\e198"}.icon-credit-card-alt:before{content:"\e199"}.icon-credit-card:before{content:"\e19a"}.icon-crop:before{content:"\e19b"}.icon-crosshair:before{content:"\e19c"}.icon-crown-alt:before{content:"\e19d"}.icon-crown:before{content:"\e19e"}.icon-cupcake:before{content:"\e19f"}.icon-curve:before{content:"\e1a0"}.icon-cut:before{content:"\e1a1"}.icon-dashboard:before{content:"\e1a2"}.icon-defrag:before{content:"\e1a3"}.icon-delete:before{content:"\e1a4"}.icon-delete-key:before{content:"\e1a5"}.icon-departure:before{content:"\e1a6"}.icon-desk:before{content:"\e1a7"}.icon-desktop:before{content:"\e1a8"}.icon-donate:before{content:"\e1a9"}.icon-dollar-bag:before{content:"\e1aa"}.icon-documents:before{content:"\e1ab"}.icon-document:before{content:"\e1ac"}.icon-document-dashed-line:before{content:"\e1ad"}.icon-dock-connector:before{content:"\e1ae"}.icon-dna:before{content:"\e1af"}.icon-display:before{content:"\e1b0"}.icon-disk-image:before{content:"\e1b1"}.icon-disc:before{content:"\e1b2"}.icon-directions:before{content:"\e1b3"}.icon-directions-alt:before{content:"\e1b4"}.icon-diploma:before{content:"\e1b5"}.icon-diploma-alt:before{content:"\e1b6"}.icon-dice:before{content:"\e1b7"}.icon-diamonds:before{content:"\e1b8"}.icon-diamond:before{content:"\e1b9"}.icon-diagonal-arrow:before{content:"\e1ba"}.icon-diagonal-arrow-alt:before{content:"\e1bb"}.icon-door-open:before{content:"\e1bc"}.icon-download-alt:before{content:"\e1bd"}.icon-download:before{content:"\e1be"}.icon-drop:before{content:"\e1bf"}.icon-eco:before{content:"\e1c0"}.icon-economy:before{content:"\e1c1"}.icon-edit:before{content:"\e1c2"}.icon-eject:before{content:"\e1c3"}.icon-employee:before{content:"\e1c4"}.icon-energy-saving-bulb:before{content:"\e1c5"}.icon-enter:before{content:"\e1c6"}.icon-equalizer:before{content:"\e1c7"}.icon-escape:before{content:"\e1c8"}.icon-ethernet:before{content:"\e1c9"}.icon-euro-bag:before{content:"\e1ca"}.icon-exit-fullscreen:before{content:"\e1cb"}.icon-eye:before{content:"\e1cc"}.icon-facebook-like:before{content:"\e1cd"}.icon-factory:before{content:"\e1ce"}.icon-font:before{content:"\e1cf"}.icon-folders:before{content:"\e1d0"}.icon-folder:before,.icon-folder-close:before{content:"\e1d1"}.icon-folder-outline:before{content:"\e1d2"}.icon-folder-open:before{content:"\e1d3"}.icon-flowerpot:before{content:"\e1d4"}.icon-flashlight:before{content:"\e1d5"}.icon-flash:before{content:"\e1d6"}.icon-flag:before{content:"\e1d7"}.icon-flag-alt:before{content:"\e1d8"}.icon-firewire:before{content:"\e1d9"}.icon-firewall:before{content:"\e1da"}.icon-fire:before{content:"\e1db"}.icon-fingerprint:before{content:"\e1dc"}.icon-filter:before{content:"\e1dd"}.icon-filter-arrows:before{content:"\e1de"}.icon-files:before{content:"\e1df"}.icon-file-cabinet:before{content:"\e1e0"}.icon-female-symbol:before{content:"\e1e1"}.icon-footprints:before{content:"\e1e2"}.icon-hammer:before{content:"\e1e3"}.icon-hand-active-alt:before{content:"\e1e4"}.icon-forking:before{content:"\e1e5"}.icon-hand-active:before{content:"\e1e6"}.icon-hand-pointer-alt:before{content:"\e1e7"}.icon-hand-pointer:before{content:"\e1e8"}.icon-handprint:before{content:"\e1e9"}.icon-handshake:before{content:"\e1ea"}.icon-handtool:before{content:"\e1eb"}.icon-hard-drive:before{content:"\e1ec"}.icon-help:before{content:"\e1ed"}.icon-graduate:before{content:"\e1ee"}.icon-gps:before{content:"\e1ef"}.icon-help-alt:before{content:"\e1f0"}.icon-height:before{content:"\e1f1"}.icon-globe:before{content:"\e1f2"}.icon-hearts:before{content:"\e1f3"}.icon-globe-inverted-europe-africa:before{content:"\e1f4"}.icon-headset:before{content:"\e1f5"}.icon-globe-inverted-asia:before{content:"\e1f6"}.icon-headphones:before{content:"\e1f7"}.icon-globe-inverted-america:before{content:"\e1f8"}.icon-hd:before{content:"\e1f9"}.icon-globe-europe---africa:before{content:"\e1fa"}.icon-hat:before{content:"\e1fb"}.icon-globe-asia:before{content:"\e1fc"}.icon-globe-alt:before{content:"\e1fd"}.icon-hard-drive-alt:before{content:"\e1fe"}.icon-glasses:before{content:"\e1ff"}.icon-gift:before{content:"\e200"}.icon-handtool-alt:before{content:"\e201"}.icon-geometry:before{content:"\e202"}.icon-game:before{content:"\e203"}.icon-fullscreen:before{content:"\e204"}.icon-fullscreen-alt:before{content:"\e205"}.icon-frame:before{content:"\e206"}.icon-frame-alt:before{content:"\e207"}.icon-camera-roll:before{content:"\e208"}.icon-bookmark:before{content:"\e209"}.icon-bill:before{content:"\e20a"}.icon-baby-stroller:before{content:"\e20b"}.icon-alarm-clock:before{content:"\e20c"}.icon-adressbook:before{content:"\e20d"}.icon-add:before{content:"\e20e"}.icon-activity:before{content:"\e20f"}.icon-untitled:before{content:"\e210"}.icon-glasses:before{content:"\e211"}.icon-camcorder:before{content:"\e212"}.icon-calendar:before{content:"\e213"}.icon-calendar-alt:before{content:"\e214"}.icon-calculator:before{content:"\e215"}.icon-bus:before{content:"\e216"}.icon-burn:before{content:"\e217"}.icon-bulleted-list:before{content:"\e218"}.icon-bug:before{content:"\e219"}.icon-brush:before{content:"\e21a"}.icon-brush-alt:before{content:"\e21b"}.icon-brush-alt-2:before{content:"\e21c"}.icon-browser-window:before{content:"\e21d"}.icon-briefcase:before{content:"\e21e"}.icon-brick:before{content:"\e21f"}.icon-brackets:before{content:"\e220"}.icon-box:before{content:"\e221"}.icon-box-open:before{content:"\e222"}.icon-box-alt:before{content:"\e223"}.icon-books:before{content:"\e224"}.icon-billboard:before{content:"\e225"}.icon-bills-dollar:before{content:"\e226"}.icon-bills-euro:before{content:"\e227"}.icon-bills-pound:before{content:"\e228"}.icon-bills-yen:before{content:"\e229"}.icon-bills:before{content:"\e22a"}.icon-binarycode:before{content:"\e22b"}.icon-binoculars:before{content:"\e22c"}.icon-bird:before{content:"\e22d"}.icon-birthday-cake:before{content:"\e22e"}.icon-blueprint:before{content:"\e22f"}.icon-block:before{content:"\e230"}.icon-bluetooth:before{content:"\e231"}.icon-boat-shipping:before{content:"\e232"}.icon-bomb:before{content:"\e233"}.icon-book-alt-2:before{content:"\e234"}.icon-bones:before{content:"\e235"}.icon-book-alt:before{content:"\e236"}.icon-book:before{content:"\e237"}.icon-bill-yen:before{content:"\e238"}.icon-award:before{content:"\e239"}.icon-bill-pound:before{content:"\e23a"}.icon-autofill:before{content:"\e23b"}.icon-bill-euro:before{content:"\e23c"}.icon-auction-hammer:before{content:"\e23d"}.icon-bill-dollar:before{content:"\e23e"}.icon-attachment:before{content:"\e23f"}.icon-bell:before{content:"\e240"}.icon-article:before{content:"\e241"}.icon-bell-off:before{content:"\e242"}.icon-art-easel:before{content:"\e243"}.icon-beer-glass:before{content:"\e244"}.icon-arrow-up:before{content:"\e245"}.icon-battery-low:before{content:"\e246"}.icon-arrow-right:before{content:"\e247"}.icon-battery-full:before{content:"\e248"}.icon-arrow-left:before{content:"\e249"}.icon-bars:before{content:"\e24a"}.icon-arrow-down:before{content:"\e24b"}.icon-barcode:before{content:"\e24c"}.icon-arrivals:before{content:"\e24d"}.icon-bar-chart:before{content:"\e24e"}.icon-application-window:before{content:"\e24f"}.icon-band-aid:before{content:"\e250"}.icon-application-window-alt:before{content:"\e251"}.icon-ball:before{content:"\e252"}.icon-application-error:before{content:"\e253"}.icon-badge-restricted:before{content:"\e254"}.icon-app:before{content:"\e255"}.icon-badge-remove:before{content:"\e256"}.icon-anchor:before{content:"\e257"}.icon-badge-count:before{content:"\e258"}.icon-alt:before{content:"\e259"}.icon-badge-add:before{content:"\e25a"}.icon-alert:before{content:"\e25b"}.icon-backspace:before{content:"\e25c"}.icon-alert-alt:before{content:"\e25d"}.fill{height:100%;min-height:100%}.shadow{box-shadow:3px 0 7px #dbdbdb}.umb-scrollable,.umb-auto-overflow{overflow:auto}.umb-abstract{display:block;margin-top:0;margin-bottom:15px;font-size:14px;color:#b3b3b3}h5{margin-top:15px;font-size:13px;font-weight:bold;color:#b3b3b3;text-transform:uppercase}h5.-border-bottom{padding-bottom:5px;border-bottom:1px solid #ececec}h5.-black{color:#000}.umb-form-actions{background:0;border:0}.datepicker td.active,.datepicker td span.active{background:#f3762c!important}.umb-datetime-picker div.info{vertical-align:middle}.umb-userwidget img{float:left;margin-right:15px}.umb-userwidget small{display:block}.popover-title{display:none}.thumbnails>li.umb-plus-btn{margin:0 10px 10px 0}.umb-plus-btn a{display:block;width:136px;height:136px;font-size:50px;line-height:136px;color:#d9d9d9;text-align:center;text-decoration:none;border:2px dashed #d9d9d9;transition:all .3s ease-in-out}.umb-plus-btn a:hover{color:#000;border:2px dashed #000}.umb-plus-btn i{margin:auto;vertical-align:middle}.umb-pane{margin:30px 20px}.umb-control-group{padding-bottom:20px;margin-bottom:15px!important;border-bottom:1px solid #f8f8f8}.umb-control-group.-no-border{border:0}.compact .umb-pane{margin:0 0 15px 0}.compact .umb-control-group{padding-bottom:10px;margin-bottom:5px!important;border-bottom:1px solid #f8f8f8}.compact label.control-label{padding-top:0!important;margin-bottom:0}.compact .controls-row{padding-top:0}.umb-pane>.umb-control-group:last-child{padding-bottom:0!important;border:0}.umb-control-group .umb-el-wrap{padding:0}.umb-control-group label.control-label,.umb-control-group .control-label{text-align:left}.umb-control-group label.control-label>div>label{padding-left:0}.umb-control-group label .help-block,.umb-control-group label small{padding-top:5px;font-size:11px;line-height:13px;color:#a0a0a0}.umb-nolabel .controls{margin-left:0}.controls-row{padding-top:5px;margin-left:240px}.umb-user-panel .controls-row{margin-left:0}.controls-row label{display:inline-block}.controls-row>div>label{padding-left:0}.block-form .controls-row{padding-top:0;margin-left:0}.hidelabel>div>.controls-row,.hidelabel>.controls-row,.hidelabel>div>.controls{padding:0;margin:0!important;border:0}.controls-row>.vertical-align-items{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.controls-row>.vertical-align-items>input.umb-editor-tiny{margin-right:5px;margin-left:5px}.controls-row>.vertical-align-items>input.umb-editor-tiny:first-child{margin-left:0}.thumbnails .selected{background:#000;border-color:#000}.umb-version{position:absolute;right:20px;bottom:5px;color:#bbb}.dashboardHideLink{display:none}.dashboardWrapper{position:relative}.dashboardWrapper h2{padding:0 0 0 45px}.dashboardWrapper h3{font-size:14px;font-weight:bold}.dashboardIcon{position:absolute;top:2px;left:2px}.umb-dashboard-control iframe{position:absolute;display:block;width:99%;height:99%;overflow:auto!important}.umb-table{word-wrap:break-word;table-layout:fixed}.umb-no-border{border:none!important}table thead a{color:#333}.umb-table tbody.ui-sortable tr{cursor:pointer}.umb-table tbody.ui-sortable tr.ui-sortable-helper{background-color:rgba(4,156,219,0.5);border:0}.umb-table tbody.ui-sortable tr.ui-sortable-helper td{border:0}.umb-table tbody.ui-sortable tr.ui-sortable-placeholder{background-color:#2e8aea;border:0}.umb-table tbody.ui-sortable tr.ui-sortable-placeholder td{height:5px;padding:0;line-height:0}.thumbnails>li.umb-thumbnail{position:relative;margin:0 10px 10px 0}.thumbnails>li.umb-thumbnail .umb-icons{position:absolute;top:0;left:0;z-index:1000;width:100%;padding:17px 0;background:#222}.thumbnails>li.umb-thumbnail .icon-crop{position:absolute;top:10px;left:10px;font-size:14px;color:#fff}.thumbnails>li.umb-thumbnail .icon-remove{position:absolute;top:10px;right:10px;font-size:14px;color:#fff}.umb-image-crop{float:left;width:400px;padding:0 0 30px 0;margin:0 30px 25px 0}.umb-image-mask{position:relative;width:399px;height:300px;margin:0 30px 0 0}.umb-image-mask:after{position:absolute;top:0;left:0;z-index:5999;width:100%;height:100%;content:"";opacity:.6;filter:alpha(opacity=6);box-shadow:inset 0 0 0 40px #fff,inset 0 0 0 41px rgba(0,0,0,0.2),inset 0 0 20px 41px rgba(0,0,0,0.2)}.umb-image-mask .icon-screenshot{position:absolute;top:125px;left:110px;z-index:1000;font-size:30px;color:#fff}.umb-image-mask .icon-circle{position:absolute;top:130px;left:115px;z-index:1;font-size:20px;color:#4285f4}.umb-crop-preview{float:left;width:400px}.umb-image-controls{width:400px}.umb-image-controls .icon-minus{float:left;padding:14px 10px 0 10px;color:#ccc}.umb-image-controls .icon-plus{float:right;padding:14px 10px 0 10px;color:#ccc;text-align:left}.umb-image-crop .range{display:block;float:left;width:82%;height:1px;margin:20px 0 0 0;background:#ccc;-webkit-appearance:none}.umb-image-crop .range::-webkit-slider-thumb{width:14px;height:14px;margin-top:-1px;background:#f2f2f2;border:1px solid #ccc;border-radius:2px;content:"1";-webkit-appearance:none}.umb-search-group li>div{padding-left:20px}.umb-search-group li>div a>i{height:100%}#search-form .form-search div{margin-top:3px}#search-form .form-search div .btn{position:relative;top:0;left:0}#dictionaryItems tr{border-top:solid 1px #d9d9d9}#dictionaryItems thead tr{font-weight:bold;border-top:0}#dictionaryItems th{font-weight:normal;text-align:left}#dictionaryItems td{text-align:center}#dictionaryItems thead td:first-of-type{text-align:left}#dictioanryItems i{font-size:18px}#dictionaryItems .icon-alert{color:#9d261d}#dictionaryItems .icon-check{color:#46a546}.umb-loader{width:100%;height:1px;margin-top:0;margin-left:-100%;background-color:#2e8aea;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}@-webkit-keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}@keyframes bounce_loadingProgressG{0%{margin-left:-100%}100%{margin-left:100%}}.umb-loader-wrapper{position:absolute;right:0;left:0;margin:10px 0;overflow:hidden}.umb-loader-wrapper.-bottom{bottom:0}.strong{font-weight:bold}.inline{display:inline}.input-label--title{margin-bottom:3px;font-weight:bold;color:#000}.input-label--small{display:inline;font-size:12px;font-weight:bold;color:rgba(0,0,0,0.7)}.input-label--small:hover{color:#000}input[type=checkbox]:checked+.input-label--small{color:#2e8aea}.panel-dialog--header{padding-bottom:10px;margin:10px 0;font-size:17.5px;font-weight:bold;line-height:20px;border-bottom:1px solid #555}.bootstrap-datetimepicker-widget,.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th,.bootstrap-datetimepicker-widget td span{border-radius:0!important}.umb-item-list{display:block;width:auto;margin:0}.umb-item-list li{display:block;width:auto}.umb-tree{width:auto;min-width:100%;margin:0}.umb-tree li{display:block;width:auto;min-width:100%}.umb-tree li.current>div,.umb-tree div.selected{background:#2e8aea}.umb-tree li.current>div a.umb-options i,.umb-tree div.selected i{background:#fff;border-color:#2e8aea;transition:opacity 120ms ease}.umb-tree li.current>div a.umb-options:hover i,.umb-tree div.selected i{opacity:.7}.umb-tree li.current>div a,.umb-tree li.current>div i.icon,.umb-tree li.current>div ins{color:white!important;background:#2e8aea;border-color:#2e8aea}.umb-tree li.root>div{padding:0}.umb-tree li.root>div h5{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin:0;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-tree li.root>div h5>a,.umb-tree-header{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:20px 0 20px 20px;box-sizing:border-box}.umb-tree *{white-space:nowrap}.umb-tree ul{width:100%;min-width:100%;padding:0;margin:0}.umb-tree ul.collapsed{display:none}.umb-tree a{text-decoration:none;cursor:pointer;outline:0}.umb-tree a:hover{text-decoration:none}.umb-tree div{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 0 5px 0;overflow:hidden;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-tree a.noSpr{background-position:0 0}.umb-tree div>a.umb-options{visibility:hidden}.umb-tree div:hover>a.umb-options{visibility:visible}.umb-tree li.root>div a,.umb-tree li.root h5,.umb-tree-header{font-size:13px;font-weight:bold;color:#b3b3b3;text-transform:uppercase}.umb-tree ins{width:16px;height:16px;margin:-4px 0 0 -16px;font-size:12px;text-decoration:none;visibility:hidden;transition:opacity 120ms ease}.umb-tree ins:hover{opacity:.7}.umb-tree li:hover ins{cursor:pointer;visibility:visible}.umb-tree li div{padding:0}.umb-tree li>div a:not(.umb-options){display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;padding:6px 0}.umb-tree .icon{margin:0 13px 0 0;font-size:20px;color:#21201c;vertical-align:middle}.umb-tree-icon{cursor:pointer}.umb-tree i.noSpr{display:inline-block;width:16px;height:16px;margin-top:1px;line-height:16px}.umb-tree div:hover{background:#f8f8f8}.umb-tree small.search-subtitle{display:block;padding-left:35px;color:#d9d9d9}.umb-tree .umb-tree-node-search{cursor:pointer}.umb-tree .umb-search-group-item{padding-left:20px}.umb-tree .umb-search-group-item-link{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.icon-check:before{content:"\e165"}.umb-tree .umb-tree-node-checked i{color:#2e8aea!important}.umb-tree .umb-tree-node-checked i:before{content:"\e165"!important}a.umb-options{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:9px 5px;margin-right:10px;text-align:center;cursor:pointer;visibility:hidden;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}a.umb-options i{display:inline-block;width:5px!important;height:5px!important;margin:0 2px 0 0;background:#000;border-radius:20px}a.umb-options i:last-child{margin:0}a.umb-options:hover{background:#e4e4e4;border-radius:0}li.root>div>a.umb-options{top:18px;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 5px}.hide-options a.umb-options{display:none!important}.hide-header h5{display:none!important}.umb-icon-item{position:relative;display:block;padding:2px;padding-left:55px}.umb-icon-item:hover{background:#f8f8f8}.umb-icon-item i.icon{position:absolute;top:8px;left:19px}.umb-icon-item a:hover div{text-decoration:underline}.umb-icon-item a{height:15px;padding-top:3px;font-size:12px;color:#525252;text-decoration:none}.umb-icon-item small{display:block;font-size:10px;color:#999}.umb-icon-item:hover a.umb-options{visibility:visible}.umb-icon-item .umb-spr{float:left}div.not-published>i.icon,div.not-published>a{opacity:.6}div.protected:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#9d261d;content:"\e256"}div.has-unpublished-version:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#46a546;content:"\e25a"}div.not-allowed>i.icon,div.not-allowed>a{cursor:not-allowed}.umb-tree li.current>div:before{color:#add8e6}div.is-container:before{position:absolute;bottom:0;padding-top:8px;padding-left:13px;font-family:'icomoon';font-size:8px;color:#04bfbf;pointer-events:none;content:"\e04e"}div.locked:before{position:absolute;bottom:0;padding-top:7px;padding-left:7px;font-family:'icomoon';font-size:20px;color:#9d261d;content:"\e0a7"}.umb-actions{padding:0;margin:12px 0 0 0;list-style:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.umb-actions li.sep{display:block;border-top:1px solid #efefef}.umb-actions li.sep:first-child{border-top:0}.umb-actions a{display:block;padding:8px 25px 8px 20px;font-size:14px;color:#000;text-decoration:none;white-space:nowrap;cursor:pointer}.umb-actions a:hover,.umb-actions a:focus,.umb-actions li.selected{color:#000!important;background:#f8f8f8!important}.umb-actions .menu-label{display:inline-block;padding-top:2px;padding-left:15px;vertical-align:middle}.umb-actions i{margin-top:2px;font-size:18px;color:#999;color:#414141;vertical-align:middle}.umb-actions-child{display:block;margin:0;list-style:none}.umb-actions-child li{display:block}.umb-actions-child a{display:block;padding-left:10px;clear:both;text-decoration:none}.umb-actions-child li .menu-label{margin-left:20px;font-size:12px;color:#000}.umb-actions-child li .menu-label small{display:block;clear:right;font-size:12px;line-height:14px;color:#ccc;white-space:normal}.umb-actions-child li a:hover .menuLabel small{text-decoration:none!important}.umb-actions-child i{font-size:32px}.umb-actions-child li.add{padding-top:20px;margin-top:20px;border-top:1px solid #e9e9e9}.umb-actions-child li.add i{opacity:.4}.umb-tree i.icon.blue{color:#2e8aea}.umb-tree i.icon.green{color:#46a546}.umb-tree i.icon.purple{color:#7a43b6}.umb-tree i.icon.orange{color:#df7f48}.umb-tree i.icon.red{color:#9d261d}.umb-tree li div.l{position:absolute;bottom:0;left:0;width:100%;height:1px;overflow:hidden}.umb-tree li div.l div{width:100%;height:1px;margin-top:0;margin-left:-100%;background-color:#2e8aea;-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:bounce_loadingProgressG;animation-name:bounce_loadingProgressG;-webkit-animation-timing-function:linear;animation-timing-function:linear;-o-animationtiming-function:linear}.umb-tree .umb-loader{height:10px;margin:10px 10px 10px 10px}body.touch .umb-tree ins{padding:7px;font-size:14px;visibility:visible}body.touch .umb-tree li div{padding-top:8px;padding-bottom:8px;font-size:110%}body.touch .umb-actions a{padding:7px 25px 7px 20px;font-size:110%}body.touch a.umb-options i{margin-top:20px}.umb-listview{width:auto!important}.umb-listview .dropdown-menu{border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);border-radius:0;box-shadow:0 5px 10px rgba(0,0,0,0)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus,.dropdown-submenu:hover>a,.dropdown-submenu:focus>a{color:#000;background:#f8f8f8}.umb-listview table{border:1px solid #d9d9d9}.umb-listview table caption{text-align:left;vertical-align:middle;background:#fff}.umb-sub-header{padding:0 0 20px 0}.umb-sub-header .header-content-right{float:right}.form-search .inner-addon{position:relative}.form-search .inner-addon [class^="icon-"],.form-search .inner-addon [class*=" icon-"]{position:absolute;top:0;padding:5px 8px;pointer-events:none}.form-search .inner-addon input[type="text"]{width:190px}.form-search .left-addon [class^="icon-"],.form-search .left-addon [class*=" icon-"]{right:inherit;left:0}.form-search .right-addon [class^="icon-"],.form-search .right-addon [class*=" icon-"]{right:0;left:inherit}.form-search .left-addon input[type="text"]{padding-right:6px;padding-left:30px!important}.form-search .right-addon input[type="text"]{padding-right:30px;padding-left:6px!important}.umb-listview table form{position:relative;margin:0}.umb-listview table input[type="text"]{width:60px;padding:4px 0 4px 20px;background:0;border:1px solid #d9d9d9;transition:all .5s}.umb-listview table input::-webkit-input-placeholder{color:#555}.umb-listview table input:-moz-placeholder{color:#555}.umb-listview table input::-moz-placeholder{color:#555}.umb-listview table input:-ms-input-placeholder{color:#555}.umb-listview table input[type="text"]:focus{width:200px;color:#000;background:#fff;border:1px solid #d9d9d9}.umb-listview table thead a{cursor:default}.umb-listview table thead a:hover{text-decoration:none}.umb-listview table thead a.sortable{cursor:pointer}.umb-listview table thead a.sortable span:hover{text-decoration:underline}.umb-listview .icon-star{color:#d9d9d9}.umb-listview .selected i.icon,.umb-listview tbody tr:hover i.icon{display:none}.umb-listview .selected input[type="checkbox"],.umb-listview tr:hover input[type="checkbox"]{display:inline-block!important}.umb-listview .inactive{color:#d9d9d9}.umb-listview .selected td{font-weight:bold}.umb-listview table thead{font-size:11px;font-weight:600;text-transform:uppercase;background-color:#fff}.umb-listview table tfoot{background:#f8f8f8}.umb-listview table tfoot td:last-child{border-left:none}.umb-listview table tfoot th{padding:0 20px}.umb-listview .label{font-size:11px;font-weight:400;color:#000;text-shadow:none;background:#f8f8f8;border:1px solid #d9d9d9}.umb-listview .table-striped tbody>tr:nth-child(even)>td,.umb-listview .table-striped tbody>tr:nth-child(even)>th{background-color:#f8f8f8}.table-striped tbody>tr:nth-child(odd)>td,.table-striped tbody>tr:nth-child(odd)>th{background:0}.umb-listview .pagination{margin:0}.umb-listview .table th{font-weight:normal}.umb-listview .showing{padding:8px 4px 2px 4px;font-size:11px;color:#b0b0b0;background:0}.umb-listview .pagination{text-align:center}.umb-listview .pagination ul{background:0;border-radius:0;box-shadow:0 1px 2px rgba(0,0,0,0)}.umb-listview .pagination ul>li>a,.pagination ul>li>span{padding:8px 4px 2px 4px;font-size:12px;color:#b0b0b0;background:0;border:0}.umb-listview .pagination ul>li.active>a,.umb-listview .pagination ul>li>a:hover{color:#000}.umb-listview .pagination ul>li.disabled>a,.umb-listview .pagination ul>li.disabled>a:hover{color:#d9d9d9}.umb-listview .table-striped tbody td{position:relative}.umb-listview .table-striped thead input[type="checkbox"]{margin-left:7px}.umb-listview .table-striped tbody input[type="checkbox"]{z-index:5;display:none;margin-left:7px}.umb-listview .table-striped tbody i{top:10px;left:6px;z-index:6;display:block;width:20px;height:20px;padding:0 0 0 4px;background:#fff}.umb-listview .table-striped tbody>tr:nth-child(even)>td i,.umb-listview .table-striped tbody>tr:nth-child(even)>th i{background-color:#f8f8f8}.table-striped tbody i:hover{display:none!important}.list-view-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 15px;margin-bottom:1px;background:#f8f8f8;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.list-view-layout__sort-handle{margin-right:15px;font-size:14px;color:#d9d9d9}.list-view-layout__name{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-right:15px;font-weight:bold;line-height:1.2em;-webkit-flex:5;-ms-flex:5;flex:5;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.list-view-layout__name-text{margin-right:3px}.list-view-layout__system{font-size:10px;font-weight:normal}.list-view-layout__path{margin-right:15px;-webkit-flex:10;-ms-flex:10;flex:10}.list-view-layout__icon{display:block;padding:6px 8px;margin-right:10px;font-size:18px;vertical-align:middle;background:#fff;border:1px solid #d9d9d9}.list-view-layout__icon:hover,.list-view-layout__icon:focus,.list-view-layout__icon:active{text-decoration:none}.list-view-layout__remove{position:relative;cursor:pointer}.list-view-add-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 0;margin-top:10px;color:#2e8aea;border:1px dashed #d9d9d9;box-sizing:border-box;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.list-view-add-layout:hover{text-decoration:none}.mceContentBody{overflow-y:hidden!important}.usky-grid IFRAME{overflow:hidden}.usky-grid .ui-sortable-helper{position:absolute!important;width:160px!important;height:80px!important;padding:5px;overflow:hidden;background:#ccc;border:dashed 1px #000!important;border-radius:5px;opacity:.4;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.usky-grid .ui-sortable-helper *{padding:0!important;margin:0!important;color:#999!important;background:none!important;border:none!important}.usky-grid .ui-sortable-helper .cell-tools{display:none!important}.usky-grid .ui-sortable-placeholder{padding:20px;font-family:icomoon;font-size:85px;line-height:65px;color:#555;text-align:center;vertical-align:middle;background-color:#f8f8f8;border:2px dashed #d9d9d9}.usky-grid .ui-sortable-placeholder:hover{border-color:#555}.usky-grid .ui-sortable-placeholder:before{content:"\e1bd"}.usky-grid-width{width:100%;margin:20px auto}.usky-grid .right{float:right}.usky-grid .tb{width:100%}.usky-grid .td{display:inline-block;width:100%;vertical-align:top;border-right:1px dashed rgba(182,182,182,0);box-sizing:border-box}.usky-grid .tb:hover .td{border-right:1px dashed rgba(182,182,182,0.3)}.usky-grid .td.last{border-right:1px dashed rgba(182,182,182,0)!important}.usky-grid .middle{text-align:center}.usky-grid .mainTb{border-collapse:separate}.usky-grid .mainTd{position:relative}.usky-grid .usky-row{position:relative;border:1px dashed rgba(0,0,0,0)}.umb-grid .tb:hover .usky-row{border-bottom:1px dashed rgba(182,182,182,0)!important}.usky-grid .usky-cell{position:relative;min-height:127px;border:1px dashed rgba(0,0,0,0)}.usky-grid .cell-tools{position:absolute;top:0;right:0;bottom:0;z-index:50;width:50px;opacity:.3;transition:all .2s ease-in-out}.umb-grid .cell-tools{width:50px!important}.usky-grid .cell-tools.with-prompt{width:200px}.usky-grid .cell-tools:hover{opacity:1}.usky-grid .cell-tools-add{position:absolute;right:0;bottom:0;left:0;margin:0 45px 1px 0;text-align:center}.usky-grid .cell-tools-add.emptyArea{margin:0 0 1px 0}.usky-grid .usky-control:hover .cell-tools-add{opacity:1}.usky-grid .cell-tools-remove{position:absolute;top:0;right:5px;z-index:500;display:inline-block;text-align:right}.usky-grid .cell-tools-remove .iconBox:hover,.usky-grid .cell-tools-remove .iconBox:hover *{background:#9d261d!important;border-color:#9d261d!important}.usky-grid .cell-tools-move{position:absolute;top:33px;right:5px;z-index:500;display:inline-block;cursor:move}.usky-grid .cell-tools-edit{position:absolute;top:66px;right:5px}.usky-grid .usky-control{position:relative;display:block;margin:10px 0 0 0;background-clip:padding-box}.usky-grid .warnhighlight,.usky-grid .td.last.warnhighlight{border:1px dashed #9d261d!important}.usky-grid .infohighlight,.usky-grid .td.last.infohighlight{border:1px dashed #2e8aea!important}.usky-grid .warnhighlight>ins.item-label{color:#9d261d;border-color:#9d261d}.usky-grid .infohighlight>ins.item-label{color:#2e8aea;border-color:#2e8aea}.usky-grid ins.item-label{position:absolute;top:-22px;left:-1px;display:none;height:20px;padding:0 7px;overflow:hidden;font-size:.8em;color:#d9d9d9;text-decoration:none;background-color:white;border:1px dashed #d9d9d9;border-bottom:1px solid white!important}.usky-grid .usky-row-inner>ins.item-label{top:-20px;left:0}.usky-grid .usky-control-inner.selectedControl,.usky-grid .usky-row-inner.selectedRow{border:1px dashed #d9d9d9}.usky-grid .usky-control-inner.selectedControl>ins.item-label,.usky-grid .usky-row-inner.selectedRow>ins.item-label{z-index:100000;display:block}.usky-grid .usky-control-placeholder{position:relative;min-height:20px;text-align:center;text-align:-moz-center;cursor:text}.usky-grid .usky-control-placeholder .placeholder{height:20px;padding:5px;font-size:14px;text-align:left;border:1px solid rgba(182,182,182,0.3);opacity:.7}.usky-grid .usky-control-placeholder:hover .placeholder{border:1px solid rgba(182,182,182,0.8)}.usky-grid .usky-editor-placeholder{position:relative;min-height:65px;padding:20px;padding-bottom:30px;text-align:center;text-align:-moz-center;background-color:white;border:4px dashed #f8f8f8}.usky-grid .usky-editor-placeholder i{display:block;margin-bottom:10px;font-size:85px;line-height:85px;color:#f8f8f8}.usky-grid textarea.textstring{display:block;overflow:hidden;color:#555;background:#fff;border:0;outline:0;resize:none}.usky-grid .usky-cell-rte textarea{display:none!important}.usky-grid .usky-cell-media .caption{display:block;width:98%;overflow:hidden;font-style:italic;background:#fff;border:0;outline:0;resize:none}.usky-grid .cellPanelRte{min-height:60px}.usky-grid .usky-cell-embed iframe{width:100%}.usky-grid .iconBox{display:inline-block;padding:4px 6px 4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.usky-grid .iconBox span.prompt{display:block;text-align:center;white-space:nowrap}.usky-grid .iconBox span.prompt>a{text-decoration:underline}.usky-grid .iconBox:hover,.usky-grid .iconBox:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.usky-grid .iconBox a:hover{color:white!important;text-decoration:none}.usky-grid .iconBox.selected{background:transparent;background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-radius:3px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-webkit-appearance:none}.usky-grid .iconBox i{display:block;font-size:16px!important;color:#5f5f5f}.usky-grid ul{display:inline-block;padding:0;margin:10px 0 0 0;text-align:center;list-style:none}.usky-grid .help-text{display:inline-block;padding:10px 20px 10px 20px;clear:both;font-size:14px;color:#555;background:#f8f8f8;border-radius:15px}.usky-grid ul li{display:inline-block;width:120px;margin:8px 8px 0 8px}.usky-grid .mce-panel{clear:both;border:none!important}.usky-grid .mce-btn button{padding:8px 6px;line-height:inherit}.usky-grid .mce-toolbar{position:absolute;z-index:100;z-index:9999999;display:inline-block;float:left;padding:-1px;margin:-1px -1px 0 -1px;background-color:#fafafa;border:1px solid rgba(207,207,207,0.7);box-shadow:2px 2px 10px 0 rgba(50,50,50,0.14)}.mce-flow-layout-item{margin:0}.usky-grid .mce-panel{background:transparent!important}.usky-grid .mce-floatpanel{background-color:#f7f7f7!important}.usky-cell-rte{border:1px solid #f8f8f8}.usky-grid .fullSizeImage{width:100%}.usky-grid .boxWidth{margin-bottom:10px;text-align:right}.usky-grid .boxWidth input{width:40px;text-align:center}.usky-grid .boxWidth label{padding:0;margin:5px 5px 0 0;font-size:10px;color:#808080}.usky-grid .usky-cell{padding-top:5px;padding-bottom:15px}.usky-grid .usky-control{padding:5px;margin:10px 0 0 0;border:1px dashed transparent}.usky-grid .usky-templates-columns{margin-top:30px}.usky-grid .usky-row-inner{margin-right:45px;border:1px dashed transparent}.usky-grid .usky-control-inner{position:relative;min-height:60px;padding:5px;margin-right:45px;margin-bottom:15px;border:1px dashed transparent}.usky-grid .uSky-templates{width:100%;overflow:hidden;text-align:center}.usky-grid .uSky-templates-template{display:inline-block;width:100px;padding-right:30px;margin:20px}.usky-grid .uSky-templates-template a.tb:hover{border:5px solid #2e8aea}.usky-grid .uSky-templates-template .tb{position:relative;width:100%;height:150px;padding:10px;cursor:pointer;background-color:#f8f8f8;border:5px solid #d9d9d9}.usky-grid .uSky-templates-template .tr{position:relative;height:100%}.usky-grid .uSky-templates-template .tb .uSky-templates-column{height:100%;border:1px dashed #d9d9d9;border-right:0}.usky-grid .uSky-templates-template .tb .uSky-templates-column.last{border-right:1px dashed #d9d9d9!important}.usky-grid a.uSky-templates-column:hover,.usky-grid a.uSky-templates-column.selected{background-color:#2e8aea}.usky-grid .templates-preview{display:inline-block;width:100%;text-align:center}.usky-grid .templates-preview small{position:absolute;bottom:-25px;left:0;width:100%;padding-top:15px}.usky-grid .templates-preview .help-text{margin:35px 35px 0 0}.usky-grid .preview-rows{position:relative;display:inline-block;width:125px;margin:35px 40px 15px 0;border:2px solid #d9d9d9;box-sizing:border-box;transition:border 200ms linear}.usky-grid .preview-rows.prevalues-rows{float:left;width:80px;margin:0 20px 20px 0}.usky-grid .preview-rows.prevalues-templates{float:left;margin:0 20px 20px 0}.usky-grid .preview-rows:hover{cursor:pointer;border-color:#2e8aea}.usky-grid .preview-rows .preview-row{display:inline-block;width:100%;vertical-align:bottom}.usky-grid .preview-rows.layout{padding:2px}.usky-grid .preview-rows.layout .preview-row{height:100%}.usky-grid .preview-rows.layout .preview-col{height:180px;border:dashed 1px #d9d9d9}.usky-grid .preview-rows.layout .preview-cell{background-color:#f8f8f8}.usky-grid .preview-rows.layout .preview-overlay{display:none}.usky-grid .preview-rows.columns{min-height:18px;padding:1px;line-height:11px}.usky-grid .preview-rows.columns.prevalues-rows{min-height:30px}.usky-grid .preview-rows .preview-col{display:block;float:left;width:33.3%;height:10px;margin:0;border:1px solid #fff;box-sizing:border-box}.usky-grid .preview-rows .preview-col .preview-cell{display:block;width:100%;height:100%;margin:0 1px 1px 0;background-color:#d9d9d9}.usky-grid .preview-rows.prevalues-templates .preview-col{height:80px}.usky-grid .preview-overlay{position:absolute;top:0;left:0;display:block;width:100%;height:100%;border:3px solid white;box-sizing:border-box}.usky-grid .cell-tools-menu{position:absolute;top:0;left:50%;z-index:6660;width:360px;height:380px;padding:7px;margin-top:-270px;margin-left:-150px;overflow:auto;background:white;border:1px solid #ccc;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.usky-grid .cell-tools-menu h5{padding:10px;margin-top:0;color:#999;border-bottom:1px solid #d9d9d9}.usky-grid .elements{display:block;padding:0;margin:0}.usky-grid .elements li{display:inline-block;width:90px;height:80px;padding:5px;margin:5px;overflow:hidden;font-size:11px}.usky-grid .elements li:hover,.usky-grid .elements li:hover *{color:white;background:#2e8aea}.usky-grid .elements a{color:#222;text-decoration:none}.usky-grid .elements i{display:block;font-size:30px;line-height:50px;color:#999}.usky-grid-configuration .uSky-templates{text-align:left}.usky-grid-configuration ul{display:block}.usky-grid-configuration ul li{display:block;width:auto;text-align:left}.usky-grid-configuration .uSky-templates .uSky-templates-template .tb{max-height:50px;padding:0;overflow:hidden;border-spacing:2px;border-width:2px!important}.usky-grid-configuration .uSky-templates .uSky-templates-template span{display:inline-block;background:#d9d9d9}.usky-grid-configuration .uSky-templates .uSky-templates-template .tb:hover{border-width:2px!important}.usky-grid-configuration .uSky-templates-column{display:block;float:left;margin-left:-1px;background:#d9d9d9;border:1px white solid!important}.usky-grid-configuration .uSky-templates-column.last{margin-right:-1px}.usky-grid-configuration .uSky-templates-column.add{font-size:20px;line-height:70px;color:#ccc;text-align:center;text-decoration:none;background:white}.usky-grid-configuration .mainTdpt{height:initial;border:0}.usky-grid-configuration .uSky-templates-rows .uSky-templates-row{width:60px;margin:0 50px 20px 0}.usky-grid-configuration .uSky-templates-rows .uSky-templates-row .tb{padding:0;border-spacing:2px;border-width:2px!important}.usky-grid-configuration .uSky-templates-rows .mainTdpt{height:10px!important}.usky-grid-configuration a.uSky-templates-column{height:70px!important}body.dragging,body.dragging *{cursor:move!important}li.dragged{position:absolute;z-index:2000;opacity:.5}.umb-sort li{display:block;padding:5px;margin:5px;background:#f8f8f8;border:1px solid #CCC}.umb-sort .placeholder{position:relative;padding:0;margin:0;border:0}.umb-sort .placeholder:before{position:absolute;top:-4px;left:-5px;width:0;height:0;margin-top:-5px;border:5px solid transparent;border-right:0;border-left-color:red;content:""}.umb-dashboards-forms-install{position:relative;top:-30px;padding-top:30px;background:url('../img/forms/installer-background.png');background-repeat:repeat-x;border-radius:0 0 200px 200px;box-shadow:inset 0 -40px 30px 25px #fff}.umb-dashboards-forms-install small{font-size:14px;opacity:.5}.umb-dashboards-forms-install .umb-loader{width:640px;height:4px}.umb-dashboards-forms-install .video_player video{width:100%;max-width:640px;border:1px solid #ededed;border-bottom:0;border-left:none;box-sizing:border-box}.umb-dashboards-forms-install .video_player input[type="range"]{position:relative;z-index:999;width:100%;max-width:640px;margin:0 auto;cursor:pointer;opacity:0}.umb-dashboards-forms-install .video_player input[type="range"]::-ms-fill-lower,.umb-dashboards-forms-install .video_player input[type="range"]::-ms-fill-upper{background:transparent}.umb-dashboards-forms-install .video_player input[type="range"]::-ms-tooltip{display:none}.umb-dashboards-forms-install .video_player .video-controls{position:relative;height:20px;max-width:640px;margin:-13px auto 40px;opacity:.8}.umb-dashboards-forms-install .video_player .loader{display:block;width:100%;height:3px;margin-top:-13px;background-color:whitesmoke}.umb-dashboards-forms-install .video_player .progress-bar{display:block;width:200px;height:100%;max-width:100%;background:#7c7e80;box-sizing:border-box}.umb-dashboards-forms-install .video_player .video-controls,.umb-dashboards-forms-install .video_player .loader,.umb-dashboards-forms-install .video_player .progress-bar{transition:all 150ms ease-in-out}.umb-dashboards-forms-install .video_player .progress-bar{transition-property:background}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls,.umb-dashboards-forms-install .video_player .video-controls:hover{margin-top:-19px;margin-bottom:46px;opacity:1}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls .loader,.umb-dashboards-forms-install .video_player .video-controls:hover .loader{height:8px}.umb-dashboards-forms-install .video_player .video_player video:hover+.video-controls .progress-bar,.umb-dashboards-forms-install .video_player .video-controls:hover .progress-bar{background:#2e99f4}.umb-dashboards-forms-install .installer-top{position:relative;height:80px;margin:0 auto 30px;overflow:hidden;line-height:80px;text-align:center}.umb-dashboards-forms-install .installer-top i,.umb-dashboards-forms-install .installer-top h3{display:inline-block}.umb-dashboards-forms-install .installer-top i{position:absolute;top:50%;margin-top:-40px}.umb-dashboards-forms-install .installer-top h3{margin:0;margin-left:100px;font-size:36px;font-weight:700;line-height:80px;letter-spacing:-1px}.umb-validation-label{position:relative;padding:1px 5px;font-size:10px;line-height:1.5em;color:white;background:#9d261d}.umb-validation-label:after{position:absolute;bottom:100%;left:10px;width:0;height:0;margin-left:-4px;pointer-events:none;border:solid transparent;border-color:rgba(255,255,255,0);border-bottom-color:#9d261d;border-width:4px;content:" "}.umb-validation-label.-arrow-left{margin-left:10px}.umb-validation-label.-arrow-left:after{position:absolute;top:50%;right:100%;bottom:auto;left:auto;width:0;height:0;margin-top:-4px;pointer-events:none;border:solid transparent;border-color:rgba(255,255,255,0);border-right-color:#9d261d;border-width:4px;content:" "}.umb-editor-wrapper{position:absolute;top:0;right:0;bottom:0;left:0;background:white}.umb-editor-header{position:absolute;top:0;right:0;left:0;height:99px;background:#f8f8f8;border-bottom:1px solid #d9d9d9}.umb-editor-container{position:absolute;top:101px;right:0;bottom:52px;left:0;overflow:auto;clear:both}.umb-editor-wrapper.-no-footer .umb-editor-container{bottom:0}.umb-editor-container.-stop-scrolling{overflow:hidden}.umb-editor-drawer{position:absolute;right:0;bottom:0;left:0;z-index:999;height:31px;padding:10px 20px;margin:0;background:#f8f8f8;border-top:1px solid #d9d9d9}.umb-editor-actions{padding:0;margin:0;list-style:none}.umb-editor-actions li{display:inline-block}.umb-overlay{position:fixed;z-index:996660;overflow:hidden;background:white;box-shadow:0 10px 20px rgba(0,0,0,0.19),0 6px 6px rgba(0,0,0,0.23);-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-overlay__form{display:-webkit-flex;display:-ms-flexbox;display:flex;height:100%;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-overlay .umb-overlay-header{padding:10px;margin-top:0;background:#f8f8f8;border-bottom:1px solid #d9d9d9;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0}.umb-overlay .umb-overlay__title{margin:7px 0;font-size:17.5px;font-weight:bold;color:#000}.umb-overlay .umb-overlay__subtitle{font-size:11.9px;color:#555}.umb-overlay .umb-overlay-container{position:relative;height:auto;overflow-x:hidden;overflow-y:auto;-webkit-flex-grow:1;-ms-flex-positive:1;flex-grow:1;-webkit-flex-shrink:1;-ms-flex-negative:1;flex-shrink:1;-webkit-flex-basis:auto;-ms-flex-preferred-size:auto;flex-basis:auto}.umb-overlay .umb-overlay-drawer{padding:10px 20px;margin:0;background:#f8f8f8;border-top:1px solid #d9d9d9;-webkit-flex-grow:0;-ms-flex-positive:0;flex-grow:0;-webkit-flex-shrink:0;-ms-flex-negative:0;flex-shrink:0;-webkit-flex-basis:31px;-ms-flex-preferred-size:31px;flex-basis:31px}.umb-overlay .umb-overlay-drawer.-auto-height{-webkit-flex-basis:auto;-ms-flex-preferred-size:auto;flex-basis:auto}.umb-overlay .umb-overlay-drawer .umb-overlay-drawer__align-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:flex-end;-ms-flex-pack:end;justify-content:flex-end}.umb-overlay .umb-overlay-drawer .umb-overlay-drawer-content .dropdown-menu{right:0;left:auto}.umb-overlay.umb-overlay-center{position:absolute;top:10px;left:50%;width:600px;height:500px;-webkit-transform:translate(-50%,0);transform:translate(-50%,0)}.umb-overlay.umb-overlay-center .umb-overlay-header{text-align:center}.umb-overlay.umb-overlay-center .umb-overlay-container{padding:20px}.umb-overlay.umb-overlay-target{width:400px;height:400px;box-sizing:border-box}.umb-overlay.umb-overlay-target .umb-overlay-header{text-align:center}.umb-overlay.umb-overlay-target .umb-overlay-container{padding:10px}.umb-overlay.umb-overlay-right{top:0;right:0;bottom:0;width:500px;border:0;box-shadow:0 0 20px rgba(0,0,0,0.19),0 0 6px rgba(0,0,0,0.23)}.umb-overlay.umb-overlay-right .umb-overlay-header{padding:20px;box-sizing:border-box;-webkit-flex-basis:100px;-ms-flex-preferred-size:100px;flex-basis:100px}.umb-overlay.umb-overlay-right .umb-overlay-container{padding:20px}.umb-overlay.umb-overlay-left{top:0;bottom:0;left:0;width:500px;margin-left:80px;border:0;box-shadow:0 0 20px rgba(0,0,0,0.19),0 0 6px rgba(0,0,0,0.23)}.umb-overlay.umb-overlay-left .umb-overlay-header{padding:20px;box-sizing:border-box;-webkit-flex-basis:100px;-ms-flex-preferred-size:100px;flex-basis:100px}.umb-overlay.umb-overlay-left .umb-overlay-container{padding:20px}@media(max-width:767px){.umb-overlay.umb-overlay-left{margin-left:60px}}.umb-overlay__item-details{position:absolute;bottom:51px;left:0;width:100%;padding:20px;background:#f8f8f8;border-bottom:1px solid #d9d9d9;box-sizing:border-box}.umb-overlay__item-details-title-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-overlay__item-details-icon{margin-right:10px;font-size:16px;color:#999;vertical-align:middle}.umb-overlay__item-details-title{margin-top:0;margin-bottom:0}.umb-overlay__item-details-description{margin-top:10px;font-size:12px}.umb-overlay .control-label,.umb-overlay .form-horizontal .control-label,.form-horizontal .umb-overlay .control-label{display:block;float:none;width:100%;margin-bottom:10px;box-sizing:border-box}.umb-overlay .controls-row,.umb-overlay .form-horizontal .controls,.form-horizontal .umb-overlay .controls{margin-left:0!important}.umb-card{position:relative;padding:5px 10px 5px 10px;background:white}.umb-card .title{padding:12px;margin:0 -10px 10px -10px;font-size:16px;font-weight:400;color:#555;text-transform:none;border-bottom:1px solid #d9d9d9}.umb-card-thumb{text-align:center}.umb-card-thumb i{display:block;padding-top:5px;font-size:20px;line-height:40px;color:#2e8aea;text-align:center}.umb-card-content .item-title{margin-bottom:3px;font-size:16px;font-weight:400;color:#1d1d1d;text-transform:none;border:0}.umb-card-content p{margin-bottom:1px;color:#555}.umb-card-actions{padding-top:10px;clear:both;border-top:#f8f8f8 1px solid}.umb-card-icons{display:block;padding:0;margin:0;text-align:center;vertical-align:middle;list-style:none}.umb-card-icons.vertical{position:absolute;top:7px;right:7px;width:1px;text-align:right}.umb-card-icons li{display:inline-block;margin:0 2px 0 2px}.umb-card-icons.vertical li{display:block;float:right;margin-bottom:3px}.umb-card-list{display:block;padding:0;margin:0}.umb-card-list li{display:block;padding-bottom:3px;border-bottom:#f8f8f8 1px solid}.umb-card-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0;margin:0 auto;list-style:none;-webkit-flex-flow:row wrap;-ms-flex-flow:row wrap;flex-flow:row wrap;-webkit-justify-content:flex-start;-ms-flex-pack:start;justify-content:flex-start}.umb-card-grid li{width:100px;height:105px;padding:5px;overflow:hidden;font-size:11px;text-align:center;box-sizing:border-box}.umb-card-grid li.-four-in-row{-webkit-flex:0 0 25%;-ms-flex:0 0 25%;flex:0 0 25%}.umb-card-grid li.-three-in-row{-webkit-flex:0 0 33.33%;-ms-flex:0 0 33.33%;flex:0 0 33.33%}.umb-card-grid .umb-card-grid-item{display:block;width:100%;height:100%}.umb-card-grid .umb-card-grid-item:hover,.umb-card-grid .umb-card-grid-item:focus,.umb-card-grid .umb-card-grid-item:hover>*,.umb-card-grid .umb-card-grid-item:focus>*{color:white;cursor:pointer;background:#2e8aea;outline:0}.umb-card-grid a{color:#222;text-decoration:none}.umb-card-grid i{display:block;font-size:30px;line-height:50px;color:#343434}.umb-btn-round{display:inline-block;padding:4px 6px 4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.umb-btn-round:hover,.umb-btn-round:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.umb-btn-round a:hover{color:white!important;text-decoration:none}.umb-btn-round i{display:block;font-size:16px!important;color:#grayLight}.umb-btn-round.alert:hover,.umb-btn-round.alert:hover *{color:white!important;text-decoration:none;background:#9d261d!important;border-color:#9d261d!important}.umb-btn-round.no-border{background:none!important;border:none!important}.umb-sub-views .umb-sub-views-action-bar{margin-bottom:40px}.umb-sub-views .umb-sub-views-action-bar .btn-link{padding-right:0;padding-left:0}.umb-sub-views .umb-sub-views-action-bar .btn-link:focus{text-decoration:none;outline:0}.umb-sub-views .umb-sub-views-nav{float:right;margin:0}.umb-sub-views .umb-sub-views-nav .umb-sub-views-nav-item{display:inline-block;margin-left:15px}.umb-sub-views .umb-sub-views-nav .umb-sub-views-nav-item.is-active .btn-link{color:#2e8aea!important}.umb-sub-views .umb-sub-views-tools{float:left;margin:0}.umb-sub-views .umb-sub-views-tools .umb-sub-views-tool{display:inline-block;margin-right:15px}.umb-sub-views-nav{display:-webkit-flex;display:-ms-flexbox;display:flex;margin:0;list-style:none}.umb-sub-views-nav-item{display:block;margin-left:20px;text-align:center;cursor:pointer}.umb-sub-views-nav-item:focus{outline:0}.umb-sub-views-nav-item:hover,.umb-sub-views-nav-item:focus{text-decoration:none}.umb-sub-views-nav-item.is-active{color:#2e8aea}.show-validation .umb-sub-views-nav-item.-has-error{color:#9d261d}.umb-sub-views-nav-item .icon{display:block;font-size:24px;text-align:center}.umb-sub-views-nav-item-text{font-size:11px}.sub-view-columns{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:40px}.sub-view-columns h5{margin-top:0}.sub-view-column-left{margin-right:70px;-webkit-flex:0 0 250px;-ms-flex:0 0 250px;flex:0 0 250px}.sub-view-column-right{-webkit-flex:1;-ms-flex:1;flex:1}.sub-view-column-section{margin-bottom:20px}.umb-editor-sub-header{position:relative;top:0;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:15px 0;margin-top:-30px;margin-bottom:30px;background:#fff;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-editor-sub-header.-umb-sticky-bar{top:100px;-webkit-transform:translate(0,50%);transform:translate(0,50%);box-shadow:0 5px 0 rgba(0,0,0,0.08),0 1px 0 rgba(0,0,0,0.16);transition:box-shadow 1s}.umb-group-builder__property-preview .umb-editor-sub-header{display:none}.umb-editor-sub-header__content-left{margin-right:auto}.umb-editor-sub-header__content-right{margin-left:auto}.umb-editor-sub-header__content-left,.umb-editor-sub-header__content-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:stretch;-ms-flex-align:stretch;align-items:stretch}.umb-editor-sub-header__section{display:-webkit-flex;display:-ms-flexbox;display:flex;padding-right:20px;padding-left:20px;border-left:1px solid #d9d9d9;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-editor-sub-header__content-left .umb-editor-sub-header__section:first-child{padding-left:0;border-left:none}.umb-editor-sub-header__content-right .umb-editor-sub-header__section{border-right:1px solid #d9d9d9;border-left:none}.umb-editor-sub-header__content-right .umb-editor-sub-header__section:last-child{padding-right:0;border-right:0}.umb-grid-selector__items{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-grid-selector__item{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;width:125px;height:150px;padding:20px;margin:0 20px 20px 0;text-align:center;background:#f8f8f8;border:1px solid #ccc;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-grid-selector__item.-default{border-width:2px}.umb-grid-selector__item.-placeholder{cursor:pointer;background:0;border:1px dashed #d9d9d9}.umb-grid-selector__item-content{margin-top:10px}.umb-grid-selector__item-icon{display:block;margin-bottom:5px;font-size:50px;line-height:50px;color:#d9d9d9}.umb-grid-selector__item-label{font-size:13px;font-weight:bold}.umb-grid-selector__item-label.-blue{color:#2e8aea}.umb-grid-selector__item-remove{position:absolute;top:5px;right:5px;cursor:pointer}.umb-grid-selector__item-default-label{position:relative;top:-3px;font-size:10px;font-weight:bold;color:#555}.umb-grid-selector__item-default-label.-blue{color:#2e8aea}.umb-grid-selector__item-add{color:#2e8aea}.umb-child-selector__child{display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:300px;padding:5px 15px;margin-bottom:5px;background:#f8f8f8;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-child-selector__child.-parent{padding-top:10px;padding-bottom:10px;background:#f1f1f1}.umb-child-selector__child.-placeholder{text-align:center;cursor:pointer;background:0;border:1px dashed #d9d9d9;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-child-selector__children-container{margin-left:30px}.umb-child-selector__child-description{-webkit-flex:1;-ms-flex:1;flex:1}.umb-child-selector__child-icon-holder{display:inline-block;width:20px;margin-right:5px;text-align:center}.umb-child-selector__child-icon{font-size:16px;vertical-align:middle}.umb-child-selector__child-name{font-size:13px}.umb-child-selector__child-name.-blue{color:#2e8aea}.umb-child-selector__child-actions{text-align:right;-webkit-flex:0 0 50px;-ms-flex:0 0 50px;flex:0 0 50px}.umb-child-selector__child-remove{cursor:pointer}.umb-group-builder__groups{padding:0;margin:0;list-style:none}.umb-group-builder__group{position:relative;min-height:86px;padding:10px 10px 5px 10px;margin:50px 0 0 0;border:2px solid #ccc;border-radius:0 10px 10px 10px;box-sizing:border-box}.umb-group-builder__group.-active{border-color:#2e8aea}.umb-group-builder__group.-inherited{border-color:#f2f2f2;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__group.-placeholder{display:-webkit-flex;display:-ms-flexbox;display:flex;min-height:86px;font-weight:bold;color:#2e8aea;cursor:pointer;border:1px dashed #d9d9d9;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group.-sortable{min-height:initial;cursor:move}.umb-group-builder__group-actions{position:absolute;top:5px;right:5px;z-index:10;opacity:0;visibility:hidden}.umb-group-builder__group-action{display:inline-block}.umb-group-builder__group-remove{position:absolute;top:-30px;right:20px;font-size:18px}.umb-group-builder__group-remove:hover{color:#0064cd;cursor:pointer}.umb-group-builder__group-title-wrapper{position:absolute;top:-45px;left:-2px;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group-title-wrapper.-placeholder{top:-44px;left:-1px}.umb-group-builder__group-title{display:-webkit-flex;display:-ms-flexbox;display:flex;height:38px;padding:5px 9px 0 9px;font-weight:bold;background:white;border:2px solid #ccc;border-bottom:0;border-radius:10px 10px 0 0;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__group-title-icon{margin-left:5px}.umb-group-builder__group-title.-active{border-color:#2e8aea}.umb-group-builder__group-title.-placeholder{width:70px;border:1px dashed #d9d9d9;border-bottom:0}.umb-group-builder__group-title.-inherited{border-color:#f2f2f2}input.umb-group-builder__group-title-input{margin-bottom:0;font-weight:bold;color:#515151;background:transparent;border-color:transparent}.umb-group-builder__group-title-input:hover{border-color:#ccc}.umb-group-builder__group-title-input.-placeholder{border:1px dashed #979797}.umb-group-builder__group-inherited-label{position:relative;top:2px;display:inline-block;margin-left:10px;font-size:13px;color:#d9d9d9}.umb-group-builder__group-sort-order{position:relative;top:2px;display:-webkit-flex;display:-ms-flexbox;display:flex;margin-left:10px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}input.umb-group-builder__group-sort-value{width:40px;padding:0 0 0 5px;margin-bottom:0;font-size:11px;border-color:#d9d9d9}.umb-group-builder__properties{min-height:35px;padding:0;margin:0;list-style:none}.umb-group-builder__property{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;margin-bottom:5px;border:1px solid transparent;border-radius:5px;box-sizing:border-box;-webkit-flex-flow:row;-ms-flex-flow:row;flex-flow:row}.umb-group-builder__property:hover{border:1px dashed #ccc}.umb-group-builder__property.-placeholder{cursor:pointer;background:#fff;border:1px dashed #d9d9d9;border-radius:5px;-webkit-animation:fadeIn .5s;animation:fadeIn .5s;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__property.-inherited{border:transparent;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__property.-inherited:hover{border:transparent}.umb-group-builder__property.-locked{border:transparent;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-group-builder__property.-locked:hover{border:transparent}.umb-group-builder__property.-sortable,.umb-group-builder__property.-sortable-locked{min-height:35px;border:0;border-radius:5px;-webkit-animation:none;animation:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-group-builder__property.-sortable{color:#222;cursor:move;background:#e9e9e9}.umb-group-builder__property.-sortable-locked{padding-left:30px;background:#f8f8f8}.umb-group-builder__property-meta{margin-right:20px;-webkit-flex:0 0 175px;-ms-flex:0 0 175px;flex:0 0 175px}.umb-group-builder__property-meta.-full-width{-webkit-flex:1;-ms-flex:1;flex:1}.umb-group-builder__property-meta-alias{margin-bottom:5px;font-size:10px;line-height:1.5;color:#555;word-break:break-word}.umb-group-builder__property-meta-label textarea{width:100%;min-height:25px;padding:0;margin-bottom:0;overflow:hidden;font-size:14px;font-weight:bold;color:#222;background:transparent;border-color:transparent;box-sizing:border-box;resize:none}.umb-group-builder__property-meta-label textarea.ng-invalid{border:0}.umb-group-builder__property-meta-description textarea{width:100%;min-height:25px;padding:0;margin-bottom:0;overflow:hidden;font-size:12px;line-height:1.5;color:#343434;background:transparent;border-color:transparent;box-sizing:border-box;resize:none}.umb-group-builder__property-preview{position:relative;height:30px;padding:35px 10px 25px 10px;overflow:hidden;background:url("../img/checkered-background-20.png");-webkit-flex:1;-ms-flex:1;flex:1}.umb-group-builder__property-preview:hover{cursor:pointer}.umb-group-builder__property-preview:focus{outline:0}.umb-group-builder__property-preview.-not-clickable:hover{cursor:auto}.umb-group-builder__property-preview .help-inline{display:none!important}.umb-group-builder__property-preview-label{position:absolute;top:0;left:0;z-index:15;padding:3px;font-size:11px;line-height:12px;text-transform:uppercase;background:#f8f8f8;opacity:.8}.umb-group-builder__property-actions{margin:15px 0 0 15px;text-align:center;-webkit-flex:0 0 40px;-ms-flex:0 0 40px;flex:0 0 40px}.umb-group-builder__property-action{position:relative;display:block;margin:0 0 10px 0;font-size:18px;cursor:pointer}.umb-group-builder__property-action:hover{color:#0064cd}.umb-group-builder__property-tags{position:absolute;top:0;left:0;z-index:20;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-group-builder__property-tags.-right{right:0;left:auto}.umb-group-builder__property-tag{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0 4px;margin-left:10px;font-size:11px;background-color:#d9d9d9}.umb-group-builder__property-tag:first-child{margin-left:0}.umb-group-builder__property-tag.-white{background-color:#fff}.umb-group-builder__property-tag-icon{margin-right:3px}.umb-group-builder__placeholder-box{display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:12px;font-weight:bold;color:#2e8aea;background:#e9e9e9;box-sizing:border-box;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-group-builder__placeholder-box.-input{height:10px;margin-bottom:5px}.umb-group-builder__placeholder-box.-input-small{width:25%;height:5px;margin-bottom:5px}.umb-group-builder__placeholder-box.-text-full-width{height:3px;margin-bottom:3px}.umb-group-builder__placeholder-box.-text-short{width:75%;height:3px;margin-bottom:3px}.umb-group-builder__group-sortable-placeholder{margin:0 0 70px 0;background:transparent;border:1px dashed #d9d9d9;border-radius:10px;border-radius:5px}.umb-group-builder__property_sortable-placeholder{margin-bottom:5px;background:transparent;border:1px dashed #d9d9d9;border-radius:5px}.umb-group-builder__no-data-text{padding-top:50px;font-size:16px;line-height:1.8em;color:#ccc;text-align:center}.umb-overlay .show-validation .ng-invalid-val-required-component .editor-placeholder{color:#9d261d;border-color:#9d261d}.content-type-editor-dialog.edit-property-settings .validation-wrapper{position:relative}.content-type-editor-dialog.edit-property-settings .validation-label{position:absolute;top:50%;right:0;font-size:12px;color:#9d261d;-webkit-transform:translate(0,-50%);transform:translate(0,-50%)}.content-type-editor-dialog.edit-property-settings textarea.editor-label{width:100%;padding-left:0;margin-bottom:0;font-size:16px;font-weight:bold;line-height:1.5em;border:0;border-color:transparent;box-shadow:none;box-sizing:border-box;resize:none}.content-type-editor-dialog.edit-property-settings textarea.editor-label:focus{outline:0;box-shadow:none!important}.content-type-editor-dialog.edit-property-settings .editor-placeholder{display:block;width:100%;height:80px;font-size:13px;font-weight:bold;line-height:80px;color:#555;color:#2e8aea;text-align:center;border:1px dashed #d9d9d9;border-radius:5px}.content-type-editor-dialog.edit-property-settings .editor-placeholder:hover{text-decoration:none}.content-type-editor-dialog.edit-property-settings .editor{margin-bottom:10px}.content-type-editor-dialog.edit-property-settings .editor .editor-icon-wrapper{float:left;width:60px;height:60px;margin-right:20px;line-height:60px;text-align:center;border:1px solid #d9d9d9;border-radius:5px}.content-type-editor-dialog.edit-property-settings .editor .editor-icon-wrapper .icon{font-size:26px}.content-type-editor-dialog.edit-property-settings .editor .editor-details{float:left;margin-top:10px}.content-type-editor-dialog.edit-property-settings .editor .editor-details .editor-name{display:block;font-weight:bold}.content-type-editor-dialog.edit-property-settings .editor .editor-details .editor-editor{display:block;font-size:12px}.content-type-editor-dialog.edit-property-settings .editor .editor-settings-icon{margin-top:8px;font-size:18px}.content-type-editor-dialog.edit-property-settings .checkbox{margin-bottom:20px}.content-type-editor-dialog.edit-property-settings .editor-description,.content-type-editor-dialog.edit-property-settings .editor-validation-pattern{min-width:100%;min-height:25px;overflow:hidden;border:0;box-sizing:border-box;resize:none}.content-type-editor-dialog.edit-property-settings .umb-dropdown{width:100%}.umb-list-view-settings__box{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:15px;background:#f8f8f8;border:1px solid #ccc;-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-list-view-settings__trigger{margin-bottom:20px}.umb-list-view-settings__box.-open{border-bottom:transparent}.umb-list-view-settings__content{display:-webkit-flex;display:-ms-flexbox;display:flex}.umb-list-view-settings__list-view-icon{margin-right:10px;font-size:20px;color:#d9d9d9}.umb-list-view-settings__name{float:left;margin-right:5px;font-size:14px;font-weight:bold}.umb-list-view-settings__create-new{font-size:12px;color:#2e8aea}.umb-list-view-settings__remove-new{font-size:12px;color:#9d261d}.umb-list-view-settings__settings{padding:20px;border:1px dashed #d9d9d9;border-top:0}.umb-table{display:-webkit-flex;display:-ms-flexbox;display:flex;min-width:640px;border:1px solid #d9d9d9;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-table a{text-decoration:none;cursor:pointer}.umb-table a:focus{text-decoration:none;outline:0}input.umb-table__input{margin:0 auto}.umb-table-head{font-size:11px;font-weight:600;text-transform:uppercase;background-color:#f8f8f8}.umb-table-head__link{position:relative;color:rgba(85,85,85,0.75);text-decoration:none;cursor:default}.umb-table-head__link:hover{color:rgba(85,85,85,0.75);text-decoration:none}.umb-table-head__link .sortable:hover{color:#000;text-decoration:none;cursor:pointer}.umb-table-thead__icon{position:absolute;padding-top:1px;padding-left:3px;font-size:13px;cursor:default}.umb-table-thead .sortable:hover{text-decoration:none;cursor:pointer}.umb-table-body .umb-table-row{font-size:13px;color:rgba(85,85,85,0.75);border-top:1px solid #d9d9d9}.umb-table-body .umb-table-row:hover{background-color:rgba(248,248,248,0.9)}.umb-table-body__link{text-decoration:none}.umb-table-body__link:hover{text-decoration:underline}.umb-table-body__icon{margin:0 auto;font-size:22px;color:#0064cd}.umb-table-body__checkicon{display:none;font-size:16px}.umb-table-body .umb-table__name{font-size:14px;font-weight:bold;color:#000}.umb-table-body__empty{height:100%;padding:20px 0;font-size:16px;color:#555;text-align:center}.-selected .umb-table-body__fileicon{display:none}.-selected .umb-table-body__checkicon{display:inline-block}.-content .-unpublished .umb-table__name>*{opacity:.4}.-content .-unpublished .umb-table-body__icon{opacity:.4}.-selected.-unpublished .umb-table-body__icon{opacity:1}.umb-table-row{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-table-row.-selected,.umb-table-row.-selected:hover{background-color:rgba(0,100,205,0.04)}.umb-table-cell{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:6px 2px;margin:auto 14px;overflow:hidden;text-align:left;-webkit-flex-flow:row nowrap;-ms-flex-flow:row nowrap;flex-flow:row nowrap;-webkit-flex:1 1 1%;-ms-flex:1 1 1%;flex:1 1 1%}.umb-table-cell>*{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;cursor:default}.umb-table-cell:first-of-type{padding:15px 0;margin:0 0 0 15px;-webkit-flex:0 0 25px;-ms-flex:0 0 25px;flex:0 0 25px}.umb-table__name{max-width:25%;-webkit-flex:1 1 25%;-ms-flex:1 1 25%;flex:1 1 25%}.umb_confirm-action__overlay{position:absolute;z-index:999999;display:-webkit-flex;display:-ms-flexbox;display:flex}.umb_confirm-action__overlay.-top{top:-50px;right:auto;bottom:auto;left:0;-webkit-animation:fadeInUp .2s;animation:fadeInUp .2s;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action{margin-bottom:5px}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action.-confirm{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-top .umb_confirm-action__overlay-action.-cancel{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-right{top:0;right:-50px;bottom:auto;left:auto;-webkit-animation:fadeInLeft .2s;animation:fadeInLeft .2s;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action{margin-left:5px}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action.-confirm{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-right .umb_confirm-action__overlay-action.-cancel{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-bottom{top:auto;right:auto;bottom:-50px;left:0;-webkit-animation:fadeInDown .2s;animation:fadeInDown .2s;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action{margin-top:5px}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action.-confirm{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay.-bottom .umb_confirm-action__overlay-action.-cancel{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-left{top:0;right:auto;bottom:auto;left:-50px;-webkit-animation:fadeInRight .2s;animation:fadeInRight .2s;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action{margin-right:5px}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action.-confirm{-webkit-order:1;-ms-flex-order:1;order:1}.umb_confirm-action__overlay.-left .umb_confirm-action__overlay-action.-cancel{-webkit-order:2;-ms-flex-order:2;order:2}.umb_confirm-action__overlay-action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:20px;height:20px;font-size:18px;color:#fff;border-radius:40px;box-shadow:0 2px 5px 0 rgba(0,0,0,0.26);-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb_confirm-action__overlay-action:hover{color:#fff;text-decoration:none}.umb_confirm-action__overlay-action.-confirm{color:#46a546!important;background:#fff}.umb_confirm-action__overlay-action.-confirm:hover{color:#46a546!important}.umb_confirm-action__overlay-action.-cancel{color:#9d261d!important;background:#fff}.umb_confirm-action__overlay-action.-cancel:hover{color:#9d261d!important}.umb-keyboard-shortcuts-overview__overlay{position:fixed;top:0;left:0;z-index:1000;width:100%;height:100%;padding-left:440px;background:#fff;box-sizing:border-box;-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-keyboard-shortcuts-overview__overlay-close{position:absolute;top:20px;right:20px;display:-webkit-flex;display:-ms-flexbox;display:flex;width:35px;height:35px;font-size:25px;line-height:1em;background-color:transparent;border-radius:20px;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-shortcuts-overview__overlay-close:hover{color:#fff;text-decoration:none;background-color:#2e8aea}.umb-keyboard-shortcuts-overview__overlay-content{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:100%;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-shortcuts-overview__keyboard-shortcuts-group{width:50%;margin-bottom:10px}.umb-keyboard-shortcuts-overview__keyboard-shortcuts-group-name{margin-bottom:0}.umb-keyboard-shortcuts-overview__keyboard-shortcut{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:7px 0;border-bottom:1px solid #d9d9d9;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-shortcuts-overview__keyboard-shortcut.-no-air{padding:0}.umb-keyboard-shortcuts-overview__keyboard-shortcut.-no-stroke{border-bottom:0}.umb-keyboard-shortcuts-overview__description{margin-right:10px;font-size:13px;font-weight:bold}.umb-keyboard-keys{display:-webkit-flex;display:-ms-flexbox;display:flex;font-size:12px;list-style:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-key-wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-right:5px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-keyboard-key{padding:1px 7px;margin-right:5px;color:#555;background:#fff;border:1px solid #d9d9d9;border-radius:5px}.umb-checkbox-list{margin-left:0;list-style:none}.umb-checkbox-list__item{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:2px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-checkbox-list__item:last-child{border-bottom:0}.umb-checkbox-list__item:hover{background-color:#f8f8f8}.umb-checkbox-list__item.-selected,.umb-checkbox-list__item.-disabled{font-weight:bold;background-color:rgba(0,100,205,0.04)}.umb-checkbox-list__item-checkbox{display:-webkit-flex;display:-ms-flexbox;display:flex;height:30px;margin-right:10px;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:0 0 30px;-ms-flex:0 0 30px;flex:0 0 30px}.umb-checkbox-list__item-checkbox.-selected{background:#2e8aea}.umb-checkbox-list__item-icon{margin-right:5px}.umb-checkbox-list__item-text{margin-bottom:0;font-size:13px;-webkit-flex:1 1 auto;-ms-flex:1 1 auto;flex:1 1 auto}.umb-checkbox-list__item-text.-faded{opacity:.5}.umb-checkbox-list__item.-disabled .umb-checkbox-list__item-text{cursor:not-allowed}.umb-checkbox-list__item-caption{margin-left:2px;font-size:11px}.umb-checkbox-list__no-data{padding-top:50px;font-size:16px;line-height:1.8em;color:#ccc;text-align:center}.umb-locked-field{position:relative;display:block;font-size:13px;color:#ccc}.umb-locked-field__wrapper{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:5px;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-locked-field__toggle{margin-right:3px}.umb-locked-field__toggle:hover,.umb-locked-field__toggle:focus{text-decoration:none}.umb-locked-field__lock-icon{color:#ccc;transition:color .25s}.umb-locked-field__lock-icon.-unlocked{color:#515151}input.umb-locked-field__input{padding:0;margin-bottom:0;font-size:13px;color:#ccc;background:rgba(255,255,255,0);border-color:transparent!important;transition:color .25s}input.umb-locked-field__input:focus{box-shadow:none!important}input.umb-locked-field__input.-unlocked{color:#515151}.umb-nav-tabs{position:absolute;z-index:10}.umb-nav-tabs.-padding-left{padding-left:20px}.umb-tab-content{position:relative;top:22px;padding-top:20px;border-top:1px solid #d9d9d9}.umb-load-indicator{position:absolute;top:50%;left:50%;padding:0;margin:0;font-size:0;list-style:none;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-load-indicator__bubble{position:absolute;top:50%;left:0;width:0;width:6px;height:0;height:6px;margin:0;border:2px solid #2e8aea;border-radius:100%;-webkit-transform:transformZ(0);transform:transformZ(0);-webkit-animation:umbLoadIndicatorAnimation 1.4s infinite;animation:umbLoadIndicatorAnimation 1.4s infinite}.umb-load-indicator__bubble:nth-child(1n){left:-16px;-webkit-animation-delay:0s;animation-delay:0s}.umb-load-indicator__bubble:nth-child(2n){left:0;-webkit-animation-delay:.15s;animation-delay:.15s}.umb-load-indicator__bubble:nth-child(3n){left:16px;-webkit-animation-delay:.30s;animation-delay:.30s}@-webkit-keyframes umbLoadIndicatorAnimation{0%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}50%{background:white;-webkit-transform:scale(1);transform:scale(1)}100%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}}@keyframes umbLoadIndicatorAnimation{0%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}50%{background:white;-webkit-transform:scale(1);transform:scale(1)}100%{background:#2e8aea;-webkit-transform:scale(0.5);transform:scale(0.5)}}.umb-breadcrumbs{margin-bottom:0;margin-left:0;list-style:none}.umb-breadcrumbs__ancestor{display:inline-block}.umb-breadcrumbs__ancestor-link,.umb-breadcrumbs__ancestor-text{font-size:11px;color:#555}.umb-breadcrumbs__ancestor-link{text-decoration:underline}.umb-breadcrumbs__ancestor-link:hover{color:#000}.umb-breadcrumbs__seperator{position:relative;top:1px;margin-right:5px;margin-left:5px;color:#ccc}.umb-media-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin-bottom:30px;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-media-grid__item{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;margin:10px;overflow:hidden;cursor:pointer;box-shadow:0 1px 1px 0 rgba(0,0,0,0.2);transition:box-shadow 100ms;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-align-self:stretch;-ms-flex-item-align:stretch;align-self:stretch}.umb-media-grid__item.-file{background-color:#f8f8f8}.umb-media-grid__item.-selected{box-shadow:0 2px 8px 0 rgba(0,0,0,0.35)}.umb-media-grid__item:hover{text-decoration:none}.umb-media-grid__item-image{position:relative;height:auto;max-width:100%!important}.umb-media-grid__item-image-placeholder{position:relative;width:100%;height:auto;max-width:100%}.umb-media-grid__image-background{position:absolute;top:0;right:0;bottom:0;left:0;background:url("../img/checkered-background.png");background-repeat:repeat;content:"";opacity:.5}.umb-media-grid__item-overlay{position:absolute;right:0;bottom:0;left:0;z-index:100;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px 10px;overflow:hidden;font-size:11px;color:white;white-space:nowrap;background:#2e8aea;opacity:0;box-sizing:border-box;transition:opacity 150ms}.umb-media-grid__item.-file .umb-media-grid__item-overlay{color:#555;background:#f8f8f8;opacity:1}.umb-media-grid__item.-file:hover .umb-media-grid__item-overlay,.umb-media-grid__item.-file.-selected .umb-media-grid__item-overlay{color:#fff;background:#2e8aea}.umb-media-grid__info{margin-right:5px}.umb-media-grid__item-overlay.-locked{opacity:1}.umb-media-grid__item:hover .umb-media-grid__item-overlay{opacity:1}.umb-media-grid__item-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.umb-media-grid__item-icon{position:absolute;top:45%;left:50%;font-size:40px!important;color:#555;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-media-grid__checkmark{position:absolute;top:10px;right:10px;z-index:2;display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:7px;color:#fff;background:#2e8aea;border:1px solid #fff;border-radius:50px;box-sizing:border-box;transition:background 100ms;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;margin-bottom:30px;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-folder-grid__folder{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px 20px;margin:5px;cursor:pointer;background:#f8f8f8;border:1px solid transparent;box-sizing:border-box;transition:border .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex:1 1 200px;-ms-flex:1 1 200px;flex:1 1 200px;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.umb-folder-grid__folder:focus,.umb-folder-grid__folder:active{text-decoration:none}.umb-folder-grid__folder:hover{text-decoration:none;background-color:#ebebeb}.umb-folder-grid__folder-description{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid__folder-icon{margin-right:15px;font-size:20px;color:#000}.umb-folder-grid__folder-name{font-size:13px;font-weight:bold;color:#000}.umb-folder-grid__folder-name:hover{text-decoration:underline}.umb-folder-grid__action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:7px;color:#fff;cursor:pointer;background:rgba(0,0,0,0.4);border:1px solid #fff;border-radius:50px;opacity:0;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-folder-grid__action.-selected{background:#2e8aea;opacity:1}.umb-content-grid{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-content-grid__item{position:relative;margin:10px;cursor:pointer;background:#f8f8f8;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-flex:0 1 200px;-ms-flex:0 1 200px;flex:0 1 200px}.umb-content-grid__item.-selected{box-shadow:0 2px 8px 0 rgba(0,0,0,0.35)}.umb-content-grid__icon-container{display:-webkit-flex;display:-ms-flexbox;display:flex;height:75px;background:#ebebeb;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-content-grid__icon{font-size:40px;color:#888}.umb-content-grid__icon.-light{color:#d9d9d9}.umb-content-grid__content{padding:15px;box-sizing:border-box}.umb-content-grid__item-name{display:inline-block;padding-bottom:10px;margin-bottom:10px;font-weight:bold;line-height:1.4em;color:black;border-bottom:1px solid #d9d9d9}.umb-content-grid__item-name:hover{text-decoration:underline}.umb-content-grid__item-name.-light{color:#d9d9d9}.umb-content-grid__details-list{margin-bottom:0;margin-left:0;font-size:11px;list-style:none}.umb-content-grid__details-list.-light{color:#d9d9d9}.umb-content-grid__details-label{display:inline-block;font-weight:bold}.umb-content-grid__details-value{display:inline-block}.umb-content-grid__checkmark{position:absolute;top:10px;right:10px;display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;color:#fff;cursor:pointer;background:#2e8aea;border:1px solid #fff;border-radius:50px;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-content-grid__item:hover .umb-content-grid__action:not(.-selected){opacity:1;-webkit-animation:fadeIn;animation:fadeIn;-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}.umb-content-grid__no-items{padding-top:50px;padding-bottom:50px;font-size:16px;font-weight:bold;color:#d9d9d9}.umb-layout-selector{position:relative;display:inline-block}.umb-layout-selector__active-layout{display:-webkit-flex;display:-ms-flexbox;display:flex;width:30px;height:30px;font-size:20px;cursor:pointer;border:1px solid transparent;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-layout-selector__active-layout:hover{border-color:#d9d9d9}.umb-layout-selector__dropdown{position:absolute;left:50%;z-index:999;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;background:#333;background:#fff;-webkit-transform:translate(-50%,0);transform:translate(-50%,0);-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-layout-selector__dropdown-item{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:5px;margin:3px 5px;cursor:pointer;border:1px solid transparent;-webkit-align-content:center;-ms-flex-line-pack:center;align-content:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.umb-layout-selector__dropdown-item:hover{border:1px solid #d9d9d9}.umb-layout-selector__dropdown-item.-active{border:1px solid #2e8aea}.umb-layout-selector__dropdown-item-icon{font-size:20px;color:#555;text-align:center}.umb-tooltip{position:fixed;z-index:1000;display:-webkit-flex;display:-ms-flexbox;display:flex;max-width:200px;padding:10px;margin-top:15px;font-size:12px;background:#fff;-webkit-animation:fadeIn;animation:fadeIn;-webkit-animation-duration:.1s;animation-duration:.1s;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}.umb-tooltip-list{padding:10px;margin-bottom:0;margin-left:0;list-style:none}.umb-tooltip-list__item{margin-bottom:5px}.umb-tooltip-list__item:last-child{margin-bottom:0}.umb-tooltip-list__item-label{margin-bottom:-3px;font-weight:bold}.umb-overlay-backdrop{position:fixed;top:0;left:0;z-index:2000;width:100%;height:100%;background-color:rgba(255,255,255,0.5);-webkit-animation:fadeIn .2s;animation:fadeIn .2s}.umb-grid IFRAME{overflow:hidden}.umb-grid .ui-sortable-helper{position:absolute!important;width:42px!important;height:42px!important;padding:5px;overflow:hidden;font-family:"icomoon";text-align:center;background-color:#2e8aea!important;border-radius:2px;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.umb-grid .ui-sortable-helper:after{font-size:22px;line-height:42px;color:#fff;content:"\e126"}.umb-grid .ui-sortable-helper *{display:none}.umb-grid .ui-sortable-helper .umb-row-title-bar,.umb-grid .ui-sortable-helper .cell-tools-add{display:none!important}.umb-grid .ui-sortable-placeholder{position:absolute;right:0;left:0;height:2px;margin-bottom:20px;background-color:#2e8aea}.umb-grid .ui-sortable-placeholder:before,.umb-grid .ui-sortable-placeholder:after{position:absolute;top:-9px;font-family:"icomoon";font-size:18px;color:#2e8aea}.umb-grid .ui-sortable-placeholder:before{left:-5px;content:"\e0e9"}.umb-grid .ui-sortable-placeholder:after{right:-5px;content:"\e0d7"}.umb-grid .umb-cell .ui-sortable-placeholder{right:10px;left:10px}.umb-grid-width{width:100%;margin:20px auto}.umb-grid .right{float:right}.umb-grid .tb{width:100%}.umb-grid .td{display:inline-block;width:100%;vertical-align:top;box-sizing:border-box}.umb-grid .middle{text-align:center}.umb-grid .mainTd{position:relative}.umb-grid .umb-column{position:relative}.umb-grid .umb-row{position:relative;padding-top:10px;margin-bottom:40px}.umb-grid .row-tools a{text-decoration:none}.umb-grid .umb-cell{position:relative}.umb-grid .umb-cell-content{position:relative;display:block;margin:10px;border:1px solid transparent;box-sizing:border-box}.umb-grid .umb-row .umb-cell-placeholder{min-height:130px;background-color:#f8f8f8;border-color:#d9d9d9;border-style:dashed;border-width:2px;transition:border-color 100ms linear}.umb-grid .umb-row .umb-cell-placeholder:hover{cursor:pointer;border-color:#2e8aea}.umb-grid .umb-cell-content.-has-editors{padding-top:38px;background-color:#fff;border-color:#d9d9d9;border-style:solid;border-width:1px}.umb-grid .umb-cell-content.-has-editors:hover{cursor:auto}.umb-grid .umb-cell-content.-has-editors.-collapsed{padding-top:10px}.umb-grid .cell-tools{position:absolute;top:10px;right:10px;font-size:16px;color:#555}.umb-grid .cell-tool{float:right;cursor:pointer}.umb-grid .cell-tool:hover{color:#0064cd}.umb-grid .cell-tools-add{color:#2e8aea}.umb-grid .cell-tools-add:focus,.umb-grid .cell-tools-add:hover{text-decoration:none}.umb-grid .cell-tools-add.-center{position:absolute;top:50%;left:50%;color:#2e8aea;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-grid .cell-tools-add.-bar{display:block;padding:5px;margin:10px;text-align:center;background:#f8f8f8;border:1px dashed #ccc}.umb-grid .cell-tools-remove{position:relative;display:inline-block}.umb-grid .cell-tools-remove .iconBox:hover,.umb-grid .cell-tools-remove .iconBox:hover *{background:#9d261d!important;border-color:#9d261d!important}.umb-grid .cell-tools-move{display:inline-block}.umb-grid .cell-tools-edit{display:inline-block;color:#fff}.umb-grid .drop-overlay{position:absolute;top:0;left:0;z-index:10;display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:100%;font-size:14px;font-weight:bold;line-height:1.3em;text-align:center;background:#fff;opacity:.9;box-sizing:border-box;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}.drop-overlay.-disable{color:#9d261d}.drop-overlay.-allow{color:#46a546}.umb-grid .drop-overlay .drop-icon{margin-bottom:20px;font-size:40px}.umb-grid .umb-control{position:relative;display:block;margin-right:10px;margin-bottom:10px;margin-left:10px;overflow:hidden}.umb-control-collapsed{padding:5px 10px;cursor:move;background-color:#f8f8f8;border-color:transparent;border-style:solid;border-width:1px}.umb-control-collapsed:hover{border-color:#2e8aea}.umb-grid .umb-control-click-overlay{position:absolute;top:0;left:0;z-index:5;width:100%;height:100%;cursor:pointer;opacity:0}.umb-grid .umb-control-click-overlay:hover{background-color:#2e8aea;opacity:.1;transition:opacity .1s}.umb-grid .umb-row-title-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;padding-right:10px;padding-left:10px}.umb-grid .umb-row-title{display:inline-block;margin-right:6px;font-size:15px;font-weight:bold;color:#000;cursor:pointer}.umb-grid .row-tools{display:inline-block;margin-left:10px;font-size:18px;color:#555}.umb-grid .row-tool{cursor:pointer}.umb-grid .umb-add-row{text-align:center}.umb-grid .umb-control-placeholder{position:relative;min-height:20px;text-align:center;text-align:-moz-center;cursor:text}.umb-grid .umb-control-placeholder .placeholder{height:20px;padding:5px;font-size:14px;text-align:left;border:1px solid rgba(182,182,182,0.3);opacity:.7}.umb-grid .umb-control-placeholder:hover .placeholder{border:1px solid rgba(182,182,182,0.8)}.umb-grid .umb-editor-placeholder{position:relative;min-height:65px;padding:20px;padding-bottom:30px;text-align:center;text-align:-moz-center;cursor:pointer;background-color:#fff;border:4px dashed #d9d9d9}.umb-grid .umb-editor-placeholder i{display:block;margin-bottom:10px;font-size:85px;line-height:85px;color:#d9d9d9}.umb-grid .umb-row.-active{background-color:#2e8aea}.umb-grid .umb-row.-active .umb-row-title-bar{cursor:move}.umb-grid .umb-row.-active .row-tool,.umb-grid .umb-row.-active .umb-row-title{color:#fff}.umb-grid .umb-row.-active .umb-grid-has-config{color:rgba(255,255,255,0.66)}.umb-grid .umb-row.-active .umb-cell .umb-grid-has-config{color:rgba(0,0,0,0.44)}.umb-grid .umb-row.-active .umb-cell .umb-cell-content{border-color:transparent}.umb-grid .umb-row.-active-child{background-color:#f8f8f8}.umb-grid .umb-row.-active-child .umb-row-title-bar{cursor:default}.umb-grid .umb-row.-active-child .umb-row-title{color:#555}.umb-grid .umb-row.-active-child .row-tool{color:rgba(0,0,0,0.23)}.umb-grid .umb-row.-active-child .umb-grid-has-config{color:rgba(0,0,0,0.44)}.umb-grid .umb-row.-active-child .umb-cell-content.-placeholder{border-color:#d9d9d9}.umb-grid .umb-row.-active-child .umb-cell-content.-placeholder:hover{border-color:rgba(85,85,85,0.44)}.umb-grid .umb-row .umb-cell.-active{border-color:#d9d9d9}.umb-grid .umb-row .umb-cell.-active .umb-cell-content.-has-editors{border-color:#2e8aea;box-shadow:3px 3px 6px rgba(0,0,0,0.07)}.umb-grid .umb-row .umb-cell.-active-child .cell-tool{color:rgba(0,0,0,0.23)}.umb-grid .umb-row .umb-cell.-active-child .umb-cell-content.-has-editors{border-color:rgba(113,136,160,0.44)}.umb-grid .umb-row-title-bar{display:-webkit-flex;display:-ms-flexbox;display:flex}.umb-grid .umb-grid-right{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center}.umb-grid .umb-tools{margin-left:auto}.umb-grid-add-more-content{text-align:center}.umb-grid .newbtn{display:inline-block;width:auto;padding:6px 15px;margin:10px auto 20px;font-size:12px;font-weight:bold;border-color:#e2e2e2;border-style:solid}.umb-grid .newbtn:hover{cursor:pointer;opacity:.77}.umb-grid textarea.textstring{display:block;overflow:hidden;color:#555;background:#fff;border:0;outline:0;resize:none}.umb-grid .umb-cell-rte textarea{display:none!important}.umb-grid .umb-cell-media .caption{display:block;width:98%;overflow:hidden;font-style:italic;background:#fff;border:0;outline:0;resize:none}.umb-grid .cellPanelRte{min-height:60px}.umb-grid .umb-cell-embed iframe{width:100%}.umb-grid .umb-cell-rte{border-color:transparent}.umb-grid .iconBox{display:inline-block;padding:4px 6px;margin:2px;cursor:pointer;background:#fff;border:1px solid #b6b6b6;border-radius:200px}.umb-grid .iconBox:hover,.umb-grid .iconBox:hover *{color:white!important;text-decoration:none;background:#2e8aea!important;border-color:#2e8aea!important}.umb-grid .iconBox span.prompt{display:block;text-align:center;white-space:nowrap}.umb-grid .iconBox span.prompt>a{text-decoration:underline}.umb-grid .iconBox a:hover{color:white!important;text-decoration:none}.umb-grid .iconBox.selected{background:transparent;background-image:linear-gradient(to bottom,#e6e6e6,#bfbfbf);background-repeat:repeat-x;border-color:#bfbfbf #bfbfbf #999;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-radius:3px;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe6e6e6',endColorstr='#ffbfbfbf',GradientType=0);zoom:1;box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-webkit-appearance:none}.umb-grid .iconBox i{display:block;font-size:16px!important;color:#5f5f5f}.umb-grid .help-text{display:inline-block;clear:both;font-size:14px;font-weight:bold;color:#000}.umb-grid .mce-panel{clear:both;background:transparent!important;border:none!important}.umb-grid .mce-btn button{padding:8px 6px;line-height:inherit}.umb-grid .mce-toolbar{display:none;background-color:#fafafa;border-bottom:1px solid rgba(207,207,207,0.7)}.umb-grid .umb-control.-active .mce-toolbar{display:block}.umb-grid .mce-flow-layout-item{margin:0}.umb-grid .mceContentBody{overflow-y:hidden!important}.umb-grid .fullSizeImage{width:100%}.umb-grid .boxWidth{margin-bottom:10px;text-align:right}.umb-grid .boxWidth input{width:40px;text-align:center}.umb-grid .boxWidth label{padding:0;margin:5px 5px 0 0;font-size:10px;color:#808080}.umb-grid .umb-control{border-color:transparent;border-style:solid;border-width:1px}.umb-grid .umb-control.-active{border-color:#2e8aea}.umb-grid .umb-templates-columns{margin-top:30px}.umb-grid .umb-control-inner{position:relative}.umb-grid .umb-control-bar{display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;padding:2px 5px;font-size:10px;color:#fff;background:#2e8aea;opacity:0;transition:height 80ms linear,opacity 80ms linear;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-grid .umb-control-title{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-grid .umb-control.-active .umb-control-bar{height:25px;cursor:move;opacity:1}.umb-grid .umb-control-tools{display:inline-block;margin-left:10px}.umb-grid .umb-control-tool{position:relative;display:inline-block;margin-right:5px;font-size:16px;cursor:pointer}.umb-grid .umb-templates{width:100%;overflow:hidden;text-align:center}.umb-grid .umb-templates-template{display:inline-block;width:100px;padding-right:30px;margin:20px}.umb-grid .umb-templates-template a.tb:hover{border:5px solid #2e8aea}.umb-grid .umb-templates-template .tb{position:relative;width:100%;height:150px;padding:10px;cursor:pointer;background-color:#f8f8f8;border:5px solid #d9d9d9}.umb-grid .umb-templates-template .tr{position:relative;height:100%}.umb-grid .umb-templates-template .tb .umb-templates-column{height:100%;border:1px dashed #d9d9d9;border-right:0}.umb-grid .umb-templates-template .tb .umb-templates-column.last{border-right:1px dashed #d9d9d9!important}.umb-grid a.umb-templates-column:hover,.umb-grid a.umb-templates-column.selected{background-color:#2e8aea}.umb-grid .templates-preview{display:inline-block;width:100%;text-align:center}.umb-grid .templates-preview small{position:absolute;bottom:-25px;left:0;width:100%;padding-top:15px}.umb-grid .templates-preview .help-text{margin:35px 35px 0 0}.umb-grid .preview-rows{position:relative;display:inline-block;width:125px;margin:15px;border:3px solid #d9d9d9;box-sizing:border-box;transition:border 100ms linear}.umb-grid .preview-rows.prevalues-rows{float:left;width:80px;margin:0 20px 20px 0}.umb-grid .preview-rows.prevalues-templates{float:left;margin:0 20px 20px 0}.umb-grid .preview-rows:hover{cursor:pointer;border-color:#2e8aea}.umb-grid .preview-rows .preview-row{display:inline-block;width:100%;vertical-align:bottom}.umb-grid .preview-rows.layout{padding:2px}.umb-grid .preview-rows.layout .preview-row{height:100%}.umb-grid .preview-rows.layout .preview-col{height:180px}.umb-grid .preview-rows.layout .preview-cell{background-color:#f8f8f8}.umb-grid .preview-rows.layout .preview-overlay{display:none}.umb-grid .preview-rows.columns{min-height:16px;padding:1px;line-height:11px}.umb-grid .preview-rows.columns.prevalues-rows{min-height:30px}.umb-grid .preview-rows .preview-col{display:block;float:left;width:33.3%;height:10px;margin:0;border:1px solid #fff;box-sizing:border-box}.umb-grid .preview-rows .preview-col .preview-cell{display:block;width:100%;height:100%;margin:0 1px 1px 0;background-color:#d9d9d9}.umb-grid .preview-rows.prevalues-templates .preview-col{height:80px}.umb-grid .preview-overlay{position:absolute;top:0;left:0;display:block;width:100%;height:100%;border:3px solid white;box-sizing:border-box}.umb-grid .umb-grid-has-config{display:inline;font-size:12px;color:rgba(0,0,0,0.44)}.umb-grid .umb-cell .umb-grid-has-config{position:absolute;top:10px;left:10px}.umb-grid .cell-tools-menu{position:absolute;top:0;left:50%;z-index:6660;width:360px;height:380px;padding:7px;margin-top:-270px;margin-left:-150px;overflow:auto;background:#fff;border:1px solid #ccc;box-shadow:3px 3px 12px 0 rgba(50,50,50,0.45)}.umb-grid .cell-tools-menu h5{padding:10px;margin-top:0;color:#999;border-bottom:1px solid #d9d9d9}.umb-grid .elements{display:block;padding:0;margin:0}.umb-grid .elements li{display:inline-block;width:90px;height:80px;padding:5px;margin:5px;overflow:hidden;font-size:11px}.umb-grid .elements li:hover,.umb-grid .elements li:hover *{color:#fff;background:#2e8aea}.umb-grid .elements a{color:#222;text-decoration:none}.umb-grid .elements i{display:block;font-size:30px;line-height:50px;color:#999}.umb-grid-configuration .umb-templates{text-align:left}.umb-grid-configuration ul{display:block}.umb-grid-configuration ul li{display:block;width:auto;text-align:left}.umb-grid-configuration .umb-templates .umb-templates-template .tb{max-height:50px;padding:0;overflow:hidden;border-spacing:2px;border-width:2px!important}.umb-grid-configuration .umb-templates .umb-templates-template span{display:inline-block;background:#d9d9d9}.umb-grid-configuration .umb-templates .umb-templates-template .tb:hover{border-width:2px!important}.umb-grid-configuration .umb-templates-column{display:block;float:left;margin-left:-1px;background:#d9d9d9;border:1px white solid!important}.umb-grid-configuration .umb-templates-column.last{margin-right:-1px}.umb-grid-configuration .umb-templates-column.add{font-size:20px;line-height:70px;color:#ccc;text-align:center;text-decoration:none;background:#fff}.umb-grid-configuration .mainTdpt{height:initial;border:0}.umb-grid-configuration .umb-templates-rows .umb-templates-row{width:60px;margin:0 50px 20px 0}.umb-grid-configuration .umb-templates-rows .umb-templates-row .tb{padding:0;border-spacing:2px;border-width:2px!important}.umb-grid-configuration .umb-templates-rows .mainTdpt{height:10px!important}.umb-grid-configuration a.umb-templates-column{height:70px!important}.umb-empty-state{font-size:16.099999999999998px;line-height:1.8em;color:#999;text-align:center}.umb-empty-state.-small{font-size:11.9px}.umb-empty-state.-large{font-size:17.5px}.umb-empty-state.-center{position:absolute;top:50%;left:50%;width:80%;max-width:400px;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-property-editor.-not-clickable{pointer-events:none}.umb-iconpicker{display:-webkit-flex;display:-ms-flexbox;display:flex;margin:0;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-iconpicker-item{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-bottom:0;overflow:hidden;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex:0 0 14.28%;-ms-flex:0 0 14.28%;flex:0 0 14.28%;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-iconpicker-item a{display:-webkit-flex;display:-ms-flexbox;display:flex;width:100%;height:100%;padding:15px 0;text-decoration:none;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-iconpicker-item a:hover,.umb-iconpicker-item a:focus{background:#f5f5f5;outline:0}.umb-iconpicker-item a:active{background:#eee}.umb-iconpicker-item i{font-size:30px}.umb-button{position:relative;overflow:hidden}.umb-button__button:focus{outline:0}.umb-button__content{display:-webkit-flex;display:-ms-flexbox;display:flex;opacity:1;transition:opacity .25s ease;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-button__icon{margin-right:5px}.umb-button__content.-hidden{opacity:0}.umb-button__progress{position:absolute;top:50%;left:50%;z-index:100;width:14px;height:14px;margin-top:-9px;margin-left:-9px;border:2px solid #ccc;border-left-color:#46a546;border-radius:40px;opacity:1;-webkit-animation:rotating .4s linear infinite;animation:rotating .4s linear infinite;transition:opacity .25s ease}.umb-button__progress.-hidden{z-index:0;opacity:0}.umb-button__progress.-white{border-color:rgba(255,255,255,0.4);border-left-color:#fff}.umb-button__success,.umb-button__error{position:absolute;top:50%;left:50%;z-index:10;font-size:20px;color:#46a546;opacity:1;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);transition:opacity .25s ease}.umb-button__success{color:#46a546}.umb-button__error{color:#9d261d}.umb-button__success.-hidden,.umb-button__error.-hidden{z-index:0;opacity:0}.umb-button__success.-white,.umb-button__error.-white{color:#fff}.umb-button__overlay{position:absolute;z-index:10;width:100%;height:100%;background:#fff;opacity:0}@-webkit-keyframes rotating{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes rotating{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.umb-button-group__toggle{padding-right:8px;padding-left:8px;margin-left:-1px}.umb-button-group__sub-buttons.-align-right{right:0;left:auto}.umb-notifications{position:absolute;right:0;bottom:52px;left:0;z-index:1000;padding:0;margin:auto;background:0;border:0;border-bottom:0;border-radius:0}.umb-notifications__notifications{position:relative;margin:0;list-style:none}.umb-notifications__notification{position:relative;padding:5px 20px;margin-bottom:0;font-size:12px;text-shadow:none;border:0;border-radius:0}.umb-notifications__notification.-extra-padding{padding-top:20px;padding-bottom:20px}.umb-file-dropzone-directive .dropzone{position:relative;width:100%;height:400px;padding:50px 0;margin:0 0 20px 0;color:#555;text-align:center;border:1px dashed #d9d9d9;transition:height .8s}.umb-file-dropzone-directive .dropzone .illustration{width:300px}.umb-file-dropzone-directive .dropzone.is-small{height:100px}.umb-file-dropzone-directive .dropzone.is-small .illustration{width:200px}.umb-file-dropzone-directive .dropzone.drag-over{border:1px dashed #343434}.umb-file-dropzone-directive .content{position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.umb-file-dropzone-directive .file-select{margin-top:10px;font-size:15px;color:#2e8aea;cursor:pointer}.umb-file-dropzone-directive .file-select:hover{color:#0064cd;text-decoration:underline}.umb-file-dropzone-directive .file-list{padding:10px 20px;margin:0 0 30px 0;list-style:none;background:#f8f8f8}.umb-file-dropzone-directive .file-list .file{position:relative;display:block;width:100%;padding:5px 0;border-top:1px solid #d9d9d9}.umb-file-dropzone-directive .file-list .file:first-child{border-top:0}.umb-file-dropzone-directive .file-list .file.ng-enter{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-list .file.ng-leave{-webkit-animation:fadeOut 2s;animation:fadeOut 2s}.umb-file-dropzone-directive .file-list .file .file-description{display:block;width:100%;font-size:12px;color:#222}.umb-file-dropzone-directive .file-list .file .file-upload-progress{display:block;width:100%}.umb-file-dropzone-directive .file-list .file .file-icon{position:absolute;right:0;bottom:0}.umb-file-dropzone-directive .file-list .file .file-icon .icon{font-size:20px}.umb-file-dropzone-directive .file-list .file .file-icon .icon.ng-enter{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-list .file .file-icon .icon.ng-leave{-webkit-animation:fadeIn .5s;animation:fadeIn .5s}.umb-file-dropzone-directive .file-progress{position:relative;height:4px;padding:2px}.umb-file-dropzone-directive .file-progress .file-progress-indicator{position:relative;display:block;width:0;height:100%;overflow:hidden;background-color:#2e8aea;border-radius:20px}.login-overlay{position:absolute;top:0;left:0;z-index:10000;width:100%;height:100%;padding:0;margin:0!important;color:#fff;background:#1d1d1d url(../img/application/logo.png) no-repeat 25px 30px fixed!important;background-size:30px 30px!important;border-radius:0}@media only screen and (-webkit-min-device-pixel-ratio:1.5),only screen and (min-resolution:144dpi){.login-overlay{background-image:url(../img/application/logo@2x.png)!important}}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-resolution:192dpi){.login-overlay{background-image:url(../img/application/logo@2x.png)!important}}@media only screen and (-webkit-min-device-pixel-ratio:3),only screen and (min-resolution:3dppx),only screen and (-webkit-min-device-pixel-ratio:3.6458333333333335),only screen and (min-resolution:350dpi){.login-overlay{background-image:url(../img/application/logo@3x.png)!important}}.login-overlay .umb-modalcolumn{background:0;border:0}.login-overlay .form{position:fixed;top:100px;left:165px;display:block;width:370px;text-align:right}.login-overlay h1{display:block;font-size:18px;font-weight:normal;color:#fff;text-align:right}.login-overlay .alert.alert-error{display:inline-block;padding-right:6px;padding-left:6px;margin-top:10px;text-align:center}@media(max-width:565px){.login-overlay .form{right:25px;left:inherit}}#hrOr{position:relative;height:30px;padding-top:20px;text-align:center}#hrOr hr{height:1px;margin:0;background-color:#555;border:0}#hrOr div{position:relative;top:-16px;width:20px;height:20px;padding:4px;margin:auto;color:#d9d9d9;background-color:black;border:1px solid #555;border-radius:50%}.umb-editor{min-width:66.6%}.umb-editor-tiny{width:50px}.umb-editor-small{width:90px}.umb-modal .umb-editor{width:95%}.umb-dialog .umb-editor{width:95%}.umb-dialog .umb-control-group .help-block{width:95%}.umb-codeeditor{width:99%}.umb-contentpicker li a:hover .hover-hide,.umb-contentpicker li a .hover-show{display:none}.umb-contentpicker li a:hover .hover-show{display:inline-block}.umb-contentpicker-popover .search-holder{padding:10px}.umb-contentpicker small:not(:last-child){padding-right:3px;border-right:1px solid #999}.umb-contentpicker small a{color:#555}div.umb-codeeditor{border:1px solid #e8e8e8}div.umb-codeeditor .umb-el-wrap{padding:0}div.umb-codeeditor .umb-btn-toolbar{padding:0;margin:0;background:#f7f7f7;border-bottom:#e8e8e8 1px solid}.mce-tinymce{border:1px solid #d9d9d9!important;border-radius:0!important}.mce-panel{background:#f8f8f8!important;border-color:#d9d9d9!important}.mce-btn-group,.mce-btn{background:none!important;border:none!important}.mce-ico{font-size:12px!important;color:#1d1d1d!important}.mce-ico.mce-i-custom[class^="icon-"],.mce-ico.mce-i-custom[class*=" icon-"]{font-family:icomoon;font-size:16px!important}.rte-editor-preval .control-group .controls>div>label .mce-ico{line-height:20px}ul.color-picker li{width:60px;padding:2px;margin:3px;border:2px solid transparent}ul.color-picker li.active{border:2px dashed #d9d9d9}ul.color-picker li a{display:block;height:50px;cursor:pointer}.control-group.color-picker-preval .thumbnail{width:30px;border:0}.control-group.color-picker-preval pre{display:inline;margin-right:20px;margin-left:10px}.control-group.color-picker-preval label{padding:6px;border:solid white 1px}.umb-mediapicker .add-link{display:inline-block;width:120px;height:120px;line-height:120px;color:#d9d9d9;text-align:center;text-decoration:none;border:2px #d9d9d9 dashed;transition:all 150ms ease-in-out}.umb-mediapicker .add-link:hover{color:#2e8aea;border-color:#2e8aea}.umb-mediapicker .picked-image{position:absolute;right:10px;bottom:10px;display:none;width:32px;height:32px;overflow:hidden;font-size:24px;line-height:36px;color:red;text-align:center;text-decoration:none;background:white;border-radius:15px;opacity:.5}.umb-thumbnails{position:relative}.umb-thumbnails i{margin:auto}.umb-thumbnails a{border:none!important;outline:0;box-shadow:none!important}.umb-sortable-thumbnails{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:0;margin:0;list-style-type:none;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-sortable-thumbnails li{position:relative;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:2px;margin:5px;background:white;border:1px solid #f8f8f8;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.umb-sortable-thumbnails li:hover a{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-sortable-thumbnails li img{display:block;max-width:100%;max-height:100%;margin:auto;background-image:url(../img/checkered-background.png)}.umb-sortable-thumbnails li img.noScale{max-width:none!important;max-height:none!important}.umb-sortable-thumbnails .icon-holder{text-align:center}.umb-sortable-thumbnails .icon-holder .icon{display:block;font-size:40px;line-height:50px;color:#555}.umb-sortable-thumbnails .umb-sortable-thumbnails__actions{position:absolute;right:10px;bottom:10px;display:-webkit-flex;display:-ms-flexbox;display:flex;text-decoration:none;opacity:0;visibility:hidden;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions{opacity:1;visibility:visible}.umb-sortable-thumbnails .umb-sortable-thumbnails__action{display:-webkit-flex;display:-ms-flexbox;display:flex;width:25px;height:25px;margin-left:5px;font-size:16px;color:#222;background:white;border-radius:15px;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.umb-sortable-thumbnails .umb-sortable-thumbnails__action.-red{color:red}.umb-sortable-thumbnails .umb-sortable-thumbnails__action:hover{text-decoration:none}.umb-cropper{position:relative}.umb-cropper img,.umb-cropper-gravity img{position:relative;top:0;left:0;height:auto;max-width:100%}.umb-cropper img{max-width:initial}.umb-cropper .overlay,.umb-cropper-gravity .overlay{position:absolute;top:0;left:0;z-index:6001;cursor:move}.umb-cropper .viewport{position:relative;height:auto;max-width:100%;margin:auto;overflow:hidden}.umb-cropper-gravity .viewport{position:relative;width:100%;height:100%;overflow:hidden}.umb-cropper .viewport:after{position:absolute;top:0;left:0;z-index:5999;width:100%;height:100%;content:"";-moz-opacity:.75;opacity:.75;filter:alpha(opacity=7);box-shadow:inset 0 0 0 20px #fff,inset 0 0 0 21px rgba(0,0,0,0.1),inset 0 0 20px 21px rgba(0,0,0,0.2)}.umb-cropper-gravity .overlay{width:14px;height:14px;text-align:center;background:#2e8aea;border:3px solid white;border-radius:20px;opacity:.8}.umb-cropper-gravity .overlay i{font-size:26px;line-height:26px;opacity:.8!important}.umb-cropper .crop-container{text-align:center}.umb-cropper .crop-slider{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:10px;margin-top:10px;border-top:1px solid #f8f8f8;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}@media(min-width:769px){.umb-cropper .crop-slider{padding:10px 50px 10px 50px}}.umb-cropper .crop-slider i{padding:0 5px;color:#555;box-sizing:border-box;-webkit-flex:0 0 25px;-ms-flex:0 0 25px;flex:0 0 25px}.umb-cropper .crop-slider i:first-of-type{text-align:right}.umb-cropper .crop-slider input{-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.umb-cropper-gravity .viewport,.umb-cropper-gravity,.umb-cropper-imageholder{display:inline-block}.umb-cropper-imageholder{float:left}.cropList{position:relative;display:inline-block;vertical-align:top}.gravity-container .viewport{max-width:600px}.gravity-container .viewport:hover{cursor:pointer}.imagecropper{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row}@media(max-width:768px){.imagecropper{float:left;max-width:100%;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column}}.imagecropper .umb-cropper__container{position:relative;max-width:100%;margin-bottom:10px;border:1px solid #f8f8f8}@media(min-width:769px){.imagecropper .umb-cropper__container{width:600px}}.umb-close-cropper{position:absolute;top:3px;right:3px;cursor:pointer}.umb-close-cropper:hover{background:#f8f8f8;opacity:.9}.imagecropper .umb-sortable-thumbnails{display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;-ms-flex-wrap:wrap;flex-wrap:wrap}.imagecropper .umb-sortable-thumbnails li{display:-webkit-flex;display:-ms-flexbox;display:flex;padding:8px;margin-top:0;-webkit-flex-direction:column;-ms-flex-direction:column;flex-direction:column;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.imagecropper .umb-sortable-thumbnails li.current{color:#000;cursor:pointer;background:#f8f8f8;border-color:#d9d9d9}.imagecropper .umb-sortable-thumbnails li:hover,.imagecropper .umb-sortable-thumbnails li.current:hover{color:#000;cursor:pointer;background:#f8f8f8;border-color:#d9d9d9;opacity:.95}.imagecropper .umb-sortable-thumbnails li .crop-name,.imagecropper .umb-sortable-thumbnails li .crop-size{display:block;font-size:13px;line-height:1;text-align:left}.imagecropper .umb-sortable-thumbnails li .crop-name{margin:10px 0 5px;font-weight:bold}.imagecropper .umb-sortable-thumbnails li .crop-size{margin:0 0 5px;font-size:10px;font-style:italic}.btn-crop-delete{display:block;text-align:left}.umb-folderbrowser .add-link{display:inline-block;width:120px;height:120px;line-height:120px;text-align:center;border:1px #d9d9d9 dashed}.umb-upload-drop-zone{margin-bottom:5px}.umb-upload-drop-zone .info,.umb-upload-button-big{display:block;padding:20px;font-size:14px;color:#d9d9d9;text-align:center;background:0;border:1px dashed #d9d9d9;opacity:1}.umb-upload-button-big:hover{color:#d9d9d9}.umb-upload-drop-zone .info i.icon,.umb-upload-button-big i.icon{font-size:55px;line-height:70px}.umb-upload-button-big{display:block}.umb-upload-button-big input{bottom:0;left:0;width:100%;height:100%}.umb-photo-folder .picrow{position:relative;overflow-y:hidden}.umb-photo-folder .picrow div,.umb-photo-preview{position:relative;display:inline-block;padding:0;margin:0;vertical-align:top;border:0}.umb-photo-folder .picrow div a:first-child{width:100%;height:100%}.umb-photo-folder .picrow div.umb-photo{width:100%;height:100%;background-color:#f8f8f8}.umb-photo-folder a:hover{text-decoration:none}.umb-photo-folder .umb-non-thumbnail{font-size:12px;color:#000;text-align:center;text-decoration:none;vertical-align:middle;background:#f8f8f8}.umb-photo-folder .selector-overlay{display:none}.umb-photo-folder .pic:hover .selector-overlay{position:absolute;right:0;bottom:0;left:0;z-index:100;display:block;padding:5px;overflow:hidden;color:white;text-align:center;text-decoration:none;white-space:nowrap;background:black;opacity:.4;text-overlow:ellipsis}.umb-photo-folder .umb-non-thumbnail i{position:relative;top:50%;display:block;margin:auto;font-size:50px;line-height:60px;color:#d9d9d9;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.umb-photo-folder .umb-non-thumbnail span{position:absolute;top:20px;display:block;width:100%;margin:auto}.umb-photo-folder .selected{position:relative}.umb-photo-folder .selected:before{position:absolute;right:10px;bottom:10px;z-index:100;display:block;width:32px;height:32px;overflow:hidden;font-family:Icomoon;font-size:24px;line-height:36px;color:black;text-align:center;background:white;border-radius:15px;content:"\e165";opacity:.5}.umb-fileupload .preview{float:left;padding:3px;margin-right:30px;margin-bottom:30px;background:#efefef;border:1px solid #a0a0a0;border-radius:5px}.umb-fileupload ul{margin-bottom:0;vertical-align:middle;list-style:none}.umb-fileupload label{padding-left:7px;font-weight:normal;vertical-align:middle}.umb-fileupload .preview-file{width:55px;height:45px;padding-top:27px;font-size:10px;color:#666;text-align:center;text-transform:uppercase}.umb-fileupload input{font-size:12px;line-height:1}.umb-member-group-box{width:45%}.umb-member-group-box:nth-child(1){float:left}.umb-member-group-box:nth-child(2){float:right}.umb-tags{padding:10px;font-size:13px;text-shadow:none;border:#f8f8f8 solid 1px}.umb-tags .tag{padding:7px;margin:7px;cursor:pointer;background:#2e8aea}.umb-tags .tag i{padding:2px}.umb-tags input{background:#fff;border:0}.bootstrap-datetimepicker-widget .btn{padding:0}.bootstrap-datetimepicker-widget .picker-switch .btn{background:0;border:0}.umb-datepicker .input-append .add-on{cursor:pointer}.umb-datepicker p{margin-top:10px}.umb-datepicker p a{color:#555}.CodeMirror,.CodeMirror-scroll{height:100%;min-height:200px}.umb-nested-boolean label{float:left;width:320px;margin-bottom:8px}.umb-nested-boolean label span{float:left;width:80%}.umb-nested-boolean label input[type='checkbox']{float:left;margin-right:10px}.typeahead,.tt-query,.tt-hint{z-index:auto!important;width:396px;height:30px;padding:8px 12px;margin-top:-4px!important;font-size:24px;line-height:30px;border:2px solid #d9d9d9;border-radius:2px!important;outline:0}.typeahead{background-color:#fff}.tt-query{box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.tt-hint{color:#d9d9d9!important}.tt-dropdown-menu{width:422px;padding:8px 0;margin-top:12px;background-color:#fff;border:1px solid #d9d9d9;border:1px solid rgba(0,0,0,0.2);border-radius:8px;box-shadow:0 5px 10px rgba(0,0,0,0.2)}.tt-suggestion{padding:3px 20px;font-size:14px;line-height:20px;cursor:pointer}.tt-suggestion.tt-cursor{color:#fff;background-color:#2e8aea}.tt-suggestion p{margin:0}*{-ms-touch-action:none}.ace_editor{height:200px}.nounderline{text-decoration:none!important}.nounderline:hover{text-decoration:underline!important}.nounderline *{text-decoration:none!important;border:0}.ui-sortable-placeholder{margin-left:0!important}.controls-row img{max-width:none}.thumbnail{border-radius:0}.thumbnail img{width:100%;max-width:100%!important}#mapCanvas img{max-width:none!important}.btn-group .dropdown-backdrop{display:none}iframe,.content-column-body{background:center center url(../img/loader.gif) no-repeat;border:0}.fileinput-button{position:relative;margin-bottom:5px;overflow:hidden}.fileinput-button input{position:absolute;top:0;right:0;margin:0;font-size:23px;cursor:pointer;opacity:0;filter:alpha(opacity=0);-webkit-transform:translate(-300px,0) scale(4);transform:translate(-300px,0) scale(4);direction:ltr}.legacy-custom-file{display:inline-block;width:16px;height:16px;margin-right:11px;background-position:center center}.icon-chevron-up:before{content:"\e128"}.icon-chevron-down:before{content:"\e0c9"}.pa-umb-overlay{-webkit-font-smoothing:antialiased;font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.pa-umb-overlay+.pa-umb-overlay{padding-top:30px;border-top:1px solid #d9d9d9}.pa-select-type{display:-webkit-flex;display:-ms-flexbox;display:flex;margin-top:15px;-webkit-flex-wrap:nowrap;-ms-flex-wrap:nowrap;flex-wrap:nowrap;-webkit-flex-direction:row;-ms-flex-direction:row;flex-direction:row;-webkit-justify-content:center;-ms-flex-pack:center;justify-content:center;-webkit-align-items:flex-start;-ms-flex-align:start;align-items:flex-start}.pa-select-type label{padding:0 20px}.pa-access-header{padding-bottom:0;margin:0 0 3px 0;font-weight:bold}.pa-access-description{margin:0;color:#b3b3b3}.pa-validation-message{display:inline-block;padding:6px 12px!important;margin:5px 0 0 0!important}.pa-select-pages label{margin:0;font-size:15px}.pa-select-pages label+.controls-row{padding-top:0}.pa-select-pages .umb-detail{margin:2px 0 5px;font-size:13px}.pa-choose-page a{font-size:15px;color:#2e8aea}.pa-choose-page a:hover,.pa-choose-page a:active,.pa-choose-page a:focus{color:#0064cd;text-decoration:none}.pa-choose-page a:before{margin-right:3px;font-weight:bold;content:"+"}.pa-choose-page .treePickerTitle{padding:3px 5px;font-size:13px;font-style:italic;font-weight:bold;color:grey;background:whitesmoke;border-bottom:0}.pa-form+.pa-form{margin-top:10px}
\ No newline at end of file
diff --git a/OurUmbraco.Site/umbraco/config/lang/en.xml b/OurUmbraco.Site/umbraco/config/lang/en.xml
index b14b101a..0842755d 100644
--- a/OurUmbraco.Site/umbraco/config/lang/en.xml
+++ b/OurUmbraco.Site/umbraco/config/lang/en.xml
@@ -265,6 +265,7 @@
Select content
Select member
Select member group
+
No icons were found
There are no parameters for this macro
@@ -1054,7 +1055,9 @@ To manage your website, simply open the Umbraco back office and start adding con
Yes, delete
was moved underneath
+
was copied underneath
Select the folder to move
+
Select the folder to copy
to in the tree structure below
All Document types
diff --git a/OurUmbraco.Site/umbraco/config/lang/en_us.xml b/OurUmbraco.Site/umbraco/config/lang/en_us.xml
index f7714711..fbc2f385 100644
--- a/OurUmbraco.Site/umbraco/config/lang/en_us.xml
+++ b/OurUmbraco.Site/umbraco/config/lang/en_us.xml
@@ -1051,7 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con
Yes, delete
was moved underneath
+
was copied underneath
Select the folder to move
+
Select the folder to copy
to in the tree structure below
All Document types
diff --git a/OurUmbraco.Site/umbraco/config/lang/fr.xml b/OurUmbraco.Site/umbraco/config/lang/fr.xml
index 9a14e75f..5652052d 100644
--- a/OurUmbraco.Site/umbraco/config/lang/fr.xml
+++ b/OurUmbraco.Site/umbraco/config/lang/fr.xml
@@ -839,43 +839,43 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
Modèle
-
Choose type of content
-
Choose a layout
-
Add a row
-
Add content
-
Drop content
-
Settings applied
+
Choisissez le type de contenu
+
Choisissez une mise en page
+
Ajouter une ligne
+
Ajouter du contenu
+
Contenu goutte
+
Paramètres appliqués
-
This content is not allowed here
-
This content is allowed here
+
Ce contenu est pas autorisée ici
+
Ce contenu est permis ici
-
Click to embed
-
Click to insert image
-
Image caption...
-
Write here...
+
Cliquez pour intégrer
+
Cliquez pour insérer l'image
+
Légende de l'image...
+
Ecrire ici...
-
Grid Layouts
-
Layouts are the overall work area for the grid editor, usually you only need one or two different layouts
-
Add Grid Layout
-
Adjust the layout by setting column widths and adding additional sections
-
Row configurations
-
Rows are predefined cells arranged horizontally
-
Add row configuration
-
Adjust the row by setting cell widths and adding additional cells
+
Layouts Grid
+
Layouts sont la superficie totale de travail pour l'éditeur de grille, en général, vous avez seulement besoin d'une ou deux configurations différentes
+
Ajouter Grid Layout
+
Ajustez la mise en page en définissant la largeur des colonnes et ajouter des sections supplémentaires
+
Configurations des lignes
+
Les lignes sont des cellules prédéfinies disposées horizontalement
+
Ajouter une configuration de la ligne
+
Ajustez la ligne en réglant la largeur des cellules et en ajoutant des cellules supplémentaires
-
Columns
-
Total combined number of columns in the grid layout
+
Colonnes
+
Nombre total combiné de colonnes dans la configuration de la grille
-
Settings
-
Configure what settings editors can change
+
Paramètres
+
Configurer quels paramètres éditeurs peuvent changer
-
Styles
-
Configure what styling editors can change
+
Modes
+
Configurer ce style éditeurs peuvent changer
-
Settings will only save if the entered json configuration is valid
+
Les réglages seulement économiser si la configuration du json saisi est valide
-
Allow all editors
-
Allow all row configurations
+
Autoriser tous les éditeurs
+
Autoriser toutes les configurations de lignes
Champ alternatif
diff --git a/OurUmbraco.Site/umbraco/developer/Macros/editMacro.aspx b/OurUmbraco.Site/umbraco/developer/Macros/editMacro.aspx
index 2fef5e05..ee0dfe02 100644
--- a/OurUmbraco.Site/umbraco/developer/Macros/editMacro.aspx
+++ b/OurUmbraco.Site/umbraco/developer/Macros/editMacro.aspx
@@ -5,11 +5,11 @@
<%@ Register TagPrefix="CD" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %>
-
+
@@ -57,10 +57,10 @@
-
+
-
-
+
+
@@ -93,16 +93,16 @@
(Assembly)
(Type)
-
+
-
+
-
+
@@ -137,6 +137,9 @@
<%=umbraco.ui.Text("general", "type",UmbracoUser)%>
|
+
+ <%=umbraco.ui.Text("general", "sort",UmbracoUser)%>
+ |
|
@@ -144,23 +147,28 @@
- |
+ |
Required
-
+
|
Required
-
+
|
-
+
Required
|
+
+ Required
+ Numbers only
+
+ |
|
@@ -170,20 +178,23 @@
Required
-
+
|
Required
-
+
|
Required
|
+
+ <%-- The macro parameter will automatically get sort order when created. --%>
+ |
|
diff --git a/OurUmbraco.Site/umbraco/js/app.js b/OurUmbraco.Site/umbraco/js/app.js
index bc34b41b..36243abd 100644
--- a/OurUmbraco.Site/umbraco/js/app.js
+++ b/OurUmbraco.Site/umbraco/js/app.js
@@ -22,15 +22,35 @@ var packages = angular.module("umbraco.packages", []);
//module is initilized.
angular.module("umbraco.views", ["umbraco.viewcache"]);
angular.module("umbraco.viewcache", [])
- .run(function($rootScope, $templateCache){
- /** For debug mode, always clear template cache to cut down on
+ .run(function($rootScope, $templateCache) {
+ /** For debug mode, always clear template cache to cut down on
dev frustration and chrome cache on templates */
- if(Umbraco.Sys.ServerVariables.isDebuggingEnabled){
- //$rootScope.$on('$viewContentLoaded', function() {
- $templateCache.removeAll();
- //});
+ if (Umbraco.Sys.ServerVariables.isDebuggingEnabled) {
+ $templateCache.removeAll();
}
- })
+ })
+ .config([
+ //This ensures that all of our angular views are cache busted, if the path starts with views/ and ends with .html, then
+ // we will append the cache busting value to it. This way all upgraded sites will not have to worry about browser cache.
+ "$provide", function($provide) {
+ return $provide.decorator("$http", [
+ "$delegate", function($delegate) {
+ var get = $delegate.get;
+ $delegate.get = function (url, config) {
+
+ if (Umbraco.Sys.ServerVariables.application && url.startsWith("views/") && url.endsWith(".html")) {
+ var rnd = Umbraco.Sys.ServerVariables.application.version + "." + Umbraco.Sys.ServerVariables.application.cdf;
+ var _op = (url.indexOf("?") > 0) ? "&" : "?";
+ url += _op + "umb__rnd=" + rnd;
+ }
+
+ return get(url, config);
+ };
+ return $delegate;
+ }
+ ]);
+ }
+ ]);
//Call a document callback if defined, this is sort of a dodgy hack to
// be able to configure angular values in the Default.cshtml
diff --git a/OurUmbraco.Site/umbraco/js/umbraco.controllers.js b/OurUmbraco.Site/umbraco/js/umbraco.controllers.js
index 0d0fc342..04496726 100644
--- a/OurUmbraco.Site/umbraco/js/umbraco.controllers.js
+++ b/OurUmbraco.Site/umbraco/js/umbraco.controllers.js
@@ -6,317 +6,317 @@
(function() {
-
-/**
- * @ngdoc controller
- * @name Umbraco.MainController
- * @function
- *
- * @description
- * The main application controller
- *
- */
-function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) {
-
- //the null is important because we do an explicit bool check on this in the view
- //the avatar is by default the umbraco logo
- $scope.authenticated = null;
- $scope.avatar = [
- { value: "assets/img/application/logo.png" },
- { value: "assets/img/application/logo@2x.png" },
- { value: "assets/img/application/logo@3x.png" }
- ];
- $scope.touchDevice = appState.getGlobalState("touchDevice");
-
-
- $scope.removeNotification = function (index) {
- notificationsService.remove(index);
- };
-
- $scope.closeDialogs = function (event) {
- //only close dialogs if non-link and non-buttons are clicked
- var el = event.target.nodeName;
- var els = ["INPUT","A","BUTTON"];
-
- if(els.indexOf(el) >= 0){return;}
-
- var parents = $(event.target).parents("a,button");
- if(parents.length > 0){
- return;
- }
-
- //SD: I've updated this so that we don't close the dialog when clicking inside of the dialog
- var nav = $(event.target).parents("#dialog");
- if (nav.length === 1) {
- return;
- }
-
- eventsService.emit("app.closeDialogs", event);
- };
-
- var evts = [];
-
- //when a user logs out or timesout
- evts.push(eventsService.on("app.notAuthenticated", function() {
- $scope.authenticated = null;
- $scope.user = null;
- }));
-
- //when the app is read/user is logged in, setup the data
- evts.push(eventsService.on("app.ready", function (evt, data) {
-
- $scope.authenticated = data.authenticated;
- $scope.user = data.user;
-
- updateChecker.check().then(function(update){
- if(update && update !== "null"){
- if(update.type !== "None"){
- var notification = {
- headline: "Update available",
- message: "Click to download",
- sticky: true,
- type: "info",
- url: update.url
- };
- notificationsService.add(notification);
- }
- }
- })
-
- //if the user has changed we need to redirect to the root so they don't try to continue editing the
- //last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true)
- if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) {
- $location.path("/").search("");
- historyService.removeAll();
- treeService.clearCache();
- }
-
- //Load locale file
- if ($scope.user.locale) {
- tmhDynamicLocale.set($scope.user.locale);
- }
-
- if ($scope.user.emailHash) {
-
- //let's attempt to load the avatar, it might not exist or we might not have
- // internet access so we'll detect it first
- $http.get("https://www.gravatar.com/avatar/" + $scope.user.emailHash + ".jpg?s=64&d=404")
- .then(
- function successCallback(response) {
- $("#avatar-img").fadeTo(1000, 0, function () {
- $scope.$apply(function () {
- //this can be null if they time out
- if ($scope.user && $scope.user.emailHash) {
- var avatarBaseUrl = "https://www.gravatar.com/avatar/",
- hash = $scope.user.emailHash;
-
- $scope.avatar = [
- { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" },
- { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" },
- { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" }
- ];
- }
- });
- $("#avatar-img").fadeTo(1000, 1);
- });
- }, function errorCallback(response) {
- //cannot load it from the server so we cannot do anything
- });
- }
-
- }));
-
- evts.push(eventsService.on("app.ysod", function(name, error) {
- $scope.ysodOverlay = {
- view: "ysod",
- error: error,
- show: true
- };
- }));
-
- //ensure to unregister from all events!
- $scope.$on('$destroy', function () {
- for (var e in evts) {
- eventsService.unsubscribe(evts[e]);
- }
- });
-
-}
-
-
-//register it
-angular.module('umbraco').controller("Umbraco.MainController", MainController).
- config(function (tmhDynamicLocaleProvider) {
- //Set url for locale files
- tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js');
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.MainController
+ * @function
+ *
+ * @description
+ * The main application controller
+ *
+ */
+function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) {
+
+ //the null is important because we do an explicit bool check on this in the view
+ //the avatar is by default the umbraco logo
+ $scope.authenticated = null;
+ $scope.avatar = [
+ { value: "assets/img/application/logo.png" },
+ { value: "assets/img/application/logo@2x.png" },
+ { value: "assets/img/application/logo@3x.png" }
+ ];
+ $scope.touchDevice = appState.getGlobalState("touchDevice");
+
+
+ $scope.removeNotification = function (index) {
+ notificationsService.remove(index);
+ };
+
+ $scope.closeDialogs = function (event) {
+ //only close dialogs if non-link and non-buttons are clicked
+ var el = event.target.nodeName;
+ var els = ["INPUT","A","BUTTON"];
+
+ if(els.indexOf(el) >= 0){return;}
+
+ var parents = $(event.target).parents("a,button");
+ if(parents.length > 0){
+ return;
+ }
+
+ //SD: I've updated this so that we don't close the dialog when clicking inside of the dialog
+ var nav = $(event.target).parents("#dialog");
+ if (nav.length === 1) {
+ return;
+ }
+
+ eventsService.emit("app.closeDialogs", event);
+ };
+
+ var evts = [];
+
+ //when a user logs out or timesout
+ evts.push(eventsService.on("app.notAuthenticated", function() {
+ $scope.authenticated = null;
+ $scope.user = null;
+ }));
+
+ //when the app is read/user is logged in, setup the data
+ evts.push(eventsService.on("app.ready", function (evt, data) {
+
+ $scope.authenticated = data.authenticated;
+ $scope.user = data.user;
+
+ updateChecker.check().then(function(update){
+ if(update && update !== "null"){
+ if(update.type !== "None"){
+ var notification = {
+ headline: "Update available",
+ message: "Click to download",
+ sticky: true,
+ type: "info",
+ url: update.url
+ };
+ notificationsService.add(notification);
+ }
+ }
+ })
+
+ //if the user has changed we need to redirect to the root so they don't try to continue editing the
+ //last item in the URL (NOTE: the user id can equal zero, so we cannot just do !data.lastUserId since that will resolve to true)
+ if (data.lastUserId !== undefined && data.lastUserId !== null && data.lastUserId !== data.user.id) {
+ $location.path("/").search("");
+ historyService.removeAll();
+ treeService.clearCache();
+ }
+
+ //Load locale file
+ if ($scope.user.locale) {
+ tmhDynamicLocale.set($scope.user.locale);
+ }
+
+ if ($scope.user.emailHash) {
+
+ //let's attempt to load the avatar, it might not exist or we might not have
+ // internet access so we'll detect it first
+ $http.get("https://www.gravatar.com/avatar/" + $scope.user.emailHash + ".jpg?s=64&d=404")
+ .then(
+ function successCallback(response) {
+ $("#avatar-img").fadeTo(1000, 0, function () {
+ $scope.$apply(function () {
+ //this can be null if they time out
+ if ($scope.user && $scope.user.emailHash) {
+ var avatarBaseUrl = "https://www.gravatar.com/avatar/",
+ hash = $scope.user.emailHash;
+
+ $scope.avatar = [
+ { value: avatarBaseUrl + hash + ".jpg?s=30&d=mm" },
+ { value: avatarBaseUrl + hash + ".jpg?s=60&d=mm" },
+ { value: avatarBaseUrl + hash + ".jpg?s=90&d=mm" }
+ ];
+ }
+ });
+ $("#avatar-img").fadeTo(1000, 1);
+ });
+ }, function errorCallback(response) {
+ //cannot load it from the server so we cannot do anything
+ });
+ }
+
+ }));
+
+ evts.push(eventsService.on("app.ysod", function(name, error) {
+ $scope.ysodOverlay = {
+ view: "ysod",
+ error: error,
+ show: true
+ };
+ }));
+
+ //ensure to unregister from all events!
+ $scope.$on('$destroy', function () {
+ for (var e in evts) {
+ eventsService.unsubscribe(evts[e]);
+ }
+ });
+
+}
+
+
+//register it
+angular.module('umbraco').controller("Umbraco.MainController", MainController).
+ config(function (tmhDynamicLocaleProvider) {
+ //Set url for locale files
+ tmhDynamicLocaleProvider.localeLocationPattern('lib/angular/1.1.5/i18n/angular-locale_{{locale}}.js');
});
-
-/**
- * @ngdoc controller
- * @name Umbraco.NavigationController
- * @function
- *
- * @description
- * Handles the section area of the app
- *
- * @param {navigationService} navigationService A reference to the navigationService
- */
-function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) {
-
- //TODO: Need to think about this and an nicer way to acheive what this is doing.
- //the tree event handler i used to subscribe to the main tree click events
- $scope.treeEventHandler = $({});
- navigationService.setupTreeEvents($scope.treeEventHandler);
-
- //Put the navigation service on this scope so we can use it's methods/properties in the view.
- // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since
- // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one.
- $scope.nav = navigationService;
- // TODO: Lets fix this, it is less than ideal to be passing in the navigationController scope to something else to be used as it's scope,
- // this is going to lead to problems/confusion. I really don't think passing scope's around is very good practice.
- $rootScope.nav = navigationService;
-
- //set up our scope vars
- $scope.showContextMenuDialog = false;
- $scope.showContextMenu = false;
- $scope.showSearchResults = false;
- $scope.menuDialogTitle = null;
- $scope.menuActions = [];
- $scope.menuNode = null;
-
- $scope.currentSection = appState.getSectionState("currentSection");
- $scope.showNavigation = appState.getGlobalState("showNavigation");
-
- //trigger search with a hotkey:
- keyboardService.bind("ctrl+shift+s", function () {
- navigationService.showSearch();
- });
-
- //trigger dialods with a hotkey:
- keyboardService.bind("esc", function () {
- eventsService.emit("app.closeDialogs");
- });
-
- $scope.selectedId = navigationService.currentId;
-
- var evts = [];
-
- //Listen for global state changes
- evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
- if (args.key === "showNavigation") {
- $scope.showNavigation = args.value;
- }
- }));
-
- //Listen for menu state changes
- evts.push(eventsService.on("appState.menuState.changed", function(e, args) {
- if (args.key === "showMenuDialog") {
- $scope.showContextMenuDialog = args.value;
- }
- if (args.key === "showMenu") {
- $scope.showContextMenu = args.value;
- }
- if (args.key === "dialogTitle") {
- $scope.menuDialogTitle = args.value;
- }
- if (args.key === "menuActions") {
- $scope.menuActions = args.value;
- }
- if (args.key === "currentNode") {
- $scope.menuNode = args.value;
- }
- }));
-
- //Listen for section state changes
- evts.push(eventsService.on("appState.treeState.changed", function(e, args) {
- var f = args;
- if (args.value.root && args.value.root.metaData.containsTrees === false) {
- $rootScope.emptySection = true;
- }
- else {
- $rootScope.emptySection = false;
- }
- }));
-
- //Listen for section state changes
- evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
- //section changed
- if (args.key === "currentSection") {
- $scope.currentSection = args.value;
- }
- //show/hide search results
- if (args.key === "showSearchResults") {
- $scope.showSearchResults = args.value;
- }
- }));
-
- //This reacts to clicks passed to the body element which emits a global call to close all dialogs
- evts.push(eventsService.on("app.closeDialogs", function(event) {
- if (appState.getGlobalState("stickyNavigation")) {
- navigationService.hideNavigation();
- //TODO: don't know why we need this? - we are inside of an angular event listener.
- angularHelper.safeApply($scope);
- }
- }));
-
- //when a user logs out or timesout
- evts.push(eventsService.on("app.notAuthenticated", function() {
- $scope.authenticated = false;
- }));
-
- //when the application is ready and the user is authorized setup the data
- evts.push(eventsService.on("app.ready", function(evt, data) {
- $scope.authenticated = true;
- }));
-
- //this reacts to the options item in the tree
- //todo, migrate to nav service
- $scope.searchShowMenu = function (ev, args) {
- //always skip default
- args.skipDefault = true;
- navigationService.showMenu(ev, args);
- };
-
- //todo, migrate to nav service
- $scope.searchHide = function () {
- navigationService.hideSearch();
- };
-
- //the below assists with hiding/showing the tree
- var treeActive = false;
-
- //Sets a service variable as soon as the user hovers the navigation with the mouse
- //used by the leaveTree method to delay hiding
- $scope.enterTree = function (event) {
- treeActive = true;
- };
-
- // Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
- $scope.leaveTree = function(event) {
- //this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down
- if (!event) {
- return;
- }
- if (!appState.getGlobalState("touchDevice")) {
- treeActive = false;
- $timeout(function() {
- if (!treeActive) {
- navigationService.hideTree();
- }
- }, 300);
- }
- };
-
- //ensure to unregister from all events!
- $scope.$on('$destroy', function () {
- for (var e in evts) {
- eventsService.unsubscribe(evts[e]);
- }
- });
-}
-
-//register it
-angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.NavigationController
+ * @function
+ *
+ * @description
+ * Handles the section area of the app
+ *
+ * @param {navigationService} navigationService A reference to the navigationService
+ */
+function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) {
+
+ //TODO: Need to think about this and an nicer way to acheive what this is doing.
+ //the tree event handler i used to subscribe to the main tree click events
+ $scope.treeEventHandler = $({});
+ navigationService.setupTreeEvents($scope.treeEventHandler);
+
+ //Put the navigation service on this scope so we can use it's methods/properties in the view.
+ // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since
+ // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one.
+ $scope.nav = navigationService;
+ // TODO: Lets fix this, it is less than ideal to be passing in the navigationController scope to something else to be used as it's scope,
+ // this is going to lead to problems/confusion. I really don't think passing scope's around is very good practice.
+ $rootScope.nav = navigationService;
+
+ //set up our scope vars
+ $scope.showContextMenuDialog = false;
+ $scope.showContextMenu = false;
+ $scope.showSearchResults = false;
+ $scope.menuDialogTitle = null;
+ $scope.menuActions = [];
+ $scope.menuNode = null;
+
+ $scope.currentSection = appState.getSectionState("currentSection");
+ $scope.showNavigation = appState.getGlobalState("showNavigation");
+
+ //trigger search with a hotkey:
+ keyboardService.bind("ctrl+shift+s", function () {
+ navigationService.showSearch();
+ });
+
+ //trigger dialods with a hotkey:
+ keyboardService.bind("esc", function () {
+ eventsService.emit("app.closeDialogs");
+ });
+
+ $scope.selectedId = navigationService.currentId;
+
+ var evts = [];
+
+ //Listen for global state changes
+ evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
+ if (args.key === "showNavigation") {
+ $scope.showNavigation = args.value;
+ }
+ }));
+
+ //Listen for menu state changes
+ evts.push(eventsService.on("appState.menuState.changed", function(e, args) {
+ if (args.key === "showMenuDialog") {
+ $scope.showContextMenuDialog = args.value;
+ }
+ if (args.key === "showMenu") {
+ $scope.showContextMenu = args.value;
+ }
+ if (args.key === "dialogTitle") {
+ $scope.menuDialogTitle = args.value;
+ }
+ if (args.key === "menuActions") {
+ $scope.menuActions = args.value;
+ }
+ if (args.key === "currentNode") {
+ $scope.menuNode = args.value;
+ }
+ }));
+
+ //Listen for section state changes
+ evts.push(eventsService.on("appState.treeState.changed", function(e, args) {
+ var f = args;
+ if (args.value.root && args.value.root.metaData.containsTrees === false) {
+ $rootScope.emptySection = true;
+ }
+ else {
+ $rootScope.emptySection = false;
+ }
+ }));
+
+ //Listen for section state changes
+ evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
+ //section changed
+ if (args.key === "currentSection") {
+ $scope.currentSection = args.value;
+ }
+ //show/hide search results
+ if (args.key === "showSearchResults") {
+ $scope.showSearchResults = args.value;
+ }
+ }));
+
+ //This reacts to clicks passed to the body element which emits a global call to close all dialogs
+ evts.push(eventsService.on("app.closeDialogs", function(event) {
+ if (appState.getGlobalState("stickyNavigation")) {
+ navigationService.hideNavigation();
+ //TODO: don't know why we need this? - we are inside of an angular event listener.
+ angularHelper.safeApply($scope);
+ }
+ }));
+
+ //when a user logs out or timesout
+ evts.push(eventsService.on("app.notAuthenticated", function() {
+ $scope.authenticated = false;
+ }));
+
+ //when the application is ready and the user is authorized setup the data
+ evts.push(eventsService.on("app.ready", function(evt, data) {
+ $scope.authenticated = true;
+ }));
+
+ //this reacts to the options item in the tree
+ //todo, migrate to nav service
+ $scope.searchShowMenu = function (ev, args) {
+ //always skip default
+ args.skipDefault = true;
+ navigationService.showMenu(ev, args);
+ };
+
+ //todo, migrate to nav service
+ $scope.searchHide = function () {
+ navigationService.hideSearch();
+ };
+
+ //the below assists with hiding/showing the tree
+ var treeActive = false;
+
+ //Sets a service variable as soon as the user hovers the navigation with the mouse
+ //used by the leaveTree method to delay hiding
+ $scope.enterTree = function (event) {
+ treeActive = true;
+ };
+
+ // Hides navigation tree, with a short delay, is cancelled if the user moves the mouse over the tree again
+ $scope.leaveTree = function(event) {
+ //this is a hack to handle IE touch events which freaks out due to no mouse events so the tree instantly shuts down
+ if (!event) {
+ return;
+ }
+ if (!appState.getGlobalState("touchDevice")) {
+ treeActive = false;
+ $timeout(function() {
+ if (!treeActive) {
+ navigationService.hideTree();
+ }
+ }, 300);
+ }
+ };
+
+ //ensure to unregister from all events!
+ $scope.$on('$destroy', function () {
+ for (var e in evts) {
+ eventsService.unsubscribe(evts[e]);
+ }
+ });
+}
+
+//register it
+angular.module('umbraco').controller("Umbraco.NavigationController", NavigationController);
/**
* @ngdoc controller
@@ -446,37 +446,37 @@ function SearchController($scope, searchService, $log, $location, navigationServ
}
//register it
angular.module('umbraco').controller("Umbraco.SearchController", SearchController);
-
-/**
- * @ngdoc controller
- * @name Umbraco.MainController
- * @function
- *
- * @description
- * The controller for the AuthorizeUpgrade login page
- *
- */
-function AuthorizeUpgradeController($scope, $window) {
-
- //Add this method to the scope - this method will be called by the login dialog controller when the login is successful
- // then we'll handle the redirect.
- $scope.submit = function (event) {
-
- var qry = $window.location.search.trimStart("?").split("&");
- var redir = _.find(qry, function(item) {
- return item.startsWith("redir=");
- });
- if (redir) {
- $window.location = decodeURIComponent(redir.split("=")[1]);
- }
- else {
- $window.location = "/";
- }
-
- };
-
-}
-
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.MainController
+ * @function
+ *
+ * @description
+ * The controller for the AuthorizeUpgrade login page
+ *
+ */
+function AuthorizeUpgradeController($scope, $window) {
+
+ //Add this method to the scope - this method will be called by the login dialog controller when the login is successful
+ // then we'll handle the redirect.
+ $scope.submit = function (event) {
+
+ var qry = $window.location.search.trimStart("?").split("&");
+ var redir = _.find(qry, function(item) {
+ return item.startsWith("redir=");
+ });
+ if (redir) {
+ $window.location = decodeURIComponent(redir.split("=")[1]);
+ }
+ else {
+ $window.location = "/";
+ }
+
+ };
+
+}
+
angular.module('umbraco').controller("Umbraco.AuthorizeUpgradeController", AuthorizeUpgradeController);
/**
* @ngdoc controller
@@ -509,204 +509,204 @@ function DashboardController($scope, $routeParams, dashboardResource, localizati
//register it
angular.module('umbraco').controller("Umbraco.DashboardController", DashboardController);
-angular.module("umbraco")
- .controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) {
- assetsService.loadJs("lib/cssparser/cssparser.js")
- .then(function () {
-
- var cssPath = $scope.dialogData.cssPath;
- $scope.cssClass = $scope.dialogData.cssClass;
-
- $scope.classes = [];
-
- $scope.change = function (newClass) {
- $scope.model.value = newClass;
- }
-
- $http.get(cssPath)
- .success(function (data) {
- var parser = new CSSParser();
- $scope.classes = parser.parse(data, false, false).cssRules;
- $scope.classes.splice(0, 0, "noclass");
- })
-
- assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css");
- assetsService.loadCss(cssPath);
- });
+angular.module("umbraco")
+ .controller("Umbraco.Dialogs.ApprovedColorPickerController", function ($scope, $http, umbPropEditorHelper, assetsService) {
+ assetsService.loadJs("lib/cssparser/cssparser.js")
+ .then(function () {
+
+ var cssPath = $scope.dialogData.cssPath;
+ $scope.cssClass = $scope.dialogData.cssClass;
+
+ $scope.classes = [];
+
+ $scope.change = function (newClass) {
+ $scope.model.value = newClass;
+ }
+
+ $http.get(cssPath)
+ .success(function (data) {
+ var parser = new CSSParser();
+ $scope.classes = parser.parse(data, false, false).cssRules;
+ $scope.classes.splice(0, 0, "noclass");
+ })
+
+ assetsService.loadCss("/App_Plugins/Lecoati.uSky.Grid/lib/uSky.Grid.ApprovedColorPicker.css");
+ assetsService.loadCss(cssPath);
+ });
});
-function ContentEditDialogController($scope, editorState, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, umbModelMapper, $http) {
-
- $scope.defaultButton = null;
- $scope.subButtons = [];
- var dialogOptions = $scope.$parent.dialogOptions;
-
- // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
- function performSave(args) {
- contentEditingHelper.contentEditorPerformSave({
- statusMessage: args.statusMessage,
- saveMethod: args.saveMethod,
- scope: $scope,
- content: $scope.content
- }).then(function (content) {
- //success
- if (dialogOptions.closeOnSave) {
- $scope.submit(content);
- }
-
- }, function(err) {
- //error
- });
- }
-
- function filterTabs(entity, blackList) {
- if (blackList) {
- _.each(entity.tabs, function (tab) {
- tab.hide = _.contains(blackList, tab.alias);
- });
- }
-
- return entity;
- };
-
- function init(content) {
- var buttons = contentEditingHelper.configureContentEditorButtons({
- create: $routeParams.create,
- content: content,
- methods: {
- saveAndPublish: $scope.saveAndPublish,
- sendToPublish: $scope.sendToPublish,
- save: $scope.save,
- unPublish: angular.noop
- }
- });
- $scope.defaultButton = buttons.defaultButton;
- $scope.subButtons = buttons.subButtons;
-
- //This is a total hack but we have really no other way of sharing data to the property editors of this
- // content item, so we'll just set the property on the content item directly
- $scope.content.isDialogEditor = true;
-
- editorState.set($scope.content);
- }
-
- //check if the entity is being passed in, otherwise load it from the server
- if (angular.isObject(dialogOptions.entity)) {
- $scope.loaded = true;
- $scope.content = filterTabs(dialogOptions.entity, dialogOptions.tabFilter);
- init($scope.content);
- }
- else {
- contentResource.getById(dialogOptions.id)
- .then(function(data) {
- $scope.loaded = true;
- $scope.content = filterTabs(data, dialogOptions.tabFilter);
- init($scope.content);
- //in one particular special case, after we've created a new item we redirect back to the edit
- // route but there might be server validation errors in the collection which we need to display
- // after the redirect, so we will bind all subscriptions which will show the server validation errors
- // if there are any and then clear them so the collection no longer persists them.
- serverValidationManager.executeAndClearAllSubscriptions();
- });
- }
-
- $scope.sendToPublish = function () {
- performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." });
- };
-
- $scope.saveAndPublish = function () {
- performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." });
- };
-
- $scope.save = function () {
- performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." });
- };
-
- // this method is called for all action buttons and then we proxy based on the btn definition
- $scope.performAction = function (btn) {
-
- if (!btn || !angular.isFunction(btn.handler)) {
- throw "btn.handler must be a function reference";
- }
-
- if (!$scope.busy) {
- btn.handler.apply(this);
- }
- };
-
-}
-
-
-angular.module("umbraco")
+function ContentEditDialogController($scope, editorState, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, umbModelMapper, $http) {
+
+ $scope.defaultButton = null;
+ $scope.subButtons = [];
+ var dialogOptions = $scope.$parent.dialogOptions;
+
+ // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
+ function performSave(args) {
+ contentEditingHelper.contentEditorPerformSave({
+ statusMessage: args.statusMessage,
+ saveMethod: args.saveMethod,
+ scope: $scope,
+ content: $scope.content
+ }).then(function (content) {
+ //success
+ if (dialogOptions.closeOnSave) {
+ $scope.submit(content);
+ }
+
+ }, function(err) {
+ //error
+ });
+ }
+
+ function filterTabs(entity, blackList) {
+ if (blackList) {
+ _.each(entity.tabs, function (tab) {
+ tab.hide = _.contains(blackList, tab.alias);
+ });
+ }
+
+ return entity;
+ };
+
+ function init(content) {
+ var buttons = contentEditingHelper.configureContentEditorButtons({
+ create: $routeParams.create,
+ content: content,
+ methods: {
+ saveAndPublish: $scope.saveAndPublish,
+ sendToPublish: $scope.sendToPublish,
+ save: $scope.save,
+ unPublish: angular.noop
+ }
+ });
+ $scope.defaultButton = buttons.defaultButton;
+ $scope.subButtons = buttons.subButtons;
+
+ //This is a total hack but we have really no other way of sharing data to the property editors of this
+ // content item, so we'll just set the property on the content item directly
+ $scope.content.isDialogEditor = true;
+
+ editorState.set($scope.content);
+ }
+
+ //check if the entity is being passed in, otherwise load it from the server
+ if (angular.isObject(dialogOptions.entity)) {
+ $scope.loaded = true;
+ $scope.content = filterTabs(dialogOptions.entity, dialogOptions.tabFilter);
+ init($scope.content);
+ }
+ else {
+ contentResource.getById(dialogOptions.id)
+ .then(function(data) {
+ $scope.loaded = true;
+ $scope.content = filterTabs(data, dialogOptions.tabFilter);
+ init($scope.content);
+ //in one particular special case, after we've created a new item we redirect back to the edit
+ // route but there might be server validation errors in the collection which we need to display
+ // after the redirect, so we will bind all subscriptions which will show the server validation errors
+ // if there are any and then clear them so the collection no longer persists them.
+ serverValidationManager.executeAndClearAllSubscriptions();
+ });
+ }
+
+ $scope.sendToPublish = function () {
+ performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." });
+ };
+
+ $scope.saveAndPublish = function () {
+ performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." });
+ };
+
+ $scope.save = function () {
+ performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." });
+ };
+
+ // this method is called for all action buttons and then we proxy based on the btn definition
+ $scope.performAction = function (btn) {
+
+ if (!btn || !angular.isFunction(btn.handler)) {
+ throw "btn.handler must be a function reference";
+ }
+
+ if (!$scope.busy) {
+ btn.handler.apply(this);
+ }
+ };
+
+}
+
+
+angular.module("umbraco")
.controller("Umbraco.Dialogs.Content.EditController", ContentEditDialogController);
-angular.module("umbraco")
- .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) {
- $scope.section = $routeParams.section;
- $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
-
- if(!$scope.section){
- $scope.section = "content";
- }
-
- $scope.sectionName = $scope.section;
-
- var rq = {};
- rq.section = $scope.section;
-
- //translate section name
- localizationService.localize("sections_" + rq.section).then(function (value) {
- $scope.sectionName = value;
- });
-
- userService.getCurrentUser().then(function(user){
-
- rq.usertype = user.userType;
- rq.lang = user.locale;
-
- if($routeParams.url){
- rq.path = decodeURIComponent($routeParams.url);
-
- if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){
- rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length);
- }
-
- if(rq.path.indexOf(".aspx") > 0){
- rq.path = rq.path.substring(0, rq.path.indexOf(".aspx"));
- }
-
- }else{
- rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method;
- }
-
- helpService.findHelp(rq).then(function(topics){
- $scope.topics = topics;
- });
-
- helpService.findVideos(rq).then(function(videos){
- $scope.videos = videos;
- });
-
- });
-
-
+angular.module("umbraco")
+ .controller("Umbraco.Dialogs.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) {
+ $scope.section = $routeParams.section;
+ $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
+
+ if(!$scope.section){
+ $scope.section = "content";
+ }
+
+ $scope.sectionName = $scope.section;
+
+ var rq = {};
+ rq.section = $scope.section;
+
+ //translate section name
+ localizationService.localize("sections_" + rq.section).then(function (value) {
+ $scope.sectionName = value;
+ });
+
+ userService.getCurrentUser().then(function(user){
+
+ rq.usertype = user.userType;
+ rq.lang = user.locale;
+
+ if($routeParams.url){
+ rq.path = decodeURIComponent($routeParams.url);
+
+ if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){
+ rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length);
+ }
+
+ if(rq.path.indexOf(".aspx") > 0){
+ rq.path = rq.path.substring(0, rq.path.indexOf(".aspx"));
+ }
+
+ }else{
+ rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method;
+ }
+
+ helpService.findHelp(rq).then(function(topics){
+ $scope.topics = topics;
+ });
+
+ helpService.findVideos(rq).then(function(videos){
+ $scope.videos = videos;
+ });
+
+ });
+
+
});
-//used for the icon picker dialog
-angular.module("umbraco")
- .controller("Umbraco.Dialogs.IconPickerController",
- function ($scope, iconHelper) {
-
- iconHelper.getIcons().then(function(icons){
- $scope.icons = icons;
- });
-
- $scope.submitClass = function(icon){
- if($scope.color)
- {
- $scope.submit(icon + " " + $scope.color);
- }else{
- $scope.submit(icon);
- }
- };
- }
+//used for the icon picker dialog
+angular.module("umbraco")
+ .controller("Umbraco.Dialogs.IconPickerController",
+ function ($scope, iconHelper) {
+
+ iconHelper.getIcons().then(function(icons){
+ $scope.icons = icons;
+ });
+
+ $scope.submitClass = function(icon){
+ if($scope.color)
+ {
+ $scope.submit(icon + " " + $scope.color);
+ }else{
+ $scope.submit(icon);
+ }
+ };
+ }
);
/**
* @ngdoc controller
@@ -1403,115 +1403,115 @@ angular.module("umbraco").controller("Umbraco.Dialogs.RteEmbedController", funct
$scope.submit($scope.form.preview);
};
});
-angular.module("umbraco").controller('Umbraco.Dialogs.Template.QueryBuilderController',
- function($scope, $http, dialogService){
-
-
- $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) {
- $scope.properties = response.data;
- });
-
- $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) {
- $scope.contentTypes = response.data;
- });
-
- $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) {
- $scope.conditions = response.data;
- });
-
-
- $scope.query = {
- contentType: {
- name: "Everything"
- },
- source:{
- name: "My website"
- },
- filters:[
- {
- property:undefined,
- operator: undefined
- }
- ],
- sort:{
- property:{
- alias: "",
- name: "",
- },
- direction: "ascending"
- }
- };
-
-
-
- $scope.chooseSource = function(query){
- dialogService.contentPicker({
- callback: function (data) {
-
- if (data.id > 0) {
- query.source = { id: data.id, name: data.name };
- } else {
- query.source.name = "My website";
- delete query.source.id;
- }
- }
- });
- };
-
- var throttledFunc = _.throttle(function() {
-
- $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) {
- $scope.result = response.data;
- });
-
- }, 200);
-
- $scope.$watch("query", function(value) {
- throttledFunc();
- }, true);
-
- $scope.getPropertyOperators = function (property) {
-
- var conditions = _.filter($scope.conditions, function(condition) {
- var index = condition.appliesTo.indexOf(property.type);
- return index >= 0;
- });
- return conditions;
- };
-
-
- $scope.addFilter = function(query){
- query.filters.push({});
- };
-
- $scope.trashFilter = function (query) {
- query.filters.splice(query,1);
- };
-
- $scope.changeSortOrder = function(query){
- if(query.sort.direction === "ascending"){
- query.sort.direction = "descending";
- }else{
- query.sort.direction = "ascending";
- }
- };
-
- $scope.setSortProperty = function(query, property){
- query.sort.property = property;
- if(property.type === "datetime"){
- query.sort.direction = "descending";
- }else{
- query.sort.direction = "ascending";
- }
- };
+angular.module("umbraco").controller('Umbraco.Dialogs.Template.QueryBuilderController',
+ function($scope, $http, dialogService){
+
+
+ $http.get("backoffice/UmbracoApi/TemplateQuery/GetAllowedProperties").then(function(response) {
+ $scope.properties = response.data;
+ });
+
+ $http.get("backoffice/UmbracoApi/TemplateQuery/GetContentTypes").then(function (response) {
+ $scope.contentTypes = response.data;
+ });
+
+ $http.get("backoffice/UmbracoApi/TemplateQuery/GetFilterConditions").then(function (response) {
+ $scope.conditions = response.data;
+ });
+
+
+ $scope.query = {
+ contentType: {
+ name: "Everything"
+ },
+ source:{
+ name: "My website"
+ },
+ filters:[
+ {
+ property:undefined,
+ operator: undefined
+ }
+ ],
+ sort:{
+ property:{
+ alias: "",
+ name: "",
+ },
+ direction: "ascending"
+ }
+ };
+
+
+
+ $scope.chooseSource = function(query){
+ dialogService.contentPicker({
+ callback: function (data) {
+
+ if (data.id > 0) {
+ query.source = { id: data.id, name: data.name };
+ } else {
+ query.source.name = "My website";
+ delete query.source.id;
+ }
+ }
+ });
+ };
+
+ var throttledFunc = _.throttle(function() {
+
+ $http.post("backoffice/UmbracoApi/TemplateQuery/PostTemplateQuery", $scope.query).then(function (response) {
+ $scope.result = response.data;
+ });
+
+ }, 200);
+
+ $scope.$watch("query", function(value) {
+ throttledFunc();
+ }, true);
+
+ $scope.getPropertyOperators = function (property) {
+
+ var conditions = _.filter($scope.conditions, function(condition) {
+ var index = condition.appliesTo.indexOf(property.type);
+ return index >= 0;
+ });
+ return conditions;
+ };
+
+
+ $scope.addFilter = function(query){
+ query.filters.push({});
+ };
+
+ $scope.trashFilter = function (query) {
+ query.filters.splice(query,1);
+ };
+
+ $scope.changeSortOrder = function(query){
+ if(query.sort.direction === "ascending"){
+ query.sort.direction = "descending";
+ }else{
+ query.sort.direction = "ascending";
+ }
+ };
+
+ $scope.setSortProperty = function(query, property){
+ query.sort.property = property;
+ if(property.type === "datetime"){
+ query.sort.direction = "descending";
+ }else{
+ query.sort.direction = "ascending";
+ }
+ };
});
-angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController',
- function($scope) {
- $scope.type = $scope.dialogOptions.type;
- $scope.section = {
- name: "",
- required: false
- };
+angular.module("umbraco").controller('Umbraco.Dialogs.Template.SnippetController',
+ function($scope) {
+ $scope.type = $scope.dialogOptions.type;
+ $scope.section = {
+ name: "",
+ required: false
+ };
});
//used for the media picker dialog
angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
@@ -1944,173 +1944,173 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
});
});
-angular.module("umbraco")
- .controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper) {
-
- $scope.history = historyService.getCurrent();
- $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
- $scope.showPasswordFields = false;
- $scope.changePasswordButtonState = "init";
-
- $scope.externalLoginProviders = externalLoginInfo.providers;
- $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl;
- var evts = [];
- evts.push(eventsService.on("historyService.add", function (e, args) {
- $scope.history = args.all;
- }));
- evts.push(eventsService.on("historyService.remove", function (e, args) {
- $scope.history = args.all;
- }));
- evts.push(eventsService.on("historyService.removeAll", function (e, args) {
- $scope.history = [];
- }));
-
- $scope.logout = function () {
-
- //Add event listener for when there are pending changes on an editor which means our route was not successful
- var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) {
- //one time listener, remove the event
- pendingChangeEvent();
- $scope.close();
- });
-
-
- //perform the path change, if it is successful then the promise will resolve otherwise it will fail
- $scope.close();
- $location.path("/logout");
- };
-
- $scope.gotoHistory = function (link) {
- $location.path(link);
- $scope.close();
- };
-
- //Manually update the remaining timeout seconds
- function updateTimeout() {
- $timeout(function () {
- if ($scope.remainingAuthSeconds > 0) {
- $scope.remainingAuthSeconds--;
- $scope.$digest();
- //recurse
- updateTimeout();
- }
-
- }, 1000, false); // 1 second, do NOT execute a global digest
- }
-
- function updateUserInfo() {
- //get the user
- userService.getCurrentUser().then(function (user) {
- $scope.user = user;
- if ($scope.user) {
- $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
- $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
- //set the timer
- updateTimeout();
-
- authResource.getCurrentUserLinkedLogins().then(function(logins) {
- //reset all to be un-linked
- for (var provider in $scope.externalLoginProviders) {
- $scope.externalLoginProviders[provider].linkedProviderKey = undefined;
- }
-
- //set the linked logins
- for (var login in logins) {
- var found = _.find($scope.externalLoginProviders, function (i) {
- return i.authType == login;
- });
- if (found) {
- found.linkedProviderKey = logins[login];
- }
- }
- });
- }
- });
- }
-
- $scope.unlink = function (e, loginProvider, providerKey) {
- var result = confirm("Are you sure you want to unlink this account?");
- if (!result) {
- e.preventDefault();
- return;
- }
-
- authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) {
- updateUserInfo();
- });
- }
-
- updateUserInfo();
-
- //remove all event handlers
- $scope.$on('$destroy', function () {
- for (var e = 0; e < evts.length; e++) {
- evts[e]();
- }
-
- });
-
- /* ---------- UPDATE PASSWORD ---------- */
-
- //create the initial model for change password property editor
- $scope.changePasswordModel = {
- alias: "_umb_password",
- view: "changepassword",
- config: {},
- value: {}
- };
-
- //go get the config for the membership provider and add it to the model
- currentUserResource.getMembershipProviderConfig().then(function(data) {
- $scope.changePasswordModel.config = data;
- //ensure the hasPassword config option is set to true (the user of course has a password already assigned)
- //this will ensure the oldPassword is shown so they can change it
- // disable reset password functionality beacuse it does not make sense inside the backoffice
- $scope.changePasswordModel.config.hasPassword = true;
- $scope.changePasswordModel.config.disableToggle = true;
- $scope.changePasswordModel.config.enableReset = false;
- });
-
- $scope.changePassword = function() {
-
- if (formHelper.submitForm({ scope: $scope })) {
-
- $scope.changePasswordButtonState = "busy";
-
- currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) {
-
- //if the password has been reset, then update our model
- if (data.value) {
- $scope.changePasswordModel.value.generatedPassword = data.value;
- }
-
- formHelper.resetForm({ scope: $scope, notifications: data.notifications });
-
- $scope.changePasswordButtonState = "success";
-
- }, function (err) {
-
- formHelper.handleError(err);
-
- $scope.changePasswordButtonState = "error";
-
- });
-
- }
-
- };
-
- $scope.togglePasswordFields = function() {
- clearPasswordFields();
- $scope.showPasswordFields = !$scope.showPasswordFields;
- }
-
- function clearPasswordFields() {
- $scope.changePasswordModel.value.newPassword = "";
- $scope.changePasswordModel.confirm = "";
- }
-
- });
+angular.module("umbraco")
+ .controller("Umbraco.Dialogs.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper) {
+
+ $scope.history = historyService.getCurrent();
+ $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
+ $scope.showPasswordFields = false;
+ $scope.changePasswordButtonState = "init";
+
+ $scope.externalLoginProviders = externalLoginInfo.providers;
+ $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl;
+ var evts = [];
+ evts.push(eventsService.on("historyService.add", function (e, args) {
+ $scope.history = args.all;
+ }));
+ evts.push(eventsService.on("historyService.remove", function (e, args) {
+ $scope.history = args.all;
+ }));
+ evts.push(eventsService.on("historyService.removeAll", function (e, args) {
+ $scope.history = [];
+ }));
+
+ $scope.logout = function () {
+
+ //Add event listener for when there are pending changes on an editor which means our route was not successful
+ var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) {
+ //one time listener, remove the event
+ pendingChangeEvent();
+ $scope.close();
+ });
+
+
+ //perform the path change, if it is successful then the promise will resolve otherwise it will fail
+ $scope.close();
+ $location.path("/logout");
+ };
+
+ $scope.gotoHistory = function (link) {
+ $location.path(link);
+ $scope.close();
+ };
+
+ //Manually update the remaining timeout seconds
+ function updateTimeout() {
+ $timeout(function () {
+ if ($scope.remainingAuthSeconds > 0) {
+ $scope.remainingAuthSeconds--;
+ $scope.$digest();
+ //recurse
+ updateTimeout();
+ }
+
+ }, 1000, false); // 1 second, do NOT execute a global digest
+ }
+
+ function updateUserInfo() {
+ //get the user
+ userService.getCurrentUser().then(function (user) {
+ $scope.user = user;
+ if ($scope.user) {
+ $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
+ $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
+ //set the timer
+ updateTimeout();
+
+ authResource.getCurrentUserLinkedLogins().then(function(logins) {
+ //reset all to be un-linked
+ for (var provider in $scope.externalLoginProviders) {
+ $scope.externalLoginProviders[provider].linkedProviderKey = undefined;
+ }
+
+ //set the linked logins
+ for (var login in logins) {
+ var found = _.find($scope.externalLoginProviders, function (i) {
+ return i.authType == login;
+ });
+ if (found) {
+ found.linkedProviderKey = logins[login];
+ }
+ }
+ });
+ }
+ });
+ }
+
+ $scope.unlink = function (e, loginProvider, providerKey) {
+ var result = confirm("Are you sure you want to unlink this account?");
+ if (!result) {
+ e.preventDefault();
+ return;
+ }
+
+ authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) {
+ updateUserInfo();
+ });
+ }
+
+ updateUserInfo();
+
+ //remove all event handlers
+ $scope.$on('$destroy', function () {
+ for (var e = 0; e < evts.length; e++) {
+ evts[e]();
+ }
+
+ });
+
+ /* ---------- UPDATE PASSWORD ---------- */
+
+ //create the initial model for change password property editor
+ $scope.changePasswordModel = {
+ alias: "_umb_password",
+ view: "changepassword",
+ config: {},
+ value: {}
+ };
+
+ //go get the config for the membership provider and add it to the model
+ currentUserResource.getMembershipProviderConfig().then(function(data) {
+ $scope.changePasswordModel.config = data;
+ //ensure the hasPassword config option is set to true (the user of course has a password already assigned)
+ //this will ensure the oldPassword is shown so they can change it
+ // disable reset password functionality beacuse it does not make sense inside the backoffice
+ $scope.changePasswordModel.config.hasPassword = true;
+ $scope.changePasswordModel.config.disableToggle = true;
+ $scope.changePasswordModel.config.enableReset = false;
+ });
+
+ $scope.changePassword = function() {
+
+ if (formHelper.submitForm({ scope: $scope })) {
+
+ $scope.changePasswordButtonState = "busy";
+
+ currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) {
+
+ //if the password has been reset, then update our model
+ if (data.value) {
+ $scope.changePasswordModel.value.generatedPassword = data.value;
+ }
+
+ formHelper.resetForm({ scope: $scope, notifications: data.notifications });
+
+ $scope.changePasswordButtonState = "success";
+
+ }, function (err) {
+
+ formHelper.handleError(err);
+
+ $scope.changePasswordButtonState = "error";
+
+ });
+
+ }
+
+ };
+
+ $scope.togglePasswordFields = function() {
+ clearPasswordFields();
+ $scope.showPasswordFields = !$scope.showPasswordFields;
+ }
+
+ function clearPasswordFields() {
+ $scope.changePasswordModel.value.newPassword = "";
+ $scope.changePasswordModel.confirm = "";
+ }
+
+ });
/**
* @ngdoc controller
@@ -2194,1073 +2194,1073 @@ angular.module('umbraco').controller("Umbraco.LoginController", function (events
});
-//used for the media picker dialog
-angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeController",
- function ($scope, $location, $log, notificationsService) {
-
- $scope.discard = function(not){
- not.args.listener();
-
- $location.search("");
-
- //we need to break the path up into path and query
- var parts = not.args.path.split("?");
- var query = {};
- if (parts.length > 1) {
- _.each(parts[1].split("&"), function(q) {
- var keyVal = q.split("=");
- query[keyVal[0]] = keyVal[1];
- });
- }
-
- $location.path(parts[0]).search(query);
- notificationsService.remove(not);
- };
-
- $scope.stay = function(not){
- notificationsService.remove(not);
- };
-
+//used for the media picker dialog
+angular.module("umbraco").controller("Umbraco.Notifications.ConfirmRouteChangeController",
+ function ($scope, $location, $log, notificationsService) {
+
+ $scope.discard = function(not){
+ not.args.listener();
+
+ $location.search("");
+
+ //we need to break the path up into path and query
+ var parts = not.args.path.split("?");
+ var query = {};
+ if (parts.length > 1) {
+ _.each(parts[1].split("&"), function(q) {
+ var keyVal = q.split("=");
+ query[keyVal[0]] = keyVal[1];
+ });
+ }
+
+ $location.path(parts[0]).search(query);
+ notificationsService.remove(not);
+ };
+
+ $scope.stay = function(not){
+ notificationsService.remove(not);
+ };
+
});
- (function() {
- "use strict";
-
- function CompositionsOverlay($scope) {
-
- var vm = this;
-
- vm.isSelected = isSelected;
-
- function isSelected(alias) {
- if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) {
- return true;
- }
- }
- }
-
- angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay);
-
-})();
+ (function() {
+ "use strict";
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.PropertyController
- * @function
- *
- * @description
- * The controller for the content type editor property dialog
- */
-
-(function() {
- "use strict";
-
- function EditorPickerOverlay($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService) {
-
- var vm = this;
-
- if (!$scope.model.title) {
- $scope.model.title = localizationService.localize("defaultdialogs_selectEditor");
- }
-
- vm.searchTerm = "";
- vm.showTabs = false;
- vm.tabsLoaded = 0;
- vm.typesAndEditors = [];
- vm.userConfigured = [];
- vm.loading = false;
- vm.tabs = [{
- active: true,
- id: 1,
- label: localizationService.localize("contentTypeEditor_availableEditors"),
- alias: "Default",
- typesAndEditors: []
- }, {
- active: false,
- id: 2,
- label: localizationService.localize("contentTypeEditor_reuse"),
- alias: "Reuse",
- userConfigured: []
- }];
-
- vm.filterItems = filterItems;
- vm.showDetailsOverlay = showDetailsOverlay;
- vm.hideDetailsOverlay = hideDetailsOverlay;
- vm.pickEditor = pickEditor;
- vm.pickDataType = pickDataType;
-
- function activate() {
-
- getGroupedDataTypes();
- getGroupedPropertyEditors();
-
- }
-
- function getGroupedPropertyEditors() {
-
- vm.loading = true;
-
- dataTypeResource.getGroupedPropertyEditors().then(function(data) {
- vm.tabs[0].typesAndEditors = data;
- vm.typesAndEditors = data;
- vm.tabsLoaded = vm.tabsLoaded + 1;
- checkIfTabContentIsLoaded();
- });
-
- }
-
- function getGroupedDataTypes() {
-
- vm.loading = true;
-
- dataTypeResource.getGroupedDataTypes().then(function(data) {
- vm.tabs[1].userConfigured = data;
- vm.userConfigured = data;
- vm.tabsLoaded = vm.tabsLoaded + 1;
- checkIfTabContentIsLoaded();
- });
-
- }
-
- function checkIfTabContentIsLoaded() {
- if (vm.tabsLoaded === 2) {
- vm.loading = false;
- vm.showTabs = true;
- }
- }
-
- function filterItems() {
- // clear item details
- $scope.model.itemDetails = null;
-
- if (vm.searchTerm) {
- vm.showFilterResult = true;
- vm.showTabs = false;
- } else {
- vm.showFilterResult = false;
- vm.showTabs = true;
- }
-
- }
-
- function showDetailsOverlay(property) {
-
- var propertyDetails = {};
- propertyDetails.icon = property.icon;
- propertyDetails.title = property.name;
-
- $scope.model.itemDetails = propertyDetails;
-
- }
-
- function hideDetailsOverlay() {
- $scope.model.itemDetails = null;
- }
-
- function pickEditor(editor) {
-
- var parentId = -1;
-
- dataTypeResource.getScaffold(parentId).then(function(dataType) {
-
- // set alias
- dataType.selectedEditor = editor.alias;
-
- // set name
- var nameArray = [];
-
- if ($scope.model.contentTypeName) {
- nameArray.push($scope.model.contentTypeName);
- }
-
- if ($scope.model.property.label) {
- nameArray.push($scope.model.property.label);
- }
-
- if (editor.name) {
- nameArray.push(editor.name);
- }
-
- // make name
- dataType.name = nameArray.join(" - ");
-
- // get pre values
- dataTypeResource.getPreValues(dataType.selectedEditor).then(function(preValues) {
-
- dataType.preValues = preValues;
-
- openEditorSettingsOverlay(dataType, true);
-
- });
-
- });
-
- }
-
- function pickDataType(selectedDataType) {
-
- dataTypeResource.getById(selectedDataType.id).then(function(dataType) {
- contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
- submitOverlay(dataType, propertyType, false);
- });
- });
-
- }
-
- function openEditorSettingsOverlay(dataType, isNew) {
- vm.editorSettingsOverlay = {
- title: localizationService.localize("contentTypeEditor_editorSettings"),
- dataType: dataType,
- view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html",
- show: true,
- submit: function(model) {
- var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
-
- dataTypeResource.save(model.dataType, preValues, isNew).then(function(newDataType) {
-
- contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
-
- submitOverlay(newDataType, propertyType, true);
-
- vm.editorSettingsOverlay.show = false;
- vm.editorSettingsOverlay = null;
-
- });
-
- });
- }
- };
-
- }
-
- function submitOverlay(dataType, propertyType, isNew) {
-
- // update property
- $scope.model.property.config = propertyType.config;
- $scope.model.property.editor = propertyType.editor;
- $scope.model.property.view = propertyType.view;
- $scope.model.property.dataTypeId = dataType.id;
- $scope.model.property.dataTypeIcon = dataType.icon;
- $scope.model.property.dataTypeName = dataType.name;
-
- $scope.model.updateSameDataTypes = isNew;
-
- $scope.model.submit($scope.model);
-
- }
-
- activate();
-
- }
-
- angular.module("umbraco").controller("Umbraco.Overlays.EditorPickerOverlay", EditorPickerOverlay);
-
-})();
+ function CompositionsOverlay($scope) {
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.PropertyController
- * @function
- *
- * @description
- * The controller for the content type editor property dialog
- */
-
- (function() {
- "use strict";
-
- function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService) {
-
- var vm = this;
-
- vm.showValidationPattern = false;
- vm.focusOnPatternField = false;
- vm.focusOnMandatoryField = false;
- vm.selectedValidationType = {};
- vm.validationTypes = [
- {
- "name": localizationService.localize("validation_validateAsEmail"),
- "key": "email",
- "pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+",
- "enableEditing": true
- },
- {
- "name": localizationService.localize("validation_validateAsNumber"),
- "key": "number",
- "pattern": "^[0-9]*$",
- "enableEditing": true
- },
- {
- "name": localizationService.localize("validation_validateAsUrl"),
- "key": "url",
- "pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}",
- "enableEditing": true
- },
- {
- "name": localizationService.localize("validation_enterCustomValidation"),
- "key": "custom",
- "pattern": "",
- "enableEditing": true
- }
- ];
-
- vm.changeValidationType = changeValidationType;
- vm.changeValidationPattern = changeValidationPattern;
- vm.openEditorPickerOverlay = openEditorPickerOverlay;
- vm.openEditorSettingsOverlay = openEditorSettingsOverlay;
-
- function activate() {
-
- matchValidationType();
-
- }
-
- function changeValidationPattern() {
- matchValidationType();
- }
-
- function openEditorPickerOverlay(property) {
-
- vm.focusOnMandatoryField = false;
-
- vm.editorPickerOverlay = {};
- vm.editorPickerOverlay.property = $scope.model.property;
- vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName;
- vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html";
- vm.editorPickerOverlay.show = true;
-
- vm.editorPickerOverlay.submit = function(model) {
-
- $scope.model.updateSameDataTypes = model.updateSameDataTypes;
-
- vm.focusOnMandatoryField = true;
-
- // update property
- property.config = model.property.config;
- property.editor = model.property.editor;
- property.view = model.property.view;
- property.dataTypeId = model.property.dataTypeId;
- property.dataTypeIcon = model.property.dataTypeIcon;
- property.dataTypeName = model.property.dataTypeName;
-
- vm.editorPickerOverlay.show = false;
- vm.editorPickerOverlay = null;
- };
-
- vm.editorPickerOverlay.close = function(model) {
- vm.editorPickerOverlay.show = false;
- vm.editorPickerOverlay = null;
- };
-
- }
-
- function openEditorSettingsOverlay(property) {
-
- vm.focusOnMandatoryField = false;
-
- // get data type
- dataTypeResource.getById(property.dataTypeId).then(function(dataType) {
-
- vm.editorSettingsOverlay = {};
- vm.editorSettingsOverlay.title = "Editor settings";
- vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html";
- vm.editorSettingsOverlay.dataType = dataType;
- vm.editorSettingsOverlay.show = true;
-
- vm.editorSettingsOverlay.submit = function(model) {
-
- var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
-
- dataTypeResource.save(model.dataType, preValues, false).then(function(newDataType) {
-
- contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
-
- // update editor
- property.config = propertyType.config;
- property.editor = propertyType.editor;
- property.view = propertyType.view;
- property.dataTypeId = newDataType.id;
- property.dataTypeIcon = newDataType.icon;
- property.dataTypeName = newDataType.name;
-
- // set flag to update same data types
- $scope.model.updateSameDataTypes = true;
-
- vm.focusOnMandatoryField = true;
-
- vm.editorSettingsOverlay.show = false;
- vm.editorSettingsOverlay = null;
-
- });
-
- });
-
- };
-
- vm.editorSettingsOverlay.close = function(oldModel) {
- vm.editorSettingsOverlay.show = false;
- vm.editorSettingsOverlay = null;
- };
-
- });
-
- }
-
- function matchValidationType() {
-
- if($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) {
-
- var match = false;
-
- // find and show if a match from the list has been chosen
- angular.forEach(vm.validationTypes, function(validationType, index){
- if($scope.model.property.validation.pattern === validationType.pattern) {
- vm.selectedValidationType = vm.validationTypes[index];
- vm.showValidationPattern = true;
- match = true;
- }
- });
-
- // if there is no match - choose the custom validation option.
- if(!match) {
- angular.forEach(vm.validationTypes, function(validationType){
- if(validationType.key === "custom") {
- vm.selectedValidationType = validationType;
- vm.showValidationPattern = true;
- }
- });
- }
- }
-
- }
-
- function changeValidationType(selectedValidationType) {
-
- if(selectedValidationType) {
- $scope.model.property.validation.pattern = selectedValidationType.pattern;
- vm.showValidationPattern = true;
-
- // set focus on textarea
- if(selectedValidationType.key === "custom") {
- vm.focusOnPatternField = true;
- }
-
- } else {
- $scope.model.property.validation.pattern = "";
- vm.showValidationPattern = false;
- }
-
- }
-
- activate();
-
- }
-
- angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay);
-
-})();
+ var vm = this;
- (function() {
- "use strict";
-
- function CopyOverlay($scope, localizationService, eventsService) {
-
- var vm = this;
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("general_copy");
- }
-
- vm.hideSearch = hideSearch;
- vm.selectResult = selectResult;
- vm.onSearchResults = onSearchResults;
-
- var dialogOptions = $scope.model;
- var searchText = "Search...";
- var node = dialogOptions.currentNode;
-
- localizationService.localize("general_search").then(function (value) {
- searchText = value + "...";
- });
-
- $scope.model.relateToOriginal = true;
- $scope.dialogTreeEventHandler = $({});
-
- vm.searchInfo = {
- searchFromId: null,
- searchFromName: null,
- showSearch: false,
- results: [],
- selectedSearchResults: []
- };
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if (args.node.metaData.listViewNode) {
- //check if list view 'search' node was selected
-
- vm.searchInfo.showSearch = true;
- vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
- vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
- }
- else {
- //eventsService.emit("editors.content.copyController.select", args);
-
- if ($scope.model.target) {
- //un-select if there's a current one selected
- $scope.model.target.selected = false;
- }
-
- $scope.model.target = args.node;
- $scope.model.target.selected = true;
- }
-
- }
-
- function nodeExpandedHandler(ev, args) {
- if (angular.isArray(args.children)) {
-
- //iterate children
- _.each(args.children, function (child) {
- //check if any of the items are list views, if so we need to add a custom
- // child: A node to activate the search
- if (child.metaData.isContainer) {
- child.hasChildren = true;
- child.children = [
- {
- level: child.level + 1,
- hasChildren: false,
- name: searchText,
- metaData: {
- listViewNode: child,
- },
- cssClass: "icon umb-tree-icon sprTree icon-search",
- cssClasses: ["not-published"]
- }
- ];
- }
- });
- }
- }
-
- function hideSearch() {
- vm.searchInfo.showSearch = false;
- vm.searchInfo.searchFromId = null;
- vm.searchInfo.searchFromName = null;
- vm.searchInfo.results = [];
- }
-
- // method to select a search result
- function selectResult(evt, result) {
- result.selected = result.selected === true ? false : true;
- nodeSelectHandler(evt, { event: evt, node: result });
- }
-
- //callback when there are search results
- function onSearchResults(results) {
- vm.searchInfo.results = results;
- vm.searchInfo.showSearch = true;
- }
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
- });
-
-
- }
-
- angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay);
-
-})();
+ vm.isSelected = isSelected;
-(function() {
- "use strict";
-
- function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) {
-
- var vm = this;
- var origWidth = 500;
- var origHeight = 300;
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("general_embed");
- }
-
- $scope.model.embed = {
- url: "",
- width: 360,
- height: 240,
- constrain: true,
- preview: "",
- success: false,
- info: "",
- supportsDimensions: ""
- };
-
- vm.showPreview = showPreview;
- vm.changeSize = changeSize;
-
- function showPreview() {
-
- if ($scope.model.embed.url) {
- $scope.model.embed.show = true;
- $scope.model.embed.preview = "";
- $scope.model.embed.info = "";
- $scope.model.embed.success = false;
-
- $http({
- method: 'GET',
- url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"),
- params: {
- url: $scope.model.embed.url,
- width: $scope.model.embed.width,
- height: $scope.model.embed.height
- }
- })
- .success(function(data) {
-
- $scope.model.embed.preview = "";
-
- switch (data.Status) {
- case 0:
- //not supported
- $scope.model.embed.info = "Not supported";
- break;
- case 1:
- //error
- $scope.model.embed.info = "Computer says no";
- break;
- case 2:
- $scope.model.embed.preview = data.Markup;
- $scope.model.embed.supportsDimensions = data.SupportsDimensions;
- $scope.model.embed.success = true;
- break;
- }
- })
- .error(function() {
- $scope.model.embed.supportsDimensions = false;
- $scope.model.embed.preview = "";
- $scope.model.embed.info = "Computer says no";
- });
- } else {
- $scope.model.embed.supportsDimensions = false;
- $scope.model.embed.preview = "";
- $scope.model.embed.info = "Please enter a URL";
- }
- }
-
- function changeSize(type) {
-
- var width, height;
-
- if ($scope.model.embed.constrain) {
- width = parseInt($scope.model.embed.width, 10);
- height = parseInt($scope.model.embed.height, 10);
- if (type == 'width') {
- origHeight = Math.round((width / origWidth) * height);
- $scope.model.embed.height = origHeight;
- } else {
- origWidth = Math.round((height / origHeight) * width);
- $scope.model.embed.width = origWidth;
- }
- }
- if ($scope.model.embed.url !== "") {
- showPreview();
- }
-
- }
-
- }
-
- angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay);
-
-})();
+ function isSelected(alias) {
+ if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) {
+ return true;
+ }
+ }
+ }
-angular.module("umbraco")
- .controller("Umbraco.Overlays.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) {
- $scope.section = $routeParams.section;
- $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
- $scope.model.subtitle = "Umbraco version" + " " + $scope.version;
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("general_help");
- }
-
- if(!$scope.section){
- $scope.section = "content";
- }
-
- $scope.sectionName = $scope.section;
-
- var rq = {};
- rq.section = $scope.section;
-
- //translate section name
- localizationService.localize("sections_" + rq.section).then(function (value) {
- $scope.sectionName = value;
- });
-
- userService.getCurrentUser().then(function(user){
-
- rq.usertype = user.userType;
- rq.lang = user.locale;
-
- if($routeParams.url){
- rq.path = decodeURIComponent($routeParams.url);
-
- if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){
- rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length);
- }
-
- if(rq.path.indexOf(".aspx") > 0){
- rq.path = rq.path.substring(0, rq.path.indexOf(".aspx"));
- }
-
- }else{
- rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method;
- }
-
- helpService.findHelp(rq).then(function(topics){
- $scope.topics = topics;
- });
-
- helpService.findVideos(rq).then(function(videos){
- $scope.videos = videos;
- });
-
- });
-
-
- });
+ angular.module("umbraco").controller("Umbraco.Overlays.CompositionsOverlay", CompositionsOverlay);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.PropertyController
- * @function
- *
- * @description
- * The controller for the content type editor property dialog
- */
-function IconPickerOverlay($scope, iconHelper, localizationService) {
-
- $scope.loading = true;
- $scope.model.hideSubmitButton = true;
-
- if (!$scope.model.title) {
- $scope.model.title = localizationService.localize("defaultdialogs_selectIcon");
- }
-
- iconHelper.getIcons().then(function(icons) {
- $scope.icons = icons;
- $scope.loading = false;
- });
-
- $scope.selectIcon = function(icon, color) {
- $scope.model.icon = icon;
- $scope.model.color = color;
- $scope.submitForm($scope.model);
- };
-
-}
-
-angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay);
+})();
-function ItemPickerOverlay($scope, localizationService) {
-
- if (!$scope.model.title) {
- $scope.model.title = localizationService.localize("defaultdialogs_selectItem");
- }
-
- $scope.model.hideSubmitButton = true;
-
- $scope.selectItem = function(item) {
- $scope.model.selectedItem = item;
- $scope.submitForm($scope.model);
- };
-
-}
-
-angular.module("umbraco").controller("Umbraco.Overlays.ItemPickerOverlay", ItemPickerOverlay);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.PropertyController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor property dialog
+ */
-//used for the media picker dialog
-angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
- function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService) {
- var dialogOptions = $scope.model;
-
- var searchText = "Search...";
- localizationService.localize("general_search").then(function (value) {
- searchText = value + "...";
- });
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("defaultdialogs_selectLink");
- }
-
- $scope.dialogTreeEventHandler = $({});
- $scope.model.target = {};
- $scope.searchInfo = {
- searchFromId: null,
- searchFromName: null,
- showSearch: false,
- results: [],
- selectedSearchResults: []
- };
-
- if (dialogOptions.currentTarget) {
- $scope.model.target = dialogOptions.currentTarget;
-
- //if we have a node ID, we fetch the current node to build the form data
- if ($scope.model.target.id) {
-
- if (!$scope.model.target.path) {
- entityResource.getPath($scope.model.target.id, "Document").then(function (path) {
- $scope.model.target.path = path;
- //now sync the tree to this path
- $scope.dialogTreeEventHandler.syncTree({ path: $scope.model.target.path, tree: "content" });
- });
- }
-
- contentResource.getNiceUrl($scope.model.target.id).then(function (url) {
- $scope.model.target.url = url;
- });
- }
- }
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if (args.node.metaData.listViewNode) {
- //check if list view 'search' node was selected
-
- $scope.searchInfo.showSearch = true;
- $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
- $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
- }
- else {
- eventsService.emit("dialogs.linkPicker.select", args);
-
- if ($scope.currentNode) {
- //un-select if there's a current one selected
- $scope.currentNode.selected = false;
- }
-
- $scope.currentNode = args.node;
- $scope.currentNode.selected = true;
- $scope.model.target.id = args.node.id;
- $scope.model.target.name = args.node.name;
-
- if (args.node.id < 0) {
- $scope.model.target.url = "/";
- }
- else {
- contentResource.getNiceUrl(args.node.id).then(function (url) {
- $scope.model.target.url = url;
- });
- }
-
- if (!angular.isUndefined($scope.model.target.isMedia)) {
- delete $scope.model.target.isMedia;
- }
- }
- }
-
- function nodeExpandedHandler(ev, args) {
- if (angular.isArray(args.children)) {
-
- //iterate children
- _.each(args.children, function (child) {
- //check if any of the items are list views, if so we need to add a custom
- // child: A node to activate the search
- if (child.metaData.isContainer) {
- child.hasChildren = true;
- child.children = [
- {
- level: child.level + 1,
- hasChildren: false,
- name: searchText,
- metaData: {
- listViewNode: child,
- },
- cssClass: "icon umb-tree-icon sprTree icon-search",
- cssClasses: ["not-published"]
- }
- ];
- }
- });
- }
- }
-
- $scope.switchToMediaPicker = function () {
- userService.getCurrentUser().then(function (userData) {
- $scope.mediaPickerOverlay = {
- view: "mediapicker",
- startNodeId: userData.startMediaId,
- show: true,
- submit: function(model) {
- var media = model.selectedImages[0];
-
- $scope.model.target.id = media.id;
- $scope.model.target.isMedia = true;
- $scope.model.target.name = media.name;
- $scope.model.target.url = mediaHelper.resolveFile(media);
-
- $scope.mediaPickerOverlay.show = false;
- $scope.mediaPickerOverlay = null;
- }
- };
- });
- };
-
- $scope.hideSearch = function () {
- $scope.searchInfo.showSearch = false;
- $scope.searchInfo.searchFromId = null;
- $scope.searchInfo.searchFromName = null;
- $scope.searchInfo.results = [];
- }
-
- // method to select a search result
- $scope.selectResult = function (evt, result) {
- result.selected = result.selected === true ? false : true;
- nodeSelectHandler(evt, {event: evt, node: result});
- };
-
- //callback when there are search results
- $scope.onSearchResults = function (results) {
- $scope.searchInfo.results = results;
- $scope.searchInfo.showSearch = true;
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
- });
- });
+(function() {
+ "use strict";
-function MacroPickerController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper, localizationService) {
-
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("defaultdialogs_selectMacro");
- }
-
- $scope.macros = [];
- $scope.model.selectedMacro = null;
- $scope.wizardStep = "macroSelect";
- $scope.model.macroParams = [];
- $scope.noMacroParams = false;
-
- $scope.changeMacro = function() {
- if ($scope.wizardStep === "macroSelect") {
- editParams();
- } else {
- submitForm();
- }
- };
-
- /** changes the view to edit the params of the selected macro */
- function editParams() {
- //get the macro params if there are any
- macroResource.getMacroParameters($scope.model.selectedMacro.id)
- .then(function (data) {
-
- //go to next page if there are params otherwise we can just exit
- if (!angular.isArray(data) || data.length === 0) {
-
- $scope.noMacroParams = true;
-
- } else {
- $scope.wizardStep = "paramSelect";
- $scope.model.macroParams = data;
-
- //fill in the data if we are editing this macro
- if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroParamsDictionary) {
- _.each($scope.model.dialogData.macroData.macroParamsDictionary, function (val, key) {
- var prop = _.find($scope.model.macroParams, function (item) {
- return item.alias == key;
- });
- if (prop) {
-
- if (_.isString(val)) {
- //we need to unescape values as they have most likely been escaped while inserted
- val = _.unescape(val);
-
- //detect if it is a json string
- if (val.detectIsJson()) {
- try {
- //Parse it to json
- prop.value = angular.fromJson(val);
- }
- catch (e) {
- // not json
- prop.value = val;
- }
- }
- else {
- prop.value = val;
- }
- }
- else {
- prop.value = val;
- }
- }
- });
-
- }
- }
-
- });
- }
-
- //here we check to see if we've been passed a selected macro and if so we'll set the
- //editor to start with parameter editing
- if ($scope.model.dialogData && $scope.model.dialogData.macroData) {
- $scope.wizardStep = "paramSelect";
- }
-
- //get the macro list - pass in a filter if it is only for rte
- entityResource.getAll("Macro", ($scope.model.dialogData && $scope.model.dialogData.richTextEditor && $scope.model.dialogData.richTextEditor === true) ? "UseInEditor=true" : null)
- .then(function (data) {
-
- //if 'allowedMacros' is specified, we need to filter
- if (angular.isArray($scope.model.dialogData.allowedMacros) && $scope.model.dialogData.allowedMacros.length > 0) {
- $scope.macros = _.filter(data, function(d) {
- return _.contains($scope.model.dialogData.allowedMacros, d.alias);
- });
- }
- else {
- $scope.macros = data;
- }
-
-
- //check if there's a pre-selected macro and if it exists
- if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroAlias) {
- var found = _.find(data, function (item) {
- return item.alias === $scope.model.dialogData.macroData.macroAlias;
- });
- if (found) {
- //select the macro and go to next screen
- $scope.model.selectedMacro = found;
- editParams();
- return;
- }
- }
- //we don't have a pre-selected macro so ensure the correct step is set
- $scope.wizardStep = "macroSelect";
- });
-
-
-}
-
-angular.module("umbraco").controller("Umbraco.Overlays.MacroPickerController", MacroPickerController);
+ function EditorPickerOverlay($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService) {
+
+ var vm = this;
+
+ if (!$scope.model.title) {
+ $scope.model.title = localizationService.localize("defaultdialogs_selectEditor");
+ }
+
+ vm.searchTerm = "";
+ vm.showTabs = false;
+ vm.tabsLoaded = 0;
+ vm.typesAndEditors = [];
+ vm.userConfigured = [];
+ vm.loading = false;
+ vm.tabs = [{
+ active: true,
+ id: 1,
+ label: localizationService.localize("contentTypeEditor_availableEditors"),
+ alias: "Default",
+ typesAndEditors: []
+ }, {
+ active: false,
+ id: 2,
+ label: localizationService.localize("contentTypeEditor_reuse"),
+ alias: "Reuse",
+ userConfigured: []
+ }];
+
+ vm.filterItems = filterItems;
+ vm.showDetailsOverlay = showDetailsOverlay;
+ vm.hideDetailsOverlay = hideDetailsOverlay;
+ vm.pickEditor = pickEditor;
+ vm.pickDataType = pickDataType;
+
+ function activate() {
+
+ getGroupedDataTypes();
+ getGroupedPropertyEditors();
+
+ }
+
+ function getGroupedPropertyEditors() {
+
+ vm.loading = true;
+
+ dataTypeResource.getGroupedPropertyEditors().then(function(data) {
+ vm.tabs[0].typesAndEditors = data;
+ vm.typesAndEditors = data;
+ vm.tabsLoaded = vm.tabsLoaded + 1;
+ checkIfTabContentIsLoaded();
+ });
+
+ }
+
+ function getGroupedDataTypes() {
+
+ vm.loading = true;
+
+ dataTypeResource.getGroupedDataTypes().then(function(data) {
+ vm.tabs[1].userConfigured = data;
+ vm.userConfigured = data;
+ vm.tabsLoaded = vm.tabsLoaded + 1;
+ checkIfTabContentIsLoaded();
+ });
+
+ }
+
+ function checkIfTabContentIsLoaded() {
+ if (vm.tabsLoaded === 2) {
+ vm.loading = false;
+ vm.showTabs = true;
+ }
+ }
+
+ function filterItems() {
+ // clear item details
+ $scope.model.itemDetails = null;
+
+ if (vm.searchTerm) {
+ vm.showFilterResult = true;
+ vm.showTabs = false;
+ } else {
+ vm.showFilterResult = false;
+ vm.showTabs = true;
+ }
+
+ }
+
+ function showDetailsOverlay(property) {
+
+ var propertyDetails = {};
+ propertyDetails.icon = property.icon;
+ propertyDetails.title = property.name;
+
+ $scope.model.itemDetails = propertyDetails;
+
+ }
+
+ function hideDetailsOverlay() {
+ $scope.model.itemDetails = null;
+ }
+
+ function pickEditor(editor) {
+
+ var parentId = -1;
+
+ dataTypeResource.getScaffold(parentId).then(function(dataType) {
+
+ // set alias
+ dataType.selectedEditor = editor.alias;
+
+ // set name
+ var nameArray = [];
+
+ if ($scope.model.contentTypeName) {
+ nameArray.push($scope.model.contentTypeName);
+ }
+
+ if ($scope.model.property.label) {
+ nameArray.push($scope.model.property.label);
+ }
+
+ if (editor.name) {
+ nameArray.push(editor.name);
+ }
+
+ // make name
+ dataType.name = nameArray.join(" - ");
+
+ // get pre values
+ dataTypeResource.getPreValues(dataType.selectedEditor).then(function(preValues) {
+
+ dataType.preValues = preValues;
+
+ openEditorSettingsOverlay(dataType, true);
+
+ });
+
+ });
+
+ }
+
+ function pickDataType(selectedDataType) {
+
+ dataTypeResource.getById(selectedDataType.id).then(function(dataType) {
+ contentTypeResource.getPropertyTypeScaffold(dataType.id).then(function(propertyType) {
+ submitOverlay(dataType, propertyType, false);
+ });
+ });
+
+ }
+
+ function openEditorSettingsOverlay(dataType, isNew) {
+ vm.editorSettingsOverlay = {
+ title: localizationService.localize("contentTypeEditor_editorSettings"),
+ dataType: dataType,
+ view: "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html",
+ show: true,
+ submit: function(model) {
+ var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
+
+ dataTypeResource.save(model.dataType, preValues, isNew).then(function(newDataType) {
+
+ contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
+
+ submitOverlay(newDataType, propertyType, true);
+
+ vm.editorSettingsOverlay.show = false;
+ vm.editorSettingsOverlay = null;
+
+ });
+
+ });
+ }
+ };
+
+ }
+
+ function submitOverlay(dataType, propertyType, isNew) {
+
+ // update property
+ $scope.model.property.config = propertyType.config;
+ $scope.model.property.editor = propertyType.editor;
+ $scope.model.property.view = propertyType.view;
+ $scope.model.property.dataTypeId = dataType.id;
+ $scope.model.property.dataTypeIcon = dataType.icon;
+ $scope.model.property.dataTypeName = dataType.name;
+
+ $scope.model.updateSameDataTypes = isNew;
+
+ $scope.model.submit($scope.model);
+
+ }
+
+ activate();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Overlays.EditorPickerOverlay", EditorPickerOverlay);
+
+})();
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.PropertyController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor property dialog
+ */
+
+ (function() {
+ "use strict";
+
+ function PropertySettingsOverlay($scope, contentTypeResource, dataTypeResource, dataTypeHelper, localizationService) {
+
+ var vm = this;
+
+ vm.showValidationPattern = false;
+ vm.focusOnPatternField = false;
+ vm.focusOnMandatoryField = false;
+ vm.selectedValidationType = {};
+ vm.validationTypes = [
+ {
+ "name": localizationService.localize("validation_validateAsEmail"),
+ "key": "email",
+ "pattern": "[a-zA-Z0-9_\.\+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-\.]+",
+ "enableEditing": true
+ },
+ {
+ "name": localizationService.localize("validation_validateAsNumber"),
+ "key": "number",
+ "pattern": "^[0-9]*$",
+ "enableEditing": true
+ },
+ {
+ "name": localizationService.localize("validation_validateAsUrl"),
+ "key": "url",
+ "pattern": "https?\:\/\/[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}",
+ "enableEditing": true
+ },
+ {
+ "name": localizationService.localize("validation_enterCustomValidation"),
+ "key": "custom",
+ "pattern": "",
+ "enableEditing": true
+ }
+ ];
+
+ vm.changeValidationType = changeValidationType;
+ vm.changeValidationPattern = changeValidationPattern;
+ vm.openEditorPickerOverlay = openEditorPickerOverlay;
+ vm.openEditorSettingsOverlay = openEditorSettingsOverlay;
+
+ function activate() {
+
+ matchValidationType();
+
+ }
+
+ function changeValidationPattern() {
+ matchValidationType();
+ }
+
+ function openEditorPickerOverlay(property) {
+
+ vm.focusOnMandatoryField = false;
+
+ vm.editorPickerOverlay = {};
+ vm.editorPickerOverlay.property = $scope.model.property;
+ vm.editorPickerOverlay.contentTypeName = $scope.model.contentTypeName;
+ vm.editorPickerOverlay.view = "views/common/overlays/contenttypeeditor/editorpicker/editorpicker.html";
+ vm.editorPickerOverlay.show = true;
+
+ vm.editorPickerOverlay.submit = function(model) {
+
+ $scope.model.updateSameDataTypes = model.updateSameDataTypes;
+
+ vm.focusOnMandatoryField = true;
+
+ // update property
+ property.config = model.property.config;
+ property.editor = model.property.editor;
+ property.view = model.property.view;
+ property.dataTypeId = model.property.dataTypeId;
+ property.dataTypeIcon = model.property.dataTypeIcon;
+ property.dataTypeName = model.property.dataTypeName;
+
+ vm.editorPickerOverlay.show = false;
+ vm.editorPickerOverlay = null;
+ };
+
+ vm.editorPickerOverlay.close = function(model) {
+ vm.editorPickerOverlay.show = false;
+ vm.editorPickerOverlay = null;
+ };
+
+ }
+
+ function openEditorSettingsOverlay(property) {
+
+ vm.focusOnMandatoryField = false;
+
+ // get data type
+ dataTypeResource.getById(property.dataTypeId).then(function(dataType) {
+
+ vm.editorSettingsOverlay = {};
+ vm.editorSettingsOverlay.title = "Editor settings";
+ vm.editorSettingsOverlay.view = "views/common/overlays/contenttypeeditor/editorsettings/editorsettings.html";
+ vm.editorSettingsOverlay.dataType = dataType;
+ vm.editorSettingsOverlay.show = true;
+
+ vm.editorSettingsOverlay.submit = function(model) {
+
+ var preValues = dataTypeHelper.createPreValueProps(model.dataType.preValues);
+
+ dataTypeResource.save(model.dataType, preValues, false).then(function(newDataType) {
+
+ contentTypeResource.getPropertyTypeScaffold(newDataType.id).then(function(propertyType) {
+
+ // update editor
+ property.config = propertyType.config;
+ property.editor = propertyType.editor;
+ property.view = propertyType.view;
+ property.dataTypeId = newDataType.id;
+ property.dataTypeIcon = newDataType.icon;
+ property.dataTypeName = newDataType.name;
+
+ // set flag to update same data types
+ $scope.model.updateSameDataTypes = true;
+
+ vm.focusOnMandatoryField = true;
+
+ vm.editorSettingsOverlay.show = false;
+ vm.editorSettingsOverlay = null;
+
+ });
+
+ });
+
+ };
+
+ vm.editorSettingsOverlay.close = function(oldModel) {
+ vm.editorSettingsOverlay.show = false;
+ vm.editorSettingsOverlay = null;
+ };
+
+ });
+
+ }
+
+ function matchValidationType() {
+
+ if($scope.model.property.validation.pattern !== null && $scope.model.property.validation.pattern !== "" && $scope.model.property.validation.pattern !== undefined) {
+
+ var match = false;
+
+ // find and show if a match from the list has been chosen
+ angular.forEach(vm.validationTypes, function(validationType, index){
+ if($scope.model.property.validation.pattern === validationType.pattern) {
+ vm.selectedValidationType = vm.validationTypes[index];
+ vm.showValidationPattern = true;
+ match = true;
+ }
+ });
+
+ // if there is no match - choose the custom validation option.
+ if(!match) {
+ angular.forEach(vm.validationTypes, function(validationType){
+ if(validationType.key === "custom") {
+ vm.selectedValidationType = validationType;
+ vm.showValidationPattern = true;
+ }
+ });
+ }
+ }
+
+ }
+
+ function changeValidationType(selectedValidationType) {
+
+ if(selectedValidationType) {
+ $scope.model.property.validation.pattern = selectedValidationType.pattern;
+ vm.showValidationPattern = true;
+
+ // set focus on textarea
+ if(selectedValidationType.key === "custom") {
+ vm.focusOnPatternField = true;
+ }
+
+ } else {
+ $scope.model.property.validation.pattern = "";
+ vm.showValidationPattern = false;
+ }
+
+ }
+
+ activate();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Overlay.PropertySettingsOverlay", PropertySettingsOverlay);
+
+})();
+
+ (function() {
+ "use strict";
+
+ function CopyOverlay($scope, localizationService, eventsService) {
+
+ var vm = this;
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("general_copy");
+ }
+
+ vm.hideSearch = hideSearch;
+ vm.selectResult = selectResult;
+ vm.onSearchResults = onSearchResults;
+
+ var dialogOptions = $scope.model;
+ var searchText = "Search...";
+ var node = dialogOptions.currentNode;
+
+ localizationService.localize("general_search").then(function (value) {
+ searchText = value + "...";
+ });
+
+ $scope.model.relateToOriginal = true;
+ $scope.dialogTreeEventHandler = $({});
+
+ vm.searchInfo = {
+ searchFromId: null,
+ searchFromName: null,
+ showSearch: false,
+ results: [],
+ selectedSearchResults: []
+ };
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if (args.node.metaData.listViewNode) {
+ //check if list view 'search' node was selected
+
+ vm.searchInfo.showSearch = true;
+ vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
+ vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
+ }
+ else {
+ //eventsService.emit("editors.content.copyController.select", args);
+
+ if ($scope.model.target) {
+ //un-select if there's a current one selected
+ $scope.model.target.selected = false;
+ }
+
+ $scope.model.target = args.node;
+ $scope.model.target.selected = true;
+ }
+
+ }
+
+ function nodeExpandedHandler(ev, args) {
+ if (angular.isArray(args.children)) {
+
+ //iterate children
+ _.each(args.children, function (child) {
+ //check if any of the items are list views, if so we need to add a custom
+ // child: A node to activate the search
+ if (child.metaData.isContainer) {
+ child.hasChildren = true;
+ child.children = [
+ {
+ level: child.level + 1,
+ hasChildren: false,
+ name: searchText,
+ metaData: {
+ listViewNode: child,
+ },
+ cssClass: "icon umb-tree-icon sprTree icon-search",
+ cssClasses: ["not-published"]
+ }
+ ];
+ }
+ });
+ }
+ }
+
+ function hideSearch() {
+ vm.searchInfo.showSearch = false;
+ vm.searchInfo.searchFromId = null;
+ vm.searchInfo.searchFromName = null;
+ vm.searchInfo.results = [];
+ }
+
+ // method to select a search result
+ function selectResult(evt, result) {
+ result.selected = result.selected === true ? false : true;
+ nodeSelectHandler(evt, { event: evt, node: result });
+ }
+
+ //callback when there are search results
+ function onSearchResults(results) {
+ vm.searchInfo.results = results;
+ vm.searchInfo.showSearch = true;
+ }
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
+ });
+
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Overlays.CopyOverlay", CopyOverlay);
+
+})();
+
+(function() {
+ "use strict";
+
+ function EmbedOverlay($scope, $http, umbRequestHelper, localizationService) {
+
+ var vm = this;
+ var origWidth = 500;
+ var origHeight = 300;
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("general_embed");
+ }
+
+ $scope.model.embed = {
+ url: "",
+ width: 360,
+ height: 240,
+ constrain: true,
+ preview: "",
+ success: false,
+ info: "",
+ supportsDimensions: ""
+ };
+
+ vm.showPreview = showPreview;
+ vm.changeSize = changeSize;
+
+ function showPreview() {
+
+ if ($scope.model.embed.url) {
+ $scope.model.embed.show = true;
+ $scope.model.embed.preview = "";
+ $scope.model.embed.info = "";
+ $scope.model.embed.success = false;
+
+ $http({
+ method: 'GET',
+ url: umbRequestHelper.getApiUrl("embedApiBaseUrl", "GetEmbed"),
+ params: {
+ url: $scope.model.embed.url,
+ width: $scope.model.embed.width,
+ height: $scope.model.embed.height
+ }
+ })
+ .success(function(data) {
+
+ $scope.model.embed.preview = "";
+
+ switch (data.Status) {
+ case 0:
+ //not supported
+ $scope.model.embed.info = "Not supported";
+ break;
+ case 1:
+ //error
+ $scope.model.embed.info = "Computer says no";
+ break;
+ case 2:
+ $scope.model.embed.preview = data.Markup;
+ $scope.model.embed.supportsDimensions = data.SupportsDimensions;
+ $scope.model.embed.success = true;
+ break;
+ }
+ })
+ .error(function() {
+ $scope.model.embed.supportsDimensions = false;
+ $scope.model.embed.preview = "";
+ $scope.model.embed.info = "Computer says no";
+ });
+ } else {
+ $scope.model.embed.supportsDimensions = false;
+ $scope.model.embed.preview = "";
+ $scope.model.embed.info = "Please enter a URL";
+ }
+ }
+
+ function changeSize(type) {
+
+ var width, height;
+
+ if ($scope.model.embed.constrain) {
+ width = parseInt($scope.model.embed.width, 10);
+ height = parseInt($scope.model.embed.height, 10);
+ if (type == 'width') {
+ origHeight = Math.round((width / origWidth) * height);
+ $scope.model.embed.height = origHeight;
+ } else {
+ origWidth = Math.round((height / origHeight) * width);
+ $scope.model.embed.width = origWidth;
+ }
+ }
+ if ($scope.model.embed.url !== "") {
+ showPreview();
+ }
+
+ }
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Overlays.EmbedOverlay", EmbedOverlay);
+
+})();
+
+angular.module("umbraco")
+ .controller("Umbraco.Overlays.HelpController", function ($scope, $location, $routeParams, helpService, userService, localizationService) {
+ $scope.section = $routeParams.section;
+ $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
+ $scope.model.subtitle = "Umbraco version" + " " + $scope.version;
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("general_help");
+ }
+
+ if(!$scope.section){
+ $scope.section = "content";
+ }
+
+ $scope.sectionName = $scope.section;
+
+ var rq = {};
+ rq.section = $scope.section;
+
+ //translate section name
+ localizationService.localize("sections_" + rq.section).then(function (value) {
+ $scope.sectionName = value;
+ });
+
+ userService.getCurrentUser().then(function(user){
+
+ rq.usertype = user.userType;
+ rq.lang = user.locale;
+
+ if($routeParams.url){
+ rq.path = decodeURIComponent($routeParams.url);
+
+ if(rq.path.indexOf(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath) === 0){
+ rq.path = rq.path.substring(Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath.length);
+ }
+
+ if(rq.path.indexOf(".aspx") > 0){
+ rq.path = rq.path.substring(0, rq.path.indexOf(".aspx"));
+ }
+
+ }else{
+ rq.path = rq.section + "/" + $routeParams.tree + "/" + $routeParams.method;
+ }
+
+ helpService.findHelp(rq).then(function(topics){
+ $scope.topics = topics;
+ });
+
+ helpService.findVideos(rq).then(function(videos){
+ $scope.videos = videos;
+ });
+
+ });
+
+
+ });
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.PropertyController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor property dialog
+ */
+function IconPickerOverlay($scope, iconHelper, localizationService) {
+
+ $scope.loading = true;
+ $scope.model.hideSubmitButton = true;
+
+ if (!$scope.model.title) {
+ $scope.model.title = localizationService.localize("defaultdialogs_selectIcon");
+ }
+
+ iconHelper.getIcons().then(function(icons) {
+ $scope.icons = icons;
+ $scope.loading = false;
+ });
+
+ $scope.selectIcon = function(icon, color) {
+ $scope.model.icon = icon;
+ $scope.model.color = color;
+ $scope.submitForm($scope.model);
+ };
+
+}
+
+angular.module("umbraco").controller("Umbraco.Overlays.IconPickerOverlay", IconPickerOverlay);
+
+function ItemPickerOverlay($scope, localizationService) {
+
+ if (!$scope.model.title) {
+ $scope.model.title = localizationService.localize("defaultdialogs_selectItem");
+ }
+
+ $scope.model.hideSubmitButton = true;
+
+ $scope.selectItem = function(item) {
+ $scope.model.selectedItem = item;
+ $scope.submitForm($scope.model);
+ };
+
+}
+
+angular.module("umbraco").controller("Umbraco.Overlays.ItemPickerOverlay", ItemPickerOverlay);
+
+//used for the media picker dialog
+angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
+ function ($scope, eventsService, dialogService, entityResource, contentResource, mediaHelper, userService, localizationService) {
+ var dialogOptions = $scope.model;
+
+ var searchText = "Search...";
+ localizationService.localize("general_search").then(function (value) {
+ searchText = value + "...";
+ });
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("defaultdialogs_selectLink");
+ }
+
+ $scope.dialogTreeEventHandler = $({});
+ $scope.model.target = {};
+ $scope.searchInfo = {
+ searchFromId: null,
+ searchFromName: null,
+ showSearch: false,
+ results: [],
+ selectedSearchResults: []
+ };
+
+ if (dialogOptions.currentTarget) {
+ $scope.model.target = dialogOptions.currentTarget;
+
+ //if we have a node ID, we fetch the current node to build the form data
+ if ($scope.model.target.id) {
+
+ if (!$scope.model.target.path) {
+ entityResource.getPath($scope.model.target.id, "Document").then(function (path) {
+ $scope.model.target.path = path;
+ //now sync the tree to this path
+ $scope.dialogTreeEventHandler.syncTree({ path: $scope.model.target.path, tree: "content" });
+ });
+ }
+
+ contentResource.getNiceUrl($scope.model.target.id).then(function (url) {
+ $scope.model.target.url = url;
+ });
+ }
+ }
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if (args.node.metaData.listViewNode) {
+ //check if list view 'search' node was selected
+
+ $scope.searchInfo.showSearch = true;
+ $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
+ $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
+ }
+ else {
+ eventsService.emit("dialogs.linkPicker.select", args);
+
+ if ($scope.currentNode) {
+ //un-select if there's a current one selected
+ $scope.currentNode.selected = false;
+ }
+
+ $scope.currentNode = args.node;
+ $scope.currentNode.selected = true;
+ $scope.model.target.id = args.node.id;
+ $scope.model.target.name = args.node.name;
+
+ if (args.node.id < 0) {
+ $scope.model.target.url = "/";
+ }
+ else {
+ contentResource.getNiceUrl(args.node.id).then(function (url) {
+ $scope.model.target.url = url;
+ });
+ }
+
+ if (!angular.isUndefined($scope.model.target.isMedia)) {
+ delete $scope.model.target.isMedia;
+ }
+ }
+ }
+
+ function nodeExpandedHandler(ev, args) {
+ if (angular.isArray(args.children)) {
+
+ //iterate children
+ _.each(args.children, function (child) {
+ //check if any of the items are list views, if so we need to add a custom
+ // child: A node to activate the search
+ if (child.metaData.isContainer) {
+ child.hasChildren = true;
+ child.children = [
+ {
+ level: child.level + 1,
+ hasChildren: false,
+ name: searchText,
+ metaData: {
+ listViewNode: child,
+ },
+ cssClass: "icon umb-tree-icon sprTree icon-search",
+ cssClasses: ["not-published"]
+ }
+ ];
+ }
+ });
+ }
+ }
+
+ $scope.switchToMediaPicker = function () {
+ userService.getCurrentUser().then(function (userData) {
+ $scope.mediaPickerOverlay = {
+ view: "mediapicker",
+ startNodeId: userData.startMediaId,
+ show: true,
+ submit: function(model) {
+ var media = model.selectedImages[0];
+
+ $scope.model.target.id = media.id;
+ $scope.model.target.isMedia = true;
+ $scope.model.target.name = media.name;
+ $scope.model.target.url = mediaHelper.resolveFile(media);
+
+ $scope.mediaPickerOverlay.show = false;
+ $scope.mediaPickerOverlay = null;
+ }
+ };
+ });
+ };
+
+ $scope.hideSearch = function () {
+ $scope.searchInfo.showSearch = false;
+ $scope.searchInfo.searchFromId = null;
+ $scope.searchInfo.searchFromName = null;
+ $scope.searchInfo.results = [];
+ }
+
+ // method to select a search result
+ $scope.selectResult = function (evt, result) {
+ result.selected = result.selected === true ? false : true;
+ nodeSelectHandler(evt, {event: evt, node: result});
+ };
+
+ //callback when there are search results
+ $scope.onSearchResults = function (results) {
+ $scope.searchInfo.results = results;
+ $scope.searchInfo.showSearch = true;
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
+ });
+ });
+
+function MacroPickerController($scope, entityResource, macroResource, umbPropEditorHelper, macroService, formHelper, localizationService) {
+
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("defaultdialogs_selectMacro");
+ }
+
+ $scope.macros = [];
+ $scope.model.selectedMacro = null;
+ $scope.wizardStep = "macroSelect";
+ $scope.model.macroParams = [];
+ $scope.noMacroParams = false;
+
+ $scope.changeMacro = function() {
+ if ($scope.wizardStep === "macroSelect") {
+ editParams();
+ } else {
+ submitForm();
+ }
+ };
+
+ /** changes the view to edit the params of the selected macro */
+ function editParams() {
+ //get the macro params if there are any
+ macroResource.getMacroParameters($scope.model.selectedMacro.id)
+ .then(function (data) {
+
+ //go to next page if there are params otherwise we can just exit
+ if (!angular.isArray(data) || data.length === 0) {
+
+ $scope.noMacroParams = true;
+
+ } else {
+ $scope.wizardStep = "paramSelect";
+ $scope.model.macroParams = data;
+
+ //fill in the data if we are editing this macro
+ if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroParamsDictionary) {
+ _.each($scope.model.dialogData.macroData.macroParamsDictionary, function (val, key) {
+ var prop = _.find($scope.model.macroParams, function (item) {
+ return item.alias == key;
+ });
+ if (prop) {
+
+ if (_.isString(val)) {
+ //we need to unescape values as they have most likely been escaped while inserted
+ val = _.unescape(val);
+
+ //detect if it is a json string
+ if (val.detectIsJson()) {
+ try {
+ //Parse it to json
+ prop.value = angular.fromJson(val);
+ }
+ catch (e) {
+ // not json
+ prop.value = val;
+ }
+ }
+ else {
+ prop.value = val;
+ }
+ }
+ else {
+ prop.value = val;
+ }
+ }
+ });
+
+ }
+ }
+
+ });
+ }
+
+ //here we check to see if we've been passed a selected macro and if so we'll set the
+ //editor to start with parameter editing
+ if ($scope.model.dialogData && $scope.model.dialogData.macroData) {
+ $scope.wizardStep = "paramSelect";
+ }
+
+ //get the macro list - pass in a filter if it is only for rte
+ entityResource.getAll("Macro", ($scope.model.dialogData && $scope.model.dialogData.richTextEditor && $scope.model.dialogData.richTextEditor === true) ? "UseInEditor=true" : null)
+ .then(function (data) {
+
+ //if 'allowedMacros' is specified, we need to filter
+ if (angular.isArray($scope.model.dialogData.allowedMacros) && $scope.model.dialogData.allowedMacros.length > 0) {
+ $scope.macros = _.filter(data, function(d) {
+ return _.contains($scope.model.dialogData.allowedMacros, d.alias);
+ });
+ }
+ else {
+ $scope.macros = data;
+ }
+
+
+ //check if there's a pre-selected macro and if it exists
+ if ($scope.model.dialogData && $scope.model.dialogData.macroData && $scope.model.dialogData.macroData.macroAlias) {
+ var found = _.find(data, function (item) {
+ return item.alias === $scope.model.dialogData.macroData.macroAlias;
+ });
+ if (found) {
+ //select the macro and go to next screen
+ $scope.model.selectedMacro = found;
+ editParams();
+ return;
+ }
+ }
+ //we don't have a pre-selected macro so ensure the correct step is set
+ $scope.wizardStep = "macroSelect";
+ });
+
+
+}
+
+angular.module("umbraco").controller("Umbraco.Overlays.MacroPickerController", MacroPickerController);
//used for the media picker dialog
angular.module("umbraco")
@@ -3586,124 +3586,124 @@ angular.module("umbraco").controller("Umbraco.Overlays.MemberGroupPickerControll
});
- (function() {
- "use strict";
-
- function MoveOverlay($scope, localizationService, eventsService) {
-
- var vm = this;
-
- vm.hideSearch = hideSearch;
- vm.selectResult = selectResult;
- vm.onSearchResults = onSearchResults;
-
- var dialogOptions = $scope.model;
- var searchText = "Search...";
- var node = dialogOptions.currentNode;
-
- localizationService.localize("general_search").then(function (value) {
- searchText = value + "...";
- });
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("actions_move");
- }
-
- $scope.model.relateToOriginal = true;
- $scope.dialogTreeEventHandler = $({});
-
- vm.searchInfo = {
- searchFromId: null,
- searchFromName: null,
- showSearch: false,
- results: [],
- selectedSearchResults: []
- };
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if (args.node.metaData.listViewNode) {
- //check if list view 'search' node was selected
-
- vm.searchInfo.showSearch = true;
- vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
- vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
- }
- else {
- //eventsService.emit("editors.content.copyController.select", args);
-
- if ($scope.model.target) {
- //un-select if there's a current one selected
- $scope.model.target.selected = false;
- }
-
- $scope.model.target = args.node;
- $scope.model.target.selected = true;
- }
-
- }
-
- function nodeExpandedHandler(ev, args) {
- if (angular.isArray(args.children)) {
-
- //iterate children
- _.each(args.children, function (child) {
- //check if any of the items are list views, if so we need to add a custom
- // child: A node to activate the search
- if (child.metaData.isContainer) {
- child.hasChildren = true;
- child.children = [
- {
- level: child.level + 1,
- hasChildren: false,
- name: searchText,
- metaData: {
- listViewNode: child,
- },
- cssClass: "icon umb-tree-icon sprTree icon-search",
- cssClasses: ["not-published"]
- }
- ];
- }
- });
- }
- }
-
- function hideSearch() {
- vm.searchInfo.showSearch = false;
- vm.searchInfo.searchFromId = null;
- vm.searchInfo.searchFromName = null;
- vm.searchInfo.results = [];
- }
-
- // method to select a search result
- function selectResult(evt, result) {
- result.selected = result.selected === true ? false : true;
- nodeSelectHandler(evt, { event: evt, node: result });
- }
-
- //callback when there are search results
- function onSearchResults(results) {
- vm.searchInfo.results = results;
- vm.searchInfo.showSearch = true;
- }
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
- });
-
-
- }
-
- angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay);
-
-})();
+ (function() {
+ "use strict";
+
+ function MoveOverlay($scope, localizationService, eventsService) {
+
+ var vm = this;
+
+ vm.hideSearch = hideSearch;
+ vm.selectResult = selectResult;
+ vm.onSearchResults = onSearchResults;
+
+ var dialogOptions = $scope.model;
+ var searchText = "Search...";
+ var node = dialogOptions.currentNode;
+
+ localizationService.localize("general_search").then(function (value) {
+ searchText = value + "...";
+ });
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("actions_move");
+ }
+
+ $scope.model.relateToOriginal = true;
+ $scope.dialogTreeEventHandler = $({});
+
+ vm.searchInfo = {
+ searchFromId: null,
+ searchFromName: null,
+ showSearch: false,
+ results: [],
+ selectedSearchResults: []
+ };
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if (args.node.metaData.listViewNode) {
+ //check if list view 'search' node was selected
+
+ vm.searchInfo.showSearch = true;
+ vm.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
+ vm.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
+ }
+ else {
+ //eventsService.emit("editors.content.copyController.select", args);
+
+ if ($scope.model.target) {
+ //un-select if there's a current one selected
+ $scope.model.target.selected = false;
+ }
+
+ $scope.model.target = args.node;
+ $scope.model.target.selected = true;
+ }
+
+ }
+
+ function nodeExpandedHandler(ev, args) {
+ if (angular.isArray(args.children)) {
+
+ //iterate children
+ _.each(args.children, function (child) {
+ //check if any of the items are list views, if so we need to add a custom
+ // child: A node to activate the search
+ if (child.metaData.isContainer) {
+ child.hasChildren = true;
+ child.children = [
+ {
+ level: child.level + 1,
+ hasChildren: false,
+ name: searchText,
+ metaData: {
+ listViewNode: child,
+ },
+ cssClass: "icon umb-tree-icon sprTree icon-search",
+ cssClasses: ["not-published"]
+ }
+ ];
+ }
+ });
+ }
+ }
+
+ function hideSearch() {
+ vm.searchInfo.showSearch = false;
+ vm.searchInfo.searchFromId = null;
+ vm.searchInfo.searchFromName = null;
+ vm.searchInfo.results = [];
+ }
+
+ // method to select a search result
+ function selectResult(evt, result) {
+ result.selected = result.selected === true ? false : true;
+ nodeSelectHandler(evt, { event: evt, node: result });
+ }
+
+ //callback when there are search results
+ function onSearchResults(results) {
+ vm.searchInfo.results = results;
+ vm.searchInfo.showSearch = true;
+ }
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
+ });
+
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Overlays.MoveOverlay", MoveOverlay);
+
+})();
//used for the media picker dialog
angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
@@ -4204,182 +4204,182 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
});
});
-angular.module("umbraco")
- .controller("Umbraco.Overlays.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper, localizationService) {
-
- $scope.history = historyService.getCurrent();
- $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
- $scope.showPasswordFields = false;
- $scope.changePasswordButtonState = "init";
- $scope.model.subtitle = "Umbraco version" + " " + $scope.version;
-
- if(!$scope.model.title) {
- $scope.model.title = localizationService.localize("general_user");
- }
-
- $scope.externalLoginProviders = externalLoginInfo.providers;
- $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl;
- var evts = [];
- evts.push(eventsService.on("historyService.add", function (e, args) {
- $scope.history = args.all;
- }));
- evts.push(eventsService.on("historyService.remove", function (e, args) {
- $scope.history = args.all;
- }));
- evts.push(eventsService.on("historyService.removeAll", function (e, args) {
- $scope.history = [];
- }));
-
- $scope.logout = function () {
-
- //Add event listener for when there are pending changes on an editor which means our route was not successful
- var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) {
- //one time listener, remove the event
- pendingChangeEvent();
- $scope.model.close();
- });
-
-
- //perform the path change, if it is successful then the promise will resolve otherwise it will fail
- $scope.model.close();
- $location.path("/logout");
- };
-
- $scope.gotoHistory = function (link) {
- $location.path(link);
- $scope.model.close();
- };
-
- //Manually update the remaining timeout seconds
- function updateTimeout() {
- $timeout(function () {
- if ($scope.remainingAuthSeconds > 0) {
- $scope.remainingAuthSeconds--;
- $scope.$digest();
- //recurse
- updateTimeout();
- }
-
- }, 1000, false); // 1 second, do NOT execute a global digest
- }
-
- function updateUserInfo() {
- //get the user
- userService.getCurrentUser().then(function (user) {
- $scope.user = user;
- if ($scope.user) {
- $scope.model.title = user.name;
- $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
- $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
- //set the timer
- updateTimeout();
-
- authResource.getCurrentUserLinkedLogins().then(function(logins) {
- //reset all to be un-linked
- for (var provider in $scope.externalLoginProviders) {
- $scope.externalLoginProviders[provider].linkedProviderKey = undefined;
- }
-
- //set the linked logins
- for (var login in logins) {
- var found = _.find($scope.externalLoginProviders, function (i) {
- return i.authType == login;
- });
- if (found) {
- found.linkedProviderKey = logins[login];
- }
- }
- });
- }
- });
- }
-
- $scope.unlink = function (e, loginProvider, providerKey) {
- var result = confirm("Are you sure you want to unlink this account?");
- if (!result) {
- e.preventDefault();
- return;
- }
-
- authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) {
- updateUserInfo();
- });
- }
-
- updateUserInfo();
-
- //remove all event handlers
- $scope.$on('$destroy', function () {
- for (var e = 0; e < evts.length; e++) {
- evts[e]();
- }
-
- });
-
- /* ---------- UPDATE PASSWORD ---------- */
-
- //create the initial model for change password property editor
- $scope.changePasswordModel = {
- alias: "_umb_password",
- view: "changepassword",
- config: {},
- value: {}
- };
-
- //go get the config for the membership provider and add it to the model
- currentUserResource.getMembershipProviderConfig().then(function(data) {
- $scope.changePasswordModel.config = data;
- //ensure the hasPassword config option is set to true (the user of course has a password already assigned)
- //this will ensure the oldPassword is shown so they can change it
- // disable reset password functionality beacuse it does not make sense inside the backoffice
- $scope.changePasswordModel.config.hasPassword = true;
- $scope.changePasswordModel.config.disableToggle = true;
- $scope.changePasswordModel.config.enableReset = false;
- });
-
- $scope.changePassword = function() {
-
- if (formHelper.submitForm({ scope: $scope })) {
-
- $scope.changePasswordButtonState = "busy";
-
- currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) {
-
- //if the password has been reset, then update our model
- if (data.value) {
- $scope.changePasswordModel.value.generatedPassword = data.value;
- }
-
- formHelper.resetForm({ scope: $scope, notifications: data.notifications });
-
- $scope.changePasswordButtonState = "success";
- $timeout(function() {
- $scope.togglePasswordFields();
- }, 2000);
-
- }, function (err) {
-
- formHelper.handleError(err);
-
- $scope.changePasswordButtonState = "error";
-
- });
-
- }
-
- };
-
- $scope.togglePasswordFields = function() {
- clearPasswordFields();
- $scope.showPasswordFields = !$scope.showPasswordFields;
- }
-
- function clearPasswordFields() {
- $scope.changePasswordModel.value.newPassword = "";
- $scope.changePasswordModel.confirm = "";
- }
-
- });
+angular.module("umbraco")
+ .controller("Umbraco.Overlays.UserController", function ($scope, $location, $timeout, userService, historyService, eventsService, externalLoginInfo, authResource, currentUserResource, formHelper, localizationService) {
+
+ $scope.history = historyService.getCurrent();
+ $scope.version = Umbraco.Sys.ServerVariables.application.version + " assembly: " + Umbraco.Sys.ServerVariables.application.assemblyVersion;
+ $scope.showPasswordFields = false;
+ $scope.changePasswordButtonState = "init";
+ $scope.model.subtitle = "Umbraco version" + " " + $scope.version;
+
+ if(!$scope.model.title) {
+ $scope.model.title = localizationService.localize("general_user");
+ }
+
+ $scope.externalLoginProviders = externalLoginInfo.providers;
+ $scope.externalLinkLoginFormAction = Umbraco.Sys.ServerVariables.umbracoUrls.externalLinkLoginsUrl;
+ var evts = [];
+ evts.push(eventsService.on("historyService.add", function (e, args) {
+ $scope.history = args.all;
+ }));
+ evts.push(eventsService.on("historyService.remove", function (e, args) {
+ $scope.history = args.all;
+ }));
+ evts.push(eventsService.on("historyService.removeAll", function (e, args) {
+ $scope.history = [];
+ }));
+
+ $scope.logout = function () {
+
+ //Add event listener for when there are pending changes on an editor which means our route was not successful
+ var pendingChangeEvent = eventsService.on("valFormManager.pendingChanges", function (e, args) {
+ //one time listener, remove the event
+ pendingChangeEvent();
+ $scope.model.close();
+ });
+
+
+ //perform the path change, if it is successful then the promise will resolve otherwise it will fail
+ $scope.model.close();
+ $location.path("/logout");
+ };
+
+ $scope.gotoHistory = function (link) {
+ $location.path(link);
+ $scope.model.close();
+ };
+
+ //Manually update the remaining timeout seconds
+ function updateTimeout() {
+ $timeout(function () {
+ if ($scope.remainingAuthSeconds > 0) {
+ $scope.remainingAuthSeconds--;
+ $scope.$digest();
+ //recurse
+ updateTimeout();
+ }
+
+ }, 1000, false); // 1 second, do NOT execute a global digest
+ }
+
+ function updateUserInfo() {
+ //get the user
+ userService.getCurrentUser().then(function (user) {
+ $scope.user = user;
+ if ($scope.user) {
+ $scope.model.title = user.name;
+ $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds;
+ $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1;
+ //set the timer
+ updateTimeout();
+
+ authResource.getCurrentUserLinkedLogins().then(function(logins) {
+ //reset all to be un-linked
+ for (var provider in $scope.externalLoginProviders) {
+ $scope.externalLoginProviders[provider].linkedProviderKey = undefined;
+ }
+
+ //set the linked logins
+ for (var login in logins) {
+ var found = _.find($scope.externalLoginProviders, function (i) {
+ return i.authType == login;
+ });
+ if (found) {
+ found.linkedProviderKey = logins[login];
+ }
+ }
+ });
+ }
+ });
+ }
+
+ $scope.unlink = function (e, loginProvider, providerKey) {
+ var result = confirm("Are you sure you want to unlink this account?");
+ if (!result) {
+ e.preventDefault();
+ return;
+ }
+
+ authResource.unlinkLogin(loginProvider, providerKey).then(function (a, b, c) {
+ updateUserInfo();
+ });
+ }
+
+ updateUserInfo();
+
+ //remove all event handlers
+ $scope.$on('$destroy', function () {
+ for (var e = 0; e < evts.length; e++) {
+ evts[e]();
+ }
+
+ });
+
+ /* ---------- UPDATE PASSWORD ---------- */
+
+ //create the initial model for change password property editor
+ $scope.changePasswordModel = {
+ alias: "_umb_password",
+ view: "changepassword",
+ config: {},
+ value: {}
+ };
+
+ //go get the config for the membership provider and add it to the model
+ currentUserResource.getMembershipProviderConfig().then(function(data) {
+ $scope.changePasswordModel.config = data;
+ //ensure the hasPassword config option is set to true (the user of course has a password already assigned)
+ //this will ensure the oldPassword is shown so they can change it
+ // disable reset password functionality beacuse it does not make sense inside the backoffice
+ $scope.changePasswordModel.config.hasPassword = true;
+ $scope.changePasswordModel.config.disableToggle = true;
+ $scope.changePasswordModel.config.enableReset = false;
+ });
+
+ $scope.changePassword = function() {
+
+ if (formHelper.submitForm({ scope: $scope })) {
+
+ $scope.changePasswordButtonState = "busy";
+
+ currentUserResource.changePassword($scope.changePasswordModel.value).then(function(data) {
+
+ //if the password has been reset, then update our model
+ if (data.value) {
+ $scope.changePasswordModel.value.generatedPassword = data.value;
+ }
+
+ formHelper.resetForm({ scope: $scope, notifications: data.notifications });
+
+ $scope.changePasswordButtonState = "success";
+ $timeout(function() {
+ $scope.togglePasswordFields();
+ }, 2000);
+
+ }, function (err) {
+
+ formHelper.handleError(err);
+
+ $scope.changePasswordButtonState = "error";
+
+ });
+
+ }
+
+ };
+
+ $scope.togglePasswordFields = function() {
+ clearPasswordFields();
+ $scope.showPasswordFields = !$scope.showPasswordFields;
+ }
+
+ function clearPasswordFields() {
+ $scope.changePasswordModel.value.newPassword = "";
+ $scope.changePasswordModel.confirm = "";
+ }
+
+ });
angular.module("umbraco")
.controller("Umbraco.Overlays.YsodController", function ($scope, legacyResource, treeService, navigationService, localizationService) {
@@ -4395,145 +4395,145 @@ angular.module("umbraco")
});
-angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
- function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) {
-
- var dialogOptions = $scope.dialogOptions;
- var searchText = "Search...";
- localizationService.localize("general_search").then(function (value) {
- searchText = value + "...";
- });
-
- $scope.relateToOriginal = true;
- $scope.recursive = true;
- $scope.dialogTreeEventHandler = $({});
- $scope.busy = false;
- $scope.searchInfo = {
- searchFromId: null,
- searchFromName: null,
- showSearch: false,
- results: [],
- selectedSearchResults: []
- }
-
- var node = dialogOptions.currentNode;
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if (args.node.metaData.listViewNode) {
- //check if list view 'search' node was selected
-
- $scope.searchInfo.showSearch = true;
- $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
- $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
- }
- else {
- eventsService.emit("editors.content.copyController.select", args);
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
-
- }
-
- function nodeExpandedHandler(ev, args) {
- if (angular.isArray(args.children)) {
-
- //iterate children
- _.each(args.children, function (child) {
- //check if any of the items are list views, if so we need to add a custom
- // child: A node to activate the search
- if (child.metaData.isContainer) {
- child.hasChildren = true;
- child.children = [
- {
- level: child.level + 1,
- hasChildren: false,
- name: searchText,
- metaData: {
- listViewNode: child,
- },
- cssClass: "icon umb-tree-icon sprTree icon-search",
- cssClasses: ["not-published"]
- }
- ];
- }
- });
- }
- }
-
- $scope.hideSearch = function () {
- $scope.searchInfo.showSearch = false;
- $scope.searchInfo.searchFromId = null;
- $scope.searchInfo.searchFromName = null;
- $scope.searchInfo.results = [];
- }
-
- // method to select a search result
- $scope.selectResult = function (evt, result) {
- result.selected = result.selected === true ? false : true;
- nodeSelectHandler(evt, { event: evt, node: result });
- };
-
- //callback when there are search results
- $scope.onSearchResults = function (results) {
- $scope.searchInfo.results = results;
- $scope.searchInfo.showSearch = true;
- };
-
- $scope.copy = function () {
-
- $scope.busy = true;
- $scope.error = false;
-
- contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
- $scope.busy = false;
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the copied content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was copied!!)
-
- navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- $scope.busy = false;
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
- });
- });
+angular.module("umbraco").controller("Umbraco.Editors.Content.CopyController",
+ function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) {
+
+ var dialogOptions = $scope.dialogOptions;
+ var searchText = "Search...";
+ localizationService.localize("general_search").then(function (value) {
+ searchText = value + "...";
+ });
+
+ $scope.relateToOriginal = true;
+ $scope.recursive = true;
+ $scope.dialogTreeEventHandler = $({});
+ $scope.busy = false;
+ $scope.searchInfo = {
+ searchFromId: null,
+ searchFromName: null,
+ showSearch: false,
+ results: [],
+ selectedSearchResults: []
+ }
+
+ var node = dialogOptions.currentNode;
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if (args.node.metaData.listViewNode) {
+ //check if list view 'search' node was selected
+
+ $scope.searchInfo.showSearch = true;
+ $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
+ $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
+ }
+ else {
+ eventsService.emit("editors.content.copyController.select", args);
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ }
+
+ function nodeExpandedHandler(ev, args) {
+ if (angular.isArray(args.children)) {
+
+ //iterate children
+ _.each(args.children, function (child) {
+ //check if any of the items are list views, if so we need to add a custom
+ // child: A node to activate the search
+ if (child.metaData.isContainer) {
+ child.hasChildren = true;
+ child.children = [
+ {
+ level: child.level + 1,
+ hasChildren: false,
+ name: searchText,
+ metaData: {
+ listViewNode: child,
+ },
+ cssClass: "icon umb-tree-icon sprTree icon-search",
+ cssClasses: ["not-published"]
+ }
+ ];
+ }
+ });
+ }
+ }
+
+ $scope.hideSearch = function () {
+ $scope.searchInfo.showSearch = false;
+ $scope.searchInfo.searchFromId = null;
+ $scope.searchInfo.searchFromName = null;
+ $scope.searchInfo.results = [];
+ }
+
+ // method to select a search result
+ $scope.selectResult = function (evt, result) {
+ result.selected = result.selected === true ? false : true;
+ nodeSelectHandler(evt, { event: evt, node: result });
+ };
+
+ //callback when there are search results
+ $scope.onSearchResults = function (results) {
+ $scope.searchInfo.results = results;
+ $scope.searchInfo.showSearch = true;
+ };
+
+ $scope.copy = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ contentResource.copy({ parentId: $scope.target.id, id: node.id, relateToOriginal: $scope.relateToOriginal, recursive: $scope.recursive })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the copied content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was copied!!)
+
+ navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
+ });
+ });
/**
* @ngdoc controller
@@ -4619,237 +4619,238 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio
angular.module("umbraco").controller("Umbraco.Editors.Content.DeleteController", ContentDeleteController);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.Content.EditController
- * @function
- *
- * @description
- * The controller for the content editor
- */
-function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http) {
-
- //setup scope vars
- $scope.defaultButton = null;
- $scope.subButtons = [];
-
- $scope.page = {};
- $scope.page.loading = false;
- $scope.page.menu = {};
- $scope.page.menu.currentNode = null;
- $scope.page.menu.currentSection = appState.getSectionState("currentSection");
- $scope.page.listViewPath = null;
- $scope.page.isNew = $routeParams.create;
- $scope.page.buttonGroupState = "init";
-
- function init(content) {
-
- var buttons = contentEditingHelper.configureContentEditorButtons({
- create: $routeParams.create,
- content: content,
- methods: {
- saveAndPublish: $scope.saveAndPublish,
- sendToPublish: $scope.sendToPublish,
- save: $scope.save,
- unPublish: $scope.unPublish
- }
- });
- $scope.defaultButton = buttons.defaultButton;
- $scope.subButtons = buttons.subButtons;
-
- editorState.set($scope.content);
-
- //We fetch all ancestors of the node to generate the footer breadcrumb navigation
- if (!$routeParams.create) {
- if (content.parentId && content.parentId != -1) {
- entityResource.getAncestors(content.id, "document")
- .then(function (anc) {
- $scope.ancestors = anc;
- });
- }
- }
- }
-
- /** Syncs the content item to it's tree node - this occurs on first load and after saving */
- function syncTreeNode(content, path, initialLoad) {
-
- if (!$scope.content.isChildOfListView) {
- navigationService.syncTree({ tree: "content", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
- $scope.page.menu.currentNode = syncArgs.node;
- });
- }
- else if (initialLoad === true) {
-
- //it's a child item, just sync the ui node to the parent
- navigationService.syncTree({ tree: "content", path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true });
-
- //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
- // from the server so that we can load in the actions menu.
- umbRequestHelper.resourcePromise(
- $http.get(content.treeNodeUrl),
- 'Failed to retrieve data for child node ' + content.id).then(function (node) {
- $scope.page.menu.currentNode = node;
- });
- }
- }
-
- // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
- function performSave(args) {
- var deferred = $q.defer();
-
- $scope.page.buttonGroupState = "busy";
-
- contentEditingHelper.contentEditorPerformSave({
- statusMessage: args.statusMessage,
- saveMethod: args.saveMethod,
- scope: $scope,
- content: $scope.content
- }).then(function (data) {
- //success
- init($scope.content);
- syncTreeNode($scope.content, data.path);
-
- $scope.page.buttonGroupState = "success";
-
- deferred.resolve(data);
- }, function (err) {
- //error
- if (err) {
- editorState.set($scope.content);
- }
-
- $scope.page.buttonGroupState = "error";
-
- deferred.reject(err);
- });
-
- return deferred.promise;
- }
-
- function resetLastListPageNumber(content) {
- // We're using rootScope to store the page number for list views, so if returning to the list
- // we can restore the page. If we've moved on to edit a piece of content that's not the list or it's children
- // we should remove this so as not to confuse if navigating to a different list
- if (!content.isChildOfListView && !content.isContainer) {
- $rootScope.lastListViewPageViewed = null;
- }
- }
-
- if ($routeParams.create) {
-
- $scope.page.loading = true;
-
- //we are creating so get an empty content item
- contentResource.getScaffold($routeParams.id, $routeParams.doctype)
- .then(function (data) {
-
- $scope.content = data;
-
- init($scope.content);
-
- resetLastListPageNumber($scope.content);
-
- $scope.page.loading = false;
-
- });
- }
- else {
-
- $scope.page.loading = true;
-
- //we are editing so get the content item from the server
- contentResource.getById($routeParams.id)
- .then(function (data) {
-
- $scope.content = data;
-
- if (data.isChildOfListView && data.trashed === false) {
- $scope.page.listViewPath = ($routeParams.page)
- ? "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page
- : "/content/content/edit/" + data.parentId;
- }
-
- init($scope.content);
-
- //in one particular special case, after we've created a new item we redirect back to the edit
- // route but there might be server validation errors in the collection which we need to display
- // after the redirect, so we will bind all subscriptions which will show the server validation errors
- // if there are any and then clear them so the collection no longer persists them.
- serverValidationManager.executeAndClearAllSubscriptions();
-
- syncTreeNode($scope.content, data.path, true);
-
- resetLastListPageNumber($scope.content);
-
- $scope.page.loading = false;
-
- });
- }
-
-
- $scope.unPublish = function () {
-
- if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) {
-
- $scope.page.buttonGroupState = "busy";
-
- contentResource.unPublish($scope.content.id)
- .then(function (data) {
-
- formHelper.resetForm({ scope: $scope, notifications: data.notifications });
-
- contentEditingHelper.handleSuccessfulSave({
- scope: $scope,
- savedContent: data,
- rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
- });
-
- init($scope.content);
-
- syncTreeNode($scope.content, data.path);
-
- $scope.page.buttonGroupState = "success";
-
- });
- }
-
- };
-
- $scope.sendToPublish = function () {
- return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending..." });
- };
-
- $scope.saveAndPublish = function () {
- return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing..." });
- };
-
- $scope.save = function () {
- return performSave({ saveMethod: contentResource.save, statusMessage: "Saving..." });
- };
-
- $scope.preview = function (content) {
-
-
- if (!$scope.busy) {
-
- // Chromes popup blocker will kick in if a window is opened
- // outwith the initial scoped request. This trick will fix that.
- //
- var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview');
- $scope.save().then(function (data) {
- // Build the correct path so both /#/ and #/ work.
- var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + data.id;
- previewWindow.location.href = redirect;
- });
-
-
- }
-
- };
-
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.Content.EditController
+ * @function
+ *
+ * @description
+ * The controller for the content editor
+ */
+function ContentEditController($scope, $rootScope, $routeParams, $q, $timeout, $window, appState, contentResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, treeService, fileManager, formHelper, umbRequestHelper, keyboardService, umbModelMapper, editorState, $http) {
+
+ //setup scope vars
+ $scope.defaultButton = null;
+ $scope.subButtons = [];
+
+ $scope.page = {};
+ $scope.page.loading = false;
+ $scope.page.menu = {};
+ $scope.page.menu.currentNode = null;
+ $scope.page.menu.currentSection = appState.getSectionState("currentSection");
+ $scope.page.listViewPath = null;
+ $scope.page.isNew = $routeParams.create;
+ $scope.page.buttonGroupState = "init";
+
+ function init(content) {
+
+ var buttons = contentEditingHelper.configureContentEditorButtons({
+ create: $routeParams.create,
+ content: content,
+ methods: {
+ saveAndPublish: $scope.saveAndPublish,
+ sendToPublish: $scope.sendToPublish,
+ save: $scope.save,
+ unPublish: $scope.unPublish
+ }
+ });
+ $scope.defaultButton = buttons.defaultButton;
+ $scope.subButtons = buttons.subButtons;
+
+ editorState.set($scope.content);
+
+ //We fetch all ancestors of the node to generate the footer breadcrumb navigation
+ if (!$routeParams.create) {
+ if (content.parentId && content.parentId != -1) {
+ entityResource.getAncestors(content.id, "document")
+ .then(function (anc) {
+ $scope.ancestors = anc;
+ });
+ }
+ }
+ }
+
+ /** Syncs the content item to it's tree node - this occurs on first load and after saving */
+ function syncTreeNode(content, path, initialLoad) {
+
+ if (!$scope.content.isChildOfListView) {
+ navigationService.syncTree({ tree: "content", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
+ $scope.page.menu.currentNode = syncArgs.node;
+ });
+ }
+ else if (initialLoad === true) {
+
+ //it's a child item, just sync the ui node to the parent
+ navigationService.syncTree({ tree: "content", path: path.substring(0, path.lastIndexOf(",")).split(","), forceReload: initialLoad !== true });
+
+ //if this is a child of a list view and it's the initial load of the editor, we need to get the tree node
+ // from the server so that we can load in the actions menu.
+ umbRequestHelper.resourcePromise(
+ $http.get(content.treeNodeUrl),
+ 'Failed to retrieve data for child node ' + content.id).then(function (node) {
+ $scope.page.menu.currentNode = node;
+ });
+ }
+ }
+
+ // This is a helper method to reduce the amount of code repitition for actions: Save, Publish, SendToPublish
+ function performSave(args) {
+ var deferred = $q.defer();
+
+ $scope.page.buttonGroupState = "busy";
+
+ contentEditingHelper.contentEditorPerformSave({
+ statusMessage: args.statusMessage,
+ saveMethod: args.saveMethod,
+ scope: $scope,
+ content: $scope.content,
+ action: args.action
+ }).then(function (data) {
+ //success
+ init($scope.content);
+ syncTreeNode($scope.content, data.path);
+
+ $scope.page.buttonGroupState = "success";
+
+ deferred.resolve(data);
+ }, function (err) {
+ //error
+ if (err) {
+ editorState.set($scope.content);
+ }
+
+ $scope.page.buttonGroupState = "error";
+
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ }
+
+ function resetLastListPageNumber(content) {
+ // We're using rootScope to store the page number for list views, so if returning to the list
+ // we can restore the page. If we've moved on to edit a piece of content that's not the list or it's children
+ // we should remove this so as not to confuse if navigating to a different list
+ if (!content.isChildOfListView && !content.isContainer) {
+ $rootScope.lastListViewPageViewed = null;
+ }
+ }
+
+ if ($routeParams.create) {
+
+ $scope.page.loading = true;
+
+ //we are creating so get an empty content item
+ contentResource.getScaffold($routeParams.id, $routeParams.doctype)
+ .then(function (data) {
+
+ $scope.content = data;
+
+ init($scope.content);
+
+ resetLastListPageNumber($scope.content);
+
+ $scope.page.loading = false;
+
+ });
+ }
+ else {
+
+ $scope.page.loading = true;
+
+ //we are editing so get the content item from the server
+ contentResource.getById($routeParams.id)
+ .then(function (data) {
+
+ $scope.content = data;
+
+ if (data.isChildOfListView && data.trashed === false) {
+ $scope.page.listViewPath = ($routeParams.page)
+ ? "/content/content/edit/" + data.parentId + "?page=" + $routeParams.page
+ : "/content/content/edit/" + data.parentId;
+ }
+
+ init($scope.content);
+
+ //in one particular special case, after we've created a new item we redirect back to the edit
+ // route but there might be server validation errors in the collection which we need to display
+ // after the redirect, so we will bind all subscriptions which will show the server validation errors
+ // if there are any and then clear them so the collection no longer persists them.
+ serverValidationManager.executeAndClearAllSubscriptions();
+
+ syncTreeNode($scope.content, data.path, true);
+
+ resetLastListPageNumber($scope.content);
+
+ $scope.page.loading = false;
+
+ });
+ }
+
+
+ $scope.unPublish = function () {
+
+ if (formHelper.submitForm({ scope: $scope, statusMessage: "Unpublishing...", skipValidation: true })) {
+
+ $scope.page.buttonGroupState = "busy";
+
+ contentResource.unPublish($scope.content.id)
+ .then(function (data) {
+
+ formHelper.resetForm({ scope: $scope, notifications: data.notifications });
+
+ contentEditingHelper.handleSuccessfulSave({
+ scope: $scope,
+ savedContent: data,
+ rebindCallback: contentEditingHelper.reBindChangedProperties($scope.content, data)
+ });
+
+ init($scope.content);
+
+ syncTreeNode($scope.content, data.path);
+
+ $scope.page.buttonGroupState = "success";
+
+ });
+ }
+
+ };
+
+ $scope.sendToPublish = function () {
+ return performSave({ saveMethod: contentResource.sendToPublish, statusMessage: "Sending...", action: "sendToPublish" });
+ };
+
+ $scope.saveAndPublish = function () {
+ return performSave({ saveMethod: contentResource.publish, statusMessage: "Publishing...", action: "publish" });
+ };
+
+ $scope.save = function () {
+ return performSave({ saveMethod: contentResource.save, statusMessage: "Saving...", action: "save" });
+ };
+
+ $scope.preview = function (content) {
+
+
+ if (!$scope.busy) {
+
+ // Chromes popup blocker will kick in if a window is opened
+ // outwith the initial scoped request. This trick will fix that.
+ //
+ var previewWindow = $window.open('preview/?id=' + content.id, 'umbpreview');
+ $scope.save().then(function (data) {
+ // Build the correct path so both /#/ and #/ work.
+ var redirect = Umbraco.Sys.ServerVariables.umbracoSettings.umbracoPath + '/preview/?id=' + data.id;
+ previewWindow.location.href = redirect;
+ });
+
+
+ }
+
+ };
+
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.Content.EditController", ContentEditController);
/**
* @ngdoc controller
@@ -4882,270 +4883,270 @@ function ContentEmptyRecycleBinController($scope, contentResource, treeService,
angular.module("umbraco").controller("Umbraco.Editors.Content.EmptyRecycleBinController", ContentEmptyRecycleBinController);
-angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
- function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) {
-
- var dialogOptions = $scope.dialogOptions;
- var searchText = "Search...";
- localizationService.localize("general_search").then(function (value) {
- searchText = value + "...";
- });
-
- $scope.dialogTreeEventHandler = $({});
- $scope.busy = false;
- $scope.searchInfo = {
- searchFromId: null,
- searchFromName: null,
- showSearch: false,
- results: [],
- selectedSearchResults: []
- }
-
- var node = dialogOptions.currentNode;
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if (args.node.metaData.listViewNode) {
- //check if list view 'search' node was selected
-
- $scope.searchInfo.showSearch = true;
- $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
- $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
- }
- else {
- eventsService.emit("editors.content.moveController.select", args);
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
- }
-
- function nodeExpandedHandler(ev, args) {
- if (angular.isArray(args.children)) {
-
- //iterate children
- _.each(args.children, function (child) {
- //check if any of the items are list views, if so we need to add a custom
- // child: A node to activate the search
- if (child.metaData.isContainer) {
- child.hasChildren = true;
- child.children = [
- {
- level: child.level + 1,
- hasChildren: false,
- name: searchText,
- metaData: {
- listViewNode: child,
- },
- cssClass: "icon umb-tree-icon sprTree icon-search",
- cssClasses: ["not-published"]
- }
- ];
- }
- });
- }
- }
-
- $scope.hideSearch = function () {
- $scope.searchInfo.showSearch = false;
- $scope.searchInfo.searchFromId = null;
- $scope.searchInfo.searchFromName = null;
- $scope.searchInfo.results = [];
- }
-
- // method to select a search result
- $scope.selectResult = function (evt, result) {
- result.selected = result.selected === true ? false : true;
- nodeSelectHandler(evt, { event: evt, node: result });
- };
-
- //callback when there are search results
- $scope.onSearchResults = function (results) {
- $scope.searchInfo.results = results;
- $scope.searchInfo.showSearch = true;
- };
-
- $scope.move = function () {
-
- $scope.busy = true;
- $scope.error = false;
-
- contentResource.move({ parentId: $scope.target.id, id: node.id })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
- $scope.busy = false;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- $scope.busy = false;
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
- });
- });
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.Content.RecycleBinController
- * @function
- *
- * @description
- * Controls the recycle bin for content
- *
- */
-
-function ContentRecycleBinController($scope, $routeParams, contentResource, navigationService, localizationService) {
-
- //ensures the list view doesn't actually load until we query for the list view config
- // for the section
- $scope.page = {};
- $scope.page.name = "Recycle Bin";
- $scope.page.nameLocked = true;
-
- //ensures the list view doesn't actually load until we query for the list view config
- // for the section
- $scope.listViewPath = null;
-
- $routeParams.id = "-20";
- contentResource.getRecycleBin().then(function (result) {
- //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
- // single property, so we'll extract that property (list view) and use it's data.
- var listproperty = result.tabs[0].properties[0];
-
- _.each(listproperty.config, function (val, key) {
- $scope.model.config[key] = val;
- });
- $scope.listViewPath = 'views/propertyeditors/listview/listview.html';
- });
-
- $scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
-
- // sync tree node
- navigationService.syncTree({ tree: "content", path: ["-1", $routeParams.id], forceReload: false });
-
- localizePageName();
-
- function localizePageName() {
-
- var pageName = "general_recycleBin";
-
- localizationService.localize(pageName).then(function (value) {
- $scope.page.name = value;
- });
-
- }
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.Content.RecycleBinController", ContentRecycleBinController);
+angular.module("umbraco").controller("Umbraco.Editors.Content.MoveController",
+ function ($scope, eventsService, contentResource, navigationService, appState, treeService, localizationService, notificationsService) {
-angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController",
- function ($scope, relationResource, contentResource, navigationService, appState, treeService) {
- var dialogOptions = $scope.dialogOptions;
-
- var node = dialogOptions.currentNode;
-
- $scope.error = null;
- $scope.success = false;
-
- relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) {
-
- if (data.length == 0) {
- $scope.success = false;
- $scope.error = {
- errorMsg: "Cannot automatically restore this item",
- data: {
- Message: "There is no 'restore' relation found for this node. Use the Move menu item to move it manually."
- }
- }
- return;
- }
-
- $scope.relation = data[0];
-
- if ($scope.relation.parentId == -1) {
- $scope.target = { id: -1, name: "Root" };
-
- } else {
- contentResource.getById($scope.relation.parentId).then(function (data) {
- $scope.target = data;
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- });
- }
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- });
-
- $scope.restore = function () {
- // this code was copied from `content.move.controller.js`
- contentResource.move({ parentId: $scope.target.id, id: node.id })
- .then(function (path) {
-
- $scope.success = true;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- });
- };
+ var dialogOptions = $scope.dialogOptions;
+ var searchText = "Search...";
+ localizationService.localize("general_search").then(function (value) {
+ searchText = value + "...";
+ });
+
+ $scope.dialogTreeEventHandler = $({});
+ $scope.busy = false;
+ $scope.searchInfo = {
+ searchFromId: null,
+ searchFromName: null,
+ showSearch: false,
+ results: [],
+ selectedSearchResults: []
+ }
+
+ var node = dialogOptions.currentNode;
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if (args.node.metaData.listViewNode) {
+ //check if list view 'search' node was selected
+
+ $scope.searchInfo.showSearch = true;
+ $scope.searchInfo.searchFromId = args.node.metaData.listViewNode.id;
+ $scope.searchInfo.searchFromName = args.node.metaData.listViewNode.name;
+ }
+ else {
+ eventsService.emit("editors.content.moveController.select", args);
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+ }
+
+ function nodeExpandedHandler(ev, args) {
+ if (angular.isArray(args.children)) {
+
+ //iterate children
+ _.each(args.children, function (child) {
+ //check if any of the items are list views, if so we need to add a custom
+ // child: A node to activate the search
+ if (child.metaData.isContainer) {
+ child.hasChildren = true;
+ child.children = [
+ {
+ level: child.level + 1,
+ hasChildren: false,
+ name: searchText,
+ metaData: {
+ listViewNode: child,
+ },
+ cssClass: "icon umb-tree-icon sprTree icon-search",
+ cssClasses: ["not-published"]
+ }
+ ];
+ }
+ });
+ }
+ }
+
+ $scope.hideSearch = function () {
+ $scope.searchInfo.showSearch = false;
+ $scope.searchInfo.searchFromId = null;
+ $scope.searchInfo.searchFromName = null;
+ $scope.searchInfo.results = [];
+ }
+
+ // method to select a search result
+ $scope.selectResult = function (evt, result) {
+ result.selected = result.selected === true ? false : true;
+ nodeSelectHandler(evt, { event: evt, node: result });
+ };
+
+ //callback when there are search results
+ $scope.onSearchResults = function (results) {
+ $scope.searchInfo.results = results;
+ $scope.searchInfo.showSearch = true;
+ };
+
+ $scope.move = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ contentResource.move({ parentId: $scope.target.id, id: node.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
+ });
+ });
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.Content.RecycleBinController
+ * @function
+ *
+ * @description
+ * Controls the recycle bin for content
+ *
+ */
+
+function ContentRecycleBinController($scope, $routeParams, contentResource, navigationService, localizationService) {
+
+ //ensures the list view doesn't actually load until we query for the list view config
+ // for the section
+ $scope.page = {};
+ $scope.page.name = "Recycle Bin";
+ $scope.page.nameLocked = true;
+
+ //ensures the list view doesn't actually load until we query for the list view config
+ // for the section
+ $scope.listViewPath = null;
+
+ $routeParams.id = "-20";
+ contentResource.getRecycleBin().then(function (result) {
+ //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
+ // single property, so we'll extract that property (list view) and use it's data.
+ var listproperty = result.tabs[0].properties[0];
+
+ _.each(listproperty.config, function (val, key) {
+ $scope.model.config[key] = val;
+ });
+ $scope.listViewPath = 'views/propertyeditors/listview/listview.html';
+ });
+
+ $scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
+
+ // sync tree node
+ navigationService.syncTree({ tree: "content", path: ["-1", $routeParams.id], forceReload: false });
+
+ localizePageName();
+
+ function localizePageName() {
+
+ var pageName = "general_recycleBin";
+
+ localizationService.localize(pageName).then(function (value) {
+ $scope.page.name = value;
+ });
+
+ }
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.Content.RecycleBinController", ContentRecycleBinController);
+
+angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController",
+ function ($scope, relationResource, contentResource, navigationService, appState, treeService) {
+ var dialogOptions = $scope.dialogOptions;
+
+ var node = dialogOptions.currentNode;
+
+ $scope.error = null;
+ $scope.success = false;
+
+ relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) {
+
+ if (data.length == 0) {
+ $scope.success = false;
+ $scope.error = {
+ errorMsg: "Cannot automatically restore this item",
+ data: {
+ Message: "There is no 'restore' relation found for this node. Use the Move menu item to move it manually."
+ }
+ }
+ return;
+ }
+
+ $scope.relation = data[0];
+
+ if ($scope.relation.parentId == -1) {
+ $scope.target = { id: -1, name: "Root" };
+
+ } else {
+ contentResource.getById($scope.relation.parentId).then(function (data) {
+ $scope.target = data;
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ });
+ }
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ });
+
+ $scope.restore = function () {
+ // this code was copied from `content.move.controller.js`
+ contentResource.move({ parentId: $scope.target.id, id: node.id })
+ .then(function (path) {
+
+ $scope.success = true;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "content", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "content", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ });
+ };
});
function startUpVideosDashboardController($scope, xmlhelper, $log, $http) {
$scope.videos = [];
@@ -5362,313 +5363,313 @@ function MediaFolderBrowserDashboardController($rootScope, $scope, contentTypeRe
}
angular.module("umbraco").controller("Umbraco.Dashboard.MediaFolderBrowserDashboardController", MediaFolderBrowserDashboardController);
-function examineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) {
-
- $scope.indexerDetails = [];
- $scope.searcherDetails = [];
- $scope.loading = true;
-
- function checkProcessing(indexer, checkActionName) {
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", checkActionName, { indexerName: indexer.name })),
- 'Failed to check index processing')
- .then(function(data) {
-
- if (data !== null && data !== "null") {
-
- //copy all resulting properties
- for (var k in data) {
- indexer[k] = data[k];
- }
- indexer.isProcessing = false;
- }
- else {
- $timeout(function () {
- //don't continue if we've tried 100 times
- if (indexer.processingAttempts < 100) {
- checkProcessing(indexer, checkActionName);
- //add an attempt
- indexer.processingAttempts++;
- }
- else {
- //we've exceeded 100 attempts, stop processing
- indexer.isProcessing = false;
- }
- }, 1000);
- }
- });
- }
-
- $scope.search = function (searcher, e) {
- if (e && e.keyCode !== 13) {
- return;
- }
-
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", {
- searcherName: searcher.name,
- query: encodeURIComponent(searcher.searchText),
- queryType: searcher.searchType
- })),
- 'Failed to search')
- .then(function(searchResults) {
- searcher.isSearching = true;
- searcher.searchResults = searchResults;
- });
- }
-
- $scope.toggle = function(provider, propName) {
- if (provider[propName] !== undefined) {
- provider[propName] = !provider[propName];
- }
- else {
- provider[propName] = true;
- }
- }
-
- $scope.rebuildIndex = function(indexer) {
- if (confirm("This will cause the index to be rebuilt. " +
- "Depending on how much content there is in your site this could take a while. " +
- "It is not recommended to rebuild an index during times of high website traffic " +
- "or when editors are editing content.")) {
-
- indexer.isProcessing = true;
- indexer.processingAttempts = 0;
-
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostRebuildIndex", { indexerName: indexer.name })),
- 'Failed to rebuild index')
- .then(function () {
-
- //rebuilding has started, nothing is returned accept a 200 status code.
- //lets poll to see if it is done.
- $timeout(function () {
- checkProcessing(indexer, "PostCheckRebuildIndex");
- }, 1000);
-
- });
- }
- }
-
- $scope.optimizeIndex = function(indexer) {
- if (confirm("This will cause the index to be optimized which will improve its performance. " +
- "It is not recommended to optimize an index during times of high website traffic " +
- "or when editors are editing content.")) {
- indexer.isProcessing = true;
-
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostOptimizeIndex", { indexerName: indexer.name })),
- 'Failed to optimize index')
- .then(function () {
-
- //optimizing has started, nothing is returned accept a 200 status code.
- //lets poll to see if it is done.
- $timeout(function () {
- checkProcessing(indexer, "PostCheckOptimizeIndex");
- }, 1000);
-
- });
- }
- }
-
- $scope.closeSearch = function(searcher) {
- searcher.isSearching = true;
- }
-
-
- //go get the data
-
- //combine two promises and execute when they are both done
- $q.all([
-
- //get the indexer details
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")),
- 'Failed to retrieve indexer details')
- .then(function(data) {
- $scope.indexerDetails = data;
- }),
-
- //get the searcher details
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")),
- 'Failed to retrieve searcher details')
- .then(function(data) {
- $scope.searcherDetails = data;
- for (var s in $scope.searcherDetails) {
- $scope.searcherDetails[s].searchType = "text";
- }
- })
-
- ]).then(function () {
- //all init loading is complete
- $scope.loading = false;
- });
-
-
-}
-angular.module("umbraco").controller("Umbraco.Dashboard.ExamineMgmtController", examineMgmtController);
-function xmlDataIntegrityReportController($scope, umbRequestHelper, $log, $http, $q, $timeout) {
-
- function check(item) {
- var action = item.check;
- umbRequestHelper.resourcePromise(
- $http.get(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)),
- 'Failed to retrieve data integrity status')
- .then(function(result) {
- item.checking = false;
- item.invalid = result === "false";
- });
- }
-
- $scope.fix = function(item) {
- var action = item.fix;
- if (item.fix) {
- if (confirm("This will cause all xml structures for this type to be rebuilt. " +
- "Depending on how much content there is in your site this could take a while. " +
- "It is not recommended to rebuild xml structures if they are not out of sync, during times of high website traffic " +
- "or when editors are editing content.")) {
- item.fixing = true;
- umbRequestHelper.resourcePromise(
- $http.post(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)),
- 'Failed to retrieve data integrity status')
- .then(function (result) {
- item.fixing = false;
- item.invalid = result === "false";
- });
- }
- }
- }
-
- $scope.items = {
- "contentXml": {
- label: "Content in the cmsContentXml table",
- checking: true,
- fixing: false,
- fix: "FixContentXmlTable",
- check: "CheckContentXmlTable"
- },
- "mediaXml": {
- label: "Media in the cmsContentXml table",
- checking: true,
- fixing: false,
- fix: "FixMediaXmlTable",
- check: "CheckMediaXmlTable"
- },
- "memberXml": {
- label: "Members in the cmsContentXml table",
- checking: true,
- fixing: false,
- fix: "FixMembersXmlTable",
- check: "CheckMembersXmlTable"
- }
- };
-
- for (var i in $scope.items) {
- check($scope.items[i]);
- }
-
-}
-angular.module("umbraco").controller("Umbraco.Dashboard.XmlDataIntegrityReportController", xmlDataIntegrityReportController);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DataType.CreateController
- * @function
- *
- * @description
- * The controller for the data type creation dialog
- */
-function DataTypeCreateController($scope, $location, navigationService, dataTypeResource, formHelper, appState) {
-
- $scope.model = {
- folderName: "",
- creatingFolder: false
- };
-
- var node = $scope.dialogOptions.currentNode;
-
- $scope.showCreateFolder = function() {
- $scope.model.creatingFolder = true;
- }
-
- $scope.createContainer = function () {
- if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: "Creating folder..." })) {
- dataTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
-
- navigationService.hideMenu();
- var currPath = node.path ? node.path : "-1";
- navigationService.syncTree({ tree: "datatypes", path: currPath + "," + folderId, forceReload: true, activate: true });
-
- formHelper.resetForm({ scope: $scope });
-
- var section = appState.getSectionState("currentSection");
-
- }, function(err) {
-
- //TODO: Handle errors
- });
- };
- }
-
- $scope.createDataType = function() {
- $location.search('create', null);
- $location.path("/developer/datatypes/edit/" + node.id).search("create", "true");
- navigationService.hideMenu();
- }
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.DataType.CreateController", DataTypeCreateController);
+function examineMgmtController($scope, umbRequestHelper, $log, $http, $q, $timeout) {
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.ContentDeleteController
- * @function
- *
- * @description
- * The controller for deleting content
- */
-function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService) {
-
- $scope.performDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- dataTypeResource.deleteById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.performContainerDelete = function () {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- dataTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController);
+ $scope.indexerDetails = [];
+ $scope.searcherDetails = [];
+ $scope.loading = true;
+
+ function checkProcessing(indexer, checkActionName) {
+ umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", checkActionName, { indexerName: indexer.name })),
+ 'Failed to check index processing')
+ .then(function(data) {
+
+ if (data !== null && data !== "null") {
+
+ //copy all resulting properties
+ for (var k in data) {
+ indexer[k] = data[k];
+ }
+ indexer.isProcessing = false;
+ }
+ else {
+ $timeout(function () {
+ //don't continue if we've tried 100 times
+ if (indexer.processingAttempts < 100) {
+ checkProcessing(indexer, checkActionName);
+ //add an attempt
+ indexer.processingAttempts++;
+ }
+ else {
+ //we've exceeded 100 attempts, stop processing
+ indexer.isProcessing = false;
+ }
+ }, 1000);
+ }
+ });
+ }
+
+ $scope.search = function (searcher, e) {
+ if (e && e.keyCode !== 13) {
+ return;
+ }
+
+ umbRequestHelper.resourcePromise(
+ $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearchResults", {
+ searcherName: searcher.name,
+ query: encodeURIComponent(searcher.searchText),
+ queryType: searcher.searchType
+ })),
+ 'Failed to search')
+ .then(function(searchResults) {
+ searcher.isSearching = true;
+ searcher.searchResults = searchResults;
+ });
+ }
+
+ $scope.toggle = function(provider, propName) {
+ if (provider[propName] !== undefined) {
+ provider[propName] = !provider[propName];
+ }
+ else {
+ provider[propName] = true;
+ }
+ }
+
+ $scope.rebuildIndex = function(indexer) {
+ if (confirm("This will cause the index to be rebuilt. " +
+ "Depending on how much content there is in your site this could take a while. " +
+ "It is not recommended to rebuild an index during times of high website traffic " +
+ "or when editors are editing content.")) {
+
+ indexer.isProcessing = true;
+ indexer.processingAttempts = 0;
+
+ umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostRebuildIndex", { indexerName: indexer.name })),
+ 'Failed to rebuild index')
+ .then(function () {
+
+ //rebuilding has started, nothing is returned accept a 200 status code.
+ //lets poll to see if it is done.
+ $timeout(function () {
+ checkProcessing(indexer, "PostCheckRebuildIndex");
+ }, 1000);
+
+ });
+ }
+ }
+
+ $scope.optimizeIndex = function(indexer) {
+ if (confirm("This will cause the index to be optimized which will improve its performance. " +
+ "It is not recommended to optimize an index during times of high website traffic " +
+ "or when editors are editing content.")) {
+ indexer.isProcessing = true;
+
+ umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "PostOptimizeIndex", { indexerName: indexer.name })),
+ 'Failed to optimize index')
+ .then(function () {
+
+ //optimizing has started, nothing is returned accept a 200 status code.
+ //lets poll to see if it is done.
+ $timeout(function () {
+ checkProcessing(indexer, "PostCheckOptimizeIndex");
+ }, 1000);
+
+ });
+ }
+ }
+
+ $scope.closeSearch = function(searcher) {
+ searcher.isSearching = true;
+ }
+
+
+ //go get the data
+
+ //combine two promises and execute when they are both done
+ $q.all([
+
+ //get the indexer details
+ umbRequestHelper.resourcePromise(
+ $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetIndexerDetails")),
+ 'Failed to retrieve indexer details')
+ .then(function(data) {
+ $scope.indexerDetails = data;
+ }),
+
+ //get the searcher details
+ umbRequestHelper.resourcePromise(
+ $http.get(umbRequestHelper.getApiUrl("examineMgmtBaseUrl", "GetSearcherDetails")),
+ 'Failed to retrieve searcher details')
+ .then(function(data) {
+ $scope.searcherDetails = data;
+ for (var s in $scope.searcherDetails) {
+ $scope.searcherDetails[s].searchType = "text";
+ }
+ })
+
+ ]).then(function () {
+ //all init loading is complete
+ $scope.loading = false;
+ });
+
+
+}
+angular.module("umbraco").controller("Umbraco.Dashboard.ExamineMgmtController", examineMgmtController);
+function xmlDataIntegrityReportController($scope, umbRequestHelper, $log, $http, $q, $timeout) {
+
+ function check(item) {
+ var action = item.check;
+ umbRequestHelper.resourcePromise(
+ $http.get(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)),
+ 'Failed to retrieve data integrity status')
+ .then(function(result) {
+ item.checking = false;
+ item.invalid = result === "false";
+ });
+ }
+
+ $scope.fix = function(item) {
+ var action = item.fix;
+ if (item.fix) {
+ if (confirm("This will cause all xml structures for this type to be rebuilt. " +
+ "Depending on how much content there is in your site this could take a while. " +
+ "It is not recommended to rebuild xml structures if they are not out of sync, during times of high website traffic " +
+ "or when editors are editing content.")) {
+ item.fixing = true;
+ umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("xmlDataIntegrityBaseUrl", action)),
+ 'Failed to retrieve data integrity status')
+ .then(function (result) {
+ item.fixing = false;
+ item.invalid = result === "false";
+ });
+ }
+ }
+ }
+
+ $scope.items = {
+ "contentXml": {
+ label: "Content in the cmsContentXml table",
+ checking: true,
+ fixing: false,
+ fix: "FixContentXmlTable",
+ check: "CheckContentXmlTable"
+ },
+ "mediaXml": {
+ label: "Media in the cmsContentXml table",
+ checking: true,
+ fixing: false,
+ fix: "FixMediaXmlTable",
+ check: "CheckMediaXmlTable"
+ },
+ "memberXml": {
+ label: "Members in the cmsContentXml table",
+ checking: true,
+ fixing: false,
+ fix: "FixMembersXmlTable",
+ check: "CheckMembersXmlTable"
+ }
+ };
+
+ for (var i in $scope.items) {
+ check($scope.items[i]);
+ }
+
+}
+angular.module("umbraco").controller("Umbraco.Dashboard.XmlDataIntegrityReportController", xmlDataIntegrityReportController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DataType.CreateController
+ * @function
+ *
+ * @description
+ * The controller for the data type creation dialog
+ */
+function DataTypeCreateController($scope, $location, navigationService, dataTypeResource, formHelper, appState) {
+
+ $scope.model = {
+ folderName: "",
+ creatingFolder: false
+ };
+
+ var node = $scope.dialogOptions.currentNode;
+
+ $scope.showCreateFolder = function() {
+ $scope.model.creatingFolder = true;
+ }
+
+ $scope.createContainer = function () {
+ if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: "Creating folder..." })) {
+ dataTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
+
+ navigationService.hideMenu();
+ var currPath = node.path ? node.path : "-1";
+ navigationService.syncTree({ tree: "datatypes", path: currPath + "," + folderId, forceReload: true, activate: true });
+
+ formHelper.resetForm({ scope: $scope });
+
+ var section = appState.getSectionState("currentSection");
+
+ }, function(err) {
+
+ //TODO: Handle errors
+ });
+ };
+ }
+
+ $scope.createDataType = function() {
+ $location.search('create', null);
+ $location.path("/developer/datatypes/edit/" + node.id).search("create", "true");
+ navigationService.hideMenu();
+ }
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.DataType.CreateController", DataTypeCreateController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.ContentDeleteController
+ * @function
+ *
+ * @description
+ * The controller for deleting content
+ */
+function DataTypeDeleteController($scope, dataTypeResource, treeService, navigationService) {
+
+ $scope.performDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ dataTypeResource.deleteById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.performContainerDelete = function () {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ dataTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.DataType.DeleteController", DataTypeDeleteController);
/**
* @ngdoc controller
@@ -5872,202 +5873,266 @@ function DataTypeEditController($scope, $routeParams, $location, appState, navig
angular.module("umbraco").controller("Umbraco.Editors.DataType.EditController", DataTypeEditController);
-angular.module("umbraco")
-.controller("Umbraco.Editors.DataType.MoveController",
- function ($scope, dataTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
- var dialogOptions = $scope.dialogOptions;
- $scope.dialogTreeEventHandler = $({});
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
-
- $scope.move = function () {
-
- $scope.busy = true;
- $scope.error = false;
-
- dataTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
- $scope.busy = false;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "dataTypes", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "dataTypes", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- eventsService.emit('app.refreshEditor');
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- $scope.busy = false;
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- });
- });
+angular.module("umbraco")
+.controller("Umbraco.Editors.DataType.MoveController",
+ function ($scope, dataTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
+ var dialogOptions = $scope.dialogOptions;
+ $scope.dialogTreeEventHandler = $({});
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.CreateController
- * @function
- *
- * @description
- * The controller for the doc type creation dialog
- */
-function DocumentTypesCreateController($scope, $location, navigationService, contentTypeResource, formHelper, appState, notificationsService, localizationService) {
-
- $scope.model = {
- allowCreateFolder: $scope.dialogOptions.currentNode.parentId === null || $scope.dialogOptions.currentNode.nodeType === "container",
- folderName: "",
- creatingFolder: false,
- };
-
- var node = $scope.dialogOptions.currentNode,
- localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
-
- $scope.showCreateFolder = function() {
- $scope.model.creatingFolder = true;
- };
-
- $scope.createContainer = function() {
-
- if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) {
-
- contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) {
-
- navigationService.hideMenu();
-
- var currPath = node.path ? node.path : "-1";
-
- navigationService.syncTree({
- tree: "documenttypes",
- path: currPath + "," + folderId,
- forceReload: true,
- activate: true
- });
-
- formHelper.resetForm({
- scope: $scope
- });
-
- var section = appState.getSectionState("currentSection");
-
- }, function(err) {
-
- $scope.error = err;
-
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- }
- };
-
- $scope.createDocType = function() {
- $location.search('create', null);
- $location.search('notemplate', null);
- $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
- navigationService.hideMenu();
- };
-
- $scope.createComponent = function() {
- $location.search('create', null);
- $location.search('notemplate', null);
- $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true");
- navigationService.hideMenu();
- };
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.DocumentTypes.CreateController", DocumentTypesCreateController);
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.DeleteController
- * @function
- *
- * @description
- * The controller for deleting content
- */
-function DocumentTypesDeleteController($scope, dataTypeResource, contentTypeResource, treeService, navigationService) {
-
- $scope.performDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- contentTypeResource.deleteById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.performContainerDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- contentTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteController", DocumentTypesDeleteController);
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.move = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ dataTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "dataTypes", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "dataTypes", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ eventsService.emit('app.refreshEditor');
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
+ });
+
+angular.module("umbraco")
+.controller("Umbraco.Editors.DocumentTypes.CopyController",
+ function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
+ var dialogOptions = $scope.dialogOptions;
+ $scope.dialogTreeEventHandler = $({});
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.copy = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ contentTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the copied content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was copied!!)
+
+ navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
+ });
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.CreateController
+ * @function
+ *
+ * @description
+ * The controller for the doc type creation dialog
+ */
+function DocumentTypesCreateController($scope, $location, navigationService, contentTypeResource, formHelper, appState, notificationsService, localizationService) {
+
+ $scope.model = {
+ allowCreateFolder: $scope.dialogOptions.currentNode.parentId === null || $scope.dialogOptions.currentNode.nodeType === "container",
+ folderName: "",
+ creatingFolder: false,
+ };
+
+ var node = $scope.dialogOptions.currentNode,
+ localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
+
+ $scope.showCreateFolder = function() {
+ $scope.model.creatingFolder = true;
+ };
+
+ $scope.createContainer = function() {
+
+ if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) {
+
+ contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) {
+
+ navigationService.hideMenu();
+
+ var currPath = node.path ? node.path : "-1";
+
+ navigationService.syncTree({
+ tree: "documenttypes",
+ path: currPath + "," + folderId,
+ forceReload: true,
+ activate: true
+ });
+
+ formHelper.resetForm({
+ scope: $scope
+ });
+
+ var section = appState.getSectionState("currentSection");
+
+ }, function(err) {
+
+ $scope.error = err;
+
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ }
+ };
+
+ $scope.createDocType = function() {
+ $location.search('create', null);
+ $location.search('notemplate', null);
+ $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
+ navigationService.hideMenu();
+ };
+
+ $scope.createComponent = function() {
+ $location.search('create', null);
+ $location.search('notemplate', null);
+ $location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true");
+ navigationService.hideMenu();
+ };
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.DocumentTypes.CreateController", DocumentTypesCreateController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.DeleteController
+ * @function
+ *
+ * @description
+ * The controller for deleting content
+ */
+function DocumentTypesDeleteController($scope, dataTypeResource, contentTypeResource, treeService, navigationService) {
+
+ $scope.performDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ contentTypeResource.deleteById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.performContainerDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ contentTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteController", DocumentTypesDeleteController);
/**
* @ngdoc controller
@@ -6445,202 +6510,202 @@ angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.DeleteContro
angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController);
})();
-angular.module("umbraco")
-.controller("Umbraco.Editors.DocumentTypes.MoveController",
- function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
- var dialogOptions = $scope.dialogOptions;
- $scope.dialogTreeEventHandler = $({});
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
-
- $scope.move = function () {
-
- $scope.busy = true;
- $scope.error = false;
-
- contentTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
- $scope.busy = false;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- eventsService.emit('app.refreshEditor');
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- $scope.busy = false;
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- });
- });
+angular.module("umbraco")
+.controller("Umbraco.Editors.DocumentTypes.MoveController",
+ function ($scope, contentTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
+ var dialogOptions = $scope.dialogOptions;
+ $scope.dialogTreeEventHandler = $({});
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.PropertyController
- * @function
- *
- * @description
- * The controller for the content type editor property dialog
- */
-(function() {
- 'use strict';
-
- function PermissionsController($scope, contentTypeResource, iconHelper, contentTypeHelper, localizationService) {
-
- /* ----------- SCOPE VARIABLES ----------- */
-
- var vm = this;
- var childNodeSelectorOverlayTitle = "";
-
- vm.contentTypes = [];
- vm.selectedChildren = [];
-
- vm.overlayTitle = "";
-
- vm.addChild = addChild;
- vm.removeChild = removeChild;
-
- /* ---------- INIT ---------- */
-
- init();
-
- function init() {
-
- childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode");
-
- contentTypeResource.getAll().then(function(contentTypes){
-
- vm.contentTypes = contentTypes;
-
- // convert legacy icons
- iconHelper.formatContentTypeIcons(vm.contentTypes);
-
- vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.contentTypes);
-
- if($scope.model.id === 0) {
- contentTypeHelper.insertChildNodePlaceholder(vm.contentTypes, $scope.model.name, $scope.model.icon, $scope.model.id);
- }
-
- });
-
- }
-
- function addChild($event) {
- vm.childNodeSelectorOverlay = {
- view: "itempicker",
- title: childNodeSelectorOverlayTitle,
- availableItems: vm.contentTypes,
- selectedItems: vm.selectedChildren,
- event: $event,
- show: true,
- submit: function(model) {
- vm.selectedChildren.push(model.selectedItem);
- $scope.model.allowedContentTypes.push(model.selectedItem.id);
- vm.childNodeSelectorOverlay.show = false;
- vm.childNodeSelectorOverlay = null;
- }
- };
- }
-
- function removeChild(selectedChild, index) {
- // remove from vm
- vm.selectedChildren.splice(index, 1);
-
- // remove from content type model
- var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id);
- $scope.model.allowedContentTypes.splice(selectedChildIndex, 1);
- }
-
- }
-
- angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PermissionsController", PermissionsController);
-})();
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.TemplatesController
- * @function
- *
- * @description
- * The controller for the content type editor templates sub view
- */
-(function() {
- 'use strict';
-
- function TemplatesController($scope, entityResource, contentTypeHelper, $routeParams) {
-
- /* ----------- SCOPE VARIABLES ----------- */
-
- var vm = this;
-
- vm.availableTemplates = [];
- vm.updateTemplatePlaceholder = false;
-
-
- /* ---------- INIT ---------- */
-
- init();
-
- function init() {
-
- entityResource.getAll("Template").then(function(templates){
-
- vm.availableTemplates = templates;
-
- // update placeholder template information on new doc types
- if (!$routeParams.notemplate && $scope.model.id === 0) {
- vm.updateTemplatePlaceholder = true;
- vm.availableTemplates = contentTypeHelper.insertTemplatePlaceholder(vm.availableTemplates);
- }
-
- });
-
- }
-
- }
-
- angular.module("umbraco").controller("Umbraco.Editors.DocumentType.TemplatesController", TemplatesController);
-})();
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.move = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ contentTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "documentTypes", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "documentTypes", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ eventsService.emit('app.refreshEditor');
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
+ });
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.PropertyController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor property dialog
+ */
+(function() {
+ 'use strict';
+
+ function PermissionsController($scope, contentTypeResource, iconHelper, contentTypeHelper, localizationService) {
+
+ /* ----------- SCOPE VARIABLES ----------- */
+
+ var vm = this;
+ var childNodeSelectorOverlayTitle = "";
+
+ vm.contentTypes = [];
+ vm.selectedChildren = [];
+
+ vm.overlayTitle = "";
+
+ vm.addChild = addChild;
+ vm.removeChild = removeChild;
+
+ /* ---------- INIT ---------- */
+
+ init();
+
+ function init() {
+
+ childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode");
+
+ contentTypeResource.getAll().then(function(contentTypes){
+
+ vm.contentTypes = contentTypes;
+
+ // convert legacy icons
+ iconHelper.formatContentTypeIcons(vm.contentTypes);
+
+ vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.contentTypes);
+
+ if($scope.model.id === 0) {
+ contentTypeHelper.insertChildNodePlaceholder(vm.contentTypes, $scope.model.name, $scope.model.icon, $scope.model.id);
+ }
+
+ });
+
+ }
+
+ function addChild($event) {
+ vm.childNodeSelectorOverlay = {
+ view: "itempicker",
+ title: childNodeSelectorOverlayTitle,
+ availableItems: vm.contentTypes,
+ selectedItems: vm.selectedChildren,
+ event: $event,
+ show: true,
+ submit: function(model) {
+ vm.selectedChildren.push(model.selectedItem);
+ $scope.model.allowedContentTypes.push(model.selectedItem.id);
+ vm.childNodeSelectorOverlay.show = false;
+ vm.childNodeSelectorOverlay = null;
+ }
+ };
+ }
+
+ function removeChild(selectedChild, index) {
+ // remove from vm
+ vm.selectedChildren.splice(index, 1);
+
+ // remove from content type model
+ var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id);
+ $scope.model.allowedContentTypes.splice(selectedChildIndex, 1);
+ }
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.DocumentType.PermissionsController", PermissionsController);
+})();
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.TemplatesController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor templates sub view
+ */
+(function() {
+ 'use strict';
+
+ function TemplatesController($scope, entityResource, contentTypeHelper, $routeParams) {
+
+ /* ----------- SCOPE VARIABLES ----------- */
+
+ var vm = this;
+
+ vm.availableTemplates = [];
+ vm.updateTemplatePlaceholder = false;
+
+
+ /* ---------- INIT ---------- */
+
+ init();
+
+ function init() {
+
+ entityResource.getAll("Template").then(function(templates){
+
+ vm.availableTemplates = templates;
+
+ // update placeholder template information on new doc types
+ if (!$routeParams.notemplate && $scope.model.id === 0) {
+ vm.updateTemplatePlaceholder = true;
+ vm.availableTemplates = contentTypeHelper.insertTemplatePlaceholder(vm.availableTemplates);
+ }
+
+ });
+
+ }
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.DocumentType.TemplatesController", TemplatesController);
+})();
/**
* @ngdoc controller
@@ -6659,73 +6724,73 @@ function mediaCreateController($scope, $routeParams, mediaTypeResource, iconHelp
}
angular.module('umbraco').controller("Umbraco.Editors.Media.CreateController", mediaCreateController);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.ContentDeleteController
- * @function
- *
- * @description
- * The controller for deleting content
- */
-function MediaDeleteController($scope, mediaResource, treeService, navigationService, editorState, $location, dialogService, notificationsService) {
-
- $scope.performDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
-
- mediaResource.deleteById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- treeService.removeNode($scope.currentNode);
-
- if (rootNode) {
- //ensure the recycle bin has child nodes now
- var recycleBin = treeService.getDescendantNode(rootNode, -21);
- if (recycleBin) {
- recycleBin.hasChildren = true;
- }
- }
-
- //if the current edited item is the same one as we're deleting, we need to navigate elsewhere
- if (editorState.current && editorState.current.id == $scope.currentNode.id) {
-
- //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent
- var location = "/media";
- if ($scope.currentNode.parentId.toString() !== "-1")
- location = "/media/media/edit/" + $scope.currentNode.parentId;
-
- $location.path(location);
- }
-
- navigationService.hideMenu();
-
- }, function (err) {
-
- $scope.currentNode.loading = false;
-
- //check if response is ysod
- if (err.status && err.status >= 500) {
- dialogService.ysodDialog(err);
- }
-
- if (err.data && angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.Media.DeleteController", MediaDeleteController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.ContentDeleteController
+ * @function
+ *
+ * @description
+ * The controller for deleting content
+ */
+function MediaDeleteController($scope, mediaResource, treeService, navigationService, editorState, $location, dialogService, notificationsService) {
+
+ $scope.performDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+
+ mediaResource.deleteById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ treeService.removeNode($scope.currentNode);
+
+ if (rootNode) {
+ //ensure the recycle bin has child nodes now
+ var recycleBin = treeService.getDescendantNode(rootNode, -21);
+ if (recycleBin) {
+ recycleBin.hasChildren = true;
+ }
+ }
+
+ //if the current edited item is the same one as we're deleting, we need to navigate elsewhere
+ if (editorState.current && editorState.current.id == $scope.currentNode.id) {
+
+ //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent
+ var location = "/media";
+ if ($scope.currentNode.parentId.toString() !== "-1")
+ location = "/media/media/edit/" + $scope.currentNode.parentId;
+
+ $location.path(location);
+ }
+
+ navigationService.hideMenu();
+
+ }, function (err) {
+
+ $scope.currentNode.loading = false;
+
+ //check if response is ysod
+ if (err.status && err.status >= 500) {
+ dialogService.ysodDialog(err);
+ }
+
+ if (err.data && angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.Media.DeleteController", MediaDeleteController);
/**
* @ngdoc controller
@@ -6879,254 +6944,318 @@ function mediaEditController($scope, $routeParams, appState, mediaResource, enti
angular.module("umbraco")
.controller("Umbraco.Editors.Media.EditController", mediaEditController);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.Media.EmptyRecycleBinController
- * @function
- *
- * @description
- * The controller for deleting media
- */
-function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService) {
-
- $scope.performDelete = function() {
-
- //(used in the UI)
- $scope.currentNode.loading = true;
-
- mediaResource.emptyRecycleBin($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
- //TODO: Need to sync tree, etc...
- treeService.removeChildNodes($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.Media.EmptyRecycleBinController", MediaEmptyRecycleBinController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.Media.EmptyRecycleBinController
+ * @function
+ *
+ * @description
+ * The controller for deleting media
+ */
+function MediaEmptyRecycleBinController($scope, mediaResource, treeService, navigationService) {
-//used for the media picker dialog
-angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
- function ($scope, eventsService, mediaResource, appState, treeService, navigationService) {
- var dialogOptions = $scope.dialogOptions;
-
- $scope.dialogTreeEventHandler = $({});
- var node = dialogOptions.currentNode;
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- eventsService.emit("editors.media.moveController.select", args);
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
-
-
- $scope.move = function () {
- mediaResource.move({ parentId: $scope.target.id, id: node.id })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "media", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "media", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- });
- };
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- });
+ $scope.performDelete = function() {
+
+ //(used in the UI)
+ $scope.currentNode.loading = true;
+
+ mediaResource.emptyRecycleBin($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+ //TODO: Need to sync tree, etc...
+ treeService.removeChildNodes($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.Media.EmptyRecycleBinController", MediaEmptyRecycleBinController);
+
+//used for the media picker dialog
+angular.module("umbraco").controller("Umbraco.Editors.Media.MoveController",
+ function ($scope, eventsService, mediaResource, appState, treeService, navigationService) {
+ var dialogOptions = $scope.dialogOptions;
+
+ $scope.dialogTreeEventHandler = $({});
+ var node = dialogOptions.currentNode;
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ eventsService.emit("editors.media.moveController.select", args);
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+
+ $scope.move = function () {
+ mediaResource.move({ parentId: $scope.target.id, id: node.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "media", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "media", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ });
+ };
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
});
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.Content.MediaRecycleBinController
- * @function
- *
- * @description
- * Controls the recycle bin for media
- *
- */
-
-function MediaRecycleBinController($scope, $routeParams, mediaResource, navigationService, localizationService) {
-
- //ensures the list view doesn't actually load until we query for the list view config
- // for the section
- $scope.page = {};
- $scope.page.name = "Recycle Bin";
- $scope.page.nameLocked = true;
-
- //ensures the list view doesn't actually load until we query for the list view config
- // for the section
- $scope.listViewPath = null;
-
- $routeParams.id = "-21";
- mediaResource.getRecycleBin().then(function (result) {
- //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
- // single property, so we'll extract that property (list view) and use it's data.
- var listproperty = result.tabs[0].properties[0];
-
- _.each(listproperty.config, function (val, key) {
- $scope.model.config[key] = val;
- });
- $scope.listViewPath = 'views/propertyeditors/listview/listview.html';
- });
-
- $scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
-
- // sync tree node
- navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false });
-
- localizePageName();
-
- function localizePageName() {
-
- var pageName = "general_recycleBin";
-
- localizationService.localize(pageName).then(function (value) {
- $scope.page.name = value;
- });
-
- }
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.Media.RecycleBinController", MediaRecycleBinController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.Content.MediaRecycleBinController
+ * @function
+ *
+ * @description
+ * Controls the recycle bin for media
+ *
+ */
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.MediaType.CreateController
- * @function
- *
- * @description
- * The controller for the media type creation dialog
- */
-function MediaTypesCreateController($scope, $location, navigationService, mediaTypeResource, formHelper, appState, localizationService) {
-
- $scope.model = {
- folderName: "",
- creatingFolder: false
- };
-
- var node = $scope.dialogOptions.currentNode,
- localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
-
- $scope.showCreateFolder = function() {
- $scope.model.creatingFolder = true;
- }
-
- $scope.createContainer = function () {
- if (formHelper.submitForm({
- scope: $scope,
- formCtrl: this.createFolderForm,
- statusMessage: localizeCreateFolder
- })) {
- mediaTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
-
- navigationService.hideMenu();
- var currPath = node.path ? node.path : "-1";
- navigationService.syncTree({ tree: "mediatypes", path: currPath + "," + folderId, forceReload: true, activate: true });
-
- formHelper.resetForm({ scope: $scope });
-
- var section = appState.getSectionState("currentSection");
-
- }, function(err) {
-
- //TODO: Handle errors
- });
- };
- }
-
- $scope.createMediaType = function() {
- $location.search('create', null);
- $location.path("/settings/mediatypes/edit/" + node.id).search("create", "true");
- navigationService.hideMenu();
- }
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.MediaTypes.CreateController", MediaTypesCreateController);
+function MediaRecycleBinController($scope, $routeParams, mediaResource, navigationService, localizationService) {
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.MediaType.DeleteController
- * @function
- *
- * @description
- * The controller for the media type delete dialog
- */
-function MediaTypesDeleteController($scope, dataTypeResource, mediaTypeResource, treeService, navigationService) {
-
- $scope.performDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- mediaTypeResource.deleteById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.performContainerDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- mediaTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteController", MediaTypesDeleteController);
+ //ensures the list view doesn't actually load until we query for the list view config
+ // for the section
+ $scope.page = {};
+ $scope.page.name = "Recycle Bin";
+ $scope.page.nameLocked = true;
+
+ //ensures the list view doesn't actually load until we query for the list view config
+ // for the section
+ $scope.listViewPath = null;
+
+ $routeParams.id = "-21";
+ mediaResource.getRecycleBin().then(function (result) {
+ //we'll get the 'content item' for the recycle bin, we know that it will contain a single tab and a
+ // single property, so we'll extract that property (list view) and use it's data.
+ var listproperty = result.tabs[0].properties[0];
+
+ _.each(listproperty.config, function (val, key) {
+ $scope.model.config[key] = val;
+ });
+ $scope.listViewPath = 'views/propertyeditors/listview/listview.html';
+ });
+
+ $scope.model = { config: { entityType: $routeParams.section, layouts: [] } };
+
+ // sync tree node
+ navigationService.syncTree({ tree: "media", path: ["-1", $routeParams.id], forceReload: false });
+
+ localizePageName();
+
+ function localizePageName() {
+
+ var pageName = "general_recycleBin";
+
+ localizationService.localize(pageName).then(function (value) {
+ $scope.page.name = value;
+ });
+
+ }
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.Media.RecycleBinController", MediaRecycleBinController);
+
+angular.module("umbraco")
+.controller("Umbraco.Editors.MediaTypes.CopyController",
+ function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
+ var dialogOptions = $scope.dialogOptions;
+ $scope.dialogTreeEventHandler = $({});
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.copy = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ mediaTypeResource.copy({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the copied content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was copied!!)
+
+ navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
+ });
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.MediaType.CreateController
+ * @function
+ *
+ * @description
+ * The controller for the media type creation dialog
+ */
+function MediaTypesCreateController($scope, $location, navigationService, mediaTypeResource, formHelper, appState, localizationService) {
+
+ $scope.model = {
+ folderName: "",
+ creatingFolder: false
+ };
+
+ var node = $scope.dialogOptions.currentNode,
+ localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
+
+ $scope.showCreateFolder = function() {
+ $scope.model.creatingFolder = true;
+ }
+
+ $scope.createContainer = function () {
+ if (formHelper.submitForm({
+ scope: $scope,
+ formCtrl: this.createFolderForm,
+ statusMessage: localizeCreateFolder
+ })) {
+ mediaTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
+
+ navigationService.hideMenu();
+ var currPath = node.path ? node.path : "-1";
+ navigationService.syncTree({ tree: "mediatypes", path: currPath + "," + folderId, forceReload: true, activate: true });
+
+ formHelper.resetForm({ scope: $scope });
+
+ var section = appState.getSectionState("currentSection");
+
+ }, function(err) {
+
+ //TODO: Handle errors
+ });
+ };
+ }
+
+ $scope.createMediaType = function() {
+ $location.search('create', null);
+ $location.path("/settings/mediatypes/edit/" + node.id).search("create", "true");
+ navigationService.hideMenu();
+ }
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.MediaTypes.CreateController", MediaTypesCreateController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.MediaType.DeleteController
+ * @function
+ *
+ * @description
+ * The controller for the media type delete dialog
+ */
+function MediaTypesDeleteController($scope, dataTypeResource, mediaTypeResource, treeService, navigationService) {
+
+ $scope.performDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ mediaTypeResource.deleteById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.performContainerDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ mediaTypeResource.deleteContainerById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteController", MediaTypesDeleteController);
/**
* @ngdoc controller
@@ -7477,147 +7606,147 @@ angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.DeleteControlle
angular.module("umbraco").controller("Umbraco.Editors.MediaTypes.EditController", MediaTypesEditController);
})();
-angular.module("umbraco")
-.controller("Umbraco.Editors.MediaTypes.MoveController",
- function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
-
- var dialogOptions = $scope.dialogOptions;
- $scope.dialogTreeEventHandler = $({});
-
- function nodeSelectHandler(ev, args) {
- args.event.preventDefault();
- args.event.stopPropagation();
-
- if ($scope.target) {
- //un-select if there's a current one selected
- $scope.target.selected = false;
- }
-
- $scope.target = args.node;
- $scope.target.selected = true;
- }
-
- $scope.move = function () {
-
- $scope.busy = true;
- $scope.error = false;
-
- mediaTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
- .then(function (path) {
- $scope.error = false;
- $scope.success = true;
- $scope.busy = false;
-
- //first we need to remove the node that launched the dialog
- treeService.removeNode($scope.currentNode);
-
- //get the currently edited node (if any)
- var activeNode = appState.getTreeState("selectedNode");
-
- //we need to do a double sync here: first sync to the moved content - but don't activate the node,
- //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
-
- navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) {
- if (activeNode) {
- var activeNodePath = treeService.getPath(activeNode).join();
- //sync to this node now - depending on what was copied this might already be synced but might not be
- navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true });
- }
- });
-
- eventsService.emit('app.refreshEditor');
-
- }, function (err) {
- $scope.success = false;
- $scope.error = err;
- $scope.busy = false;
- //show any notifications
- if (angular.isArray(err.data.notifications)) {
- for (var i = 0; i < err.data.notifications.length; i++) {
- notificationsService.showNotification(err.data.notifications[i]);
- }
- }
- });
- };
-
- $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
-
- $scope.$on('$destroy', function () {
- $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
- });
- });
+angular.module("umbraco")
+.controller("Umbraco.Editors.MediaTypes.MoveController",
+ function ($scope, mediaTypeResource, treeService, navigationService, notificationsService, appState, eventsService) {
-(function() {
- 'use strict';
-
- function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService) {
-
- /* ----------- SCOPE VARIABLES ----------- */
-
- var vm = this;
- var childNodeSelectorOverlayTitle = "";
-
- vm.mediaTypes = [];
- vm.selectedChildren = [];
-
- vm.addChild = addChild;
- vm.removeChild = removeChild;
-
- /* ---------- INIT ---------- */
-
- init();
-
- function init() {
-
- childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode");
-
- mediaTypeResource.getAll().then(function(mediaTypes){
-
- vm.mediaTypes = mediaTypes;
-
- // convert legacy icons
- iconHelper.formatContentTypeIcons(vm.mediaTypes);
-
- vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.mediaTypes);
-
- if($scope.model.id === 0) {
- contentTypeHelper.insertChildNodePlaceholder(vm.mediaTypes, $scope.model.name, $scope.model.icon, $scope.model.id);
- }
-
- });
-
- }
-
- function addChild($event) {
- vm.childNodeSelectorOverlay = {
- view: "itempicker",
- title: childNodeSelectorOverlayTitle,
- availableItems: vm.mediaTypes,
- selectedItems: vm.selectedChildren,
- event: $event,
- show: true,
- submit: function(model) {
- vm.selectedChildren.push(model.selectedItem);
- $scope.model.allowedContentTypes.push(model.selectedItem.id);
- vm.childNodeSelectorOverlay.show = false;
- vm.childNodeSelectorOverlay = null;
- }
- };
- }
-
- function removeChild(selectedChild, index) {
- // remove from vm
- vm.selectedChildren.splice(index, 1);
-
- // remove from content type model
- var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id);
- $scope.model.allowedContentTypes.splice(selectedChildIndex, 1);
- }
-
- }
-
- angular.module("umbraco").controller("Umbraco.Editors.MediaType.PermissionsController", PermissionsController);
-})();
+ var dialogOptions = $scope.dialogOptions;
+ $scope.dialogTreeEventHandler = $({});
+
+ function nodeSelectHandler(ev, args) {
+ args.event.preventDefault();
+ args.event.stopPropagation();
+
+ if ($scope.target) {
+ //un-select if there's a current one selected
+ $scope.target.selected = false;
+ }
+
+ $scope.target = args.node;
+ $scope.target.selected = true;
+ }
+
+ $scope.move = function () {
+
+ $scope.busy = true;
+ $scope.error = false;
+
+ mediaTypeResource.move({ parentId: $scope.target.id, id: dialogOptions.currentNode.id })
+ .then(function (path) {
+ $scope.error = false;
+ $scope.success = true;
+ $scope.busy = false;
+
+ //first we need to remove the node that launched the dialog
+ treeService.removeNode($scope.currentNode);
+
+ //get the currently edited node (if any)
+ var activeNode = appState.getTreeState("selectedNode");
+
+ //we need to do a double sync here: first sync to the moved content - but don't activate the node,
+ //then sync to the currenlty edited content (note: this might not be the content that was moved!!)
+
+ navigationService.syncTree({ tree: "mediaTypes", path: path, forceReload: true, activate: false }).then(function (args) {
+ if (activeNode) {
+ var activeNodePath = treeService.getPath(activeNode).join();
+ //sync to this node now - depending on what was copied this might already be synced but might not be
+ navigationService.syncTree({ tree: "mediaTypes", path: activeNodePath, forceReload: false, activate: true });
+ }
+ });
+
+ eventsService.emit('app.refreshEditor');
+
+ }, function (err) {
+ $scope.success = false;
+ $scope.error = err;
+ $scope.busy = false;
+ //show any notifications
+ if (angular.isArray(err.data.notifications)) {
+ for (var i = 0; i < err.data.notifications.length; i++) {
+ notificationsService.showNotification(err.data.notifications[i]);
+ }
+ }
+ });
+ };
+
+ $scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
+
+ $scope.$on('$destroy', function () {
+ $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
+ });
+ });
+
+(function() {
+ 'use strict';
+
+ function PermissionsController($scope, mediaTypeResource, iconHelper, contentTypeHelper, localizationService) {
+
+ /* ----------- SCOPE VARIABLES ----------- */
+
+ var vm = this;
+ var childNodeSelectorOverlayTitle = "";
+
+ vm.mediaTypes = [];
+ vm.selectedChildren = [];
+
+ vm.addChild = addChild;
+ vm.removeChild = removeChild;
+
+ /* ---------- INIT ---------- */
+
+ init();
+
+ function init() {
+
+ childNodeSelectorOverlayTitle = localizationService.localize("contentTypeEditor_chooseChildNode");
+
+ mediaTypeResource.getAll().then(function(mediaTypes){
+
+ vm.mediaTypes = mediaTypes;
+
+ // convert legacy icons
+ iconHelper.formatContentTypeIcons(vm.mediaTypes);
+
+ vm.selectedChildren = contentTypeHelper.makeObjectArrayFromId($scope.model.allowedContentTypes, vm.mediaTypes);
+
+ if($scope.model.id === 0) {
+ contentTypeHelper.insertChildNodePlaceholder(vm.mediaTypes, $scope.model.name, $scope.model.icon, $scope.model.id);
+ }
+
+ });
+
+ }
+
+ function addChild($event) {
+ vm.childNodeSelectorOverlay = {
+ view: "itempicker",
+ title: childNodeSelectorOverlayTitle,
+ availableItems: vm.mediaTypes,
+ selectedItems: vm.selectedChildren,
+ event: $event,
+ show: true,
+ submit: function(model) {
+ vm.selectedChildren.push(model.selectedItem);
+ $scope.model.allowedContentTypes.push(model.selectedItem.id);
+ vm.childNodeSelectorOverlay.show = false;
+ vm.childNodeSelectorOverlay = null;
+ }
+ };
+ }
+
+ function removeChild(selectedChild, index) {
+ // remove from vm
+ vm.selectedChildren.splice(index, 1);
+
+ // remove from content type model
+ var selectedChildIndex = $scope.model.allowedContentTypes.indexOf(selectedChild.id);
+ $scope.model.allowedContentTypes.splice(selectedChildIndex, 1);
+ }
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.MediaType.PermissionsController", PermissionsController);
+})();
/**
* @ngdoc controller
@@ -7850,558 +7979,558 @@ function MemberEditController($scope, $routeParams, $location, $q, $window, appS
angular.module("umbraco").controller("Umbraco.Editors.Member.EditController", MemberEditController);
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.Member.ListController
- * @function
- *
- * @description
- * The controller for the member list view
- */
-function MemberListController($scope, $routeParams, $location, $q, $window, appState, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, umbModelMapper, editorState, localizationService) {
-
- //setup scope vars
- $scope.currentSection = appState.getSectionState("currentSection");
- $scope.currentNode = null; //the editors affiliated node
-
- $scope.page = {};
- $scope.page.lockedName = true;
- $scope.page.loading = true;
-
- //we are editing so get the content item from the server
- memberResource.getListNode($routeParams.id)
- .then(function (data) {
-
- $scope.content = data;
-
- //translate "All Members"
- if ($scope.content != null && $scope.content.name != null && $scope.content.name.replace(" ", "").toLowerCase() == "allmembers") {
- localizationService.localize("member_allMembers").then(function (value) {
- $scope.content.name = value;
- });
- }
-
- editorState.set($scope.content);
-
- navigationService.syncTree({ tree: "member", path: data.path.split(",") }).then(function (syncArgs) {
- $scope.currentNode = syncArgs.node;
- });
-
- //in one particular special case, after we've created a new item we redirect back to the edit
- // route but there might be server validation errors in the collection which we need to display
- // after the redirect, so we will bind all subscriptions which will show the server validation errors
- // if there are any and then clear them so the collection no longer persists them.
- serverValidationManager.executeAndClearAllSubscriptions();
-
- $scope.page.loading = false;
-
- });
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.Member.ListController", MemberListController);
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.Member.ListController
+ * @function
+ *
+ * @description
+ * The controller for the member list view
+ */
+function MemberListController($scope, $routeParams, $location, $q, $window, appState, memberResource, entityResource, navigationService, notificationsService, angularHelper, serverValidationManager, contentEditingHelper, fileManager, formHelper, umbModelMapper, editorState, localizationService) {
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.MemberType.CreateController
- * @function
- *
- * @description
- * The controller for the member type creation dialog
- */
-function MemberTypesCreateController($scope, $location, navigationService, memberTypeResource, formHelper, appState, localizationService) {
-
- $scope.model = {
- folderName: "",
- creatingFolder: false
- };
-
- var node = $scope.dialogOptions.currentNode,
- localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
-
-
- $scope.showCreateFolder = function() {
- $scope.model.creatingFolder = true;
- }
-
- $scope.createContainer = function () {
- if (formHelper.submitForm({
- scope: $scope,
- formCtrl: this.createFolderForm,
- statusMessage: localizeCreateFolder
- })) {
- memberTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
-
- navigationService.hideMenu();
- var currPath = node.path ? node.path : "-1";
- navigationService.syncTree({ tree: "membertypes", path: currPath + "," + folderId, forceReload: true, activate: true });
-
- formHelper.resetForm({ scope: $scope });
-
- var section = appState.getSectionState("currentSection");
-
- }, function(err) {
-
- //TODO: Handle errors
- });
- };
- }
-
- $scope.createMemberType = function() {
- $location.search('create', null);
- $location.path("/settings/membertypes/edit/" + node.id).search("create", "true");
- navigationService.hideMenu();
- }
-}
-
-angular.module('umbraco').controller("Umbraco.Editors.MemberTypes.CreateController", MemberTypesCreateController);
+ //setup scope vars
+ $scope.currentSection = appState.getSectionState("currentSection");
+ $scope.currentNode = null; //the editors affiliated node
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.DeleteController
- * @function
- *
- * @description
- * The controller for deleting content
- */
-function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) {
-
- $scope.performDelete = function() {
-
- //mark it for deletion (used in the UI)
- $scope.currentNode.loading = true;
- memberTypeResource.deleteById($scope.currentNode.id).then(function () {
- $scope.currentNode.loading = false;
-
- //get the root node before we remove it
- var rootNode = treeService.getTreeRoot($scope.currentNode);
-
- //TODO: Need to sync tree, etc...
- treeService.removeNode($scope.currentNode);
- navigationService.hideMenu();
- });
-
- };
-
- $scope.cancel = function() {
- navigationService.hideDialog();
- };
-}
-
-angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.DeleteController", MemberTypesDeleteController);
+ $scope.page = {};
+ $scope.page.lockedName = true;
+ $scope.page.loading = true;
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.MemberType.EditController
- * @function
- *
- * @description
- * The controller for the member type editor
- */
-(function () {
- "use strict";
-
- function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService, overlayHelper, contentTypeHelper) {
-
- var vm = this;
- var localizeSaving = localizationService.localize("general_saving");
-
- vm.save = save;
-
- vm.currentNode = null;
- vm.contentType = {};
- vm.page = {};
- vm.page.loading = false;
- vm.page.saveButtonState = "init";
- vm.page.navigation = [
- {
- "name": localizationService.localize("general_design"),
- "icon": "icon-document-dashed-line",
- "view": "views/membertypes/views/design/design.html",
- "active": true
- }
- ];
-
- vm.page.keyboardShortcutsOverview = [
- {
- "name": localizationService.localize("shortcuts_shortcut"),
- "shortcuts": [
- {
- "description": localizationService.localize("shortcuts_addTab"),
- "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
- },
- {
- "description": localizationService.localize("shortcuts_addProperty"),
- "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
- },
- {
- "description": localizationService.localize("shortcuts_addEditor"),
- "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
- },
- {
- "description": localizationService.localize("shortcuts_editDataType"),
- "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
- }
- ]
- }
- ];
-
- contentTypeHelper.checkModelsBuilderStatus().then(function (result) {
- vm.page.modelsBuilder = result;
- if (result) {
- //Models builder mode:
- vm.page.defaultButton = {
- hotKey: "ctrl+s",
- hotKeyWhenHidden: true,
- labelKey: "buttons_save",
- letter: "S",
- type: "submit",
- handler: function () { vm.save(); }
- };
- vm.page.subButtons = [{
- hotKey: "ctrl+g",
- hotKeyWhenHidden: true,
- labelKey: "buttons_saveAndGenerateModels",
- letter: "G",
- handler: function () {
-
- vm.page.saveButtonState = "busy";
-
- vm.save().then(function (result) {
-
- vm.page.saveButtonState = "busy";
-
- localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) {
- localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) {
- notificationsService.info(headerValue, msgValue);
- });
- });
-
- contentTypeHelper.generateModels().then(function (result) {
-
- if (result.success) {
-
- //re-check model status
- contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) {
- vm.page.modelsBuilder = statusResult;
- });
-
- //clear and add success
- vm.page.saveButtonState = "init";
- localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) {
- notificationsService.success(value);
- });
-
- } else {
- vm.page.saveButtonState = "error";
- localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) {
- notificationsService.error(value);
- });
- }
-
- }, function () {
- vm.page.saveButtonState = "error";
- localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) {
- notificationsService.error(value);
- });
- });
-
-
- });
-
- }
- }];
- }
- });
-
- if ($routeParams.create) {
-
- vm.page.loading = true;
-
- //we are creating so get an empty data type item
- memberTypeResource.getScaffold($routeParams.id)
- .then(function (dt) {
- init(dt);
-
- vm.page.loading = false;
- });
- }
- else {
-
- vm.page.loading = true;
-
- memberTypeResource.getById($routeParams.id).then(function (dt) {
- init(dt);
-
- syncTreeNode(vm.contentType, dt.path, true);
-
- vm.page.loading = false;
- });
- }
-
- function save() {
- // only save if there is no overlays open
- if(overlayHelper.getNumberOfOverlays() === 0) {
-
- var deferred = $q.defer();
-
- vm.page.saveButtonState = "busy";
-
- contentEditingHelper.contentEditorPerformSave({
- statusMessage: localizeSaving,
- saveMethod: memberTypeResource.save,
- scope: $scope,
- content: vm.contentType,
- //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc
- // type when server side validation fails - as opposed to content where we are capable of saving the content
- // item if server side validation fails
- redirectOnFailure: false,
- // we need to rebind... the IDs that have been created!
- rebindCallback: function (origContentType, savedContentType) {
- vm.contentType.id = savedContentType.id;
- vm.contentType.groups.forEach(function (group) {
- if (!group.name) return;
-
- var k = 0;
- while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name)
- k++;
- if (k == savedContentType.groups.length) {
- group.id = 0;
- return;
- }
-
- var savedGroup = savedContentType.groups[k];
- if (!group.id) group.id = savedGroup.id;
-
- group.properties.forEach(function (property) {
- if (property.id || !property.alias) return;
-
- k = 0;
- while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias)
- k++;
- if (k == savedGroup.properties.length) {
- property.id = 0;
- return;
- }
-
- var savedProperty = savedGroup.properties[k];
- property.id = savedProperty.id;
- });
- });
- }
- }).then(function (data) {
- //success
- syncTreeNode(vm.contentType, data.path);
-
- vm.page.saveButtonState = "success";
-
- deferred.resolve(data);
- }, function (err) {
- //error
- if (err) {
- editorState.set($scope.content);
- }
- else {
- localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
- localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
- notificationsService.error(headerValue, msgValue);
- });
- });
- }
-
- vm.page.saveButtonState = "error";
-
- deferred.reject(err);
- });
-
- return deferred.promise;
- }
-
- }
-
- function init(contentType) {
-
- // set all tab to inactive
- if (contentType.groups.length !== 0) {
- angular.forEach(contentType.groups, function (group) {
-
- angular.forEach(group.properties, function (property) {
- // get data type details for each property
- getDataTypeDetails(property);
- });
-
- });
- }
-
- // convert legacy icons
- convertLegacyIcons(contentType);
-
- //set a shared state
- editorState.set(contentType);
-
- vm.contentType = contentType;
-
- }
-
- function convertLegacyIcons(contentType) {
-
- // make array to store contentType icon
- var contentTypeArray = [];
-
- // push icon to array
- contentTypeArray.push({ "icon": contentType.icon });
-
- // run through icon method
- iconHelper.formatContentTypeIcons(contentTypeArray);
-
- // set icon back on contentType
- contentType.icon = contentTypeArray[0].icon;
-
- }
-
- function getDataTypeDetails(property) {
-
- if (property.propertyState !== "init") {
-
- dataTypeResource.getById(property.dataTypeId)
- .then(function (dataType) {
- property.dataTypeIcon = dataType.icon;
- property.dataTypeName = dataType.name;
- });
- }
- }
-
- /** Syncs the content type to it's tree node - this occurs on first load and after saving */
- function syncTreeNode(dt, path, initialLoad) {
-
- navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
- vm.currentNode = syncArgs.node;
- });
-
- }
-
-
- }
-
- angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController);
-
-})();
+ //we are editing so get the content item from the server
+ memberResource.getListNode($routeParams.id)
+ .then(function (data) {
-angular.module("umbraco")
-.controller("Umbraco.Editors.MemberTypes.MoveController",
- function($scope){
-
- });
+ $scope.content = data;
-function imageFilePickerController($scope) {
-
- $scope.pick = function() {
- $scope.mediaPickerDialog = {};
- $scope.mediaPickerDialog.view = "mediapicker";
- $scope.mediaPickerDialog.show = true;
-
- $scope.mediaPickerDialog.submit = function(model) {
- $scope.model.value = model.selectedImages[0].image;
- $scope.mediaPickerDialog.show = false;
- $scope.mediaPickerDialog = null;
- };
-
- $scope.mediaPickerDialog.close = function(oldModel) {
- $scope.mediaPickerDialog.show = false;
- $scope.mediaPickerDialog = null;
- };
- };
-
-}
-
-angular.module('umbraco').controller("Umbraco.PrevalueEditors.ImageFilePickerController", imageFilePickerController);
+ //translate "All Members"
+ if ($scope.content != null && $scope.content.name != null && $scope.content.name.replace(" ", "").toLowerCase() == "allmembers") {
+ localizationService.localize("member_allMembers").then(function (value) {
+ $scope.content.name = value;
+ });
+ }
+
+ editorState.set($scope.content);
+
+ navigationService.syncTree({ tree: "member", path: data.path.split(",") }).then(function (syncArgs) {
+ $scope.currentNode = syncArgs.node;
+ });
+
+ //in one particular special case, after we've created a new item we redirect back to the edit
+ // route but there might be server validation errors in the collection which we need to display
+ // after the redirect, so we will bind all subscriptions which will show the server validation errors
+ // if there are any and then clear them so the collection no longer persists them.
+ serverValidationManager.executeAndClearAllSubscriptions();
+
+ $scope.page.loading = false;
+
+ });
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.Member.ListController", MemberListController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.MemberType.CreateController
+ * @function
+ *
+ * @description
+ * The controller for the member type creation dialog
+ */
+function MemberTypesCreateController($scope, $location, navigationService, memberTypeResource, formHelper, appState, localizationService) {
+
+ $scope.model = {
+ folderName: "",
+ creatingFolder: false
+ };
+
+ var node = $scope.dialogOptions.currentNode,
+ localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
+
+
+ $scope.showCreateFolder = function() {
+ $scope.model.creatingFolder = true;
+ }
+
+ $scope.createContainer = function () {
+ if (formHelper.submitForm({
+ scope: $scope,
+ formCtrl: this.createFolderForm,
+ statusMessage: localizeCreateFolder
+ })) {
+ memberTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
+
+ navigationService.hideMenu();
+ var currPath = node.path ? node.path : "-1";
+ navigationService.syncTree({ tree: "membertypes", path: currPath + "," + folderId, forceReload: true, activate: true });
+
+ formHelper.resetForm({ scope: $scope });
+
+ var section = appState.getSectionState("currentSection");
+
+ }, function(err) {
+
+ //TODO: Handle errors
+ });
+ };
+ }
+
+ $scope.createMemberType = function() {
+ $location.search('create', null);
+ $location.path("/settings/membertypes/edit/" + node.id).search("create", "true");
+ navigationService.hideMenu();
+ }
+}
+
+angular.module('umbraco').controller("Umbraco.Editors.MemberTypes.CreateController", MemberTypesCreateController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.DeleteController
+ * @function
+ *
+ * @description
+ * The controller for deleting content
+ */
+function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) {
+
+ $scope.performDelete = function() {
+
+ //mark it for deletion (used in the UI)
+ $scope.currentNode.loading = true;
+ memberTypeResource.deleteById($scope.currentNode.id).then(function () {
+ $scope.currentNode.loading = false;
+
+ //get the root node before we remove it
+ var rootNode = treeService.getTreeRoot($scope.currentNode);
+
+ //TODO: Need to sync tree, etc...
+ treeService.removeNode($scope.currentNode);
+ navigationService.hideMenu();
+ });
+
+ };
+
+ $scope.cancel = function() {
+ navigationService.hideDialog();
+ };
+}
+
+angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.DeleteController", MemberTypesDeleteController);
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.MemberType.EditController
+ * @function
+ *
+ * @description
+ * The controller for the member type editor
+ */
+(function () {
+ "use strict";
+
+ function MemberTypesEditController($scope, $rootScope, $routeParams, $log, $filter, memberTypeResource, dataTypeResource, editorState, iconHelper, formHelper, navigationService, contentEditingHelper, notificationsService, $q, localizationService, overlayHelper, contentTypeHelper) {
+
+ var vm = this;
+ var localizeSaving = localizationService.localize("general_saving");
+
+ vm.save = save;
+
+ vm.currentNode = null;
+ vm.contentType = {};
+ vm.page = {};
+ vm.page.loading = false;
+ vm.page.saveButtonState = "init";
+ vm.page.navigation = [
+ {
+ "name": localizationService.localize("general_design"),
+ "icon": "icon-document-dashed-line",
+ "view": "views/membertypes/views/design/design.html",
+ "active": true
+ }
+ ];
+
+ vm.page.keyboardShortcutsOverview = [
+ {
+ "name": localizationService.localize("shortcuts_shortcut"),
+ "shortcuts": [
+ {
+ "description": localizationService.localize("shortcuts_addTab"),
+ "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
+ },
+ {
+ "description": localizationService.localize("shortcuts_addProperty"),
+ "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
+ },
+ {
+ "description": localizationService.localize("shortcuts_addEditor"),
+ "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
+ },
+ {
+ "description": localizationService.localize("shortcuts_editDataType"),
+ "keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
+ }
+ ]
+ }
+ ];
+
+ contentTypeHelper.checkModelsBuilderStatus().then(function (result) {
+ vm.page.modelsBuilder = result;
+ if (result) {
+ //Models builder mode:
+ vm.page.defaultButton = {
+ hotKey: "ctrl+s",
+ hotKeyWhenHidden: true,
+ labelKey: "buttons_save",
+ letter: "S",
+ type: "submit",
+ handler: function () { vm.save(); }
+ };
+ vm.page.subButtons = [{
+ hotKey: "ctrl+g",
+ hotKeyWhenHidden: true,
+ labelKey: "buttons_saveAndGenerateModels",
+ letter: "G",
+ handler: function () {
+
+ vm.page.saveButtonState = "busy";
+
+ vm.save().then(function (result) {
+
+ vm.page.saveButtonState = "busy";
+
+ localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) {
+ localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) {
+ notificationsService.info(headerValue, msgValue);
+ });
+ });
+
+ contentTypeHelper.generateModels().then(function (result) {
+
+ if (result.success) {
+
+ //re-check model status
+ contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) {
+ vm.page.modelsBuilder = statusResult;
+ });
+
+ //clear and add success
+ vm.page.saveButtonState = "init";
+ localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) {
+ notificationsService.success(value);
+ });
+
+ } else {
+ vm.page.saveButtonState = "error";
+ localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) {
+ notificationsService.error(value);
+ });
+ }
+
+ }, function () {
+ vm.page.saveButtonState = "error";
+ localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) {
+ notificationsService.error(value);
+ });
+ });
+
+
+ });
+
+ }
+ }];
+ }
+ });
+
+ if ($routeParams.create) {
+
+ vm.page.loading = true;
+
+ //we are creating so get an empty data type item
+ memberTypeResource.getScaffold($routeParams.id)
+ .then(function (dt) {
+ init(dt);
+
+ vm.page.loading = false;
+ });
+ }
+ else {
+
+ vm.page.loading = true;
+
+ memberTypeResource.getById($routeParams.id).then(function (dt) {
+ init(dt);
+
+ syncTreeNode(vm.contentType, dt.path, true);
+
+ vm.page.loading = false;
+ });
+ }
+
+ function save() {
+ // only save if there is no overlays open
+ if(overlayHelper.getNumberOfOverlays() === 0) {
+
+ var deferred = $q.defer();
+
+ vm.page.saveButtonState = "busy";
+
+ contentEditingHelper.contentEditorPerformSave({
+ statusMessage: localizeSaving,
+ saveMethod: memberTypeResource.save,
+ scope: $scope,
+ content: vm.contentType,
+ //We do not redirect on failure for doc types - this is because it is not possible to actually save the doc
+ // type when server side validation fails - as opposed to content where we are capable of saving the content
+ // item if server side validation fails
+ redirectOnFailure: false,
+ // we need to rebind... the IDs that have been created!
+ rebindCallback: function (origContentType, savedContentType) {
+ vm.contentType.id = savedContentType.id;
+ vm.contentType.groups.forEach(function (group) {
+ if (!group.name) return;
+
+ var k = 0;
+ while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name)
+ k++;
+ if (k == savedContentType.groups.length) {
+ group.id = 0;
+ return;
+ }
+
+ var savedGroup = savedContentType.groups[k];
+ if (!group.id) group.id = savedGroup.id;
+
+ group.properties.forEach(function (property) {
+ if (property.id || !property.alias) return;
+
+ k = 0;
+ while (k < savedGroup.properties.length && savedGroup.properties[k].alias != property.alias)
+ k++;
+ if (k == savedGroup.properties.length) {
+ property.id = 0;
+ return;
+ }
+
+ var savedProperty = savedGroup.properties[k];
+ property.id = savedProperty.id;
+ });
+ });
+ }
+ }).then(function (data) {
+ //success
+ syncTreeNode(vm.contentType, data.path);
+
+ vm.page.saveButtonState = "success";
+
+ deferred.resolve(data);
+ }, function (err) {
+ //error
+ if (err) {
+ editorState.set($scope.content);
+ }
+ else {
+ localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
+ localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
+ notificationsService.error(headerValue, msgValue);
+ });
+ });
+ }
+
+ vm.page.saveButtonState = "error";
+
+ deferred.reject(err);
+ });
+
+ return deferred.promise;
+ }
+
+ }
+
+ function init(contentType) {
+
+ // set all tab to inactive
+ if (contentType.groups.length !== 0) {
+ angular.forEach(contentType.groups, function (group) {
+
+ angular.forEach(group.properties, function (property) {
+ // get data type details for each property
+ getDataTypeDetails(property);
+ });
+
+ });
+ }
+
+ // convert legacy icons
+ convertLegacyIcons(contentType);
+
+ //set a shared state
+ editorState.set(contentType);
+
+ vm.contentType = contentType;
+
+ }
+
+ function convertLegacyIcons(contentType) {
+
+ // make array to store contentType icon
+ var contentTypeArray = [];
+
+ // push icon to array
+ contentTypeArray.push({ "icon": contentType.icon });
+
+ // run through icon method
+ iconHelper.formatContentTypeIcons(contentTypeArray);
+
+ // set icon back on contentType
+ contentType.icon = contentTypeArray[0].icon;
+
+ }
+
+ function getDataTypeDetails(property) {
+
+ if (property.propertyState !== "init") {
+
+ dataTypeResource.getById(property.dataTypeId)
+ .then(function (dataType) {
+ property.dataTypeIcon = dataType.icon;
+ property.dataTypeName = dataType.name;
+ });
+ }
+ }
+
+ /** Syncs the content type to it's tree node - this occurs on first load and after saving */
+ function syncTreeNode(dt, path, initialLoad) {
+
+ navigationService.syncTree({ tree: "membertypes", path: path.split(","), forceReload: initialLoad !== true }).then(function (syncArgs) {
+ vm.currentNode = syncArgs.node;
+ });
+
+ }
+
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.Editors.MemberTypes.EditController", MemberTypesEditController);
+
+})();
+
+angular.module("umbraco")
+.controller("Umbraco.Editors.MemberTypes.MoveController",
+ function($scope){
+
+ });
+
+function imageFilePickerController($scope) {
+
+ $scope.pick = function() {
+ $scope.mediaPickerDialog = {};
+ $scope.mediaPickerDialog.view = "mediapicker";
+ $scope.mediaPickerDialog.show = true;
+
+ $scope.mediaPickerDialog.submit = function(model) {
+ $scope.model.value = model.selectedImages[0].image;
+ $scope.mediaPickerDialog.show = false;
+ $scope.mediaPickerDialog = null;
+ };
+
+ $scope.mediaPickerDialog.close = function(oldModel) {
+ $scope.mediaPickerDialog.show = false;
+ $scope.mediaPickerDialog = null;
+ };
+ };
+
+}
+
+angular.module('umbraco').controller("Umbraco.PrevalueEditors.ImageFilePickerController", imageFilePickerController);
+
+//this controller simply tells the dialogs service to open a mediaPicker window
+//with a specified callback, this callback will receive an object with a selection on it
+function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) {
+
+ function trim(str, chr) {
+ var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
+ return str.replace(rgxtrim, '');
+ }
+
+ $scope.renderModel = [];
+
+ var dialogOptions = {
+ multiPicker: false,
+ entityType: "Media",
+ section: "media",
+ treeAlias: "media"
+ };
+
+ $scope.openContentPicker = function() {
+ $scope.contentPickerOverlay = dialogOptions;
+ $scope.contentPickerOverlay.view = "treePicker";
+ $scope.contentPickerOverlay.show = true;
+
+ $scope.contentPickerOverlay.submit = function(model) {
+
+ if ($scope.contentPickerOverlay.multiPicker) {
+ _.each(model.selection, function (item, i) {
+ $scope.add(item);
+ });
+ }
+ else {
+ $scope.clear();
+ $scope.add(model.selection[0]);
+ }
+
+ $scope.contentPickerOverlay.show = false;
+ $scope.contentPickerOverlay = null;
+ };
+
+ $scope.contentPickerOverlay.close = function(oldModel) {
+ $scope.contentPickerOverlay.show = false;
+ $scope.contentPickerOverlay = null;
+ };
+ }
+
+ $scope.remove =function(index, event){
+ event.preventDefault();
+ $scope.renderModel.splice(index, 1);
+ };
+
+ $scope.clear = function() {
+ $scope.renderModel = [];
+ };
+
+ $scope.add = function (item) {
+ var currIds = _.map($scope.renderModel, function (i) {
+ return i.id;
+ });
+ if (currIds.indexOf(item.id) < 0) {
+ item.icon = iconHelper.convertFromLegacyIcon(item.icon);
+ $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon});
+ }
+ };
+
+ var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+ var currIds = _.map($scope.renderModel, function (i) {
+ return i.id;
+ });
+ $scope.model.value = trim(currIds.join(), ",");
+ });
+
+ //when the scope is destroyed we need to unsubscribe
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+ //load media data
+ var modelIds = $scope.model.value ? $scope.model.value.split(',') : [];
+ entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) {
+ _.each(data, function (item, i) {
+ item.icon = iconHelper.convertFromLegacyIcon(item.icon);
+ $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
+ });
+ });
+
+}
-//this controller simply tells the dialogs service to open a mediaPicker window
-//with a specified callback, this callback will receive an object with a selection on it
-function mediaPickerController($scope, dialogService, entityResource, $log, iconHelper) {
-
- function trim(str, chr) {
- var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g');
- return str.replace(rgxtrim, '');
- }
-
- $scope.renderModel = [];
-
- var dialogOptions = {
- multiPicker: false,
- entityType: "Media",
- section: "media",
- treeAlias: "media"
- };
-
- $scope.openContentPicker = function() {
- $scope.contentPickerOverlay = dialogOptions;
- $scope.contentPickerOverlay.view = "treePicker";
- $scope.contentPickerOverlay.show = true;
-
- $scope.contentPickerOverlay.submit = function(model) {
-
- if ($scope.contentPickerOverlay.multiPicker) {
- _.each(model.selection, function (item, i) {
- $scope.add(item);
- });
- }
- else {
- $scope.clear();
- $scope.add(model.selection[0]);
- }
-
- $scope.contentPickerOverlay.show = false;
- $scope.contentPickerOverlay = null;
- };
-
- $scope.contentPickerOverlay.close = function(oldModel) {
- $scope.contentPickerOverlay.show = false;
- $scope.contentPickerOverlay = null;
- };
- }
-
- $scope.remove =function(index, event){
- event.preventDefault();
- $scope.renderModel.splice(index, 1);
- };
-
- $scope.clear = function() {
- $scope.renderModel = [];
- };
-
- $scope.add = function (item) {
- var currIds = _.map($scope.renderModel, function (i) {
- return i.id;
- });
- if (currIds.indexOf(item.id) < 0) {
- item.icon = iconHelper.convertFromLegacyIcon(item.icon);
- $scope.renderModel.push({name: item.name, id: item.id, icon: item.icon});
- }
- };
-
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
- var currIds = _.map($scope.renderModel, function (i) {
- return i.id;
- });
- $scope.model.value = trim(currIds.join(), ",");
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
-
- //load media data
- var modelIds = $scope.model.value ? $scope.model.value.split(',') : [];
- entityResource.getByIds(modelIds, dialogOptions.entityType).then(function (data) {
- _.each(data, function (item, i) {
- item.icon = iconHelper.convertFromLegacyIcon(item.icon);
- $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon });
- });
- });
-
-}
-
angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController);
angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesController",
function ($scope, $timeout) {
@@ -8593,77 +8722,77 @@ angular.module('umbraco')
}
});
-//this controller simply tells the dialogs service to open a mediaPicker window
-//with a specified callback, this callback will receive an object with a selection on it
-angular.module('umbraco')
-.controller("Umbraco.PrevalueEditors.TreeSourceController",
-
- function($scope, dialogService, entityResource, $log, iconHelper){
-
- if (!$scope.model) {
- $scope.model = {};
- }
- if (!$scope.model.value) {
- $scope.model.value = {
- type: "content"
- };
- }
-
- if($scope.model.value.id && $scope.model.value.type !== "member"){
- var ent = "Document";
- if($scope.model.value.type === "media"){
- ent = "Media";
- }
-
- entityResource.getById($scope.model.value.id, ent).then(function(item){
- item.icon = iconHelper.convertFromLegacyIcon(item.icon);
- $scope.node = item;
- });
- }
-
-
- $scope.openContentPicker =function(){
- $scope.treePickerOverlay = {
- view: "treepicker",
- section: $scope.model.value.type,
- treeAlias: $scope.model.value.type,
- multiPicker: false,
- show: true,
- submit: function(model) {
- var item = model.selection[0];
- populate(item);
- $scope.treePickerOverlay.show = false;
- $scope.treePickerOverlay = null;
- }
- };
- };
-
- $scope.clear = function() {
- $scope.model.value.id = undefined;
- $scope.node = undefined;
- $scope.model.value.query = undefined;
- };
-
-
- //we always need to ensure we dont submit anything broken
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
- if($scope.model.value.type === "member"){
- $scope.model.value.id = -1;
- $scope.model.value.query = "";
- }
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
-
- function populate(item){
- $scope.clear();
- item.icon = iconHelper.convertFromLegacyIcon(item.icon);
- $scope.node = item;
- $scope.model.value.id = item.id;
- }
+//this controller simply tells the dialogs service to open a mediaPicker window
+//with a specified callback, this callback will receive an object with a selection on it
+angular.module('umbraco')
+.controller("Umbraco.PrevalueEditors.TreeSourceController",
+
+ function($scope, dialogService, entityResource, $log, iconHelper){
+
+ if (!$scope.model) {
+ $scope.model = {};
+ }
+ if (!$scope.model.value) {
+ $scope.model.value = {
+ type: "content"
+ };
+ }
+
+ if($scope.model.value.id && $scope.model.value.type !== "member"){
+ var ent = "Document";
+ if($scope.model.value.type === "media"){
+ ent = "Media";
+ }
+
+ entityResource.getById($scope.model.value.id, ent).then(function(item){
+ item.icon = iconHelper.convertFromLegacyIcon(item.icon);
+ $scope.node = item;
+ });
+ }
+
+
+ $scope.openContentPicker =function(){
+ $scope.treePickerOverlay = {
+ view: "treepicker",
+ section: $scope.model.value.type,
+ treeAlias: $scope.model.value.type,
+ multiPicker: false,
+ show: true,
+ submit: function(model) {
+ var item = model.selection[0];
+ populate(item);
+ $scope.treePickerOverlay.show = false;
+ $scope.treePickerOverlay = null;
+ }
+ };
+ };
+
+ $scope.clear = function() {
+ $scope.model.value.id = undefined;
+ $scope.node = undefined;
+ $scope.model.value.query = undefined;
+ };
+
+
+ //we always need to ensure we dont submit anything broken
+ var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+ if($scope.model.value.type === "member"){
+ $scope.model.value.id = -1;
+ $scope.model.value.query = "";
+ }
+ });
+
+ //when the scope is destroyed we need to unsubscribe
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+ function populate(item){
+ $scope.clear();
+ item.icon = iconHelper.convertFromLegacyIcon(item.icon);
+ $scope.node = item;
+ $scope.model.value.id = item.id;
+ }
});
function booleanEditorController($scope, $rootScope, assetsService) {
@@ -8671,6 +8800,11 @@ function booleanEditorController($scope, $rootScope, assetsService) {
$scope.renderModel = {
value: false
};
+
+ if ($scope.model.config && $scope.model.config.default && $scope.model.config.default.toString() === "1" && $scope.model && !$scope.model.value) {
+ $scope.renderModel.value = true;
+ }
+
if ($scope.model && $scope.model.value && ($scope.model.value.toString() === "1" || angular.lowercase($scope.model.value) === "true")) {
$scope.renderModel.value = true;
}
@@ -8681,7 +8815,7 @@ function booleanEditorController($scope, $rootScope, assetsService) {
$scope.$watch("renderModel.value", function (newVal) {
$scope.model.value = newVal === true ? "1" : "0";
});
-
+
//here we declare a special method which will be called whenever the value has changed from the server
//this is instead of doing a watch on the model.value = faster
$scope.model.onValueChanged = function (newVal, oldVal) {
@@ -8691,6 +8825,7 @@ function booleanEditorController($scope, $rootScope, assetsService) {
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.BooleanController", booleanEditorController);
+
angular.module("umbraco").controller("Umbraco.PropertyEditors.ChangePasswordController",
function ($scope, $routeParams) {
@@ -8927,93 +9062,93 @@ function ColorPickerController($scope) {
angular.module("umbraco").controller("Umbraco.PropertyEditors.ColorPickerController", ColorPickerController);
-angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController",
- function ($scope, $timeout, assetsService, angularHelper, $element) {
- //NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
- var defaultColor = "000000";
-
- $scope.newColor = defaultColor;
- $scope.hasError = false;
-
- assetsService.load([
- //"lib/spectrum/tinycolor.js",
- "lib/spectrum/spectrum.js"
- ], $scope).then(function () {
- var elem = $element.find("input");
- elem.spectrum({
- color: null,
- showInitial: false,
- chooseText: "choose", // TODO: These can be localised
- cancelText: "cancel", // TODO: These can be localised
- preferredFormat: "hex",
- showInput: true,
- clickoutFiresChange: true,
- hide: function (color) {
- //show the add butotn
- $element.find(".btn.add").show();
- },
- change: function (color) {
- angularHelper.safeApply($scope, function () {
- $scope.newColor = color.toHexString().trimStart("#"); // #ff0000
- });
- },
- show: function() {
- //hide the add butotn
- $element.find(".btn.add").hide();
- }
- });
- });
-
- if (!angular.isArray($scope.model.value)) {
- //make an array from the dictionary
- var items = [];
- for (var i in $scope.model.value) {
- items.push({
- value: $scope.model.value[i],
- id: i
- });
- }
- //now make the editor model the array
- $scope.model.value = items;
- }
-
- $scope.remove = function (item, evt) {
-
- evt.preventDefault();
-
- $scope.model.value = _.reject($scope.model.value, function (x) {
- return x.value === item.value;
- });
-
- };
-
- $scope.add = function (evt) {
-
- evt.preventDefault();
-
- if ($scope.newColor) {
- var exists = _.find($scope.model.value, function(item) {
- return item.value.toUpperCase() == $scope.newColor.toUpperCase();
- });
- if (!exists) {
- $scope.model.value.push({ value: $scope.newColor });
- //$scope.newColor = defaultColor;
- // set colorpicker to default color
- //var elem = $element.find("input");
- //elem.spectrum("set", $scope.newColor);
- $scope.hasError = false;
- return;
- }
-
- //there was an error, do the highlight (will be set back by the directive)
- $scope.hasError = true;
- }
-
- };
-
- //load the separate css for the editor to avoid it blocking our js loading
- assetsService.loadCss("lib/spectrum/spectrum.css");
- });
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiColorPickerController",
+ function ($scope, $timeout, assetsService, angularHelper, $element) {
+ //NOTE: We need to make each color an object, not just a string because you cannot 2-way bind to a primitive.
+ var defaultColor = "000000";
+
+ $scope.newColor = defaultColor;
+ $scope.hasError = false;
+
+ assetsService.load([
+ //"lib/spectrum/tinycolor.js",
+ "lib/spectrum/spectrum.js"
+ ], $scope).then(function () {
+ var elem = $element.find("input");
+ elem.spectrum({
+ color: null,
+ showInitial: false,
+ chooseText: "choose", // TODO: These can be localised
+ cancelText: "cancel", // TODO: These can be localised
+ preferredFormat: "hex",
+ showInput: true,
+ clickoutFiresChange: true,
+ hide: function (color) {
+ //show the add butotn
+ $element.find(".btn.add").show();
+ },
+ change: function (color) {
+ angularHelper.safeApply($scope, function () {
+ $scope.newColor = color.toHexString().trimStart("#"); // #ff0000
+ });
+ },
+ show: function() {
+ //hide the add butotn
+ $element.find(".btn.add").hide();
+ }
+ });
+ });
+
+ if (!angular.isArray($scope.model.value)) {
+ //make an array from the dictionary
+ var items = [];
+ for (var i in $scope.model.value) {
+ items.push({
+ value: $scope.model.value[i],
+ id: i
+ });
+ }
+ //now make the editor model the array
+ $scope.model.value = items;
+ }
+
+ $scope.remove = function (item, evt) {
+
+ evt.preventDefault();
+
+ $scope.model.value = _.reject($scope.model.value, function (x) {
+ return x.value === item.value;
+ });
+
+ };
+
+ $scope.add = function (evt) {
+
+ evt.preventDefault();
+
+ if ($scope.newColor) {
+ var exists = _.find($scope.model.value, function(item) {
+ return item.value.toUpperCase() == $scope.newColor.toUpperCase();
+ });
+ if (!exists) {
+ $scope.model.value.push({ value: $scope.newColor });
+ //$scope.newColor = defaultColor;
+ // set colorpicker to default color
+ //var elem = $element.find("input");
+ //elem.spectrum("set", $scope.newColor);
+ $scope.hasError = false;
+ return;
+ }
+
+ //there was an error, do the highlight (will be set back by the directive)
+ $scope.hasError = true;
+ }
+
+ };
+
+ //load the separate css for the editor to avoid it blocking our js loading
+ assetsService.loadCss("lib/spectrum/spectrum.css");
+ });
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
@@ -9712,35 +9847,35 @@ angular.module("umbraco")
}
});
-angular.module("umbraco")
-
-//this controller is obsolete and should not be used anymore
-//it proxies everything to the system media list view which has overtaken
-//all the work this property editor used to perform
-.controller("Umbraco.PropertyEditors.FolderBrowserController",
- function ($rootScope, $scope, contentTypeResource) {
- //get the system media listview
- contentTypeResource.getPropertyTypeScaffold(-96)
- .then(function(dt) {
-
- $scope.fakeProperty = {
- alias: "contents",
- config: dt.config,
- description: "",
- editor: dt.editor,
- hideLabel: true,
- id: 1,
- label: "Contents:",
- validation: {
- mandatory: false,
- pattern: null
- },
- value: "",
- view: dt.view
- };
-
- });
-});
+angular.module("umbraco")
+
+//this controller is obsolete and should not be used anymore
+//it proxies everything to the system media list view which has overtaken
+//all the work this property editor used to perform
+.controller("Umbraco.PropertyEditors.FolderBrowserController",
+ function ($rootScope, $scope, contentTypeResource) {
+ //get the system media listview
+ contentTypeResource.getPropertyTypeScaffold(-96)
+ .then(function(dt) {
+
+ $scope.fakeProperty = {
+ alias: "contents",
+ config: dt.config,
+ description: "",
+ editor: dt.editor,
+ hideLabel: true,
+ id: 1,
+ label: "Contents:",
+ validation: {
+ mandatory: false,
+ pattern: null
+ },
+ value: "",
+ view: dt.view
+ };
+
+ });
+});
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.GoogleMapsController",
@@ -9839,2232 +9974,2237 @@ angular.module("umbraco")
initMap();
};
});
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.GridPrevalueEditor.LayoutConfigController",
- function ($scope) {
-
- $scope.currentLayout = $scope.model.currentLayout;
- $scope.columns = $scope.model.columns;
- $scope.rows = $scope.model.rows;
-
- $scope.scaleUp = function(section, max, overflow){
- var add = 1;
- if(overflow !== true){
- add = (max > 1) ? 1 : max;
- }
- //var add = (max > 1) ? 1 : max;
- section.grid = section.grid+add;
- };
-
- $scope.scaleDown = function(section){
- var remove = (section.grid > 1) ? 1 : section.grid;
- section.grid = section.grid-remove;
- };
-
- $scope.percentage = function(spans){
- return ((spans / $scope.columns) * 100).toFixed(8);
- };
-
- $scope.toggleCollection = function(collection, toggle){
- if(toggle){
- collection = [];
- }else{
- delete collection;
- }
- };
-
-
-
- /****************
- Section
- *****************/
- $scope.configureSection = function(section, template){
- if(section === undefined){
- var space = ($scope.availableLayoutSpace > 4) ? 4 : $scope.availableLayoutSpace;
- section = {
- grid: space
- };
- template.sections.push(section);
- }
-
- $scope.currentSection = section;
- };
-
-
- $scope.deleteSection = function(index){
- $scope.currentTemplate.sections.splice(index, 1);
- };
-
- $scope.closeSection = function(){
- $scope.currentSection = undefined;
- };
-
- $scope.$watch("currentLayout", function(layout){
- if(layout){
- var total = 0;
- _.forEach(layout.sections, function(section){
- total = (total + section.grid);
- });
-
- $scope.availableLayoutSpace = $scope.columns - total;
- }
- }, true);
- });
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.GridPrevalueEditor.LayoutConfigController",
+ function ($scope) {
-function RowConfigController($scope) {
-
- $scope.currentRow = $scope.model.currentRow;
- $scope.editors = $scope.model.editors;
- $scope.columns = $scope.model.columns;
-
- $scope.scaleUp = function(section, max, overflow) {
- var add = 1;
- if (overflow !== true) {
- add = (max > 1) ? 1 : max;
- }
- //var add = (max > 1) ? 1 : max;
- section.grid = section.grid + add;
- };
-
- $scope.scaleDown = function(section) {
- var remove = (section.grid > 1) ? 1 : section.grid;
- section.grid = section.grid - remove;
- };
-
- $scope.percentage = function(spans) {
- return ((spans / $scope.columns) * 100).toFixed(8);
- };
-
- $scope.toggleCollection = function(collection, toggle) {
- if (toggle) {
- collection = [];
- }
- else {
- delete collection;
- }
- };
-
-
- /****************
- area
- *****************/
- $scope.configureCell = function(cell, row) {
- if ($scope.currentCell && $scope.currentCell === cell) {
- delete $scope.currentCell;
- }
- else {
- if (cell === undefined) {
- var available = $scope.availableRowSpace;
- var space = 4;
-
- if (available < 4 && available > 0) {
- space = available;
- }
-
- cell = {
- grid: space
- };
-
- row.areas.push(cell);
- }
- $scope.currentCell = cell;
- }
- };
-
- $scope.deleteArea = function(index) {
- $scope.currentRow.areas.splice(index, 1);
- };
- $scope.closeArea = function() {
- $scope.currentCell = undefined;
- };
-
- $scope.nameChanged = false;
- var originalName = $scope.currentRow.name;
- $scope.$watch("currentRow", function(row) {
- if (row) {
-
- var total = 0;
- _.forEach(row.areas, function(area) {
- total = (total + area.grid);
- });
-
- $scope.availableRowSpace = $scope.columns - total;
-
- if (originalName) {
- if (originalName != row.name) {
- $scope.nameChanged = true;
- }
- else {
- $scope.nameChanged = false;
- }
- }
- }
- }, true);
-
-}
-
-angular.module("umbraco").controller("Umbraco.PropertyEditors.GridPrevalueEditor.RowConfigController", RowConfigController);
+ $scope.currentLayout = $scope.model.currentLayout;
+ $scope.columns = $scope.model.columns;
+ $scope.rows = $scope.model.rows;
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.Grid.EmbedController",
- function ($scope, $rootScope, $timeout) {
-
- $scope.setEmbed = function(){
- $scope.embedDialog = {};
- $scope.embedDialog.view = "embed";
- $scope.embedDialog.show = true;
-
- $scope.embedDialog.submit = function(model) {
- $scope.control.value = model.embed.preview;
- $scope.embedDialog.show = false;
- $scope.embedDialog = null;
- };
-
- $scope.embedDialog.close = function(oldModel) {
- $scope.embedDialog.show = false;
- $scope.embedDialog = null;
- };
-
- };
-
- $timeout(function(){
- if($scope.control.$initializing){
- $scope.setEmbed();
- }
- }, 200);
-});
+ $scope.scaleUp = function(section, max, overflow){
+ var add = 1;
+ if(overflow !== true){
+ add = (max > 1) ? 1 : max;
+ }
+ //var add = (max > 1) ? 1 : max;
+ section.grid = section.grid+add;
+ };
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.Grid.MacroController",
- function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) {
-
- $scope.title = "Click to insert macro";
-
- $scope.setMacro = function(){
-
- var dialogData = {
- richTextEditor: true,
- macroData: $scope.control.value || {
- macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias
- ? $scope.control.editor.config.macroAlias : ""
- }
- };
-
- $scope.macroPickerOverlay = {};
- $scope.macroPickerOverlay.view = "macropicker";
- $scope.macroPickerOverlay.dialogData = dialogData;
- $scope.macroPickerOverlay.show = true;
-
- $scope.macroPickerOverlay.submit = function(model) {
-
- var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
-
- $scope.control.value = {
- macroAlias: macroObject.macroAlias,
- macroParamsDictionary: macroObject.macroParamsDictionary
- };
-
- $scope.setPreview($scope.control.value );
-
- $scope.macroPickerOverlay.show = false;
- $scope.macroPickerOverlay = null;
- };
-
- $scope.macroPickerOverlay.close = function(oldModel) {
- $scope.macroPickerOverlay.show = false;
- $scope.macroPickerOverlay = null;
- };
-
- };
-
- $scope.setPreview = function(macro){
- var contentId = $routeParams.id;
-
- macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary)
- .then(function (htmlResult) {
- $scope.title = macro.macroAlias;
- if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){
- $scope.preview = htmlResult;
- }
- });
-
- };
-
- $timeout(function(){
- if($scope.control.$initializing){
- $scope.setMacro();
- }else if($scope.control.value){
- $scope.setPreview($scope.control.value);
- }
- }, 200);
-});
+ $scope.scaleDown = function(section){
+ var remove = (section.grid > 1) ? 1 : section.grid;
+ section.grid = section.grid-remove;
+ };
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.Grid.MediaController",
- function ($scope, $rootScope, $timeout) {
-
- $scope.setImage = function(){
- $scope.mediaPickerOverlay = {};
- $scope.mediaPickerOverlay.view = "mediapicker";
- $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined;
- $scope.mediaPickerOverlay.showDetails = true;
- $scope.mediaPickerOverlay.disableFolderSelect = true;
- $scope.mediaPickerOverlay.onlyImages = true;
- $scope.mediaPickerOverlay.show = true;
-
- $scope.mediaPickerOverlay.submit = function(model) {
- var selectedImage = model.selectedImages[0];
-
- $scope.control.value = {
- focalPoint: selectedImage.focalPoint,
- id: selectedImage.id,
- image: selectedImage.image,
- altText: selectedImage.altText
- };
-
- $scope.setUrl();
-
- $scope.mediaPickerOverlay.show = false;
- $scope.mediaPickerOverlay = null;
- };
-
- $scope.mediaPickerOverlay.close = function(oldModel) {
- $scope.mediaPickerOverlay.show = false;
- $scope.mediaPickerOverlay = null;
- };
- };
-
- $scope.setUrl = function(){
-
- if($scope.control.value.image){
- var url = $scope.control.value.image;
-
- if($scope.control.editor.config && $scope.control.editor.config.size){
- url += "?width=" + $scope.control.editor.config.size.width;
- url += "&height=" + $scope.control.editor.config.size.height;
- url += "&animationprocessmode=first";
-
- if($scope.control.value.focalPoint){
- url += "¢er=" + $scope.control.value.focalPoint.top +"," + $scope.control.value.focalPoint.left;
- url += "&mode=crop";
- }
- }
-
- // set default size if no crop present (moved from the view)
- if (url.indexOf('?') == -1)
- {
- url += "?width=800&upscale=false&animationprocessmode=false"
- }
- $scope.url = url;
- }
- };
-
- $timeout(function(){
- if($scope.control.$initializing){
- $scope.setImage();
- }else if($scope.control.value){
- $scope.setUrl();
- }
- }, 200);
-});
+ $scope.percentage = function(spans){
+ return ((spans / $scope.columns) * 100).toFixed(8);
+ };
-(function() {
- "use strict";
-
- function GridRichTextEditorController($scope, tinyMceService, macroService) {
-
- var vm = this;
-
- vm.openLinkPicker = openLinkPicker;
- vm.openMediaPicker = openMediaPicker;
- vm.openMacroPicker = openMacroPicker;
- vm.openEmbed = openEmbed;
-
- function openLinkPicker(editor, currentTarget, anchorElement) {
- vm.linkPickerOverlay = {
- view: "linkpicker",
- currentTarget: currentTarget,
- show: true,
- submit: function(model) {
- tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
- vm.linkPickerOverlay.show = false;
- vm.linkPickerOverlay = null;
- }
- };
- }
-
- function openMediaPicker(editor, currentTarget, userData) {
- vm.mediaPickerOverlay = {
- currentTarget: currentTarget,
- onlyImages: true,
- showDetails: true,
- startNodeId: userData.startMediaId,
- view: "mediapicker",
- show: true,
- submit: function(model) {
- tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]);
- vm.mediaPickerOverlay.show = false;
- vm.mediaPickerOverlay = null;
- }
- };
- }
-
- function openEmbed(editor) {
- vm.embedOverlay = {
- view: "embed",
- show: true,
- submit: function(model) {
- tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview);
- vm.embedOverlay.show = false;
- vm.embedOverlay = null;
- }
- };
- }
-
- function openMacroPicker(editor, dialogData) {
- vm.macroPickerOverlay = {
- view: "macropicker",
- dialogData: dialogData,
- show: true,
- submit: function(model) {
- var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
- tinyMceService.insertMacroInEditor(editor, macroObject, $scope);
- vm.macroPickerOverlay.show = false;
- vm.macroPickerOverlay = null;
- }
- };
- }
-
-
-
- }
-
- angular.module("umbraco").controller("Umbraco.PropertyEditors.Grid.RichTextEditorController", GridRichTextEditorController);
-
-})();
+ $scope.toggleCollection = function(collection, toggle){
+ if(toggle){
+ collection = [];
+ }else{
+ delete collection;
+ }
+ };
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.Grid.TextStringController",
- function ($scope, $rootScope, $timeout, dialogService) {
-
-
-
- });
-
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.GridController",
- function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper) {
-
- // Grid status variables
- var placeHolder = "";
- var currentForm = angularHelper.getCurrentForm($scope);
-
- $scope.currentRow = null;
- $scope.currentCell = null;
- $scope.currentToolsControl = null;
- $scope.currentControl = null;
- $scope.openRTEToolbarId = null;
- $scope.hasSettings = false;
- $scope.showRowConfigurations = true;
- $scope.sortMode = false;
- $scope.reorderKey = "general_reorder";
-
- // *********************************************
- // Sortable options
- // *********************************************
-
- var draggedRteSettings;
-
- $scope.sortableOptionsRow = {
- distance: 10,
- cursor: "move",
- placeholder: "ui-sortable-placeholder",
- handle: ".umb-row-title-bar",
- helper: "clone",
- forcePlaceholderSize: true,
- tolerance: "pointer",
- zIndex: 999999999999999999,
- scrollSensitivity: 100,
- cursorAt: {
- top: 40,
- left: 60
- },
-
- sort: function (event, ui) {
- /* prevent vertical scroll out of the screen */
- var max = $(".umb-grid").width() - 150;
- if (parseInt(ui.helper.css("left")) > max) {
- ui.helper.css({ "left": max + "px" });
- }
- if (parseInt(ui.helper.css("left")) < 20) {
- ui.helper.css({ "left": 20 });
- }
- },
-
- start: function (e, ui) {
-
- // Fade out row when sorting
- ui.item.context.style.display = "block";
- ui.item.context.style.opacity = "0.5";
-
- draggedRteSettings = {};
- ui.item.find(".mceNoEditor").each(function () {
- // remove all RTEs in the dragged row and save their settings
- var id = $(this).attr("id");
- draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings;
- // tinyMCE.execCommand("mceRemoveEditor", false, id);
- });
- },
-
- stop: function (e, ui) {
-
- // Fade in row when sorting stops
- ui.item.context.style.opacity = "1";
-
- // reset all RTEs affected by the dragging
- ui.item.parents(".umb-column").find(".mceNoEditor").each(function () {
- var id = $(this).attr("id");
- draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings;
- tinyMCE.execCommand("mceRemoveEditor", false, id);
- tinyMCE.init(draggedRteSettings[id]);
- });
- currentForm.$setDirty();
- }
- };
-
- var notIncludedRte = [];
- var cancelMove = false;
-
- $scope.sortableOptionsCell = {
- distance: 10,
- cursor: "move",
- placeholder: "ui-sortable-placeholder",
- handle: ".umb-control-handle",
- helper: "clone",
- connectWith: ".umb-cell-inner",
- forcePlaceholderSize: true,
- tolerance: "pointer",
- zIndex: 999999999999999999,
- scrollSensitivity: 100,
- cursorAt: {
- top: 45,
- left: 90
- },
-
- sort: function (event, ui) {
-
- /* prevent vertical scroll out of the screen */
- var position = parseInt(ui.item.parent().offset().left) + parseInt(ui.helper.css("left")) - parseInt($(".umb-grid").offset().left);
- var max = $(".umb-grid").width() - 220;
- if (position > max) {
- ui.helper.css({ "left": max - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
- }
- if (position < 0) {
- ui.helper.css({ "left": 0 - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
- }
- },
-
- over: function (event, ui) {
- var allowedEditors = $(event.target).scope().area.allowed;
-
- if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) {
-
- $scope.$apply(function () {
- $(event.target).scope().area.dropNotAllowed = true;
- });
-
- ui.placeholder.hide();
- cancelMove = true;
- }
- else {
- if ($(event.target).scope().area.controls.length == 0){
-
- $scope.$apply(function () {
- $(event.target).scope().area.dropOnEmpty = true;
- });
- ui.placeholder.hide();
- } else {
- ui.placeholder.show();
- }
- cancelMove = false;
- }
- },
-
- out: function(event, ui) {
- $scope.$apply(function () {
- $(event.target).scope().area.dropNotAllowed = false;
- $(event.target).scope().area.dropOnEmpty = false;
- });
- },
-
- update: function (event, ui) {
- /* add all RTEs which are affected by the dragging */
- if (!ui.sender) {
- if (cancelMove) {
- ui.item.sortable.cancel();
- }
- ui.item.parents(".umb-cell.content").find(".mceNoEditor").each(function () {
- if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
- notIncludedRte.splice(0, 0, $(this).attr("id"));
- }
- });
- }
- else {
- $(event.target).find(".mceNoEditor").each(function () {
- if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
- notIncludedRte.splice(0, 0, $(this).attr("id"));
- }
- });
- }
- currentForm.$setDirty();
- },
-
- start: function (e, ui) {
-
- // fade out control when sorting
- ui.item.context.style.display = "block";
- ui.item.context.style.opacity = "0.5";
-
- // reset dragged RTE settings in case a RTE isn't dragged
- draggedRteSettings = undefined;
- ui.item.context.style.display = "block";
- ui.item.find(".mceNoEditor").each(function () {
- notIncludedRte = [];
- var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") });
-
- // save the dragged RTE settings
- if(editors) {
- draggedRteSettings = editors.settings;
-
- // remove the dragged RTE
- tinyMCE.execCommand("mceRemoveEditor", false, $(this).attr("id"));
-
- }
-
- });
- },
-
- stop: function (e, ui) {
-
- // Fade in control when sorting stops
- ui.item.context.style.opacity = "1";
-
- ui.item.parents(".umb-cell-content").find(".mceNoEditor").each(function () {
- if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
- // add all dragged's neighbouring RTEs in the new cell
- notIncludedRte.splice(0, 0, $(this).attr("id"));
- }
- });
- $timeout(function () {
- // reconstruct the dragged RTE (could be undefined when dragging something else than RTE)
- if (draggedRteSettings !== undefined) {
- tinyMCE.init(draggedRteSettings);
- }
-
- _.forEach(notIncludedRte, function (id) {
- // reset all the other RTEs
- if (draggedRteSettings === undefined || id !== draggedRteSettings.id) {
- var rteSettings = _.findWhere(tinyMCE.editors, { id: id }).settings;
- tinyMCE.execCommand("mceRemoveEditor", false, id);
- tinyMCE.init(rteSettings);
- }
- });
- }, 500, false);
-
- $scope.$apply(function () {
-
- var cell = $(e.target).scope().area;
- cell.hasActiveChild = hasActiveChild(cell, cell.controls);
- cell.active = false;
- });
- }
-
- };
-
- $scope.toggleSortMode = function() {
- $scope.sortMode = !$scope.sortMode;
- if($scope.sortMode) {
- $scope.reorderKey = "general_reorderDone";
- } else {
- $scope.reorderKey = "general_reorder";
- }
- };
-
- $scope.showReorderButton = function() {
- if($scope.model.value && $scope.model.value.sections) {
- for(var i = 0; $scope.model.value.sections.length > i; i++) {
- var section = $scope.model.value.sections[i];
- if(section.rows && section.rows.length > 0) {
- return true;
- }
- }
- }
- };
-
- // *********************************************
- // Add items overlay menu
- // *********************************************
- $scope.openEditorOverlay = function(event, area, index, key) {
- $scope.editorOverlay = {
- view: "itempicker",
- filter: false,
- title: localizationService.localize("grid_insertControl"),
- availableItems: area.$allowedEditors,
- event: event,
- show: true,
- submit: function(model) {
- $scope.addControl(model.selectedItem, area, index);
- $scope.editorOverlay.show = false;
- $scope.editorOverlay = null;
- }
- };
- };
-
- // *********************************************
- // Template management functions
- // *********************************************
-
- $scope.addTemplate = function (template) {
- $scope.model.value = angular.copy(template);
-
- //default row data
- _.forEach($scope.model.value.sections, function (section) {
- $scope.initSection(section);
- });
- };
-
-
- // *********************************************
- // Row management function
- // *********************************************
-
- $scope.clickRow = function(index, rows) {
- rows[index].active = true;
- };
-
- $scope.clickOutsideRow = function(index, rows) {
- rows[index].active = false;
- };
-
- function getAllowedLayouts(section) {
-
- var layouts = $scope.model.config.items.layouts;
-
- //This will occur if it is a new section which has been
- // created from a 'template'
- if (section.allowed && section.allowed.length > 0) {
- return _.filter(layouts, function (layout) {
- return _.indexOf(section.allowed, layout.name) >= 0;
- });
- }
- else {
-
-
- return layouts;
- }
- }
-
- $scope.addRow = function (section, layout) {
-
- //copy the selected layout into the rows collection
- var row = angular.copy(layout);
-
- // Init row value
- row = $scope.initRow(row);
-
- // Push the new row
- if (row) {
- section.rows.push(row);
- }
-
- currentForm.$setDirty();
-
- $scope.showRowConfigurations = false;
-
- };
-
- $scope.removeRow = function (section, $index) {
- if (section.rows.length > 0) {
- section.rows.splice($index, 1);
- $scope.currentRow = null;
- $scope.openRTEToolbarId = null;
- currentForm.$setDirty();
- }
-
- if(section.rows.length === 0) {
- $scope.showRowConfigurations = true;
- }
- };
-
- $scope.editGridItemSettings = function (gridItem, itemType) {
-
- placeHolder = "{0}";
-
- var styles, config;
- if (itemType === 'control') {
- styles = null;
- config = angular.copy(gridItem.editor.config.settings);
- } else {
- styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
- config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
- }
-
- if(angular.isObject(gridItem.config)){
- _.each(config, function(cfg){
- var val = gridItem.config[cfg.key];
- if(val){
- cfg.value = stripModifier(val, cfg.modifier);
- }
- });
- }
-
- if(angular.isObject(gridItem.styles)){
- _.each(styles, function(style){
- var val = gridItem.styles[style.key];
- if(val){
- style.value = stripModifier(val, style.modifier);
- }
- });
- }
-
- $scope.gridItemSettingsDialog = {};
- $scope.gridItemSettingsDialog.view = "views/propertyeditors/grid/dialogs/config.html";
- $scope.gridItemSettingsDialog.title = "Settings";
- $scope.gridItemSettingsDialog.styles = styles;
- $scope.gridItemSettingsDialog.config = config;
-
- $scope.gridItemSettingsDialog.show = true;
-
- $scope.gridItemSettingsDialog.submit = function(model) {
-
- var styleObject = {};
- var configObject = {};
-
- _.each(model.styles, function(style){
- if(style.value){
- styleObject[style.key] = addModifier(style.value, style.modifier);
- }
- });
- _.each(model.config, function (cfg) {
- if (cfg.value) {
- configObject[cfg.key] = addModifier(cfg.value, cfg.modifier);
- }
- });
-
- gridItem.styles = styleObject;
- gridItem.config = configObject;
- gridItem.hasConfig = gridItemHasConfig(styleObject, configObject);
-
- currentForm.$setDirty();
-
- $scope.gridItemSettingsDialog.show = false;
- $scope.gridItemSettingsDialog = null;
- };
-
- $scope.gridItemSettingsDialog.close = function(oldModel) {
- $scope.gridItemSettingsDialog.show = false;
- $scope.gridItemSettingsDialog = null;
- };
-
- };
-
- function stripModifier(val, modifier) {
- if (!val || !modifier || modifier.indexOf(placeHolder) < 0) {
- return val;
- } else {
- var paddArray = modifier.split(placeHolder);
- if(paddArray.length == 1){
- if (modifier.indexOf(placeHolder) === 0) {
- return val.slice(0, -paddArray[0].length);
- } else {
- return val.slice(paddArray[0].length, 0);
- }
- } else {
- if (paddArray[1].length === 0) {
- return val.slice(paddArray[0].length);
- }
- return val.slice(paddArray[0].length, -paddArray[1].length);
- }
- }
- }
-
- var addModifier = function(val, modifier){
- if (!modifier || modifier.indexOf(placeHolder) < 0) {
- return val;
- } else {
- return modifier.replace(placeHolder, val);
- }
- };
-
- function gridItemHasConfig(styles, config) {
-
- if(_.isEmpty(styles) && _.isEmpty(config)) {
- return false;
- } else {
- return true;
- }
-
- }
-
- // *********************************************
- // Area management functions
- // *********************************************
-
- $scope.clickCell = function(index, cells, row) {
- cells[index].active = true;
- row.hasActiveChild = true;
- };
-
- $scope.clickOutsideCell = function(index, cells, row) {
- cells[index].active = false;
- row.hasActiveChild = hasActiveChild(row, cells);
- };
-
- $scope.cellPreview = function (cell) {
- if (cell && cell.$allowedEditors) {
- var editor = cell.$allowedEditors[0];
- return editor.icon;
- } else {
- return "icon-layout";
- }
- };
-
-
- // *********************************************
- // Control management functions
- // *********************************************
- $scope.clickControl = function (index, controls, cell) {
- controls[index].active = true;
- cell.hasActiveChild = true;
- };
-
- $scope.clickOutsideControl = function (index, controls, cell) {
- controls[index].active = false;
- cell.hasActiveChild = hasActiveChild(cell, controls);
- };
-
- function hasActiveChild(item, children) {
-
- var activeChild = false;
-
- for(var i = 0; children.length > i; i++) {
- var child = children[i];
-
- if(child.active) {
- activeChild = true;
- }
- }
-
- if(activeChild) {
- return true;
- }
-
- }
-
-
- var guid = (function () {
- function s4() {
- return Math.floor((1 + Math.random()) * 0x10000)
- .toString(16)
- .substring(1);
- }
- return function () {
- return s4() + s4() + "-" + s4() + "-" + s4() + "-" +
- s4() + "-" + s4() + s4() + s4();
- };
- })();
-
- $scope.setUniqueId = function (cell, index) {
- return guid();
- };
-
- $scope.addControl = function (editor, cell, index, initialize) {
-
- initialize = (initialize !== false);
-
- var newControl = {
- value: null,
- editor: editor,
- $initializing: initialize
- };
-
- if (index === undefined) {
- index = cell.controls.length;
- }
-
- newControl.active = true;
-
- //populate control
- $scope.initControl(newControl, index + 1);
-
- cell.controls.push(newControl);
-
- };
-
- $scope.addTinyMce = function (cell) {
- var rte = $scope.getEditor("rte");
- $scope.addControl(rte, cell);
- };
-
- $scope.getEditor = function (alias) {
- return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; });
- };
-
- $scope.removeControl = function (cell, $index) {
- $scope.currentControl = null;
- cell.controls.splice($index, 1);
- };
-
- $scope.percentage = function (spans) {
- return ((spans / $scope.model.config.items.columns) * 100).toFixed(8);
- };
-
-
- $scope.clearPrompt = function (scopedObject, e) {
- scopedObject.deletePrompt = false;
- e.preventDefault();
- e.stopPropagation();
- };
-
- $scope.togglePrompt = function (scopedObject) {
- scopedObject.deletePrompt = !scopedObject.deletePrompt;
- };
-
- $scope.hidePrompt = function (scopedObject) {
- scopedObject.deletePrompt = false;
- };
-
- $scope.toggleAddRow = function() {
- $scope.showRowConfigurations = !$scope.showRowConfigurations;
- };
-
-
- // *********************************************
- // Initialization
- // these methods are called from ng-init on the template
- // so we can controll their first load data
- //
- // intialization sets non-saved data like percentage sizing, allowed editors and
- // other data that should all be pre-fixed with $ to strip it out on save
- // *********************************************
-
- // *********************************************
- // Init template + sections
- // *********************************************
- $scope.initContent = function () {
- var clear = true;
-
- //settings indicator shortcut
- if ( ($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) {
- $scope.hasSettings = true;
- }
-
- //ensure the grid has a column value set,
- //if nothing is found, set it to 12
- if ($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)) {
- $scope.model.config.items.columns = parseInt($scope.model.config.items.columns);
- } else {
- $scope.model.config.items.columns = 12;
- }
-
- if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0 && $scope.model.value.sections[0].rows && $scope.model.value.sections[0].rows.length > 0) {
-
- if ($scope.model.value.name && angular.isArray($scope.model.config.items.templates)) {
-
- //This will occur if it is an existing value, in which case
- // we need to determine which layout was applied by looking up
- // the name
- // TODO: We need to change this to an immutable ID!!
-
- var found = _.find($scope.model.config.items.templates, function (t) {
- return t.name === $scope.model.value.name;
- });
-
- if (found && angular.isArray(found.sections) && found.sections.length === $scope.model.value.sections.length) {
-
- //Cool, we've found the template associated with our current value with matching sections counts, now we need to
- // merge this template data on to our current value (as if it was new) so that we can preserve what is and isn't
- // allowed for this template based on the current config.
-
- _.each(found.sections, function (templateSection, index) {
- angular.extend($scope.model.value.sections[index], angular.copy(templateSection));
- });
-
- }
- }
-
- _.forEach($scope.model.value.sections, function (section, index) {
-
- if (section.grid > 0) {
- $scope.initSection(section);
-
- //we do this to ensure that the grid can be reset by deleting the last row
- if (section.rows.length > 0) {
- clear = false;
- }
- } else {
- $scope.model.value.sections.splice(index, 1);
- }
- });
- } else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) {
- $scope.addTemplate($scope.model.config.items.templates[0]);
- clear = false;
- }
-
- if (clear) {
- $scope.model.value = undefined;
- }
- };
-
- $scope.initSection = function (section) {
- section.$percentage = $scope.percentage(section.grid);
-
- section.$allowedLayouts = getAllowedLayouts(section);
-
- if (!section.rows || section.rows.length === 0) {
- section.rows = [];
- if(section.$allowedLayouts.length === 1){
- $scope.addRow(section, section.$allowedLayouts[0]);
- }
- } else {
- _.forEach(section.rows, function (row, index) {
- if (!row.$initialized) {
- var initd = $scope.initRow(row);
-
- //if init fails, remove
- if (!initd) {
- section.rows.splice(index, 1);
- } else {
- section.rows[index] = initd;
- }
- }
- });
-
- // if there is more than one row added - hide row add tools
- $scope.showRowConfigurations = false;
- }
- };
-
-
- // *********************************************
- // Init layout / row
- // *********************************************
- $scope.initRow = function (row) {
-
- //merge the layout data with the original config data
- //if there are no config info on this, splice it out
- var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; });
-
- if (!original) {
- return null;
- } else {
- //make a copy to not touch the original config
- original = angular.copy(original);
- original.styles = row.styles;
- original.config = row.config;
- original.hasConfig = gridItemHasConfig(row.styles, row.config);
-
-
- //sync area configuration
- _.each(original.areas, function (area, areaIndex) {
-
-
- if (area.grid > 0) {
- var currentArea = row.areas[areaIndex];
-
- if (currentArea) {
- area.config = currentArea.config;
- area.styles = currentArea.styles;
- area.hasConfig = gridItemHasConfig(currentArea.styles, currentArea.config);
- }
-
- //set editor permissions
- if (!area.allowed || area.allowAll === true) {
- area.$allowedEditors = $scope.availableEditors;
- area.$allowsRTE = true;
- } else {
- area.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
- return _.indexOf(area.allowed, editor.alias) >= 0;
- });
-
- if (_.indexOf(area.allowed, "rte") >= 0) {
- area.$allowsRTE = true;
- }
- }
-
- //copy over existing controls into the new areas
- if (row.areas.length > areaIndex && row.areas[areaIndex].controls) {
- area.controls = currentArea.controls;
-
- _.forEach(area.controls, function (control, controlIndex) {
- $scope.initControl(control, controlIndex);
- });
-
- } else {
- //if empty
- area.controls = [];
-
- //if only one allowed editor
- if(area.$allowedEditors.length === 1){
- $scope.addControl(area.$allowedEditors[0], area, 0, false);
- }
- }
-
- //set width
- area.$percentage = $scope.percentage(area.grid);
- area.$uniqueId = $scope.setUniqueId();
-
- } else {
- original.areas.splice(areaIndex, 1);
- }
- });
-
- //replace the old row
- original.$initialized = true;
-
- //set a disposable unique ID
- original.$uniqueId = $scope.setUniqueId();
-
- //set a no disposable unique ID (util for row styling)
- original.id = !row.id ? $scope.setUniqueId() : row.id;
-
- return original;
- }
-
- };
-
-
- // *********************************************
- // Init control
- // *********************************************
-
- $scope.initControl = function (control, index) {
- control.$index = index;
- control.$uniqueId = $scope.setUniqueId();
-
- //error handling in case of missing editor..
- //should only happen if stripped earlier
- if (!control.editor) {
- control.$editorPath = "views/propertyeditors/grid/editors/error.html";
- }
-
- if (!control.$editorPath) {
- var editorConfig = $scope.getEditor(control.editor.alias);
-
- if (editorConfig) {
- control.editor = editorConfig;
-
- //if its an absolute path
- if (control.editor.view.startsWith("/") || control.editor.view.startsWith("~/")) {
- control.$editorPath = umbRequestHelper.convertVirtualToAbsolutePath(control.editor.view);
- }
- else {
- //use convention
- control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html";
- }
- }
- else {
- control.$editorPath = "views/propertyeditors/grid/editors/error.html";
- }
- }
-
-
- };
-
-
- gridService.getGridEditors().then(function (response) {
- $scope.availableEditors = response.data;
-
- $scope.contentReady = true;
-
- // *********************************************
- // Init grid
- // *********************************************
- $scope.initContent();
-
- });
-
- //Clean the grid value before submitting to the server, we don't need
- // all of that grid configuration in the value to be stored!! All of that
- // needs to be merged in at runtime to ensure that the real config values are used
- // if they are ever updated.
-
- var unsubscribe = $scope.$on("formSubmitting", function () {
-
- if ($scope.model.value && $scope.model.value.sections) {
- _.each($scope.model.value.sections, function(section) {
- if (section.rows) {
- _.each(section.rows, function (row) {
- if (row.areas) {
- _.each(row.areas, function (area) {
-
- //Remove the 'editors' - these are the allowed editors, these will
- // be injected at runtime to this editor, it should not be persisted
-
- if (area.editors) {
- delete area.editors;
- }
-
- if (area.controls) {
- _.each(area.controls, function (control) {
- if (control.editor) {
- //replace
- var alias = control.editor.alias;
- control.editor = {
- alias: alias
- };
- }
- });
- }
- });
- }
- });
- }
- });
- }
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on("$destroy", function () {
- unsubscribe();
- });
-
- });
-angular.module("umbraco")
- .controller("Umbraco.PropertyEditors.GridPrevalueEditorController",
- function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout) {
-
- var emptyModel = {
- styles:[
- {
- label: "Set a background image",
- description: "Set a row background",
- key: "background-image",
- view: "imagepicker",
- modifier: "url({0})"
- }
- ],
-
- config:[
- {
- label: "Class",
- description: "Set a css class",
- key: "class",
- view: "textstring"
- }
- ],
-
- columns: 12,
- templates:[
- {
- name: "1 column layout",
- sections: [
- {
- grid: 12,
- }
- ]
- },
- {
- name: "2 column layout",
- sections: [
- {
- grid: 4,
- },
- {
- grid: 8
- }
- ]
- }
- ],
-
-
- layouts:[
- {
- name: "Headline",
- areas: [
- {
- grid: 12,
- editors: ["headline"]
- }
- ]
- },
- {
- name: "Article",
- areas: [
- {
- grid: 4
- },
- {
- grid: 8
- }
- ]
- }
- ]
- };
-
- /****************
- template
- *****************/
-
- $scope.configureTemplate = function(template) {
-
- var templatesCopy = angular.copy($scope.model.value.templates);
-
- if (template === undefined) {
- template = {
- name: "",
- sections: [
-
- ]
- };
- $scope.model.value.templates.push(template);
- }
-
- $scope.layoutConfigOverlay = {};
- $scope.layoutConfigOverlay.view = "views/propertyEditors/grid/dialogs/layoutconfig.html";
- $scope.layoutConfigOverlay.currentLayout = template;
- $scope.layoutConfigOverlay.rows = $scope.model.value.layouts;
- $scope.layoutConfigOverlay.columns = $scope.model.value.columns;
- $scope.layoutConfigOverlay.show = true;
-
- $scope.layoutConfigOverlay.submit = function(model) {
- $scope.layoutConfigOverlay.show = false;
- $scope.layoutConfigOverlay = null;
- };
-
- $scope.layoutConfigOverlay.close = function(oldModel) {
-
- //reset templates
- $scope.model.value.templates = templatesCopy;
-
- $scope.layoutConfigOverlay.show = false;
- $scope.layoutConfigOverlay = null;
- }
-
- };
-
- $scope.deleteTemplate = function(index){
- $scope.model.value.templates.splice(index, 1);
- };
-
-
- /****************
- Row
- *****************/
-
- $scope.configureLayout = function(layout) {
-
- var layoutsCopy = angular.copy($scope.model.value.layouts);
-
- if(layout === undefined){
- layout = {
- name: "",
- areas:[
-
- ]
- };
- $scope.model.value.layouts.push(layout);
- }
-
- $scope.rowConfigOverlay = {};
- $scope.rowConfigOverlay.view = "views/propertyEditors/grid/dialogs/rowconfig.html";
- $scope.rowConfigOverlay.currentRow = layout;
- $scope.rowConfigOverlay.editors = $scope.editors;
- $scope.rowConfigOverlay.columns = $scope.model.value.columns;
- $scope.rowConfigOverlay.show = true;
-
- $scope.rowConfigOverlay.submit = function(model) {
- $scope.rowConfigOverlay.show = false;
- $scope.rowConfigOverlay = null;
- };
-
- $scope.rowConfigOverlay.close = function(oldModel) {
- $scope.model.value.layouts = layoutsCopy;
- $scope.rowConfigOverlay.show = false;
- $scope.rowConfigOverlay = null;
- };
-
- };
-
- //var rowDeletesPending = false;
- $scope.deleteLayout = function(index) {
-
- $scope.rowDeleteOverlay = {};
- $scope.rowDeleteOverlay.view = "views/propertyEditors/grid/dialogs/rowdeleteconfirm.html";
- $scope.rowDeleteOverlay.dialogData = {
- rowName: $scope.model.value.layouts[index].name
- };
- $scope.rowDeleteOverlay.show = true;
-
- $scope.rowDeleteOverlay.submit = function(model) {
-
- $scope.model.value.layouts.splice(index, 1);
-
- $scope.rowDeleteOverlay.show = false;
- $scope.rowDeleteOverlay = null;
- };
-
- $scope.rowDeleteOverlay.close = function(oldModel) {
- $scope.rowDeleteOverlay.show = false;
- $scope.rowDeleteOverlay = null;
- };
-
- };
-
-
- /****************
- utillities
- *****************/
- $scope.toggleCollection = function(collection, toggle){
- if(toggle){
- collection = [];
- }else{
- delete collection;
- }
- };
-
- $scope.percentage = function(spans){
- return ((spans / $scope.model.value.columns) * 100).toFixed(8);
- };
-
- $scope.zeroWidthFilter = function (cell) {
- return cell.grid > 0;
- };
-
- /****************
- Config
- *****************/
-
- $scope.removeConfigValue = function(collection, index){
- collection.splice(index, 1);
- };
-
- var editConfigCollection = function(configValues, title, callback) {
-
- $scope.editConfigCollectionOverlay = {};
- $scope.editConfigCollectionOverlay.view = "views/propertyeditors/grid/dialogs/editconfig.html";
- $scope.editConfigCollectionOverlay.config = configValues;
- $scope.editConfigCollectionOverlay.title = title;
- $scope.editConfigCollectionOverlay.show = true;
-
- $scope.editConfigCollectionOverlay.submit = function(model) {
-
- callback(model.config)
-
- $scope.editConfigCollectionOverlay.show = false;
- $scope.editConfigCollectionOverlay = null;
- };
-
- $scope.editConfigCollectionOverlay.close = function(oldModel) {
- $scope.editConfigCollectionOverlay.show = false;
- $scope.editConfigCollectionOverlay = null;
- };
-
- };
-
- $scope.editConfig = function() {
- editConfigCollection($scope.model.value.config, "Settings", function(data) {
- $scope.model.value.config = data;
- });
- };
-
- $scope.editStyles = function() {
- editConfigCollection($scope.model.value.styles, "Styling", function(data){
- $scope.model.value.styles = data;
- });
- };
-
- /****************
- editors
- *****************/
- gridService.getGridEditors().then(function(response){
- $scope.editors = response.data;
- });
-
-
- /* init grid data */
- if (!$scope.model.value || $scope.model.value === "" || !$scope.model.value.templates) {
- $scope.model.value = emptyModel;
- } else {
-
- if (!$scope.model.value.columns) {
- $scope.model.value.columns = emptyModel.columns;
- }
-
-
- if (!$scope.model.value.config) {
- $scope.model.value.config = [];
- }
-
- if (!$scope.model.value.styles) {
- $scope.model.value.styles = [];
- }
- }
-
- /****************
- Clean up
- *****************/
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
- var ts = $scope.model.value.templates;
- var ls = $scope.model.value.layouts;
-
- _.each(ts, function(t){
- _.each(t.sections, function(section, index){
- if(section.grid === 0){
- t.sections.splice(index, 1);
- }
- });
- });
-
- _.each(ls, function(l){
- _.each(l.areas, function(area, index){
- if(area.grid === 0){
- l.areas.splice(index, 1);
- }
- });
- });
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
-
- });
+ /****************
+ Section
+ *****************/
+ $scope.configureSection = function(section, template){
+ if(section === undefined){
+ var space = ($scope.availableLayoutSpace > 4) ? 4 : $scope.availableLayoutSpace;
+ section = {
+ grid: space
+ };
+ template.sections.push(section);
+ }
+
+ $scope.currentSection = section;
+ };
-//this controller simply tells the dialogs service to open a mediaPicker window
-//with a specified callback, this callback will receive an object with a selection on it
-angular.module('umbraco')
- .controller("Umbraco.PropertyEditors.ImageCropperController",
- function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager, angularHelper) {
-
- var config = angular.copy($scope.model.config);
-
- //move previously saved value to the editor
- if ($scope.model.value) {
- //backwards compat with the old file upload (incase some-one swaps them..)
- if (angular.isString($scope.model.value)) {
- config.src = $scope.model.value;
- $scope.model.value = config;
- } else if ($scope.model.value.crops) {
- //sync any config changes with the editor and drop outdated crops
- _.each($scope.model.value.crops, function (saved) {
- var configured = _.find(config.crops, function (item) { return item.alias === saved.alias });
-
- if (configured && configured.height === saved.height && configured.width === saved.width) {
- configured.coordinates = saved.coordinates;
- }
- });
- $scope.model.value.crops = config.crops;
-
- //restore focalpoint if missing
- if (!$scope.model.value.focalPoint) {
- $scope.model.value.focalPoint = { left: 0.5, top: 0.5 };
- }
- }
-
- $scope.imageSrc = $scope.model.value.src;
- }
-
-
- //crop a specific crop
- $scope.crop = function (crop) {
- $scope.currentCrop = crop;
- $scope.currentPoint = undefined;
- };
-
- //done cropping
- $scope.done = function () {
- $scope.currentCrop = undefined;
- $scope.currentPoint = undefined;
- };
-
- //crop a specific crop
- $scope.clear = function (crop) {
- //clear current uploaded files
- fileManager.setFiles($scope.model.alias, []);
-
- //clear the ui
- $scope.imageSrc = undefined;
- if ($scope.model.value) {
- delete $scope.model.value;
- }
-
- // set form to dirty to tricker discard changes dialog
- var currForm = angularHelper.getCurrentForm($scope);
- currForm.$setDirty();
- };
-
- //show previews
- $scope.togglePreviews = function () {
- if ($scope.showPreviews) {
- $scope.showPreviews = false;
- $scope.tempShowPreviews = false;
- } else {
- $scope.showPreviews = true;
- }
- };
-
- //on image selected, update the cropper
- $scope.$on("filesSelected", function (ev, args) {
- $scope.model.value = config;
-
- if (args.files && args.files[0]) {
-
- fileManager.setFiles($scope.model.alias, args.files);
-
- var reader = new FileReader();
- reader.onload = function (e) {
-
- $scope.$apply(function () {
- $scope.imageSrc = e.target.result;
- });
-
- };
-
- reader.readAsDataURL(args.files[0]);
- }
- });
-
-
- //here we declare a special method which will be called whenever the value has changed from the server
- $scope.model.onValueChanged = function (newVal, oldVal) {
- //clear current uploaded files
- fileManager.setFiles($scope.model.alias, []);
- };
-
- var unsubscribe = $scope.$on("formSubmitting", function () {
- $scope.done();
- });
-
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
- })
- .run(function (mediaHelper, umbRequestHelper) {
- if (mediaHelper && mediaHelper.registerFileResolver) {
-
- //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
- // they contain different data structures so if we need to query against it we need to be aware of this.
- mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
- if (property.value.src) {
-
- if (thumbnail === true) {
- return property.value.src + "?width=500&mode=max&animationprocessmode=first";
- }
- else {
- return property.value.src;
- }
-
- //this is a fallback in case the cropper has been asssigned a upload field
- }
- else if (angular.isString(property.value)) {
- if (thumbnail) {
-
- if (mediaHelper.detectIfImageByExtension(property.value)) {
-
- var thumbnailUrl = umbRequestHelper.getApiUrl(
- "imagesApiBaseUrl",
- "GetBigThumbnail",
- [{ originalImagePath: property.value }]);
-
- return thumbnailUrl;
- }
- else {
- return null;
- }
-
- }
- else {
- return property.value;
- }
- }
-
- return null;
- });
- }
- });
-angular.module("umbraco").controller("Umbraco.PrevalueEditors.CropSizesController",
- function ($scope, $timeout) {
-
- if (!$scope.model.value) {
- $scope.model.value = [];
- }
-
- $scope.remove = function (item, evt) {
- evt.preventDefault();
- $scope.model.value = _.reject($scope.model.value, function (x) {
- return x.alias === item.alias;
- });
- };
-
- $scope.edit = function (item, evt) {
- evt.preventDefault();
- $scope.newItem = item;
- };
-
- $scope.cancel = function (evt) {
- evt.preventDefault();
- $scope.newItem = null;
- };
-
- $scope.add = function (evt) {
- evt.preventDefault();
-
- if ($scope.newItem && $scope.newItem.alias &&
- angular.isNumber($scope.newItem.width) && angular.isNumber($scope.newItem.height) &&
- $scope.newItem.width > 0 && $scope.newItem.height > 0) {
-
- var exists = _.find($scope.model.value, function (item) { return $scope.newItem.alias === item.alias; });
- if (!exists) {
- $scope.model.value.push($scope.newItem);
- $scope.newItem = {};
- $scope.hasError = false;
- return;
- }
- }
-
- //there was an error, do the highlight (will be set back by the directive)
- $scope.hasError = true;
- };
+ $scope.deleteSection = function(index){
+ $scope.currentTemplate.sections.splice(index, 1);
+ };
+
+ $scope.closeSection = function(){
+ $scope.currentSection = undefined;
+ };
+
+ $scope.$watch("currentLayout", function(layout){
+ if(layout){
+ var total = 0;
+ _.forEach(layout.sections, function(section){
+ total = (total + section.grid);
+ });
+
+ $scope.availableLayoutSpace = $scope.columns - total;
+ }
+ }, true);
+ });
+
+function RowConfigController($scope) {
+
+ $scope.currentRow = $scope.model.currentRow;
+ $scope.editors = $scope.model.editors;
+ $scope.columns = $scope.model.columns;
+
+ $scope.scaleUp = function(section, max, overflow) {
+ var add = 1;
+ if (overflow !== true) {
+ add = (max > 1) ? 1 : max;
+ }
+ //var add = (max > 1) ? 1 : max;
+ section.grid = section.grid + add;
+ };
+
+ $scope.scaleDown = function(section) {
+ var remove = (section.grid > 1) ? 1 : section.grid;
+ section.grid = section.grid - remove;
+ };
+
+ $scope.percentage = function(spans) {
+ return ((spans / $scope.columns) * 100).toFixed(8);
+ };
+
+ $scope.toggleCollection = function(collection, toggle) {
+ if (toggle) {
+ collection = [];
+ }
+ else {
+ delete collection;
+ }
+ };
+
+
+ /****************
+ area
+ *****************/
+ $scope.configureCell = function(cell, row) {
+ if ($scope.currentCell && $scope.currentCell === cell) {
+ delete $scope.currentCell;
+ }
+ else {
+ if (cell === undefined) {
+ var available = $scope.availableRowSpace;
+ var space = 4;
+
+ if (available < 4 && available > 0) {
+ space = available;
+ }
+
+ cell = {
+ grid: space
+ };
+
+ row.areas.push(cell);
+ }
+ $scope.currentCell = cell;
+ }
+ };
+
+ $scope.deleteArea = function(index) {
+ $scope.currentRow.areas.splice(index, 1);
+ };
+ $scope.closeArea = function() {
+ $scope.currentCell = undefined;
+ };
+
+ $scope.nameChanged = false;
+ var originalName = $scope.currentRow.name;
+ $scope.$watch("currentRow", function(row) {
+ if (row) {
+
+ var total = 0;
+ _.forEach(row.areas, function(area) {
+ total = (total + area.grid);
+ });
+
+ $scope.availableRowSpace = $scope.columns - total;
+
+ if (originalName) {
+ if (originalName != row.name) {
+ $scope.nameChanged = true;
+ }
+ else {
+ $scope.nameChanged = false;
+ }
+ }
+ }
+ }, true);
+
+}
+
+angular.module("umbraco").controller("Umbraco.PropertyEditors.GridPrevalueEditor.RowConfigController", RowConfigController);
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.Grid.EmbedController",
+ function ($scope, $rootScope, $timeout) {
+
+ $scope.setEmbed = function(){
+ $scope.embedDialog = {};
+ $scope.embedDialog.view = "embed";
+ $scope.embedDialog.show = true;
+
+ $scope.embedDialog.submit = function(model) {
+ $scope.control.value = model.embed.preview;
+ $scope.embedDialog.show = false;
+ $scope.embedDialog = null;
+ };
+
+ $scope.embedDialog.close = function(oldModel) {
+ $scope.embedDialog.show = false;
+ $scope.embedDialog = null;
+ };
+
+ };
+
+ $timeout(function(){
+ if($scope.control.$initializing){
+ $scope.setEmbed();
+ }
+ }, 200);
+});
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.Grid.MacroController",
+ function ($scope, $rootScope, $timeout, dialogService, macroResource, macroService, $routeParams) {
+
+ $scope.title = "Click to insert macro";
+
+ $scope.setMacro = function(){
+
+ var dialogData = {
+ richTextEditor: true,
+ macroData: $scope.control.value || {
+ macroAlias: $scope.control.editor.config && $scope.control.editor.config.macroAlias
+ ? $scope.control.editor.config.macroAlias : ""
+ }
+ };
+
+ $scope.macroPickerOverlay = {};
+ $scope.macroPickerOverlay.view = "macropicker";
+ $scope.macroPickerOverlay.dialogData = dialogData;
+ $scope.macroPickerOverlay.show = true;
+
+ $scope.macroPickerOverlay.submit = function(model) {
+
+ var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
+
+ $scope.control.value = {
+ macroAlias: macroObject.macroAlias,
+ macroParamsDictionary: macroObject.macroParamsDictionary
+ };
+
+ $scope.setPreview($scope.control.value );
+
+ $scope.macroPickerOverlay.show = false;
+ $scope.macroPickerOverlay = null;
+ };
+
+ $scope.macroPickerOverlay.close = function(oldModel) {
+ $scope.macroPickerOverlay.show = false;
+ $scope.macroPickerOverlay = null;
+ };
+
+ };
+
+ $scope.setPreview = function(macro){
+ var contentId = $routeParams.id;
+
+ macroResource.getMacroResultAsHtmlForEditor(macro.macroAlias, contentId, macro.macroParamsDictionary)
+ .then(function (htmlResult) {
+ $scope.title = macro.macroAlias;
+ if(htmlResult.trim().length > 0 && htmlResult.indexOf("Macro:") < 0){
+ $scope.preview = htmlResult;
+ }
+ });
+
+ };
+
+ $timeout(function(){
+ if($scope.control.$initializing){
+ $scope.setMacro();
+ }else if($scope.control.value){
+ $scope.setPreview($scope.control.value);
+ }
+ }, 200);
+});
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.Grid.MediaController",
+ function ($scope, $rootScope, $timeout) {
+
+ $scope.setImage = function(){
+ $scope.mediaPickerOverlay = {};
+ $scope.mediaPickerOverlay.view = "mediapicker";
+ $scope.mediaPickerOverlay.cropSize = $scope.control.editor.config && $scope.control.editor.config.size ? $scope.control.editor.config.size : undefined;
+ $scope.mediaPickerOverlay.showDetails = true;
+ $scope.mediaPickerOverlay.disableFolderSelect = true;
+ $scope.mediaPickerOverlay.onlyImages = true;
+ $scope.mediaPickerOverlay.show = true;
+
+ $scope.mediaPickerOverlay.submit = function(model) {
+ var selectedImage = model.selectedImages[0];
+
+ $scope.control.value = {
+ focalPoint: selectedImage.focalPoint,
+ id: selectedImage.id,
+ image: selectedImage.image,
+ altText: selectedImage.altText
+ };
+
+ $scope.setUrl();
+
+ $scope.mediaPickerOverlay.show = false;
+ $scope.mediaPickerOverlay = null;
+ };
+
+ $scope.mediaPickerOverlay.close = function(oldModel) {
+ $scope.mediaPickerOverlay.show = false;
+ $scope.mediaPickerOverlay = null;
+ };
+ };
+
+ $scope.setUrl = function(){
+
+ if($scope.control.value.image){
+ var url = $scope.control.value.image;
+
+ if($scope.control.editor.config && $scope.control.editor.config.size){
+ url += "?width=" + $scope.control.editor.config.size.width;
+ url += "&height=" + $scope.control.editor.config.size.height;
+ url += "&animationprocessmode=first";
+
+ if($scope.control.value.focalPoint){
+ url += "¢er=" + $scope.control.value.focalPoint.top +"," + $scope.control.value.focalPoint.left;
+ url += "&mode=crop";
+ }
+ }
+
+ // set default size if no crop present (moved from the view)
+ if (url.indexOf('?') == -1)
+ {
+ url += "?width=800&upscale=false&animationprocessmode=false"
+ }
+ $scope.url = url;
+ }
+ };
+
+ $timeout(function(){
+ if($scope.control.$initializing){
+ $scope.setImage();
+ }else if($scope.control.value){
+ $scope.setUrl();
+ }
+ }, 200);
+});
+
+(function() {
+ "use strict";
+
+ function GridRichTextEditorController($scope, tinyMceService, macroService) {
+
+ var vm = this;
+
+ vm.openLinkPicker = openLinkPicker;
+ vm.openMediaPicker = openMediaPicker;
+ vm.openMacroPicker = openMacroPicker;
+ vm.openEmbed = openEmbed;
+
+ function openLinkPicker(editor, currentTarget, anchorElement) {
+ vm.linkPickerOverlay = {
+ view: "linkpicker",
+ currentTarget: currentTarget,
+ show: true,
+ submit: function(model) {
+ tinyMceService.insertLinkInEditor(editor, model.target, anchorElement);
+ vm.linkPickerOverlay.show = false;
+ vm.linkPickerOverlay = null;
+ }
+ };
+ }
+
+ function openMediaPicker(editor, currentTarget, userData) {
+ vm.mediaPickerOverlay = {
+ currentTarget: currentTarget,
+ onlyImages: true,
+ showDetails: true,
+ startNodeId: userData.startMediaId,
+ view: "mediapicker",
+ show: true,
+ submit: function(model) {
+ tinyMceService.insertMediaInEditor(editor, model.selectedImages[0]);
+ vm.mediaPickerOverlay.show = false;
+ vm.mediaPickerOverlay = null;
+ }
+ };
+ }
+
+ function openEmbed(editor) {
+ vm.embedOverlay = {
+ view: "embed",
+ show: true,
+ submit: function(model) {
+ tinyMceService.insertEmbeddedMediaInEditor(editor, model.embed.preview);
+ vm.embedOverlay.show = false;
+ vm.embedOverlay = null;
+ }
+ };
+ }
+
+ function openMacroPicker(editor, dialogData) {
+ vm.macroPickerOverlay = {
+ view: "macropicker",
+ dialogData: dialogData,
+ show: true,
+ submit: function(model) {
+ var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
+ tinyMceService.insertMacroInEditor(editor, macroObject, $scope);
+ vm.macroPickerOverlay.show = false;
+ vm.macroPickerOverlay = null;
+ }
+ };
+ }
+
+
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.PropertyEditors.Grid.RichTextEditorController", GridRichTextEditorController);
+
+})();
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.Grid.TextStringController",
+ function ($scope, $rootScope, $timeout, dialogService) {
+
+
+
+ });
+
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.GridController",
+ function ($scope, $http, assetsService, localizationService, $rootScope, dialogService, gridService, mediaResource, imageHelper, $timeout, umbRequestHelper, angularHelper) {
+
+ // Grid status variables
+ var placeHolder = "";
+ var currentForm = angularHelper.getCurrentForm($scope);
+
+ $scope.currentRow = null;
+ $scope.currentCell = null;
+ $scope.currentToolsControl = null;
+ $scope.currentControl = null;
+ $scope.openRTEToolbarId = null;
+ $scope.hasSettings = false;
+ $scope.showRowConfigurations = true;
+ $scope.sortMode = false;
+ $scope.reorderKey = "general_reorder";
+
+ // *********************************************
+ // Sortable options
+ // *********************************************
+
+ var draggedRteSettings;
+
+ $scope.sortableOptionsRow = {
+ distance: 10,
+ cursor: "move",
+ placeholder: "ui-sortable-placeholder",
+ handle: ".umb-row-title-bar",
+ helper: "clone",
+ forcePlaceholderSize: true,
+ tolerance: "pointer",
+ zIndex: 999999999999999999,
+ scrollSensitivity: 100,
+ cursorAt: {
+ top: 40,
+ left: 60
+ },
+
+ sort: function (event, ui) {
+ /* prevent vertical scroll out of the screen */
+ var max = $(".umb-grid").width() - 150;
+ if (parseInt(ui.helper.css("left")) > max) {
+ ui.helper.css({ "left": max + "px" });
+ }
+ if (parseInt(ui.helper.css("left")) < 20) {
+ ui.helper.css({ "left": 20 });
+ }
+ },
+
+ start: function (e, ui) {
+
+ // Fade out row when sorting
+ ui.item.context.style.display = "block";
+ ui.item.context.style.opacity = "0.5";
+
+ draggedRteSettings = {};
+ ui.item.find(".mceNoEditor").each(function () {
+ // remove all RTEs in the dragged row and save their settings
+ var id = $(this).attr("id");
+ draggedRteSettings[id] = _.findWhere(tinyMCE.editors, { id: id }).settings;
+ // tinyMCE.execCommand("mceRemoveEditor", false, id);
+ });
+ },
+
+ stop: function (e, ui) {
+
+ // Fade in row when sorting stops
+ ui.item.context.style.opacity = "1";
+
+ // reset all RTEs affected by the dragging
+ ui.item.parents(".umb-column").find(".mceNoEditor").each(function () {
+ var id = $(this).attr("id");
+ draggedRteSettings[id] = draggedRteSettings[id] || _.findWhere(tinyMCE.editors, { id: id }).settings;
+ tinyMCE.execCommand("mceRemoveEditor", false, id);
+ tinyMCE.init(draggedRteSettings[id]);
+ });
+ currentForm.$setDirty();
+ }
+ };
+
+ var notIncludedRte = [];
+ var cancelMove = false;
+
+ $scope.sortableOptionsCell = {
+ distance: 10,
+ cursor: "move",
+ placeholder: "ui-sortable-placeholder",
+ handle: ".umb-control-handle",
+ helper: "clone",
+ connectWith: ".umb-cell-inner",
+ forcePlaceholderSize: true,
+ tolerance: "pointer",
+ zIndex: 999999999999999999,
+ scrollSensitivity: 100,
+ cursorAt: {
+ top: 45,
+ left: 90
+ },
+
+ sort: function (event, ui) {
+
+ /* prevent vertical scroll out of the screen */
+ var position = parseInt(ui.item.parent().offset().left) + parseInt(ui.helper.css("left")) - parseInt($(".umb-grid").offset().left);
+ var max = $(".umb-grid").width() - 220;
+ if (position > max) {
+ ui.helper.css({ "left": max - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
+ }
+ if (position < 0) {
+ ui.helper.css({ "left": 0 - parseInt(ui.item.parent().offset().left) + parseInt($(".umb-grid").offset().left) + "px" });
+ }
+ },
+
+ over: function (event, ui) {
+ var allowedEditors = $(event.target).scope().area.allowed;
+
+ if ($.inArray(ui.item.scope().control.editor.alias, allowedEditors) < 0 && allowedEditors) {
+
+ $scope.$apply(function () {
+ $(event.target).scope().area.dropNotAllowed = true;
+ });
+
+ ui.placeholder.hide();
+ cancelMove = true;
+ }
+ else {
+ if ($(event.target).scope().area.controls.length == 0){
+
+ $scope.$apply(function () {
+ $(event.target).scope().area.dropOnEmpty = true;
+ });
+ ui.placeholder.hide();
+ } else {
+ ui.placeholder.show();
+ }
+ cancelMove = false;
+ }
+ },
+
+ out: function(event, ui) {
+ $scope.$apply(function () {
+ $(event.target).scope().area.dropNotAllowed = false;
+ $(event.target).scope().area.dropOnEmpty = false;
+ });
+ },
+
+ update: function (event, ui) {
+ /* add all RTEs which are affected by the dragging */
+ if (!ui.sender) {
+ if (cancelMove) {
+ ui.item.sortable.cancel();
+ }
+ ui.item.parents(".umb-cell.content").find(".mceNoEditor").each(function () {
+ if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
+ notIncludedRte.splice(0, 0, $(this).attr("id"));
+ }
+ });
+ }
+ else {
+ $(event.target).find(".mceNoEditor").each(function () {
+ if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
+ notIncludedRte.splice(0, 0, $(this).attr("id"));
+ }
+ });
+ }
+ currentForm.$setDirty();
+ },
+
+ start: function (e, ui) {
+
+ // fade out control when sorting
+ ui.item.context.style.display = "block";
+ ui.item.context.style.opacity = "0.5";
+
+ // reset dragged RTE settings in case a RTE isn't dragged
+ draggedRteSettings = undefined;
+ ui.item.context.style.display = "block";
+ ui.item.find(".mceNoEditor").each(function () {
+ notIncludedRte = [];
+ var editors = _.findWhere(tinyMCE.editors, { id: $(this).attr("id") });
+
+ // save the dragged RTE settings
+ if(editors) {
+ draggedRteSettings = editors.settings;
+
+ // remove the dragged RTE
+ tinyMCE.execCommand("mceRemoveEditor", false, $(this).attr("id"));
+
+ }
+
+ });
+ },
+
+ stop: function (e, ui) {
+
+ // Fade in control when sorting stops
+ ui.item.context.style.opacity = "1";
+
+ ui.item.parents(".umb-cell-content").find(".mceNoEditor").each(function () {
+ if ($.inArray($(this).attr("id"), notIncludedRte) < 0) {
+ // add all dragged's neighbouring RTEs in the new cell
+ notIncludedRte.splice(0, 0, $(this).attr("id"));
+ }
+ });
+ $timeout(function () {
+ // reconstruct the dragged RTE (could be undefined when dragging something else than RTE)
+ if (draggedRteSettings !== undefined) {
+ tinyMCE.init(draggedRteSettings);
+ }
+
+ _.forEach(notIncludedRte, function (id) {
+ // reset all the other RTEs
+ if (draggedRteSettings === undefined || id !== draggedRteSettings.id) {
+ var rteSettings = _.findWhere(tinyMCE.editors, { id: id }).settings;
+ tinyMCE.execCommand("mceRemoveEditor", false, id);
+ tinyMCE.init(rteSettings);
+ }
+ });
+ }, 500, false);
+
+ $scope.$apply(function () {
+
+ var cell = $(e.target).scope().area;
+ cell.hasActiveChild = hasActiveChild(cell, cell.controls);
+ cell.active = false;
+ });
+ }
+
+ };
+
+ $scope.toggleSortMode = function() {
+ $scope.sortMode = !$scope.sortMode;
+ if($scope.sortMode) {
+ $scope.reorderKey = "general_reorderDone";
+ } else {
+ $scope.reorderKey = "general_reorder";
+ }
+ };
+
+ $scope.showReorderButton = function() {
+ if($scope.model.value && $scope.model.value.sections) {
+ for(var i = 0; $scope.model.value.sections.length > i; i++) {
+ var section = $scope.model.value.sections[i];
+ if(section.rows && section.rows.length > 0) {
+ return true;
+ }
+ }
+ }
+ };
+
+ // *********************************************
+ // Add items overlay menu
+ // *********************************************
+ $scope.openEditorOverlay = function(event, area, index, key) {
+ $scope.editorOverlay = {
+ view: "itempicker",
+ filter: false,
+ title: localizationService.localize("grid_insertControl"),
+ availableItems: area.$allowedEditors,
+ event: event,
+ show: true,
+ submit: function(model) {
+ $scope.addControl(model.selectedItem, area, index);
+ $scope.editorOverlay.show = false;
+ $scope.editorOverlay = null;
+ }
+ };
+ };
+
+ // *********************************************
+ // Template management functions
+ // *********************************************
+
+ $scope.addTemplate = function (template) {
+ $scope.model.value = angular.copy(template);
+
+ //default row data
+ _.forEach($scope.model.value.sections, function (section) {
+ $scope.initSection(section);
+ });
+ };
+
+
+ // *********************************************
+ // Row management function
+ // *********************************************
+
+ $scope.clickRow = function(index, rows) {
+ rows[index].active = true;
+ };
+
+ $scope.clickOutsideRow = function(index, rows) {
+ rows[index].active = false;
+ };
+
+ function getAllowedLayouts(section) {
+
+ var layouts = $scope.model.config.items.layouts;
+
+ //This will occur if it is a new section which has been
+ // created from a 'template'
+ if (section.allowed && section.allowed.length > 0) {
+ return _.filter(layouts, function (layout) {
+ return _.indexOf(section.allowed, layout.name) >= 0;
+ });
+ }
+ else {
+
+
+ return layouts;
+ }
+ }
+
+ $scope.addRow = function (section, layout) {
+
+ //copy the selected layout into the rows collection
+ var row = angular.copy(layout);
+
+ // Init row value
+ row = $scope.initRow(row);
+
+ // Push the new row
+ if (row) {
+ section.rows.push(row);
+ }
+
+ currentForm.$setDirty();
+
+ $scope.showRowConfigurations = false;
+
+ };
+
+ $scope.removeRow = function (section, $index) {
+ if (section.rows.length > 0) {
+ section.rows.splice($index, 1);
+ $scope.currentRow = null;
+ $scope.openRTEToolbarId = null;
+ currentForm.$setDirty();
+ }
+
+ if(section.rows.length === 0) {
+ $scope.showRowConfigurations = true;
+ }
+ };
+
+ $scope.editGridItemSettings = function (gridItem, itemType) {
+
+ placeHolder = "{0}";
+
+ var styles, config;
+ if (itemType === 'control') {
+ styles = null;
+ config = angular.copy(gridItem.editor.config.settings);
+ } else {
+ styles = _.filter(angular.copy($scope.model.config.items.styles), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
+ config = _.filter(angular.copy($scope.model.config.items.config), function (item) { return (item.applyTo === undefined || item.applyTo === itemType); });
+ }
+
+ if(angular.isObject(gridItem.config)){
+ _.each(config, function(cfg){
+ var val = gridItem.config[cfg.key];
+ if(val){
+ cfg.value = stripModifier(val, cfg.modifier);
+ }
+ });
+ }
+
+ if(angular.isObject(gridItem.styles)){
+ _.each(styles, function(style){
+ var val = gridItem.styles[style.key];
+ if(val){
+ style.value = stripModifier(val, style.modifier);
+ }
+ });
+ }
+
+ $scope.gridItemSettingsDialog = {};
+ $scope.gridItemSettingsDialog.view = "views/propertyeditors/grid/dialogs/config.html";
+ $scope.gridItemSettingsDialog.title = "Settings";
+ $scope.gridItemSettingsDialog.styles = styles;
+ $scope.gridItemSettingsDialog.config = config;
+
+ $scope.gridItemSettingsDialog.show = true;
+
+ $scope.gridItemSettingsDialog.submit = function(model) {
+
+ var styleObject = {};
+ var configObject = {};
+
+ _.each(model.styles, function(style){
+ if(style.value){
+ styleObject[style.key] = addModifier(style.value, style.modifier);
+ }
+ });
+ _.each(model.config, function (cfg) {
+ if (cfg.value) {
+ configObject[cfg.key] = addModifier(cfg.value, cfg.modifier);
+ }
+ });
+
+ gridItem.styles = styleObject;
+ gridItem.config = configObject;
+ gridItem.hasConfig = gridItemHasConfig(styleObject, configObject);
+
+ currentForm.$setDirty();
+
+ $scope.gridItemSettingsDialog.show = false;
+ $scope.gridItemSettingsDialog = null;
+ };
+
+ $scope.gridItemSettingsDialog.close = function(oldModel) {
+ $scope.gridItemSettingsDialog.show = false;
+ $scope.gridItemSettingsDialog = null;
+ };
+
+ };
+
+ function stripModifier(val, modifier) {
+ if (!val || !modifier || modifier.indexOf(placeHolder) < 0) {
+ return val;
+ } else {
+ var paddArray = modifier.split(placeHolder);
+ if(paddArray.length == 1){
+ if (modifier.indexOf(placeHolder) === 0) {
+ return val.slice(0, -paddArray[0].length);
+ } else {
+ return val.slice(paddArray[0].length, 0);
+ }
+ } else {
+ if (paddArray[1].length === 0) {
+ return val.slice(paddArray[0].length);
+ }
+ return val.slice(paddArray[0].length, -paddArray[1].length);
+ }
+ }
+ }
+
+ var addModifier = function(val, modifier){
+ if (!modifier || modifier.indexOf(placeHolder) < 0) {
+ return val;
+ } else {
+ return modifier.replace(placeHolder, val);
+ }
+ };
+
+ function gridItemHasConfig(styles, config) {
+
+ if(_.isEmpty(styles) && _.isEmpty(config)) {
+ return false;
+ } else {
+ return true;
+ }
+
+ }
+
+ // *********************************************
+ // Area management functions
+ // *********************************************
+
+ $scope.clickCell = function(index, cells, row) {
+ cells[index].active = true;
+ row.hasActiveChild = true;
+ };
+
+ $scope.clickOutsideCell = function(index, cells, row) {
+ cells[index].active = false;
+ row.hasActiveChild = hasActiveChild(row, cells);
+ };
+
+ $scope.cellPreview = function (cell) {
+ if (cell && cell.$allowedEditors) {
+ var editor = cell.$allowedEditors[0];
+ return editor.icon;
+ } else {
+ return "icon-layout";
+ }
+ };
+
+
+ // *********************************************
+ // Control management functions
+ // *********************************************
+ $scope.clickControl = function (index, controls, cell) {
+ controls[index].active = true;
+ cell.hasActiveChild = true;
+ };
+
+ $scope.clickOutsideControl = function (index, controls, cell) {
+ controls[index].active = false;
+ cell.hasActiveChild = hasActiveChild(cell, controls);
+ };
+
+ function hasActiveChild(item, children) {
+
+ var activeChild = false;
+
+ for(var i = 0; children.length > i; i++) {
+ var child = children[i];
+
+ if(child.active) {
+ activeChild = true;
+ }
+ }
+
+ if(activeChild) {
+ return true;
+ }
+
+ }
+
+
+ var guid = (function () {
+ function s4() {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ }
+ return function () {
+ return s4() + s4() + "-" + s4() + "-" + s4() + "-" +
+ s4() + "-" + s4() + s4() + s4();
+ };
+ })();
+
+ $scope.setUniqueId = function (cell, index) {
+ return guid();
+ };
+
+ $scope.addControl = function (editor, cell, index, initialize) {
+
+ initialize = (initialize !== false);
+
+ var newControl = {
+ value: null,
+ editor: editor,
+ $initializing: initialize
+ };
+
+ if (index === undefined) {
+ index = cell.controls.length;
+ }
+
+ newControl.active = true;
+
+ //populate control
+ $scope.initControl(newControl, index + 1);
+
+ cell.controls.push(newControl);
+
+ };
+
+ $scope.addTinyMce = function (cell) {
+ var rte = $scope.getEditor("rte");
+ $scope.addControl(rte, cell);
+ };
+
+ $scope.getEditor = function (alias) {
+ return _.find($scope.availableEditors, function (editor) { return editor.alias === alias; });
+ };
+
+ $scope.removeControl = function (cell, $index) {
+ $scope.currentControl = null;
+ cell.controls.splice($index, 1);
+ };
+
+ $scope.percentage = function (spans) {
+ return ((spans / $scope.model.config.items.columns) * 100).toFixed(8);
+ };
+
+
+ $scope.clearPrompt = function (scopedObject, e) {
+ scopedObject.deletePrompt = false;
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ $scope.togglePrompt = function (scopedObject) {
+ scopedObject.deletePrompt = !scopedObject.deletePrompt;
+ };
+
+ $scope.hidePrompt = function (scopedObject) {
+ scopedObject.deletePrompt = false;
+ };
+
+ $scope.toggleAddRow = function() {
+ $scope.showRowConfigurations = !$scope.showRowConfigurations;
+ };
+
+
+ // *********************************************
+ // Initialization
+ // these methods are called from ng-init on the template
+ // so we can controll their first load data
+ //
+ // intialization sets non-saved data like percentage sizing, allowed editors and
+ // other data that should all be pre-fixed with $ to strip it out on save
+ // *********************************************
+
+ // *********************************************
+ // Init template + sections
+ // *********************************************
+ $scope.initContent = function () {
+ var clear = true;
+
+ //settings indicator shortcut
+ if ( ($scope.model.config.items.config && $scope.model.config.items.config.length > 0) || ($scope.model.config.items.styles && $scope.model.config.items.styles.length > 0)) {
+ $scope.hasSettings = true;
+ }
+
+ //ensure the grid has a column value set,
+ //if nothing is found, set it to 12
+ if ($scope.model.config.items.columns && angular.isString($scope.model.config.items.columns)) {
+ $scope.model.config.items.columns = parseInt($scope.model.config.items.columns);
+ } else {
+ $scope.model.config.items.columns = 12;
+ }
+
+ if ($scope.model.value && $scope.model.value.sections && $scope.model.value.sections.length > 0 && $scope.model.value.sections[0].rows && $scope.model.value.sections[0].rows.length > 0) {
+
+ if ($scope.model.value.name && angular.isArray($scope.model.config.items.templates)) {
+
+ //This will occur if it is an existing value, in which case
+ // we need to determine which layout was applied by looking up
+ // the name
+ // TODO: We need to change this to an immutable ID!!
+
+ var found = _.find($scope.model.config.items.templates, function (t) {
+ return t.name === $scope.model.value.name;
+ });
+
+ if (found && angular.isArray(found.sections) && found.sections.length === $scope.model.value.sections.length) {
+
+ //Cool, we've found the template associated with our current value with matching sections counts, now we need to
+ // merge this template data on to our current value (as if it was new) so that we can preserve what is and isn't
+ // allowed for this template based on the current config.
+
+ _.each(found.sections, function (templateSection, index) {
+ angular.extend($scope.model.value.sections[index], angular.copy(templateSection));
+ });
+
+ }
+ }
+
+ _.forEach($scope.model.value.sections, function (section, index) {
+
+ if (section.grid > 0) {
+ $scope.initSection(section);
+
+ //we do this to ensure that the grid can be reset by deleting the last row
+ if (section.rows.length > 0) {
+ clear = false;
+ }
+ } else {
+ $scope.model.value.sections.splice(index, 1);
+ }
+ });
+ } else if ($scope.model.config.items.templates && $scope.model.config.items.templates.length === 1) {
+ $scope.addTemplate($scope.model.config.items.templates[0]);
+ clear = false;
+ }
+
+ if (clear) {
+ $scope.model.value = undefined;
+ }
+ };
+
+ $scope.initSection = function (section) {
+ section.$percentage = $scope.percentage(section.grid);
+
+ section.$allowedLayouts = getAllowedLayouts(section);
+
+ if (!section.rows || section.rows.length === 0) {
+ section.rows = [];
+ if(section.$allowedLayouts.length === 1){
+ $scope.addRow(section, section.$allowedLayouts[0]);
+ }
+ } else {
+ _.forEach(section.rows, function (row, index) {
+ if (!row.$initialized) {
+ var initd = $scope.initRow(row);
+
+ //if init fails, remove
+ if (!initd) {
+ section.rows.splice(index, 1);
+ } else {
+ section.rows[index] = initd;
+ }
+ }
+ });
+
+ // if there is more than one row added - hide row add tools
+ $scope.showRowConfigurations = false;
+ }
+ };
+
+
+ // *********************************************
+ // Init layout / row
+ // *********************************************
+ $scope.initRow = function (row) {
+
+ //merge the layout data with the original config data
+ //if there are no config info on this, splice it out
+ var original = _.find($scope.model.config.items.layouts, function (o) { return o.name === row.name; });
+
+ if (!original) {
+ return null;
+ } else {
+ //make a copy to not touch the original config
+ original = angular.copy(original);
+ original.styles = row.styles;
+ original.config = row.config;
+ original.hasConfig = gridItemHasConfig(row.styles, row.config);
+
+
+ //sync area configuration
+ _.each(original.areas, function (area, areaIndex) {
+
+
+ if (area.grid > 0) {
+ var currentArea = row.areas[areaIndex];
+
+ if (currentArea) {
+ area.config = currentArea.config;
+ area.styles = currentArea.styles;
+ area.hasConfig = gridItemHasConfig(currentArea.styles, currentArea.config);
+ }
+
+ //set editor permissions
+ if (!area.allowed || area.allowAll === true) {
+ area.$allowedEditors = $scope.availableEditors;
+ area.$allowsRTE = true;
+ } else {
+ area.$allowedEditors = _.filter($scope.availableEditors, function (editor) {
+ return _.indexOf(area.allowed, editor.alias) >= 0;
+ });
+
+ if (_.indexOf(area.allowed, "rte") >= 0) {
+ area.$allowsRTE = true;
+ }
+ }
+
+ //copy over existing controls into the new areas
+ if (row.areas.length > areaIndex && row.areas[areaIndex].controls) {
+ area.controls = currentArea.controls;
+
+ _.forEach(area.controls, function (control, controlIndex) {
+ $scope.initControl(control, controlIndex);
+ });
+
+ } else {
+ //if empty
+ area.controls = [];
+
+ //if only one allowed editor
+ if(area.$allowedEditors.length === 1){
+ $scope.addControl(area.$allowedEditors[0], area, 0, false);
+ }
+ }
+
+ //set width
+ area.$percentage = $scope.percentage(area.grid);
+ area.$uniqueId = $scope.setUniqueId();
+
+ } else {
+ original.areas.splice(areaIndex, 1);
+ }
+ });
+
+ //replace the old row
+ original.$initialized = true;
+
+ //set a disposable unique ID
+ original.$uniqueId = $scope.setUniqueId();
+
+ //set a no disposable unique ID (util for row styling)
+ original.id = !row.id ? $scope.setUniqueId() : row.id;
+
+ return original;
+ }
+
+ };
+
+
+ // *********************************************
+ // Init control
+ // *********************************************
+
+ $scope.initControl = function (control, index) {
+ control.$index = index;
+ control.$uniqueId = $scope.setUniqueId();
+
+ //error handling in case of missing editor..
+ //should only happen if stripped earlier
+ if (!control.editor) {
+ control.$editorPath = "views/propertyeditors/grid/editors/error.html";
+ }
+
+ if (!control.$editorPath) {
+ var editorConfig = $scope.getEditor(control.editor.alias);
+
+ if (editorConfig) {
+ control.editor = editorConfig;
+
+ //if its an absolute path
+ if (control.editor.view.startsWith("/") || control.editor.view.startsWith("~/")) {
+ control.$editorPath = umbRequestHelper.convertVirtualToAbsolutePath(control.editor.view);
+ }
+ else {
+ //use convention
+ control.$editorPath = "views/propertyeditors/grid/editors/" + control.editor.view + ".html";
+ }
+ }
+ else {
+ control.$editorPath = "views/propertyeditors/grid/editors/error.html";
+ }
+ }
+
+
+ };
+
+
+ gridService.getGridEditors().then(function (response) {
+ $scope.availableEditors = response.data;
+
+ $scope.contentReady = true;
+
+ // *********************************************
+ // Init grid
+ // *********************************************
+ $scope.initContent();
+
+ });
+
+ //Clean the grid value before submitting to the server, we don't need
+ // all of that grid configuration in the value to be stored!! All of that
+ // needs to be merged in at runtime to ensure that the real config values are used
+ // if they are ever updated.
+
+ var unsubscribe = $scope.$on("formSubmitting", function () {
+
+ if ($scope.model.value && $scope.model.value.sections) {
+ _.each($scope.model.value.sections, function(section) {
+ if (section.rows) {
+ _.each(section.rows, function (row) {
+ if (row.areas) {
+ _.each(row.areas, function (area) {
+
+ //Remove the 'editors' - these are the allowed editors, these will
+ // be injected at runtime to this editor, it should not be persisted
+
+ if (area.editors) {
+ delete area.editors;
+ }
+
+ if (area.controls) {
+ _.each(area.controls, function (control) {
+ if (control.editor) {
+ //replace
+ var alias = control.editor.alias;
+ control.editor = {
+ alias: alias
+ };
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+ }
+ });
+
+ //when the scope is destroyed we need to unsubscribe
+ $scope.$on("$destroy", function () {
+ unsubscribe();
+ });
+
+ });
+
+angular.module("umbraco")
+ .controller("Umbraco.PropertyEditors.GridPrevalueEditorController",
+ function ($scope, $http, assetsService, $rootScope, dialogService, mediaResource, gridService, imageHelper, $timeout) {
+
+ var emptyModel = {
+ styles:[
+ {
+ label: "Set a background image",
+ description: "Set a row background",
+ key: "background-image",
+ view: "imagepicker",
+ modifier: "url({0})"
+ }
+ ],
+
+ config:[
+ {
+ label: "Class",
+ description: "Set a css class",
+ key: "class",
+ view: "textstring"
+ }
+ ],
+
+ columns: 12,
+ templates:[
+ {
+ name: "1 column layout",
+ sections: [
+ {
+ grid: 12,
+ }
+ ]
+ },
+ {
+ name: "2 column layout",
+ sections: [
+ {
+ grid: 4,
+ },
+ {
+ grid: 8
+ }
+ ]
+ }
+ ],
+
+
+ layouts:[
+ {
+ name: "Headline",
+ areas: [
+ {
+ grid: 12,
+ editors: ["headline"]
+ }
+ ]
+ },
+ {
+ name: "Article",
+ areas: [
+ {
+ grid: 4
+ },
+ {
+ grid: 8
+ }
+ ]
+ }
+ ]
+ };
+
+ /****************
+ template
+ *****************/
+
+ $scope.configureTemplate = function(template) {
+
+ var templatesCopy = angular.copy($scope.model.value.templates);
+
+ if (template === undefined) {
+ template = {
+ name: "",
+ sections: [
+
+ ]
+ };
+ $scope.model.value.templates.push(template);
+ }
+
+ $scope.layoutConfigOverlay = {};
+ $scope.layoutConfigOverlay.view = "views/propertyEditors/grid/dialogs/layoutconfig.html";
+ $scope.layoutConfigOverlay.currentLayout = template;
+ $scope.layoutConfigOverlay.rows = $scope.model.value.layouts;
+ $scope.layoutConfigOverlay.columns = $scope.model.value.columns;
+ $scope.layoutConfigOverlay.show = true;
+
+ $scope.layoutConfigOverlay.submit = function(model) {
+ $scope.layoutConfigOverlay.show = false;
+ $scope.layoutConfigOverlay = null;
+ };
+
+ $scope.layoutConfigOverlay.close = function(oldModel) {
+
+ //reset templates
+ $scope.model.value.templates = templatesCopy;
+
+ $scope.layoutConfigOverlay.show = false;
+ $scope.layoutConfigOverlay = null;
+ }
+
+ };
+
+ $scope.deleteTemplate = function(index){
+ $scope.model.value.templates.splice(index, 1);
+ };
+
+
+ /****************
+ Row
+ *****************/
+
+ $scope.configureLayout = function(layout) {
+
+ var layoutsCopy = angular.copy($scope.model.value.layouts);
+
+ if(layout === undefined){
+ layout = {
+ name: "",
+ areas:[
+
+ ]
+ };
+ $scope.model.value.layouts.push(layout);
+ }
+
+ $scope.rowConfigOverlay = {};
+ $scope.rowConfigOverlay.view = "views/propertyEditors/grid/dialogs/rowconfig.html";
+ $scope.rowConfigOverlay.currentRow = layout;
+ $scope.rowConfigOverlay.editors = $scope.editors;
+ $scope.rowConfigOverlay.columns = $scope.model.value.columns;
+ $scope.rowConfigOverlay.show = true;
+
+ $scope.rowConfigOverlay.submit = function(model) {
+ $scope.rowConfigOverlay.show = false;
+ $scope.rowConfigOverlay = null;
+ };
+
+ $scope.rowConfigOverlay.close = function(oldModel) {
+ $scope.model.value.layouts = layoutsCopy;
+ $scope.rowConfigOverlay.show = false;
+ $scope.rowConfigOverlay = null;
+ };
+
+ };
+
+ //var rowDeletesPending = false;
+ $scope.deleteLayout = function(index) {
+
+ $scope.rowDeleteOverlay = {};
+ $scope.rowDeleteOverlay.view = "views/propertyEditors/grid/dialogs/rowdeleteconfirm.html";
+ $scope.rowDeleteOverlay.dialogData = {
+ rowName: $scope.model.value.layouts[index].name
+ };
+ $scope.rowDeleteOverlay.show = true;
+
+ $scope.rowDeleteOverlay.submit = function(model) {
+
+ $scope.model.value.layouts.splice(index, 1);
+
+ $scope.rowDeleteOverlay.show = false;
+ $scope.rowDeleteOverlay = null;
+ };
+
+ $scope.rowDeleteOverlay.close = function(oldModel) {
+ $scope.rowDeleteOverlay.show = false;
+ $scope.rowDeleteOverlay = null;
+ };
+
+ };
+
+
+ /****************
+ utillities
+ *****************/
+ $scope.toggleCollection = function(collection, toggle){
+ if(toggle){
+ collection = [];
+ }else{
+ delete collection;
+ }
+ };
+
+ $scope.percentage = function(spans){
+ return ((spans / $scope.model.value.columns) * 100).toFixed(8);
+ };
+
+ $scope.zeroWidthFilter = function (cell) {
+ return cell.grid > 0;
+ };
+
+ /****************
+ Config
+ *****************/
+
+ $scope.removeConfigValue = function(collection, index){
+ collection.splice(index, 1);
+ };
+
+ var editConfigCollection = function(configValues, title, callback) {
+
+ $scope.editConfigCollectionOverlay = {};
+ $scope.editConfigCollectionOverlay.view = "views/propertyeditors/grid/dialogs/editconfig.html";
+ $scope.editConfigCollectionOverlay.config = configValues;
+ $scope.editConfigCollectionOverlay.title = title;
+ $scope.editConfigCollectionOverlay.show = true;
+
+ $scope.editConfigCollectionOverlay.submit = function(model) {
+
+ callback(model.config)
+
+ $scope.editConfigCollectionOverlay.show = false;
+ $scope.editConfigCollectionOverlay = null;
+ };
+
+ $scope.editConfigCollectionOverlay.close = function(oldModel) {
+ $scope.editConfigCollectionOverlay.show = false;
+ $scope.editConfigCollectionOverlay = null;
+ };
+
+ };
+
+ $scope.editConfig = function() {
+ editConfigCollection($scope.model.value.config, "Settings", function(data) {
+ $scope.model.value.config = data;
+ });
+ };
+
+ $scope.editStyles = function() {
+ editConfigCollection($scope.model.value.styles, "Styling", function(data){
+ $scope.model.value.styles = data;
+ });
+ };
+
+ /****************
+ editors
+ *****************/
+ gridService.getGridEditors().then(function(response){
+ $scope.editors = response.data;
+ });
+
+
+ /* init grid data */
+ if (!$scope.model.value || $scope.model.value === "" || !$scope.model.value.templates) {
+ $scope.model.value = emptyModel;
+ } else {
+
+ if (!$scope.model.value.columns) {
+ $scope.model.value.columns = emptyModel.columns;
+ }
+
+
+ if (!$scope.model.value.config) {
+ $scope.model.value.config = [];
+ }
+
+ if (!$scope.model.value.styles) {
+ $scope.model.value.styles = [];
+ }
+ }
+
+ /****************
+ Clean up
+ *****************/
+ var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+ var ts = $scope.model.value.templates;
+ var ls = $scope.model.value.layouts;
+
+ _.each(ts, function(t){
+ _.each(t.sections, function(section, index){
+ if(section.grid === 0){
+ t.sections.splice(index, 1);
+ }
+ });
+ });
+
+ _.each(ls, function(l){
+ _.each(l.areas, function(area, index){
+ if(area.grid === 0){
+ l.areas.splice(index, 1);
+ }
+ });
+ });
+ });
+
+ //when the scope is destroyed we need to unsubscribe
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+ });
+
+//this controller simply tells the dialogs service to open a mediaPicker window
+//with a specified callback, this callback will receive an object with a selection on it
+angular.module('umbraco')
+ .controller("Umbraco.PropertyEditors.ImageCropperController",
+ function ($rootScope, $routeParams, $scope, $log, mediaHelper, cropperHelper, $timeout, editorState, umbRequestHelper, fileManager, angularHelper) {
+
+ var config = angular.copy($scope.model.config);
+ $scope.imageIsLoaded = false;
+
+ //move previously saved value to the editor
+ if ($scope.model.value) {
+ //backwards compat with the old file upload (incase some-one swaps them..)
+ if (angular.isString($scope.model.value)) {
+ config.src = $scope.model.value;
+ $scope.model.value = config;
+ } else if ($scope.model.value.crops) {
+ //sync any config changes with the editor and drop outdated crops
+ _.each($scope.model.value.crops, function (saved) {
+ var configured = _.find(config.crops, function (item) { return item.alias === saved.alias });
+
+ if (configured && configured.height === saved.height && configured.width === saved.width) {
+ configured.coordinates = saved.coordinates;
+ }
+ });
+ $scope.model.value.crops = config.crops;
+
+ //restore focalpoint if missing
+ if (!$scope.model.value.focalPoint) {
+ $scope.model.value.focalPoint = { left: 0.5, top: 0.5 };
+ }
+ }
+
+ $scope.imageSrc = $scope.model.value.src;
+ }
+
+
+ //crop a specific crop
+ $scope.crop = function (crop) {
+ $scope.currentCrop = crop;
+ $scope.currentPoint = undefined;
+ };
+
+ //done cropping
+ $scope.done = function () {
+ $scope.currentCrop = undefined;
+ $scope.currentPoint = undefined;
+ };
+
+ //crop a specific crop
+ $scope.clear = function (crop) {
+ //clear current uploaded files
+ fileManager.setFiles($scope.model.alias, []);
+
+ //clear the ui
+ $scope.imageSrc = undefined;
+ if ($scope.model.value) {
+ delete $scope.model.value;
+ }
+
+ // set form to dirty to tricker discard changes dialog
+ var currForm = angularHelper.getCurrentForm($scope);
+ currForm.$setDirty();
+ };
+
+ //show previews
+ $scope.togglePreviews = function () {
+ if ($scope.showPreviews) {
+ $scope.showPreviews = false;
+ $scope.tempShowPreviews = false;
+ } else {
+ $scope.showPreviews = true;
+ }
+ };
+
+ $scope.imageLoaded = function() {
+ $scope.imageIsLoaded = true;
+ };
+
+ //on image selected, update the cropper
+ $scope.$on("filesSelected", function (ev, args) {
+ $scope.model.value = config;
+
+ if (args.files && args.files[0]) {
+
+ fileManager.setFiles($scope.model.alias, args.files);
+
+ var reader = new FileReader();
+ reader.onload = function (e) {
+
+ $scope.$apply(function () {
+ $scope.imageSrc = e.target.result;
+ });
+
+ };
+
+ reader.readAsDataURL(args.files[0]);
+ }
+ });
+
+
+ //here we declare a special method which will be called whenever the value has changed from the server
+ $scope.model.onValueChanged = function (newVal, oldVal) {
+ //clear current uploaded files
+ fileManager.setFiles($scope.model.alias, []);
+ };
+
+ var unsubscribe = $scope.$on("formSubmitting", function () {
+ $scope.done();
+ });
+
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+ })
+ .run(function (mediaHelper, umbRequestHelper) {
+ if (mediaHelper && mediaHelper.registerFileResolver) {
+
+ //NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
+ // they contain different data structures so if we need to query against it we need to be aware of this.
+ mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
+ if (property.value.src) {
+
+ if (thumbnail === true) {
+ return property.value.src + "?width=500&mode=max&animationprocessmode=first";
+ }
+ else {
+ return property.value.src;
+ }
+
+ //this is a fallback in case the cropper has been asssigned a upload field
+ }
+ else if (angular.isString(property.value)) {
+ if (thumbnail) {
+
+ if (mediaHelper.detectIfImageByExtension(property.value)) {
+
+ var thumbnailUrl = umbRequestHelper.getApiUrl(
+ "imagesApiBaseUrl",
+ "GetBigThumbnail",
+ [{ originalImagePath: property.value }]);
+
+ return thumbnailUrl;
+ }
+ else {
+ return null;
+ }
+
+ }
+ else {
+ return property.value;
+ }
+ }
+
+ return null;
+ });
+ }
+ });
+
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.CropSizesController",
+ function ($scope, $timeout) {
+
+ if (!$scope.model.value) {
+ $scope.model.value = [];
+ }
+
+ $scope.remove = function (item, evt) {
+ evt.preventDefault();
+ $scope.model.value = _.reject($scope.model.value, function (x) {
+ return x.alias === item.alias;
+ });
+ };
+
+ $scope.edit = function (item, evt) {
+ evt.preventDefault();
+ $scope.newItem = item;
+ };
+
+ $scope.cancel = function (evt) {
+ evt.preventDefault();
+ $scope.newItem = null;
+ };
+
+ $scope.add = function (evt) {
+ evt.preventDefault();
+
+ if ($scope.newItem && $scope.newItem.alias &&
+ angular.isNumber($scope.newItem.width) && angular.isNumber($scope.newItem.height) &&
+ $scope.newItem.width > 0 && $scope.newItem.height > 0) {
+
+ var exists = _.find($scope.model.value, function (item) { return $scope.newItem.alias === item.alias; });
+ if (!exists) {
+ $scope.model.value.push($scope.newItem);
+ $scope.newItem = {};
+ $scope.hasError = false;
+ return;
+ }
+ }
+
+ //there was an error, do the highlight (will be set back by the directive)
+ $scope.hasError = true;
+ };
});
-function includePropsPreValsController($rootScope, $scope, localizationService, contentTypeResource) {
-
- if (!$scope.model.value) {
- $scope.model.value = [];
- }
-
- $scope.propertyAliases = [];
- $scope.selectedField = null;
- $scope.systemFields = [
- { value: "sortOrder" },
- { value: "updateDate" },
- { value: "updater" },
- { value: "createDate" },
- { value: "owner" },
- { value: "published"},
- { value: "contentTypeAlias" },
- { value: "email" },
- { value: "username" }
- ];
-
- $scope.getLocalizedKey = function(alias) {
- switch (alias) {
- case "name":
- return "general_name";
- case "sortOrder":
- return "general_sort";
- case "updateDate":
- return "content_updateDate";
- case "updater":
- return "content_updatedBy";
- case "createDate":
- return "content_createDate";
- case "owner":
- return "content_createBy";
- case "published":
- return "content_isPublished";
- case "contentTypeAlias":
- //NOTE: This will just be 'Document' type even if it's for media/members since this is just a pre-val editor and we don't have a key for 'Content Type Alias'
- return "content_documentType";
- case "email":
- return "general_email";
- case "username":
- return "general_username";
- }
- return alias;
- }
-
- $scope.removeField = function(e) {
- $scope.model.value = _.reject($scope.model.value, function (x) {
- return x.alias === e.alias;
- });
- }
-
- //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared
- _.each($scope.systemFields, function (e, i) {
- var key = $scope.getLocalizedKey(e.value);
- localizationService.localize(key).then(function (v) {
- e.name = v;
-
- switch (e.value) {
- case "updater":
- e.name += " (Content only)";
- break;
- case "published":
- e.name += " (Content only)";
- break;
- case "email":
- e.name += " (Members only)";
- break;
- case "username":
- e.name += " (Members only)";
- break;
- }
-
- });
- });
-
- // Return a helper with preserved width of cells
- var fixHelper = function (e, ui) {
- var h = ui.clone();
-
- h.children().each(function () {
- $(this).width($(this).width());
- });
- h.css("background-color", "lightgray");
-
- return h;
- };
-
- $scope.sortableOptions = {
- helper: fixHelper,
- handle: ".handle",
- opacity: 0.5,
- axis: 'y',
- containment: 'parent',
- cursor: 'move',
- items: '> tr',
- tolerance: 'pointer',
- update: function (e, ui) {
-
- // Get the new and old index for the moved element (using the text as the identifier)
- var newIndex = ui.item.index();
- var movedAlias = $('.alias-value', ui.item).text().trim();
- var originalIndex = getAliasIndexByText(movedAlias);
-
- // Move the element in the model
- if (originalIndex > -1) {
- var movedElement = $scope.model.value[originalIndex];
- $scope.model.value.splice(originalIndex, 1);
- $scope.model.value.splice(newIndex, 0, movedElement);
- }
- }
- };
-
- contentTypeResource.getAllPropertyTypeAliases().then(function(data) {
- $scope.propertyAliases = data;
- });
-
- $scope.addField = function () {
-
- var val = $scope.selectedField;
- var isSystem = val.startsWith("_system_");
- if (isSystem) {
- val = val.trimStart("_system_");
- }
-
- var exists = _.find($scope.model.value, function (i) {
- return i.alias === val;
- });
- if (!exists) {
- $scope.model.value.push({
- alias: val,
- isSystem: isSystem ? 1 : 0
- });
- }
- }
-
- function getAliasIndexByText(value) {
- for (var i = 0; i < $scope.model.value.length; i++) {
- if ($scope.model.value[i].alias === value) {
- return i;
- }
- }
-
- return -1;
- }
-
-}
-
-
+function includePropsPreValsController($rootScope, $scope, localizationService, contentTypeResource) {
+
+ if (!$scope.model.value) {
+ $scope.model.value = [];
+ }
+
+ $scope.propertyAliases = [];
+ $scope.selectedField = null;
+ $scope.systemFields = [
+ { value: "sortOrder" },
+ { value: "updateDate" },
+ { value: "updater" },
+ { value: "createDate" },
+ { value: "owner" },
+ { value: "published"},
+ { value: "contentTypeAlias" },
+ { value: "email" },
+ { value: "username" }
+ ];
+
+ $scope.getLocalizedKey = function(alias) {
+ switch (alias) {
+ case "name":
+ return "general_name";
+ case "sortOrder":
+ return "general_sort";
+ case "updateDate":
+ return "content_updateDate";
+ case "updater":
+ return "content_updatedBy";
+ case "createDate":
+ return "content_createDate";
+ case "owner":
+ return "content_createBy";
+ case "published":
+ return "content_isPublished";
+ case "contentTypeAlias":
+ //NOTE: This will just be 'Document' type even if it's for media/members since this is just a pre-val editor and we don't have a key for 'Content Type Alias'
+ return "content_documentType";
+ case "email":
+ return "general_email";
+ case "username":
+ return "general_username";
+ }
+ return alias;
+ }
+
+ $scope.removeField = function(e) {
+ $scope.model.value = _.reject($scope.model.value, function (x) {
+ return x.alias === e.alias;
+ });
+ }
+
+ //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared
+ _.each($scope.systemFields, function (e, i) {
+ var key = $scope.getLocalizedKey(e.value);
+ localizationService.localize(key).then(function (v) {
+ e.name = v;
+
+ switch (e.value) {
+ case "updater":
+ e.name += " (Content only)";
+ break;
+ case "published":
+ e.name += " (Content only)";
+ break;
+ case "email":
+ e.name += " (Members only)";
+ break;
+ case "username":
+ e.name += " (Members only)";
+ break;
+ }
+
+ });
+ });
+
+ // Return a helper with preserved width of cells
+ var fixHelper = function (e, ui) {
+ var h = ui.clone();
+
+ h.children().each(function () {
+ $(this).width($(this).width());
+ });
+ h.css("background-color", "lightgray");
+
+ return h;
+ };
+
+ $scope.sortableOptions = {
+ helper: fixHelper,
+ handle: ".handle",
+ opacity: 0.5,
+ axis: 'y',
+ containment: 'parent',
+ cursor: 'move',
+ items: '> tr',
+ tolerance: 'pointer',
+ update: function (e, ui) {
+
+ // Get the new and old index for the moved element (using the text as the identifier)
+ var newIndex = ui.item.index();
+ var movedAlias = $('.alias-value', ui.item).text().trim();
+ var originalIndex = getAliasIndexByText(movedAlias);
+
+ // Move the element in the model
+ if (originalIndex > -1) {
+ var movedElement = $scope.model.value[originalIndex];
+ $scope.model.value.splice(originalIndex, 1);
+ $scope.model.value.splice(newIndex, 0, movedElement);
+ }
+ }
+ };
+
+ contentTypeResource.getAllPropertyTypeAliases().then(function(data) {
+ $scope.propertyAliases = data;
+ });
+
+ $scope.addField = function () {
+
+ var val = $scope.selectedField;
+ var isSystem = val.startsWith("_system_");
+ if (isSystem) {
+ val = val.trimStart("_system_");
+ }
+
+ var exists = _.find($scope.model.value, function (i) {
+ return i.alias === val;
+ });
+ if (!exists) {
+ $scope.model.value.push({
+ alias: val,
+ isSystem: isSystem ? 1 : 0
+ });
+ }
+ }
+
+ function getAliasIndexByText(value) {
+ for (var i = 0; i < $scope.model.value.length; i++) {
+ if ($scope.model.value[i].alias === value) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+}
+
+
angular.module("umbraco").controller("Umbraco.PrevalueEditors.IncludePropertiesListViewController", includePropsPreValsController);
-/**
- * @ngdoc controller
- * @name Umbraco.PrevalueEditors.ListViewLayoutsPreValsController
- * @function
- *
- * @description
- * The controller for configuring layouts for list views
- */
-(function() {
- "use strict";
-
- function ListViewLayoutsPreValsController($scope) {
-
- var vm = this;
- vm.focusLayoutName = false;
-
- vm.layoutsSortableOptions = {
- distance: 10,
- tolerance: "pointer",
- opacity: 0.7,
- scroll: true,
- cursor: "move",
- handle: ".list-view-layout__sort-handle"
- };
-
- vm.addLayout = addLayout;
- vm.showPrompt = showPrompt;
- vm.hidePrompt = hidePrompt;
- vm.removeLayout = removeLayout;
- vm.openIconPicker = openIconPicker;
-
- function activate() {
-
-
-
- }
-
- function addLayout() {
-
- vm.focusLayoutName = false;
-
- var layout = {
- "name": "",
- "path": "",
- "icon": "icon-stop",
- "selected": true
- };
-
- $scope.model.value.push(layout);
-
- }
-
- function showPrompt(layout) {
- layout.deletePrompt = true;
- }
-
- function hidePrompt(layout) {
- layout.deletePrompt = false;
- }
-
- function removeLayout($index, layout) {
- $scope.model.value.splice($index, 1);
- }
-
- function openIconPicker(layout) {
- vm.iconPickerDialog = {
- view: "iconpicker",
- show: true,
- submit: function(model) {
- if (model.color) {
- layout.icon = model.icon + " " + model.color;
- } else {
- layout.icon = model.icon;
- }
- vm.focusLayoutName = true;
- vm.iconPickerDialog.show = false;
- vm.iconPickerDialog = null;
- }
- };
- }
-
- activate();
-
- }
-
- angular.module("umbraco").controller("Umbraco.PrevalueEditors.ListViewLayoutsPreValsController", ListViewLayoutsPreValsController);
-
-})();
+/**
+ * @ngdoc controller
+ * @name Umbraco.PrevalueEditors.ListViewLayoutsPreValsController
+ * @function
+ *
+ * @description
+ * The controller for configuring layouts for list views
+ */
+(function() {
+ "use strict";
-/**
- * @ngdoc controller
- * @name Umbraco.Editors.DocumentType.EditController
- * @function
- *
- * @description
- * The controller for the content type editor
- */
-(function() {
- "use strict";
-
- function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper) {
-
- var vm = this;
-
- vm.nodeId = $scope.contentId;
- //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
- //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
- vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!.");
-
- vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
- vm.activeDrag = false;
- vm.mediaDetailsTooltip = {};
- vm.itemsWithoutFolders = [];
- vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
-
- vm.dragEnter = dragEnter;
- vm.dragLeave = dragLeave;
- vm.onFilesQueue = onFilesQueue;
- vm.onUploadComplete = onUploadComplete;
-
- vm.hoverMediaItemDetails = hoverMediaItemDetails;
- vm.selectContentItem = selectContentItem;
- vm.selectItem = selectItem;
- vm.selectFolder = selectFolder;
- vm.goToItem = goToItem;
-
- function activate() {
- vm.itemsWithoutFolders = filterOutFolders($scope.items);
- }
-
- function filterOutFolders(items) {
-
- var newArray = [];
-
- if(items && items.length ) {
-
- for (var i = 0; items.length > i; i++) {
- var item = items[i];
- var isFolder = !mediaHelper.hasFilePropertyType(item);
-
- if (!isFolder) {
- newArray.push(item);
- }
- }
-
- }
-
- return newArray;
- }
-
- function dragEnter(el, event) {
- vm.activeDrag = true;
- }
-
- function dragLeave(el, event) {
- vm.activeDrag = false;
- }
-
- function onFilesQueue() {
- vm.activeDrag = false;
- }
-
- function onUploadComplete() {
- $scope.getContent($scope.contentId);
- }
-
- function hoverMediaItemDetails(item, event, hover) {
-
- if (hover && !vm.mediaDetailsTooltip.show) {
-
- vm.mediaDetailsTooltip.event = event;
- vm.mediaDetailsTooltip.item = item;
- vm.mediaDetailsTooltip.show = true;
-
- } else if (!hover && vm.mediaDetailsTooltip.show) {
-
- vm.mediaDetailsTooltip.show = false;
-
- }
-
- }
-
- function selectContentItem(item, $event, $index) {
- listViewHelper.selectHandler(item, $index, $scope.items, $scope.selection, $event);
- }
-
- function selectItem(item, $event, $index) {
- listViewHelper.selectHandler(item, $index, vm.itemsWithoutFolders, $scope.selection, $event);
- }
-
- function selectFolder(folder, $event, $index) {
- listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event);
- }
-
- function goToItem(item, $event, $index) {
- $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
- }
-
- activate();
-
- }
-
- angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.GridLayoutController", ListViewGridLayoutController);
-
-})();
+ function ListViewLayoutsPreValsController($scope) {
-(function() {
- "use strict";
-
- function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) {
-
- var vm = this;
-
- vm.nodeId = $scope.contentId;
- //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
- //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
- vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!.");
- vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
- vm.activeDrag = false;
- vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
-
- vm.selectItem = selectItem;
- vm.clickItem = clickItem;
- vm.selectAll = selectAll;
- vm.isSelectedAll = isSelectedAll;
- vm.isSortDirection = isSortDirection;
- vm.sort = sort;
- vm.dragEnter = dragEnter;
- vm.dragLeave = dragLeave;
- vm.onFilesQueue = onFilesQueue;
- vm.onUploadComplete = onUploadComplete;
-
- function selectAll($event) {
- listViewHelper.selectAllItems($scope.items, $scope.selection, $event);
- }
-
- function isSelectedAll() {
- return listViewHelper.isSelectedAll($scope.items, $scope.selection);
- }
-
- function selectItem(selectedItem, $index, $event) {
- listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event);
- }
-
- function clickItem(item) {
- $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
- }
-
- function isSortDirection(col, direction) {
- return listViewHelper.setSortingDirection(col, direction, $scope.options);
- }
-
- function sort(field, allow) {
- if (allow) {
- listViewHelper.setSorting(field, allow, $scope.options);
- $scope.getContent($scope.contentId);
- }
- }
-
- // Dropzone upload functions
- function dragEnter(el, event) {
- vm.activeDrag = true;
- }
-
- function dragLeave(el, event) {
- vm.activeDrag = false;
- }
-
- function onFilesQueue() {
- vm.activeDrag = false;
- }
-
- function onUploadComplete() {
- $scope.getContent($scope.contentId);
- }
-
- }
-
- angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController);
-
-})();
+ var vm = this;
+ vm.focusLayoutName = false;
-function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper) {
+ vm.layoutsSortableOptions = {
+ distance: 10,
+ tolerance: "pointer",
+ opacity: 0.7,
+ scroll: true,
+ cursor: "move",
+ handle: ".list-view-layout__sort-handle"
+ };
+
+ vm.addLayout = addLayout;
+ vm.showPrompt = showPrompt;
+ vm.hidePrompt = hidePrompt;
+ vm.removeLayout = removeLayout;
+ vm.openIconPicker = openIconPicker;
+
+ function activate() {
+
+
+
+ }
+
+ function addLayout() {
+
+ vm.focusLayoutName = false;
+
+ var layout = {
+ "name": "",
+ "path": "",
+ "icon": "icon-stop",
+ "selected": true
+ };
+
+ $scope.model.value.push(layout);
+
+ }
+
+ function showPrompt(layout) {
+ layout.deletePrompt = true;
+ }
+
+ function hidePrompt(layout) {
+ layout.deletePrompt = false;
+ }
+
+ function removeLayout($index, layout) {
+ $scope.model.value.splice($index, 1);
+ }
+
+ function openIconPicker(layout) {
+ vm.iconPickerDialog = {
+ view: "iconpicker",
+ show: true,
+ submit: function(model) {
+ if (model.color) {
+ layout.icon = model.icon + " " + model.color;
+ } else {
+ layout.icon = model.icon;
+ }
+ vm.focusLayoutName = true;
+ vm.iconPickerDialog.show = false;
+ vm.iconPickerDialog = null;
+ }
+ };
+ }
+
+ activate();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.PrevalueEditors.ListViewLayoutsPreValsController", ListViewLayoutsPreValsController);
+
+})();
+
+/**
+ * @ngdoc controller
+ * @name Umbraco.Editors.DocumentType.EditController
+ * @function
+ *
+ * @description
+ * The controller for the content type editor
+ */
+(function() {
+ "use strict";
+
+ function ListViewGridLayoutController($scope, $routeParams, mediaHelper, mediaResource, $location, listViewHelper) {
+
+ var vm = this;
+
+ vm.nodeId = $scope.contentId;
+ //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
+ //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
+ vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/\./g, "!.");
+
+ vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
+ vm.activeDrag = false;
+ vm.mediaDetailsTooltip = {};
+ vm.itemsWithoutFolders = [];
+ vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
+
+ vm.dragEnter = dragEnter;
+ vm.dragLeave = dragLeave;
+ vm.onFilesQueue = onFilesQueue;
+ vm.onUploadComplete = onUploadComplete;
+
+ vm.hoverMediaItemDetails = hoverMediaItemDetails;
+ vm.selectContentItem = selectContentItem;
+ vm.selectItem = selectItem;
+ vm.selectFolder = selectFolder;
+ vm.goToItem = goToItem;
+
+ function activate() {
+ vm.itemsWithoutFolders = filterOutFolders($scope.items);
+ }
+
+ function filterOutFolders(items) {
+
+ var newArray = [];
+
+ if(items && items.length ) {
+
+ for (var i = 0; items.length > i; i++) {
+ var item = items[i];
+ var isFolder = !mediaHelper.hasFilePropertyType(item);
+
+ if (!isFolder) {
+ newArray.push(item);
+ }
+ }
+
+ }
+
+ return newArray;
+ }
+
+ function dragEnter(el, event) {
+ vm.activeDrag = true;
+ }
+
+ function dragLeave(el, event) {
+ vm.activeDrag = false;
+ }
+
+ function onFilesQueue() {
+ vm.activeDrag = false;
+ }
+
+ function onUploadComplete() {
+ $scope.getContent($scope.contentId);
+ }
+
+ function hoverMediaItemDetails(item, event, hover) {
+
+ if (hover && !vm.mediaDetailsTooltip.show) {
+
+ vm.mediaDetailsTooltip.event = event;
+ vm.mediaDetailsTooltip.item = item;
+ vm.mediaDetailsTooltip.show = true;
+
+ } else if (!hover && vm.mediaDetailsTooltip.show) {
+
+ vm.mediaDetailsTooltip.show = false;
+
+ }
+
+ }
+
+ function selectContentItem(item, $event, $index) {
+ listViewHelper.selectHandler(item, $index, $scope.items, $scope.selection, $event);
+ }
+
+ function selectItem(item, $event, $index) {
+ listViewHelper.selectHandler(item, $index, vm.itemsWithoutFolders, $scope.selection, $event);
+ }
+
+ function selectFolder(folder, $event, $index) {
+ listViewHelper.selectHandler(folder, $index, $scope.folders, $scope.selection, $event);
+ }
+
+ function goToItem(item, $event, $index) {
+ $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
+ }
+
+ activate();
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.GridLayoutController", ListViewGridLayoutController);
+
+})();
+
+(function() {
+ "use strict";
+
+ function ListViewListLayoutController($scope, listViewHelper, $location, mediaHelper) {
+
+ var vm = this;
+
+ vm.nodeId = $scope.contentId;
+ //vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes);
+ //instead of passing in a whitelist, we pass in a blacklist by adding ! to the ext
+ vm.acceptedFileTypes = mediaHelper.formatFileTypes(Umbraco.Sys.ServerVariables.umbracoSettings.disallowedUploadFiles).replace(/./g, "!.");
+ vm.maxFileSize = Umbraco.Sys.ServerVariables.umbracoSettings.maxFileSize + "KB";
+ vm.activeDrag = false;
+ vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20';
+
+ vm.selectItem = selectItem;
+ vm.clickItem = clickItem;
+ vm.selectAll = selectAll;
+ vm.isSelectedAll = isSelectedAll;
+ vm.isSortDirection = isSortDirection;
+ vm.sort = sort;
+ vm.dragEnter = dragEnter;
+ vm.dragLeave = dragLeave;
+ vm.onFilesQueue = onFilesQueue;
+ vm.onUploadComplete = onUploadComplete;
+
+ function selectAll($event) {
+ listViewHelper.selectAllItems($scope.items, $scope.selection, $event);
+ }
+
+ function isSelectedAll() {
+ return listViewHelper.isSelectedAll($scope.items, $scope.selection);
+ }
+
+ function selectItem(selectedItem, $index, $event) {
+ listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event);
+ }
+
+ function clickItem(item) {
+ $location.path($scope.entityType + '/' + $scope.entityType + '/edit/' + item.id);
+ }
+
+ function isSortDirection(col, direction) {
+ return listViewHelper.setSortingDirection(col, direction, $scope.options);
+ }
+
+ function sort(field, allow) {
+ if (allow) {
+ listViewHelper.setSorting(field, allow, $scope.options);
+ $scope.getContent($scope.contentId);
+ }
+ }
+
+ // Dropzone upload functions
+ function dragEnter(el, event) {
+ vm.activeDrag = true;
+ }
+
+ function dragLeave(el, event) {
+ vm.activeDrag = false;
+ }
+
+ function onFilesQueue() {
+ vm.activeDrag = false;
+ }
+
+ function onUploadComplete() {
+ $scope.getContent($scope.contentId);
+ }
+
+ }
+
+ angular.module("umbraco").controller("Umbraco.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController);
+
+})();
+
+function listViewController($rootScope, $scope, $routeParams, $injector, $cookieStore, notificationsService, iconHelper, dialogService, editorState, localizationService, $location, appState, $timeout, $q, mediaResource, listViewHelper, userService) {
//this is a quick check to see if we're in create mode, if so just exit - we cannot show children for content
// that isn't created yet, if we continue this will use the parent id in the route params which isn't what
@@ -12124,8 +12264,85 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
totalPages: 0,
items: []
};
+
+ $scope.currentNodePermissions = {}
+
+ //Just ensure we do have an editorState
+ if (editorState.current) {
+ //Fetch current node allowed actions for the current user
+ //This is the current node & not each individual child node in the list
+ var currentUserPermissions = editorState.current.allowedActions;
+
+ //Create a nicer model rather than the funky & hard to remember permissions strings
+ $scope.currentNodePermissions = {
+ "canCopy": _.contains(currentUserPermissions, 'O'), //Magic Char = O
+ "canCreate": _.contains(currentUserPermissions, 'C'), //Magic Char = C
+ "canDelete": _.contains(currentUserPermissions, 'D'), //Magic Char = D
+ "canMove": _.contains(currentUserPermissions, 'M'), //Magic Char = M
+ "canPublish": _.contains(currentUserPermissions, 'U'), //Magic Char = U
+ "canUnpublish": _.contains(currentUserPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
+ };
+ }
+
+ //when this is null, we don't check permissions
+ $scope.buttonPermissions = null;
+
+ //When we are dealing with 'content', we need to deal with permissions on child nodes.
+ // Currently there is no real good way to
+ if ($scope.entityType === "content") {
+
+ var idsWithPermissions = null;
+
+ $scope.buttonPermissions = {
+ canCopy: true,
+ canCreate: true,
+ canDelete: true,
+ canMove: true,
+ canPublish: true,
+ canUnpublish: true
+ };
+
+ $scope.$watch(function() {
+ return $scope.selection.length;
+ }, function(newVal, oldVal) {
+
+ if ((idsWithPermissions == null && newVal > 0) || (idsWithPermissions != null)) {
+
+ //get all of the selected ids
+ var ids = _.map($scope.selection, function(i) {
+ return i.id.toString();
+ });
+
+ //remove the dictionary items that don't have matching ids
+ var filtered = {};
+ _.each(idsWithPermissions, function (value, key, list) {
+ if (_.contains(ids, key)) {
+ filtered[key] = value;
+ }
+ });
+ idsWithPermissions = filtered;
+
+ //find all ids that we haven't looked up permissions for
+ var existingIds = _.keys(idsWithPermissions);
+ var missingLookup = _.map(_.difference(ids, existingIds), function (i) {
+ return Number(i);
+ });
+
+ if (missingLookup.length > 0) {
+ contentResource.getPermissions(missingLookup).then(function(p) {
+ $scope.buttonPermissions = listViewHelper.getButtonPermissions(p, idsWithPermissions);
+ });
+ }
+ else {
+ $scope.buttonPermissions = listViewHelper.getButtonPermissions({}, idsWithPermissions);
+ }
+ }
+ });
+
+ }
$scope.options = {
+ displayAtTabNumber: $scope.model.config.displayAtTabNumber ? $scope.model.config.displayAtTabNumber : 1,
pageSize: $scope.model.config.pageSize ? $scope.model.config.pageSize : 10,
pageNumber: ($routeParams.page && Number($routeParams.page) != NaN && Number($routeParams.page) > 0) ? $routeParams.page : 1,
filter: '',
@@ -12231,9 +12448,8 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
getListResultsCallback(id, $scope.options).then(function(data) {
$scope.actionInProgress = false;
-
$scope.listViewResultSet = data;
-
+
//update all values for display
if ($scope.listViewResultSet.items) {
_.each($scope.listViewResultSet.items, function(e, index) {
@@ -12557,183 +12773,183 @@ function listViewController($rootScope, $scope, $routeParams, $injector, $cookie
angular.module("umbraco").controller("Umbraco.PropertyEditors.ListViewController", listViewController);
-function sortByPreValsController($rootScope, $scope, localizationService) {
-
- $scope.sortByFields = [
- { value: "SortOrder", key: "general_sort" },
- { value: "Name", key: "general_name" },
- { value: "VersionDate", key: "content_updateDate" },
- { value: "Updater", key: "content_updatedBy" },
- { value: "CreateDate", key: "content_createDate" },
- { value: "Owner", key: "content_createBy" },
- { value: "ContentTypeAlias", key: "content_documentType" },
- { value: "Published", key: "content_isPublished" },
- { value: "Email", key: "general_email" },
- { value: "Username", key: "general_username" }
- ];
-
- //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared
- _.each($scope.sortByFields, function (e, i) {
- localizationService.localize(e.key).then(function (v) {
- e.name = v;
-
- switch (e.value) {
- case "Updater":
- e.name += " (Content only)";
- break;
- case "Published":
- e.name += " (Content only)";
- break;
- case "Email":
- e.name += " (Members only)";
- break;
- case "Username":
- e.name += " (Members only)";
- break;
- }
- });
- });
-
-}
-
-
-angular.module("umbraco").controller("Umbraco.PrevalueEditors.SortByListViewController", sortByPreValsController);
-//DO NOT DELETE THIS, this is in use...
-angular.module('umbraco')
-.controller("Umbraco.PropertyEditors.MacroContainerController",
-
- function($scope, dialogService, entityResource, macroService){
- $scope.renderModel = [];
-
- if($scope.model.value){
- var macros = $scope.model.value.split('>');
-
- angular.forEach(macros, function(syntax, key){
- if(syntax && syntax.length > 10){
- //re-add the char we split on
- syntax = syntax + ">";
- var parsed = macroService.parseMacroSyntax(syntax);
- if(!parsed){
- parsed = {};
- }
-
- parsed.syntax = syntax;
- collectDetails(parsed);
- $scope.renderModel.push(parsed);
- }
- });
- }
-
-
- function collectDetails(macro){
- macro.details = "";
- if(macro.macroParamsDictionary){
- angular.forEach((macro.macroParamsDictionary), function(value, key){
- macro.details += key + ": " + value + " ";
- });
- }
- }
-
- function openDialog(index){
- var dialogData = {
- allowedMacros: $scope.model.config.allowed
- };
-
- if(index !== null && $scope.renderModel[index]) {
- var macro = $scope.renderModel[index];
- dialogData["macroData"] = macro;
- }
-
- $scope.macroPickerOverlay = {};
- $scope.macroPickerOverlay.view = "macropicker";
- $scope.macroPickerOverlay.dialogData = dialogData;
- $scope.macroPickerOverlay.show = true;
-
- $scope.macroPickerOverlay.submit = function(model) {
-
- var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
- collectDetails(macroObject);
-
- //update the raw syntax and the list...
- if(index !== null && $scope.renderModel[index]) {
- $scope.renderModel[index] = macroObject;
- } else {
- $scope.renderModel.push(macroObject);
- }
-
- $scope.macroPickerOverlay.show = false;
- $scope.macroPickerOverlay = null;
- };
-
- $scope.macroPickerOverlay.close = function(oldModel) {
- $scope.macroPickerOverlay.show = false;
- $scope.macroPickerOverlay = null;
- };
-
- }
-
-
-
- $scope.edit =function(index){
- openDialog(index);
- };
-
- $scope.add = function () {
-
- if ($scope.model.config.max && $scope.model.config.max > 0 && $scope.renderModel.length >= $scope.model.config.max) {
- //cannot add more than the max
- return;
- }
-
- openDialog();
- };
-
- $scope.remove =function(index){
- $scope.renderModel.splice(index, 1);
- };
-
- $scope.clear = function() {
- $scope.model.value = "";
- $scope.renderModel = [];
- };
-
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
- var syntax = [];
- angular.forEach($scope.renderModel, function(value, key){
- syntax.push(value.syntax);
- });
-
- $scope.model.value = syntax.join("");
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
-
-
- function trim(str, chr) {
- var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g');
- return str.replace(rgxtrim, '');
- }
-
-});
+function sortByPreValsController($rootScope, $scope, localizationService) {
-function MacroListController($scope, entityResource) {
-
- $scope.items = [];
-
- entityResource.getAll("Macro").then(function(items) {
- _.each(items, function(i) {
- $scope.items.push({ name: i.name, alias: i.alias });
- });
-
- });
-
-
-}
-
-angular.module("umbraco").controller("Umbraco.PrevalueEditors.MacroList", MacroListController);
+ $scope.sortByFields = [
+ { value: "SortOrder", key: "general_sort" },
+ { value: "Name", key: "general_name" },
+ { value: "VersionDate", key: "content_updateDate" },
+ { value: "Updater", key: "content_updatedBy" },
+ { value: "CreateDate", key: "content_createDate" },
+ { value: "Owner", key: "content_createBy" },
+ { value: "ContentTypeAlias", key: "content_documentType" },
+ { value: "Published", key: "content_isPublished" },
+ { value: "Email", key: "general_email" },
+ { value: "Username", key: "general_username" }
+ ];
+
+ //now we'll localize these strings, for some reason the directive doesn't work inside of the select group with an ng-model declared
+ _.each($scope.sortByFields, function (e, i) {
+ localizationService.localize(e.key).then(function (v) {
+ e.name = v;
+
+ switch (e.value) {
+ case "Updater":
+ e.name += " (Content only)";
+ break;
+ case "Published":
+ e.name += " (Content only)";
+ break;
+ case "Email":
+ e.name += " (Members only)";
+ break;
+ case "Username":
+ e.name += " (Members only)";
+ break;
+ }
+ });
+ });
+
+}
+
+
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.SortByListViewController", sortByPreValsController);
+//DO NOT DELETE THIS, this is in use...
+angular.module('umbraco')
+.controller("Umbraco.PropertyEditors.MacroContainerController",
+
+ function($scope, dialogService, entityResource, macroService){
+ $scope.renderModel = [];
+
+ if($scope.model.value){
+ var macros = $scope.model.value.split('>');
+
+ angular.forEach(macros, function(syntax, key){
+ if(syntax && syntax.length > 10){
+ //re-add the char we split on
+ syntax = syntax + ">";
+ var parsed = macroService.parseMacroSyntax(syntax);
+ if(!parsed){
+ parsed = {};
+ }
+
+ parsed.syntax = syntax;
+ collectDetails(parsed);
+ $scope.renderModel.push(parsed);
+ }
+ });
+ }
+
+
+ function collectDetails(macro){
+ macro.details = "";
+ if(macro.macroParamsDictionary){
+ angular.forEach((macro.macroParamsDictionary), function(value, key){
+ macro.details += key + ": " + value + " ";
+ });
+ }
+ }
+
+ function openDialog(index){
+ var dialogData = {
+ allowedMacros: $scope.model.config.allowed
+ };
+
+ if(index !== null && $scope.renderModel[index]) {
+ var macro = $scope.renderModel[index];
+ dialogData["macroData"] = macro;
+ }
+
+ $scope.macroPickerOverlay = {};
+ $scope.macroPickerOverlay.view = "macropicker";
+ $scope.macroPickerOverlay.dialogData = dialogData;
+ $scope.macroPickerOverlay.show = true;
+
+ $scope.macroPickerOverlay.submit = function(model) {
+
+ var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, dialogData.renderingEngine);
+ collectDetails(macroObject);
+
+ //update the raw syntax and the list...
+ if(index !== null && $scope.renderModel[index]) {
+ $scope.renderModel[index] = macroObject;
+ } else {
+ $scope.renderModel.push(macroObject);
+ }
+
+ $scope.macroPickerOverlay.show = false;
+ $scope.macroPickerOverlay = null;
+ };
+
+ $scope.macroPickerOverlay.close = function(oldModel) {
+ $scope.macroPickerOverlay.show = false;
+ $scope.macroPickerOverlay = null;
+ };
+
+ }
+
+
+
+ $scope.edit =function(index){
+ openDialog(index);
+ };
+
+ $scope.add = function () {
+
+ if ($scope.model.config.max && $scope.model.config.max > 0 && $scope.renderModel.length >= $scope.model.config.max) {
+ //cannot add more than the max
+ return;
+ }
+
+ openDialog();
+ };
+
+ $scope.remove =function(index){
+ $scope.renderModel.splice(index, 1);
+ };
+
+ $scope.clear = function() {
+ $scope.model.value = "";
+ $scope.renderModel = [];
+ };
+
+ var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+ var syntax = [];
+ angular.forEach($scope.renderModel, function(value, key){
+ syntax.push(value.syntax);
+ });
+
+ $scope.model.value = syntax.join("");
+ });
+
+ //when the scope is destroyed we need to unsubscribe
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+
+ function trim(str, chr) {
+ var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^'+chr+'+|'+chr+'+$', 'g');
+ return str.replace(rgxtrim, '');
+ }
+
+});
+
+function MacroListController($scope, entityResource) {
+
+ $scope.items = [];
+
+ entityResource.getAll("Macro").then(function(items) {
+ _.each(items, function(i) {
+ $scope.items.push({ name: i.name, alias: i.alias });
+ });
+
+ });
+
+
+}
+
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.MacroList", MacroListController);
//inject umbracos assetsServce and dialog service
function MarkdownEditorController($scope, $element, assetsService, dialogService, angularHelper, $timeout) {
@@ -13209,28 +13425,28 @@ function MultipleTextBoxController($scope) {
angular.module("umbraco").controller("Umbraco.PropertyEditors.MultipleTextBoxController", MultipleTextBoxController);
-angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController",
- function($scope) {
-
- if (angular.isObject($scope.model.config.items)) {
-
- //now we need to format the items in the dictionary because we always want to have an array
- var newItems = [];
- var vals = _.values($scope.model.config.items);
- var keys = _.keys($scope.model.config.items);
- for (var i = 0; i < vals.length; i++) {
- newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value });
- }
-
- //ensure the items are sorted by the provided sort order
- newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
-
- //re-assign
- $scope.model.config.items = newItems;
-
- }
-
- });
+angular.module("umbraco").controller("Umbraco.PropertyEditors.RadioButtonsController",
+ function($scope) {
+
+ if (angular.isObject($scope.model.config.items)) {
+
+ //now we need to format the items in the dictionary because we always want to have an array
+ var newItems = [];
+ var vals = _.values($scope.model.config.items);
+ var keys = _.keys($scope.model.config.items);
+ for (var i = 0; i < vals.length; i++) {
+ newItems.push({ id: keys[i], sortOrder: vals[i].sortOrder, value: vals[i].value });
+ }
+
+ //ensure the items are sorted by the provided sort order
+ newItems.sort(function (a, b) { return (a.sortOrder > b.sortOrder) ? 1 : ((b.sortOrder > a.sortOrder) ? -1 : 0); });
+
+ //re-assign
+ $scope.model.config.items = newItems;
+
+ }
+
+ });
/**
* @ngdoc controller
@@ -13725,7 +13941,7 @@ angular.module("umbraco")
};
});
-
+
//Create the embedded plugin
tinyMceService.createInsertEmbeddedMedia(editor, $scope, function() {
@@ -13810,80 +14026,126 @@ angular.module("umbraco")
});
-angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
- function ($scope, $timeout, $log, tinyMceService, stylesheetResource) {
- var cfg = tinyMceService.defaultPrevalues();
-
- if($scope.model.value){
- if(angular.isString($scope.model.value)){
- $scope.model.value = cfg;
- }
- }else{
- $scope.model.value = cfg;
- }
-
- if (!$scope.model.value.stylesheets) {
- $scope.model.value.stylesheets = [];
- }
- if (!$scope.model.value.toolbar) {
- $scope.model.value.toolbar = [];
- }
- if (!$scope.model.value.maxImageSize && $scope.model.value.maxImageSize != 0) {
- $scope.model.value.maxImageSize = cfg.maxImageSize;
- }
-
- tinyMceService.configuration().then(function(config){
- $scope.tinyMceConfig = config;
-
- });
-
- stylesheetResource.getAll().then(function(stylesheets){
- $scope.stylesheets = stylesheets;
- });
-
- $scope.selected = function(cmd, alias, lookup){
- if (lookup && angular.isArray(lookup)) {
- cmd.selected = lookup.indexOf(alias) >= 0;
- return cmd.selected;
- }
- return false;
- };
-
- $scope.selectCommand = function(command){
- var index = $scope.model.value.toolbar.indexOf(command.frontEndCommand);
-
- if(command.selected && index === -1){
- $scope.model.value.toolbar.push(command.frontEndCommand);
- }else if(index >= 0){
- $scope.model.value.toolbar.splice(index, 1);
- }
- };
-
- $scope.selectStylesheet = function (css) {
-
- var index = $scope.model.value.stylesheets.indexOf(css.name);
-
- if(css.selected && index === -1){
- $scope.model.value.stylesheets.push(css.name);
- }else if(index >= 0){
- $scope.model.value.stylesheets.splice(index, 1);
- }
- };
-
- var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
-
- var commands = _.where($scope.tinyMceConfig.commands, {selected: true});
- $scope.model.value.toolbar = _.pluck(commands, "frontEndCommand");
-
- });
-
- //when the scope is destroyed we need to unsubscribe
- $scope.$on('$destroy', function () {
- unsubscribe();
- });
-
- });
+angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController",
+ function ($scope, $timeout, $log, tinyMceService, stylesheetResource, assetsService) {
+ var cfg = tinyMceService.defaultPrevalues();
+ if($scope.model.value){
+ if(angular.isString($scope.model.value)){
+ $scope.model.value = cfg;
+ }
+ }else{
+ $scope.model.value = cfg;
+ }
+
+ if (!$scope.model.value.stylesheets) {
+ $scope.model.value.stylesheets = [];
+ }
+ if (!$scope.model.value.toolbar) {
+ $scope.model.value.toolbar = [];
+ }
+ if (!$scope.model.value.maxImageSize && $scope.model.value.maxImageSize != 0) {
+ $scope.model.value.maxImageSize = cfg.maxImageSize;
+ }
+
+ tinyMceService.configuration().then(function(config){
+ $scope.tinyMceConfig = config;
+
+ // extend commands with properties for font-icon and if it is a custom command
+ $scope.tinyMceConfig.commands = _.map($scope.tinyMceConfig.commands, function (obj) {
+ var icon = getFontIcon(obj.frontEndCommand);
+ return angular.extend(obj, {
+ fontIcon: icon.name,
+ isCustom: icon.isCustom
+ });
+ });
+ });
+
+ stylesheetResource.getAll().then(function(stylesheets){
+ $scope.stylesheets = stylesheets;
+ });
+
+ $scope.selected = function(cmd, alias, lookup){
+ if (lookup && angular.isArray(lookup)) {
+ cmd.selected = lookup.indexOf(alias) >= 0;
+ return cmd.selected;
+ }
+ return false;
+ };
+
+ $scope.selectCommand = function(command){
+ var index = $scope.model.value.toolbar.indexOf(command.frontEndCommand);
+
+ if(command.selected && index === -1){
+ $scope.model.value.toolbar.push(command.frontEndCommand);
+ }else if(index >= 0){
+ $scope.model.value.toolbar.splice(index, 1);
+ }
+ };
+
+ $scope.selectStylesheet = function (css) {
+
+ var index = $scope.model.value.stylesheets.indexOf(css.name);
+
+ if(css.selected && index === -1){
+ $scope.model.value.stylesheets.push(css.name);
+ }else if(index >= 0){
+ $scope.model.value.stylesheets.splice(index, 1);
+ }
+ };
+
+ // map properties for specific commands
+ function getFontIcon(alias) {
+ var icon = { name: alias, isCustom: false };
+
+ switch (alias) {
+ case "codemirror":
+ icon.name = "code";
+ icon.isCustom = false;
+ break;
+ case "styleselect":
+ icon.name = "icon-list";
+ icon.isCustom = true;
+ break;
+ case "umbembeddialog":
+ icon.name = "icon-tv";
+ icon.isCustom = true;
+ break;
+ case "umbmediapicker":
+ icon.name = "icon-picture";
+ icon.isCustom = true;
+ break;
+ case "umbmacro":
+ icon.name = "icon-settings-alt";
+ icon.isCustom = true;
+ break;
+ case "umbmacro":
+ icon.name = "icon-settings-alt";
+ icon.isCustom = true;
+ break;
+ default:
+ icon.name = alias;
+ icon.isCustom = false;
+ }
+
+ return icon;
+ }
+
+ var unsubscribe = $scope.$on("formSubmitting", function (ev, args) {
+
+ var commands = _.where($scope.tinyMceConfig.commands, {selected: true});
+ $scope.model.value.toolbar = _.pluck(commands, "frontEndCommand");
+
+ });
+
+ // when the scope is destroyed we need to unsubscribe
+ $scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+ // load TinyMCE skin which contains css for font-icons
+ assetsService.loadCss("lib/tinymce/skins/umbraco/skin.min.css");
+ });
function sliderController($scope, $log, $element, assetsService, angularHelper) {
//configure some defaults
diff --git a/OurUmbraco.Site/umbraco/js/umbraco.directives.js b/OurUmbraco.Site/umbraco/js/umbraco.directives.js
index eed66d10..d3b5b1bc 100644
--- a/OurUmbraco.Site/umbraco/js/umbraco.directives.js
+++ b/OurUmbraco.Site/umbraco/js/umbraco.directives.js
@@ -1,8639 +1,10871 @@
-/*! umbraco
- * https://github.com/umbraco/umbraco-cms/
- * Copyright (c) 2016 Umbraco HQ;
- * Licensed
- */
-
-(function() {
-
-angular.module("umbraco.directives", ["umbraco.directives.editors", "umbraco.directives.html", "umbraco.directives.validation", "ui.sortable"]);
-angular.module("umbraco.directives.editors", []);
-angular.module("umbraco.directives.html", []);
-angular.module("umbraco.directives.validation", []);
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:autoScale
- * @element div
- * @function
- *
- * @description
- * Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
- * So if you only want to scale the div to 70 pixels from the bottom you pass "70"
- *
- * @example
-
-
-
-
-
- */
-angular.module("umbraco.directives")
- .directive('autoScale', function ($window) {
- return function (scope, el, attrs) {
-
- var totalOffset = 0;
- var offsety = parseInt(attrs.autoScale, 10);
- var window = angular.element($window);
- if (offsety !== undefined){
- totalOffset += offsety;
- }
-
- setTimeout(function () {
- el.height(window.height() - (el.offset().top + totalOffset));
- }, 500);
-
- window.bind("resize", function () {
- el.height(window.height() - (el.offset().top + totalOffset));
- });
-
- };
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbPanel
-* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor
- * exceeds the height of the window
-**/
-angular.module("umbraco.directives.html")
- .directive('detectFold', function ($timeout, $log, windowResizeListener) {
- return {
- require: "^?umbTabs",
- restrict: 'A',
- link: function (scope, el, attrs, tabsCtrl) {
-
- var firstRun = false;
- var parent = $(".umb-panel-body");
- var winHeight = $(window).height();
- var calculate = function () {
- if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) {
-
- //now that the element is visible, set the flag in a couple of seconds,
- // this will ensure that loading time of a current tab get's completed and that
- // we eventually stop watching to save on CPU time
- $timeout(function() {
- firstRun = true;
- }, 4000);
-
- //var parent = el.parent();
- var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
- //var belowFold = (el.offset().top + el.height()) > winHeight;
- if (hasOverflow) {
- el.addClass("umb-bottom-bar");
-
- //I wish we didn't have to put this logic here but unfortunately we
- // do. This needs to calculate the left offest to place the bottom bar
- // depending on if the left column splitter has been moved by the user
- // (based on the nav-resize directive)
- var wrapper = $("#mainwrapper");
- var contentPanel = $("#leftcolumn").next();
- var contentPanelLeftPx = contentPanel.css("left");
-
- el.css({ left: contentPanelLeftPx });
- }
- }
- return firstRun;
- };
-
- var resizeCallback = function(size) {
- winHeight = size.height;
- el.removeClass("umb-bottom-bar");
- calculate();
- };
-
- windowResizeListener.register(resizeCallback);
-
- //Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute
- // the watcher since it will be recalculated when the tab changes!
- if (el.closest(".umb-tab-pane").index() === 0) {
- //run a watcher to ensure that the calculation occurs until it's firstRun but ensure
- // the calculations are throttled to save a bit of CPU
- var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) {
- if (newVal !== oldVal) {
- listener();
- }
- });
- }
-
- //listen for tab changes
- if (tabsCtrl != null) {
- tabsCtrl.onTabShown(function (args) {
- calculate();
- });
- }
-
- //ensure to unregister
- scope.$on('$destroy', function() {
- windowResizeListener.unregister(resizeCallback);
- });
- }
- };
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbItemSorter
-* @function
-* @element ANY
-* @restrict E
-* @description A re-usable directive for sorting items
-**/
-function umbItemSorter(angularHelper) {
- return {
- scope: {
- model: "="
- },
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/directives/_obsolete/umb-item-sorter.html',
- link: function(scope, element, attrs, ctrl) {
- var defaultModel = {
- okButton: "Ok",
- successMsg: "Sorting successful",
- complete: false
- };
- //assign user vals to default
- angular.extend(defaultModel, scope.model);
- //re-assign merged to user
- scope.model = defaultModel;
-
- scope.performSort = function() {
- scope.$emit("umbItemSorter.sorting", {
- sortedItems: scope.model.itemsToSort
- });
- };
-
- scope.handleCancel = function () {
- scope.$emit("umbItemSorter.cancel");
- };
-
- scope.handleOk = function() {
- scope.$emit("umbItemSorter.ok");
- };
-
- //defines the options for the jquery sortable
- scope.sortableOptions = {
- axis: 'y',
- cursor: "move",
- placeholder: "ui-sortable-placeholder",
- update: function (ev, ui) {
- //highlight the item when the position is changed
- $(ui.item).effect("highlight", { color: "#049cdb" }, 500);
- },
- stop: function (ev, ui) {
- //the ui-sortable directive already ensures that our list is re-sorted, so now we just
- // need to update the sortOrder to the index of each item
- angularHelper.safeApply(scope, function () {
- angular.forEach(scope.itemsToSort, function (val, index) {
- val.sortOrder = index + 1;
- });
-
- });
- }
- };
- }
- };
-}
-
-angular.module('umbraco.directives').directive("umbItemSorter", umbItemSorter);
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbContentName
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-angular.module("umbraco.directives")
- .directive('umbContentName', function ($timeout, localizationService) {
- return {
- require: "ngModel",
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-content-name.html',
-
- scope: {
- placeholder: '@placeholder',
- model: '=ngModel',
- ngDisabled: '='
- },
- link: function(scope, element, attrs, ngModel) {
-
- var inputElement = element.find("input");
- if(scope.placeholder && scope.placeholder[0] === "@"){
- localizationService.localize(scope.placeholder.substring(1))
- .then(function(value){
- scope.placeholder = value;
- });
- }
-
- var mX, mY, distance;
-
- function calculateDistance(elem, mouseX, mouseY) {
-
- var cx = Math.max(Math.min(mouseX, elem.offset().left + elem.width()), elem.offset().left);
- var cy = Math.max(Math.min(mouseY, elem.offset().top + elem.height()), elem.offset().top);
- return Math.sqrt((mouseX - cx) * (mouseX - cx) + (mouseY - cy) * (mouseY - cy));
- }
-
- var mouseMoveDebounce = _.throttle(function (e) {
- mX = e.pageX;
- mY = e.pageY;
- // not focused and not over element
- if (!inputElement.is(":focus") && !inputElement.hasClass("ng-invalid")) {
- // on page
- if (mX >= inputElement.offset().left) {
- distance = calculateDistance(inputElement, mX, mY);
- if (distance <= 155) {
-
- distance = 1 - (100 / 150 * distance / 100);
- inputElement.css("border", "1px solid rgba(175,175,175, " + distance + ")");
- inputElement.css("background-color", "rgba(255,255,255, " + distance + ")");
- }
- }
-
- }
-
- }, 15);
-
- $(document).bind("mousemove", mouseMoveDebounce);
-
- $timeout(function(){
- if(!scope.model){
- scope.goEdit();
- }
- }, 100, false);
-
- scope.goEdit = function(){
- scope.editMode = true;
-
- $timeout(function () {
- inputElement.focus();
- }, 100, false);
- };
-
- scope.exitEdit = function(){
- if(scope.model && scope.model !== ""){
- scope.editMode = false;
- }
- };
-
- //unbind doc event!
- scope.$on('$destroy', function () {
- $(document).unbind("mousemove", mouseMoveDebounce);
- });
- }
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbHeader
-* @restrict E
-* @function
-* @description
-* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead
-**/
-angular.module("umbraco.directives")
-.directive('umbHeader', function ($parse, $timeout) {
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/directives/_obsolete/umb-header.html',
- //create a new isolated scope assigning a tabs property from the attribute 'tabs'
- //which is bound to the parent scope property passed in
- scope: {
- tabs: "="
- },
- link: function (scope, iElement, iAttrs) {
-
- scope.showTabs = iAttrs.tabs ? true : false;
- scope.visibleTabs = [];
-
- //since tabs are loaded async, we need to put a watch on them to determine
- // when they are loaded, then we can close the watch
- var tabWatch = scope.$watch("tabs", function (newValue, oldValue) {
-
- angular.forEach(newValue, function(val, index){
- var tab = {id: val.id, label: val.label};
- scope.visibleTabs.push(tab);
- });
-
- //don't process if we cannot or have already done so
- if (!newValue) {return;}
- if (!newValue.length || newValue.length === 0){return;}
-
- //we need to do a timeout here so that the current sync operation can complete
- // and update the UI, then this will fire and the UI elements will be available.
- $timeout(function () {
-
- //use bootstrap tabs API to show the first one
- iElement.find(".nav-tabs a:first").tab('show');
-
- //enable the tab drop
- iElement.find('.nav-pills, .nav-tabs').tabdrop();
-
- //ensure to destroy tabdrop (unbinds window resize listeners)
- scope.$on('$destroy', function () {
- iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy");
- });
-
- //stop watching now
- tabWatch();
- }, 200);
-
- });
- }
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:login
-* @function
-* @element ANY
-* @restrict E
-**/
-function loginDirective() {
- return {
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/directives/_obsolete/umb-login.html'
- };
-}
-
-angular.module('umbraco.directives').directive("umbLogin", loginDirective);
-
-angular.module("umbraco.directives")
-.directive('umbOptionsMenu', function ($injector, treeService, navigationService, umbModelMapper, appState) {
- return {
- scope: {
- currentSection: "@",
- currentNode: "="
- },
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-optionsmenu.html',
- link: function (scope, element, attrs, ctrl) {
-
- //adds a handler to the context menu item click, we need to handle this differently
- //depending on what the menu item is supposed to do.
- scope.executeMenuItem = function (action) {
- navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
- };
-
- //callback method to go and get the options async
- scope.getOptions = function () {
-
- if (!scope.currentNode) {
- return;
- }
-
- //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
- appState.setMenuState("currentNode", scope.currentNode);
-
- if (!scope.actions) {
- treeService.getMenu({ treeNode: scope.currentNode })
- .then(function (data) {
- scope.actions = data.menuItems;
- });
- }
- };
-
- }
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbPhotoFolder
-* @restrict E
-**/
-angular.module("umbraco.directives.html")
- .directive('umbPhotoFolder', function($compile, $log, $timeout, $filter, umbPhotoFolderHelper) {
-
- return {
- restrict: 'E',
- replace: true,
- require: '?ngModel',
- terminate: true,
- templateUrl: 'views/directives/_obsolete/umb-photo-folder.html',
- link: function(scope, element, attrs, ngModel) {
-
- var lastWatch = null;
-
- ngModel.$render = function() {
- if (ngModel.$modelValue) {
-
- $timeout(function() {
- var photos = ngModel.$modelValue;
-
- scope.clickHandler = scope.$eval(element.attr('on-click'));
-
-
- var imagesOnly = element.attr('images-only') === "true";
-
-
- var margin = element.attr('border') ? parseInt(element.attr('border'), 10) : 5;
- var startingIndex = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0;
- var minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420;
- var minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 100;
- var maxHeight = element.attr('max-height') ? parseInt(element.attr('max-height'), 10) : 300;
- var idealImgPerRow = element.attr('ideal-items-per-row') ? parseInt(element.attr('ideal-items-per-row'), 10) : 5;
- var fixedRowWidth = Math.max(element.width(), minWidth);
-
- scope.containerStyle = { width: fixedRowWidth + "px" };
- scope.rows = umbPhotoFolderHelper.buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
-
- if (attrs.filterBy) {
-
- //we track the watches that we create, we don't want to create multiple, so clear it
- // if it already exists before creating another.
- if (lastWatch) {
- lastWatch();
- }
-
- //TODO: Need to debounce this so it doesn't filter too often!
- lastWatch = scope.$watch(attrs.filterBy, function (newVal, oldVal) {
- if (newVal && newVal !== oldVal) {
- var p = $filter('filter')(photos, newVal, false);
- scope.baseline = 0;
- var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
- scope.rows = m;
- }
- });
- }
-
- }, 500); //end timeout
- } //end if modelValue
-
- }; //end $render
- }
- };
- });
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:umbSort
- * @element div
- * @function
- *
- * @description
- * Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
- * So if you only want to scale the div to 70 pixels from the bottom you pass "70"
- *
- * @example
-
-
-
-
-
- */
-angular.module("umbraco.directives")
- .value('umbSortContextInternal',{})
- .directive('umbSort', function($log,umbSortContextInternal) {
- return {
- require: '?ngModel',
- link: function(scope, element, attrs, ngModel) {
- var adjustment;
-
- var cfg = scope.$eval(element.attr('umb-sort')) || {};
-
- scope.model = ngModel;
-
- scope.opts = cfg;
- scope.opts.containerSelector= cfg.containerSelector || ".umb-" + cfg.group + "-container",
- scope.opts.nested= cfg.nested || true,
- scope.opts.drop= cfg.drop || true,
- scope.opts.drag= cfg.drag || true,
- scope.opts.clone = cfg.clone || "";
- scope.opts.mode = cfg.mode || "list";
-
- scope.opts.itemSelectorFull = $.trim(scope.opts.itemPath + " " + scope.opts.itemSelector);
-
- /*
- scope.opts.isValidTarget = function(item, container) {
- if(container.el.is(".umb-" + scope.opts.group + "-container")){
- return true;
- }
- return false;
- };
- */
-
- element.addClass("umb-sort");
- element.addClass("umb-" + cfg.group + "-container");
-
- scope.opts.onDrag = function (item, position) {
- if(scope.opts.mode === "list"){
- item.css({
- left: position.left - adjustment.left,
- top: position.top - adjustment.top
- });
- }
- };
-
-
- scope.opts.onDrop = function (item, targetContainer, _super) {
-
- if(scope.opts.mode === "list"){
- //list mode
- var clonedItem = $(scope.opts.clone).css({height: 0});
- item.after(clonedItem);
- clonedItem.animate({'height': item.height()});
-
- item.animate(clonedItem.position(), function () {
- clonedItem.detach();
- _super(item);
- });
- }
-
- var children = $(scope.opts.itemSelectorFull, targetContainer.el);
- var targetIndex = children.index(item);
- var targetScope = $(targetContainer.el[0]).scope();
-
-
- if(targetScope === umbSortContextInternal.sourceScope){
- if(umbSortContextInternal.sourceScope.opts.onSortHandler){
- var _largs = {
- oldIndex: umbSortContextInternal.sourceIndex,
- newIndex: targetIndex,
- scope: umbSortContextInternal.sourceScope
- };
-
- umbSortContextInternal.sourceScope.opts.onSortHandler.call(this, item, _largs);
- }
- }else{
-
-
- if(targetScope.opts.onDropHandler){
- var args = {
- sourceScope: umbSortContextInternal.sourceScope,
- sourceIndex: umbSortContextInternal.sourceIndex,
- sourceContainer: umbSortContextInternal.sourceContainer,
-
- targetScope: targetScope,
- targetIndex: targetIndex,
- targetContainer: targetContainer
- };
-
- targetScope.opts.onDropHandler.call(this, item, args);
- }
-
- if(umbSortContextInternal.sourceScope.opts.onReleaseHandler){
- var _args = {
- sourceScope: umbSortContextInternal.sourceScope,
- sourceIndex: umbSortContextInternal.sourceIndex,
- sourceContainer: umbSortContextInternal.sourceContainer,
-
- targetScope: targetScope,
- targetIndex: targetIndex,
- targetContainer: targetContainer
- };
-
- umbSortContextInternal.sourceScope.opts.onReleaseHandler.call(this, item, _args);
- }
- }
- };
-
- scope.changeIndex = function(from, to){
- scope.$apply(function(){
- var i = ngModel.$modelValue.splice(from, 1)[0];
- ngModel.$modelValue.splice(to, 0, i);
- });
- };
-
- scope.move = function(args){
- var from = args.sourceIndex;
- var to = args.targetIndex;
-
- if(args.sourceContainer === args.targetContainer){
- scope.changeIndex(from, to);
- }else{
- scope.$apply(function(){
- var i = args.sourceScope.model.$modelValue.splice(from, 1)[0];
- args.targetScope.model.$modelvalue.splice(to,0, i);
- });
- }
- };
-
- scope.opts.onDragStart = function (item, container, _super) {
- var children = $(scope.opts.itemSelectorFull, container.el);
- var offset = item.offset();
-
- umbSortContextInternal.sourceIndex = children.index(item);
- umbSortContextInternal.sourceScope = $(container.el[0]).scope();
- umbSortContextInternal.sourceContainer = container;
-
- //current.item = ngModel.$modelValue.splice(current.index, 1)[0];
-
- var pointer = container.rootGroup.pointer;
- adjustment = {
- left: pointer.left - offset.left,
- top: pointer.top - offset.top
- };
-
- _super(item, container);
- };
-
- element.sortable( scope.opts );
- }
- };
-
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTabView
-* @restrict E
-**/
-angular.module("umbraco.directives")
-.directive('umbTabView', function($timeout, $log){
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/directives/_obsolete/umb-tab-view.html'
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbUploadDropzone
-* @restrict E
-**/
-angular.module("umbraco.directives.html")
- .directive('umbUploadDropzone', function(){
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/directives/_obsolete/umb-upload-dropzone.html'
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:navResize
-* @restrict A
- *
- * @description
- * Handles how the navigation responds to window resizing and controls how the draggable resize panel works
-**/
-angular.module("umbraco.directives")
- .directive('navResize', function (appState, eventsService, windowResizeListener) {
- return {
- restrict: 'A',
- link: function (scope, element, attrs, ctrl) {
-
- var minScreenSize = 1100;
- var resizeEnabled = false;
-
- function setTreeMode() {
- appState.setGlobalState("showNavigation", appState.getGlobalState("isTablet") === false);
- }
-
- function enableResize() {
- //only enable when the size is correct and it's not already enabled
- if (!resizeEnabled && appState.getGlobalState("isTablet") === false) {
- element.resizable(
- {
- containment: $("#mainwrapper"),
- autoHide: true,
- handles: "e",
- alsoResize: ".navigation-inner-container",
- resize: function(e, ui) {
- var wrapper = $("#mainwrapper");
- var contentPanel = $("#contentwrapper");
- var umbNotification = $("#umb-notifications-wrapper");
- var apps = $("#applications");
- var bottomBar = contentPanel.find(".umb-bottom-bar");
- var navOffeset = $("#navOffset");
-
- var leftPanelWidth = ui.element.width() + apps.width();
-
- contentPanel.css({ left: leftPanelWidth });
- bottomBar.css({ left: leftPanelWidth });
- umbNotification.css({ left: leftPanelWidth });
-
- navOffeset.css({ "margin-left": ui.element.outerWidth() });
- },
- stop: function (e, ui) {
-
- }
- });
-
- resizeEnabled = true;
- }
- }
-
- function resetResize() {
- if (resizeEnabled) {
- //kill the resize
- element.resizable("destroy");
- element.css("width", "");
-
- var navInnerContainer = element.find(".navigation-inner-container");
-
- navInnerContainer.css("width", "");
- $("#contentwrapper").css("left", "");
- $("#umb-notifications-wrapper").css("left", "");
- $("#navOffset").css("margin-left", "");
-
- resizeEnabled = false;
- }
- }
-
- var evts = [];
-
- //Listen for global state changes
- evts.push(eventsService.on("appState.globalState.changed", function (e, args) {
- if (args.key === "showNavigation") {
- if (args.value === false) {
- resetResize();
- }
- else {
- enableResize();
- }
- }
- }));
-
- var resizeCallback = function(size) {
- //set the global app state
- appState.setGlobalState("isTablet", (size.width <= minScreenSize));
- setTreeMode();
- };
-
- windowResizeListener.register(resizeCallback);
-
- //ensure to unregister from all events and kill jquery plugins
- scope.$on('$destroy', function () {
- windowResizeListener.unregister(resizeCallback);
- for (var e in evts) {
- eventsService.unsubscribe(evts[e]);
- }
- var navInnerContainer = element.find(".navigation-inner-container");
- navInnerContainer.resizable("destroy");
- });
-
- //init
- //set the global app state
- appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
- setTreeMode();
- }
- };
- });
-
-angular.module("umbraco.directives")
-.directive('sectionIcon', function ($compile, iconHelper) {
- return {
- restrict: 'E',
- replace: true,
-
- link: function (scope, element, attrs) {
-
- var icon = attrs.icon;
-
- if (iconHelper.isLegacyIcon(icon)) {
- //its a known legacy icon, convert to a new one
- element.html("");
- }
- else if (iconHelper.isFileBasedIcon(icon)) {
- var convert = iconHelper.convertFromLegacyImage(icon);
- if(convert){
- element.html("");
- }else{
- element.html("
");
- }
- //it's a file, normally legacy so look in the icon tray images
- }
- else {
- //it's normal
- element.html("");
- }
- }
- };
-});
-angular.module("umbraco.directives")
-.directive('umbContextMenu', function (navigationService) {
- return {
- scope: {
- menuDialogTitle: "@",
- currentSection: "@",
- currentNode: "=",
- menuActions: "="
- },
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/application/umb-contextmenu.html',
- link: function (scope, element, attrs, ctrl) {
-
- //adds a handler to the context menu item click, we need to handle this differently
- //depending on what the menu item is supposed to do.
- scope.executeMenuItem = function (action) {
- navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
- };
- }
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbNavigation
-* @restrict E
-**/
-function umbNavigationDirective() {
- return {
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/components/application/umb-navigation.html'
- };
-}
-
-angular.module('umbraco.directives').directive("umbNavigation", umbNavigationDirective);
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbSections
-* @restrict E
-**/
-function sectionsDirective($timeout, $window, navigationService, treeService, sectionResource, appState, eventsService, $location) {
- return {
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/components/application/umb-sections.html',
- link: function (scope, element, attr, ctrl) {
-
- //setup scope vars
- scope.maxSections = 7;
- scope.overflowingSections = 0;
- scope.sections = [];
- scope.currentSection = appState.getSectionState("currentSection");
- scope.showTray = false; //appState.getGlobalState("showTray");
- scope.stickyNavigation = appState.getGlobalState("stickyNavigation");
- scope.needTray = false;
- scope.trayAnimation = function() {
- if (scope.showTray) {
- return 'slide';
- }
- else if (scope.showTray === false) {
- return 'slide';
- }
- else {
- return '';
- }
- };
-
- function loadSections(){
- sectionResource.getSections()
- .then(function (result) {
- scope.sections = result;
- calculateHeight();
- });
- }
-
- function calculateHeight(){
- $timeout(function(){
- //total height minus room for avatar and help icon
- var height = $(window).height()-200;
- scope.totalSections = scope.sections.length;
- scope.maxSections = Math.floor(height / 70);
- scope.needTray = false;
-
- if(scope.totalSections > scope.maxSections){
- scope.needTray = true;
- scope.overflowingSections = scope.maxSections - scope.totalSections;
- }
- });
- }
-
- var evts = [];
-
- //Listen for global state changes
- evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
- if (args.key === "showTray") {
- scope.showTray = args.value;
- }
- if (args.key === "stickyNavigation") {
- scope.stickyNavigation = args.value;
- }
- }));
-
- evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
- if (args.key === "currentSection") {
- scope.currentSection = args.value;
- }
- }));
-
- evts.push(eventsService.on("app.reInitialize", function(e, args) {
- //re-load the sections if we're re-initializing (i.e. package installed)
- loadSections();
- }));
-
- //ensure to unregister from all events!
- scope.$on('$destroy', function () {
- for (var e in evts) {
- eventsService.unsubscribe(evts[e]);
- }
- });
-
- //on page resize
- window.onresize = calculateHeight;
-
- scope.avatarClick = function(){
-
- if(scope.helpDialog) {
- closeHelpDialog();
- }
-
- if(!scope.userDialog) {
- scope.userDialog = {
- view: "user",
- show: true,
- close: function(oldModel) {
- closeUserDialog();
- }
- };
- } else {
- closeUserDialog();
- }
-
- };
-
- function closeUserDialog() {
- scope.userDialog.show = false;
- scope.userDialog = null;
- }
-
- scope.helpClick = function(){
-
- if(scope.userDialog) {
- closeUserDialog();
- }
-
- if(!scope.helpDialog) {
- scope.helpDialog = {
- view: "help",
- show: true,
- close: function(oldModel) {
- closeHelpDialog();
- }
- };
- } else {
- closeHelpDialog();
- }
-
- };
-
- function closeHelpDialog() {
- scope.helpDialog.show = false;
- scope.helpDialog = null;
- }
-
- scope.sectionClick = function (event, section) {
-
- if (event.ctrlKey ||
- event.shiftKey ||
- event.metaKey || // apple
- (event.button && event.button === 1) // middle click, >IE9 + everyone else
- ) {
- return;
- }
-
- if (scope.userDialog) {
- closeUserDialog();
- }
- if (scope.helpDialog) {
- closeHelpDialog();
- }
-
- navigationService.hideSearch();
- navigationService.showTree(section.alias);
- $location.path("/" + section.alias);
- };
-
- scope.sectionDblClick = function(section){
- navigationService.reloadSection(section.alias);
- };
-
- scope.trayClick = function () {
- // close dialogs
- if (scope.userDialog) {
- closeUserDialog();
- }
- if (scope.helpDialog) {
- closeHelpDialog();
- }
-
- if (appState.getGlobalState("showTray") === true) {
- navigationService.hideTray();
- } else {
- navigationService.showTray();
- }
- };
-
- loadSections();
-
- }
- };
-}
-
-angular.module('umbraco.directives').directive("umbSections", sectionsDirective);
-
-(function() {
- 'use strict';
-
- function ButtonDirective($timeout) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.style = null;
-
- function activate() {
-
- if (!scope.state) {
- scope.state = "init";
- }
-
- if (scope.buttonStyle) {
- scope.style = "btn-" + scope.buttonStyle;
- }
-
- }
-
- activate();
-
- var unbindStateWatcher = scope.$watch('state', function(newValue, oldValue) {
-
- if (newValue === 'success' || newValue === 'error') {
- $timeout(function() {
- scope.state = 'init';
- }, 2000);
- }
-
- });
-
- scope.$on('$destroy', function() {
- unbindStateWatcher();
- });
-
- }
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/buttons/umb-button.html',
- link: link,
- scope: {
- action: "&?",
- href: "@?",
- type: "@",
- buttonStyle: "@?",
- state: "=?",
- shortcut: "@?",
- shortcutWhenHidden: "@",
- label: "@?",
- labelKey: "@?",
- icon: "@?",
- disabled: "="
- }
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbButton', ButtonDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function ButtonGroupDirective() {
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/buttons/umb-button-group.html',
- scope: {
- defaultButton: "=",
- subButtons: "=",
- state: "=?",
- direction: "@?",
- float: "@?"
- }
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorSubHeaderDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubHeader', EditorSubHeaderDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorSubHeaderContentLeftDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-left.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubHeaderContentLeft', EditorSubHeaderContentLeftDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorSubHeaderContentRightDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-right.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubHeaderContentRight', EditorSubHeaderContentRightDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorSubHeaderSectionDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-section.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubHeaderSection', EditorSubHeaderSectionDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function BreadcrumbsDirective() {
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-breadcrumbs.html',
- scope: {
- ancestors: "=",
- entityType: "@"
- }
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorContainerDirective(overlayHelper) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.numberOfOverlays = 0;
-
- scope.$watch(function(){
- return overlayHelper.getNumberOfOverlays();
- }, function (newValue) {
- scope.numberOfOverlays = newValue;
- });
-
- }
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-container.html',
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorFooterDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-footer.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorFooter', EditorFooterDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorFooterContentLeftDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-footer-content-left.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorFooterContentLeft', EditorFooterContentLeftDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorFooterContentRightDirective() {
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-footer-content-right.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorFooterContentRight', EditorFooterContentRightDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorHeaderDirective(iconHelper) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.openIconPicker = function() {
- scope.dialogModel = {
- view: "iconpicker",
- show: true,
- submit: function(model) {
- if (model.color) {
- scope.icon = model.icon + " " + model.color;
- } else {
- scope.icon = model.icon;
- }
-
- // set form to dirty
- ctrl.$setDirty();
-
- scope.dialogModel.show = false;
- scope.dialogModel = null;
- }
- };
- };
- }
-
- var directive = {
- require: '^form',
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-header.html',
- scope: {
- tabs: "=",
- actions: "=",
- name: "=",
- nameLocked: "=",
- menu: "=",
- icon: "=",
- hideIcon: "@",
- alias: "=",
- hideAlias: "@",
- description: "=",
- hideDescription: "@",
- navigation: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorHeader', EditorHeaderDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) {
-
- function link(scope, el, attr, ctrl) {
-
- //adds a handler to the context menu item click, we need to handle this differently
- //depending on what the menu item is supposed to do.
- scope.executeMenuItem = function (action) {
- navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
- };
-
- //callback method to go and get the options async
- scope.getOptions = function () {
-
- if (!scope.currentNode) {
- return;
- }
-
- //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
- appState.setMenuState("currentNode", scope.currentNode);
-
- if (!scope.actions) {
- treeService.getMenu({ treeNode: scope.currentNode })
- .then(function (data) {
- scope.actions = data.menuItems;
- });
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-menu.html',
- link: link,
- scope: {
- currentNode: "=",
- currentSection: "@"
- }
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorNavigationDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.showNavigation = true;
-
- scope.clickNavigationItem = function(selectedItem) {
- setItemToActive(selectedItem);
- runItemAction(selectedItem);
- };
-
- function runItemAction(selectedItem) {
- if (selectedItem.action) {
- selectedItem.action(selectedItem);
- }
- }
-
- function setItemToActive(selectedItem) {
- // set all other views to inactive
- if (selectedItem.view) {
-
- for (var index = 0; index < scope.navigation.length; index++) {
- var item = scope.navigation[index];
- item.active = false;
- }
-
- // set view to active
- selectedItem.active = true;
-
- }
- }
-
- function activate() {
-
- // hide navigation if there is only 1 item
- if (scope.navigation.length <= 1) {
- scope.showNavigation = false;
- }
-
- }
-
- activate();
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-navigation.html',
- scope: {
- navigation: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorSubViewsDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.activeView = {};
-
- // set toolbar from selected navigation item
- function setActiveView(items) {
-
- for (var index = 0; index < items.length; index++) {
-
- var item = items[index];
-
- if (item.active && item.view) {
- scope.activeView = item;
- }
- }
- }
-
- // watch for navigation changes
- scope.$watch('subViews', function(newValue, oldValue) {
- if (newValue) {
- setActiveView(newValue);
- }
- }, true);
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-sub-views.html',
- scope: {
- subViews: "=",
- model: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function EditorViewDirective() {
-
- function link(scope, el, attr) {
-
- if(attr.footer) {
- scope.footer = attr.footer;
- }
-
- }
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/editor/umb-editor-view.html',
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEditorView', EditorViewDirective);
-
-})();
-
-/**
-* @description Utillity directives for key and field events
-**/
-angular.module('umbraco.directives')
-
-.directive('onKeyup', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onKeyup);
- };
- elm.on("keyup", f);
- scope.$on("$destroy", function(){ elm.off("keyup", f);} );
- }
- };
-})
-
-.directive('onKeydown', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onKeydown);
- };
- elm.on("keydown", f);
- scope.$on("$destroy", function(){ elm.off("keydown", f);} );
- }
- };
-})
-
-.directive('onBlur', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onBlur);
- };
- elm.on("blur", f);
- scope.$on("$destroy", function(){ elm.off("blur", f);} );
- }
- };
-})
-
-.directive('onFocus', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onFocus);
- };
- elm.on("focus", f);
- scope.$on("$destroy", function(){ elm.off("focus", f);} );
- }
- };
-})
-
-.directive('onDragEnter', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onDragEnter);
- };
- elm.on("dragenter", f);
- scope.$on("$destroy", function(){ elm.off("dragenter", f);} );
- }
- };
-})
-
-.directive('onDragLeave', function () {
- return function (scope, elm, attrs) {
- var f = function (event) {
- var rect = this.getBoundingClientRect();
- var getXY = function getCursorPosition(event) {
- var x, y;
-
- if (typeof event.clientX === 'undefined') {
- // try touch screen
- x = event.pageX + document.documentElement.scrollLeft;
- y = event.pageY + document.documentElement.scrollTop;
- } else {
- x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
- y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
- }
-
- return { x: x, y : y };
- };
-
- var e = getXY(event.originalEvent);
-
- // Check the mouseEvent coordinates are outside of the rectangle
- if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
- scope.$apply(attrs.onDragLeave);
- }
- };
-
- elm.on("dragleave", f);
- scope.$on("$destroy", function(){ elm.off("dragleave", f);} );
- };
-})
-
-.directive('onDragOver', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onDragOver);
- };
- elm.on("dragover", f);
- scope.$on("$destroy", function(){ elm.off("dragover", f);} );
- }
- };
-})
-
-.directive('onDragStart', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onDragStart);
- };
- elm.on("dragstart", f);
- scope.$on("$destroy", function(){ elm.off("dragstart", f);} );
- }
- };
-})
-
-.directive('onDragEnd', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onDragEnd);
- };
- elm.on("dragend", f);
- scope.$on("$destroy", function(){ elm.off("dragend", f);} );
- }
- };
-})
-
-.directive('onDrop', function () {
- return {
- link: function (scope, elm, attrs) {
- var f = function () {
- scope.$apply(attrs.onDrop);
- };
- elm.on("drop", f);
- scope.$on("$destroy", function(){ elm.off("drop", f);} );
- }
- };
-})
-
-.directive('onOutsideClick', function ($timeout) {
- return function (scope, element, attrs) {
-
- var eventBindings = [];
-
- function oneTimeClick(event) {
- var el = event.target.nodeName;
-
- //ignore link and button clicks
- var els = ["INPUT","A","BUTTON"];
- if(els.indexOf(el) >= 0){return;}
-
- // ignore children of links and buttons
- // ignore clicks on new overlay
- var parents = $(event.target).parents("a,button,.umb-overlay");
- if(parents.length > 0){
- return;
- }
-
- // ignore clicks on dialog from old dialog service
- var oldDialog = $(el).parents("#old-dialog-service");
- if (oldDialog.length === 1) {
- return;
- }
-
- // ignore clicks in tinyMCE dropdown(floatpanel)
- var floatpanel = $(el).parents(".mce-floatpanel");
- if (floatpanel.length === 1) {
- return;
- }
-
- //ignore clicks inside this element
- if( $(element).has( $(event.target) ).length > 0 ){
- return;
- }
-
- scope.$apply(attrs.onOutsideClick);
- }
-
-
- $timeout(function(){
-
- if ("bindClickOn" in attrs) {
-
- eventBindings.push(scope.$watch(function() {
- return attrs.bindClickOn;
- }, function(newValue) {
- if (newValue === "true") {
- $(document).on("click", oneTimeClick);
- } else {
- $(document).off("click", oneTimeClick);
- }
- }));
-
- } else {
- $(document).on("click", oneTimeClick);
- }
-
- scope.$on("$destroy", function() {
- $(document).off("click", oneTimeClick);
-
- // unbind watchers
- for (var e in eventBindings) {
- eventBindings[e]();
- }
-
- });
- }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.
-
- };
-})
-
-.directive('onRightClick',function(){
-
- document.oncontextmenu = function (e) {
- if(e.target.hasAttribute('on-right-click')) {
- e.preventDefault();
- e.stopPropagation();
- return false;
- }
- };
-
- return function(scope,el,attrs){
- el.on('contextmenu',function(e){
- e.preventDefault();
- e.stopPropagation();
- scope.$apply(attrs.onRightClick);
- return false;
- });
- };
-})
-
-.directive('onDelayedMouseleave', function ($timeout, $parse) {
- return {
-
- restrict: 'A',
-
- link: function (scope, element, attrs, ctrl) {
- var active = false;
- var fn = $parse(attrs.onDelayedMouseleave);
-
- var leave_f = function(event) {
- var callback = function() {
- fn(scope, {$event:event});
- };
-
- active = false;
- $timeout(function(){
- if(active === false){
- scope.$apply(callback);
- }
- }, 650);
- };
-
- var enter_f = function(event, args){
- active = true;
- };
-
-
- element.on("mouseleave", leave_f);
- element.on("mouseenter", enter_f);
-
- //unsub events
- scope.$on("$destroy", function(){
- element.off("mouseleave", leave_f);
- element.off("mouseenter", enter_f);
- });
- }
- };
- });
-
-/*
-
- http://vitalets.github.io/checklist-model/
-
-*/
-angular.module('umbraco.directives')
-.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
- // contains
- function contains(arr, item) {
- if (angular.isArray(arr)) {
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- return true;
- }
- }
- }
- return false;
- }
-
- // add
- function add(arr, item) {
- arr = angular.isArray(arr) ? arr : [];
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- return arr;
- }
- }
- arr.push(item);
- return arr;
- }
-
- // remove
- function remove(arr, item) {
- if (angular.isArray(arr)) {
- for (var i = 0; i < arr.length; i++) {
- if (angular.equals(arr[i], item)) {
- arr.splice(i, 1);
- break;
- }
- }
- }
- return arr;
- }
-
- // http://stackoverflow.com/a/19228302/1458162
- function postLinkFn(scope, elem, attrs) {
- // compile with `ng-model` pointing to `checked`
- $compile(elem)(scope);
-
- // getter / setter for original model
- var getter = $parse(attrs.checklistModel);
- var setter = getter.assign;
-
- // value added to list
- var value = $parse(attrs.checklistValue)(scope.$parent);
-
- // watch UI checked change
- scope.$watch('checked', function(newValue, oldValue) {
- if (newValue === oldValue) {
- return;
- }
- var current = getter(scope.$parent);
- if (newValue === true) {
- setter(scope.$parent, add(current, value));
- } else {
- setter(scope.$parent, remove(current, value));
- }
- });
-
- // watch original model change
- scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) {
- scope.checked = contains(newArr, value);
- }, true);
- }
-
- return {
- restrict: 'A',
- priority: 1000,
- terminal: true,
- scope: true,
- compile: function(tElement, tAttrs) {
- if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) {
- throw 'checklist-model should be applied to `input[type="checkbox"]`.';
- }
-
- if (!tAttrs.checklistValue) {
- throw 'You should provide `checklist-value`.';
- }
-
- // exclude recursion
- tElement.removeAttr('checklist-model');
-
- // local scope var storing individual checkbox model
- tElement.attr('ng-model', 'checked');
-
- return postLinkFn;
- }
- };
-}]);
-angular.module("umbraco.directives")
-.directive("contenteditable", function() {
-
- return {
- require: "ngModel",
- link: function(scope, element, attrs, ngModel) {
-
- function read() {
- ngModel.$setViewValue(element.html());
- }
-
- ngModel.$render = function() {
- element.html(ngModel.$viewValue || "");
- };
-
-
- element.bind("focus", function(){
-
- var range = document.createRange();
- range.selectNodeContents(element[0]);
-
- var sel = window.getSelection();
- sel.removeAllRanges();
- sel.addRange(range);
-
- });
-
- element.bind("blur keyup change", function() {
- scope.$apply(read);
- });
- }
-
- };
-
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:fixNumber
-* @restrict A
-* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model
-* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug.
-**/
-function fixNumber($parse) {
- return {
- restrict: "A",
- require: "ngModel",
-
- link: function (scope, elem, attrs, ctrl) {
-
- //parse ngModel onload
- var modelVal = scope.$eval(attrs.ngModel);
- if (modelVal) {
- var asNum = parseFloat(modelVal, 10);
- if (!isNaN(asNum)) {
- $parse(attrs.ngModel).assign(scope, asNum);
- }
- }
-
- //always return an int to the model
- ctrl.$parsers.push(function (value) {
- if (value === 0) {
- return 0;
- }
- return parseFloat(value || '', 10);
- });
-
- //always try to format the model value as an int
- ctrl.$formatters.push(function (value) {
- if (angular.isString(value)) {
- return parseFloat(value, 10);
- }
- return value;
- });
-
- //This fixes this angular issue:
- //https://github.com/angular/angular.js/issues/2144
- // which doesn't actually validate the number input properly since the model only changes when a real number is entered
- // but the input box still allows non-numbers to be entered which do not validate (only via html5)
- if (typeof elem.prop('validity') === 'undefined') {
- return;
- }
-
- elem.bind('input', function (e) {
- var validity = elem.prop('validity');
- scope.$apply(function () {
- ctrl.$setValidity('number', !validity.badInput);
- });
- });
- }
- };
-}
-angular.module('umbraco.directives').directive("fixNumber", fixNumber);
-angular.module("umbraco.directives").directive('focusWhen', function ($timeout) {
- return {
- restrict: 'A',
- link: function (scope, elm, attrs, ctrl) {
- attrs.$observe("focusWhen", function (newValue) {
- if (newValue === "true") {
- $timeout(function () {
- elm.focus();
- });
- }
- });
- }
- };
-});
-
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:hexBgColor
-* @restrict A
-* @description Used to set a hex background color on an element, this will detect valid hex and when it is valid it will set the color, otherwise
-* a color will not be set.
-**/
-function hexBgColor() {
- return {
- restrict: "A",
- link: function (scope, element, attr, formCtrl) {
-
- var origColor = null;
- if (attr.hexBgOrig) {
- //set the orig based on the attribute if there is one
- origColor = attr.hexBgOrig;
- }
-
- attr.$observe("hexBgColor", function (newVal) {
- if (newVal) {
- if (!origColor) {
- //get the orig color before changing it
- origColor = element.css("border-color");
- }
- //validate it - test with and without the leading hash.
- if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
- element.css("background-color", "#" + newVal);
- return;
- }
- if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
- element.css("background-color", newVal);
- return;
- }
- }
- element.css("background-color", origColor);
- });
-
- }
- };
-}
-angular.module('umbraco.directives').directive("hexBgColor", hexBgColor);
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:headline
- **/
-angular.module("umbraco.directives")
- .directive('hotkey', function($window, keyboardService, $log) {
-
- return function(scope, el, attrs) {
-
- var options = {};
- var keyCombo = attrs.hotkey;
-
- if (!keyCombo) {
- //support data binding
- keyCombo = scope.$eval(attrs["hotkey"]);
- }
-
- function activate() {
-
- if (keyCombo) {
-
- // disable shortcuts in input fields if keycombo is 1 character
- if (keyCombo.length === 1) {
- options = {
- inputDisabled: true
- };
- }
-
- keyboardService.bind(keyCombo, function() {
-
- var element = $(el);
- var activeElementType = document.activeElement.tagName;
- var clickableElements = ["A", "BUTTON"];
-
- if (element.is("a,div,button,input[type='button'],input[type='submit'],input[type='checkbox']") && !element.is(':disabled')) {
-
- if (element.is(':visible') || attrs.hotkeyWhenHidden) {
-
- if (attrs.hotkeyWhen && attrs.hotkeyWhen === "false") {
- return;
- }
-
- // when keycombo is enter and a link or button has focus - click the link or button instead of using the hotkey
- if (keyCombo === "enter" && clickableElements.indexOf(activeElementType) === 0) {
- document.activeElement.click();
- } else {
- element.click();
- }
-
- }
-
- } else {
- element.focus();
- }
-
- }, options);
-
- el.on('$destroy', function() {
- keyboardService.unbind(keyCombo);
- });
-
- }
-
- }
-
- activate();
-
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:preventDefault
-**/
-angular.module("umbraco.directives")
- .directive('preventDefault', function() {
- return function(scope, element, attrs) {
-
- var enabled = true;
- //check if there's a value for the attribute, if there is and it's false then we conditionally don't
- //prevent default.
- if (attrs.preventDefault) {
- attrs.$observe("preventDefault", function (newVal) {
- enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
- });
- }
-
- $(element).click(function (event) {
- if (event.metaKey || event.ctrlKey) {
- return;
- }
- else {
- if (enabled === true) {
- event.preventDefault();
- }
- }
- });
- };
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:preventEnterSubmit
-* @description prevents a form from submitting when the enter key is pressed on an input field
-**/
-angular.module("umbraco.directives")
- .directive('preventEnterSubmit', function() {
- return function(scope, element, attrs) {
-
- var enabled = true;
- //check if there's a value for the attribute, if there is and it's false then we conditionally don't
- //prevent default.
- if (attrs.preventEnterSubmit) {
- attrs.$observe("preventEnterSubmit", function (newVal) {
- enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
- });
- }
-
- $(element).keypress(function (event) {
- if (event.which === 13) {
- event.preventDefault();
- }
- });
- };
- });
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:resizeToContent
- * @element div
- * @function
- *
- * @description
- * Resize iframe's automatically to fit to the content they contain
- *
- * @example
-
-
-
-
-
- */
-angular.module("umbraco.directives")
- .directive('resizeToContent', function ($window, $timeout) {
- return function (scope, el, attrs) {
- var iframe = el[0];
- var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow;
- if (iframeWin.document.body) {
-
- $timeout(function(){
- var height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight;
- el.height(height);
- }, 3000);
- }
- };
- });
-
-angular.module("umbraco.directives")
- .directive('selectOnFocus', function () {
- return function (scope, el, attrs) {
- $(el).bind("click", function () {
- var editmode = $(el).data("editmode");
- //If editmode is true a click is handled like a normal click
- if (!editmode) {
- //Initial click, select entire text
- this.select();
- //Set the edit mode so subsequent clicks work normally
- $(el).data("editmode", true);
- }
- }).
- bind("blur", function () {
- //Reset on focus lost
- $(el).data("editmode", false);
- });
- };
- });
-
-angular.module("umbraco.directives")
- .directive('umbAutoFocus', function($timeout) {
-
- return function(scope, element, attr){
- var update = function() {
- //if it uses its default naming
- if(element.val() === "" || attr.focusOnFilled){
- element.focus();
- }
- };
-
- $timeout(function() {
- update();
- });
- };
-});
-
-angular.module("umbraco.directives")
- .directive('umbAutoResize', function($timeout) {
- return {
- require: ["^?umbTabs", "ngModel"],
- link: function(scope, element, attr, controllersArr) {
-
- var domEl = element[0];
- var domElType = domEl.type;
- var umbTabsController = controllersArr[0];
- var ngModelController = controllersArr[1];
-
- // IE elements
- var isIEFlag = false;
- var wrapper = angular.element('#umb-ie-resize-input-wrapper');
- var mirror = angular.element('');
-
- function isIE() {
-
- var ua = window.navigator.userAgent;
- var msie = ua.indexOf("MSIE ");
-
- if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./) || navigator.userAgent.match(/Edge\/\d+/)) {
- return true;
- } else {
- return false;
- }
-
- }
-
- function activate() {
-
- // check if browser is Internet Explorere
- isIEFlag = isIE();
-
- // scrollWidth on element does not work in IE on inputs
- // we have to do some dirty dom element copying.
- if (isIEFlag === true && domElType === "text") {
- setupInternetExplorerElements();
- }
-
- }
-
- function setupInternetExplorerElements() {
-
- if (!wrapper.length) {
- wrapper = angular.element('');
- angular.element('body').append(wrapper);
- }
-
- angular.forEach(['fontFamily', 'fontSize', 'fontWeight', 'fontStyle',
- 'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent',
- 'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle',
- 'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight'
- ], function(value) {
- mirror.css(value, element.css(value));
- });
-
- wrapper.append(mirror);
-
- }
-
- function resizeInternetExplorerInput() {
-
- mirror.text(element.val() || attr.placeholder);
- element.css('width', mirror.outerWidth() + 1);
-
- }
-
- function resizeInput() {
-
- if (domEl.scrollWidth !== domEl.clientWidth) {
- if (ngModelController.$modelValue) {
- element.width(domEl.scrollWidth);
- }
- }
-
- if(!ngModelController.$modelValue && attr.placeholder) {
- attr.$set('size', attr.placeholder.length);
- element.width('auto');
- }
-
- }
-
- function resizeTextarea() {
-
- if(domEl.scrollHeight !== domEl.clientHeight) {
-
- element.height(domEl.scrollHeight);
-
- }
-
- }
-
- var update = function(force) {
-
-
- if (force === true) {
-
- if (domElType === "textarea") {
- element.height(0);
- } else if (domElType === "text") {
- element.width(0);
- }
-
- }
-
-
- if (isIEFlag === true && domElType === "text") {
-
- resizeInternetExplorerInput();
-
- } else {
-
- if (domElType === "textarea") {
-
- resizeTextarea();
-
- } else if (domElType === "text") {
-
- resizeInput();
-
- }
-
- }
-
- };
-
- activate();
-
- //listen for tab changes
- if (umbTabsController != null) {
- umbTabsController.onTabShown(function(args) {
- update();
- });
- }
-
- // listen for ng-model changes
- var unbindModelWatcher = scope.$watch(function() {
- return ngModelController.$modelValue;
- }, function(newValue) {
- update(true);
- });
-
- scope.$on('$destroy', function() {
- element.unbind('keyup keydown keypress change', update);
- element.unbind('blur', update(true));
- unbindModelWatcher();
-
- // clean up IE dom element
- if (isIEFlag === true && domElType === "text") {
- mirror.remove();
- }
-
- });
- }
- };
- });
-
-/*
-example usage:
-
-jsonEditing is a string which we edit in a textarea. we try parsing to JSON with each change. when it is valid, propagate model changes via ngModelCtrl
-
-use isolate scope to prevent model propagation when invalid - will update manually. cannot replace with template, or will override ngModelCtrl, and not hide behind facade
-
-will override element type to textarea and add own attribute ngModel tied to jsonEditing
- */
-
-angular.module("umbraco.directives")
- .directive('umbRawModel', function () {
- return {
- restrict: 'A',
- require: 'ngModel',
- template: '',
- replace : true,
- scope: {
- model: '=umbRawModel',
- validateOn:'='
- },
- link: function (scope, element, attrs, ngModelCtrl) {
-
- function setEditing (value) {
- scope.jsonEditing = angular.copy( jsonToString(value));
- }
-
- function updateModel (value) {
- scope.model = stringToJson(value);
- }
-
- function setValid() {
- ngModelCtrl.$setValidity('json', true);
- }
-
- function setInvalid () {
- ngModelCtrl.$setValidity('json', false);
- }
-
- function stringToJson(text) {
- try {
- return angular.fromJson(text);
- } catch (err) {
- setInvalid();
- return text;
- }
- }
-
- function jsonToString(object) {
- // better than JSON.stringify(), because it formats + filters $$hashKey etc.
- // NOTE that this will remove all $-prefixed values
- return angular.toJson(object, true);
- }
-
- function isValidJson(model) {
- var flag = true;
- try {
- angular.fromJson(model);
- } catch (err) {
- flag = false;
- }
- return flag;
- }
-
- //init
- setEditing(scope.model);
-
- var onInputChange = function(newval,oldval){
- if (newval !== oldval) {
- if (isValidJson(newval)) {
- setValid();
- updateModel(newval);
- } else {
- setInvalid();
- }
- }
- };
-
- if(scope.validateOn){
- element.on(scope.validateOn, function(){
- scope.$apply(function(){
- onInputChange(scope.jsonEditing);
- });
- });
- }else{
- //check for changes going out
- scope.$watch('jsonEditing', onInputChange, true);
- }
-
- //check for changes coming in
- scope.$watch('model', function (newval, oldval) {
- if (newval !== oldval) {
- setEditing(newval);
- }
- }, true);
-
- }
- };
- });
-
-(function() {
- 'use strict';
-
- function SelectWhen($timeout) {
-
- function link(scope, el, attr, ctrl) {
-
- attr.$observe("umbSelectWhen", function(newValue) {
- if (newValue === "true") {
- $timeout(function() {
- el.select();
- });
- }
- });
-
- }
-
- var directive = {
- restrict: 'A',
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbSelectWhen', SelectWhen);
-
-})();
-
-angular.module("umbraco.directives")
- .directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) {
- return {
- scope: {
- uniqueId: '=',
- value: '=',
- onClick: '&',
- onFocus: '&',
- onBlur: '&',
- configuration:"=",
- onMediaPickerClick: "=",
- onEmbedClick: "=",
- onMacroPickerClick: "=",
- onLinkPickerClick: "="
- },
- template: "",
- replace: true,
- link: function (scope, element, attrs) {
-
- var initTiny = function () {
-
- //we always fetch the default one, and then override parts with our own
- tinyMceService.configuration().then(function (tinyMceConfig) {
-
-
-
- //config value from general tinymce.config file
- var validElements = tinyMceConfig.validElements;
- var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}];
-
- //These are absolutely required in order for the macros to render inline
- //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
- var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style]";
-
- var invalidElements = tinyMceConfig.inValidElements;
- var plugins = _.map(tinyMceConfig.plugins, function (plugin) {
- if (plugin.useOnFrontend) {
- return plugin.name;
- }
- }).join(" ") + " autoresize";
-
- //config value on the data type
- var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"].join(" | ");
- var stylesheets = [];
-
- var styleFormats = [];
- var await = [];
-
- //queue file loading
- if (typeof (tinymce) === "undefined") {
- await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
- }
-
-
- if(scope.configuration && scope.configuration.toolbar){
- toolbar = scope.configuration.toolbar.join(' | ');
- }
-
-
- if(scope.configuration && scope.configuration.stylesheets){
- angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){
-
- stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + stylesheet + ".css");
- await.push(stylesheetResource.getRulesByName(stylesheet).then(function (rules) {
- angular.forEach(rules, function (rule) {
- var r = {};
- r.title = rule.name;
- if (rule.selector[0] === ".") {
- r.inline = "span";
- r.classes = rule.selector.substring(1);
- }else if (rule.selector[0] === "#") {
- //Even though this will render in the style drop down, it will not actually be applied
- // to the elements, don't think TinyMCE even supports this and it doesn't really make much sense
- // since only one element can have one id.
- r.inline = "span";
- r.attributes = { id: rule.selector.substring(1) };
- }else {
- r.block = rule.selector;
- }
- styleFormats.push(r);
- });
- }));
- });
- }else{
- stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css");
- styleFormats = fallbackStyles;
- }
-
- //stores a reference to the editor
- var tinyMceEditor = null;
- $q.all(await).then(function () {
-
- var uniqueId = scope.uniqueId;
-
- //create a baseline Config to exten upon
- var baseLineConfigObj = {
- mode: "exact",
- skin: "umbraco",
- plugins: plugins,
- valid_elements: validElements,
- invalid_elements: invalidElements,
- extended_valid_elements: extendedValidElements,
- menubar: false,
- statusbar: false,
- relative_urls: false,
- toolbar: toolbar,
- content_css: stylesheets,
- style_formats: styleFormats,
- autoresize_bottom_margin: 0
- };
-
-
- if (tinyMceConfig.customConfig) {
-
- //if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
- // convert it to json instead of having it as a string since this is what tinymce requires
- for (var i in tinyMceConfig.customConfig) {
- var val = tinyMceConfig.customConfig[i];
- if (val) {
- val = val.toString().trim();
- if (val.detectIsJson()) {
- try {
- tinyMceConfig.customConfig[i] = JSON.parse(val);
- //now we need to check if this custom config key is defined in our baseline, if it is we don't want to
- //overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise
- //if it's an object it will overwrite the baseline
- if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) {
- //concat it and below this concat'd array will overwrite the baseline in angular.extend
- tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]);
- }
- }
- catch (e) {
- //cannot parse, we'll just leave it
- }
- }
- }
- }
-
- angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
- }
-
- //set all the things that user configs should not be able to override
- baseLineConfigObj.elements = uniqueId;
- baseLineConfigObj.setup = function (editor) {
-
- //set the reference
- tinyMceEditor = editor;
-
-
- //enable browser based spell checking
- editor.on('init', function (e) {
-
- editor.getBody().setAttribute('spellcheck', true);
-
- //force overflow to hidden to prevent no needed scroll
- editor.getBody().style.overflow = "hidden";
-
- $timeout(function(){
- if(scope.value === null){
- editor.focus();
- }
- }, 400);
-
- });
-
- //when we leave the editor (maybe)
- editor.on('blur', function (e) {
- editor.save();
- angularHelper.safeApply(scope, function () {
- scope.value = editor.getContent();
-
- var _toolbar = $(editor.editorContainer)
- .find(".mce-toolbar");
-
- if(scope.onBlur){
- scope.onBlur();
- }
-
- });
- });
-
- // Focus on editor
- editor.on('focus', function (e) {
- angularHelper.safeApply(scope, function () {
-
- if(scope.onFocus){
- scope.onFocus();
- }
-
- });
- });
-
- // Click on editor
- editor.on('click', function (e) {
- angularHelper.safeApply(scope, function () {
-
- if(scope.onClick){
- scope.onClick();
- }
-
- });
- });
-
- //when buttons modify content
- editor.on('ExecCommand', function (e) {
- editor.save();
- angularHelper.safeApply(scope, function () {
- scope.value = editor.getContent();
- });
- });
-
- // Update model on keypress
- editor.on('KeyUp', function (e) {
- editor.save();
- angularHelper.safeApply(scope, function () {
- scope.value = editor.getContent();
- });
- });
-
- // Update model on change, i.e. copy/pasted text, plugins altering content
- editor.on('SetContent', function (e) {
- if (!e.initial) {
- editor.save();
- angularHelper.safeApply(scope, function () {
- scope.value = editor.getContent();
- });
- }
- });
-
- editor.on('ObjectResized', function (e) {
- var qs = "?width=" + e.width + "&height=" + e.height;
- var srcAttr = $(e.target).attr("src");
- var path = srcAttr.split("?")[0];
- $(e.target).attr("data-mce-src", path + qs);
- });
-
- //Create the insert link plugin
- tinyMceService.createLinkPicker(editor, scope, function(currentTarget, anchorElement){
- if(scope.onLinkPickerClick) {
- scope.onLinkPickerClick(editor, currentTarget, anchorElement);
- }
- });
-
- //Create the insert media plugin
- tinyMceService.createMediaPicker(editor, scope, function(currentTarget, userData){
- if(scope.onMediaPickerClick) {
- scope.onMediaPickerClick(editor, currentTarget, userData);
- }
- });
-
- //Create the embedded plugin
- tinyMceService.createInsertEmbeddedMedia(editor, scope, function(){
- if(scope.onEmbedClick) {
- scope.onEmbedClick(editor);
- }
- });
-
- //Create the insert macro plugin
- tinyMceService.createInsertMacro(editor, scope, function(dialogData){
- if(scope.onMacroPickerClick) {
- scope.onMacroPickerClick(editor, dialogData);
- }
- });
-
- };
-
- /** Loads in the editor */
- function loadTinyMce() {
-
- //we need to add a timeout here, to force a redraw so TinyMCE can find
- //the elements needed
- $timeout(function () {
- tinymce.DOM.events.domLoaded = true;
- tinymce.init(baseLineConfigObj);
- }, 150, false);
- }
-
- loadTinyMce();
-
- //here we declare a special method which will be called whenever the value has changed from the server
- //this is instead of doing a watch on the model.value = faster
- //scope.model.onValueChanged = function (newVal, oldVal) {
- // //update the display val again if it has changed from the server;
- // tinyMceEditor.setContent(newVal, { format: 'raw' });
- // //we need to manually fire this event since it is only ever fired based on loading from the DOM, this
- // // is required for our plugins listening to this event to execute
- // tinyMceEditor.fire('LoadContent', null);
- //};
-
- //listen for formSubmitting event (the result is callback used to remove the event subscription)
- var unsubscribe = scope.$on("formSubmitting", function () {
- //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer
- // we do parse it out on the server side but would be nice to do that on the client side before as well.
- scope.value = tinyMceEditor.getContent();
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
- // element might still be there even after the modal has been hidden.
- scope.$on('$destroy', function () {
- unsubscribe();
- });
-
- });
-
- });
-
- };
-
- initTiny();
-
- }
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbProperty
-* @restrict E
-**/
-angular.module("umbraco.directives.html")
- .directive('umbControlGroup', function (localizationService) {
- return {
- scope: {
- label: "@label",
- description: "@",
- hideLabel: "@",
- alias: "@"
- },
- require: '?^form',
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/html/umb-control-group.html',
- link: function (scope, element, attr, formCtrl) {
-
- scope.formValid = function() {
- if (formCtrl) {
- return formCtrl.$valid;
- }
- //there is no form.
- return true;
- };
-
- if (scope.label && scope.label[0] === "@") {
- scope.labelstring = localizationService.localize(scope.label.substring(1));
- }
- else {
- scope.labelstring = scope.label;
- }
-
- if (scope.description && scope.description[0] === "@") {
- scope.descriptionstring = localizationService.localize(scope.description.substring(1));
- }
- else {
- scope.descriptionstring = scope.description;
- }
-
- }
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbProperty
-* @restrict E
-**/
-angular.module("umbraco.directives.html")
- .directive('umbPane', function () {
- return {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/html/umb-pane.html'
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbPanel
-* @restrict E
-**/
-angular.module("umbraco.directives.html")
- .directive('umbPanel', function($timeout, $log){
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/components/html/umb-panel.html'
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbImageCrop
-* @restrict E
-* @function
-**/
-angular.module("umbraco.directives")
- .directive('umbImageCrop',
- function ($timeout, localizationService, cropperHelper, $log) {
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/imaging/umb-image-crop.html',
- scope: {
- src: '=',
- width: '@',
- height: '@',
- crop: "=",
- center: "=",
- maxSize: '@'
- },
-
- link: function(scope, element, attrs) {
- scope.width = 400;
- scope.height = 320;
-
- scope.dimensions = {
- image: {},
- cropper:{},
- viewport:{},
- margin: 20,
- scale: {
- min: 0.3,
- max: 3,
- current: 1
- }
- };
-
-
- //live rendering of viewport and image styles
- scope.style = function () {
- return {
- 'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px',
- 'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px'
- };
- };
-
-
- //elements
- var $viewport = element.find(".viewport");
- var $image = element.find("img");
- var $overlay = element.find(".overlay");
- var $container = element.find(".crop-container");
-
- //default constraints for drag n drop
- var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin}, };
- scope.constraints = constraints;
-
-
- //set constaints for cropping drag and drop
- var setConstraints = function(){
- constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width;
- constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height;
- };
-
-
- var setDimensions = function(originalImage){
- originalImage.width("auto");
- originalImage.height("auto");
-
- var image = {};
- image.originalWidth = originalImage.width();
- image.originalHeight = originalImage.height();
-
- image.width = image.originalWidth;
- image.height = image.originalHeight;
- image.left = originalImage[0].offsetLeft;
- image.top = originalImage[0].offsetTop;
-
- scope.dimensions.image = image;
-
- //unscaled editor size
- //var viewPortW = $viewport.width();
- //var viewPortH = $viewport.height();
- var _viewPortW = parseInt(scope.width, 10);
- var _viewPortH = parseInt(scope.height, 10);
-
- //if we set a constraint we will scale it down if needed
- if(scope.maxSize){
- var ratioCalculation = cropperHelper.scaleToMaxSize(
- _viewPortW,
- _viewPortH,
- scope.maxSize);
-
- //so if we have a max size, override the thumb sizes
- _viewPortW = ratioCalculation.width;
- _viewPortH = ratioCalculation.height;
- }
-
- scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin;
- scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin;
- scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin;
- scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
- };
-
-
- //when loading an image without any crop info, we center and fit it
- var resizeImageToEditor = function(){
- //returns size fitting the cropper
- var size = cropperHelper.calculateAspectRatioFit(
- scope.dimensions.image.width,
- scope.dimensions.image.height,
- scope.dimensions.cropper.width,
- scope.dimensions.cropper.height,
- true);
-
- //sets the image size and updates the scope
- scope.dimensions.image.width = size.width;
- scope.dimensions.image.height = size.height;
-
- //calculate the best suited ratios
- scope.dimensions.scale.min = size.ratio;
- scope.dimensions.scale.max = 2;
- scope.dimensions.scale.current = size.ratio;
-
- //center the image
- var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper);
- scope.dimensions.top = position.top;
- scope.dimensions.left = position.left;
-
- setConstraints();
- };
-
- //resize to a given ratio
- var resizeImageToScale = function(ratio){
- //do stuff
- var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
- scope.dimensions.image.width = size.width;
- scope.dimensions.image.height = size.height;
-
- setConstraints();
- validatePosition(scope.dimensions.image.left, scope.dimensions.image.top);
- };
-
- //resize the image to a predefined crop coordinate
- var resizeImageToCrop = function(){
- scope.dimensions.image = cropperHelper.convertToStyle(
- scope.crop,
- {width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight},
- scope.dimensions.cropper,
- scope.dimensions.margin);
-
- var ratioCalculation = cropperHelper.calculateAspectRatioFit(
- scope.dimensions.image.originalWidth,
- scope.dimensions.image.originalHeight,
- scope.dimensions.cropper.width,
- scope.dimensions.cropper.height,
- true);
-
- scope.dimensions.scale.current = scope.dimensions.image.ratio;
-
- //min max based on original width/height
- scope.dimensions.scale.min = ratioCalculation.ratio;
- scope.dimensions.scale.max = 2;
- };
-
-
-
- var validatePosition = function(left, top){
- if(left > constraints.left.max)
- {
- left = constraints.left.max;
- }
-
- if(left <= constraints.left.min){
- left = constraints.left.min;
- }
-
- if(top > constraints.top.max)
- {
- top = constraints.top.max;
- }
- if(top <= constraints.top.min){
- top = constraints.top.min;
- }
-
- if(scope.dimensions.image.left !== left){
- scope.dimensions.image.left = left;
- }
-
- if(scope.dimensions.image.top !== top){
- scope.dimensions.image.top = top;
- }
- };
-
-
- //sets scope.crop to the recalculated % based crop
- var calculateCropBox = function(){
- scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin);
- };
-
-
- //Drag and drop positioning, using jquery ui draggable
- var onStartDragPosition, top, left;
- $overlay.draggable({
- drag: function(event, ui) {
- scope.$apply(function(){
- validatePosition(ui.position.left, ui.position.top);
- });
- },
- stop: function(event, ui){
- scope.$apply(function(){
- //make sure that every validates one more time...
- validatePosition(ui.position.left, ui.position.top);
-
- calculateCropBox();
- scope.dimensions.image.rnd = Math.random();
- });
- }
- });
-
-
-
- var init = function(image){
- scope.loaded = false;
-
- //set dimensions on image, viewport, cropper etc
- setDimensions(image);
-
- //if we have a crop already position the image
- if(scope.crop){
- resizeImageToCrop();
- }else{
- resizeImageToEditor();
- }
-
- //sets constaints for the cropper
- setConstraints();
- scope.loaded = true;
- };
-
-
- /// WATCHERS ////
- scope.$watchCollection('[width, height]', function(newValues, oldValues){
- //we have to reinit the whole thing if
- //one of the external params changes
- if(newValues !== oldValues){
- setDimensions($image);
- setConstraints();
- }
- });
-
- var throttledResizing = _.throttle(function(){
- resizeImageToScale(scope.dimensions.scale.current);
- calculateCropBox();
- }, 100);
-
-
- //happens when we change the scale
- scope.$watch("dimensions.scale.current", function(){
- if(scope.loaded){
- throttledResizing();
- }
- });
-
- //ie hack
- if(window.navigator.userAgent.indexOf("MSIE ")){
- var ranger = element.find("input");
- ranger.bind("change",function(){
- scope.$apply(function(){
- scope.dimensions.scale.current = ranger.val();
- });
- });
- }
-
- //// INIT /////
- $image.load(function(){
- $timeout(function(){
- init($image);
- });
- });
- }
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbCropsy
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-angular.module("umbraco.directives")
- .directive('umbImageGravity', function ($timeout, localizationService, $log) {
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/imaging/umb-image-gravity.html',
- scope: {
- src: '=',
- center: "="
- },
- link: function(scope, element, attrs) {
-
- //Internal values for keeping track of the dot and the size of the editor
- scope.dimensions = {
- width: 0,
- height: 0,
- left: 0,
- top: 0
- };
-
- //elements
- var $viewport = element.find(".viewport");
- var $image = element.find("img");
- var $overlay = element.find(".overlay");
-
- scope.style = function () {
- if(scope.dimensions.width <= 0){
- setDimensions();
- }
-
- return {
- 'top': scope.dimensions.top + 'px',
- 'left': scope.dimensions.left + 'px'
- };
- };
-
- var setDimensions = function(){
- scope.dimensions.width = $image.width();
- scope.dimensions.height = $image.height();
-
- if(scope.center){
- scope.dimensions.left = scope.center.left * scope.dimensions.width -10;
- scope.dimensions.top = scope.center.top * scope.dimensions.height -10;
- }else{
- scope.center = { left: 0.5, top: 0.5 };
- }
- };
-
- var calculateGravity = function(){
- scope.dimensions.left = $overlay[0].offsetLeft;
- scope.dimensions.top = $overlay[0].offsetTop;
-
- scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width;
- scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height;
- };
-
- var lazyEndEvent = _.debounce(function(){
- scope.$apply(function(){
- scope.$emit("imageFocalPointStop");
- });
- }, 2000);
-
-
- //Drag and drop positioning, using jquery ui draggable
- //TODO ensure that the point doesnt go outside the box
- $overlay.draggable({
- containment: "parent",
- start: function(){
- scope.$apply(function(){
- scope.$emit("imageFocalPointStart");
- });
- },
- stop: function() {
- scope.$apply(function(){
- calculateGravity();
- });
-
- lazyEndEvent();
- }
- });
-
- //// INIT /////
- $image.load(function(){
- $timeout(function(){
- setDimensions();
- });
- });
- }
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbCropsy
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-angular.module("umbraco.directives")
- .directive('umbImageThumbnail',
- function ($timeout, localizationService, cropperHelper, $log) {
- return {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/imaging/umb-image-thumbnail.html',
-
- scope: {
- src: '=',
- width: '@',
- height: '@',
- center: "=",
- crop: "=",
- maxSize: '@'
- },
-
- link: function(scope, element, attrs) {
- //// INIT /////
- var $image = element.find("img");
-
- $image.load(function(){
- $timeout(function(){
- $image.width("auto");
- $image.height("auto");
-
- scope.image = {};
- scope.image.width = $image[0].width;
- scope.image.height = $image[0].height;
-
- //we force a lower thumbnail size to fit the max size
- //we do not compare to the image dimensions, but the thumbs
- if(scope.maxSize){
- var ratioCalculation = cropperHelper.calculateAspectRatioFit(
- scope.width,
- scope.height,
- scope.maxSize,
- scope.maxSize,
- false);
-
- //so if we have a max size, override the thumb sizes
- scope.width = ratioCalculation.width;
- scope.height = ratioCalculation.height;
- }
-
- setPreviewStyle();
- });
- });
-
- /// WATCHERS ////
- scope.$watchCollection('[crop, center]', function(newValues, oldValues){
- //we have to reinit the whole thing if
- //one of the external params changes
- setPreviewStyle();
- });
-
- scope.$watch("center", function(){
- setPreviewStyle();
- }, true);
-
- function setPreviewStyle(){
- if(scope.crop && scope.image){
- scope.preview = cropperHelper.convertToStyle(
- scope.crop,
- scope.image,
- {width: scope.width, height: scope.height},
- 0);
- }else if(scope.image){
-
- //returns size fitting the cropper
- var p = cropperHelper.calculateAspectRatioFit(
- scope.image.width,
- scope.image.height,
- scope.width,
- scope.height,
- true);
-
-
- if(scope.center){
- var xy = cropperHelper.alignToCoordinates(p, scope.center, {width: scope.width, height: scope.height});
- p.top = xy.top;
- p.left = xy.left;
- }else{
-
- }
-
- p.position = "absolute";
- scope.preview = p;
- }
- }
- }
- };
- });
-
-angular.module("umbraco.directives")
-
- .directive('localize', function ($log, localizationService) {
- return {
- restrict: 'E',
- scope:{
- key: '@'
- },
- replace: true,
-
- link: function (scope, element, attrs) {
- var key = scope.key;
- localizationService.localize(key).then(function(value){
- element.html(value);
- });
- }
- };
- })
-
- .directive('localize', function ($log, localizationService) {
- return {
- restrict: 'A',
- link: function (scope, element, attrs) {
- var keys = attrs.localize.split(',');
-
- angular.forEach(keys, function(value, key){
- var attr = element.attr(value);
-
- if(attr){
- if(attr[0] === '@'){
-
- var t = localizationService.tokenize(attr.substring(1), scope);
- localizationService.localize(t.key, t.tokens).then(function(val){
- element.attr(value, val);
- });
-
- }
- }
- });
- }
- };
-
- });
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:umbNotifications
- */
-
-(function() {
- 'use strict';
-
- function NotificationDirective(notificationsService) {
-
- function link(scope, el, attr, ctrl) {
-
- //subscribes to notifications in the notification service
- scope.notifications = notificationsService.current;
- scope.$watch('notificationsService.current', function (newVal, oldVal, scope) {
- if (newVal) {
- scope.notifications = newVal;
- }
- });
-
- }
-
- var directive = {
- restrict: "E",
- replace: true,
- templateUrl: 'views/components/notifications/umb-notifications.html',
- link: link
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbNotifications', NotificationDirective);
-
-})();
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:umbProperty
- * @restrict E
- **/
-
-(function() {
- 'use strict';
-
- function OverlayDirective($timeout, formHelper, overlayHelper, localizationService) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.directive = {
- enableConfirmButton: false
- };
-
- var overlayNumber = 0;
- var numberOfOverlays = 0;
- var isRegistered = false;
-
- var modelCopy = {};
-
- function activate() {
-
- setView();
-
- setButtonText();
-
- modelCopy = makeModelCopy(scope.model);
-
- $timeout(function() {
-
- if (scope.position === "target") {
- setTargetPosition();
- }
-
- // this has to be done inside a timeout to ensure the destroy
- // event on other overlays is run before registering a new one
- registerOverlay();
-
- setOverlayIndent();
-
- });
-
- }
-
- function setView() {
-
- if (scope.view) {
-
- if (scope.view.indexOf(".html") === -1) {
- var viewAlias = scope.view.toLowerCase();
- scope.view = "views/common/overlays/" + viewAlias + "/" + viewAlias + ".html";
- }
-
- }
-
- }
-
- function setButtonText() {
- if (!scope.model.closeButtonLabelKey && !scope.model.closeButtonLabel) {
- scope.model.closeButtonLabel = localizationService.localize("general_close");
- }
- if (!scope.model.submitButtonLabelKey && !scope.model.submitButtonLabel) {
- scope.model.submitButtonLabel = localizationService.localize("general_submit");
- }
- }
-
- function registerOverlay() {
-
- overlayNumber = overlayHelper.registerOverlay();
-
- $(document).bind("keydown.overlay-" + overlayNumber, function(event) {
-
- if (event.which === 27) {
-
- numberOfOverlays = overlayHelper.getNumberOfOverlays();
-
- if(numberOfOverlays === overlayNumber) {
- scope.closeOverLay();
- }
-
- event.preventDefault();
- }
-
- if (event.which === 13) {
-
- numberOfOverlays = overlayHelper.getNumberOfOverlays();
-
- if(numberOfOverlays === overlayNumber) {
-
- var activeElementType = document.activeElement.tagName;
- var clickableElements = ["A", "BUTTON"];
- var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter");
-
- if(clickableElements.indexOf(activeElementType) === 0) {
- document.activeElement.click();
- event.preventDefault();
- } else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
-
-
- } else {
- scope.$apply(function () {
- scope.submitForm(scope.model);
- });
- event.preventDefault();
- }
-
- }
-
- }
-
- });
-
- isRegistered = true;
-
- }
-
- function unregisterOverlay() {
-
- if(isRegistered) {
-
- overlayHelper.unregisterOverlay();
-
- $(document).unbind("keydown.overlay-" + overlayNumber);
-
- isRegistered = false;
- }
-
- }
-
- function makeModelCopy(object) {
-
- var newObject = {};
-
- for (var key in object) {
- if (key !== "event") {
- newObject[key] = angular.copy(object[key]);
- }
- }
-
- return newObject;
-
- }
-
- function setOverlayIndent() {
-
- var overlayIndex = overlayNumber - 1;
- var indentSize = overlayIndex * 20;
- var overlayWidth = el.context.clientWidth;
-
- el.css('width', overlayWidth - indentSize);
-
- if(scope.position === "center" || scope.position === "target") {
- var overlayTopPosition = el.context.offsetTop;
- el.css('top', overlayTopPosition + indentSize);
- }
-
- }
-
- function setTargetPosition() {
-
- var container = $("#contentwrapper");
- var containerLeft = container[0].offsetLeft;
- var containerRight = containerLeft + container[0].offsetWidth;
- var containerTop = container[0].offsetTop;
- var containerBottom = containerTop + container[0].offsetHeight;
-
- var mousePositionClickX = null;
- var mousePositionClickY = null;
- var elementHeight = null;
- var elementWidth = null;
-
- var position = {
- right: "inherit",
- left: "inherit",
- top: "inherit",
- bottom: "inherit"
- };
-
- // if mouse click position is know place element with mouse in center
- if (scope.model.event && scope.model.event) {
-
- // click position
- mousePositionClickX = scope.model.event.pageX;
- mousePositionClickY = scope.model.event.pageY;
-
- // element size
- elementHeight = el.context.clientHeight;
- elementWidth = el.context.clientWidth;
-
- // move element to this position
- position.left = mousePositionClickX - (elementWidth / 2);
- position.top = mousePositionClickY - (elementHeight / 2);
-
- // check to see if element is outside screen
- // outside right
- if (position.left + elementWidth > containerRight) {
- position.right = 10;
- position.left = "inherit";
- }
-
- // outside bottom
- if (position.top + elementHeight > containerBottom) {
- position.bottom = 10;
- position.top = "inherit";
- }
-
- // outside left
- if (position.left < containerLeft) {
- position.left = containerLeft + 10;
- position.right = "inherit";
- }
-
- // outside top
- if (position.top < containerTop) {
- position.top = 10;
- position.bottom = "inherit";
- }
-
- el.css(position);
-
- }
-
- }
-
- scope.submitForm = function(model) {
- if(scope.model.submit) {
- if (formHelper.submitForm({scope: scope})) {
- formHelper.resetForm({ scope: scope });
-
- if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) {
- scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
- } else {
- unregisterOverlay();
- scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
- }
-
- }
- }
- };
-
- scope.cancelConfirmSubmit = function() {
- scope.model.confirmSubmit.show = false;
- };
-
- scope.closeOverLay = function() {
-
- unregisterOverlay();
-
- if (scope.model.close) {
- scope.model = modelCopy;
- scope.model.close(scope.model);
- } else {
- scope.model.show = false;
- scope.model = null;
- }
-
- };
-
- // angular does not support ng-show on custom directives
- // width isolated scopes. So we have to make our own.
- if (attr.hasOwnProperty("ngShow")) {
- scope.$watch("ngShow", function(value) {
- if (value) {
- el.show();
- activate();
- } else {
- unregisterOverlay();
- el.hide();
- }
- });
- } else {
- activate();
- }
-
- scope.$on('$destroy', function(){
- unregisterOverlay();
- });
-
- }
-
- var directive = {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/overlays/umb-overlay.html',
- scope: {
- ngShow: "=",
- model: "=",
- view: "=",
- position: "@"
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbOverlay', OverlayDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function OverlayBackdropDirective(overlayHelper) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.numberOfOverlays = 0;
-
- scope.$watch(function(){
- return overlayHelper.getNumberOfOverlays();
- }, function (newValue) {
- scope.numberOfOverlays = newValue;
- });
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/overlays/umb-overlay-backdrop.html',
- link: link
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbProperty
-* @restrict E
-**/
-angular.module("umbraco.directives")
- .directive('umbProperty', function (umbPropEditorHelper) {
- return {
- scope: {
- property: "="
- },
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/property/umb-property.html',
- link: function(scope) {
- scope.propertyAlias = Umbraco.Sys.ServerVariables.isDebuggingEnabled === true ? scope.property.alias : null;
- },
- //Define a controller for this directive to expose APIs to other directives
- controller: function ($scope, $timeout) {
-
- var self = this;
-
- //set the API properties/methods
-
- self.property = $scope.property;
- self.setPropertyError = function(errorMsg) {
- $scope.property.propertyErrorMessage = errorMsg;
- };
- }
- };
- });
-/**
-* @ngdoc directive
-* @function
-* @name umbraco.directives.directive:umbPropertyEditor
-* @requires formController
-* @restrict E
-**/
-
-//share property editor directive function
-var _umbPropertyEditor = function (umbPropEditorHelper) {
- return {
- scope: {
- model: "=",
- isPreValue: "@",
- preview: "@"
- },
-
- require: "^form",
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/property/umb-property-editor.html',
- link: function (scope, element, attrs, ctrl) {
-
- //we need to copy the form controller val to our isolated scope so that
- //it get's carried down to the child scopes of this!
- //we'll also maintain the current form name.
- scope[ctrl.$name] = ctrl;
-
- if(!scope.model.alias){
- scope.model.alias = Math.random().toString(36).slice(2);
- }
-
- scope.$watch("model.view", function(val){
- scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue);
- });
- }
- };
- };
-
-//Preffered is the umb-property-editor as its more explicit - but we keep umb-editor for backwards compat
-angular.module("umbraco.directives").directive('umbPropertyEditor', _umbPropertyEditor);
-angular.module("umbraco.directives").directive('umbEditor', _umbPropertyEditor);
-
-angular.module("umbraco.directives.html")
- .directive('umbPropertyGroup', function () {
- return {
- transclude: true,
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/property/umb-property-group.html'
- };
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTab
-* @restrict E
-**/
-angular.module("umbraco.directives")
-.directive('umbTab', function ($parse, $timeout) {
- return {
- restrict: 'E',
- replace: true,
- transclude: 'true',
- templateUrl: 'views/components/tabs/umb-tab.html'
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTabs
-* @restrict A
-* @description Used to bind to bootstrap tab events so that sub directives can use this API to listen to tab changes
-**/
-angular.module("umbraco.directives")
-.directive('umbTabs', function () {
- return {
- restrict: 'A',
- controller: function ($scope, $element, $attrs) {
-
- var callbacks = [];
- this.onTabShown = function(cb) {
- callbacks.push(cb);
- };
-
- function tabShown(event) {
-
- var curr = $(event.target); // active tab
- var prev = $(event.relatedTarget); // previous tab
-
- for (var c in callbacks) {
- callbacks[c].apply(this, [{current: curr, previous: prev}]);
- }
- }
-
- //NOTE: it MUST be done this way - binding to an ancestor element that exists
- // in the DOM to bind to the dynamic elements that will be created.
- // It would be nicer to create this event handler as a directive for which child
- // directives can attach to.
- $element.on('shown', '.nav-tabs a', tabShown);
-
- //ensure to unregister
- $scope.$on('$destroy', function () {
- $element.off('shown', '.nav-tabs a', tabShown);
-
- for (var c in callbacks) {
- delete callbacks[c];
- }
- callbacks = null;
- });
- }
- };
-});
-(function() {
- 'use strict';
-
- function UmbTabsContentDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.view = attr.view;
-
- }
-
- var directive = {
- restrict: "E",
- replace: true,
- transclude: 'true',
- templateUrl: "views/components/tabs/umb-tabs-content.html",
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbTabsContent', UmbTabsContentDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function UmbTabsNavDirective($timeout) {
-
- function link(scope, el, attr) {
-
- function activate() {
-
- $timeout(function () {
-
- //use bootstrap tabs API to show the first one
- el.find("a:first").tab('show');
-
- //enable the tab drop
- el.tabdrop();
-
- });
-
- }
-
- var unbindModelWatch = scope.$watch('model', function(newValue, oldValue){
-
- activate();
-
- });
-
-
- scope.$on('$destroy', function () {
-
- //ensure to destroy tabdrop (unbinds window resize listeners)
- el.tabdrop("destroy");
-
- unbindModelWatch();
-
- });
-
- }
-
- var directive = {
- restrict: "E",
- replace: true,
- templateUrl: "views/components/tabs/umb-tabs-nav.html",
- scope: {
- model: "=",
- tabdrop: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbTabsNav', UmbTabsNavDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTree
-* @restrict E
-**/
-function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificationsService, $timeout, userService) {
-
- return {
- restrict: 'E',
- replace: true,
- terminal: false,
-
- scope: {
- section: '@',
- treealias: '@',
- hideoptions: '@',
- hideheader: '@',
- cachekey: '@',
- isdialog: '@',
- //Custom query string arguments to pass in to the tree as a string, example: "startnodeid=123&something=value"
- customtreeparams: '@',
- eventhandler: '=',
- enablecheckboxes: '@',
- enablelistviewsearch: '@'
- },
-
- compile: function(element, attrs) {
- //config
- //var showheader = (attrs.showheader !== 'false');
- var hideoptions = (attrs.hideoptions === 'true') ? "hide-options" : "";
- var template = '- ';
- template += '';
- template += '' +
- '
' +
- '
';
-
- element.replaceWith(template);
-
- return function(scope, elem, attr, controller) {
-
- //flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should
- // re-load the tree again. For example, if we hover over 'content' the content tree is shown. Then we hover
- // outside of the tree and the tree 'un-loads'. When we re-hover over 'content', we don't want to re-load the
- // entire tree again since we already still have it in memory. Of course if the section is different we will
- // reload it. This saves a lot on processing if someone is navigating in and out of the same section many times
- // since it saves on data retreival and DOM processing.
- var lastSection = "";
-
- //setup a default internal handler
- if (!scope.eventhandler) {
- scope.eventhandler = $({});
- }
-
- //flag to enable/disable delete animations
- var deleteAnimations = false;
-
-
- /** Helper function to emit tree events */
- function emitEvent(eventName, args) {
- if (scope.eventhandler) {
- $(scope.eventhandler).trigger(eventName, args);
- }
- }
-
- /** This will deleteAnimations to true after the current digest */
- function enableDeleteAnimations() {
- //do timeout so that it re-enables them after this digest
- $timeout(function () {
- //enable delete animations
- deleteAnimations = true;
- }, 0, false);
- }
-
-
- /*this is the only external interface a tree has */
- function setupExternalEvents() {
- if (scope.eventhandler) {
-
- scope.eventhandler.clearCache = function(section) {
- treeService.clearCache({ section: section });
- };
-
- scope.eventhandler.load = function(section) {
- scope.section = section;
- loadTree();
- };
-
- scope.eventhandler.reloadNode = function(node) {
-
- if (!node) {
- node = scope.currentNode;
- }
-
- if (node) {
- scope.loadChildren(node, true);
- }
- };
-
- /**
- Used to do the tree syncing. If the args.tree is not specified we are assuming it has been
- specified previously using the _setActiveTreeType
- */
- scope.eventhandler.syncTree = function(args) {
- if (!args) {
- throw "args cannot be null";
- }
- if (!args.path) {
- throw "args.path cannot be null";
- }
-
- var deferred = $q.defer();
-
- //this is super complex but seems to be working in other places, here we're listening for our
- // own events, once the tree is sycned we'll resolve our promise.
- scope.eventhandler.one("treeSynced", function (e, syncArgs) {
- deferred.resolve(syncArgs);
- });
-
- //this should normally be set unless it is being called from legacy
- // code, so set the active tree type before proceeding.
- if (args.tree) {
- loadActiveTree(args.tree);
- }
-
- if (angular.isString(args.path)) {
- args.path = args.path.replace('"', '').split(',');
- }
-
- //reset current node selection
- //scope.currentNode = null;
-
- //Filter the path for root node ids (we don't want to pass in -1 or 'init')
-
- args.path = _.filter(args.path, function (item) { return (item !== "init" && item !== "-1"); });
-
- //Once those are filtered we need to check if the current user has a special start node id,
- // if they do, then we're going to trim the start of the array for anything found from that start node
- // and previous so that the tree syncs properly. The tree syncs from the top down and if there are parts
- // of the tree's path in there that don't actually exist in the dom/model then syncing will not work.
-
- userService.getCurrentUser().then(function(userData) {
-
- var startNodes = [userData.startContentId, userData.startMediaId];
- _.each(startNodes, function (i) {
- var found = _.find(args.path, function (p) {
- return String(p) === String(i);
- });
- if (found) {
- args.path = args.path.splice(_.indexOf(args.path, found));
- }
- });
-
-
- loadPath(args.path, args.forceReload, args.activate);
-
- });
-
-
-
- return deferred.promise;
- };
-
- /**
- Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
- have to set an active tree and then sync, the new API does this in one method by using syncTree.
- loadChildren is optional but if it is set, it will set the current active tree and load the root
- node's children - this is synonymous with the legacy refreshTree method - again should not be used
- and should only be used for the legacy code to work.
- */
- scope.eventhandler._setActiveTreeType = function(treeAlias, loadChildren) {
- loadActiveTree(treeAlias, loadChildren);
- };
- }
- }
-
-
- //helper to load a specific path on the active tree as soon as its ready
- function loadPath(path, forceReload, activate) {
-
- if (scope.activeTree) {
- syncTree(scope.activeTree, path, forceReload, activate);
- }
- else {
- scope.eventhandler.one("activeTreeLoaded", function (e, args) {
- syncTree(args.tree, path, forceReload, activate);
- });
- }
- }
-
-
- //given a tree alias, this will search the current section tree for the specified tree alias and
- //set that to the activeTree
- //NOTE: loadChildren is ONLY used for legacy purposes, do not use this when syncing the tree as it will cause problems
- // since there will be double request and event handling operations.
- function loadActiveTree(treeAlias, loadChildren) {
- if (!treeAlias) {
- return;
- }
-
- scope.activeTree = undefined;
-
- function doLoad(tree) {
- var childrenAndSelf = [tree].concat(tree.children);
- scope.activeTree = _.find(childrenAndSelf, function (node) {
- if(node && node.metaData && node.metaData.treeAlias) {
- return node.metaData.treeAlias.toUpperCase() === treeAlias.toUpperCase();
- }
- return false;
- });
-
- if (!scope.activeTree) {
- throw "Could not find the tree " + treeAlias + ", activeTree has not been set";
- }
-
- //This is only used for the legacy tree method refreshTree!
- if (loadChildren) {
- scope.activeTree.expanded = true;
- scope.loadChildren(scope.activeTree, false).then(function() {
- emitEvent("activeTreeLoaded", { tree: scope.activeTree });
- });
- }
- else {
- emitEvent("activeTreeLoaded", { tree: scope.activeTree });
- }
- }
-
- if (scope.tree) {
- doLoad(scope.tree.root);
- }
- else {
- scope.eventhandler.one("treeLoaded", function(e, args) {
- doLoad(args.tree.root);
- });
- }
- }
-
-
- /** Method to load in the tree data */
-
- function loadTree() {
- if (!scope.loading && scope.section) {
- scope.loading = true;
-
- //anytime we want to load the tree we need to disable the delete animations
- deleteAnimations = false;
-
- //default args
- var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false };
-
- //add the extra query string params if specified
- if (scope.customtreeparams) {
- args["queryString"] = scope.customtreeparams;
- }
-
- treeService.getTree(args)
- .then(function(data) {
- //set the data once we have it
- scope.tree = data;
-
- enableDeleteAnimations();
-
- scope.loading = false;
-
- //set the root as the current active tree
- scope.activeTree = scope.tree.root;
- emitEvent("treeLoaded", { tree: scope.tree });
- emitEvent("treeNodeExpanded", { tree: scope.tree, node: scope.tree.root, children: scope.tree.root.children });
-
- }, function(reason) {
- scope.loading = false;
- notificationsService.error("Tree Error", reason);
- });
- }
- }
-
- /** syncs the tree, the treeNode can be ANY tree node in the tree that requires syncing */
- function syncTree(treeNode, path, forceReload, activate) {
-
- deleteAnimations = false;
-
- treeService.syncTree({
- node: treeNode,
- path: path,
- forceReload: forceReload
- }).then(function (data) {
-
- if (activate === undefined || activate === true) {
- scope.currentNode = data;
- }
-
- emitEvent("treeSynced", { node: data, activate: activate });
-
- enableDeleteAnimations();
- });
-
- }
-
- scope.selectEnabledNodeClass = function (node) {
- return node ?
- node.selected ?
- 'icon umb-tree-icon sprTree icon-check blue temporary' :
- '' :
- '';
- };
-
- /** method to set the current animation for the node.
- * This changes dynamically based on if we are changing sections or just loading normal tree data.
- * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations.
- */
- scope.animation = function() {
- if (deleteAnimations && scope.tree && scope.tree.root && scope.tree.root.expanded) {
- return { leave: 'tree-node-delete-leave' };
- }
- else {
- return {};
- }
- };
-
- /* helper to force reloading children of a tree node */
- scope.loadChildren = function(node, forceReload) {
- var deferred = $q.defer();
-
- //emit treeNodeExpanding event, if a callback object is set on the tree
- emitEvent("treeNodeExpanding", { tree: scope.tree, node: node });
-
- //standardising
- if (!node.children) {
- node.children = [];
- }
-
- if (forceReload || (node.hasChildren && node.children.length === 0)) {
- //get the children from the tree service
- treeService.loadNodeChildren({ node: node, section: scope.section })
- .then(function(data) {
- //emit expanded event
- emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data });
-
- enableDeleteAnimations();
-
- deferred.resolve(data);
- });
- }
- else {
- emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: node.children });
- node.expanded = true;
-
- enableDeleteAnimations();
-
- deferred.resolve(node.children);
- }
-
- return deferred.promise;
- };
-
- /**
- Method called when the options button next to the root node is called.
- The tree doesnt know about this, so it raises an event to tell the parent controller
- about it.
- */
- scope.options = function(n, ev) {
- emitEvent("treeOptionsClick", { element: elem, node: n, event: ev });
- };
-
- /**
- Method called when an item is clicked in the tree, this passes the
- DOM element, the tree node object and the original click
- and emits it as a treeNodeSelect element if there is a callback object
- defined on the tree
- */
- scope.select = function (n, ev) {
- //on tree select we need to remove the current node -
- // whoever handles this will need to make sure the correct node is selected
- //reset current node selection
- scope.currentNode = null;
-
- emitEvent("treeNodeSelect", { element: elem, node: n, event: ev });
- };
-
- scope.altSelect = function(n, ev) {
- emitEvent("treeNodeAltSelect", { element: elem, tree: scope.tree, node: n, event: ev });
- };
-
- //watch for section changes
- scope.$watch("section", function(newVal, oldVal) {
-
- if (!scope.tree) {
- loadTree();
- }
-
- if (!newVal) {
- //store the last section loaded
- lastSection = oldVal;
- }
- else if (newVal !== oldVal && newVal !== lastSection) {
- //only reload the tree data and Dom if the newval is different from the old one
- // and if the last section loaded is different from the requested one.
- loadTree();
-
- //store the new section to be loaded as the last section
- //clear any active trees to reset lookups
- lastSection = newVal;
- }
- });
-
- setupExternalEvents();
- loadTree();
- };
- }
- };
-}
-
-angular.module("umbraco.directives").directive('umbTree', umbTreeDirective);
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:umbTreeItem
- * @element li
- * @function
- *
- * @description
- * Renders a list item, representing a single node in the tree.
- * Includes element to toggle children, and a menu toggling button
- *
- * **note:** This directive is only used internally in the umbTree directive
- *
- * @example
-
-
-
-
-
- */
-angular.module("umbraco.directives")
-.directive('umbTreeItem', function ($compile, $http, $templateCache, $interpolate, $log, $location, $rootScope, $window, treeService, $timeout, localizationService) {
- return {
- restrict: 'E',
- replace: true,
-
- scope: {
- section: '@',
- eventhandler: '=',
- currentNode: '=',
- node: '=',
- tree: '='
- },
-
- //TODO: Remove more of the binding from this template and move the DOM manipulation to be manually done in the link function,
- // this will greatly improve performance since there's potentially a lot of nodes being rendered = a LOT of watches!
-
- template: '' +
- '' +
- //NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
- //'
' +
- '
' +
- '
' +
- '
' +
- //NOTE: These are the 'option' elipses
- '
' +
- '
' +
- '
' +
- '',
-
- link: function (scope, element, attrs) {
-
- localizationService.localize("general_search").then(function (value) {
- scope.searchAltText = value;
- });
-
- //flag to enable/disable delete animations, default for an item is true
- var deleteAnimations = true;
-
- // Helper function to emit tree events
- function emitEvent(eventName, args) {
-
- if (scope.eventhandler) {
- $(scope.eventhandler).trigger(eventName, args);
- }
- }
-
- // updates the node's DOM/styles
- function setupNodeDom(node, tree) {
-
- //get the first div element
- element.children(":first")
- //set the padding
- .css("padding-left", (node.level * 20) + "px");
-
- //remove first 'ins' if there is no children
- //show/hide last 'ins' depending on children
- if (!node.hasChildren) {
- element.find("ins:first").remove();
- element.find("ins").last().hide();
- }
- else {
- element.find("ins").last().show();
- }
-
- var icon = element.find("i:first");
- icon.addClass(node.cssClass);
- icon.attr("title", node.routePath);
-
- element.find("a:first").text(node.name);
-
- if (!node.menuUrl) {
- element.find("a.umb-options").remove();
- }
-
- if (node.style) {
- element.find("i:first").attr("style", node.style);
- }
- }
-
- //This will deleteAnimations to true after the current digest
- function enableDeleteAnimations() {
- //do timeout so that it re-enables them after this digest
- $timeout(function () {
- //enable delete animations
- deleteAnimations = true;
- }, 0, false);
- }
-
- /** Returns the css classses assigned to the node (div element) */
- scope.getNodeCssClass = function (node) {
- if (!node) {
- return '';
- }
- var css = [];
- if (node.cssClasses) {
- _.each(node.cssClasses, function(c) {
- css.push(c);
- });
- }
- if (node.selected) {
- css.push("umb-tree-node-checked");
- }
- return css.join(" ");
- };
-
- //add a method to the node which we can use to call to update the node data if we need to ,
- // this is done by sync tree, we don't want to add a $watch for each node as that would be crazy insane slow
- // so we have to do this
- scope.node.updateNodeData = function (newNode) {
- _.extend(scope.node, newNode);
- //now update the styles
- setupNodeDom(scope.node, scope.tree);
- };
-
- /**
- Method called when the options button next to a node is called
- In the main tree this opens the menu, but internally the tree doesnt
- know about this, so it simply raises an event to tell the parent controller
- about it.
- */
- scope.options = function (n, ev) {
- emitEvent("treeOptionsClick", { element: element, tree: scope.tree, node: n, event: ev });
- };
-
- /**
- Method called when an item is clicked in the tree, this passes the
- DOM element, the tree node object and the original click
- and emits it as a treeNodeSelect element if there is a callback object
- defined on the tree
- */
- scope.select = function (n, ev) {
- if (ev.ctrlKey ||
- ev.shiftKey ||
- ev.metaKey || // apple
- (ev.button && ev.button === 1) // middle click, >IE9 + everyone else
- ) {
- return;
- }
-
- emitEvent("treeNodeSelect", { element: element, tree: scope.tree, node: n, event: ev });
- ev.preventDefault();
- };
-
- /**
- Method called when an item is right-clicked in the tree, this passes the
- DOM element, the tree node object and the original click
- and emits it as a treeNodeSelect element if there is a callback object
- defined on the tree
- */
- scope.altSelect = function (n, ev) {
- emitEvent("treeNodeAltSelect", { element: element, tree: scope.tree, node: n, event: ev });
- };
-
- /** method to set the current animation for the node.
- * This changes dynamically based on if we are changing sections or just loading normal tree data.
- * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations.
- */
- scope.animation = function () {
- if (scope.node.showHideAnimation) {
- return scope.node.showHideAnimation;
- }
- if (deleteAnimations && scope.node.expanded) {
- return { leave: 'tree-node-delete-leave' };
- }
- else {
- return {};
- }
- };
-
- /**
- Method called when a node in the tree is expanded, when clicking the arrow
- takes the arrow DOM element and node data as parameters
- emits treeNodeCollapsing event if already expanded and treeNodeExpanding if collapsed
- */
- scope.load = function (node) {
- if (node.expanded) {
- deleteAnimations = false;
- emitEvent("treeNodeCollapsing", { tree: scope.tree, node: node, element: element });
- node.expanded = false;
- }
- else {
- scope.loadChildren(node, false);
- }
- };
-
- /* helper to force reloading children of a tree node */
- scope.loadChildren = function (node, forceReload) {
- //emit treeNodeExpanding event, if a callback object is set on the tree
- emitEvent("treeNodeExpanding", { tree: scope.tree, node: node });
-
- if (node.hasChildren && (forceReload || !node.children || (angular.isArray(node.children) && node.children.length === 0))) {
- //get the children from the tree service
- treeService.loadNodeChildren({ node: node, section: scope.section })
- .then(function (data) {
- //emit expanded event
- emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data });
- enableDeleteAnimations();
- });
- }
- else {
- emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: node.children });
- node.expanded = true;
- enableDeleteAnimations();
- }
- };
-
- //if the current path contains the node id, we will auto-expand the tree item children
-
- setupNodeDom(scope.node, scope.tree);
-
- var template = '';
- var newElement = angular.element(template);
- $compile(newElement)(scope);
- element.append(newElement);
-
- }
- };
-});
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTreeSearchBox
-* @function
-* @element ANY
-* @restrict E
-**/
-function treeSearchBox(localizationService, searchService, $q) {
- return {
- scope: {
- searchFromId: "@",
- searchFromName: "@",
- showSearch: "@",
- section: "@",
- hideSearchCallback: "=",
- searchCallback: "="
- },
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/components/tree/umb-tree-search-box.html',
- link: function (scope, element, attrs, ctrl) {
-
- scope.term = "";
- scope.hideSearch = function() {
- scope.term = "";
- scope.hideSearchCallback();
- };
-
- localizationService.localize("general_typeToSearch").then(function (value) {
- scope.searchPlaceholderText = value;
- });
-
- if (!scope.showSearch) {
- scope.showSearch = "false";
- }
-
- //used to cancel any request in progress if another one needs to take it's place
- var canceler = null;
-
- function performSearch() {
- if (scope.term) {
- scope.results = [];
-
- //a canceler exists, so perform the cancelation operation and reset
- if (canceler) {
- console.log("CANCELED!");
- canceler.resolve();
- canceler = $q.defer();
- }
- else {
- canceler = $q.defer();
- }
-
- var searchArgs = {
- term: scope.term,
- canceler: canceler
- };
-
- //append a start node context if there is one
- if (scope.searchFromId) {
- searchArgs["searchFrom"] = scope.searchFromId;
- }
-
- searcher(searchArgs).then(function (data) {
- scope.searchCallback(data);
- //set back to null so it can be re-created
- canceler = null;
- });
- }
- }
-
- scope.$watch("term", _.debounce(function(newVal, oldVal) {
- scope.$apply(function() {
- if (newVal !== null && newVal !== undefined && newVal !== oldVal) {
- performSearch();
- }
- });
- }, 200));
-
- var searcher = searchService.searchContent;
- //search
- if (scope.section === "member") {
- searcher = searchService.searchMembers;
- }
- else if (scope.section === "media") {
- searcher = searchService.searchMedia;
- }
- }
- };
-}
-angular.module('umbraco.directives').directive("umbTreeSearchBox", treeSearchBox);
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbTreeSearchResults
-* @function
-* @element ANY
-* @restrict E
-**/
-function treeSearchResults() {
- return {
- scope: {
- results: "=",
- selectResultCallback: "="
- },
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/components/tree/umb-tree-search-results.html',
- link: function (scope, element, attrs, ctrl) {
-
- }
- };
-}
-angular.module('umbraco.directives').directive("umbTreeSearchResults", treeSearchResults);
-
-angular.module("umbraco.directives")
- .directive('umbGenerateAlias', function ($timeout, entityResource) {
- return {
- restrict: 'E',
- templateUrl: 'views/components/umb-generate-alias.html',
- replace: true,
- scope: {
- alias: '=',
- aliasFrom: '=',
- enableLock: '=?',
- serverValidationField: '@'
- },
- link: function (scope, element, attrs, ctrl) {
-
- var eventBindings = [];
- var bindWatcher = true;
- var generateAliasTimeout = "";
- var updateAlias = false;
-
- scope.locked = true;
- scope.placeholderText = "Enter alias...";
-
- function generateAlias(value) {
-
- if (generateAliasTimeout) {
- $timeout.cancel(generateAliasTimeout);
- }
-
- if( value !== undefined && value !== "" && value !== null) {
-
- scope.alias = "";
- scope.placeholderText = "Generating Alias...";
-
- generateAliasTimeout = $timeout(function () {
- updateAlias = true;
- entityResource.getSafeAlias(value, true).then(function (safeAlias) {
- if (updateAlias) {
- scope.alias = safeAlias.alias;
- }
- });
- }, 500);
-
- } else {
- updateAlias = true;
- scope.alias = "";
- scope.placeholderText = "Enter alias...";
- }
-
- }
-
- // if alias gets unlocked - stop watching alias
- eventBindings.push(scope.$watch('locked', function(newValue, oldValue){
- if(newValue === false) {
- bindWatcher = false;
- }
- }));
-
- // validate custom entered alias
- eventBindings.push(scope.$watch('alias', function(newValue, oldValue){
-
- if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) {
- // add watcher
- eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) {
- if(bindWatcher) {
- generateAlias(newValue);
- }
- }));
- }
-
- }));
-
- // clean up
- scope.$on('$destroy', function(){
- // unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
- });
-
- }
- };
- });
-
-(function() {
- 'use strict';
-
- function ChildSelectorDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- var eventBindings = [];
- scope.dialogModel = {};
- scope.showDialog = false;
-
- scope.removeChild = function(selectedChild, $index) {
- if(scope.onRemove) {
- scope.onRemove(selectedChild, $index);
- }
- };
-
- scope.addChild = function($event) {
- if(scope.onAdd) {
- scope.onAdd($event);
- }
- };
-
- function syncParentName() {
-
- // update name on available item
- angular.forEach(scope.availableChildren, function(availableChild){
- if(availableChild.id === scope.parentId) {
- availableChild.name = scope.parentName;
- }
- });
-
- // update name on selected child
- angular.forEach(scope.selectedChildren, function(selectedChild){
- if(selectedChild.id === scope.parentId) {
- selectedChild.name = scope.parentName;
- }
- });
-
- }
-
- function syncParentIcon() {
-
- // update icon on available item
- angular.forEach(scope.availableChildren, function(availableChild){
- if(availableChild.id === scope.parentId) {
- availableChild.icon = scope.parentIcon;
- }
- });
-
- // update icon on selected child
- angular.forEach(scope.selectedChildren, function(selectedChild){
- if(selectedChild.id === scope.parentId) {
- selectedChild.icon = scope.parentIcon;
- }
- });
-
- }
-
- eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){
-
- if (newValue === oldValue) { return; }
- if ( oldValue === undefined || newValue === undefined) { return; }
-
- syncParentName();
-
- }));
-
- eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){
-
- if (newValue === oldValue) { return; }
- if ( oldValue === undefined || newValue === undefined) { return; }
-
- syncParentIcon();
- }));
-
- // clean up
- scope.$on('$destroy', function(){
- // unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
- });
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-child-selector.html',
- scope: {
- selectedChildren: '=',
- availableChildren: "=",
- parentName: "=",
- parentIcon: "=",
- parentId: "=",
- onRemove: "=",
- onAdd: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbChildSelector', ChildSelectorDirective);
-
-})();
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:umbConfirm
- * @function
- * @description
- * A confirmation dialog
- *
- * @restrict E
- */
-function confirmDirective() {
- return {
- restrict: "E", // restrict to an element
- replace: true, // replace the html element with the template
- templateUrl: 'views/components/umb-confirm.html',
- scope: {
- onConfirm: '=',
- onCancel: '=',
- caption: '@'
- },
- link: function (scope, element, attr, ctrl) {
-
- }
- };
-}
-angular.module('umbraco.directives').directive("umbConfirm", confirmDirective);
-
-(function() {
- 'use strict';
-
- function ConfirmAction() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.clickConfirm = function() {
- if(scope.onConfirm) {
- scope.onConfirm();
- }
- };
-
- scope.clickCancel = function() {
- if(scope.onCancel) {
- scope.onCancel();
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-confirm-action.html',
- scope: {
- direction: "@",
- onConfirm: "&",
- onCancel: "&"
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbConfirmAction', ConfirmAction);
-
-})();
-
-(function() {
- 'use strict';
-
- function ContentGridDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.clickItem = function(item, $event, $index) {
- if(scope.onClick) {
- scope.onClick(item, $event, $index);
- }
- };
-
- scope.clickItemName = function(item, $event, $index) {
- if(scope.onClickName) {
- scope.onClickName(item, $event, $index);
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-content-grid.html',
- scope: {
- content: '=',
- contentProperties: "=",
- onClick: "=",
- onClickName: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbContentGrid', ContentGridDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function UmbDisableFormValidation() {
-
- var directive = {
- restrict: 'A',
- require: '?form',
- link: function (scope, elm, attrs, ctrl) {
- //override the $setValidity function of the form to disable validation
- ctrl.$setValidity = function () { };
- }
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbDisableFormValidation', UmbDisableFormValidation);
-
-})();
-
-(function() {
- 'use strict';
-
- function EmptyStateDirective() {
-
- var directive = {
- restrict: 'E',
- replace: true,
- transclude: true,
- templateUrl: 'views/components/umb-empty-state.html',
- scope: {
- size: '@',
- position: '@'
- }
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbEmptyState', EmptyStateDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function FolderGridDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.clickFolder = function(folder, $event, $index) {
- if(scope.onClick) {
- scope.onClick(folder, $event, $index);
- }
- };
-
- scope.clickFolderName = function(folder, $event, $index) {
- if(scope.onClickName) {
- scope.onClickName(folder, $event, $index);
- $event.stopPropagation();
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-folder-grid.html',
- scope: {
- folders: '=',
- onClick: "=",
- onClickName: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbFolderGrid', FolderGridDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function GridSelector() {
-
- function link(scope, el, attr, ctrl) {
-
- var eventBindings = [];
- scope.dialogModel = {};
- scope.showDialog = false;
- scope.itemLabel = "";
-
- // set default item name
- if(!scope.itemName){
- scope.itemLabel = "item";
- } else {
- scope.itemLabel = scope.itemName;
- }
-
- scope.removeItem = function(selectedItem) {
- var selectedItemIndex = scope.selectedItems.indexOf(selectedItem);
- scope.selectedItems.splice(selectedItemIndex, 1);
- };
-
- scope.removeDefaultItem = function() {
-
- // it will be the last item so we can clear the array
- scope.selectedItems = [];
-
- // remove as default item
- scope.defaultItem = null;
-
- };
-
- scope.openItemPicker = function($event){
- scope.dialogModel = {
- view: "itempicker",
- title: "Choose " + scope.itemLabel,
- availableItems: scope.availableItems,
- selectedItems: scope.selectedItems,
- event: $event,
- show: true,
- submit: function(model) {
- scope.selectedItems.push(model.selectedItem);
-
- // if no default item - set item as default
- if(scope.defaultItem === null) {
- scope.setAsDefaultItem(model.selectedItem);
- }
-
- scope.dialogModel.show = false;
- scope.dialogModel = null;
- }
- };
- };
-
- scope.setAsDefaultItem = function(selectedItem) {
-
- // clear default item
- scope.defaultItem = {};
-
- // set as default item
- scope.defaultItem = selectedItem;
- };
-
- function updatePlaceholders() {
-
- // update default item
- if(scope.defaultItem !== null && scope.defaultItem.placeholder) {
-
- scope.defaultItem.name = scope.name;
-
- if(scope.alias !== null && scope.alias !== undefined) {
- scope.defaultItem.alias = scope.alias;
- }
-
- }
-
- // update selected items
- angular.forEach(scope.selectedItems, function(selectedItem) {
- if(selectedItem.placeholder) {
-
- selectedItem.name = scope.name;
-
- if(scope.alias !== null && scope.alias !== undefined) {
- selectedItem.alias = scope.alias;
- }
-
- }
- });
-
- // update availableItems
- angular.forEach(scope.availableItems, function(availableItem) {
- if(availableItem.placeholder) {
-
- availableItem.name = scope.name;
-
- if(scope.alias !== null && scope.alias !== undefined) {
- availableItem.alias = scope.alias;
- }
-
- }
- });
-
- }
-
- function activate() {
-
- // add watchers for updating placeholde name and alias
- if(scope.updatePlaceholder) {
- eventBindings.push(scope.$watch('name', function(newValue, oldValue){
- updatePlaceholders();
- }));
-
- eventBindings.push(scope.$watch('alias', function(newValue, oldValue){
- updatePlaceholders();
- }));
- }
-
- }
-
- activate();
-
- // clean up
- scope.$on('$destroy', function(){
-
- // clear watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
-
- });
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-grid-selector.html',
- scope: {
- name: "=",
- alias: "=",
- selectedItems: '=',
- availableItems: "=",
- defaultItem: "=",
- itemName: "@",
- updatePlaceholder: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbGridSelector', GridSelector);
-
-})();
-
-(function() {
- 'use strict';
-
- function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService) {
-
- function link(scope, el, attr, ctrl) {
-
- var validationTranslated = "";
- var tabNoSortOrderTranslated = "";
-
- scope.sortingMode = false;
- scope.toolbar = [];
- scope.sortableOptionsGroup = {};
- scope.sortableOptionsProperty = {};
- scope.sortingButtonKey = "general_reorder";
-
- function activate() {
-
- setSortingOptions();
-
- // set placeholder property on each group
- if (scope.model.groups.length !== 0) {
- angular.forEach(scope.model.groups, function(group) {
- addInitProperty(group);
- });
- }
-
- // add init tab
- addInitGroup(scope.model.groups);
-
- activateFirstGroup(scope.model.groups);
-
- // localize texts
- localizationService.localize("validation_validation").then(function(value) {
- validationTranslated = value;
- });
-
- localizationService.localize("contentTypeEditor_tabHasNoSortOrder").then(function(value) {
- tabNoSortOrderTranslated = value;
- });
- }
-
- function setSortingOptions() {
-
- scope.sortableOptionsGroup = {
- distance: 10,
- tolerance: "pointer",
- opacity: 0.7,
- scroll: true,
- cursor: "move",
- placeholder: "umb-group-builder__group-sortable-placeholder",
- zIndex: 6000,
- handle: ".umb-group-builder__group-handle",
- items: ".umb-group-builder__group-sortable",
- start: function(e, ui) {
- ui.placeholder.height(ui.item.height());
- },
- stop: function(e, ui) {
- updateTabsSortOrder();
- },
- };
-
- scope.sortableOptionsProperty = {
- distance: 10,
- tolerance: "pointer",
- connectWith: ".umb-group-builder__properties",
- opacity: 0.7,
- scroll: true,
- cursor: "move",
- placeholder: "umb-group-builder__property_sortable-placeholder",
- zIndex: 6000,
- handle: ".umb-group-builder__property-handle",
- items: ".umb-group-builder__property-sortable",
- start: function(e, ui) {
- ui.placeholder.height(ui.item.height());
- },
- stop: function(e, ui) {
- updatePropertiesSortOrder();
- }
- };
-
- }
-
- function updateTabsSortOrder() {
-
- var first = true;
- var prevSortOrder = 0;
-
- scope.model.groups.map(function(group){
-
- var index = scope.model.groups.indexOf(group);
-
- if(group.tabState !== "init") {
-
- // set the first not inherited tab to sort order 0
- if(!group.inherited && first) {
-
- // set the first tab sort order to 0 if prev is 0
- if( prevSortOrder === 0 ) {
- group.sortOrder = 0;
- // when the first tab is inherited and sort order is not 0
- } else {
- group.sortOrder = prevSortOrder + 1;
- }
-
- first = false;
-
- } else if(!group.inherited && !first) {
-
- // find next group
- var nextGroup = scope.model.groups[index + 1];
-
- // if a groups is dropped in the middle of to groups with
- // same sort order. Give it the dropped group same sort order
- if( prevSortOrder === nextGroup.sortOrder ) {
- group.sortOrder = prevSortOrder;
- } else {
- group.sortOrder = prevSortOrder + 1;
- }
-
- }
-
- // store this tabs sort order as reference for the next
- prevSortOrder = group.sortOrder;
-
- }
-
- });
-
- }
-
- function filterAvailableCompositions(selectedContentType, selecting) {
-
- //selecting = true if the user has check the item, false if the user has unchecked the item
-
- var selectedContentTypeAliases = selecting ?
- //the user has selected the item so add to the current list
- _.union(scope.compositionsDialogModel.compositeContentTypes, [selectedContentType.alias]) :
- //the user has unselected the item so remove from the current list
- _.reject(scope.compositionsDialogModel.compositeContentTypes, function(i) {
- return i === selectedContentType.alias;
- });
-
- //get the currently assigned property type aliases - ensure we pass these to the server side filer
- var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
- return _.map(g.properties, function(p) {
- return p.alias;
- });
- })), function (f) {
- return f !== null && f !== undefined;
- });
-
- //use a different resource lookup depending on the content type type
- var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
-
- return resourceLookup(scope.model.id, selectedContentTypeAliases, propAliasesExisting).then(function (filteredAvailableCompositeTypes) {
- _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (current) {
- //reset first
- current.allowed = true;
- //see if this list item is found in the response (allowed) list
- var found = _.find(filteredAvailableCompositeTypes, function (f) {
- return current.contentType.alias === f.contentType.alias;
- });
-
- //allow if the item was found in the response (allowed) list -
- // and ensure its set to allowed if it is currently checked,
- // DO not allow if it's a locked content type.
- current.allowed = scope.model.lockedCompositeContentTypes.indexOf(current.contentType.alias) === -1 &&
- (selectedContentTypeAliases.indexOf(current.contentType.alias) !== -1) || ((found !== null && found !== undefined) ? found.allowed : false);
-
- });
- });
- }
-
- function updatePropertiesSortOrder() {
-
- angular.forEach(scope.model.groups, function(group){
- if( group.tabState !== "init" ) {
- group.properties = contentTypeHelper.updatePropertiesSortOrder(group.properties);
- }
- });
-
- }
-
- function setupAvailableContentTypesModel(result) {
- scope.compositionsDialogModel.availableCompositeContentTypes = result;
- //iterate each one and set it up
- _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (c) {
- //enable it if it's part of the selected model
- if (scope.compositionsDialogModel.compositeContentTypes.indexOf(c.contentType.alias) !== -1) {
- c.allowed = true;
- }
-
- //set the inherited flags
- c.inherited = false;
- if (scope.model.lockedCompositeContentTypes.indexOf(c.contentType.alias) > -1) {
- c.inherited = true;
- }
- // convert icons for composite content types
- iconHelper.formatContentTypeIcons([c.contentType]);
- });
- }
-
- /* ---------- DELETE PROMT ---------- */
-
- scope.togglePrompt = function (object) {
- object.deletePrompt = !object.deletePrompt;
- };
-
- scope.hidePrompt = function (object) {
- object.deletePrompt = false;
- };
-
- /* ---------- TOOLBAR ---------- */
-
- scope.toggleSortingMode = function(tool) {
-
- if (scope.sortingMode === true) {
-
- var sortOrderMissing = false;
-
- for (var i = 0; i < scope.model.groups.length; i++) {
- var group = scope.model.groups[i];
- if (group.tabState !== "init" && group.sortOrder === undefined) {
- sortOrderMissing = true;
- group.showSortOrderMissing = true;
- notificationsService.error(validationTranslated + ": " + group.name + " " + tabNoSortOrderTranslated);
- }
- }
-
- if (!sortOrderMissing) {
- scope.sortingMode = false;
- scope.sortingButtonKey = "general_reorder";
- }
-
- } else {
-
- scope.sortingMode = true;
- scope.sortingButtonKey = "general_reorderDone";
-
- }
-
- };
-
- scope.openCompositionsDialog = function() {
-
- scope.compositionsDialogModel = {
- title: "Compositions",
- contentType: scope.model,
- compositeContentTypes: scope.model.compositeContentTypes,
- view: "views/common/overlays/contenttypeeditor/compositions/compositions.html",
- confirmSubmit: {
- title: "Warning",
- description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?",
- checkboxLabel: "I know what I'm doing",
- enable: true
- },
- submit: function(model, oldModel, confirmed) {
-
- var compositionRemoved = false;
-
- // check if any compositions has been removed
- for(var i = 0; oldModel.compositeContentTypes.length > i; i++) {
-
- var oldComposition = oldModel.compositeContentTypes[i];
-
- if(_.contains(model.compositeContentTypes, oldComposition) === false) {
- compositionRemoved = true;
- }
-
- }
-
- // show overlay confirm box if compositions has been removed.
- if(compositionRemoved && confirmed === false) {
-
- scope.compositionsDialogModel.confirmSubmit.show = true;
-
- // submit overlay if no compositions has been removed
- // or the action has been confirmed
- } else {
-
- // make sure that all tabs has an init property
- if (scope.model.groups.length !== 0) {
- angular.forEach(scope.model.groups, function(group) {
- addInitProperty(group);
- });
- }
-
- // remove overlay
- scope.compositionsDialogModel.show = false;
- scope.compositionsDialogModel = null;
- }
-
- },
- close: function(oldModel) {
-
- // reset composition changes
- scope.model.groups = oldModel.contentType.groups;
- scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes;
-
- // remove overlay
- scope.compositionsDialogModel.show = false;
- scope.compositionsDialogModel = null;
-
- },
- selectCompositeContentType: function (selectedContentType) {
-
- //first check if this is a new selection - we need to store this value here before any further digests/async
- // because after that the scope.model.compositeContentTypes will be populated with the selected value.
- var newSelection = scope.model.compositeContentTypes.indexOf(selectedContentType.alias) === -1;
-
- if (newSelection) {
- //merge composition with content type
-
- //use a different resource lookup depending on the content type type
- var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getById : mediaTypeResource.getById;
-
- resourceLookup(selectedContentType.id).then(function (composition) {
- //based on the above filtering we shouldn't be able to select an invalid one, but let's be safe and
- // double check here.
- var overlappingAliases = contentTypeHelper.validateAddingComposition(scope.model, composition);
- if (overlappingAliases.length > 0) {
- //this will create an invalid composition, need to uncheck it
- scope.compositionsDialogModel.compositeContentTypes.splice(
- scope.compositionsDialogModel.compositeContentTypes.indexOf(composition.alias), 1);
- //dissallow this until something else is unchecked
- selectedContentType.allowed = false;
- }
- else {
- contentTypeHelper.mergeCompositeContentType(scope.model, composition);
- }
-
- //based on the selection, we need to filter the available composite types list
- filterAvailableCompositions(selectedContentType, newSelection).then(function () {
- //TODO: Here we could probably re-enable selection if we previously showed a throbber or something
- });
- });
- }
- else {
- // split composition from content type
- contentTypeHelper.splitCompositeContentType(scope.model, selectedContentType);
-
- //based on the selection, we need to filter the available composite types list
- filterAvailableCompositions(selectedContentType, newSelection).then(function () {
- //TODO: Here we could probably re-enable selection if we previously showed a throbber or something
- });
- }
-
- }
- };
-
- var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
- var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount;
-
- //get the currently assigned property type aliases - ensure we pass these to the server side filer
- var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
- return _.map(g.properties, function(p) {
- return p.alias;
- });
- })), function(f) {
- return f !== null && f !== undefined;
- });
- $q.all([
- //get available composite types
- availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) {
- setupAvailableContentTypesModel(result);
- }),
- //get content type count
- countContentTypeResource().then(function(result) {
- scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10);
- })
- ]).then(function() {
- //resolves when both other promises are done, now show it
- scope.compositionsDialogModel.show = true;
- });
-
- };
-
-
- /* ---------- GROUPS ---------- */
-
- scope.addGroup = function(group) {
-
- // set group sort order
- var index = scope.model.groups.indexOf(group);
- var prevGroup = scope.model.groups[index - 1];
-
- if( index > 0) {
- // set index to 1 higher than the previous groups sort order
- group.sortOrder = prevGroup.sortOrder + 1;
-
- } else {
- // first group - sort order will be 0
- group.sortOrder = 0;
- }
-
- // activate group
- scope.activateGroup(group);
-
- };
-
- scope.activateGroup = function(selectedGroup) {
-
- // set all other groups that are inactive to active
- angular.forEach(scope.model.groups, function(group) {
- // skip init tab
- if (group.tabState !== "init") {
- group.tabState = "inActive";
- }
- });
-
- selectedGroup.tabState = "active";
-
- };
-
- scope.removeGroup = function(groupIndex) {
- scope.model.groups.splice(groupIndex, 1);
- addInitGroup(scope.model.groups);
- };
-
- scope.updateGroupTitle = function(group) {
- if (group.properties.length === 0) {
- addInitProperty(group);
- }
- };
-
- scope.changeSortOrderValue = function(group) {
-
- if (group.sortOrder !== undefined) {
- group.showSortOrderMissing = false;
- }
- scope.model.groups = $filter('orderBy')(scope.model.groups, 'sortOrder');
- };
-
- function addInitGroup(groups) {
-
- // check i init tab already exists
- var addGroup = true;
-
- angular.forEach(groups, function(group) {
- if (group.tabState === "init") {
- addGroup = false;
- }
- });
-
- if (addGroup) {
- groups.push({
- properties: [],
- parentTabContentTypes: [],
- parentTabContentTypeNames: [],
- name: "",
- tabState: "init"
- });
- }
-
- return groups;
- }
-
- function activateFirstGroup(groups) {
- if (groups && groups.length > 0) {
- var firstGroup = groups[0];
- if(!firstGroup.tabState || firstGroup.tabState === "inActive") {
- firstGroup.tabState = "active";
- }
- }
- }
-
- /* ---------- PROPERTIES ---------- */
-
- scope.addProperty = function(property, group) {
-
- // set property sort order
- var index = group.properties.indexOf(property);
- var prevProperty = group.properties[index - 1];
-
- if( index > 0) {
- // set index to 1 higher than the previous property sort order
- property.sortOrder = prevProperty.sortOrder + 1;
-
- } else {
- // first property - sort order will be 0
- property.sortOrder = 0;
- }
-
- // open property settings dialog
- scope.editPropertyTypeSettings(property, group);
-
- };
-
- scope.editPropertyTypeSettings = function(property, group) {
-
- if (!property.inherited && !property.locked) {
-
- scope.propertySettingsDialogModel = {};
- scope.propertySettingsDialogModel.title = "Property settings";
- scope.propertySettingsDialogModel.property = property;
- scope.propertySettingsDialogModel.contentType = scope.contentType;
- scope.propertySettingsDialogModel.contentTypeName = scope.model.name;
- scope.propertySettingsDialogModel.view = "views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html";
- scope.propertySettingsDialogModel.show = true;
-
- // set state to active to access the preview
- property.propertyState = "active";
-
- // set property states
- property.dialogIsOpen = true;
-
- scope.propertySettingsDialogModel.submit = function(model) {
-
- property.inherited = false;
- property.dialogIsOpen = false;
-
- // update existing data types
- if(model.updateSameDataTypes) {
- updateSameDataTypes(property);
- }
-
- // remove dialog
- scope.propertySettingsDialogModel.show = false;
- scope.propertySettingsDialogModel = null;
-
- // push new init property to group
- addInitProperty(group);
-
- // set focus on init property
- var numberOfProperties = group.properties.length;
- group.properties[numberOfProperties - 1].focus = true;
-
- // push new init tab to the scope
- addInitGroup(scope.model.groups);
-
- };
-
- scope.propertySettingsDialogModel.close = function(oldModel) {
-
- // reset all property changes
- property.label = oldModel.property.label;
- property.alias = oldModel.property.alias;
- property.description = oldModel.property.description;
- property.config = oldModel.property.config;
- property.editor = oldModel.property.editor;
- property.view = oldModel.property.view;
- property.dataTypeId = oldModel.property.dataTypeId;
- property.dataTypeIcon = oldModel.property.dataTypeIcon;
- property.dataTypeName = oldModel.property.dataTypeName;
- property.validation.mandatory = oldModel.property.validation.mandatory;
- property.validation.pattern = oldModel.property.validation.pattern;
- property.showOnMemberProfile = oldModel.property.showOnMemberProfile;
- property.memberCanEdit = oldModel.property.memberCanEdit;
-
- // because we set state to active, to show a preview, we have to check if has been filled out
- // label is required so if it is not filled we know it is a placeholder
- if(oldModel.property.editor === undefined || oldModel.property.editor === null || oldModel.property.editor === "") {
- property.propertyState = "init";
- } else {
- property.propertyState = oldModel.property.propertyState;
- }
-
- // remove dialog
- scope.propertySettingsDialogModel.show = false;
- scope.propertySettingsDialogModel = null;
-
- };
-
- }
- };
-
- scope.deleteProperty = function(tab, propertyIndex) {
-
- // remove property
- tab.properties.splice(propertyIndex, 1);
-
- // if the last property in group is an placeholder - remove add new tab placeholder
- if(tab.properties.length === 1 && tab.properties[0].propertyState === "init") {
-
- angular.forEach(scope.model.groups, function(group, index, groups){
- if(group.tabState === 'init') {
- groups.splice(index, 1);
- }
- });
-
- }
-
- };
-
- function addInitProperty(group) {
-
- var addInitPropertyBool = true;
- var initProperty = {
- label: null,
- alias: null,
- propertyState: "init",
- validation: {
- mandatory: false,
- pattern: null
- }
- };
-
- // check if there already is an init property
- angular.forEach(group.properties, function(property) {
- if (property.propertyState === "init") {
- addInitPropertyBool = false;
- }
- });
-
- if (addInitPropertyBool) {
- group.properties.push(initProperty);
- }
-
- return group;
- }
-
- function updateSameDataTypes(newProperty) {
-
- // find each property
- angular.forEach(scope.model.groups, function(group){
- angular.forEach(group.properties, function(property){
-
- if(property.dataTypeId === newProperty.dataTypeId) {
-
- // update property data
- property.config = newProperty.config;
- property.editor = newProperty.editor;
- property.view = newProperty.view;
- property.dataTypeId = newProperty.dataTypeId;
- property.dataTypeIcon = newProperty.dataTypeIcon;
- property.dataTypeName = newProperty.dataTypeName;
-
- }
-
- });
- });
- }
-
-
- var unbindModelWatcher = scope.$watch('model', function(newValue, oldValue) {
- if (newValue !== undefined && newValue.groups !== undefined) {
- activate();
- }
- });
-
- // clean up
- scope.$on('$destroy', function(){
- unbindModelWatcher();
- });
-
- }
-
- var directive = {
- restrict: "E",
- replace: true,
- templateUrl: "views/components/umb-groups-builder.html",
- scope: {
- model: "=",
- compositions: "=",
- sorting: "=",
- contentType: "@"
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbGroupsBuilder', GroupsBuilderDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function KeyboardShortcutsOverviewDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.shortcutOverlay = false;
-
- scope.toggleShortcutsOverlay = function() {
- scope.shortcutOverlay = !scope.shortcutOverlay;
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html',
- link: link,
- scope: {
- model: "="
- }
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbLaunchMiniEditor
-* @restrict E
-* @function
-* @description
-* Used on a button to launch a mini content editor editor dialog
-**/
-angular.module("umbraco.directives")
- .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) {
- return {
- restrict: 'A',
- replace: false,
- scope: {
- node: '=umbLaunchMiniEditor',
- },
- link: function(scope, element, attrs) {
-
- var launched = false;
-
- element.click(function() {
-
- if (launched === true) {
- return;
- }
-
- launched = true;
-
- //We need to store the current files selected in the file manager locally because the fileManager
- // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
- // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
- // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
- var currFiles = _.groupBy(fileManager.getFiles(), "alias");
- fileManager.clearFiles();
-
- //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
- // any property editors that are working with editorState get given the correct entity, otherwise strange things will
- // start happening.
- var currEditorState = editorState.getCurrent();
-
- dialogService.open({
- template: "views/common/dialogs/content/edit.html",
- id: scope.node.id,
- closeOnSave: true,
- tabFilter: ["Generic properties"],
- callback: function (data) {
-
- //set the node name back
- scope.node.name = data.name;
-
- //reset the fileManager to what it was
- fileManager.clearFiles();
- _.each(currFiles, function (val, key) {
- fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
- });
-
- //reset the editor state
- editorState.set(currEditorState);
-
- //Now we need to check if the content item that was edited was actually the same content item
- // as the main content editor and if so, update all property data
- if (data.id === currEditorState.id) {
- var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
- }
-
- launched = false;
- },
- closeCallback: function () {
- //reset the fileManager to what it was
- fileManager.clearFiles();
- _.each(currFiles, function (val, key) {
- fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
- });
-
- //reset the editor state
- editorState.set(currEditorState);
-
- launched = false;
- }
- });
-
- });
-
- }
- };
- });
-(function() {
- 'use strict';
-
- function LayoutSelectorDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.layoutDropDownIsOpen = false;
- scope.showLayoutSelector = true;
-
- function activate() {
-
- setVisibility();
-
- setActiveLayout(scope.layouts);
-
- }
-
- function setVisibility() {
-
- var numberOfAllowedLayouts = getNumberOfAllowedLayouts(scope.layouts);
-
- if(numberOfAllowedLayouts === 1) {
- scope.showLayoutSelector = false;
- }
-
- }
-
- function getNumberOfAllowedLayouts(layouts) {
-
- var allowedLayouts = 0;
-
- for (var i = 0; layouts.length > i; i++) {
-
- var layout = layouts[i];
-
- if(layout.selected === true) {
- allowedLayouts++;
- }
-
- }
-
- return allowedLayouts;
- }
-
- function setActiveLayout(layouts) {
-
- for (var i = 0; layouts.length > i; i++) {
- var layout = layouts[i];
- if(layout.path === scope.activeLayout.path) {
- layout.active = true;
- }
- }
-
- }
-
- scope.pickLayout = function(selectedLayout) {
- if(scope.onLayoutSelect) {
- scope.onLayoutSelect(selectedLayout);
- scope.layoutDropDownIsOpen = false;
- }
- };
-
- scope.toggleLayoutDropdown = function() {
- scope.layoutDropDownIsOpen = !scope.layoutDropDownIsOpen;
- };
-
- scope.closeLayoutDropdown = function() {
- scope.layoutDropDownIsOpen = false;
- };
-
- activate();
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-layout-selector.html',
- scope: {
- layouts: '=',
- activeLayout: '=',
- onLayoutSelect: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbLayoutSelector', LayoutSelectorDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function ListViewLayoutDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.getContent = function(contentId) {
- if(scope.onGetContent) {
- scope.onGetContent(contentId);
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-list-view-layout.html',
- scope: {
- contentId: '=',
- folders: '=',
- items: '=',
- selection: '=',
- options: '=',
- entityType: '@',
- onGetContent: '='
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function ListViewSettingsDirective(contentTypeResource, dataTypeResource, dataTypeHelper) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.dataType = {};
- scope.editDataTypeSettings = false;
- scope.customListViewCreated = false;
-
- /* ---------- INIT ---------- */
-
- function activate() {
-
- if(scope.enableListView) {
-
- dataTypeResource.getByName(scope.listViewName)
- .then(function(dataType) {
-
- scope.dataType = dataType;
-
- scope.customListViewCreated = checkForCustomListView();
-
- });
-
- } else {
-
- scope.dataType = {};
-
- }
-
- }
-
- /* ----------- LIST VIEW SETTINGS --------- */
-
- scope.toggleEditListViewDataTypeSettings = function() {
- scope.editDataTypeSettings = !scope.editDataTypeSettings;
- };
-
- scope.saveListViewDataType = function() {
-
- var preValues = dataTypeHelper.createPreValueProps(scope.dataType.preValues);
-
- dataTypeResource.save(scope.dataType, preValues, false).then(function(dataType) {
-
- // store data type
- scope.dataType = dataType;
-
- // hide settings panel
- scope.editDataTypeSettings = false;
-
- });
-
- };
-
-
- /* ---------- CUSTOM LIST VIEW ---------- */
-
- scope.createCustomListViewDataType = function() {
-
- dataTypeResource.createCustomListView(scope.modelAlias).then(function(dataType) {
-
- // store data type
- scope.dataType = dataType;
-
- // set list view name on scope
- scope.listViewName = dataType.name;
-
- // change state to custom list view
- scope.customListViewCreated = true;
-
- // show settings panel
- scope.editDataTypeSettings = true;
-
- });
-
- };
-
- scope.removeCustomListDataType = function() {
-
- scope.editDataTypeSettings = false;
-
- // delete custom list view data type
- dataTypeResource.deleteById(scope.dataType.id).then(function(dataType) {
-
- // set list view name on scope
- if(scope.contentType === "documentType") {
-
- scope.listViewName = "List View - Content";
-
- } else if(scope.contentType === "mediaType") {
-
- scope.listViewName = "List View - Media";
-
- }
-
- // get default data type
- dataTypeResource.getByName(scope.listViewName)
- .then(function(dataType) {
-
- // store data type
- scope.dataType = dataType;
-
- // change state to default list view
- scope.customListViewCreated = false;
-
- });
- });
-
- };
-
- /* ----------- SCOPE WATCHERS ----------- */
- var unbindEnableListViewWatcher = scope.$watch('enableListView', function(newValue, oldValue){
-
- if(newValue !== undefined) {
- activate();
- }
-
- });
-
- // clean up
- scope.$on('$destroy', function(){
- unbindEnableListViewWatcher();
- });
-
- /* ----------- METHODS ---------- */
-
- function checkForCustomListView() {
- return scope.dataType.name === "List View - " + scope.modelAlias;
- }
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-list-view-settings.html',
- scope: {
- enableListView: "=",
- listViewName: "=",
- modelAlias: "=",
- contentType: "@"
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbListViewSettings', ListViewSettingsDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function UmbLoadIndicatorDirective() {
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-load-indicator.html'
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbLoadIndicator', UmbLoadIndicatorDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbContentName
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-(function() {
- 'use strict';
-
- function LockedFieldDirective($timeout, localizationService) {
-
- function link(scope, el, attr, ngModelCtrl) {
-
- function activate() {
-
- // if locked state is not defined as an attr set default state
- if (scope.locked === undefined || scope.locked === null) {
- scope.locked = true;
- }
-
- // if regex validation is not defined as an attr set default state
- // if this is set to an empty string then regex validation can be ignored.
- if (scope.regexValidation === undefined || scope.regexValidation === null) {
- scope.regexValidation = "^[a-zA-Z]\\w.*$";
- }
-
- if (scope.serverValidationField === undefined || scope.serverValidationField === null) {
- scope.serverValidationField = "";
- }
-
- // if locked state is not defined as an attr set default state
- if (scope.placeholderText === undefined || scope.placeholderText === null) {
- scope.placeholderText = "Enter value...";
- }
-
- }
-
- scope.lock = function() {
- scope.locked = true;
- };
-
- scope.unlock = function() {
- scope.locked = false;
- };
-
- activate();
-
- }
-
- var directive = {
- require: "ngModel",
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-locked-field.html',
- scope: {
- ngModel: "=",
- locked: "=?",
- placeholderText: "=?",
- regexValidation: "=?",
- serverValidationField: "@"
- },
- link: link
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbLockedField', LockedFieldDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function MediaGridDirective($filter, mediaHelper) {
-
- function link(scope, el, attr, ctrl) {
-
- var itemDefaultHeight = 200;
- var itemDefaultWidth = 200;
- var itemMaxWidth = 200;
- var itemMaxHeight = 200;
- var itemMinWidth = 125;
- var itemMinHeight = 125;
-
- function activate() {
-
- if (scope.itemMaxWidth) {
- itemMaxWidth = scope.itemMaxWidth;
- }
-
- if (scope.itemMaxHeight) {
- itemMaxHeight = scope.itemMaxHeight;
- }
-
- if (scope.itemMinWidth) {
- itemMinWidth = scope.itemMinWidth;
- }
-
- if (scope.itemMinWidth) {
- itemMinHeight = scope.itemMinHeight;
- }
-
- for (var i = 0; scope.items.length > i; i++) {
- var item = scope.items[i];
- setItemData(item);
- setOriginalSize(item, itemMaxHeight);
- }
-
- if (scope.items.length > 0) {
- setFlexValues(scope.items);
- }
-
- }
-
- function setItemData(item) {
- item.isFolder = !mediaHelper.hasFilePropertyType(item);
- if (!item.isFolder) {
- item.thumbnail = mediaHelper.resolveFile(item, true);
- item.image = mediaHelper.resolveFile(item, false);
- }
- }
-
- function setOriginalSize(item, maxHeight) {
-
- //set to a square by default
- item.width = itemDefaultWidth;
- item.height = itemDefaultHeight;
- item.aspectRatio = 1;
-
- var widthProp = _.find(item.properties, function(v) {
- return (v.alias === "umbracoWidth");
- });
-
- if (widthProp && widthProp.value) {
- item.width = parseInt(widthProp.value, 10);
- if (isNaN(item.width)) {
- item.width = itemDefaultWidth;
- }
- }
-
- var heightProp = _.find(item.properties, function(v) {
- return (v.alias === "umbracoHeight");
- });
-
- if (heightProp && heightProp.value) {
- item.height = parseInt(heightProp.value, 10);
- if (isNaN(item.height)) {
- item.height = itemDefaultWidth;
- }
- }
-
- item.aspectRatio = item.width / item.height;
-
- // set max width and height
- // landscape
- if (item.aspectRatio >= 1) {
- if (item.width > itemMaxWidth) {
- item.width = itemMaxWidth;
- item.height = itemMaxWidth / item.aspectRatio;
- }
- // portrait
- } else {
- if (item.height > itemMaxHeight) {
- item.height = itemMaxHeight;
- item.width = itemMaxHeight * item.aspectRatio;
- }
- }
-
- }
-
- function setFlexValues(mediaItems) {
-
- var flexSortArray = mediaItems;
- var smallestImageWidth = null;
- var widestImageAspectRatio = null;
-
- // sort array after image width with the widest image first
- flexSortArray = $filter('orderBy')(flexSortArray, 'width', true);
-
- // find widest image aspect ratio
- widestImageAspectRatio = flexSortArray[0].aspectRatio;
-
- // find smallest image width
- smallestImageWidth = flexSortArray[flexSortArray.length - 1].width;
-
- for (var i = 0; flexSortArray.length > i; i++) {
-
- var mediaItem = flexSortArray[i];
- var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio);
-
- if (flex === 0) {
- flex = 1;
- }
-
- var imageMinFlexWidth = smallestImageWidth * flex;
-
- var flexStyle = {
- "flex": flex + " 1 " + imageMinFlexWidth + "px",
- "max-width": mediaItem.width + "px",
- "min-width": itemMinWidth + "px",
- "min-height": itemMinHeight + "px"
- };
-
- mediaItem.flexStyle = flexStyle;
-
- }
-
- }
-
- scope.clickItem = function(item, $event, $index) {
- if (scope.onClick) {
- scope.onClick(item, $event, $index);
- }
- };
-
- scope.clickItemName = function(item, $event, $index) {
- if (scope.onClickName) {
- scope.onClickName(item, $event, $index);
- $event.stopPropagation();
- }
- };
-
- scope.hoverItemDetails = function(item, $event, hover) {
- if (scope.onDetailsHover) {
- scope.onDetailsHover(item, $event, hover);
- }
- };
-
- var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) {
- if (angular.isArray(newValue)) {
- activate();
- }
- });
-
- scope.$on('$destroy', function() {
- unbindItemsWatcher();
- });
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-media-grid.html',
- scope: {
- items: '=',
- onDetailsHover: "=",
- onClick: '=',
- onClickName: "=",
- filterBy: "=",
- itemMaxWidth: "@",
- itemMaxHeight: "@",
- itemMinWidth: "@",
- itemMinHeight: "@"
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function PaginationDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- function activate() {
-
- scope.pagination = [];
-
- var i = 0;
-
- if (scope.totalPages <= 10) {
- for (i = 0; i < scope.totalPages; i++) {
- scope.pagination.push({
- val: (i + 1),
- isActive: scope.pageNumber === (i + 1)
- });
- }
- }
- else {
- //if there is more than 10 pages, we need to do some fancy bits
-
- //get the max index to start
- var maxIndex = scope.totalPages - 10;
- //set the start, but it can't be below zero
- var start = Math.max(scope.pageNumber - 5, 0);
- //ensure that it's not too far either
- start = Math.min(maxIndex, start);
-
- for (i = start; i < (10 + start) ; i++) {
- scope.pagination.push({
- val: (i + 1),
- isActive: scope.pageNumber === (i + 1)
- });
- }
-
- //now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing
- if (start > 0) {
- scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false});
- }
-
- //same for the end
- if (start < maxIndex) {
- scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: scope.totalPages, isActive: false });
- }
- }
-
- }
-
- scope.next = function() {
- if (scope.onNext && scope.pageNumber < scope.totalPages) {
- scope.pageNumber++;
- scope.onNext(scope.pageNumber);
- }
- };
-
- scope.prev = function(pageNumber) {
- if (scope.onPrev && scope.pageNumber > 1) {
- scope.pageNumber--;
- scope.onPrev(scope.pageNumber);
- }
- };
-
- scope.goToPage = function(pageNumber) {
- if(scope.onGoToPage) {
- scope.pageNumber = pageNumber + 1;
- scope.onGoToPage(scope.pageNumber);
- }
- };
-
- var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
- activate();
- });
-
- scope.$on('$destroy', function(){
- unbindPageNumberWatcher();
- });
-
- activate();
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-pagination.html',
- scope: {
- pageNumber: "=",
- totalPages: "=",
- onNext: "=",
- onPrev: "=",
- onGoToPage: "="
- },
- link: link
- };
-
- return directive;
-
- }
-
- angular.module('umbraco.directives').directive('umbPagination', PaginationDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function StickyBarDirective($rootScope) {
-
- function link(scope, el, attr, ctrl) {
-
- var bar = $(el);
- var scrollableContainer = null;
- var clonedBar = null;
- var cloneIsMade = false;
- var barTop = bar.context.offsetTop;
-
- function activate() {
-
- if (attr.scrollableContainer) {
- scrollableContainer = $(attr.scrollableContainer);
- } else {
- scrollableContainer = $(window);
- }
-
- scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
- $(window).on('resize.umbStickyBar', determineVisibility);
-
- scope.$on('$destroy', function() {
- scrollableContainer.off('.umbStickyBar');
- $(window).off('.umbStickyBar');
- });
-
- }
-
- function determineVisibility() {
-
- var scrollTop = scrollableContainer.scrollTop();
-
- if (scrollTop > barTop) {
-
- if (!cloneIsMade) {
-
- createClone();
-
- clonedBar.css({
- 'visibility': 'visible'
- });
-
- } else {
-
- calculateSize();
-
- }
-
- } else {
-
- if (cloneIsMade) {
-
- //remove cloned element (switched places with original on creation)
- bar.remove();
- bar = clonedBar;
- clonedBar = null;
-
- bar.removeClass('-umb-sticky-bar');
- bar.css({
- position: 'relative',
- 'width': 'auto',
- 'height': 'auto',
- 'z-index': 'auto',
- 'visibility': 'visible'
- });
-
- cloneIsMade = false;
-
- }
-
- }
-
- }
-
- function calculateSize() {
- clonedBar.css({
- width: bar.outerWidth(),
- height: bar.height()
- });
- }
-
- function createClone() {
- //switch place with cloned element, to keep binding intact
- clonedBar = bar;
- bar = clonedBar.clone();
- clonedBar.after(bar);
- clonedBar.addClass('-umb-sticky-bar');
- clonedBar.css({
- 'position': 'fixed',
- 'z-index': 500,
- 'visibility': 'hidden'
- });
-
- cloneIsMade = true;
- calculateSize();
-
- }
-
- activate();
-
- }
-
- var directive = {
- restrict: 'A',
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function TableDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- scope.clickItem = function(item, $event) {
- if(scope.onClick) {
- scope.onClick(item);
- $event.stopPropagation();
- }
- };
-
- scope.selectItem = function(item, $index, $event) {
- if(scope.onSelect) {
- scope.onSelect(item, $index, $event);
- $event.stopPropagation();
- }
- };
-
- scope.selectAll = function($event) {
- if(scope.onSelectAll) {
- scope.onSelectAll($event);
- }
- };
-
- scope.isSelectedAll = function() {
- if(scope.onSelectedAll && scope.items && scope.items.length > 0) {
- return scope.onSelectedAll();
- }
- };
-
- scope.isSortDirection = function (col, direction) {
- if (scope.onSortingDirection) {
- return scope.onSortingDirection(col, direction);
- }
- };
-
- scope.sort = function(field, allow) {
- if(scope.onSort) {
- scope.onSort(field, allow);
- }
- };
-
- }
-
- var directive = {
- restrict: 'E',
- replace: true,
- templateUrl: 'views/components/umb-table.html',
- scope: {
- items: '=',
- itemProperties: '=',
- allowSelectAll: '=',
- onSelect: '=',
- onClick: '=',
- onSelectAll: '=',
- onSelectedAll: '=',
- onSortingDirection: '=',
- onSort: '='
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbTable', TableDirective);
-
-})();
-
-(function() {
- 'use strict';
-
- function TooltipDirective($timeout) {
-
- function link(scope, el, attr, ctrl) {
-
- scope.tooltipStyles = {};
- scope.tooltipStyles.left = 0;
- scope.tooltipStyles.top = 0;
-
- function activate() {
-
- $timeout(function() {
- setTooltipPosition(scope.event);
- });
-
- }
-
- function setTooltipPosition(event) {
-
- var container = $("#contentwrapper");
- var containerLeft = container[0].offsetLeft;
- var containerRight = containerLeft + container[0].offsetWidth;
- var containerTop = container[0].offsetTop;
- var containerBottom = containerTop + container[0].offsetHeight;
-
- var elementHeight = null;
- var elementWidth = null;
-
- var position = {
- right: "inherit",
- left: "inherit",
- top: "inherit",
- bottom: "inherit"
- };
-
- // element size
- elementHeight = el.context.clientHeight;
- elementWidth = el.context.clientWidth;
-
- position.left = event.pageX - (elementWidth / 2);
- position.top = event.pageY;
-
- // check to see if element is outside screen
- // outside right
- if (position.left + elementWidth > containerRight) {
- position.right = 10;
- position.left = "inherit";
- }
-
- // outside bottom
- if (position.top + elementHeight > containerBottom) {
- position.bottom = 10;
- position.top = "inherit";
- }
-
- // outside left
- if (position.left < containerLeft) {
- position.left = containerLeft + 10;
- position.right = "inherit";
- }
-
- // outside top
- if (position.top < containerTop) {
- position.top = 10;
- position.bottom = "inherit";
- }
-
- scope.tooltipStyles = position;
-
- el.css(position);
-
- }
-
- activate();
-
- }
-
- var directive = {
- restrict: 'E',
- transclude: true,
- replace: true,
- templateUrl: 'views/components/umb-tooltip.html',
- scope: {
- event: "="
- },
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbTooltip', TooltipDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbContentName
-* @restrict E
-* @function
-* @description
-* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
-**/
-
-/*
-TODO
-.directive("umbFileDrop", function ($timeout, $upload, localizationService, umbRequestHelper){
-
- return{
- restrict: "A",
- link: function(scope, element, attrs){
-
- //load in the options model
-
-
- }
- }
-})
-*/
-
-angular.module("umbraco.directives")
-
-.directive('umbFileDropzone', function ($timeout, Upload, localizationService, umbRequestHelper) {
- return {
-
- restrict: 'E',
- replace: true,
-
- templateUrl: 'views/components/upload/umb-file-dropzone.html',
-
- scope: {
- parentId: '@',
- contentTypeAlias: '@',
- propertyAlias: '@',
- accept: '@',
- maxFileSize: '@',
-
- compact: '@',
- hideDropzone: '@',
-
- filesQueued: '=',
- handleFile: '=',
- filesUploaded: '='
- },
-
- link: function(scope, element, attrs) {
-
- scope.queue = [];
- scope.done = [];
- scope.rejected = [];
- scope.currentFile = undefined;
-
- function _filterFile(file) {
-
- var ignoreFileNames = ['Thumbs.db'];
- var ignoreFileTypes = ['directory'];
-
- // ignore files with names from the list
- // ignore files with types from the list
- // ignore files which starts with "."
- if(ignoreFileNames.indexOf(file.name) === -1 &&
- ignoreFileTypes.indexOf(file.type) === -1 &&
- file.name.indexOf(".") !== 0) {
- return true;
- } else {
- return false;
- }
-
- }
-
- function _filesQueued(files, event){
-
- //Push into the queue
- angular.forEach(files, function(file){
-
- if(_filterFile(file) === true) {
-
- if(file.$error) {
- scope.rejected.push(file);
- } else {
- scope.queue.push(file);
- }
-
- }
-
- });
-
- //when queue is done, kick the uploader
- if(!scope.working){
- _processQueueItem();
- }
- }
-
-
- function _processQueueItem(){
-
- if(scope.queue.length > 0){
- scope.currentFile = scope.queue.shift();
- _upload(scope.currentFile);
- }else if(scope.done.length > 0){
-
- if(scope.filesUploaded){
- //queue is empty, trigger the done action
- scope.filesUploaded(scope.done);
- }
-
- //auto-clear the done queue after 3 secs
- var currentLength = scope.done.length;
- $timeout(function(){
- scope.done.splice(0, currentLength);
- }, 3000);
- }
- }
-
- function _upload(file) {
-
- scope.propertyAlias = scope.propertyAlias ? scope.propertyAlias : "umbracoFile";
- scope.contentTypeAlias = scope.contentTypeAlias ? scope.contentTypeAlias : "Image";
-
- Upload.upload({
- url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"),
- fields: {
- 'currentFolder': scope.parentId,
- 'contentTypeAlias': scope.contentTypeAlias,
- 'propertyAlias': scope.propertyAlias,
- 'path': file.path
- },
- file: file
- }).progress(function (evt) {
-
- // calculate progress in percentage
- var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
-
- // set percentage property on file
- file.uploadProgress = progressPercentage;
-
- // set uploading status on file
- file.uploadStatus = "uploading";
-
- }).success(function (data, status, headers, config) {
-
- if(data.notifications && data.notifications.length > 0) {
-
- // set error status on file
- file.uploadStatus = "error";
-
- // Throw message back to user with the cause of the error
- file.serverErrorMessage = data.notifications[0].message;
-
- // Put the file in the rejected pool
- scope.rejected.push(file);
-
- } else {
-
- // set done status on file
- file.uploadStatus = "done";
-
- // set date/time for when done - used for sorting
- file.doneDate = new Date();
-
- // Put the file in the done pool
- scope.done.push(file);
-
- }
-
- scope.currentFile = undefined;
-
- //after processing, test if everthing is done
- _processQueueItem();
-
- }).error( function (evt, status, headers, config) {
-
- // set status done
- file.uploadStatus = "error";
-
- //if the service returns a detailed error
- if (evt.InnerException) {
- file.serverErrorMessage = evt.InnerException.ExceptionMessage;
-
- //Check if its the common "too large file" exception
- if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
- file.serverErrorMessage = "File too large to upload";
- }
-
- } else if (evt.Message) {
- file.serverErrorMessage = evt.Message;
- }
-
- // If file not found, server will return a 404 and display this message
- if(status === 404 ) {
- file.serverErrorMessage = "File not found";
- }
-
- //after processing, test if everthing is done
- scope.rejected.push(file);
- scope.currentFile = undefined;
-
- _processQueueItem();
- });
- }
-
-
- scope.handleFiles = function(files, event){
- if(scope.filesQueued){
- scope.filesQueued(files, event);
- }
-
- _filesQueued(files, event);
-
- };
-
- }
-
-
- };
- });
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbFileUpload
-* @function
-* @restrict A
-* @scope
-* @description
-* Listens for file input control changes and emits events when files are selected for use in other controllers.
-**/
-function umbFileUpload() {
- return {
- restrict: "A",
- scope: true, //create a new scope
- link: function (scope, el, attrs) {
- el.bind('change', function (event) {
- var files = event.target.files;
- //emit event upward
- scope.$emit("filesSelected", { files: files });
- });
- }
- };
-}
-
-angular.module('umbraco.directives').directive("umbFileUpload", umbFileUpload);
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:umbFileUpload
-* @function
-* @restrict A
-* @scope
-* @description
-* A single file upload field that will reset itself based on the object passed in for the rebuild parameter. This
-* is required because the only way to reset an upload control is to replace it's html.
-**/
-function umbSingleFileUpload($compile) {
- return {
- restrict: "E",
- scope: {
- rebuild: "="
- },
- replace: true,
- template: "",
- link: function (scope, el, attrs) {
-
- scope.$watch("rebuild", function (newVal, oldVal) {
- if (newVal && newVal !== oldVal) {
- //recompile it!
- el.html("");
- $compile(el.contents())(scope);
- }
- });
-
- }
- };
-}
-
-angular.module('umbraco.directives').directive("umbSingleFileUpload", umbSingleFileUpload);
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:noDirtyCheck
-* @restrict A
-* @description Can be attached to form inputs to prevent them from setting the form as dirty (http://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs)
-**/
-function noDirtyCheck() {
- return {
- restrict: 'A',
- require: 'ngModel',
- link: function (scope, elm, attrs, ctrl) {
- elm.focus(function () {
- ctrl.$pristine = false;
- });
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck);
-(function() {
- 'use strict';
-
- function SetDirtyOnChange() {
-
- function link(scope, el, attr, ctrl) {
-
- var initValue = attr.umbSetDirtyOnChange;
-
- attr.$observe("umbSetDirtyOnChange", function (newValue) {
- if(newValue !== initValue) {
- ctrl.$setDirty();
- }
- });
-
- }
-
- var directive = {
- require: "^form",
- restrict: 'A',
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('umbSetDirtyOnChange', SetDirtyOnChange);
-
-})();
-
-/**
- * General-purpose validator for ngModel.
- * angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using
- * an arbitrary validation function requires creation of a custom formatters and / or parsers.
- * The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s).
- * A validator function will trigger validation on both model and input changes.
- *
- * @example
- * @example
- * @example
- * @example
- *
- * @param val-custom {string|object literal} If strings is passed it should be a scope's function to be used as a validator.
- * If an object literal is passed a key denotes a validation error key while a value should be a validator function.
- * In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result.
- */
-
- /*
- This code comes from the angular UI project, we had to change the directive name and module
- but other then that its unmodified
- */
-angular.module('umbraco.directives.validation')
-.directive('valCustom', function () {
-
- return {
- restrict: 'A',
- require: 'ngModel',
- link: function (scope, elm, attrs, ctrl) {
- var validateFn, watch, validators = {},
- validateExpr = scope.$eval(attrs.valCustom);
-
- if (!validateExpr){ return;}
-
- if (angular.isString(validateExpr)) {
- validateExpr = { validator: validateExpr };
- }
-
- angular.forEach(validateExpr, function (exprssn, key) {
- validateFn = function (valueToValidate) {
- var expression = scope.$eval(exprssn, { '$value' : valueToValidate });
- if (angular.isObject(expression) && angular.isFunction(expression.then)) {
- // expression is a promise
- expression.then(function(){
- ctrl.$setValidity(key, true);
- }, function(){
- ctrl.$setValidity(key, false);
- });
- return valueToValidate;
- } else if (expression) {
- // expression is true
- ctrl.$setValidity(key, true);
- return valueToValidate;
- } else {
- // expression is false
- ctrl.$setValidity(key, false);
- return undefined;
- }
- };
- validators[key] = validateFn;
-
- ctrl.$parsers.push(validateFn);
- });
-
- function apply_watch(watch)
- {
- //string - update all validators on expression change
- if (angular.isString(watch))
- {
- scope.$watch(watch, function(){
- angular.forEach(validators, function(validatorFn){
- validatorFn(ctrl.$modelValue);
- });
- });
- return;
- }
-
- //array - update all validators on change of any expression
- if (angular.isArray(watch))
- {
- angular.forEach(watch, function(expression){
- scope.$watch(expression, function()
- {
- angular.forEach(validators, function(validatorFn){
- validatorFn(ctrl.$modelValue);
- });
- });
- });
- return;
- }
-
- //object - update appropriate validator
- if (angular.isObject(watch))
- {
- angular.forEach(watch, function(expression, validatorKey)
- {
- //value is string - look after one expression
- if (angular.isString(expression))
- {
- scope.$watch(expression, function(){
- validators[validatorKey](ctrl.$modelValue);
- });
- }
-
- //value is array - look after all expressions in array
- if (angular.isArray(expression))
- {
- angular.forEach(expression, function(intExpression)
- {
- scope.$watch(intExpression, function(){
- validators[validatorKey](ctrl.$modelValue);
- });
- });
- }
- });
- }
- }
- // Support for val-custom-watch
- if (attrs.valCustomWatch){
- apply_watch( scope.$eval(attrs.valCustomWatch) );
- }
- }
- };
-});
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valHighlight
-* @restrict A
-* @description Used on input fields when you want to signal that they are in error, this will highlight the item for 1 second
-**/
-function valHighlight($timeout) {
- return {
- restrict: "A",
- link: function (scope, element, attrs, ctrl) {
-
- attrs.$observe("valHighlight", function (newVal) {
- if (newVal === "true") {
- element.addClass("highlight-error");
- $timeout(function () {
- //set the bound scope property to false
- scope[attrs.valHighlight] = false;
- }, 1000);
- }
- else {
- element.removeClass("highlight-error");
- }
- });
-
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valHighlight", valHighlight);
-
-angular.module('umbraco.directives.validation')
- .directive('valCompare',function () {
- return {
- require: "ngModel",
- link: function (scope, elem, attrs, ctrl) {
-
- //TODO: Pretty sure this should be done using a requires ^form in the directive declaration
- var otherInput = elem.inheritedData("$formController")[attrs.valCompare];
-
- ctrl.$parsers.push(function(value) {
- if(value === otherInput.$viewValue) {
- ctrl.$setValidity("valCompare", true);
- return value;
- }
- ctrl.$setValidity("valCompare", false);
- });
-
- otherInput.$parsers.push(function(value) {
- ctrl.$setValidity("valCompare", value === ctrl.$viewValue);
- return value;
- });
- }
- };
-});
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:valEmail
- * @restrict A
- * @description A custom directive to validate an email address string, this is required because angular's default validator is incorrect.
- **/
-function valEmail(valEmailExpression) {
-
- return {
- require: 'ngModel',
- restrict: "A",
- link: function (scope, elm, attrs, ctrl) {
-
- var patternValidator = function (viewValue) {
- //NOTE: we don't validate on empty values, use required validator for that
- if (!viewValue || valEmailExpression.EMAIL_REGEXP.test(viewValue)) {
- // it is valid
- ctrl.$setValidity('valEmail', true);
- //assign a message to the validator
- ctrl.errorMsg = "";
- return viewValue;
- }
- else {
- // it is invalid, return undefined (no model update)
- ctrl.$setValidity('valEmail', false);
- //assign a message to the validator
- ctrl.errorMsg = "Invalid email";
- return undefined;
- }
- };
-
- ctrl.$parsers.push(patternValidator);
- }
- };
-}
-
-angular.module('umbraco.directives.validation')
- .directive("valEmail", valEmail)
- .factory('valEmailExpression', function() {
- return {
- EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i
- };
- });
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valFormManager
-* @restrict A
-* @require formController
-* @description Used to broadcast an event to all elements inside this one to notify that form validation has
-* changed. If we don't use this that means you have to put a watch for each directive on a form's validation
-* changing which would result in much higher processing. We need to actually watch the whole $error collection of a form
-* because just watching $valid or $invalid doesn't acurrately trigger form validation changing.
-* This also sets the show-validation (or a custom) css class on the element when the form is invalid - this lets
-* us css target elements to be displayed when the form is submitting/submitted.
-* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will
-* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly.
-**/
-function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams) {
- return {
- require: "form",
- restrict: "A",
- controller: function($scope) {
- //This exposes an API for direct use with this directive
-
- var unsubscribe = [];
- var self = this;
-
- //This is basically the same as a directive subscribing to an event but maybe a little
- // nicer since the other directive can use this directive's API instead of a magical event
- this.onValidationStatusChanged = function (cb) {
- unsubscribe.push($scope.$on("valStatusChanged", function(evt, args) {
- cb.apply(self, [evt, args]);
- }));
- };
-
- //Ensure to remove the event handlers when this instance is destroyted
- $scope.$on('$destroy', function () {
- for (var u in unsubscribe) {
- unsubscribe[u]();
- }
- });
- },
- link: function (scope, element, attr, formCtrl) {
-
- scope.$watch(function () {
- return formCtrl.$error;
- }, function (e) {
- scope.$broadcast("valStatusChanged", { form: formCtrl });
-
- //find all invalid elements' .control-group's and apply the error class
- var inError = element.find(".control-group .ng-invalid").closest(".control-group");
- inError.addClass("error");
-
- //find all control group's that have no error and ensure the class is removed
- var noInError = element.find(".control-group .ng-valid").closest(".control-group").not(inError);
- noInError.removeClass("error");
-
- }, true);
-
- var className = attr.valShowValidation ? attr.valShowValidation : "show-validation";
- var savingEventName = attr.savingEvent ? attr.savingEvent : "formSubmitting";
- var savedEvent = attr.savedEvent ? attr.savingEvent : "formSubmitted";
-
- //This tracks if the user is currently saving a new item, we use this to determine
- // if we should display the warning dialog that they are leaving the page - if a new item
- // is being saved we never want to display that dialog, this will also cause problems when there
- // are server side validation issues.
- var isSavingNewItem = false;
-
- //we should show validation if there are any msgs in the server validation collection
- if (serverValidationManager.items.length > 0) {
- element.addClass(className);
- }
-
- var unsubscribe = [];
-
- //listen for the forms saving event
- unsubscribe.push(scope.$on(savingEventName, function(ev, args) {
- element.addClass(className);
-
- //set the flag so we can check to see if we should display the error.
- isSavingNewItem = $routeParams.create;
- }));
-
- //listen for the forms saved event
- unsubscribe.push(scope.$on(savedEvent, function(ev, args) {
- //remove validation class
- element.removeClass(className);
-
- //clear form state as at this point we retrieve new data from the server
- //and all validation will have cleared at this point
- formCtrl.$setPristine();
- }));
-
- //This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but
- // the form has pending changes
- var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
- if (!formCtrl.$dirty || isSavingNewItem) {
- return;
- }
-
- var path = nextLocation.split("#")[1];
- if (path) {
- if (path.indexOf("%253") || path.indexOf("%252")) {
- path = decodeURIComponent(path);
- }
-
- if (!notificationsService.hasView()) {
- var msg = { view: "confirmroutechange", args: { path: path, listener: locationEvent } };
- notificationsService.add(msg);
- }
-
- //prevent the route!
- event.preventDefault();
-
- //raise an event
- eventsService.emit("valFormManager.pendingChanges", true);
- }
-
- });
- unsubscribe.push(locationEvent);
-
- //Ensure to remove the event handler when this instance is destroyted
- scope.$on('$destroy', function() {
- for (var u in unsubscribe) {
- unsubscribe[u]();
- }
- });
-
- $timeout(function(){
- formCtrl.$setPristine();
- }, 1000);
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valFormManager", valFormManager);
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valPropertyMsg
-* @restrict A
-* @element textarea
-* @requires formController
-* @description This directive is used to control the display of the property level validation message.
-* We will listen for server side validation changes
-* and when an error is detected for this property we'll show the error message.
-* In order for this directive to work, the valStatusChanged directive must be placed on the containing form.
-**/
-function valPropertyMsg(serverValidationManager) {
-
- return {
- scope: {
- property: "="
- },
- require: "^form", //require that this directive is contained within an ngForm
- replace: true, //replace the element with the template
- restrict: "E", //restrict to element
- template: "{{errorMsg}}
",
-
- /**
- Our directive requries a reference to a form controller
- which gets passed in to this parameter
- */
- link: function (scope, element, attrs, formCtrl) {
-
- var watcher = null;
-
- // Gets the error message to display
- function getErrorMsg() {
- //this can be null if no property was assigned
- if (scope.property) {
- //first try to get the error msg from the server collection
- var err = serverValidationManager.getPropertyError(scope.property.alias, "");
- //if there's an error message use it
- if (err && err.errorMsg) {
- return err.errorMsg;
- }
- else {
- return scope.property.propertyErrorMessage ? scope.property.propertyErrorMessage : "Property has errors";
- }
-
- }
- return "Property has errors";
- }
-
- // We need to subscribe to any changes to our model (based on user input)
- // This is required because when we have a server error we actually invalidate
- // the form which means it cannot be resubmitted.
- // So once a field is changed that has a server error assigned to it
- // we need to re-validate it for the server side validator so the user can resubmit
- // the form. Of course normal client-side validators will continue to execute.
- function startWatch() {
- //if there's not already a watch
- if (!watcher) {
- watcher = scope.$watch("property.value", function (newValue, oldValue) {
-
- if (!newValue || angular.equals(newValue, oldValue)) {
- return;
- }
-
- var errCount = 0;
- for (var e in formCtrl.$error) {
- if (angular.isArray(formCtrl.$error[e])) {
- errCount++;
- }
- }
-
- //we are explicitly checking for valServer errors here, since we shouldn't auto clear
- // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
- // is the only one, then we'll clear.
-
- if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
- scope.errorMsg = "";
- formCtrl.$setValidity('valPropertyMsg', true);
- stopWatch();
- }
- }, true);
- }
- }
-
- //clear the watch when the property validator is valid again
- function stopWatch() {
- if (watcher) {
- watcher();
- watcher = null;
- }
- }
-
- //if there's any remaining errors in the server validation service then we should show them.
- var showValidation = serverValidationManager.items.length > 0;
- var hasError = false;
-
- //create properties on our custom scope so we can use it in our template
- scope.errorMsg = "";
-
- var unsubscribe = [];
-
- //listen for form error changes
- unsubscribe.push(scope.$on("valStatusChanged", function(evt, args) {
- if (args.form.$invalid) {
-
- //first we need to check if the valPropertyMsg validity is invalid
- if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) {
- //since we already have an error we'll just return since this means we've already set the
- // hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe
- return;
- }
- else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) {
- //check if it's one of the properties that is invalid in the current content property
- hasError = true;
- //update the validation message if we don't already have one assigned.
- if (showValidation && scope.errorMsg === "") {
- scope.errorMsg = getErrorMsg();
- }
- }
- else {
- hasError = false;
- scope.errorMsg = "";
- }
- }
- else {
- hasError = false;
- scope.errorMsg = "";
- }
- }, true));
-
- //listen for the forms saving event
- unsubscribe.push(scope.$on("formSubmitting", function(ev, args) {
- showValidation = true;
- if (hasError && scope.errorMsg === "") {
- scope.errorMsg = getErrorMsg();
- }
- else if (!hasError) {
- scope.errorMsg = "";
- stopWatch();
- }
- }));
-
- //listen for the forms saved event
- unsubscribe.push(scope.$on("formSubmitted", function(ev, args) {
- showValidation = false;
- scope.errorMsg = "";
- formCtrl.$setValidity('valPropertyMsg', true);
- stopWatch();
- }));
-
- //listen for server validation changes
- // NOTE: we pass in "" in order to listen for all validation changes to the content property, not for
- // validation changes to fields in the property this is because some server side validators may not
- // return the field name for which the error belongs too, just the property for which it belongs.
- // It's important to note that we need to subscribe to server validation changes here because we always must
- // indicate that a content property is invalid at the property level since developers may not actually implement
- // the correct field validation in their property editors.
-
- if (scope.property) { //this can be null if no property was assigned
- serverValidationManager.subscribe(scope.property.alias, "", function (isValid, propertyErrors, allErrors) {
- hasError = !isValid;
- if (hasError) {
- //set the error message to the server message
- scope.errorMsg = propertyErrors[0].errorMsg;
- //flag that the current validator is invalid
- formCtrl.$setValidity('valPropertyMsg', false);
- startWatch();
- }
- else {
- scope.errorMsg = "";
- //flag that the current validator is valid
- formCtrl.$setValidity('valPropertyMsg', true);
- stopWatch();
- }
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
- stopWatch();
- serverValidationManager.unsubscribe(scope.property.alias, "");
- });
- }
-
- //when the scope is disposed we need to unsubscribe
- scope.$on('$destroy', function () {
- for (var u in unsubscribe) {
- unsubscribe[u]();
- }
- });
- }
-
-
- };
-}
-angular.module('umbraco.directives.validation').directive("valPropertyMsg", valPropertyMsg);
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valPropertyValidator
-* @restrict A
-* @description Performs any custom property value validation checks on the client side. This allows property editors to be highly flexible when it comes to validation
- on the client side. Typically if a property editor stores a primitive value (i.e. string) then the client side validation can easily be taken care of
- with standard angular directives such as ng-required. However since some property editors store complex data such as JSON, a given property editor
- might require custom validation. This directive can be used to validate an Umbraco property in any way that a developer would like by specifying a
- callback method to perform the validation. The result of this method must return an object in the format of
- {isValid: true, errorKey: 'required', errorMsg: 'Something went wrong' }
- The error message returned will also be displayed for the property level validation message.
- This directive should only be used when dealing with complex models, if custom validation needs to be performed with primitive values, use the simpler
- angular validation directives instead since this will watch the entire model.
-**/
-
-function valPropertyValidator(serverValidationManager) {
- return {
- scope: {
- valPropertyValidator: "="
- },
-
- // The element must have ng-model attribute and be inside an umbProperty directive
- require: ['ngModel', '?^umbProperty'],
-
- restrict: "A",
-
- link: function (scope, element, attrs, ctrls) {
-
- var modelCtrl = ctrls[0];
- var propCtrl = ctrls.length > 1 ? ctrls[1] : null;
-
- // Check whether the scope has a valPropertyValidator method
- if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) {
- throw new Error('val-property-validator directive must specify a function to call');
- }
-
- var initResult = scope.valPropertyValidator();
-
- // Validation method
- var validate = function (viewValue) {
- // Calls the validition method
- var result = scope.valPropertyValidator();
- if (!result.errorKey || result.isValid === undefined || !result.errorMsg) {
- throw "The result object from valPropertyValidator does not contain required properties: isValid, errorKey, errorMsg";
- }
- if (result.isValid === true) {
- // Tell the controller that the value is valid
- modelCtrl.$setValidity(result.errorKey, true);
- if (propCtrl) {
- propCtrl.setPropertyError(null);
- }
- }
- else {
- // Tell the controller that the value is invalid
- modelCtrl.$setValidity(result.errorKey, false);
- if (propCtrl) {
- propCtrl.setPropertyError(result.errorMsg);
- }
- }
- };
-
- // Parsers are called as soon as the value in the form input is modified
- modelCtrl.$parsers.push(validate);
-
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valPropertyValidator", valPropertyValidator);
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:valRegex
- * @restrict A
- * @description A custom directive to allow for matching a value against a regex string.
- * NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string
- **/
-function valRegex() {
-
- return {
- require: 'ngModel',
- restrict: "A",
- link: function (scope, elm, attrs, ctrl) {
-
- var flags = "";
- var regex;
- var eventBindings = [];
-
- attrs.$observe("valRegexFlags", function (newVal) {
- if (newVal) {
- flags = newVal;
- }
- });
-
- attrs.$observe("valRegex", function (newVal) {
- if (newVal) {
- try {
- var resolved = newVal;
- if (resolved) {
- regex = new RegExp(resolved, flags);
- }
- else {
- regex = new RegExp(attrs.valRegex, flags);
- }
- }
- catch (e) {
- regex = new RegExp(attrs.valRegex, flags);
- }
- }
- });
-
- eventBindings.push(scope.$watch('ngModel', function(newValue, oldValue){
- if(newValue && newValue !== oldValue) {
- patternValidator(newValue);
- }
- }));
-
- var patternValidator = function (viewValue) {
- if (regex) {
- //NOTE: we don't validate on empty values, use required validator for that
- if (!viewValue || regex.test(viewValue.toString())) {
- // it is valid
- ctrl.$setValidity('valRegex', true);
- //assign a message to the validator
- ctrl.errorMsg = "";
- return viewValue;
- }
- else {
- // it is invalid, return undefined (no model update)
- ctrl.$setValidity('valRegex', false);
- //assign a message to the validator
- ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
- return undefined;
- }
- }
- };
-
- scope.$on('$destroy', function(){
- // unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
- });
-
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valRegex", valRegex);
-
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:valServer
- * @restrict A
- * @description This directive is used to associate a content property with a server-side validation response
- * so that the validators in angular are updated based on server-side feedback.
- **/
-function valServer(serverValidationManager) {
- return {
- require: ['ngModel', '?^umbProperty'],
- restrict: "A",
- link: function (scope, element, attr, ctrls) {
-
- var modelCtrl = ctrls[0];
- var umbPropCtrl = ctrls.length > 1 ? ctrls[1] : null;
- if (!umbPropCtrl) {
- //we cannot proceed, this validator will be disabled
- return;
- }
-
- var watcher = null;
-
- //Need to watch the value model for it to change, previously we had subscribed to
- //modelCtrl.$viewChangeListeners but this is not good enough if you have an editor that
- // doesn't specifically have a 2 way ng binding. This is required because when we
- // have a server error we actually invalidate the form which means it cannot be
- // resubmitted. So once a field is changed that has a server error assigned to it
- // we need to re-validate it for the server side validator so the user can resubmit
- // the form. Of course normal client-side validators will continue to execute.
- function startWatch() {
- //if there's not already a watch
- if (!watcher) {
- watcher = scope.$watch(function () {
- return modelCtrl.$modelValue;
- }, function (newValue, oldValue) {
-
- if (!newValue || angular.equals(newValue, oldValue)) {
- return;
- }
-
- if (modelCtrl.$invalid) {
- modelCtrl.$setValidity('valServer', true);
- stopWatch();
- }
- }, true);
- }
- }
-
- function stopWatch() {
- if (watcher) {
- watcher();
- watcher = null;
- }
- }
-
- var currentProperty = umbPropCtrl.property;
-
- //default to 'value' if nothing is set
- var fieldName = "value";
- if (attr.valServer) {
- fieldName = scope.$eval(attr.valServer);
- if (!fieldName) {
- //eval returned nothing so just use the string
- fieldName = attr.valServer;
- }
- }
-
- //subscribe to the server validation changes
- serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) {
- if (!isValid) {
- modelCtrl.$setValidity('valServer', false);
- //assign an error msg property to the current validator
- modelCtrl.errorMsg = propertyErrors[0].errorMsg;
- startWatch();
- }
- else {
- modelCtrl.$setValidity('valServer', true);
- //reset the error message
- modelCtrl.errorMsg = "";
- stopWatch();
- }
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
- stopWatch();
- serverValidationManager.unsubscribe(currentProperty.alias, fieldName);
- });
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valServer", valServer);
-/**
- * @ngdoc directive
- * @name umbraco.directives.directive:valServerField
- * @restrict A
- * @description This directive is used to associate a content field (not user defined) with a server-side validation response
- * so that the validators in angular are updated based on server-side feedback.
- **/
-function valServerField(serverValidationManager) {
- return {
- require: 'ngModel',
- restrict: "A",
- link: function (scope, element, attr, ctrl) {
-
- var fieldName = null;
- var eventBindings = [];
-
- attr.$observe("valServerField", function (newVal) {
- if (newVal && fieldName === null) {
- fieldName = newVal;
-
- //subscribe to the changed event of the view model. This is required because when we
- // have a server error we actually invalidate the form which means it cannot be
- // resubmitted. So once a field is changed that has a server error assigned to it
- // we need to re-validate it for the server side validator so the user can resubmit
- // the form. Of course normal client-side validators will continue to execute.
- eventBindings.push(scope.$watch('ngModel', function(newValue){
- if (ctrl.$invalid) {
- ctrl.$setValidity('valServerField', true);
- }
- }));
-
- //subscribe to the server validation changes
- serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
- if (!isValid) {
- ctrl.$setValidity('valServerField', false);
- //assign an error msg property to the current validator
- ctrl.errorMsg = fieldErrors[0].errorMsg;
- }
- else {
- ctrl.$setValidity('valServerField', true);
- //reset the error message
- ctrl.errorMsg = "";
- }
- });
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
- // but they are a different callback instance than the above.
- element.bind('$destroy', function () {
- serverValidationManager.unsubscribe(null, fieldName);
- });
- }
- });
-
- scope.$on('$destroy', function(){
- // unbind watchers
- for(var e in eventBindings) {
- eventBindings[e]();
- }
- });
-
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valServerField", valServerField);
-
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valTab
-* @restrict A
-* @description Used to show validation warnings for a tab to indicate that the tab content has validations errors in its data.
-* In order for this directive to work, the valFormManager directive must be placed on the containing form.
-**/
-function valTab() {
- return {
- require: ['^form', '^valFormManager'],
- restrict: "A",
- link: function (scope, element, attr, ctrs) {
-
- var valFormManager = ctrs[1];
- var tabId = "tab" + scope.tab.id;
- scope.tabHasError = false;
-
- //listen for form validation changes
- valFormManager.onValidationStatusChanged(function (evt, args) {
- if (!args.form.$valid) {
- var tabContent = element.closest(".umb-panel").find("#" + tabId);
- //check if the validation messages are contained inside of this tabs
- if (tabContent.find(".ng-invalid").length > 0) {
- scope.tabHasError = true;
- } else {
- scope.tabHasError = false;
- }
- }
- else {
- scope.tabHasError = false;
- }
- });
-
- }
- };
-}
-angular.module('umbraco.directives.validation').directive("valTab", valTab);
-function valToggleMsg(serverValidationManager) {
- return {
- require: "^form",
- restrict: "A",
-
- /**
- Our directive requries a reference to a form controller which gets passed in to this parameter
- */
- link: function (scope, element, attr, formCtrl) {
-
- if (!attr.valToggleMsg){
- throw "valToggleMsg requires that a reference to a validator is specified";
- }
- if (!attr.valMsgFor){
- throw "valToggleMsg requires that the attribute valMsgFor exists on the element";
- }
- if (!formCtrl[attr.valMsgFor]) {
- throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name;
- }
-
- //if there's any remaining errors in the server validation service then we should show them.
- var showValidation = serverValidationManager.items.length > 0;
- var hasCustomMsg = element.contents().length > 0;
-
- //add a watch to the validator for the value (i.e. myForm.value.$error.required )
- scope.$watch(function () {
- //sometimes if a dialog closes in the middle of digest we can get null references here
-
- return (formCtrl && formCtrl[attr.valMsgFor]) ? formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] : null;
- }, function () {
- //sometimes if a dialog closes in the middle of digest we can get null references here
- if ((formCtrl && formCtrl[attr.valMsgFor])) {
- if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) {
- element.show();
- //display the error message if this element has no contents
- if (!hasCustomMsg) {
- element.html(formCtrl[attr.valMsgFor].errorMsg);
- }
- }
- else {
- element.hide();
- }
- }
- });
-
- var unsubscribe = [];
-
- //listen for the saving event (the result is a callback method which is called to unsubscribe)
- unsubscribe.push(scope.$on("formSubmitting", function(ev, args) {
- showValidation = true;
- if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) {
- element.show();
- //display the error message if this element has no contents
- if (!hasCustomMsg) {
- element.html(formCtrl[attr.valMsgFor].errorMsg);
- }
- }
- else {
- element.hide();
- }
- }));
-
- //listen for the saved event (the result is a callback method which is called to unsubscribe)
- unsubscribe.push(scope.$on("formSubmitted", function(ev, args) {
- showValidation = false;
- element.hide();
- }));
-
- //when the element is disposed we need to unsubscribe!
- // NOTE: this is very important otherwise if this directive is part of a modal, the listener still exists because the dom
- // element might still be there even after the modal has been hidden.
- element.bind('$destroy', function () {
- for (var u in unsubscribe) {
- unsubscribe[u]();
- }
- });
-
- }
- };
-}
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valToggleMsg
-* @restrict A
-* @element input
-* @requires formController
-* @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ?
-**/
-angular.module('umbraco.directives.validation').directive("valToggleMsg", valToggleMsg);
-angular.module('umbraco.directives.validation')
-.directive('valTriggerChange', function($sniffer) {
- return {
- link : function(scope, elem, attrs) {
- elem.bind('click', function(){
- $(attrs.valTriggerChange).trigger($sniffer.hasEvent('input') ? 'input' : 'change');
- });
- },
- priority : 1
- };
-});
-/**
- * Konami Code directive for AngularJS
- * @version v0.0.1
- * @license MIT License, http://www.opensource.org/licenses/MIT
- */
-
-angular.module('umbraco.directives')
- .directive('konamiCode', ['$document', function ($document) {
- var konamiKeysDefault = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
-
- return {
- restrict: 'A',
- link: function (scope, element, attr) {
-
- if (!attr.konamiCode) {
- throw ('Konami directive must receive an expression as value.');
- }
-
- // Let user define a custom code.
- var konamiKeys = attr.konamiKeys || konamiKeysDefault;
- var keyIndex = 0;
-
- /**
- * Fired when konami code is type.
- */
- function activated() {
- if ('konamiOnce' in attr) {
- stopListening();
- }
- // Execute expression.
- scope.$eval(attr.konamiCode);
- }
-
- /**
- * Handle keydown events.
- */
- function keydown(e) {
- if (e.keyCode === konamiKeys[keyIndex++]) {
- if (keyIndex === konamiKeys.length) {
- keyIndex = 0;
- activated();
- }
- } else {
- keyIndex = 0;
- }
- }
-
- /**
- * Stop to listen typing.
- */
- function stopListening() {
- $document.off('keydown', keydown);
- }
-
- // Start listening to key typing.
- $document.on('keydown', keydown);
-
- // Stop listening when scope is destroyed.
- scope.$on('$destroy', stopListening);
- }
- };
- }]);
-(function() {
- 'use strict';
-
- function ValRequireComponentDirective() {
-
- function link(scope, el, attr, ngModel) {
-
- var unbindModelWatcher = scope.$watch(function () {
- return ngModel.$modelValue;
- }, function(newValue) {
-
- if(newValue === undefined || newValue === null || newValue === "") {
- ngModel.$setValidity("valRequiredComponent", false);
- } else {
- ngModel.$setValidity("valRequiredComponent", true);
- }
-
- });
-
- // clean up
- scope.$on('$destroy', function(){
- unbindModelWatcher();
- });
-
- }
-
- var directive = {
- require: 'ngModel',
- restrict: "A",
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('valRequireComponent', ValRequireComponentDirective);
-
-})();
-
-/**
-* @ngdoc directive
-* @name umbraco.directives.directive:valSubView
-* @restrict A
-* @description Used to show validation warnings for a editor sub view to indicate that the section content has validation errors in its data.
-* In order for this directive to work, the valFormManager directive must be placed on the containing form.
-**/
-(function() {
- 'use strict';
-
- function valSubViewDirective() {
-
- function link(scope, el, attr, ctrl) {
-
- var valFormManager = ctrl[1];
- scope.subView.hasError = false;
-
- //listen for form validation changes
- valFormManager.onValidationStatusChanged(function (evt, args) {
- if (!args.form.$valid) {
-
- var subViewContent = el.find(".ng-invalid");
-
- if (subViewContent.length > 0) {
- scope.subView.hasError = true;
- } else {
- scope.subView.hasError = false;
- }
-
- }
- else {
- scope.subView.hasError = false;
- }
- });
-
- }
-
- var directive = {
- require: ['^form', '^valFormManager'],
- restrict: "A",
- link: link
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('valSubView', valSubViewDirective);
-
-})();
-
-
+/*! umbraco
+ * https://github.com/umbraco/umbraco-cms/
+ * Copyright (c) 2016 Umbraco HQ;
+ * Licensed
+ */
+
+(function() {
+
+angular.module("umbraco.directives", ["umbraco.directives.editors", "umbraco.directives.html", "umbraco.directives.validation", "ui.sortable"]);
+angular.module("umbraco.directives.editors", []);
+angular.module("umbraco.directives.html", []);
+angular.module("umbraco.directives.validation", []);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:autoScale
+* @element div
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @function
+* @description
+* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
+* So if you only want to scale the div to 70 pixels from the bottom you pass "70"
+
+* @example
+*
+*
+*
+*
+*
+**/
+
+angular.module("umbraco.directives")
+ .directive('autoScale', function ($window) {
+ return function (scope, el, attrs) {
+
+ var totalOffset = 0;
+ var offsety = parseInt(attrs.autoScale, 10);
+ var window = angular.element($window);
+ if (offsety !== undefined){
+ totalOffset += offsety;
+ }
+
+ setTimeout(function () {
+ el.height(window.height() - (el.offset().top + totalOffset));
+ }, 500);
+
+ window.bind("resize", function () {
+ el.height(window.height() - (el.offset().top + totalOffset));
+ });
+
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:detectFold
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @description This is used for the editor buttons to ensure they are displayed correctly if the horizontal overflow of the editor
+* exceeds the height of the window
+**/
+
+angular.module("umbraco.directives.html")
+ .directive('detectFold', function ($timeout, $log, windowResizeListener) {
+ return {
+ require: "^?umbTabs",
+ restrict: 'A',
+ link: function (scope, el, attrs, tabsCtrl) {
+
+ var firstRun = false;
+ var parent = $(".umb-panel-body");
+ var winHeight = $(window).height();
+ var calculate = function () {
+ if (el && el.is(":visible") && !el.hasClass("umb-bottom-bar")) {
+
+ //now that the element is visible, set the flag in a couple of seconds,
+ // this will ensure that loading time of a current tab get's completed and that
+ // we eventually stop watching to save on CPU time
+ $timeout(function() {
+ firstRun = true;
+ }, 4000);
+
+ //var parent = el.parent();
+ var hasOverflow = parent.innerHeight() < parent[0].scrollHeight;
+ //var belowFold = (el.offset().top + el.height()) > winHeight;
+ if (hasOverflow) {
+ el.addClass("umb-bottom-bar");
+
+ //I wish we didn't have to put this logic here but unfortunately we
+ // do. This needs to calculate the left offest to place the bottom bar
+ // depending on if the left column splitter has been moved by the user
+ // (based on the nav-resize directive)
+ var wrapper = $("#mainwrapper");
+ var contentPanel = $("#leftcolumn").next();
+ var contentPanelLeftPx = contentPanel.css("left");
+
+ el.css({ left: contentPanelLeftPx });
+ }
+ }
+ return firstRun;
+ };
+
+ var resizeCallback = function(size) {
+ winHeight = size.height;
+ el.removeClass("umb-bottom-bar");
+ calculate();
+ };
+
+ windowResizeListener.register(resizeCallback);
+
+ //Only execute the watcher if this tab is the active (first) tab on load, otherwise there's no reason to execute
+ // the watcher since it will be recalculated when the tab changes!
+ if (el.closest(".umb-tab-pane").index() === 0) {
+ //run a watcher to ensure that the calculation occurs until it's firstRun but ensure
+ // the calculations are throttled to save a bit of CPU
+ var listener = scope.$watch(_.throttle(calculate, 1000), function (newVal, oldVal) {
+ if (newVal !== oldVal) {
+ listener();
+ }
+ });
+ }
+
+ //listen for tab changes
+ if (tabsCtrl != null) {
+ tabsCtrl.onTabShown(function (args) {
+ calculate();
+ });
+ }
+
+ //ensure to unregister
+ scope.$on('$destroy', function() {
+ windowResizeListener.unregister(resizeCallback);
+ });
+ }
+ };
+ });
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbItemSorter
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @function
+* @element ANY
+* @restrict E
+* @description A re-usable directive for sorting items
+**/
+
+function umbItemSorter(angularHelper) {
+ return {
+ scope: {
+ model: "="
+ },
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/directives/_obsolete/umb-item-sorter.html',
+ link: function(scope, element, attrs, ctrl) {
+ var defaultModel = {
+ okButton: "Ok",
+ successMsg: "Sorting successful",
+ complete: false
+ };
+ //assign user vals to default
+ angular.extend(defaultModel, scope.model);
+ //re-assign merged to user
+ scope.model = defaultModel;
+
+ scope.performSort = function() {
+ scope.$emit("umbItemSorter.sorting", {
+ sortedItems: scope.model.itemsToSort
+ });
+ };
+
+ scope.handleCancel = function () {
+ scope.$emit("umbItemSorter.cancel");
+ };
+
+ scope.handleOk = function() {
+ scope.$emit("umbItemSorter.ok");
+ };
+
+ //defines the options for the jquery sortable
+ scope.sortableOptions = {
+ axis: 'y',
+ cursor: "move",
+ placeholder: "ui-sortable-placeholder",
+ update: function (ev, ui) {
+ //highlight the item when the position is changed
+ $(ui.item).effect("highlight", { color: "#049cdb" }, 500);
+ },
+ stop: function (ev, ui) {
+ //the ui-sortable directive already ensures that our list is re-sorted, so now we just
+ // need to update the sortOrder to the index of each item
+ angularHelper.safeApply(scope, function () {
+ angular.forEach(scope.itemsToSort, function (val, index) {
+ val.sortOrder = index + 1;
+ });
+
+ });
+ }
+ };
+ }
+ };
+}
+
+angular.module('umbraco.directives').directive("umbItemSorter", umbItemSorter);
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbContentName
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @restrict E
+* @function
+* @description
+* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
+**/
+
+angular.module("umbraco.directives")
+ .directive('umbContentName', function ($timeout, localizationService) {
+ return {
+ require: "ngModel",
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/directives/_obsolete/umb-content-name.html',
+
+ scope: {
+ placeholder: '@placeholder',
+ model: '=ngModel',
+ ngDisabled: '='
+ },
+ link: function(scope, element, attrs, ngModel) {
+
+ var inputElement = element.find("input");
+ if(scope.placeholder && scope.placeholder[0] === "@"){
+ localizationService.localize(scope.placeholder.substring(1))
+ .then(function(value){
+ scope.placeholder = value;
+ });
+ }
+
+ var mX, mY, distance;
+
+ function calculateDistance(elem, mouseX, mouseY) {
+
+ var cx = Math.max(Math.min(mouseX, elem.offset().left + elem.width()), elem.offset().left);
+ var cy = Math.max(Math.min(mouseY, elem.offset().top + elem.height()), elem.offset().top);
+ return Math.sqrt((mouseX - cx) * (mouseX - cx) + (mouseY - cy) * (mouseY - cy));
+ }
+
+ var mouseMoveDebounce = _.throttle(function (e) {
+ mX = e.pageX;
+ mY = e.pageY;
+ // not focused and not over element
+ if (!inputElement.is(":focus") && !inputElement.hasClass("ng-invalid")) {
+ // on page
+ if (mX >= inputElement.offset().left) {
+ distance = calculateDistance(inputElement, mX, mY);
+ if (distance <= 155) {
+
+ distance = 1 - (100 / 150 * distance / 100);
+ inputElement.css("border", "1px solid rgba(175,175,175, " + distance + ")");
+ inputElement.css("background-color", "rgba(255,255,255, " + distance + ")");
+ }
+ }
+
+ }
+
+ }, 15);
+
+ $(document).bind("mousemove", mouseMoveDebounce);
+
+ $timeout(function(){
+ if(!scope.model){
+ scope.goEdit();
+ }
+ }, 100, false);
+
+ scope.goEdit = function(){
+ scope.editMode = true;
+
+ $timeout(function () {
+ inputElement.focus();
+ }, 100, false);
+ };
+
+ scope.exitEdit = function(){
+ if(scope.model && scope.model !== ""){
+ scope.editMode = false;
+ }
+ };
+
+ //unbind doc event!
+ scope.$on('$destroy', function () {
+ $(document).unbind("mousemove", mouseMoveDebounce);
+ });
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbHeader
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @restrict E
+* @function
+* @description
+* The header on an editor that contains tabs using bootstrap tabs - THIS IS OBSOLETE, use umbTabHeader instead
+**/
+
+angular.module("umbraco.directives")
+.directive('umbHeader', function ($parse, $timeout) {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: 'true',
+ templateUrl: 'views/directives/_obsolete/umb-header.html',
+ //create a new isolated scope assigning a tabs property from the attribute 'tabs'
+ //which is bound to the parent scope property passed in
+ scope: {
+ tabs: "="
+ },
+ link: function (scope, iElement, iAttrs) {
+
+ scope.showTabs = iAttrs.tabs ? true : false;
+ scope.visibleTabs = [];
+
+ //since tabs are loaded async, we need to put a watch on them to determine
+ // when they are loaded, then we can close the watch
+ var tabWatch = scope.$watch("tabs", function (newValue, oldValue) {
+
+ angular.forEach(newValue, function(val, index){
+ var tab = {id: val.id, label: val.label};
+ scope.visibleTabs.push(tab);
+ });
+
+ //don't process if we cannot or have already done so
+ if (!newValue) {return;}
+ if (!newValue.length || newValue.length === 0){return;}
+
+ //we need to do a timeout here so that the current sync operation can complete
+ // and update the UI, then this will fire and the UI elements will be available.
+ $timeout(function () {
+
+ //use bootstrap tabs API to show the first one
+ iElement.find(".nav-tabs a:first").tab('show');
+
+ //enable the tab drop
+ iElement.find('.nav-pills, .nav-tabs').tabdrop();
+
+ //ensure to destroy tabdrop (unbinds window resize listeners)
+ scope.$on('$destroy', function () {
+ iElement.find('.nav-pills, .nav-tabs').tabdrop("destroy");
+ });
+
+ //stop watching now
+ tabWatch();
+ }, 200);
+
+ });
+ }
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbLogin
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @function
+* @element ANY
+* @restrict E
+**/
+
+function loginDirective() {
+ return {
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/directives/_obsolete/umb-login.html'
+ };
+}
+
+angular.module('umbraco.directives').directive("umbLogin", loginDirective);
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbOptionsMenu
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @function
+* @element ANY
+* @restrict E
+**/
+
+angular.module("umbraco.directives")
+.directive('umbOptionsMenu', function ($injector, treeService, navigationService, umbModelMapper, appState) {
+ return {
+ scope: {
+ currentSection: "@",
+ currentNode: "="
+ },
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/directives/_obsolete/umb-optionsmenu.html',
+ link: function (scope, element, attrs, ctrl) {
+
+ //adds a handler to the context menu item click, we need to handle this differently
+ //depending on what the menu item is supposed to do.
+ scope.executeMenuItem = function (action) {
+ navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
+ };
+
+ //callback method to go and get the options async
+ scope.getOptions = function () {
+
+ if (!scope.currentNode) {
+ return;
+ }
+
+ //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
+ appState.setMenuState("currentNode", scope.currentNode);
+
+ if (!scope.actions) {
+ treeService.getMenu({ treeNode: scope.currentNode })
+ .then(function (data) {
+ scope.actions = data.menuItems;
+ });
+ }
+ };
+
+ }
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbPhotoFolder
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+* @restrict E
+**/
+
+angular.module("umbraco.directives.html")
+ .directive('umbPhotoFolder', function($compile, $log, $timeout, $filter, umbPhotoFolderHelper) {
+
+ return {
+ restrict: 'E',
+ replace: true,
+ require: '?ngModel',
+ terminate: true,
+ templateUrl: 'views/directives/_obsolete/umb-photo-folder.html',
+ link: function(scope, element, attrs, ngModel) {
+
+ var lastWatch = null;
+
+ ngModel.$render = function() {
+ if (ngModel.$modelValue) {
+
+ $timeout(function() {
+ var photos = ngModel.$modelValue;
+
+ scope.clickHandler = scope.$eval(element.attr('on-click'));
+
+
+ var imagesOnly = element.attr('images-only') === "true";
+
+
+ var margin = element.attr('border') ? parseInt(element.attr('border'), 10) : 5;
+ var startingIndex = element.attr('baseline') ? parseInt(element.attr('baseline'), 10) : 0;
+ var minWidth = element.attr('min-width') ? parseInt(element.attr('min-width'), 10) : 420;
+ var minHeight = element.attr('min-height') ? parseInt(element.attr('min-height'), 10) : 100;
+ var maxHeight = element.attr('max-height') ? parseInt(element.attr('max-height'), 10) : 300;
+ var idealImgPerRow = element.attr('ideal-items-per-row') ? parseInt(element.attr('ideal-items-per-row'), 10) : 5;
+ var fixedRowWidth = Math.max(element.width(), minWidth);
+
+ scope.containerStyle = { width: fixedRowWidth + "px" };
+ scope.rows = umbPhotoFolderHelper.buildGrid(photos, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
+
+ if (attrs.filterBy) {
+
+ //we track the watches that we create, we don't want to create multiple, so clear it
+ // if it already exists before creating another.
+ if (lastWatch) {
+ lastWatch();
+ }
+
+ //TODO: Need to debounce this so it doesn't filter too often!
+ lastWatch = scope.$watch(attrs.filterBy, function (newVal, oldVal) {
+ if (newVal && newVal !== oldVal) {
+ var p = $filter('filter')(photos, newVal, false);
+ scope.baseline = 0;
+ var m = umbPhotoFolderHelper.buildGrid(p, fixedRowWidth, maxHeight, startingIndex, minHeight, idealImgPerRow, margin, imagesOnly);
+ scope.rows = m;
+ }
+ });
+ }
+
+ }, 500); //end timeout
+ } //end if modelValue
+
+ }; //end $render
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbSort
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+*
+* @element div
+* @function
+*
+* @description
+* Resize div's automatically to fit to the bottom of the screen, as an optional parameter an y-axis offset can be set
+* So if you only want to scale the div to 70 pixels from the bottom you pass "70"
+*
+* @example
+*
+*
+*
+*
+*
+**/
+
+angular.module("umbraco.directives")
+ .value('umbSortContextInternal',{})
+ .directive('umbSort', function($log,umbSortContextInternal) {
+ return {
+ require: '?ngModel',
+ link: function(scope, element, attrs, ngModel) {
+ var adjustment;
+
+ var cfg = scope.$eval(element.attr('umb-sort')) || {};
+
+ scope.model = ngModel;
+
+ scope.opts = cfg;
+ scope.opts.containerSelector= cfg.containerSelector || ".umb-" + cfg.group + "-container",
+ scope.opts.nested= cfg.nested || true,
+ scope.opts.drop= cfg.drop || true,
+ scope.opts.drag= cfg.drag || true,
+ scope.opts.clone = cfg.clone || "";
+ scope.opts.mode = cfg.mode || "list";
+
+ scope.opts.itemSelectorFull = $.trim(scope.opts.itemPath + " " + scope.opts.itemSelector);
+
+ /*
+ scope.opts.isValidTarget = function(item, container) {
+ if(container.el.is(".umb-" + scope.opts.group + "-container")){
+ return true;
+ }
+ return false;
+ };
+ */
+
+ element.addClass("umb-sort");
+ element.addClass("umb-" + cfg.group + "-container");
+
+ scope.opts.onDrag = function (item, position) {
+ if(scope.opts.mode === "list"){
+ item.css({
+ left: position.left - adjustment.left,
+ top: position.top - adjustment.top
+ });
+ }
+ };
+
+
+ scope.opts.onDrop = function (item, targetContainer, _super) {
+
+ if(scope.opts.mode === "list"){
+ //list mode
+ var clonedItem = $(scope.opts.clone).css({height: 0});
+ item.after(clonedItem);
+ clonedItem.animate({'height': item.height()});
+
+ item.animate(clonedItem.position(), function () {
+ clonedItem.detach();
+ _super(item);
+ });
+ }
+
+ var children = $(scope.opts.itemSelectorFull, targetContainer.el);
+ var targetIndex = children.index(item);
+ var targetScope = $(targetContainer.el[0]).scope();
+
+
+ if(targetScope === umbSortContextInternal.sourceScope){
+ if(umbSortContextInternal.sourceScope.opts.onSortHandler){
+ var _largs = {
+ oldIndex: umbSortContextInternal.sourceIndex,
+ newIndex: targetIndex,
+ scope: umbSortContextInternal.sourceScope
+ };
+
+ umbSortContextInternal.sourceScope.opts.onSortHandler.call(this, item, _largs);
+ }
+ }else{
+
+
+ if(targetScope.opts.onDropHandler){
+ var args = {
+ sourceScope: umbSortContextInternal.sourceScope,
+ sourceIndex: umbSortContextInternal.sourceIndex,
+ sourceContainer: umbSortContextInternal.sourceContainer,
+
+ targetScope: targetScope,
+ targetIndex: targetIndex,
+ targetContainer: targetContainer
+ };
+
+ targetScope.opts.onDropHandler.call(this, item, args);
+ }
+
+ if(umbSortContextInternal.sourceScope.opts.onReleaseHandler){
+ var _args = {
+ sourceScope: umbSortContextInternal.sourceScope,
+ sourceIndex: umbSortContextInternal.sourceIndex,
+ sourceContainer: umbSortContextInternal.sourceContainer,
+
+ targetScope: targetScope,
+ targetIndex: targetIndex,
+ targetContainer: targetContainer
+ };
+
+ umbSortContextInternal.sourceScope.opts.onReleaseHandler.call(this, item, _args);
+ }
+ }
+ };
+
+ scope.changeIndex = function(from, to){
+ scope.$apply(function(){
+ var i = ngModel.$modelValue.splice(from, 1)[0];
+ ngModel.$modelValue.splice(to, 0, i);
+ });
+ };
+
+ scope.move = function(args){
+ var from = args.sourceIndex;
+ var to = args.targetIndex;
+
+ if(args.sourceContainer === args.targetContainer){
+ scope.changeIndex(from, to);
+ }else{
+ scope.$apply(function(){
+ var i = args.sourceScope.model.$modelValue.splice(from, 1)[0];
+ args.targetScope.model.$modelvalue.splice(to,0, i);
+ });
+ }
+ };
+
+ scope.opts.onDragStart = function (item, container, _super) {
+ var children = $(scope.opts.itemSelectorFull, container.el);
+ var offset = item.offset();
+
+ umbSortContextInternal.sourceIndex = children.index(item);
+ umbSortContextInternal.sourceScope = $(container.el[0]).scope();
+ umbSortContextInternal.sourceContainer = container;
+
+ //current.item = ngModel.$modelValue.splice(current.index, 1)[0];
+
+ var pointer = container.rootGroup.pointer;
+ adjustment = {
+ left: pointer.left - offset.left,
+ top: pointer.top - offset.top
+ };
+
+ _super(item, container);
+ };
+
+ element.sortable( scope.opts );
+ }
+ };
+
+ });
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTabView
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+*
+* @restrict E
+**/
+
+angular.module("umbraco.directives")
+.directive('umbTabView', function($timeout, $log){
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: 'true',
+ templateUrl: 'views/directives/_obsolete/umb-tab-view.html'
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbUploadDropzone
+* @deprecated
+* We plan to remove this directive in the next major version of umbraco (8.0). The directive is not recommended to use.
+*
+* @restrict E
+**/
+
+angular.module("umbraco.directives.html")
+ .directive('umbUploadDropzone', function(){
+ return {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/directives/_obsolete/umb-upload-dropzone.html'
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:navResize
+* @restrict A
+ *
+ * @description
+ * Handles how the navigation responds to window resizing and controls how the draggable resize panel works
+**/
+angular.module("umbraco.directives")
+ .directive('navResize', function (appState, eventsService, windowResizeListener) {
+ return {
+ restrict: 'A',
+ link: function (scope, element, attrs, ctrl) {
+
+ var minScreenSize = 1100;
+ var resizeEnabled = false;
+
+ function setTreeMode() {
+ appState.setGlobalState("showNavigation", appState.getGlobalState("isTablet") === false);
+ }
+
+ function enableResize() {
+ //only enable when the size is correct and it's not already enabled
+ if (!resizeEnabled && appState.getGlobalState("isTablet") === false) {
+ element.resizable(
+ {
+ containment: $("#mainwrapper"),
+ autoHide: true,
+ handles: "e",
+ alsoResize: ".navigation-inner-container",
+ resize: function(e, ui) {
+ var wrapper = $("#mainwrapper");
+ var contentPanel = $("#contentwrapper");
+ var umbNotification = $("#umb-notifications-wrapper");
+ var apps = $("#applications");
+ var bottomBar = contentPanel.find(".umb-bottom-bar");
+ var navOffeset = $("#navOffset");
+
+ var leftPanelWidth = ui.element.width() + apps.width();
+
+ contentPanel.css({ left: leftPanelWidth });
+ bottomBar.css({ left: leftPanelWidth });
+ umbNotification.css({ left: leftPanelWidth });
+
+ navOffeset.css({ "margin-left": ui.element.outerWidth() });
+ },
+ stop: function (e, ui) {
+
+ }
+ });
+
+ resizeEnabled = true;
+ }
+ }
+
+ function resetResize() {
+ if (resizeEnabled) {
+ //kill the resize
+ element.resizable("destroy");
+ element.css("width", "");
+
+ var navInnerContainer = element.find(".navigation-inner-container");
+
+ navInnerContainer.css("width", "");
+ $("#contentwrapper").css("left", "");
+ $("#umb-notifications-wrapper").css("left", "");
+ $("#navOffset").css("margin-left", "");
+
+ resizeEnabled = false;
+ }
+ }
+
+ var evts = [];
+
+ //Listen for global state changes
+ evts.push(eventsService.on("appState.globalState.changed", function (e, args) {
+ if (args.key === "showNavigation") {
+ if (args.value === false) {
+ resetResize();
+ }
+ else {
+ enableResize();
+ }
+ }
+ }));
+
+ var resizeCallback = function(size) {
+ //set the global app state
+ appState.setGlobalState("isTablet", (size.width <= minScreenSize));
+ setTreeMode();
+ };
+
+ windowResizeListener.register(resizeCallback);
+
+ //ensure to unregister from all events and kill jquery plugins
+ scope.$on('$destroy', function () {
+ windowResizeListener.unregister(resizeCallback);
+ for (var e in evts) {
+ eventsService.unsubscribe(evts[e]);
+ }
+ var navInnerContainer = element.find(".navigation-inner-container");
+ navInnerContainer.resizable("destroy");
+ });
+
+ //init
+ //set the global app state
+ appState.setGlobalState("isTablet", ($(window).width() <= minScreenSize));
+ setTreeMode();
+ }
+ };
+ });
+
+angular.module("umbraco.directives")
+.directive('sectionIcon', function ($compile, iconHelper) {
+ return {
+ restrict: 'E',
+ replace: true,
+
+ link: function (scope, element, attrs) {
+
+ var icon = attrs.icon;
+
+ if (iconHelper.isLegacyIcon(icon)) {
+ //its a known legacy icon, convert to a new one
+ element.html("");
+ }
+ else if (iconHelper.isFileBasedIcon(icon)) {
+ var convert = iconHelper.convertFromLegacyImage(icon);
+ if(convert){
+ element.html("");
+ }else{
+ element.html("
");
+ }
+ //it's a file, normally legacy so look in the icon tray images
+ }
+ else {
+ //it's normal
+ element.html("");
+ }
+ }
+ };
+});
+angular.module("umbraco.directives")
+.directive('umbContextMenu', function (navigationService) {
+ return {
+ scope: {
+ menuDialogTitle: "@",
+ currentSection: "@",
+ currentNode: "=",
+ menuActions: "="
+ },
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/application/umb-contextmenu.html',
+ link: function (scope, element, attrs, ctrl) {
+
+ //adds a handler to the context menu item click, we need to handle this differently
+ //depending on what the menu item is supposed to do.
+ scope.executeMenuItem = function (action) {
+ navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
+ };
+ }
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbNavigation
+* @restrict E
+**/
+function umbNavigationDirective() {
+ return {
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/components/application/umb-navigation.html'
+ };
+}
+
+angular.module('umbraco.directives').directive("umbNavigation", umbNavigationDirective);
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbSections
+* @restrict E
+**/
+function sectionsDirective($timeout, $window, navigationService, treeService, sectionResource, appState, eventsService, $location) {
+ return {
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/components/application/umb-sections.html',
+ link: function (scope, element, attr, ctrl) {
+
+ //setup scope vars
+ scope.maxSections = 7;
+ scope.overflowingSections = 0;
+ scope.sections = [];
+ scope.currentSection = appState.getSectionState("currentSection");
+ scope.showTray = false; //appState.getGlobalState("showTray");
+ scope.stickyNavigation = appState.getGlobalState("stickyNavigation");
+ scope.needTray = false;
+ scope.trayAnimation = function() {
+ if (scope.showTray) {
+ return 'slide';
+ }
+ else if (scope.showTray === false) {
+ return 'slide';
+ }
+ else {
+ return '';
+ }
+ };
+
+ function loadSections(){
+ sectionResource.getSections()
+ .then(function (result) {
+ scope.sections = result;
+ calculateHeight();
+ });
+ }
+
+ function calculateHeight(){
+ $timeout(function(){
+ //total height minus room for avatar and help icon
+ var height = $(window).height()-200;
+ scope.totalSections = scope.sections.length;
+ scope.maxSections = Math.floor(height / 70);
+ scope.needTray = false;
+
+ if(scope.totalSections > scope.maxSections){
+ scope.needTray = true;
+ scope.overflowingSections = scope.maxSections - scope.totalSections;
+ }
+ });
+ }
+
+ var evts = [];
+
+ //Listen for global state changes
+ evts.push(eventsService.on("appState.globalState.changed", function(e, args) {
+ if (args.key === "showTray") {
+ scope.showTray = args.value;
+ }
+ if (args.key === "stickyNavigation") {
+ scope.stickyNavigation = args.value;
+ }
+ }));
+
+ evts.push(eventsService.on("appState.sectionState.changed", function(e, args) {
+ if (args.key === "currentSection") {
+ scope.currentSection = args.value;
+ }
+ }));
+
+ evts.push(eventsService.on("app.reInitialize", function(e, args) {
+ //re-load the sections if we're re-initializing (i.e. package installed)
+ loadSections();
+ }));
+
+ //ensure to unregister from all events!
+ scope.$on('$destroy', function () {
+ for (var e in evts) {
+ eventsService.unsubscribe(evts[e]);
+ }
+ });
+
+ //on page resize
+ window.onresize = calculateHeight;
+
+ scope.avatarClick = function(){
+
+ if(scope.helpDialog) {
+ closeHelpDialog();
+ }
+
+ if(!scope.userDialog) {
+ scope.userDialog = {
+ view: "user",
+ show: true,
+ close: function(oldModel) {
+ closeUserDialog();
+ }
+ };
+ } else {
+ closeUserDialog();
+ }
+
+ };
+
+ function closeUserDialog() {
+ scope.userDialog.show = false;
+ scope.userDialog = null;
+ }
+
+ scope.helpClick = function(){
+
+ if(scope.userDialog) {
+ closeUserDialog();
+ }
+
+ if(!scope.helpDialog) {
+ scope.helpDialog = {
+ view: "help",
+ show: true,
+ close: function(oldModel) {
+ closeHelpDialog();
+ }
+ };
+ } else {
+ closeHelpDialog();
+ }
+
+ };
+
+ function closeHelpDialog() {
+ scope.helpDialog.show = false;
+ scope.helpDialog = null;
+ }
+
+ scope.sectionClick = function (event, section) {
+
+ if (event.ctrlKey ||
+ event.shiftKey ||
+ event.metaKey || // apple
+ (event.button && event.button === 1) // middle click, >IE9 + everyone else
+ ) {
+ return;
+ }
+
+ if (scope.userDialog) {
+ closeUserDialog();
+ }
+ if (scope.helpDialog) {
+ closeHelpDialog();
+ }
+
+ navigationService.hideSearch();
+ navigationService.showTree(section.alias);
+ $location.path("/" + section.alias);
+ };
+
+ scope.sectionDblClick = function(section){
+ navigationService.reloadSection(section.alias);
+ };
+
+ scope.trayClick = function () {
+ // close dialogs
+ if (scope.userDialog) {
+ closeUserDialog();
+ }
+ if (scope.helpDialog) {
+ closeHelpDialog();
+ }
+
+ if (appState.getGlobalState("showTray") === true) {
+ navigationService.hideTray();
+ } else {
+ navigationService.showTray();
+ }
+ };
+
+ loadSections();
+
+ }
+ };
+}
+
+angular.module('umbraco.directives').directive("umbSections", sectionsDirective);
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbButton
+@restrict E
+@scope
+
+@description
+Use this directive to render an umbraco button. The directive can be used to generate all types of buttons, set type, style, translation, shortcut and much more.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller(myService) {
+
+ var vm = this;
+ vm.buttonState = "init";
+
+ vm.clickButton = clickButton;
+
+ function clickButton() {
+
+ vm.buttonState = "busy";
+
+ myService.clickButton().then(function() {
+ vm.buttonState = "success";
+ }, function() {
+ vm.buttonState = "error";
+ });
+
+ }
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+
+ })();
+
+
+@param {callback} action The button action which should be performed when the button is clicked.
+@param {string=} href Url/Path to navigato to.
+@param {string=} type Set the button type ("button" or "submit").
+@param {string=} buttonStyle Set the style of the button. The directive uses the default bootstrap styles ("primary", "info", "success", "warning", "danger", "inverse", "link").
+@param {string=} state Set a progress state on the button ("init", "busy", "success", "error").
+@param {string=} shortcut Set a keyboard shortcut for the button ("ctrl+c").
+@param {string=} label Set the button label.
+@param {string=} labelKey Set a localization key to make a multi lingual button ("general_buttonText").
+@param {string=} icon Set a button icon. Can only be used when buttonStyle is "link".
+@param {boolean=} disabled Set to true to disable the button.
+**/
+
+(function() {
+ 'use strict';
+
+ function ButtonDirective($timeout) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.style = null;
+
+ function activate() {
+
+ if (!scope.state) {
+ scope.state = "init";
+ }
+
+ if (scope.buttonStyle) {
+ scope.style = "btn-" + scope.buttonStyle;
+ }
+
+ }
+
+ activate();
+
+ var unbindStateWatcher = scope.$watch('state', function(newValue, oldValue) {
+
+ if (newValue === 'success' || newValue === 'error') {
+ $timeout(function() {
+ scope.state = 'init';
+ }, 2000);
+ }
+
+ });
+
+ scope.$on('$destroy', function() {
+ unbindStateWatcher();
+ });
+
+ }
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/buttons/umb-button.html',
+ link: link,
+ scope: {
+ action: "&?",
+ href: "@?",
+ type: "@",
+ buttonStyle: "@?",
+ state: "=?",
+ shortcut: "@?",
+ shortcutWhenHidden: "@",
+ label: "@?",
+ labelKey: "@?",
+ icon: "@?",
+ disabled: "="
+ }
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbButton', ButtonDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbButtonGroup
+@restrict E
+@scope
+
+@description
+Use this directive to render a button with a dropdown of alternative actions.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.buttonGroup = {
+ defaultButton: {
+ labelKey: "general_defaultButton",
+ hotKey: "ctrl+d",
+ hotKeyWhenHidden: true,
+ handler: function() {
+ // do magic here
+ }
+ },
+ subButtons: [
+ {
+ labelKey: "general_subButton",
+ hotKey: "ctrl+b",
+ hotKeyWhenHidden: true,
+ handler: function() {
+ // do magic here
+ }
+ }
+ ]
+ };
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+
+ })();
+
+
+Button model description
+
+ -
+ labekKey
+ (string) -
+ Set a localization key to make a multi lingual button ("general_buttonText").
+
+ -
+ hotKey
+ (array) -
+ Set a keyboard shortcut for the button ("ctrl+c").
+
+ -
+ hotKeyWhenHidden
+ (boolean) -
+ As a default the hotkeys only works on elements visible in the UI. Set to
true to set a hotkey on the hidden sub buttons.
+
+ -
+ handler
+ (callback) -
+ Set a callback to handle button click events.
+
+
+
+@param {object} defaultButton The model of the default button.
+@param {array} subButtons Array of sub buttons.
+@param {string=} state Set a progress state on the button ("init", "busy", "success", "error").
+@param {string=} direction Set the direction of the dropdown ("up", "down").
+@param {string=} float Set the float of the dropdown. ("left", "right").
+**/
+
+(function() {
+ 'use strict';
+
+ function ButtonGroupDirective() {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/buttons/umb-button-group.html',
+ scope: {
+ defaultButton: "=",
+ subButtons: "=",
+ state: "=?",
+ direction: "@?",
+ float: "@?"
+ }
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbButtonGroup', ButtonGroupDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorSubHeader
+@restrict E
+
+@description
+Use this directive to construct a sub header in the main editor window.
+The sub header is sticky and will follow along down the page when scrolling.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorSubHeaderDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/subheader/umb-editor-sub-header.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubHeader', EditorSubHeaderDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorSubHeaderContentLeft
+@restrict E
+
+@description
+Use this directive to left align content in a sub header in the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorSubHeaderContentLeftDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-left.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubHeaderContentLeft', EditorSubHeaderContentLeftDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorSubHeaderContentRight
+@restrict E
+
+@description
+Use this directive to rigt align content in a sub header in the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderSection umbEditorSubHeaderSection}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorSubHeaderContentRightDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-content-right.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubHeaderContentRight', EditorSubHeaderContentRightDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorSubHeaderSection
+@restrict E
+
+@description
+Use this directive to create sections, divided by borders, in a sub header in the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorSubHeader umbEditorSubHeader}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentLeft umbEditorSubHeaderContentLeft}
+ - {@link umbraco.directives.directive:umbEditorSubHeaderContentRight umbEditorSubHeaderContentRight}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorSubHeaderSectionDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/subheader/umb-editor-sub-header-section.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubHeaderSection', EditorSubHeaderSectionDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbBreadcrumbs
+@restrict E
+@scope
+
+@description
+Use this directive to generate a list of breadcrumbs.
+
+Markup example
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller(myService) {
+
+ var vm = this;
+ vm.ancestors = [];
+
+ myService.getAncestors().then(function(ancestors){
+ vm.ancestors = ancestors;
+ });
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {array} ancestors Array of ancestors
+@param {string} entityType The content entity type (member, media, content).
+**/
+
+(function() {
+ 'use strict';
+
+ function BreadcrumbsDirective() {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-breadcrumbs.html',
+ scope: {
+ ancestors: "=",
+ entityType: "@"
+ }
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbBreadcrumbs', BreadcrumbsDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorContainer
+@restrict E
+
+@description
+Use this directive to construct a main content area inside the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+
+ // main content here
+
+
+
+ // footer content here
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorView umbEditorView}
+ - {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
+ - {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorContainerDirective(overlayHelper) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.numberOfOverlays = 0;
+
+ scope.$watch(function(){
+ return overlayHelper.getNumberOfOverlays();
+ }, function (newValue) {
+ scope.numberOfOverlays = newValue;
+ });
+
+ }
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-container.html',
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorContainer', EditorContainerDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorFooter
+@restrict E
+
+@description
+Use this directive to construct a footer inside the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorView umbEditorView}
+ - {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
+ - {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
+ - {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
+ - {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorFooterDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-footer.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorFooter', EditorFooterDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorFooterContentLeft
+@restrict E
+
+@description
+Use this directive to align content left inside the main editor footer.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorView umbEditorView}
+ - {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
+ - {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
+ - {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
+ - {@link umbraco.directives.directive:umbEditorFooterContentRight umbEditorFooterContentRight}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorFooterContentLeftDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-footer-content-left.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorFooterContentLeft', EditorFooterContentLeftDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorFooterContentRight
+@restrict E
+
+@description
+Use this directive to align content right inside the main editor footer.
+
+Markup example
+
+
+
+
+
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorView umbEditorView}
+ - {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
+ - {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
+ - {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
+ - {@link umbraco.directives.directive:umbEditorFooterContentLeft umbEditorFooterContentLeft}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorFooterContentRightDirective() {
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-footer-content-right.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorFooterContentRight', EditorFooterContentRightDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorHeader
+@restrict E
+@scope
+
+@description
+Use this directive to construct a header inside the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+
+Markup example - with tabs
+
+
+
+
+Controller example - with tabs
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.content = {
+ name: "",
+ tabs: [
+ {
+ id: 1,
+ label: "Tab 1",
+ alias: "tab1",
+ active: true
+ },
+ {
+ id: 2,
+ label: "Tab 2",
+ alias: "tab2",
+ active: false
+ }
+ ]
+ };
+
+ }
+
+ angular.module("umbraco").controller("MySection.Controller", Controller);
+ })();
+
+
+Markup example - with sub views
+
+
+
+
+
+
+
+
+Controller example - with sub views
+
+ (function () {
+
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.content = {
+ name: "",
+ navigation: [
+ {
+ "name": "Section 1",
+ "icon": "icon-document-dashed-line",
+ "view": "/App_Plugins/path/to/html.html",
+ "active": true
+ },
+ {
+ "name": "Section 2",
+ "icon": "icon-list",
+ "view": "/App_Plugins/path/to/html.html",
+ }
+ ]
+ };
+
+ }
+
+ angular.module("umbraco").controller("MySection.Controller", Controller);
+ })();
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorView umbEditorView}
+ - {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
+ - {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
+
+
+@param {string} name The content name.
+@param {array=} tabs Array of tabs. See example above.
+@param {array=} navigation Array of sub views. See example above.
+@param {boolean=} nameLocked Set to true to lock the name.
+@param {object=} menu Add a context menu to the editor.
+@param {string=} icon Show and edit the content icon. Opens an overlay to change the icon.
+@param {boolean=} hideIcon Set to true to hide icon.
+@param {string=} alias show and edit the content alias.
+@param {boolean=} hideAlias Set to true to hide alias.
+@param {string=} description Add a description to the content.
+@param {boolean=} hideDescription Set to true to hide description.
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorHeaderDirective(iconHelper) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.openIconPicker = function() {
+ scope.dialogModel = {
+ view: "iconpicker",
+ show: true,
+ submit: function(model) {
+ if (model.color) {
+ scope.icon = model.icon + " " + model.color;
+ } else {
+ scope.icon = model.icon;
+ }
+
+ // set form to dirty
+ ctrl.$setDirty();
+
+ scope.dialogModel.show = false;
+ scope.dialogModel = null;
+ }
+ };
+ };
+ }
+
+ var directive = {
+ require: '^form',
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-header.html',
+ scope: {
+ tabs: "=",
+ actions: "=",
+ name: "=",
+ nameLocked: "=",
+ menu: "=",
+ icon: "=",
+ hideIcon: "@",
+ alias: "=",
+ hideAlias: "@",
+ description: "=",
+ hideDescription: "@",
+ navigation: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorHeader', EditorHeaderDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) {
+
+ function link(scope, el, attr, ctrl) {
+
+ //adds a handler to the context menu item click, we need to handle this differently
+ //depending on what the menu item is supposed to do.
+ scope.executeMenuItem = function (action) {
+ navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection);
+ };
+
+ //callback method to go and get the options async
+ scope.getOptions = function () {
+
+ if (!scope.currentNode) {
+ return;
+ }
+
+ //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu)
+ appState.setMenuState("currentNode", scope.currentNode);
+
+ if (!scope.actions) {
+ treeService.getMenu({ treeNode: scope.currentNode })
+ .then(function (data) {
+ scope.actions = data.menuItems;
+ });
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-menu.html',
+ link: link,
+ scope: {
+ currentNode: "=",
+ currentSection: "@"
+ }
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function EditorNavigationDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.showNavigation = true;
+
+ scope.clickNavigationItem = function(selectedItem) {
+ setItemToActive(selectedItem);
+ runItemAction(selectedItem);
+ };
+
+ function runItemAction(selectedItem) {
+ if (selectedItem.action) {
+ selectedItem.action(selectedItem);
+ }
+ }
+
+ function setItemToActive(selectedItem) {
+ // set all other views to inactive
+ if (selectedItem.view) {
+
+ for (var index = 0; index < scope.navigation.length; index++) {
+ var item = scope.navigation[index];
+ item.active = false;
+ }
+
+ // set view to active
+ selectedItem.active = true;
+
+ }
+ }
+
+ function activate() {
+
+ // hide navigation if there is only 1 item
+ if (scope.navigation.length <= 1) {
+ scope.showNavigation = false;
+ }
+
+ }
+
+ activate();
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-navigation.html',
+ scope: {
+ navigation: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives.html').directive('umbEditorNavigation', EditorNavigationDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function EditorSubViewsDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.activeView = {};
+
+ // set toolbar from selected navigation item
+ function setActiveView(items) {
+
+ for (var index = 0; index < items.length; index++) {
+
+ var item = items[index];
+
+ if (item.active && item.view) {
+ scope.activeView = item;
+ }
+ }
+ }
+
+ // watch for navigation changes
+ scope.$watch('subViews', function(newValue, oldValue) {
+ if (newValue) {
+ setActiveView(newValue);
+ }
+ }, true);
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-sub-views.html',
+ scope: {
+ subViews: "=",
+ model: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorSubViews', EditorSubViewsDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEditorView
+@restrict E
+@scope
+
+@description
+Use this directive to construct the main editor window.
+
+Markup example
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ }
+
+ angular.module("umbraco").controller("MySection.Controller", Controller);
+ })();
+
+
+
+Use in combination with
+
+ - {@link umbraco.directives.directive:umbEditorHeader umbEditorHeader}
+ - {@link umbraco.directives.directive:umbEditorContainer umbEditorContainer}
+ - {@link umbraco.directives.directive:umbEditorFooter umbEditorFooter}
+
+**/
+
+(function() {
+ 'use strict';
+
+ function EditorViewDirective() {
+
+ function link(scope, el, attr) {
+
+ if(attr.footer) {
+ scope.footer = attr.footer;
+ }
+
+ }
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/editor/umb-editor-view.html',
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEditorView', EditorViewDirective);
+
+})();
+
+/**
+* @description Utillity directives for key and field events
+**/
+angular.module('umbraco.directives')
+
+.directive('onKeyup', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onKeyup);
+ };
+ elm.on("keyup", f);
+ scope.$on("$destroy", function(){ elm.off("keyup", f);} );
+ }
+ };
+})
+
+.directive('onKeydown', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onKeydown);
+ };
+ elm.on("keydown", f);
+ scope.$on("$destroy", function(){ elm.off("keydown", f);} );
+ }
+ };
+})
+
+.directive('onBlur', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onBlur);
+ };
+ elm.on("blur", f);
+ scope.$on("$destroy", function(){ elm.off("blur", f);} );
+ }
+ };
+})
+
+.directive('onFocus', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onFocus);
+ };
+ elm.on("focus", f);
+ scope.$on("$destroy", function(){ elm.off("focus", f);} );
+ }
+ };
+})
+
+.directive('onDragEnter', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onDragEnter);
+ };
+ elm.on("dragenter", f);
+ scope.$on("$destroy", function(){ elm.off("dragenter", f);} );
+ }
+ };
+})
+
+.directive('onDragLeave', function () {
+ return function (scope, elm, attrs) {
+ var f = function (event) {
+ var rect = this.getBoundingClientRect();
+ var getXY = function getCursorPosition(event) {
+ var x, y;
+
+ if (typeof event.clientX === 'undefined') {
+ // try touch screen
+ x = event.pageX + document.documentElement.scrollLeft;
+ y = event.pageY + document.documentElement.scrollTop;
+ } else {
+ x = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ y = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+ }
+
+ return { x: x, y : y };
+ };
+
+ var e = getXY(event.originalEvent);
+
+ // Check the mouseEvent coordinates are outside of the rectangle
+ if (e.x > rect.left + rect.width - 1 || e.x < rect.left || e.y > rect.top + rect.height - 1 || e.y < rect.top) {
+ scope.$apply(attrs.onDragLeave);
+ }
+ };
+
+ elm.on("dragleave", f);
+ scope.$on("$destroy", function(){ elm.off("dragleave", f);} );
+ };
+})
+
+.directive('onDragOver', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onDragOver);
+ };
+ elm.on("dragover", f);
+ scope.$on("$destroy", function(){ elm.off("dragover", f);} );
+ }
+ };
+})
+
+.directive('onDragStart', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onDragStart);
+ };
+ elm.on("dragstart", f);
+ scope.$on("$destroy", function(){ elm.off("dragstart", f);} );
+ }
+ };
+})
+
+.directive('onDragEnd', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onDragEnd);
+ };
+ elm.on("dragend", f);
+ scope.$on("$destroy", function(){ elm.off("dragend", f);} );
+ }
+ };
+})
+
+.directive('onDrop', function () {
+ return {
+ link: function (scope, elm, attrs) {
+ var f = function () {
+ scope.$apply(attrs.onDrop);
+ };
+ elm.on("drop", f);
+ scope.$on("$destroy", function(){ elm.off("drop", f);} );
+ }
+ };
+})
+
+.directive('onOutsideClick', function ($timeout) {
+ return function (scope, element, attrs) {
+
+ var eventBindings = [];
+
+ function oneTimeClick(event) {
+ var el = event.target.nodeName;
+
+ //ignore link and button clicks
+ var els = ["INPUT","A","BUTTON"];
+ if(els.indexOf(el) >= 0){return;}
+
+ // ignore children of links and buttons
+ // ignore clicks on new overlay
+ var parents = $(event.target).parents("a,button,.umb-overlay");
+ if(parents.length > 0){
+ return;
+ }
+
+ // ignore clicks on dialog from old dialog service
+ var oldDialog = $(el).parents("#old-dialog-service");
+ if (oldDialog.length === 1) {
+ return;
+ }
+
+ // ignore clicks in tinyMCE dropdown(floatpanel)
+ var floatpanel = $(el).parents(".mce-floatpanel");
+ if (floatpanel.length === 1) {
+ return;
+ }
+
+ //ignore clicks inside this element
+ if( $(element).has( $(event.target) ).length > 0 ){
+ return;
+ }
+
+ scope.$apply(attrs.onOutsideClick);
+ }
+
+
+ $timeout(function(){
+
+ if ("bindClickOn" in attrs) {
+
+ eventBindings.push(scope.$watch(function() {
+ return attrs.bindClickOn;
+ }, function(newValue) {
+ if (newValue === "true") {
+ $(document).on("click", oneTimeClick);
+ } else {
+ $(document).off("click", oneTimeClick);
+ }
+ }));
+
+ } else {
+ $(document).on("click", oneTimeClick);
+ }
+
+ scope.$on("$destroy", function() {
+ $(document).off("click", oneTimeClick);
+
+ // unbind watchers
+ for (var e in eventBindings) {
+ eventBindings[e]();
+ }
+
+ });
+ }); // Temp removal of 1 sec timeout to prevent bug where overlay does not open. We need to find a better solution.
+
+ };
+})
+
+.directive('onRightClick',function(){
+
+ document.oncontextmenu = function (e) {
+ if(e.target.hasAttribute('on-right-click')) {
+ e.preventDefault();
+ e.stopPropagation();
+ return false;
+ }
+ };
+
+ return function(scope,el,attrs){
+ el.on('contextmenu',function(e){
+ e.preventDefault();
+ e.stopPropagation();
+ scope.$apply(attrs.onRightClick);
+ return false;
+ });
+ };
+})
+
+.directive('onDelayedMouseleave', function ($timeout, $parse) {
+ return {
+
+ restrict: 'A',
+
+ link: function (scope, element, attrs, ctrl) {
+ var active = false;
+ var fn = $parse(attrs.onDelayedMouseleave);
+
+ var leave_f = function(event) {
+ var callback = function() {
+ fn(scope, {$event:event});
+ };
+
+ active = false;
+ $timeout(function(){
+ if(active === false){
+ scope.$apply(callback);
+ }
+ }, 650);
+ };
+
+ var enter_f = function(event, args){
+ active = true;
+ };
+
+
+ element.on("mouseleave", leave_f);
+ element.on("mouseenter", enter_f);
+
+ //unsub events
+ scope.$on("$destroy", function(){
+ element.off("mouseleave", leave_f);
+ element.off("mouseenter", enter_f);
+ });
+ }
+ };
+ });
+
+/*
+
+ http://vitalets.github.io/checklist-model/
+
+*/
+angular.module('umbraco.directives')
+.directive('checklistModel', ['$parse', '$compile', function($parse, $compile) {
+ // contains
+ function contains(arr, item) {
+ if (angular.isArray(arr)) {
+ for (var i = 0; i < arr.length; i++) {
+ if (angular.equals(arr[i], item)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // add
+ function add(arr, item) {
+ arr = angular.isArray(arr) ? arr : [];
+ for (var i = 0; i < arr.length; i++) {
+ if (angular.equals(arr[i], item)) {
+ return arr;
+ }
+ }
+ arr.push(item);
+ return arr;
+ }
+
+ // remove
+ function remove(arr, item) {
+ if (angular.isArray(arr)) {
+ for (var i = 0; i < arr.length; i++) {
+ if (angular.equals(arr[i], item)) {
+ arr.splice(i, 1);
+ break;
+ }
+ }
+ }
+ return arr;
+ }
+
+ // http://stackoverflow.com/a/19228302/1458162
+ function postLinkFn(scope, elem, attrs) {
+ // compile with `ng-model` pointing to `checked`
+ $compile(elem)(scope);
+
+ // getter / setter for original model
+ var getter = $parse(attrs.checklistModel);
+ var setter = getter.assign;
+
+ // value added to list
+ var value = $parse(attrs.checklistValue)(scope.$parent);
+
+ // watch UI checked change
+ scope.$watch('checked', function(newValue, oldValue) {
+ if (newValue === oldValue) {
+ return;
+ }
+ var current = getter(scope.$parent);
+ if (newValue === true) {
+ setter(scope.$parent, add(current, value));
+ } else {
+ setter(scope.$parent, remove(current, value));
+ }
+ });
+
+ // watch original model change
+ scope.$parent.$watch(attrs.checklistModel, function(newArr, oldArr) {
+ scope.checked = contains(newArr, value);
+ }, true);
+ }
+
+ return {
+ restrict: 'A',
+ priority: 1000,
+ terminal: true,
+ scope: true,
+ compile: function(tElement, tAttrs) {
+ if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) {
+ throw 'checklist-model should be applied to `input[type="checkbox"]`.';
+ }
+
+ if (!tAttrs.checklistValue) {
+ throw 'You should provide `checklist-value`.';
+ }
+
+ // exclude recursion
+ tElement.removeAttr('checklist-model');
+
+ // local scope var storing individual checkbox model
+ tElement.attr('ng-model', 'checked');
+
+ return postLinkFn;
+ }
+ };
+}]);
+angular.module("umbraco.directives")
+.directive("contenteditable", function() {
+
+ return {
+ require: "ngModel",
+ link: function(scope, element, attrs, ngModel) {
+
+ function read() {
+ ngModel.$setViewValue(element.html());
+ }
+
+ ngModel.$render = function() {
+ element.html(ngModel.$viewValue || "");
+ };
+
+
+ element.bind("focus", function(){
+
+ var range = document.createRange();
+ range.selectNodeContents(element[0]);
+
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);
+
+ });
+
+ element.bind("blur keyup change", function() {
+ scope.$apply(read);
+ });
+ }
+
+ };
+
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:fixNumber
+* @restrict A
+* @description Used in conjunction with type='number' input fields to ensure that the bound value is converted to a number when using ng-model
+* because normally it thinks it's a string and also validation doesn't work correctly due to an angular bug.
+**/
+function fixNumber($parse) {
+ return {
+ restrict: "A",
+ require: "ngModel",
+
+ link: function (scope, elem, attrs, ctrl) {
+
+ //parse ngModel onload
+ var modelVal = scope.$eval(attrs.ngModel);
+ if (modelVal) {
+ var asNum = parseFloat(modelVal, 10);
+ if (!isNaN(asNum)) {
+ $parse(attrs.ngModel).assign(scope, asNum);
+ }
+ }
+
+ //always return an int to the model
+ ctrl.$parsers.push(function (value) {
+ if (value === 0) {
+ return 0;
+ }
+ return parseFloat(value || '', 10);
+ });
+
+ //always try to format the model value as an int
+ ctrl.$formatters.push(function (value) {
+ if (angular.isString(value)) {
+ return parseFloat(value, 10);
+ }
+ return value;
+ });
+
+ //This fixes this angular issue:
+ //https://github.com/angular/angular.js/issues/2144
+ // which doesn't actually validate the number input properly since the model only changes when a real number is entered
+ // but the input box still allows non-numbers to be entered which do not validate (only via html5)
+ if (typeof elem.prop('validity') === 'undefined') {
+ return;
+ }
+
+ elem.bind('input', function (e) {
+ var validity = elem.prop('validity');
+ scope.$apply(function () {
+ ctrl.$setValidity('number', !validity.badInput);
+ });
+ });
+ }
+ };
+}
+angular.module('umbraco.directives').directive("fixNumber", fixNumber);
+angular.module("umbraco.directives").directive('focusWhen', function ($timeout) {
+ return {
+ restrict: 'A',
+ link: function (scope, elm, attrs, ctrl) {
+ attrs.$observe("focusWhen", function (newValue) {
+ if (newValue === "true") {
+ $timeout(function () {
+ elm.focus();
+ });
+ }
+ });
+ }
+ };
+});
+
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:hexBgColor
+* @restrict A
+* @description Used to set a hex background color on an element, this will detect valid hex and when it is valid it will set the color, otherwise
+* a color will not be set.
+**/
+function hexBgColor() {
+ return {
+ restrict: "A",
+ link: function (scope, element, attr, formCtrl) {
+
+ var origColor = null;
+ if (attr.hexBgOrig) {
+ //set the orig based on the attribute if there is one
+ origColor = attr.hexBgOrig;
+ }
+
+ attr.$observe("hexBgColor", function (newVal) {
+ if (newVal) {
+ if (!origColor) {
+ //get the orig color before changing it
+ origColor = element.css("border-color");
+ }
+ //validate it - test with and without the leading hash.
+ if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
+ element.css("background-color", "#" + newVal);
+ return;
+ }
+ if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
+ element.css("background-color", newVal);
+ return;
+ }
+ }
+ element.css("background-color", origColor);
+ });
+
+ }
+ };
+}
+angular.module('umbraco.directives').directive("hexBgColor", hexBgColor);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:hotkey
+**/
+
+angular.module("umbraco.directives")
+ .directive('hotkey', function($window, keyboardService, $log) {
+
+ return function(scope, el, attrs) {
+
+ var options = {};
+ var keyCombo = attrs.hotkey;
+
+ if (!keyCombo) {
+ //support data binding
+ keyCombo = scope.$eval(attrs["hotkey"]);
+ }
+
+ function activate() {
+
+ if (keyCombo) {
+
+ // disable shortcuts in input fields if keycombo is 1 character
+ if (keyCombo.length === 1) {
+ options = {
+ inputDisabled: true
+ };
+ }
+
+ keyboardService.bind(keyCombo, function() {
+
+ var element = $(el);
+ var activeElementType = document.activeElement.tagName;
+ var clickableElements = ["A", "BUTTON"];
+
+ if (element.is("a,div,button,input[type='button'],input[type='submit'],input[type='checkbox']") && !element.is(':disabled')) {
+
+ if (element.is(':visible') || attrs.hotkeyWhenHidden) {
+
+ if (attrs.hotkeyWhen && attrs.hotkeyWhen === "false") {
+ return;
+ }
+
+ // when keycombo is enter and a link or button has focus - click the link or button instead of using the hotkey
+ if (keyCombo === "enter" && clickableElements.indexOf(activeElementType) === 0) {
+ document.activeElement.click();
+ } else {
+ element.click();
+ }
+
+ }
+
+ } else {
+ element.focus();
+ }
+
+ }, options);
+
+ el.on('$destroy', function() {
+ keyboardService.unbind(keyCombo);
+ });
+
+ }
+
+ }
+
+ activate();
+
+ };
+ });
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:preventDefault
+
+@description
+Use this directive to prevent default action of an element. Effectively implementing jQuery's preventdefault
+
+Markup example
+
+
+ Don't go to Umbraco.com
+
+
+**/
+angular.module("umbraco.directives")
+ .directive('preventDefault', function() {
+ return function(scope, element, attrs) {
+
+ var enabled = true;
+ //check if there's a value for the attribute, if there is and it's false then we conditionally don't
+ //prevent default.
+ if (attrs.preventDefault) {
+ attrs.$observe("preventDefault", function (newVal) {
+ enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
+ });
+ }
+
+ $(element).click(function (event) {
+ if (event.metaKey || event.ctrlKey) {
+ return;
+ }
+ else {
+ if (enabled === true) {
+ event.preventDefault();
+ }
+ }
+ });
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:preventEnterSubmit
+* @description prevents a form from submitting when the enter key is pressed on an input field
+**/
+angular.module("umbraco.directives")
+ .directive('preventEnterSubmit', function() {
+ return function(scope, element, attrs) {
+
+ var enabled = true;
+ //check if there's a value for the attribute, if there is and it's false then we conditionally don't
+ //prevent default.
+ if (attrs.preventEnterSubmit) {
+ attrs.$observe("preventEnterSubmit", function (newVal) {
+ enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
+ });
+ }
+
+ $(element).keypress(function (event) {
+ if (event.which === 13) {
+ event.preventDefault();
+ }
+ });
+ };
+ });
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:resizeToContent
+ * @element div
+ * @function
+ *
+ * @description
+ * Resize iframe's automatically to fit to the content they contain
+ *
+ * @example
+
+
+
+
+
+ */
+angular.module("umbraco.directives")
+ .directive('resizeToContent', function ($window, $timeout) {
+ return function (scope, el, attrs) {
+ var iframe = el[0];
+ var iframeWin = iframe.contentWindow || iframe.contentDocument.parentWindow;
+ if (iframeWin.document.body) {
+
+ $timeout(function(){
+ var height = iframeWin.document.documentElement.scrollHeight || iframeWin.document.body.scrollHeight;
+ el.height(height);
+ }, 3000);
+ }
+ };
+ });
+
+angular.module("umbraco.directives")
+ .directive('selectOnFocus', function () {
+ return function (scope, el, attrs) {
+ $(el).bind("click", function () {
+ var editmode = $(el).data("editmode");
+ //If editmode is true a click is handled like a normal click
+ if (!editmode) {
+ //Initial click, select entire text
+ this.select();
+ //Set the edit mode so subsequent clicks work normally
+ $(el).data("editmode", true);
+ }
+ }).
+ bind("blur", function () {
+ //Reset on focus lost
+ $(el).data("editmode", false);
+ });
+ };
+ });
+
+angular.module("umbraco.directives")
+ .directive('umbAutoFocus', function($timeout) {
+
+ return function(scope, element, attr){
+ var update = function() {
+ //if it uses its default naming
+ if(element.val() === "" || attr.focusOnFilled){
+ element.focus();
+ }
+ };
+
+ $timeout(function() {
+ update();
+ });
+ };
+});
+
+angular.module("umbraco.directives")
+ .directive('umbAutoResize', function($timeout) {
+ return {
+ require: ["^?umbTabs", "ngModel"],
+ link: function(scope, element, attr, controllersArr) {
+
+ var domEl = element[0];
+ var domElType = domEl.type;
+ var umbTabsController = controllersArr[0];
+ var ngModelController = controllersArr[1];
+
+ // IE elements
+ var isIEFlag = false;
+ var wrapper = angular.element('#umb-ie-resize-input-wrapper');
+ var mirror = angular.element('');
+
+ function isIE() {
+
+ var ua = window.navigator.userAgent;
+ var msie = ua.indexOf("MSIE ");
+
+ if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./) || navigator.userAgent.match(/Edge\/\d+/)) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ function activate() {
+
+ // check if browser is Internet Explorere
+ isIEFlag = isIE();
+
+ // scrollWidth on element does not work in IE on inputs
+ // we have to do some dirty dom element copying.
+ if (isIEFlag === true && domElType === "text") {
+ setupInternetExplorerElements();
+ }
+
+ }
+
+ function setupInternetExplorerElements() {
+
+ if (!wrapper.length) {
+ wrapper = angular.element('');
+ angular.element('body').append(wrapper);
+ }
+
+ angular.forEach(['fontFamily', 'fontSize', 'fontWeight', 'fontStyle',
+ 'letterSpacing', 'textTransform', 'wordSpacing', 'textIndent',
+ 'boxSizing', 'borderRightWidth', 'borderLeftWidth', 'borderLeftStyle', 'borderRightStyle',
+ 'paddingLeft', 'paddingRight', 'marginLeft', 'marginRight'
+ ], function(value) {
+ mirror.css(value, element.css(value));
+ });
+
+ wrapper.append(mirror);
+
+ }
+
+ function resizeInternetExplorerInput() {
+
+ mirror.text(element.val() || attr.placeholder);
+ element.css('width', mirror.outerWidth() + 1);
+
+ }
+
+ function resizeInput() {
+
+ if (domEl.scrollWidth !== domEl.clientWidth) {
+ if (ngModelController.$modelValue) {
+ element.width(domEl.scrollWidth);
+ }
+ }
+
+ if(!ngModelController.$modelValue && attr.placeholder) {
+ attr.$set('size', attr.placeholder.length);
+ element.width('auto');
+ }
+
+ }
+
+ function resizeTextarea() {
+
+ if(domEl.scrollHeight !== domEl.clientHeight) {
+
+ element.height(domEl.scrollHeight);
+
+ }
+
+ }
+
+ var update = function(force) {
+
+
+ if (force === true) {
+
+ if (domElType === "textarea") {
+ element.height(0);
+ } else if (domElType === "text") {
+ element.width(0);
+ }
+
+ }
+
+
+ if (isIEFlag === true && domElType === "text") {
+
+ resizeInternetExplorerInput();
+
+ } else {
+
+ if (domElType === "textarea") {
+
+ resizeTextarea();
+
+ } else if (domElType === "text") {
+
+ resizeInput();
+
+ }
+
+ }
+
+ };
+
+ activate();
+
+ //listen for tab changes
+ if (umbTabsController != null) {
+ umbTabsController.onTabShown(function(args) {
+ update();
+ });
+ }
+
+ // listen for ng-model changes
+ var unbindModelWatcher = scope.$watch(function() {
+ return ngModelController.$modelValue;
+ }, function(newValue) {
+ update(true);
+ });
+
+ scope.$on('$destroy', function() {
+ element.unbind('keyup keydown keypress change', update);
+ element.unbind('blur', update(true));
+ unbindModelWatcher();
+
+ // clean up IE dom element
+ if (isIEFlag === true && domElType === "text") {
+ mirror.remove();
+ }
+
+ });
+ }
+ };
+ });
+
+/*
+example usage:
+
+jsonEditing is a string which we edit in a textarea. we try parsing to JSON with each change. when it is valid, propagate model changes via ngModelCtrl
+
+use isolate scope to prevent model propagation when invalid - will update manually. cannot replace with template, or will override ngModelCtrl, and not hide behind facade
+
+will override element type to textarea and add own attribute ngModel tied to jsonEditing
+ */
+
+angular.module("umbraco.directives")
+ .directive('umbRawModel', function () {
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ template: '',
+ replace : true,
+ scope: {
+ model: '=umbRawModel',
+ validateOn:'='
+ },
+ link: function (scope, element, attrs, ngModelCtrl) {
+
+ function setEditing (value) {
+ scope.jsonEditing = angular.copy( jsonToString(value));
+ }
+
+ function updateModel (value) {
+ scope.model = stringToJson(value);
+ }
+
+ function setValid() {
+ ngModelCtrl.$setValidity('json', true);
+ }
+
+ function setInvalid () {
+ ngModelCtrl.$setValidity('json', false);
+ }
+
+ function stringToJson(text) {
+ try {
+ return angular.fromJson(text);
+ } catch (err) {
+ setInvalid();
+ return text;
+ }
+ }
+
+ function jsonToString(object) {
+ // better than JSON.stringify(), because it formats + filters $$hashKey etc.
+ // NOTE that this will remove all $-prefixed values
+ return angular.toJson(object, true);
+ }
+
+ function isValidJson(model) {
+ var flag = true;
+ try {
+ angular.fromJson(model);
+ } catch (err) {
+ flag = false;
+ }
+ return flag;
+ }
+
+ //init
+ setEditing(scope.model);
+
+ var onInputChange = function(newval,oldval){
+ if (newval !== oldval) {
+ if (isValidJson(newval)) {
+ setValid();
+ updateModel(newval);
+ } else {
+ setInvalid();
+ }
+ }
+ };
+
+ if(scope.validateOn){
+ element.on(scope.validateOn, function(){
+ scope.$apply(function(){
+ onInputChange(scope.jsonEditing);
+ });
+ });
+ }else{
+ //check for changes going out
+ scope.$watch('jsonEditing', onInputChange, true);
+ }
+
+ //check for changes coming in
+ scope.$watch('model', function (newval, oldval) {
+ if (newval !== oldval) {
+ setEditing(newval);
+ }
+ }, true);
+
+ }
+ };
+ });
+
+(function() {
+ 'use strict';
+
+ function SelectWhen($timeout) {
+
+ function link(scope, el, attr, ctrl) {
+
+ attr.$observe("umbSelectWhen", function(newValue) {
+ if (newValue === "true") {
+ $timeout(function() {
+ el.select();
+ });
+ }
+ });
+
+ }
+
+ var directive = {
+ restrict: 'A',
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbSelectWhen', SelectWhen);
+
+})();
+
+angular.module("umbraco.directives")
+ .directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) {
+ return {
+ scope: {
+ uniqueId: '=',
+ value: '=',
+ onClick: '&',
+ onFocus: '&',
+ onBlur: '&',
+ configuration:"=",
+ onMediaPickerClick: "=",
+ onEmbedClick: "=",
+ onMacroPickerClick: "=",
+ onLinkPickerClick: "="
+ },
+ template: "",
+ replace: true,
+ link: function (scope, element, attrs) {
+
+ var initTiny = function () {
+
+ //we always fetch the default one, and then override parts with our own
+ tinyMceService.configuration().then(function (tinyMceConfig) {
+
+
+
+ //config value from general tinymce.config file
+ var validElements = tinyMceConfig.validElements;
+ var fallbackStyles = [{title: "Page header", block: "h2"}, {title: "Section header", block: "h3"}, {title: "Paragraph header", block: "h4"}, {title: "Normal", block: "p"}, {title: "Quote", block: "blockquote"}, {title: "Code", block: "code"}];
+
+ //These are absolutely required in order for the macros to render inline
+ //we put these as extended elements because they get merged on top of the normal allowed elements by tiny mce
+ var extendedValidElements = "@[id|class|style],-div[id|dir|class|align|style],ins[datetime|cite],-ul[class|style],-li[class|style],-h1[id|dir|class|align|style],-h2[id|dir|class|align|style],-h3[id|dir|class|align|style],-h4[id|dir|class|align|style],-h5[id|dir|class|align|style],-h6[id|style|dir|class|align],span[id|class|style]";
+
+ var invalidElements = tinyMceConfig.inValidElements;
+ var plugins = _.map(tinyMceConfig.plugins, function (plugin) {
+ if (plugin.useOnFrontend) {
+ return plugin.name;
+ }
+ }).join(" ") + " autoresize";
+
+ //config value on the data type
+ var toolbar = ["code", "styleselect", "bold", "italic", "alignleft", "aligncenter", "alignright", "bullist", "numlist", "link", "umbmediapicker", "umbembeddialog"].join(" | ");
+ var stylesheets = [];
+
+ var styleFormats = [];
+ var await = [];
+
+ //queue file loading
+ if (typeof (tinymce) === "undefined") {
+ await.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
+ }
+
+
+ if(scope.configuration && scope.configuration.toolbar){
+ toolbar = scope.configuration.toolbar.join(' | ');
+ }
+
+
+ if(scope.configuration && scope.configuration.stylesheets){
+ angular.forEach(scope.configuration.stylesheets, function(stylesheet, key){
+
+ stylesheets.push(Umbraco.Sys.ServerVariables.umbracoSettings.cssPath + "/" + stylesheet + ".css");
+ await.push(stylesheetResource.getRulesByName(stylesheet).then(function (rules) {
+ angular.forEach(rules, function (rule) {
+ var r = {};
+ r.title = rule.name;
+ if (rule.selector[0] === ".") {
+ r.inline = "span";
+ r.classes = rule.selector.substring(1);
+ }else if (rule.selector[0] === "#") {
+ //Even though this will render in the style drop down, it will not actually be applied
+ // to the elements, don't think TinyMCE even supports this and it doesn't really make much sense
+ // since only one element can have one id.
+ r.inline = "span";
+ r.attributes = { id: rule.selector.substring(1) };
+ }else {
+ r.block = rule.selector;
+ }
+ styleFormats.push(r);
+ });
+ }));
+ });
+ }else{
+ stylesheets.push("views/propertyeditors/grid/config/grid.default.rtestyles.css");
+ styleFormats = fallbackStyles;
+ }
+
+ //stores a reference to the editor
+ var tinyMceEditor = null;
+ $q.all(await).then(function () {
+
+ var uniqueId = scope.uniqueId;
+
+ //create a baseline Config to exten upon
+ var baseLineConfigObj = {
+ mode: "exact",
+ skin: "umbraco",
+ plugins: plugins,
+ valid_elements: validElements,
+ invalid_elements: invalidElements,
+ extended_valid_elements: extendedValidElements,
+ menubar: false,
+ statusbar: false,
+ relative_urls: false,
+ toolbar: toolbar,
+ content_css: stylesheets,
+ style_formats: styleFormats,
+ autoresize_bottom_margin: 0
+ };
+
+
+ if (tinyMceConfig.customConfig) {
+
+ //if there is some custom config, we need to see if the string value of each item might actually be json and if so, we need to
+ // convert it to json instead of having it as a string since this is what tinymce requires
+ for (var i in tinyMceConfig.customConfig) {
+ var val = tinyMceConfig.customConfig[i];
+ if (val) {
+ val = val.toString().trim();
+ if (val.detectIsJson()) {
+ try {
+ tinyMceConfig.customConfig[i] = JSON.parse(val);
+ //now we need to check if this custom config key is defined in our baseline, if it is we don't want to
+ //overwrite the baseline config item if it is an array, we want to concat the items in the array, otherwise
+ //if it's an object it will overwrite the baseline
+ if (angular.isArray(baseLineConfigObj[i]) && angular.isArray(tinyMceConfig.customConfig[i])) {
+ //concat it and below this concat'd array will overwrite the baseline in angular.extend
+ tinyMceConfig.customConfig[i] = baseLineConfigObj[i].concat(tinyMceConfig.customConfig[i]);
+ }
+ }
+ catch (e) {
+ //cannot parse, we'll just leave it
+ }
+ }
+ }
+ }
+
+ angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
+ }
+
+ //set all the things that user configs should not be able to override
+ baseLineConfigObj.elements = uniqueId;
+ baseLineConfigObj.setup = function (editor) {
+
+ //set the reference
+ tinyMceEditor = editor;
+
+
+ //enable browser based spell checking
+ editor.on('init', function (e) {
+
+ editor.getBody().setAttribute('spellcheck', true);
+
+ //force overflow to hidden to prevent no needed scroll
+ editor.getBody().style.overflow = "hidden";
+
+ $timeout(function(){
+ if(scope.value === null){
+ editor.focus();
+ }
+ }, 400);
+
+ });
+
+ //when we leave the editor (maybe)
+ editor.on('blur', function (e) {
+ editor.save();
+ angularHelper.safeApply(scope, function () {
+ scope.value = editor.getContent();
+
+ var _toolbar = $(editor.editorContainer)
+ .find(".mce-toolbar");
+
+ if(scope.onBlur){
+ scope.onBlur();
+ }
+
+ });
+ });
+
+ // Focus on editor
+ editor.on('focus', function (e) {
+ angularHelper.safeApply(scope, function () {
+
+ if(scope.onFocus){
+ scope.onFocus();
+ }
+
+ });
+ });
+
+ // Click on editor
+ editor.on('click', function (e) {
+ angularHelper.safeApply(scope, function () {
+
+ if(scope.onClick){
+ scope.onClick();
+ }
+
+ });
+ });
+
+ //when buttons modify content
+ editor.on('ExecCommand', function (e) {
+ editor.save();
+ angularHelper.safeApply(scope, function () {
+ scope.value = editor.getContent();
+ });
+ });
+
+ // Update model on keypress
+ editor.on('KeyUp', function (e) {
+ editor.save();
+ angularHelper.safeApply(scope, function () {
+ scope.value = editor.getContent();
+ });
+ });
+
+ // Update model on change, i.e. copy/pasted text, plugins altering content
+ editor.on('SetContent', function (e) {
+ if (!e.initial) {
+ editor.save();
+ angularHelper.safeApply(scope, function () {
+ scope.value = editor.getContent();
+ });
+ }
+ });
+
+ editor.on('ObjectResized', function (e) {
+ var qs = "?width=" + e.width + "&height=" + e.height;
+ var srcAttr = $(e.target).attr("src");
+ var path = srcAttr.split("?")[0];
+ $(e.target).attr("data-mce-src", path + qs);
+ });
+
+ //Create the insert link plugin
+ tinyMceService.createLinkPicker(editor, scope, function(currentTarget, anchorElement){
+ if(scope.onLinkPickerClick) {
+ scope.onLinkPickerClick(editor, currentTarget, anchorElement);
+ }
+ });
+
+ //Create the insert media plugin
+ tinyMceService.createMediaPicker(editor, scope, function(currentTarget, userData){
+ if(scope.onMediaPickerClick) {
+ scope.onMediaPickerClick(editor, currentTarget, userData);
+ }
+ });
+
+ //Create the embedded plugin
+ tinyMceService.createInsertEmbeddedMedia(editor, scope, function(){
+ if(scope.onEmbedClick) {
+ scope.onEmbedClick(editor);
+ }
+ });
+
+ //Create the insert macro plugin
+ tinyMceService.createInsertMacro(editor, scope, function(dialogData){
+ if(scope.onMacroPickerClick) {
+ scope.onMacroPickerClick(editor, dialogData);
+ }
+ });
+
+ };
+
+ /** Loads in the editor */
+ function loadTinyMce() {
+
+ //we need to add a timeout here, to force a redraw so TinyMCE can find
+ //the elements needed
+ $timeout(function () {
+ tinymce.DOM.events.domLoaded = true;
+ tinymce.init(baseLineConfigObj);
+ }, 150, false);
+ }
+
+ loadTinyMce();
+
+ //here we declare a special method which will be called whenever the value has changed from the server
+ //this is instead of doing a watch on the model.value = faster
+ //scope.model.onValueChanged = function (newVal, oldVal) {
+ // //update the display val again if it has changed from the server;
+ // tinyMceEditor.setContent(newVal, { format: 'raw' });
+ // //we need to manually fire this event since it is only ever fired based on loading from the DOM, this
+ // // is required for our plugins listening to this event to execute
+ // tinyMceEditor.fire('LoadContent', null);
+ //};
+
+ //listen for formSubmitting event (the result is callback used to remove the event subscription)
+ var unsubscribe = scope.$on("formSubmitting", function () {
+ //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer
+ // we do parse it out on the server side but would be nice to do that on the client side before as well.
+ scope.value = tinyMceEditor.getContent();
+ });
+
+ //when the element is disposed we need to unsubscribe!
+ // NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
+ // element might still be there even after the modal has been hidden.
+ scope.$on('$destroy', function () {
+ unsubscribe();
+ });
+
+ });
+
+ });
+
+ };
+
+ initTiny();
+
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbControlGroup
+* @restrict E
+**/
+angular.module("umbraco.directives.html")
+ .directive('umbControlGroup', function (localizationService) {
+ return {
+ scope: {
+ label: "@label",
+ description: "@",
+ hideLabel: "@",
+ alias: "@"
+ },
+ require: '?^form',
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/html/umb-control-group.html',
+ link: function (scope, element, attr, formCtrl) {
+
+ scope.formValid = function() {
+ if (formCtrl) {
+ return formCtrl.$valid;
+ }
+ //there is no form.
+ return true;
+ };
+
+ if (scope.label && scope.label[0] === "@") {
+ scope.labelstring = localizationService.localize(scope.label.substring(1));
+ }
+ else {
+ scope.labelstring = scope.label;
+ }
+
+ if (scope.description && scope.description[0] === "@") {
+ scope.descriptionstring = localizationService.localize(scope.description.substring(1));
+ }
+ else {
+ scope.descriptionstring = scope.description;
+ }
+
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbPane
+* @restrict E
+**/
+angular.module("umbraco.directives.html")
+ .directive('umbPane', function () {
+ return {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/html/umb-pane.html'
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbPanel
+* @restrict E
+**/
+angular.module("umbraco.directives.html")
+ .directive('umbPanel', function($timeout, $log){
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: 'true',
+ templateUrl: 'views/components/html/umb-panel.html'
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbImageCrop
+* @restrict E
+* @function
+**/
+angular.module("umbraco.directives")
+ .directive('umbImageCrop',
+ function ($timeout, localizationService, cropperHelper, $log) {
+ return {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/imaging/umb-image-crop.html',
+ scope: {
+ src: '=',
+ width: '@',
+ height: '@',
+ crop: "=",
+ center: "=",
+ maxSize: '@'
+ },
+
+ link: function(scope, element, attrs) {
+ scope.width = 400;
+ scope.height = 320;
+
+ scope.dimensions = {
+ image: {},
+ cropper:{},
+ viewport:{},
+ margin: 20,
+ scale: {
+ min: 0.3,
+ max: 3,
+ current: 1
+ }
+ };
+
+
+ //live rendering of viewport and image styles
+ scope.style = function () {
+ return {
+ 'height': (parseInt(scope.dimensions.viewport.height, 10)) + 'px',
+ 'width': (parseInt(scope.dimensions.viewport.width, 10)) + 'px'
+ };
+ };
+
+
+ //elements
+ var $viewport = element.find(".viewport");
+ var $image = element.find("img");
+ var $overlay = element.find(".overlay");
+ var $container = element.find(".crop-container");
+
+ //default constraints for drag n drop
+ var constraints = {left: {max: scope.dimensions.margin, min: scope.dimensions.margin}, top: {max: scope.dimensions.margin, min: scope.dimensions.margin}, };
+ scope.constraints = constraints;
+
+
+ //set constaints for cropping drag and drop
+ var setConstraints = function(){
+ constraints.left.min = scope.dimensions.margin + scope.dimensions.cropper.width - scope.dimensions.image.width;
+ constraints.top.min = scope.dimensions.margin + scope.dimensions.cropper.height - scope.dimensions.image.height;
+ };
+
+
+ var setDimensions = function(originalImage){
+ originalImage.width("auto");
+ originalImage.height("auto");
+
+ var image = {};
+ image.originalWidth = originalImage.width();
+ image.originalHeight = originalImage.height();
+
+ image.width = image.originalWidth;
+ image.height = image.originalHeight;
+ image.left = originalImage[0].offsetLeft;
+ image.top = originalImage[0].offsetTop;
+
+ scope.dimensions.image = image;
+
+ //unscaled editor size
+ //var viewPortW = $viewport.width();
+ //var viewPortH = $viewport.height();
+ var _viewPortW = parseInt(scope.width, 10);
+ var _viewPortH = parseInt(scope.height, 10);
+
+ //if we set a constraint we will scale it down if needed
+ if(scope.maxSize){
+ var ratioCalculation = cropperHelper.scaleToMaxSize(
+ _viewPortW,
+ _viewPortH,
+ scope.maxSize);
+
+ //so if we have a max size, override the thumb sizes
+ _viewPortW = ratioCalculation.width;
+ _viewPortH = ratioCalculation.height;
+ }
+
+ scope.dimensions.viewport.width = _viewPortW + 2 * scope.dimensions.margin;
+ scope.dimensions.viewport.height = _viewPortH + 2 * scope.dimensions.margin;
+ scope.dimensions.cropper.width = _viewPortW; // scope.dimensions.viewport.width - 2 * scope.dimensions.margin;
+ scope.dimensions.cropper.height = _viewPortH; // scope.dimensions.viewport.height - 2 * scope.dimensions.margin;
+ };
+
+
+ //when loading an image without any crop info, we center and fit it
+ var resizeImageToEditor = function(){
+ //returns size fitting the cropper
+ var size = cropperHelper.calculateAspectRatioFit(
+ scope.dimensions.image.width,
+ scope.dimensions.image.height,
+ scope.dimensions.cropper.width,
+ scope.dimensions.cropper.height,
+ true);
+
+ //sets the image size and updates the scope
+ scope.dimensions.image.width = size.width;
+ scope.dimensions.image.height = size.height;
+
+ //calculate the best suited ratios
+ scope.dimensions.scale.min = size.ratio;
+ scope.dimensions.scale.max = 2;
+ scope.dimensions.scale.current = size.ratio;
+
+ //center the image
+ var position = cropperHelper.centerInsideViewPort(scope.dimensions.image, scope.dimensions.cropper);
+ scope.dimensions.top = position.top;
+ scope.dimensions.left = position.left;
+
+ setConstraints();
+ };
+
+ //resize to a given ratio
+ var resizeImageToScale = function(ratio){
+ //do stuff
+ var size = cropperHelper.calculateSizeToRatio(scope.dimensions.image.originalWidth, scope.dimensions.image.originalHeight, ratio);
+ scope.dimensions.image.width = size.width;
+ scope.dimensions.image.height = size.height;
+
+ setConstraints();
+ validatePosition(scope.dimensions.image.left, scope.dimensions.image.top);
+ };
+
+ //resize the image to a predefined crop coordinate
+ var resizeImageToCrop = function(){
+ scope.dimensions.image = cropperHelper.convertToStyle(
+ scope.crop,
+ {width: scope.dimensions.image.originalWidth, height: scope.dimensions.image.originalHeight},
+ scope.dimensions.cropper,
+ scope.dimensions.margin);
+
+ var ratioCalculation = cropperHelper.calculateAspectRatioFit(
+ scope.dimensions.image.originalWidth,
+ scope.dimensions.image.originalHeight,
+ scope.dimensions.cropper.width,
+ scope.dimensions.cropper.height,
+ true);
+
+ scope.dimensions.scale.current = scope.dimensions.image.ratio;
+
+ //min max based on original width/height
+ scope.dimensions.scale.min = ratioCalculation.ratio;
+ scope.dimensions.scale.max = 2;
+ };
+
+
+
+ var validatePosition = function(left, top){
+ if(left > constraints.left.max)
+ {
+ left = constraints.left.max;
+ }
+
+ if(left <= constraints.left.min){
+ left = constraints.left.min;
+ }
+
+ if(top > constraints.top.max)
+ {
+ top = constraints.top.max;
+ }
+ if(top <= constraints.top.min){
+ top = constraints.top.min;
+ }
+
+ if(scope.dimensions.image.left !== left){
+ scope.dimensions.image.left = left;
+ }
+
+ if(scope.dimensions.image.top !== top){
+ scope.dimensions.image.top = top;
+ }
+ };
+
+
+ //sets scope.crop to the recalculated % based crop
+ var calculateCropBox = function(){
+ scope.crop = cropperHelper.pixelsToCoordinates(scope.dimensions.image, scope.dimensions.cropper.width, scope.dimensions.cropper.height, scope.dimensions.margin);
+ };
+
+
+ //Drag and drop positioning, using jquery ui draggable
+ var onStartDragPosition, top, left;
+ $overlay.draggable({
+ drag: function(event, ui) {
+ scope.$apply(function(){
+ validatePosition(ui.position.left, ui.position.top);
+ });
+ },
+ stop: function(event, ui){
+ scope.$apply(function(){
+ //make sure that every validates one more time...
+ validatePosition(ui.position.left, ui.position.top);
+
+ calculateCropBox();
+ scope.dimensions.image.rnd = Math.random();
+ });
+ }
+ });
+
+
+
+ var init = function(image){
+ scope.loaded = false;
+
+ //set dimensions on image, viewport, cropper etc
+ setDimensions(image);
+
+ //if we have a crop already position the image
+ if(scope.crop){
+ resizeImageToCrop();
+ }else{
+ resizeImageToEditor();
+ }
+
+ //sets constaints for the cropper
+ setConstraints();
+ scope.loaded = true;
+ };
+
+
+ /// WATCHERS ////
+ scope.$watchCollection('[width, height]', function(newValues, oldValues){
+ //we have to reinit the whole thing if
+ //one of the external params changes
+ if(newValues !== oldValues){
+ setDimensions($image);
+ setConstraints();
+ }
+ });
+
+ var throttledResizing = _.throttle(function(){
+ resizeImageToScale(scope.dimensions.scale.current);
+ calculateCropBox();
+ }, 100);
+
+
+ //happens when we change the scale
+ scope.$watch("dimensions.scale.current", function(){
+ if(scope.loaded){
+ throttledResizing();
+ }
+ });
+
+ //ie hack
+ if(window.navigator.userAgent.indexOf("MSIE ")){
+ var ranger = element.find("input");
+ ranger.bind("change",function(){
+ scope.$apply(function(){
+ scope.dimensions.scale.current = ranger.val();
+ });
+ });
+ }
+
+ //// INIT /////
+ $image.load(function(){
+ $timeout(function(){
+ init($image);
+ });
+ });
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbImageGravity
+* @restrict E
+* @function
+* @description
+**/
+angular.module("umbraco.directives")
+ .directive('umbImageGravity', function ($timeout, localizationService, $log) {
+ return {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/imaging/umb-image-gravity.html',
+ scope: {
+ src: '=',
+ center: "=",
+ onImageLoaded: "="
+ },
+ link: function(scope, element, attrs) {
+
+ //Internal values for keeping track of the dot and the size of the editor
+ scope.dimensions = {
+ width: 0,
+ height: 0,
+ left: 0,
+ top: 0
+ };
+
+ scope.loaded = false;
+
+ //elements
+ var $viewport = element.find(".viewport");
+ var $image = element.find("img");
+ var $overlay = element.find(".overlay");
+
+ scope.style = function () {
+ if(scope.dimensions.width <= 0){
+ setDimensions();
+ }
+
+ return {
+ 'top': scope.dimensions.top + 'px',
+ 'left': scope.dimensions.left + 'px'
+ };
+ };
+
+ scope.setFocalPoint = function(event) {
+
+ scope.$emit("imageFocalPointStart");
+
+ var offsetX = event.offsetX - 10;
+ var offsetY = event.offsetY - 10;
+
+ calculateGravity(offsetX, offsetY);
+
+ lazyEndEvent();
+
+ };
+
+ var setDimensions = function(){
+ scope.dimensions.width = $image.width();
+ scope.dimensions.height = $image.height();
+
+ if(scope.center){
+ scope.dimensions.left = scope.center.left * scope.dimensions.width -10;
+ scope.dimensions.top = scope.center.top * scope.dimensions.height -10;
+ }else{
+ scope.center = { left: 0.5, top: 0.5 };
+ }
+ };
+
+ var calculateGravity = function(offsetX, offsetY){
+ scope.dimensions.left = offsetX;
+ scope.dimensions.top = offsetY;
+
+ scope.center.left = (scope.dimensions.left+10) / scope.dimensions.width;
+ scope.center.top = (scope.dimensions.top+10) / scope.dimensions.height;
+ };
+
+ var lazyEndEvent = _.debounce(function(){
+ scope.$apply(function(){
+ scope.$emit("imageFocalPointStop");
+ });
+ }, 2000);
+
+
+ //Drag and drop positioning, using jquery ui draggable
+ //TODO ensure that the point doesnt go outside the box
+ $overlay.draggable({
+ containment: "parent",
+ start: function(){
+ scope.$apply(function(){
+ scope.$emit("imageFocalPointStart");
+ });
+ },
+ stop: function() {
+ scope.$apply(function(){
+ var offsetX = $overlay[0].offsetLeft;
+ var offsetY = $overlay[0].offsetTop;
+ calculateGravity(offsetX, offsetY);
+ });
+
+ lazyEndEvent();
+ }
+ });
+
+ //// INIT /////
+ $image.load(function(){
+ $timeout(function(){
+ setDimensions();
+ scope.loaded = true;
+ scope.onImageLoaded();
+ });
+ });
+
+ $(window).on('resize.umbImageGravity', function(){
+ scope.$apply(function(){
+ $timeout(function(){
+ setDimensions();
+ });
+ var offsetX = $overlay[0].offsetLeft;
+ var offsetY = $overlay[0].offsetTop;
+ calculateGravity(offsetX, offsetY);
+ });
+ });
+
+ scope.$on('$destroy', function() {
+ $(window).off('.umbImageGravity');
+ });
+
+ }
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbImageThumbnail
+* @restrict E
+* @function
+* @description
+**/
+angular.module("umbraco.directives")
+ .directive('umbImageThumbnail',
+ function ($timeout, localizationService, cropperHelper, $log) {
+ return {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/imaging/umb-image-thumbnail.html',
+
+ scope: {
+ src: '=',
+ width: '@',
+ height: '@',
+ center: "=",
+ crop: "=",
+ maxSize: '@'
+ },
+
+ link: function(scope, element, attrs) {
+ //// INIT /////
+ var $image = element.find("img");
+ scope.loaded = false;
+
+ $image.load(function(){
+ $timeout(function(){
+ $image.width("auto");
+ $image.height("auto");
+
+ scope.image = {};
+ scope.image.width = $image[0].width;
+ scope.image.height = $image[0].height;
+
+ //we force a lower thumbnail size to fit the max size
+ //we do not compare to the image dimensions, but the thumbs
+ if(scope.maxSize){
+ var ratioCalculation = cropperHelper.calculateAspectRatioFit(
+ scope.width,
+ scope.height,
+ scope.maxSize,
+ scope.maxSize,
+ false);
+
+ //so if we have a max size, override the thumb sizes
+ scope.width = ratioCalculation.width;
+ scope.height = ratioCalculation.height;
+ }
+
+ setPreviewStyle();
+ scope.loaded = true;
+ });
+ });
+
+ /// WATCHERS ////
+ scope.$watchCollection('[crop, center]', function(newValues, oldValues){
+ //we have to reinit the whole thing if
+ //one of the external params changes
+ setPreviewStyle();
+ });
+
+ scope.$watch("center", function(){
+ setPreviewStyle();
+ }, true);
+
+ function setPreviewStyle(){
+ if(scope.crop && scope.image){
+ scope.preview = cropperHelper.convertToStyle(
+ scope.crop,
+ scope.image,
+ {width: scope.width, height: scope.height},
+ 0);
+ }else if(scope.image){
+
+ //returns size fitting the cropper
+ var p = cropperHelper.calculateAspectRatioFit(
+ scope.image.width,
+ scope.image.height,
+ scope.width,
+ scope.height,
+ true);
+
+
+ if(scope.center){
+ var xy = cropperHelper.alignToCoordinates(p, scope.center, {width: scope.width, height: scope.height});
+ p.top = xy.top;
+ p.left = xy.left;
+ }else{
+
+ }
+
+ p.position = "absolute";
+ scope.preview = p;
+ }
+ }
+ }
+ };
+ });
+
+angular.module("umbraco.directives")
+
+ .directive('localize', function ($log, localizationService) {
+ return {
+ restrict: 'E',
+ scope:{
+ key: '@'
+ },
+ replace: true,
+
+ link: function (scope, element, attrs) {
+ var key = scope.key;
+ localizationService.localize(key).then(function(value){
+ element.html(value);
+ });
+ }
+ };
+ })
+
+ .directive('localize', function ($log, localizationService) {
+ return {
+ restrict: 'A',
+ link: function (scope, element, attrs) {
+ var keys = attrs.localize.split(',');
+
+ angular.forEach(keys, function(value, key){
+ var attr = element.attr(value);
+
+ if(attr){
+ if(attr[0] === '@'){
+
+ var t = localizationService.tokenize(attr.substring(1), scope);
+ localizationService.localize(t.key, t.tokens).then(function(val){
+ element.attr(value, val);
+ });
+
+ }
+ }
+ });
+ }
+ };
+
+ });
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:umbNotifications
+ */
+
+(function() {
+ 'use strict';
+
+ function NotificationDirective(notificationsService) {
+
+ function link(scope, el, attr, ctrl) {
+
+ //subscribes to notifications in the notification service
+ scope.notifications = notificationsService.current;
+ scope.$watch('notificationsService.current', function (newVal, oldVal, scope) {
+ if (newVal) {
+ scope.notifications = newVal;
+ }
+ });
+
+ }
+
+ var directive = {
+ restrict: "E",
+ replace: true,
+ templateUrl: 'views/components/notifications/umb-notifications.html',
+ link: link
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbNotifications', NotificationDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbOverlay
+@restrict E
+@scope
+
+@description
+
+Markup example
+
+
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.openOverlay = openOverlay;
+
+ function openOverlay() {
+
+ vm.overlay = {
+ view: "mediapicker",
+ show: true,
+ submit: function(model) {
+
+ vm.overlay.show = false;
+ vm.overlay = null;
+ },
+ close: function(oldModel) {
+ vm.overlay.show = false;
+ vm.overlay = null;
+ }
+ }
+
+ };
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+General Options
+Lorem ipsum dolor sit amet..
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+ | model.title |
+ String |
+ Set the title of the overlay. |
+
+
+ | model.subTitle |
+ String |
+ Set the subtitle of the overlay. |
+
+
+ | model.submitButtonLabel |
+ String |
+ Set an alternate submit button text |
+
+
+ | model.submitButtonLabelKey |
+ String |
+ Set an alternate submit button label key for localized texts |
+
+
+ | model.hideSubmitButton |
+ Boolean |
+ Hides the submit button |
+
+
+ | model.closeButtonLabel |
+ String |
+ Set an alternate close button text |
+
+
+ | model.closeButtonLabelKey |
+ String |
+ Set an alternate close button label key for localized texts |
+
+
+ | model.show |
+ Boolean |
+ Show/hide the overlay |
+
+
+ | model.submit |
+ Function |
+ Callback function when the overlay submits. Returns the overlay model object |
+
+
+ | model.close |
+ Function |
+ Callback function when the overlay closes. Returns a copy of the overlay model object before being modified |
+
+
+
+
+Content Picker
+Opens a content picker.
+view: contentpicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+ | model.multiPicker |
+ Boolean |
+ Pick one or multiple items |
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+ | model.selection |
+ Array |
+ Array of content objects |
+
+
+
+
+Icon Picker
+Opens an icon picker.
+view: iconpicker
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+ | model.icon |
+ String |
+ The icon class |
+
+
+
+Item Picker
+Opens an item picker.
+view: itempicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.availableItems |
+ Array |
+ Array of available items |
+
+
+ | model.selectedItems |
+ Array |
+ Array of selected items. When passed in the selected items will be filtered from the available items. |
+
+
+ | model.filter |
+ Boolean |
+ Set to false to hide the filter |
+
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+ | model.selectedItem |
+ Object |
+ The selected item |
+
+
+
+Macro Picker
+Opens a media picker.
+view: macropicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.dialogData |
+ Object |
+ Object which contains array of allowedMacros. Set to null to allow all. |
+
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+
+ | model.macroParams |
+ Array |
+ Array of macro params |
+
+
+ | model.selectedMacro |
+ Object |
+ The selected macro |
+
+
+
+
+Media Picker
+Opens a media picker.
+view: mediapicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.multiPicker |
+ Boolean |
+ Pick one or multiple items |
+
+
+ | model.onlyImages |
+ Boolean |
+ Only display files that have an image file-extension |
+
+
+ | model.disableFolderSelect |
+ Boolean |
+ Disable folder selection |
+
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+
+ | model.selectedImages |
+ Array |
+ Array of selected images |
+
+
+
+
+Member Group Picker
+Opens a member group picker.
+view: membergrouppicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.multiPicker |
+ Boolean |
+ Pick one or multiple items |
+
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+
+ | model.selectedMemberGroup |
+ String |
+ The selected member group |
+
+
+ | model.selectedMemberGroups (multiPicker) |
+ Array |
+ The selected member groups |
+
+
+
+
+Member Picker
+Opens a member picker.
+view: memberpicker
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.multiPicker |
+ Boolean |
+ Pick one or multiple items |
+
+
+
+
+
+
+ | Returns |
+ Type |
+ Details |
+
+
+
+
+ | model.selection |
+ Array |
+ Array of selected members/td>
+ |
+
+
+
+YSOD
+Opens an overlay to show a custom YSOD.
+view: ysod
+
+
+
+ | Param |
+ Type |
+ Details |
+
+
+
+
+ | model.error |
+ Object |
+ Error object |
+
+
+
+
+@param {object} model Overlay options.
+@param {string} view Path to view or one of the default view names.
+@param {string} position The overlay position ("left", "right", "center": "target").
+**/
+
+(function() {
+ 'use strict';
+
+ function OverlayDirective($timeout, formHelper, overlayHelper, localizationService) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.directive = {
+ enableConfirmButton: false
+ };
+
+ var overlayNumber = 0;
+ var numberOfOverlays = 0;
+ var isRegistered = false;
+
+ var modelCopy = {};
+
+ function activate() {
+
+ setView();
+
+ setButtonText();
+
+ modelCopy = makeModelCopy(scope.model);
+
+ $timeout(function() {
+
+ if (scope.position === "target") {
+ setTargetPosition();
+ }
+
+ // this has to be done inside a timeout to ensure the destroy
+ // event on other overlays is run before registering a new one
+ registerOverlay();
+
+ setOverlayIndent();
+
+ });
+
+ }
+
+ function setView() {
+
+ if (scope.view) {
+
+ if (scope.view.indexOf(".html") === -1) {
+ var viewAlias = scope.view.toLowerCase();
+ scope.view = "views/common/overlays/" + viewAlias + "/" + viewAlias + ".html";
+ }
+
+ }
+
+ }
+
+ function setButtonText() {
+ if (!scope.model.closeButtonLabelKey && !scope.model.closeButtonLabel) {
+ scope.model.closeButtonLabel = localizationService.localize("general_close");
+ }
+ if (!scope.model.submitButtonLabelKey && !scope.model.submitButtonLabel) {
+ scope.model.submitButtonLabel = localizationService.localize("general_submit");
+ }
+ }
+
+ function registerOverlay() {
+
+ overlayNumber = overlayHelper.registerOverlay();
+
+ $(document).bind("keydown.overlay-" + overlayNumber, function(event) {
+
+ if (event.which === 27) {
+
+ numberOfOverlays = overlayHelper.getNumberOfOverlays();
+
+ if(numberOfOverlays === overlayNumber) {
+ scope.closeOverLay();
+ }
+
+ event.preventDefault();
+ }
+
+ if (event.which === 13) {
+
+ numberOfOverlays = overlayHelper.getNumberOfOverlays();
+
+ if(numberOfOverlays === overlayNumber) {
+
+ var activeElementType = document.activeElement.tagName;
+ var clickableElements = ["A", "BUTTON"];
+ var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter");
+
+ if(clickableElements.indexOf(activeElementType) === 0) {
+ document.activeElement.click();
+ event.preventDefault();
+ } else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
+
+
+ } else {
+ scope.$apply(function () {
+ scope.submitForm(scope.model);
+ });
+ event.preventDefault();
+ }
+
+ }
+
+ }
+
+ });
+
+ isRegistered = true;
+
+ }
+
+ function unregisterOverlay() {
+
+ if(isRegistered) {
+
+ overlayHelper.unregisterOverlay();
+
+ $(document).unbind("keydown.overlay-" + overlayNumber);
+
+ isRegistered = false;
+ }
+
+ }
+
+ function makeModelCopy(object) {
+
+ var newObject = {};
+
+ for (var key in object) {
+ if (key !== "event") {
+ newObject[key] = angular.copy(object[key]);
+ }
+ }
+
+ return newObject;
+
+ }
+
+ function setOverlayIndent() {
+
+ var overlayIndex = overlayNumber - 1;
+ var indentSize = overlayIndex * 20;
+ var overlayWidth = el.context.clientWidth;
+
+ el.css('width', overlayWidth - indentSize);
+
+ if(scope.position === "center" || scope.position === "target") {
+ var overlayTopPosition = el.context.offsetTop;
+ el.css('top', overlayTopPosition + indentSize);
+ }
+
+ }
+
+ function setTargetPosition() {
+
+ var container = $("#contentwrapper");
+ var containerLeft = container[0].offsetLeft;
+ var containerRight = containerLeft + container[0].offsetWidth;
+ var containerTop = container[0].offsetTop;
+ var containerBottom = containerTop + container[0].offsetHeight;
+
+ var mousePositionClickX = null;
+ var mousePositionClickY = null;
+ var elementHeight = null;
+ var elementWidth = null;
+
+ var position = {
+ right: "inherit",
+ left: "inherit",
+ top: "inherit",
+ bottom: "inherit"
+ };
+
+ // if mouse click position is know place element with mouse in center
+ if (scope.model.event && scope.model.event) {
+
+ // click position
+ mousePositionClickX = scope.model.event.pageX;
+ mousePositionClickY = scope.model.event.pageY;
+
+ // element size
+ elementHeight = el.context.clientHeight;
+ elementWidth = el.context.clientWidth;
+
+ // move element to this position
+ position.left = mousePositionClickX - (elementWidth / 2);
+ position.top = mousePositionClickY - (elementHeight / 2);
+
+ // check to see if element is outside screen
+ // outside right
+ if (position.left + elementWidth > containerRight) {
+ position.right = 10;
+ position.left = "inherit";
+ }
+
+ // outside bottom
+ if (position.top + elementHeight > containerBottom) {
+ position.bottom = 10;
+ position.top = "inherit";
+ }
+
+ // outside left
+ if (position.left < containerLeft) {
+ position.left = containerLeft + 10;
+ position.right = "inherit";
+ }
+
+ // outside top
+ if (position.top < containerTop) {
+ position.top = 10;
+ position.bottom = "inherit";
+ }
+
+ el.css(position);
+
+ }
+
+ }
+
+ scope.submitForm = function(model) {
+ if(scope.model.submit) {
+ if (formHelper.submitForm({scope: scope})) {
+ formHelper.resetForm({ scope: scope });
+
+ if(scope.model.confirmSubmit && scope.model.confirmSubmit.enable && !scope.directive.enableConfirmButton) {
+ scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
+ } else {
+ unregisterOverlay();
+ scope.model.submit(model, modelCopy, scope.directive.enableConfirmButton);
+ }
+
+ }
+ }
+ };
+
+ scope.cancelConfirmSubmit = function() {
+ scope.model.confirmSubmit.show = false;
+ };
+
+ scope.closeOverLay = function() {
+
+ unregisterOverlay();
+
+ if (scope.model.close) {
+ scope.model = modelCopy;
+ scope.model.close(scope.model);
+ } else {
+ scope.model.show = false;
+ scope.model = null;
+ }
+
+ };
+
+ // angular does not support ng-show on custom directives
+ // width isolated scopes. So we have to make our own.
+ if (attr.hasOwnProperty("ngShow")) {
+ scope.$watch("ngShow", function(value) {
+ if (value) {
+ el.show();
+ activate();
+ } else {
+ unregisterOverlay();
+ el.hide();
+ }
+ });
+ } else {
+ activate();
+ }
+
+ scope.$on('$destroy', function(){
+ unregisterOverlay();
+ });
+
+ }
+
+ var directive = {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/overlays/umb-overlay.html',
+ scope: {
+ ngShow: "=",
+ model: "=",
+ view: "=",
+ position: "@"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbOverlay', OverlayDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function OverlayBackdropDirective(overlayHelper) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.numberOfOverlays = 0;
+
+ scope.$watch(function(){
+ return overlayHelper.getNumberOfOverlays();
+ }, function (newValue) {
+ scope.numberOfOverlays = newValue;
+ });
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/overlays/umb-overlay-backdrop.html',
+ link: link
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbOverlayBackdrop', OverlayBackdropDirective);
+
+})();
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbProperty
+* @restrict E
+**/
+angular.module("umbraco.directives")
+ .directive('umbProperty', function (umbPropEditorHelper) {
+ return {
+ scope: {
+ property: "="
+ },
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/property/umb-property.html',
+ link: function(scope) {
+ scope.propertyAlias = Umbraco.Sys.ServerVariables.isDebuggingEnabled === true ? scope.property.alias : null;
+ },
+ //Define a controller for this directive to expose APIs to other directives
+ controller: function ($scope, $timeout) {
+
+ var self = this;
+
+ //set the API properties/methods
+
+ self.property = $scope.property;
+ self.setPropertyError = function(errorMsg) {
+ $scope.property.propertyErrorMessage = errorMsg;
+ };
+ }
+ };
+ });
+/**
+* @ngdoc directive
+* @function
+* @name umbraco.directives.directive:umbPropertyEditor
+* @requires formController
+* @restrict E
+**/
+
+//share property editor directive function
+var _umbPropertyEditor = function (umbPropEditorHelper) {
+ return {
+ scope: {
+ model: "=",
+ isPreValue: "@",
+ preview: "@"
+ },
+
+ require: "^form",
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/property/umb-property-editor.html',
+ link: function (scope, element, attrs, ctrl) {
+
+ //we need to copy the form controller val to our isolated scope so that
+ //it get's carried down to the child scopes of this!
+ //we'll also maintain the current form name.
+ scope[ctrl.$name] = ctrl;
+
+ if(!scope.model.alias){
+ scope.model.alias = Math.random().toString(36).slice(2);
+ }
+
+ scope.$watch("model.view", function(val){
+ scope.propertyEditorView = umbPropEditorHelper.getViewPath(scope.model.view, scope.isPreValue);
+ });
+ }
+ };
+ };
+
+//Preffered is the umb-property-editor as its more explicit - but we keep umb-editor for backwards compat
+angular.module("umbraco.directives").directive('umbPropertyEditor', _umbPropertyEditor);
+angular.module("umbraco.directives").directive('umbEditor', _umbPropertyEditor);
+
+angular.module("umbraco.directives.html")
+ .directive('umbPropertyGroup', function () {
+ return {
+ transclude: true,
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/property/umb-property-group.html'
+ };
+ });
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTab
+* @restrict E
+**/
+angular.module("umbraco.directives")
+.directive('umbTab', function ($parse, $timeout) {
+ return {
+ restrict: 'E',
+ replace: true,
+ transclude: 'true',
+ templateUrl: 'views/components/tabs/umb-tab.html'
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTabs
+* @restrict A
+* @description Used to bind to bootstrap tab events so that sub directives can use this API to listen to tab changes
+**/
+angular.module("umbraco.directives")
+.directive('umbTabs', function () {
+ return {
+ restrict: 'A',
+ controller: function ($scope, $element, $attrs) {
+
+ var callbacks = [];
+ this.onTabShown = function(cb) {
+ callbacks.push(cb);
+ };
+
+ function tabShown(event) {
+
+ var curr = $(event.target); // active tab
+ var prev = $(event.relatedTarget); // previous tab
+
+ for (var c in callbacks) {
+ callbacks[c].apply(this, [{current: curr, previous: prev}]);
+ }
+ }
+
+ //NOTE: it MUST be done this way - binding to an ancestor element that exists
+ // in the DOM to bind to the dynamic elements that will be created.
+ // It would be nicer to create this event handler as a directive for which child
+ // directives can attach to.
+ $element.on('shown', '.nav-tabs a', tabShown);
+
+ //ensure to unregister
+ $scope.$on('$destroy', function () {
+ $element.off('shown', '.nav-tabs a', tabShown);
+
+ for (var c in callbacks) {
+ delete callbacks[c];
+ }
+ callbacks = null;
+ });
+ }
+ };
+});
+(function() {
+ 'use strict';
+
+ function UmbTabsContentDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.view = attr.view;
+
+ }
+
+ var directive = {
+ restrict: "E",
+ replace: true,
+ transclude: 'true',
+ templateUrl: "views/components/tabs/umb-tabs-content.html",
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbTabsContent', UmbTabsContentDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function UmbTabsNavDirective($timeout) {
+
+ function link(scope, el, attr) {
+
+ function activate() {
+
+ $timeout(function () {
+
+ //use bootstrap tabs API to show the first one
+ el.find("a:first").tab('show');
+
+ //enable the tab drop
+ el.tabdrop();
+
+ });
+
+ }
+
+ var unbindModelWatch = scope.$watch('model', function(newValue, oldValue){
+
+ activate();
+
+ });
+
+
+ scope.$on('$destroy', function () {
+
+ //ensure to destroy tabdrop (unbinds window resize listeners)
+ el.tabdrop("destroy");
+
+ unbindModelWatch();
+
+ });
+
+ }
+
+ var directive = {
+ restrict: "E",
+ replace: true,
+ templateUrl: "views/components/tabs/umb-tabs-nav.html",
+ scope: {
+ model: "=",
+ tabdrop: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbTabsNav', UmbTabsNavDirective);
+
+})();
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTree
+* @restrict E
+**/
+function umbTreeDirective($compile, $log, $q, $rootScope, treeService, notificationsService, $timeout, userService) {
+
+ return {
+ restrict: 'E',
+ replace: true,
+ terminal: false,
+
+ scope: {
+ section: '@',
+ treealias: '@',
+ hideoptions: '@',
+ hideheader: '@',
+ cachekey: '@',
+ isdialog: '@',
+ //Custom query string arguments to pass in to the tree as a string, example: "startnodeid=123&something=value"
+ customtreeparams: '@',
+ eventhandler: '=',
+ enablecheckboxes: '@',
+ enablelistviewsearch: '@'
+ },
+
+ compile: function(element, attrs) {
+ //config
+ //var showheader = (attrs.showheader !== 'false');
+ var hideoptions = (attrs.hideoptions === 'true') ? "hide-options" : "";
+ var template = '- ';
+ template += '';
+ template += '' +
+ '
' +
+ '
';
+
+ element.replaceWith(template);
+
+ return function(scope, elem, attr, controller) {
+
+ //flag to track the last loaded section when the tree 'un-loads'. We use this to determine if we should
+ // re-load the tree again. For example, if we hover over 'content' the content tree is shown. Then we hover
+ // outside of the tree and the tree 'un-loads'. When we re-hover over 'content', we don't want to re-load the
+ // entire tree again since we already still have it in memory. Of course if the section is different we will
+ // reload it. This saves a lot on processing if someone is navigating in and out of the same section many times
+ // since it saves on data retreival and DOM processing.
+ var lastSection = "";
+
+ //setup a default internal handler
+ if (!scope.eventhandler) {
+ scope.eventhandler = $({});
+ }
+
+ //flag to enable/disable delete animations
+ var deleteAnimations = false;
+
+
+ /** Helper function to emit tree events */
+ function emitEvent(eventName, args) {
+ if (scope.eventhandler) {
+ $(scope.eventhandler).trigger(eventName, args);
+ }
+ }
+
+ /** This will deleteAnimations to true after the current digest */
+ function enableDeleteAnimations() {
+ //do timeout so that it re-enables them after this digest
+ $timeout(function () {
+ //enable delete animations
+ deleteAnimations = true;
+ }, 0, false);
+ }
+
+
+ /*this is the only external interface a tree has */
+ function setupExternalEvents() {
+ if (scope.eventhandler) {
+
+ scope.eventhandler.clearCache = function(section) {
+ treeService.clearCache({ section: section });
+ };
+
+ scope.eventhandler.load = function(section) {
+ scope.section = section;
+ loadTree();
+ };
+
+ scope.eventhandler.reloadNode = function(node) {
+
+ if (!node) {
+ node = scope.currentNode;
+ }
+
+ if (node) {
+ scope.loadChildren(node, true);
+ }
+ };
+
+ /**
+ Used to do the tree syncing. If the args.tree is not specified we are assuming it has been
+ specified previously using the _setActiveTreeType
+ */
+ scope.eventhandler.syncTree = function(args) {
+ if (!args) {
+ throw "args cannot be null";
+ }
+ if (!args.path) {
+ throw "args.path cannot be null";
+ }
+
+ var deferred = $q.defer();
+
+ //this is super complex but seems to be working in other places, here we're listening for our
+ // own events, once the tree is sycned we'll resolve our promise.
+ scope.eventhandler.one("treeSynced", function (e, syncArgs) {
+ deferred.resolve(syncArgs);
+ });
+
+ //this should normally be set unless it is being called from legacy
+ // code, so set the active tree type before proceeding.
+ if (args.tree) {
+ loadActiveTree(args.tree);
+ }
+
+ if (angular.isString(args.path)) {
+ args.path = args.path.replace('"', '').split(',');
+ }
+
+ //reset current node selection
+ //scope.currentNode = null;
+
+ //Filter the path for root node ids (we don't want to pass in -1 or 'init')
+
+ args.path = _.filter(args.path, function (item) { return (item !== "init" && item !== "-1"); });
+
+ //Once those are filtered we need to check if the current user has a special start node id,
+ // if they do, then we're going to trim the start of the array for anything found from that start node
+ // and previous so that the tree syncs properly. The tree syncs from the top down and if there are parts
+ // of the tree's path in there that don't actually exist in the dom/model then syncing will not work.
+
+ userService.getCurrentUser().then(function(userData) {
+
+ var startNodes = [userData.startContentId, userData.startMediaId];
+ _.each(startNodes, function (i) {
+ var found = _.find(args.path, function (p) {
+ return String(p) === String(i);
+ });
+ if (found) {
+ args.path = args.path.splice(_.indexOf(args.path, found));
+ }
+ });
+
+
+ loadPath(args.path, args.forceReload, args.activate);
+
+ });
+
+
+
+ return deferred.promise;
+ };
+
+ /**
+ Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
+ have to set an active tree and then sync, the new API does this in one method by using syncTree.
+ loadChildren is optional but if it is set, it will set the current active tree and load the root
+ node's children - this is synonymous with the legacy refreshTree method - again should not be used
+ and should only be used for the legacy code to work.
+ */
+ scope.eventhandler._setActiveTreeType = function(treeAlias, loadChildren) {
+ loadActiveTree(treeAlias, loadChildren);
+ };
+ }
+ }
+
+
+ //helper to load a specific path on the active tree as soon as its ready
+ function loadPath(path, forceReload, activate) {
+
+ if (scope.activeTree) {
+ syncTree(scope.activeTree, path, forceReload, activate);
+ }
+ else {
+ scope.eventhandler.one("activeTreeLoaded", function (e, args) {
+ syncTree(args.tree, path, forceReload, activate);
+ });
+ }
+ }
+
+
+ //given a tree alias, this will search the current section tree for the specified tree alias and
+ //set that to the activeTree
+ //NOTE: loadChildren is ONLY used for legacy purposes, do not use this when syncing the tree as it will cause problems
+ // since there will be double request and event handling operations.
+ function loadActiveTree(treeAlias, loadChildren) {
+ if (!treeAlias) {
+ return;
+ }
+
+ scope.activeTree = undefined;
+
+ function doLoad(tree) {
+ var childrenAndSelf = [tree].concat(tree.children);
+ scope.activeTree = _.find(childrenAndSelf, function (node) {
+ if(node && node.metaData && node.metaData.treeAlias) {
+ return node.metaData.treeAlias.toUpperCase() === treeAlias.toUpperCase();
+ }
+ return false;
+ });
+
+ if (!scope.activeTree) {
+ throw "Could not find the tree " + treeAlias + ", activeTree has not been set";
+ }
+
+ //This is only used for the legacy tree method refreshTree!
+ if (loadChildren) {
+ scope.activeTree.expanded = true;
+ scope.loadChildren(scope.activeTree, false).then(function() {
+ emitEvent("activeTreeLoaded", { tree: scope.activeTree });
+ });
+ }
+ else {
+ emitEvent("activeTreeLoaded", { tree: scope.activeTree });
+ }
+ }
+
+ if (scope.tree) {
+ doLoad(scope.tree.root);
+ }
+ else {
+ scope.eventhandler.one("treeLoaded", function(e, args) {
+ doLoad(args.tree.root);
+ });
+ }
+ }
+
+
+ /** Method to load in the tree data */
+
+ function loadTree() {
+ if (!scope.loading && scope.section) {
+ scope.loading = true;
+
+ //anytime we want to load the tree we need to disable the delete animations
+ deleteAnimations = false;
+
+ //default args
+ var args = { section: scope.section, tree: scope.treealias, cacheKey: scope.cachekey, isDialog: scope.isdialog ? scope.isdialog : false };
+
+ //add the extra query string params if specified
+ if (scope.customtreeparams) {
+ args["queryString"] = scope.customtreeparams;
+ }
+
+ treeService.getTree(args)
+ .then(function(data) {
+ //set the data once we have it
+ scope.tree = data;
+
+ enableDeleteAnimations();
+
+ scope.loading = false;
+
+ //set the root as the current active tree
+ scope.activeTree = scope.tree.root;
+ emitEvent("treeLoaded", { tree: scope.tree });
+ emitEvent("treeNodeExpanded", { tree: scope.tree, node: scope.tree.root, children: scope.tree.root.children });
+
+ }, function(reason) {
+ scope.loading = false;
+ notificationsService.error("Tree Error", reason);
+ });
+ }
+ }
+
+ /** syncs the tree, the treeNode can be ANY tree node in the tree that requires syncing */
+ function syncTree(treeNode, path, forceReload, activate) {
+
+ deleteAnimations = false;
+
+ treeService.syncTree({
+ node: treeNode,
+ path: path,
+ forceReload: forceReload
+ }).then(function (data) {
+
+ if (activate === undefined || activate === true) {
+ scope.currentNode = data;
+ }
+
+ emitEvent("treeSynced", { node: data, activate: activate });
+
+ enableDeleteAnimations();
+ });
+
+ }
+
+ scope.selectEnabledNodeClass = function (node) {
+ return node ?
+ node.selected ?
+ 'icon umb-tree-icon sprTree icon-check blue temporary' :
+ '' :
+ '';
+ };
+
+ /** method to set the current animation for the node.
+ * This changes dynamically based on if we are changing sections or just loading normal tree data.
+ * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations.
+ */
+ scope.animation = function() {
+ if (deleteAnimations && scope.tree && scope.tree.root && scope.tree.root.expanded) {
+ return { leave: 'tree-node-delete-leave' };
+ }
+ else {
+ return {};
+ }
+ };
+
+ /* helper to force reloading children of a tree node */
+ scope.loadChildren = function(node, forceReload) {
+ var deferred = $q.defer();
+
+ //emit treeNodeExpanding event, if a callback object is set on the tree
+ emitEvent("treeNodeExpanding", { tree: scope.tree, node: node });
+
+ //standardising
+ if (!node.children) {
+ node.children = [];
+ }
+
+ if (forceReload || (node.hasChildren && node.children.length === 0)) {
+ //get the children from the tree service
+ treeService.loadNodeChildren({ node: node, section: scope.section })
+ .then(function(data) {
+ //emit expanded event
+ emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data });
+
+ enableDeleteAnimations();
+
+ deferred.resolve(data);
+ });
+ }
+ else {
+ emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: node.children });
+ node.expanded = true;
+
+ enableDeleteAnimations();
+
+ deferred.resolve(node.children);
+ }
+
+ return deferred.promise;
+ };
+
+ /**
+ Method called when the options button next to the root node is called.
+ The tree doesnt know about this, so it raises an event to tell the parent controller
+ about it.
+ */
+ scope.options = function(n, ev) {
+ emitEvent("treeOptionsClick", { element: elem, node: n, event: ev });
+ };
+
+ /**
+ Method called when an item is clicked in the tree, this passes the
+ DOM element, the tree node object and the original click
+ and emits it as a treeNodeSelect element if there is a callback object
+ defined on the tree
+ */
+ scope.select = function (n, ev) {
+ //on tree select we need to remove the current node -
+ // whoever handles this will need to make sure the correct node is selected
+ //reset current node selection
+ scope.currentNode = null;
+
+ emitEvent("treeNodeSelect", { element: elem, node: n, event: ev });
+ };
+
+ scope.altSelect = function(n, ev) {
+ emitEvent("treeNodeAltSelect", { element: elem, tree: scope.tree, node: n, event: ev });
+ };
+
+ //watch for section changes
+ scope.$watch("section", function(newVal, oldVal) {
+
+ if (!scope.tree) {
+ loadTree();
+ }
+
+ if (!newVal) {
+ //store the last section loaded
+ lastSection = oldVal;
+ }
+ else if (newVal !== oldVal && newVal !== lastSection) {
+ //only reload the tree data and Dom if the newval is different from the old one
+ // and if the last section loaded is different from the requested one.
+ loadTree();
+
+ //store the new section to be loaded as the last section
+ //clear any active trees to reset lookups
+ lastSection = newVal;
+ }
+ });
+
+ setupExternalEvents();
+ loadTree();
+ };
+ }
+ };
+}
+
+angular.module("umbraco.directives").directive('umbTree', umbTreeDirective);
+
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:umbTreeItem
+ * @element li
+ * @function
+ *
+ * @description
+ * Renders a list item, representing a single node in the tree.
+ * Includes element to toggle children, and a menu toggling button
+ *
+ * **note:** This directive is only used internally in the umbTree directive
+ *
+ * @example
+
+
+
+
+
+ */
+angular.module("umbraco.directives")
+.directive('umbTreeItem', function ($compile, $http, $templateCache, $interpolate, $log, $location, $rootScope, $window, treeService, $timeout, localizationService) {
+ return {
+ restrict: 'E',
+ replace: true,
+
+ scope: {
+ section: '@',
+ eventhandler: '=',
+ currentNode: '=',
+ node: '=',
+ tree: '='
+ },
+
+ //TODO: Remove more of the binding from this template and move the DOM manipulation to be manually done in the link function,
+ // this will greatly improve performance since there's potentially a lot of nodes being rendered = a LOT of watches!
+
+ template: '' +
+ '' +
+ //NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
+ //'
' +
+ '
' +
+ '
' +
+ '
' +
+ //NOTE: These are the 'option' elipses
+ '
' +
+ '
' +
+ '
' +
+ '',
+
+ link: function (scope, element, attrs) {
+
+ localizationService.localize("general_search").then(function (value) {
+ scope.searchAltText = value;
+ });
+
+ //flag to enable/disable delete animations, default for an item is true
+ var deleteAnimations = true;
+
+ // Helper function to emit tree events
+ function emitEvent(eventName, args) {
+
+ if (scope.eventhandler) {
+ $(scope.eventhandler).trigger(eventName, args);
+ }
+ }
+
+ // updates the node's DOM/styles
+ function setupNodeDom(node, tree) {
+
+ //get the first div element
+ element.children(":first")
+ //set the padding
+ .css("padding-left", (node.level * 20) + "px");
+
+ //remove first 'ins' if there is no children
+ //show/hide last 'ins' depending on children
+ if (!node.hasChildren) {
+ element.find("ins:first").remove();
+ element.find("ins").last().hide();
+ }
+ else {
+ element.find("ins").last().show();
+ }
+
+ var icon = element.find("i:first");
+ icon.addClass(node.cssClass);
+ icon.attr("title", node.routePath);
+
+ element.find("a:first").text(node.name);
+
+ if (!node.menuUrl) {
+ element.find("a.umb-options").remove();
+ }
+
+ if (node.style) {
+ element.find("i:first").attr("style", node.style);
+ }
+ }
+
+ //This will deleteAnimations to true after the current digest
+ function enableDeleteAnimations() {
+ //do timeout so that it re-enables them after this digest
+ $timeout(function () {
+ //enable delete animations
+ deleteAnimations = true;
+ }, 0, false);
+ }
+
+ /** Returns the css classses assigned to the node (div element) */
+ scope.getNodeCssClass = function (node) {
+ if (!node) {
+ return '';
+ }
+ var css = [];
+ if (node.cssClasses) {
+ _.each(node.cssClasses, function(c) {
+ css.push(c);
+ });
+ }
+ if (node.selected) {
+ css.push("umb-tree-node-checked");
+ }
+ return css.join(" ");
+ };
+
+ //add a method to the node which we can use to call to update the node data if we need to ,
+ // this is done by sync tree, we don't want to add a $watch for each node as that would be crazy insane slow
+ // so we have to do this
+ scope.node.updateNodeData = function (newNode) {
+ _.extend(scope.node, newNode);
+ //now update the styles
+ setupNodeDom(scope.node, scope.tree);
+ };
+
+ /**
+ Method called when the options button next to a node is called
+ In the main tree this opens the menu, but internally the tree doesnt
+ know about this, so it simply raises an event to tell the parent controller
+ about it.
+ */
+ scope.options = function (n, ev) {
+ emitEvent("treeOptionsClick", { element: element, tree: scope.tree, node: n, event: ev });
+ };
+
+ /**
+ Method called when an item is clicked in the tree, this passes the
+ DOM element, the tree node object and the original click
+ and emits it as a treeNodeSelect element if there is a callback object
+ defined on the tree
+ */
+ scope.select = function (n, ev) {
+ if (ev.ctrlKey ||
+ ev.shiftKey ||
+ ev.metaKey || // apple
+ (ev.button && ev.button === 1) // middle click, >IE9 + everyone else
+ ) {
+ return;
+ }
+
+ emitEvent("treeNodeSelect", { element: element, tree: scope.tree, node: n, event: ev });
+ ev.preventDefault();
+ };
+
+ /**
+ Method called when an item is right-clicked in the tree, this passes the
+ DOM element, the tree node object and the original click
+ and emits it as a treeNodeSelect element if there is a callback object
+ defined on the tree
+ */
+ scope.altSelect = function (n, ev) {
+ emitEvent("treeNodeAltSelect", { element: element, tree: scope.tree, node: n, event: ev });
+ };
+
+ /** method to set the current animation for the node.
+ * This changes dynamically based on if we are changing sections or just loading normal tree data.
+ * When changing sections we don't want all of the tree-ndoes to do their 'leave' animations.
+ */
+ scope.animation = function () {
+ if (scope.node.showHideAnimation) {
+ return scope.node.showHideAnimation;
+ }
+ if (deleteAnimations && scope.node.expanded) {
+ return { leave: 'tree-node-delete-leave' };
+ }
+ else {
+ return {};
+ }
+ };
+
+ /**
+ Method called when a node in the tree is expanded, when clicking the arrow
+ takes the arrow DOM element and node data as parameters
+ emits treeNodeCollapsing event if already expanded and treeNodeExpanding if collapsed
+ */
+ scope.load = function (node) {
+ if (node.expanded) {
+ deleteAnimations = false;
+ emitEvent("treeNodeCollapsing", { tree: scope.tree, node: node, element: element });
+ node.expanded = false;
+ }
+ else {
+ scope.loadChildren(node, false);
+ }
+ };
+
+ /* helper to force reloading children of a tree node */
+ scope.loadChildren = function (node, forceReload) {
+ //emit treeNodeExpanding event, if a callback object is set on the tree
+ emitEvent("treeNodeExpanding", { tree: scope.tree, node: node });
+
+ if (node.hasChildren && (forceReload || !node.children || (angular.isArray(node.children) && node.children.length === 0))) {
+ //get the children from the tree service
+ treeService.loadNodeChildren({ node: node, section: scope.section })
+ .then(function (data) {
+ //emit expanded event
+ emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: data });
+ enableDeleteAnimations();
+ });
+ }
+ else {
+ emitEvent("treeNodeExpanded", { tree: scope.tree, node: node, children: node.children });
+ node.expanded = true;
+ enableDeleteAnimations();
+ }
+ };
+
+ //if the current path contains the node id, we will auto-expand the tree item children
+
+ setupNodeDom(scope.node, scope.tree);
+
+ var template = '';
+ var newElement = angular.element(template);
+ $compile(newElement)(scope);
+ element.append(newElement);
+
+ }
+ };
+});
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTreeSearchBox
+* @function
+* @element ANY
+* @restrict E
+**/
+function treeSearchBox(localizationService, searchService, $q) {
+ return {
+ scope: {
+ searchFromId: "@",
+ searchFromName: "@",
+ showSearch: "@",
+ section: "@",
+ hideSearchCallback: "=",
+ searchCallback: "="
+ },
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/components/tree/umb-tree-search-box.html',
+ link: function (scope, element, attrs, ctrl) {
+
+ scope.term = "";
+ scope.hideSearch = function() {
+ scope.term = "";
+ scope.hideSearchCallback();
+ };
+
+ localizationService.localize("general_typeToSearch").then(function (value) {
+ scope.searchPlaceholderText = value;
+ });
+
+ if (!scope.showSearch) {
+ scope.showSearch = "false";
+ }
+
+ //used to cancel any request in progress if another one needs to take it's place
+ var canceler = null;
+
+ function performSearch() {
+ if (scope.term) {
+ scope.results = [];
+
+ //a canceler exists, so perform the cancelation operation and reset
+ if (canceler) {
+ console.log("CANCELED!");
+ canceler.resolve();
+ canceler = $q.defer();
+ }
+ else {
+ canceler = $q.defer();
+ }
+
+ var searchArgs = {
+ term: scope.term,
+ canceler: canceler
+ };
+
+ //append a start node context if there is one
+ if (scope.searchFromId) {
+ searchArgs["searchFrom"] = scope.searchFromId;
+ }
+
+ searcher(searchArgs).then(function (data) {
+ scope.searchCallback(data);
+ //set back to null so it can be re-created
+ canceler = null;
+ });
+ }
+ }
+
+ scope.$watch("term", _.debounce(function(newVal, oldVal) {
+ scope.$apply(function() {
+ if (newVal !== null && newVal !== undefined && newVal !== oldVal) {
+ performSearch();
+ }
+ });
+ }, 200));
+
+ var searcher = searchService.searchContent;
+ //search
+ if (scope.section === "member") {
+ searcher = searchService.searchMembers;
+ }
+ else if (scope.section === "media") {
+ searcher = searchService.searchMedia;
+ }
+ }
+ };
+}
+angular.module('umbraco.directives').directive("umbTreeSearchBox", treeSearchBox);
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbTreeSearchResults
+* @function
+* @element ANY
+* @restrict E
+**/
+function treeSearchResults() {
+ return {
+ scope: {
+ results: "=",
+ selectResultCallback: "="
+ },
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/components/tree/umb-tree-search-results.html',
+ link: function (scope, element, attrs, ctrl) {
+
+ }
+ };
+}
+angular.module('umbraco.directives').directive("umbTreeSearchResults", treeSearchResults);
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbGenerateAlias
+@restrict E
+@scope
+
+@description
+Use this directive to generate a camelCased umbraco alias.
+When the aliasFrom value is changed the directive will get a formatted alias from the server and update the alias model. If "enableLock" is set to true
+the directive will use {@link umbraco.directives.directive:umbLockedField umbLockedField} to lock and unlock the alias.
+
+Markup example
+
+
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.name = "";
+ vm.alias = "";
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {string} alias (binding): The model where the alias is bound.
+@param {string} aliasFrom (binding): The model to generate the alias from.
+@param {boolean=} enableLock (binding): Set to true to add a lock next to the alias from where it can be unlocked and changed.
+**/
+
+angular.module("umbraco.directives")
+ .directive('umbGenerateAlias', function ($timeout, entityResource) {
+ return {
+ restrict: 'E',
+ templateUrl: 'views/components/umb-generate-alias.html',
+ replace: true,
+ scope: {
+ alias: '=',
+ aliasFrom: '=',
+ enableLock: '=?',
+ serverValidationField: '@'
+ },
+ link: function (scope, element, attrs, ctrl) {
+
+ var eventBindings = [];
+ var bindWatcher = true;
+ var generateAliasTimeout = "";
+ var updateAlias = false;
+
+ scope.locked = true;
+ scope.placeholderText = "Enter alias...";
+
+ function generateAlias(value) {
+
+ if (generateAliasTimeout) {
+ $timeout.cancel(generateAliasTimeout);
+ }
+
+ if( value !== undefined && value !== "" && value !== null) {
+
+ scope.alias = "";
+ scope.placeholderText = "Generating Alias...";
+
+ generateAliasTimeout = $timeout(function () {
+ updateAlias = true;
+ entityResource.getSafeAlias(value, true).then(function (safeAlias) {
+ if (updateAlias) {
+ scope.alias = safeAlias.alias;
+ }
+ });
+ }, 500);
+
+ } else {
+ updateAlias = true;
+ scope.alias = "";
+ scope.placeholderText = "Enter alias...";
+ }
+
+ }
+
+ // if alias gets unlocked - stop watching alias
+ eventBindings.push(scope.$watch('locked', function(newValue, oldValue){
+ if(newValue === false) {
+ bindWatcher = false;
+ }
+ }));
+
+ // validate custom entered alias
+ eventBindings.push(scope.$watch('alias', function(newValue, oldValue){
+
+ if(scope.alias === "" && bindWatcher === true || scope.alias === null && bindWatcher === true) {
+ // add watcher
+ eventBindings.push(scope.$watch('aliasFrom', function(newValue, oldValue) {
+ if(bindWatcher) {
+ generateAlias(newValue);
+ }
+ }));
+ }
+
+ }));
+
+ // clean up
+ scope.$on('$destroy', function(){
+ // unbind watchers
+ for(var e in eventBindings) {
+ eventBindings[e]();
+ }
+ });
+
+ }
+ };
+ });
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbChildSelector
+@restrict E
+@scope
+
+@description
+Use this directive to render a ui component for selecting child items to a parent node.
+
+Markup example
+
+
+
+
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.id = 1;
+ vm.name = "My Parent element";
+ vm.icon = "icon-document";
+ vm.selectedChildren = [];
+ vm.availableChildren = [
+ {
+ id: 1,
+ alias: "item1",
+ name: "Item 1",
+ icon: "icon-document"
+ },
+ {
+ id: 2,
+ alias: "item2",
+ name: "Item 2",
+ icon: "icon-document"
+ }
+ ];
+
+ vm.addChild = addChild;
+ vm.removeChild = removeChild;
+
+ function addChild($event) {
+ vm.overlay = {
+ view: "itempicker",
+ title: "Choose child",
+ availableItems: vm.availableChildren,
+ selectedItems: vm.selectedChildren,
+ event: $event,
+ show: true,
+ submit: function(model) {
+
+ // add selected child
+ vm.selectedChildren.push(model.selectedItem);
+
+ // close overlay
+ vm.overlay.show = false;
+ vm.overlay = null;
+ }
+ };
+ }
+
+ function removeChild($index) {
+ vm.selectedChildren.splice($index, 1);
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+
+ })();
+
+
+@param {array} selectedChildren (binding): Array of selected children.
+@param {array} availableChildren (binding: Array of items available for selection.
+@param {string} parentName (binding): The parent name.
+@param {string} parentIcon (binding): The parent icon.
+@param {number} parentId (binding): The parent id.
+@param {callback} onRemove (binding): Callback when the remove button is clicked on an item.
+ The callback returns:
+
+ child: The selected item.
+ $index: The selected item index.
+
+@param {callback} onAdd (binding): Callback when the add button is clicked.
+ The callback returns:
+
+ $event: The select event.
+
+**/
+
+(function() {
+ 'use strict';
+
+ function ChildSelectorDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ var eventBindings = [];
+ scope.dialogModel = {};
+ scope.showDialog = false;
+
+ scope.removeChild = function(selectedChild, $index) {
+ if(scope.onRemove) {
+ scope.onRemove(selectedChild, $index);
+ }
+ };
+
+ scope.addChild = function($event) {
+ if(scope.onAdd) {
+ scope.onAdd($event);
+ }
+ };
+
+ function syncParentName() {
+
+ // update name on available item
+ angular.forEach(scope.availableChildren, function(availableChild){
+ if(availableChild.id === scope.parentId) {
+ availableChild.name = scope.parentName;
+ }
+ });
+
+ // update name on selected child
+ angular.forEach(scope.selectedChildren, function(selectedChild){
+ if(selectedChild.id === scope.parentId) {
+ selectedChild.name = scope.parentName;
+ }
+ });
+
+ }
+
+ function syncParentIcon() {
+
+ // update icon on available item
+ angular.forEach(scope.availableChildren, function(availableChild){
+ if(availableChild.id === scope.parentId) {
+ availableChild.icon = scope.parentIcon;
+ }
+ });
+
+ // update icon on selected child
+ angular.forEach(scope.selectedChildren, function(selectedChild){
+ if(selectedChild.id === scope.parentId) {
+ selectedChild.icon = scope.parentIcon;
+ }
+ });
+
+ }
+
+ eventBindings.push(scope.$watch('parentName', function(newValue, oldValue){
+
+ if (newValue === oldValue) { return; }
+ if ( oldValue === undefined || newValue === undefined) { return; }
+
+ syncParentName();
+
+ }));
+
+ eventBindings.push(scope.$watch('parentIcon', function(newValue, oldValue){
+
+ if (newValue === oldValue) { return; }
+ if ( oldValue === undefined || newValue === undefined) { return; }
+
+ syncParentIcon();
+ }));
+
+ // clean up
+ scope.$on('$destroy', function(){
+ // unbind watchers
+ for(var e in eventBindings) {
+ eventBindings[e]();
+ }
+ });
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-child-selector.html',
+ scope: {
+ selectedChildren: '=',
+ availableChildren: "=",
+ parentName: "=",
+ parentIcon: "=",
+ parentId: "=",
+ onRemove: "=",
+ onAdd: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbChildSelector', ChildSelectorDirective);
+
+})();
+
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:umbConfirm
+ * @function
+ * @description
+ * A confirmation dialog
+ *
+ * @restrict E
+ */
+function confirmDirective() {
+ return {
+ restrict: "E", // restrict to an element
+ replace: true, // replace the html element with the template
+ templateUrl: 'views/components/umb-confirm.html',
+ scope: {
+ onConfirm: '=',
+ onCancel: '=',
+ caption: '@'
+ },
+ link: function (scope, element, attr, ctrl) {
+
+ }
+ };
+}
+angular.module('umbraco.directives').directive("umbConfirm", confirmDirective);
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbConfirmAction
+@restrict E
+@scope
+
+@description
+Use this directive to toggle a confirmation prompt for an action.
+The prompt consists of a checkmark and a cross to confirm or cancel the action.
+The prompt can be opened in four direction up, down, left or right.
+
+Markup example
+
+
+
+
+Controller example
+
+ (function () {
+
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.promptIsVisible = false;
+
+ vm.confirmAction = confirmAction;
+ vm.showPrompt = showPrompt;
+ vm.hidePrompt = hidePrompt;
+
+ function confirmAction() {
+ // confirm logic here
+ }
+
+ function showPrompt() {
+ vm.promptIsVisible = true;
+ }
+
+ function hidePrompt() {
+ vm.promptIsVisible = false;
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {string} direction The direction the prompt opens ("up", "down", "left", "right").
+@param {callback} onConfirm Callback when the checkmark is clicked.
+@param {callback} onCancel Callback when the cross is clicked.
+**/
+
+(function() {
+ 'use strict';
+
+ function ConfirmAction() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.clickConfirm = function() {
+ if(scope.onConfirm) {
+ scope.onConfirm();
+ }
+ };
+
+ scope.clickCancel = function() {
+ if(scope.onCancel) {
+ scope.onCancel();
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-confirm-action.html',
+ scope: {
+ direction: "@",
+ onConfirm: "&",
+ onCancel: "&"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbConfirmAction', ConfirmAction);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbContentGrid
+@restrict E
+@scope
+
+@description
+Use this directive to generate a list of content items presented as a flexbox grid.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.contentItems = [
+ {
+ "name": "Cape",
+ "published": true,
+ "icon": "icon-document",
+ "updateDate": "15-02-2016",
+ "owner": "Mr. Batman",
+ "selected": false
+ },
+ {
+ "name": "Utility Belt",
+ "published": true,
+ "icon": "icon-document",
+ "updateDate": "15-02-2016",
+ "owner": "Mr. Batman",
+ "selected": false
+ },
+ {
+ "name": "Cave",
+ "published": true,
+ "icon": "icon-document",
+ "updateDate": "15-02-2016",
+ "owner": "Mr. Batman",
+ "selected": false
+ }
+ ];
+ vm.includeProperties = [
+ {
+ "alias": "updateDate",
+ "header": "Last edited"
+ },
+ {
+ "alias": "owner",
+ "header": "Created by"
+ }
+ ];
+
+ vm.clickItem = clickItem;
+ vm.selectItem = selectItem;
+
+
+ function clickItem(item, $event, $index){
+ // do magic here
+ }
+
+ function selectItem(item, $event, $index) {
+ // set item.selected = true; to select the item
+ // do magic here
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {array} content (binding): Array of content items.
+@param {array=} contentProperties (binding): Array of content item properties to include in the item. If left empty the item will only show the item icon and name.
+@param {callback=} onClick (binding): Callback method to handle click events on the content item.
+ The callback returns:
+
+ item: The clicked item
+ $event: The select event
+ $index: The item index
+
+@param {callback=} onClickName (binding): Callback method to handle click events on the checkmark icon.
+ The callback returns:
+
+ item: The selected item
+ $event: The select event
+ $index: The item index
+
+**/
+
+(function() {
+ 'use strict';
+
+ function ContentGridDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.clickItem = function(item, $event, $index) {
+ if(scope.onClick) {
+ scope.onClick(item, $event, $index);
+ }
+ };
+
+ scope.clickItemName = function(item, $event, $index) {
+ if(scope.onClickName) {
+ scope.onClickName(item, $event, $index);
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-content-grid.html',
+ scope: {
+ content: '=',
+ contentProperties: "=",
+ onClick: "=",
+ onClickName: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbContentGrid', ContentGridDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function UmbDisableFormValidation() {
+
+ var directive = {
+ restrict: 'A',
+ require: '?form',
+ link: function (scope, elm, attrs, ctrl) {
+ //override the $setValidity function of the form to disable validation
+ ctrl.$setValidity = function () { };
+ }
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbDisableFormValidation', UmbDisableFormValidation);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbEmptyState
+@restrict E
+@scope
+
+@description
+Use this directive to show an empty state message.
+
+Markup example
+
+
+
+
+ // Empty state content
+
+
+
+
+
+@param {string=} size Set the size of the text ("small", "large").
+@param {string=} position Set the position of the text ("center").
+**/
+
+(function() {
+ 'use strict';
+
+ function EmptyStateDirective() {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ transclude: true,
+ templateUrl: 'views/components/umb-empty-state.html',
+ scope: {
+ size: '@',
+ position: '@'
+ }
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbEmptyState', EmptyStateDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbFolderGrid
+@restrict E
+@scope
+
+@description
+Use this directive to generate a list of folders presented as a flexbox grid.
+
+Markup example
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller(myService) {
+
+ var vm = this;
+ vm.folders = [
+ {
+ "name": "Folder 1",
+ "icon": "icon-folder",
+ "selected": false
+ },
+ {
+ "name": "Folder 2",
+ "icon": "icon-folder",
+ "selected": false
+ }
+
+ ];
+
+ vm.clickFolder = clickFolder;
+ vm.selectFolder = selectFolder;
+
+ myService.getFolders().then(function(folders){
+ vm.folders = folders;
+ });
+
+ function clickFolder(folder){
+ // Execute when clicking folder name/link
+ }
+
+ function selectFolder(folder, event, index) {
+ // Execute when clicking folder
+ // set folder.selected = true; to show checkmark icon
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {array} folders (binding): Array of folders
+@param {callback=} onClick (binding): Callback method to handle click events on the folder.
+ The callback returns:
+
+ folder: The selected folder
+
+@param {callback=} onSelect (binding): Callback method to handle click events on the checkmark icon.
+ The callback returns:
+
+ folder: The selected folder
+ $event: The select event
+ $index: The folder index
+
+**/
+
+(function() {
+ 'use strict';
+
+ function FolderGridDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.clickFolder = function(folder, $event, $index) {
+ if(scope.onClick) {
+ scope.onClick(folder, $event, $index);
+ }
+ };
+
+ scope.clickFolderName = function(folder, $event, $index) {
+ if(scope.onClickName) {
+ scope.onClickName(folder, $event, $index);
+ $event.stopPropagation();
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-folder-grid.html',
+ scope: {
+ folders: '=',
+ onClick: "=",
+ onClickName: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbFolderGrid', FolderGridDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function GridSelector() {
+
+ function link(scope, el, attr, ctrl) {
+
+ var eventBindings = [];
+ scope.dialogModel = {};
+ scope.showDialog = false;
+ scope.itemLabel = "";
+
+ // set default item name
+ if(!scope.itemName){
+ scope.itemLabel = "item";
+ } else {
+ scope.itemLabel = scope.itemName;
+ }
+
+ scope.removeItem = function(selectedItem) {
+ var selectedItemIndex = scope.selectedItems.indexOf(selectedItem);
+ scope.selectedItems.splice(selectedItemIndex, 1);
+ };
+
+ scope.removeDefaultItem = function() {
+
+ // it will be the last item so we can clear the array
+ scope.selectedItems = [];
+
+ // remove as default item
+ scope.defaultItem = null;
+
+ };
+
+ scope.openItemPicker = function($event){
+ scope.dialogModel = {
+ view: "itempicker",
+ title: "Choose " + scope.itemLabel,
+ availableItems: scope.availableItems,
+ selectedItems: scope.selectedItems,
+ event: $event,
+ show: true,
+ submit: function(model) {
+ scope.selectedItems.push(model.selectedItem);
+
+ // if no default item - set item as default
+ if(scope.defaultItem === null) {
+ scope.setAsDefaultItem(model.selectedItem);
+ }
+
+ scope.dialogModel.show = false;
+ scope.dialogModel = null;
+ }
+ };
+ };
+
+ scope.setAsDefaultItem = function(selectedItem) {
+
+ // clear default item
+ scope.defaultItem = {};
+
+ // set as default item
+ scope.defaultItem = selectedItem;
+ };
+
+ function updatePlaceholders() {
+
+ // update default item
+ if(scope.defaultItem !== null && scope.defaultItem.placeholder) {
+
+ scope.defaultItem.name = scope.name;
+
+ if(scope.alias !== null && scope.alias !== undefined) {
+ scope.defaultItem.alias = scope.alias;
+ }
+
+ }
+
+ // update selected items
+ angular.forEach(scope.selectedItems, function(selectedItem) {
+ if(selectedItem.placeholder) {
+
+ selectedItem.name = scope.name;
+
+ if(scope.alias !== null && scope.alias !== undefined) {
+ selectedItem.alias = scope.alias;
+ }
+
+ }
+ });
+
+ // update availableItems
+ angular.forEach(scope.availableItems, function(availableItem) {
+ if(availableItem.placeholder) {
+
+ availableItem.name = scope.name;
+
+ if(scope.alias !== null && scope.alias !== undefined) {
+ availableItem.alias = scope.alias;
+ }
+
+ }
+ });
+
+ }
+
+ function activate() {
+
+ // add watchers for updating placeholde name and alias
+ if(scope.updatePlaceholder) {
+ eventBindings.push(scope.$watch('name', function(newValue, oldValue){
+ updatePlaceholders();
+ }));
+
+ eventBindings.push(scope.$watch('alias', function(newValue, oldValue){
+ updatePlaceholders();
+ }));
+ }
+
+ }
+
+ activate();
+
+ // clean up
+ scope.$on('$destroy', function(){
+
+ // clear watchers
+ for(var e in eventBindings) {
+ eventBindings[e]();
+ }
+
+ });
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-grid-selector.html',
+ scope: {
+ name: "=",
+ alias: "=",
+ selectedItems: '=',
+ availableItems: "=",
+ defaultItem: "=",
+ itemName: "@",
+ updatePlaceholder: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbGridSelector', GridSelector);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService) {
+
+ function link(scope, el, attr, ctrl) {
+
+ var validationTranslated = "";
+ var tabNoSortOrderTranslated = "";
+
+ scope.sortingMode = false;
+ scope.toolbar = [];
+ scope.sortableOptionsGroup = {};
+ scope.sortableOptionsProperty = {};
+ scope.sortingButtonKey = "general_reorder";
+
+ function activate() {
+
+ setSortingOptions();
+
+ // set placeholder property on each group
+ if (scope.model.groups.length !== 0) {
+ angular.forEach(scope.model.groups, function(group) {
+ addInitProperty(group);
+ });
+ }
+
+ // add init tab
+ addInitGroup(scope.model.groups);
+
+ activateFirstGroup(scope.model.groups);
+
+ // localize texts
+ localizationService.localize("validation_validation").then(function(value) {
+ validationTranslated = value;
+ });
+
+ localizationService.localize("contentTypeEditor_tabHasNoSortOrder").then(function(value) {
+ tabNoSortOrderTranslated = value;
+ });
+ }
+
+ function setSortingOptions() {
+
+ scope.sortableOptionsGroup = {
+ distance: 10,
+ tolerance: "pointer",
+ opacity: 0.7,
+ scroll: true,
+ cursor: "move",
+ placeholder: "umb-group-builder__group-sortable-placeholder",
+ zIndex: 6000,
+ handle: ".umb-group-builder__group-handle",
+ items: ".umb-group-builder__group-sortable",
+ start: function(e, ui) {
+ ui.placeholder.height(ui.item.height());
+ },
+ stop: function(e, ui) {
+ updateTabsSortOrder();
+ },
+ };
+
+ scope.sortableOptionsProperty = {
+ distance: 10,
+ tolerance: "pointer",
+ connectWith: ".umb-group-builder__properties",
+ opacity: 0.7,
+ scroll: true,
+ cursor: "move",
+ placeholder: "umb-group-builder__property_sortable-placeholder",
+ zIndex: 6000,
+ handle: ".umb-group-builder__property-handle",
+ items: ".umb-group-builder__property-sortable",
+ start: function(e, ui) {
+ ui.placeholder.height(ui.item.height());
+ },
+ stop: function(e, ui) {
+ updatePropertiesSortOrder();
+ }
+ };
+
+ }
+
+ function updateTabsSortOrder() {
+
+ var first = true;
+ var prevSortOrder = 0;
+
+ scope.model.groups.map(function(group){
+
+ var index = scope.model.groups.indexOf(group);
+
+ if(group.tabState !== "init") {
+
+ // set the first not inherited tab to sort order 0
+ if(!group.inherited && first) {
+
+ // set the first tab sort order to 0 if prev is 0
+ if( prevSortOrder === 0 ) {
+ group.sortOrder = 0;
+ // when the first tab is inherited and sort order is not 0
+ } else {
+ group.sortOrder = prevSortOrder + 1;
+ }
+
+ first = false;
+
+ } else if(!group.inherited && !first) {
+
+ // find next group
+ var nextGroup = scope.model.groups[index + 1];
+
+ // if a groups is dropped in the middle of to groups with
+ // same sort order. Give it the dropped group same sort order
+ if( prevSortOrder === nextGroup.sortOrder ) {
+ group.sortOrder = prevSortOrder;
+ } else {
+ group.sortOrder = prevSortOrder + 1;
+ }
+
+ }
+
+ // store this tabs sort order as reference for the next
+ prevSortOrder = group.sortOrder;
+
+ }
+
+ });
+
+ }
+
+ function filterAvailableCompositions(selectedContentType, selecting) {
+
+ //selecting = true if the user has check the item, false if the user has unchecked the item
+
+ var selectedContentTypeAliases = selecting ?
+ //the user has selected the item so add to the current list
+ _.union(scope.compositionsDialogModel.compositeContentTypes, [selectedContentType.alias]) :
+ //the user has unselected the item so remove from the current list
+ _.reject(scope.compositionsDialogModel.compositeContentTypes, function(i) {
+ return i === selectedContentType.alias;
+ });
+
+ //get the currently assigned property type aliases - ensure we pass these to the server side filer
+ var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
+ return _.map(g.properties, function(p) {
+ return p.alias;
+ });
+ })), function (f) {
+ return f !== null && f !== undefined;
+ });
+
+ //use a different resource lookup depending on the content type type
+ var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
+
+ return resourceLookup(scope.model.id, selectedContentTypeAliases, propAliasesExisting).then(function (filteredAvailableCompositeTypes) {
+ _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (current) {
+ //reset first
+ current.allowed = true;
+ //see if this list item is found in the response (allowed) list
+ var found = _.find(filteredAvailableCompositeTypes, function (f) {
+ return current.contentType.alias === f.contentType.alias;
+ });
+
+ //allow if the item was found in the response (allowed) list -
+ // and ensure its set to allowed if it is currently checked,
+ // DO not allow if it's a locked content type.
+ current.allowed = scope.model.lockedCompositeContentTypes.indexOf(current.contentType.alias) === -1 &&
+ (selectedContentTypeAliases.indexOf(current.contentType.alias) !== -1) || ((found !== null && found !== undefined) ? found.allowed : false);
+
+ });
+ });
+ }
+
+ function updatePropertiesSortOrder() {
+
+ angular.forEach(scope.model.groups, function(group){
+ if( group.tabState !== "init" ) {
+ group.properties = contentTypeHelper.updatePropertiesSortOrder(group.properties);
+ }
+ });
+
+ }
+
+ function setupAvailableContentTypesModel(result) {
+ scope.compositionsDialogModel.availableCompositeContentTypes = result;
+ //iterate each one and set it up
+ _.each(scope.compositionsDialogModel.availableCompositeContentTypes, function (c) {
+ //enable it if it's part of the selected model
+ if (scope.compositionsDialogModel.compositeContentTypes.indexOf(c.contentType.alias) !== -1) {
+ c.allowed = true;
+ }
+
+ //set the inherited flags
+ c.inherited = false;
+ if (scope.model.lockedCompositeContentTypes.indexOf(c.contentType.alias) > -1) {
+ c.inherited = true;
+ }
+ // convert icons for composite content types
+ iconHelper.formatContentTypeIcons([c.contentType]);
+ });
+ }
+
+ /* ---------- DELETE PROMT ---------- */
+
+ scope.togglePrompt = function (object) {
+ object.deletePrompt = !object.deletePrompt;
+ };
+
+ scope.hidePrompt = function (object) {
+ object.deletePrompt = false;
+ };
+
+ /* ---------- TOOLBAR ---------- */
+
+ scope.toggleSortingMode = function(tool) {
+
+ if (scope.sortingMode === true) {
+
+ var sortOrderMissing = false;
+
+ for (var i = 0; i < scope.model.groups.length; i++) {
+ var group = scope.model.groups[i];
+ if (group.tabState !== "init" && group.sortOrder === undefined) {
+ sortOrderMissing = true;
+ group.showSortOrderMissing = true;
+ notificationsService.error(validationTranslated + ": " + group.name + " " + tabNoSortOrderTranslated);
+ }
+ }
+
+ if (!sortOrderMissing) {
+ scope.sortingMode = false;
+ scope.sortingButtonKey = "general_reorder";
+ }
+
+ } else {
+
+ scope.sortingMode = true;
+ scope.sortingButtonKey = "general_reorderDone";
+
+ }
+
+ };
+
+ scope.openCompositionsDialog = function() {
+
+ scope.compositionsDialogModel = {
+ title: "Compositions",
+ contentType: scope.model,
+ compositeContentTypes: scope.model.compositeContentTypes,
+ view: "views/common/overlays/contenttypeeditor/compositions/compositions.html",
+ confirmSubmit: {
+ title: "Warning",
+ description: "Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?",
+ checkboxLabel: "I know what I'm doing",
+ enable: true
+ },
+ submit: function(model, oldModel, confirmed) {
+
+ var compositionRemoved = false;
+
+ // check if any compositions has been removed
+ for(var i = 0; oldModel.compositeContentTypes.length > i; i++) {
+
+ var oldComposition = oldModel.compositeContentTypes[i];
+
+ if(_.contains(model.compositeContentTypes, oldComposition) === false) {
+ compositionRemoved = true;
+ }
+
+ }
+
+ // show overlay confirm box if compositions has been removed.
+ if(compositionRemoved && confirmed === false) {
+
+ scope.compositionsDialogModel.confirmSubmit.show = true;
+
+ // submit overlay if no compositions has been removed
+ // or the action has been confirmed
+ } else {
+
+ // make sure that all tabs has an init property
+ if (scope.model.groups.length !== 0) {
+ angular.forEach(scope.model.groups, function(group) {
+ addInitProperty(group);
+ });
+ }
+
+ // remove overlay
+ scope.compositionsDialogModel.show = false;
+ scope.compositionsDialogModel = null;
+ }
+
+ },
+ close: function(oldModel) {
+
+ // reset composition changes
+ scope.model.groups = oldModel.contentType.groups;
+ scope.model.compositeContentTypes = oldModel.contentType.compositeContentTypes;
+
+ // remove overlay
+ scope.compositionsDialogModel.show = false;
+ scope.compositionsDialogModel = null;
+
+ },
+ selectCompositeContentType: function (selectedContentType) {
+
+ //first check if this is a new selection - we need to store this value here before any further digests/async
+ // because after that the scope.model.compositeContentTypes will be populated with the selected value.
+ var newSelection = scope.model.compositeContentTypes.indexOf(selectedContentType.alias) === -1;
+
+ if (newSelection) {
+ //merge composition with content type
+
+ //use a different resource lookup depending on the content type type
+ var resourceLookup = scope.contentType === "documentType" ? contentTypeResource.getById : mediaTypeResource.getById;
+
+ resourceLookup(selectedContentType.id).then(function (composition) {
+ //based on the above filtering we shouldn't be able to select an invalid one, but let's be safe and
+ // double check here.
+ var overlappingAliases = contentTypeHelper.validateAddingComposition(scope.model, composition);
+ if (overlappingAliases.length > 0) {
+ //this will create an invalid composition, need to uncheck it
+ scope.compositionsDialogModel.compositeContentTypes.splice(
+ scope.compositionsDialogModel.compositeContentTypes.indexOf(composition.alias), 1);
+ //dissallow this until something else is unchecked
+ selectedContentType.allowed = false;
+ }
+ else {
+ contentTypeHelper.mergeCompositeContentType(scope.model, composition);
+ }
+
+ //based on the selection, we need to filter the available composite types list
+ filterAvailableCompositions(selectedContentType, newSelection).then(function () {
+ //TODO: Here we could probably re-enable selection if we previously showed a throbber or something
+ });
+ });
+ }
+ else {
+ // split composition from content type
+ contentTypeHelper.splitCompositeContentType(scope.model, selectedContentType);
+
+ //based on the selection, we need to filter the available composite types list
+ filterAvailableCompositions(selectedContentType, newSelection).then(function () {
+ //TODO: Here we could probably re-enable selection if we previously showed a throbber or something
+ });
+ }
+
+ }
+ };
+
+ var availableContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getAvailableCompositeContentTypes : mediaTypeResource.getAvailableCompositeContentTypes;
+ var countContentTypeResource = scope.contentType === "documentType" ? contentTypeResource.getCount : mediaTypeResource.getCount;
+
+ //get the currently assigned property type aliases - ensure we pass these to the server side filer
+ var propAliasesExisting = _.filter(_.flatten(_.map(scope.model.groups, function(g) {
+ return _.map(g.properties, function(p) {
+ return p.alias;
+ });
+ })), function(f) {
+ return f !== null && f !== undefined;
+ });
+ $q.all([
+ //get available composite types
+ availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) {
+ setupAvailableContentTypesModel(result);
+ }),
+ //get content type count
+ countContentTypeResource().then(function(result) {
+ scope.compositionsDialogModel.totalContentTypes = parseInt(result, 10);
+ })
+ ]).then(function() {
+ //resolves when both other promises are done, now show it
+ scope.compositionsDialogModel.show = true;
+ });
+
+ };
+
+
+ /* ---------- GROUPS ---------- */
+
+ scope.addGroup = function(group) {
+
+ // set group sort order
+ var index = scope.model.groups.indexOf(group);
+ var prevGroup = scope.model.groups[index - 1];
+
+ if( index > 0) {
+ // set index to 1 higher than the previous groups sort order
+ group.sortOrder = prevGroup.sortOrder + 1;
+
+ } else {
+ // first group - sort order will be 0
+ group.sortOrder = 0;
+ }
+
+ // activate group
+ scope.activateGroup(group);
+
+ };
+
+ scope.activateGroup = function(selectedGroup) {
+
+ // set all other groups that are inactive to active
+ angular.forEach(scope.model.groups, function(group) {
+ // skip init tab
+ if (group.tabState !== "init") {
+ group.tabState = "inActive";
+ }
+ });
+
+ selectedGroup.tabState = "active";
+
+ };
+
+ scope.removeGroup = function(groupIndex) {
+ scope.model.groups.splice(groupIndex, 1);
+ addInitGroup(scope.model.groups);
+ };
+
+ scope.updateGroupTitle = function(group) {
+ if (group.properties.length === 0) {
+ addInitProperty(group);
+ }
+ };
+
+ scope.changeSortOrderValue = function(group) {
+
+ if (group.sortOrder !== undefined) {
+ group.showSortOrderMissing = false;
+ }
+ scope.model.groups = $filter('orderBy')(scope.model.groups, 'sortOrder');
+ };
+
+ function addInitGroup(groups) {
+
+ // check i init tab already exists
+ var addGroup = true;
+
+ angular.forEach(groups, function(group) {
+ if (group.tabState === "init") {
+ addGroup = false;
+ }
+ });
+
+ if (addGroup) {
+ groups.push({
+ properties: [],
+ parentTabContentTypes: [],
+ parentTabContentTypeNames: [],
+ name: "",
+ tabState: "init"
+ });
+ }
+
+ return groups;
+ }
+
+ function activateFirstGroup(groups) {
+ if (groups && groups.length > 0) {
+ var firstGroup = groups[0];
+ if(!firstGroup.tabState || firstGroup.tabState === "inActive") {
+ firstGroup.tabState = "active";
+ }
+ }
+ }
+
+ /* ---------- PROPERTIES ---------- */
+
+ scope.addProperty = function(property, group) {
+
+ // set property sort order
+ var index = group.properties.indexOf(property);
+ var prevProperty = group.properties[index - 1];
+
+ if( index > 0) {
+ // set index to 1 higher than the previous property sort order
+ property.sortOrder = prevProperty.sortOrder + 1;
+
+ } else {
+ // first property - sort order will be 0
+ property.sortOrder = 0;
+ }
+
+ // open property settings dialog
+ scope.editPropertyTypeSettings(property, group);
+
+ };
+
+ scope.editPropertyTypeSettings = function(property, group) {
+
+ if (!property.inherited && !property.locked) {
+
+ scope.propertySettingsDialogModel = {};
+ scope.propertySettingsDialogModel.title = "Property settings";
+ scope.propertySettingsDialogModel.property = property;
+ scope.propertySettingsDialogModel.contentType = scope.contentType;
+ scope.propertySettingsDialogModel.contentTypeName = scope.model.name;
+ scope.propertySettingsDialogModel.view = "views/common/overlays/contenttypeeditor/propertysettings/propertysettings.html";
+ scope.propertySettingsDialogModel.show = true;
+
+ // set state to active to access the preview
+ property.propertyState = "active";
+
+ // set property states
+ property.dialogIsOpen = true;
+
+ scope.propertySettingsDialogModel.submit = function(model) {
+
+ property.inherited = false;
+ property.dialogIsOpen = false;
+
+ // update existing data types
+ if(model.updateSameDataTypes) {
+ updateSameDataTypes(property);
+ }
+
+ // remove dialog
+ scope.propertySettingsDialogModel.show = false;
+ scope.propertySettingsDialogModel = null;
+
+ // push new init property to group
+ addInitProperty(group);
+
+ // set focus on init property
+ var numberOfProperties = group.properties.length;
+ group.properties[numberOfProperties - 1].focus = true;
+
+ // push new init tab to the scope
+ addInitGroup(scope.model.groups);
+
+ };
+
+ scope.propertySettingsDialogModel.close = function(oldModel) {
+
+ // reset all property changes
+ property.label = oldModel.property.label;
+ property.alias = oldModel.property.alias;
+ property.description = oldModel.property.description;
+ property.config = oldModel.property.config;
+ property.editor = oldModel.property.editor;
+ property.view = oldModel.property.view;
+ property.dataTypeId = oldModel.property.dataTypeId;
+ property.dataTypeIcon = oldModel.property.dataTypeIcon;
+ property.dataTypeName = oldModel.property.dataTypeName;
+ property.validation.mandatory = oldModel.property.validation.mandatory;
+ property.validation.pattern = oldModel.property.validation.pattern;
+ property.showOnMemberProfile = oldModel.property.showOnMemberProfile;
+ property.memberCanEdit = oldModel.property.memberCanEdit;
+
+ // because we set state to active, to show a preview, we have to check if has been filled out
+ // label is required so if it is not filled we know it is a placeholder
+ if(oldModel.property.editor === undefined || oldModel.property.editor === null || oldModel.property.editor === "") {
+ property.propertyState = "init";
+ } else {
+ property.propertyState = oldModel.property.propertyState;
+ }
+
+ // remove dialog
+ scope.propertySettingsDialogModel.show = false;
+ scope.propertySettingsDialogModel = null;
+
+ };
+
+ }
+ };
+
+ scope.deleteProperty = function(tab, propertyIndex) {
+
+ // remove property
+ tab.properties.splice(propertyIndex, 1);
+
+ // if the last property in group is an placeholder - remove add new tab placeholder
+ if(tab.properties.length === 1 && tab.properties[0].propertyState === "init") {
+
+ angular.forEach(scope.model.groups, function(group, index, groups){
+ if(group.tabState === 'init') {
+ groups.splice(index, 1);
+ }
+ });
+
+ }
+
+ };
+
+ function addInitProperty(group) {
+
+ var addInitPropertyBool = true;
+ var initProperty = {
+ label: null,
+ alias: null,
+ propertyState: "init",
+ validation: {
+ mandatory: false,
+ pattern: null
+ }
+ };
+
+ // check if there already is an init property
+ angular.forEach(group.properties, function(property) {
+ if (property.propertyState === "init") {
+ addInitPropertyBool = false;
+ }
+ });
+
+ if (addInitPropertyBool) {
+ group.properties.push(initProperty);
+ }
+
+ return group;
+ }
+
+ function updateSameDataTypes(newProperty) {
+
+ // find each property
+ angular.forEach(scope.model.groups, function(group){
+ angular.forEach(group.properties, function(property){
+
+ if(property.dataTypeId === newProperty.dataTypeId) {
+
+ // update property data
+ property.config = newProperty.config;
+ property.editor = newProperty.editor;
+ property.view = newProperty.view;
+ property.dataTypeId = newProperty.dataTypeId;
+ property.dataTypeIcon = newProperty.dataTypeIcon;
+ property.dataTypeName = newProperty.dataTypeName;
+
+ }
+
+ });
+ });
+ }
+
+
+ var unbindModelWatcher = scope.$watch('model', function(newValue, oldValue) {
+ if (newValue !== undefined && newValue.groups !== undefined) {
+ activate();
+ }
+ });
+
+ // clean up
+ scope.$on('$destroy', function(){
+ unbindModelWatcher();
+ });
+
+ }
+
+ var directive = {
+ restrict: "E",
+ replace: true,
+ templateUrl: "views/components/umb-groups-builder.html",
+ scope: {
+ model: "=",
+ compositions: "=",
+ sorting: "=",
+ contentType: "@"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbGroupsBuilder', GroupsBuilderDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbkeyboardShortcutsOverview
+@restrict E
+@scope
+
+@description
+
+Use this directive to show an overview of keyboard shortcuts in an editor.
+The directive will render an overview trigger wich shows how the overview is opened.
+When this combination is hit an overview is opened with shortcuts based on the model sent to the directive.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.keyboardShortcutsOverview = [
+ {
+ "name": "Sections",
+ "shortcuts": [
+ {
+ "description": "Navigate sections",
+ "keys": [
+ {"key": "1"},
+ {"key": "4"}
+ ],
+ "keyRange": true
+ }
+ ]
+ },
+ {
+ "name": "Design",
+ "shortcuts": [
+ {
+ "description": "Add tab",
+ "keys": [
+ {"key": "alt"},
+ {"key": "shift"},
+ {"key": "t"}
+ ]
+ }
+ ]
+ }
+ ];
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+Model description
+
+ -
+ name
+ (string) -
+ Sets the shortcut section name.
+
+ -
+ shortcuts
+ (array) -
+ Array of available shortcuts in the section.
+
+
+ -
+ description
+ (string) -
+ Short description of the shortcut.
+
+ -
+ keys
+ (array) -
+ Array of keys in the shortcut.
+
+
+ -
+ key
+ (string) -
+ The invidual key in the shortcut.
+
+
+ -
+ keyRange
+ (boolean) -
+ Set to
true to show a key range. It combines the shortcut keys with "-" instead of "+".
+
+
+
+
+@param {object} model keyboard shortcut model. See description and example above.
+**/
+
+(function() {
+ 'use strict';
+
+ function KeyboardShortcutsOverviewDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.shortcutOverlay = false;
+
+ scope.toggleShortcutsOverlay = function() {
+ scope.shortcutOverlay = !scope.shortcutOverlay;
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-keyboard-shortcuts-overview.html',
+ link: link,
+ scope: {
+ model: "="
+ }
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbKeyboardShortcutsOverview', KeyboardShortcutsOverviewDirective);
+
+})();
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbLaunchMiniEditor
+* @restrict E
+* @function
+* @description
+* Used on a button to launch a mini content editor editor dialog
+**/
+angular.module("umbraco.directives")
+ .directive('umbLaunchMiniEditor', function (dialogService, editorState, fileManager, contentEditingHelper) {
+ return {
+ restrict: 'A',
+ replace: false,
+ scope: {
+ node: '=umbLaunchMiniEditor',
+ },
+ link: function(scope, element, attrs) {
+
+ var launched = false;
+
+ element.click(function() {
+
+ if (launched === true) {
+ return;
+ }
+
+ launched = true;
+
+ //We need to store the current files selected in the file manager locally because the fileManager
+ // is a singleton and is shared globally. The mini dialog will also be referencing the fileManager
+ // and we don't want it to be sharing the same files as the main editor. So we'll store the current files locally here,
+ // clear them out and then launch the dialog. When the dialog closes, we'll reset the fileManager to it's previous state.
+ var currFiles = _.groupBy(fileManager.getFiles(), "alias");
+ fileManager.clearFiles();
+
+ //We need to store the original editorState entity because it will need to change when the mini editor is loaded so that
+ // any property editors that are working with editorState get given the correct entity, otherwise strange things will
+ // start happening.
+ var currEditorState = editorState.getCurrent();
+
+ dialogService.open({
+ template: "views/common/dialogs/content/edit.html",
+ id: scope.node.id,
+ closeOnSave: true,
+ tabFilter: ["Generic properties"],
+ callback: function (data) {
+
+ //set the node name back
+ scope.node.name = data.name;
+
+ //reset the fileManager to what it was
+ fileManager.clearFiles();
+ _.each(currFiles, function (val, key) {
+ fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
+ });
+
+ //reset the editor state
+ editorState.set(currEditorState);
+
+ //Now we need to check if the content item that was edited was actually the same content item
+ // as the main content editor and if so, update all property data
+ if (data.id === currEditorState.id) {
+ var changed = contentEditingHelper.reBindChangedProperties(currEditorState, data);
+ }
+
+ launched = false;
+ },
+ closeCallback: function () {
+ //reset the fileManager to what it was
+ fileManager.clearFiles();
+ _.each(currFiles, function (val, key) {
+ fileManager.setFiles(key, _.map(currFiles['upload'], function (i) { return i.file; }));
+ });
+
+ //reset the editor state
+ editorState.set(currEditorState);
+
+ launched = false;
+ }
+ });
+
+ });
+
+ }
+ };
+ });
+(function() {
+ 'use strict';
+
+ function LayoutSelectorDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.layoutDropDownIsOpen = false;
+ scope.showLayoutSelector = true;
+
+ function activate() {
+
+ setVisibility();
+
+ setActiveLayout(scope.layouts);
+
+ }
+
+ function setVisibility() {
+
+ var numberOfAllowedLayouts = getNumberOfAllowedLayouts(scope.layouts);
+
+ if(numberOfAllowedLayouts === 1) {
+ scope.showLayoutSelector = false;
+ }
+
+ }
+
+ function getNumberOfAllowedLayouts(layouts) {
+
+ var allowedLayouts = 0;
+
+ for (var i = 0; layouts.length > i; i++) {
+
+ var layout = layouts[i];
+
+ if(layout.selected === true) {
+ allowedLayouts++;
+ }
+
+ }
+
+ return allowedLayouts;
+ }
+
+ function setActiveLayout(layouts) {
+
+ for (var i = 0; layouts.length > i; i++) {
+ var layout = layouts[i];
+ if(layout.path === scope.activeLayout.path) {
+ layout.active = true;
+ }
+ }
+
+ }
+
+ scope.pickLayout = function(selectedLayout) {
+ if(scope.onLayoutSelect) {
+ scope.onLayoutSelect(selectedLayout);
+ scope.layoutDropDownIsOpen = false;
+ }
+ };
+
+ scope.toggleLayoutDropdown = function() {
+ scope.layoutDropDownIsOpen = !scope.layoutDropDownIsOpen;
+ };
+
+ scope.closeLayoutDropdown = function() {
+ scope.layoutDropDownIsOpen = false;
+ };
+
+ activate();
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-layout-selector.html',
+ scope: {
+ layouts: '=',
+ activeLayout: '=',
+ onLayoutSelect: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbLayoutSelector', LayoutSelectorDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function ListViewLayoutDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.getContent = function(contentId) {
+ if(scope.onGetContent) {
+ scope.onGetContent(contentId);
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-list-view-layout.html',
+ scope: {
+ contentId: '=',
+ folders: '=',
+ items: '=',
+ selection: '=',
+ options: '=',
+ entityType: '@',
+ onGetContent: '='
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbListViewLayout', ListViewLayoutDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function ListViewSettingsDirective(contentTypeResource, dataTypeResource, dataTypeHelper) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.dataType = {};
+ scope.editDataTypeSettings = false;
+ scope.customListViewCreated = false;
+
+ /* ---------- INIT ---------- */
+
+ function activate() {
+
+ if(scope.enableListView) {
+
+ dataTypeResource.getByName(scope.listViewName)
+ .then(function(dataType) {
+
+ scope.dataType = dataType;
+
+ scope.customListViewCreated = checkForCustomListView();
+
+ });
+
+ } else {
+
+ scope.dataType = {};
+
+ }
+
+ }
+
+ /* ----------- LIST VIEW SETTINGS --------- */
+
+ scope.toggleEditListViewDataTypeSettings = function() {
+ scope.editDataTypeSettings = !scope.editDataTypeSettings;
+ };
+
+ scope.saveListViewDataType = function() {
+
+ var preValues = dataTypeHelper.createPreValueProps(scope.dataType.preValues);
+
+ dataTypeResource.save(scope.dataType, preValues, false).then(function(dataType) {
+
+ // store data type
+ scope.dataType = dataType;
+
+ // hide settings panel
+ scope.editDataTypeSettings = false;
+
+ });
+
+ };
+
+
+ /* ---------- CUSTOM LIST VIEW ---------- */
+
+ scope.createCustomListViewDataType = function() {
+
+ dataTypeResource.createCustomListView(scope.modelAlias).then(function(dataType) {
+
+ // store data type
+ scope.dataType = dataType;
+
+ // set list view name on scope
+ scope.listViewName = dataType.name;
+
+ // change state to custom list view
+ scope.customListViewCreated = true;
+
+ // show settings panel
+ scope.editDataTypeSettings = true;
+
+ });
+
+ };
+
+ scope.removeCustomListDataType = function() {
+
+ scope.editDataTypeSettings = false;
+
+ // delete custom list view data type
+ dataTypeResource.deleteById(scope.dataType.id).then(function(dataType) {
+
+ // set list view name on scope
+ if(scope.contentType === "documentType") {
+
+ scope.listViewName = "List View - Content";
+
+ } else if(scope.contentType === "mediaType") {
+
+ scope.listViewName = "List View - Media";
+
+ }
+
+ // get default data type
+ dataTypeResource.getByName(scope.listViewName)
+ .then(function(dataType) {
+
+ // store data type
+ scope.dataType = dataType;
+
+ // change state to default list view
+ scope.customListViewCreated = false;
+
+ });
+ });
+
+ };
+
+ /* ----------- SCOPE WATCHERS ----------- */
+ var unbindEnableListViewWatcher = scope.$watch('enableListView', function(newValue, oldValue){
+
+ if(newValue !== undefined) {
+ activate();
+ }
+
+ });
+
+ // clean up
+ scope.$on('$destroy', function(){
+ unbindEnableListViewWatcher();
+ });
+
+ /* ----------- METHODS ---------- */
+
+ function checkForCustomListView() {
+ return scope.dataType.name === "List View - " + scope.modelAlias;
+ }
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-list-view-settings.html',
+ scope: {
+ enableListView: "=",
+ listViewName: "=",
+ modelAlias: "=",
+ contentType: "@"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbListViewSettings', ListViewSettingsDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbLoadIndicator
+@restrict E
+
+@description
+Use this directive to generate a loading indicator.
+
+Markup example
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller(myService) {
+
+ var vm = this;
+
+ vm.content = "";
+ vm.loading = true;
+
+ myService.getContent().then(function(content){
+ vm.content = content;
+ vm.loading = false;
+ });
+
+ }
+½
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+**/
+
+(function() {
+ 'use strict';
+
+ function UmbLoadIndicatorDirective() {
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-load-indicator.html'
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbLoadIndicator', UmbLoadIndicatorDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbLockedField
+@restrict E
+@scope
+
+@description
+Use this directive to render a value with a lock next to it. When the lock is clicked the value gets unlocked and can be edited.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.value = "My locked text";
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+
+ })();
+
+
+@param {string} ngModel (binding): The locked text.
+@param {boolean=} locked (binding): true by default. Set to false to unlock the text.
+@param {string=} placeholderText (binding): If ngModel is empty this text will be shown.
+@param {string=} regexValidation (binding): Set a regex expression for validation of the field.
+@param {string=} serverValidationField (attribute): Set a server validation field.
+**/
+
+(function() {
+ 'use strict';
+
+ function LockedFieldDirective($timeout, localizationService) {
+
+ function link(scope, el, attr, ngModelCtrl) {
+
+ function activate() {
+
+ // if locked state is not defined as an attr set default state
+ if (scope.locked === undefined || scope.locked === null) {
+ scope.locked = true;
+ }
+
+ // if regex validation is not defined as an attr set default state
+ // if this is set to an empty string then regex validation can be ignored.
+ if (scope.regexValidation === undefined || scope.regexValidation === null) {
+ scope.regexValidation = "^[a-zA-Z]\\w.*$";
+ }
+
+ if (scope.serverValidationField === undefined || scope.serverValidationField === null) {
+ scope.serverValidationField = "";
+ }
+
+ // if locked state is not defined as an attr set default state
+ if (scope.placeholderText === undefined || scope.placeholderText === null) {
+ scope.placeholderText = "Enter value...";
+ }
+
+ }
+
+ scope.lock = function() {
+ scope.locked = true;
+ };
+
+ scope.unlock = function() {
+ scope.locked = false;
+ };
+
+ activate();
+
+ }
+
+ var directive = {
+ require: "ngModel",
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-locked-field.html',
+ scope: {
+ ngModel: "=",
+ locked: "=?",
+ placeholderText: "=?",
+ regexValidation: "=?",
+ serverValidationField: "@"
+ },
+ link: link
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbLockedField', LockedFieldDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbMediaGrid
+@restrict E
+@scope
+
+@description
+Use this directive to generate a thumbnail grid of media items.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.mediaItems = [];
+
+ vm.clickItem = clickItem;
+ vm.clickItemName = clickItemName;
+
+ myService.getMediaItems().then(function (mediaItems) {
+ vm.mediaItems = mediaItems;
+ });
+
+ function clickItem(item, $event, $index){
+ // do magic here
+ }
+
+ function clickItemName(item, $event, $index) {
+ // set item.selected = true; to select the item
+ // do magic here
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {array} items (binding): Array of media items.
+@param {callback=} onDetailsHover (binding): Callback method when the details icon is hovered.
+ The callback returns:
+
+ item: The hovered item
+ $event: The hover event
+ hover: Boolean to tell if the item is hovered or not
+
+@param {callback=} onClick (binding): Callback method to handle click events on the media item.
+ The callback returns:
+
+ item: The clicked item
+ $event: The click event
+ $index: The item index
+
+@param {callback=} onClickName (binding): Callback method to handle click events on the media item name.
+ The callback returns:
+
+ item: The clicked item
+ $event: The click event
+ $index: The item index
+
+@param {string=} filterBy (binding): String to filter media items by
+@param {string=} itemMaxWidth (attribute): Sets a max width on the media item thumbnails.
+@param {string=} itemMaxHeight (attribute): Sets a max height on the media item thumbnails.
+@param {string=} itemMinWidth (attribute): Sets a min width on the media item thumbnails.
+@param {string=} itemMinHeight (attribute): Sets a min height on the media item thumbnails.
+
+**/
+
+(function() {
+ 'use strict';
+
+ function MediaGridDirective($filter, mediaHelper) {
+
+ function link(scope, el, attr, ctrl) {
+
+ var itemDefaultHeight = 200;
+ var itemDefaultWidth = 200;
+ var itemMaxWidth = 200;
+ var itemMaxHeight = 200;
+ var itemMinWidth = 125;
+ var itemMinHeight = 125;
+
+ function activate() {
+
+ if (scope.itemMaxWidth) {
+ itemMaxWidth = scope.itemMaxWidth;
+ }
+
+ if (scope.itemMaxHeight) {
+ itemMaxHeight = scope.itemMaxHeight;
+ }
+
+ if (scope.itemMinWidth) {
+ itemMinWidth = scope.itemMinWidth;
+ }
+
+ if (scope.itemMinWidth) {
+ itemMinHeight = scope.itemMinHeight;
+ }
+
+ for (var i = 0; scope.items.length > i; i++) {
+ var item = scope.items[i];
+ setItemData(item);
+ setOriginalSize(item, itemMaxHeight);
+ }
+
+ if (scope.items.length > 0) {
+ setFlexValues(scope.items);
+ }
+
+ }
+
+ function setItemData(item) {
+ item.isFolder = !mediaHelper.hasFilePropertyType(item);
+ if (!item.isFolder) {
+ item.thumbnail = mediaHelper.resolveFile(item, true);
+ item.image = mediaHelper.resolveFile(item, false);
+ }
+ }
+
+ function setOriginalSize(item, maxHeight) {
+
+ //set to a square by default
+ item.width = itemDefaultWidth;
+ item.height = itemDefaultHeight;
+ item.aspectRatio = 1;
+
+ var widthProp = _.find(item.properties, function(v) {
+ return (v.alias === "umbracoWidth");
+ });
+
+ if (widthProp && widthProp.value) {
+ item.width = parseInt(widthProp.value, 10);
+ if (isNaN(item.width)) {
+ item.width = itemDefaultWidth;
+ }
+ }
+
+ var heightProp = _.find(item.properties, function(v) {
+ return (v.alias === "umbracoHeight");
+ });
+
+ if (heightProp && heightProp.value) {
+ item.height = parseInt(heightProp.value, 10);
+ if (isNaN(item.height)) {
+ item.height = itemDefaultWidth;
+ }
+ }
+
+ item.aspectRatio = item.width / item.height;
+
+ // set max width and height
+ // landscape
+ if (item.aspectRatio >= 1) {
+ if (item.width > itemMaxWidth) {
+ item.width = itemMaxWidth;
+ item.height = itemMaxWidth / item.aspectRatio;
+ }
+ // portrait
+ } else {
+ if (item.height > itemMaxHeight) {
+ item.height = itemMaxHeight;
+ item.width = itemMaxHeight * item.aspectRatio;
+ }
+ }
+
+ }
+
+ function setFlexValues(mediaItems) {
+
+ var flexSortArray = mediaItems;
+ var smallestImageWidth = null;
+ var widestImageAspectRatio = null;
+
+ // sort array after image width with the widest image first
+ flexSortArray = $filter('orderBy')(flexSortArray, 'width', true);
+
+ // find widest image aspect ratio
+ widestImageAspectRatio = flexSortArray[0].aspectRatio;
+
+ // find smallest image width
+ smallestImageWidth = flexSortArray[flexSortArray.length - 1].width;
+
+ for (var i = 0; flexSortArray.length > i; i++) {
+
+ var mediaItem = flexSortArray[i];
+ var flex = 1 / (widestImageAspectRatio / mediaItem.aspectRatio);
+
+ if (flex === 0) {
+ flex = 1;
+ }
+
+ var imageMinFlexWidth = smallestImageWidth * flex;
+
+ var flexStyle = {
+ "flex": flex + " 1 " + imageMinFlexWidth + "px",
+ "max-width": mediaItem.width + "px",
+ "min-width": itemMinWidth + "px",
+ "min-height": itemMinHeight + "px"
+ };
+
+ mediaItem.flexStyle = flexStyle;
+
+ }
+
+ }
+
+ scope.clickItem = function(item, $event, $index) {
+ if (scope.onClick) {
+ scope.onClick(item, $event, $index);
+ }
+ };
+
+ scope.clickItemName = function(item, $event, $index) {
+ if (scope.onClickName) {
+ scope.onClickName(item, $event, $index);
+ $event.stopPropagation();
+ }
+ };
+
+ scope.hoverItemDetails = function(item, $event, hover) {
+ if (scope.onDetailsHover) {
+ scope.onDetailsHover(item, $event, hover);
+ }
+ };
+
+ var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) {
+ if (angular.isArray(newValue)) {
+ activate();
+ }
+ });
+
+ scope.$on('$destroy', function() {
+ unbindItemsWatcher();
+ });
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-media-grid.html',
+ scope: {
+ items: '=',
+ onDetailsHover: "=",
+ onClick: '=',
+ onClickName: "=",
+ filterBy: "=",
+ itemMaxWidth: "@",
+ itemMaxHeight: "@",
+ itemMinWidth: "@",
+ itemMinHeight: "@"
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbMediaGrid', MediaGridDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbPagination
+@restrict E
+@scope
+
+@description
+Use this directive to generate a pagination.
+
+Markup example
+
+
+
+
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+
+ vm.pagination = {
+ pageNumber: 1,
+ totalPages: 10
+ }
+
+ vm.nextPage = nextPage;
+ vm.prevPage = prevPage;
+ vm.goToPage = goToPage;
+
+ function nextPage(pageNumber) {
+ // do magic here
+ console.log(pageNumber);
+ alert("nextpage");
+ }
+
+ function prevPage(pageNumber) {
+ // do magic here
+ console.log(pageNumber);
+ alert("prevpage");
+ }
+
+ function goToPage(pageNumber) {
+ // do magic here
+ console.log(pageNumber);
+ alert("go to");
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+ })();
+
+
+@param {number} pageNumber (binding): Current page number.
+@param {number} totalPages (binding): The total number of pages.
+@param {callback} onNext (binding): Callback method to go to the next page.
+ The callback returns:
+
+ pageNumber: The page number
+
+@param {callback=} onPrev (binding): Callback method to go to the previous page.
+ The callback returns:
+
+ pageNumber: The page number
+
+@param {callback=} onGoToPage (binding): Callback method to go to a specific page.
+ The callback returns:
+
+ pageNumber: The page number
+
+**/
+
+(function() {
+ 'use strict';
+
+ function PaginationDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ function activate() {
+
+ scope.pagination = [];
+
+ var i = 0;
+
+ if (scope.totalPages <= 10) {
+ for (i = 0; i < scope.totalPages; i++) {
+ scope.pagination.push({
+ val: (i + 1),
+ isActive: scope.pageNumber === (i + 1)
+ });
+ }
+ }
+ else {
+ //if there is more than 10 pages, we need to do some fancy bits
+
+ //get the max index to start
+ var maxIndex = scope.totalPages - 10;
+ //set the start, but it can't be below zero
+ var start = Math.max(scope.pageNumber - 5, 0);
+ //ensure that it's not too far either
+ start = Math.min(maxIndex, start);
+
+ for (i = start; i < (10 + start) ; i++) {
+ scope.pagination.push({
+ val: (i + 1),
+ isActive: scope.pageNumber === (i + 1)
+ });
+ }
+
+ //now, if the start is greater than 0 then '1' will not be displayed, so do the elipses thing
+ if (start > 0) {
+ scope.pagination.unshift({ name: "First", val: 1, isActive: false }, {val: "...",isActive: false});
+ }
+
+ //same for the end
+ if (start < maxIndex) {
+ scope.pagination.push({ val: "...", isActive: false }, { name: "Last", val: scope.totalPages, isActive: false });
+ }
+ }
+
+ }
+
+ scope.next = function() {
+ if (scope.onNext && scope.pageNumber < scope.totalPages) {
+ scope.pageNumber++;
+ scope.onNext(scope.pageNumber);
+ }
+ };
+
+ scope.prev = function(pageNumber) {
+ if (scope.onPrev && scope.pageNumber > 1) {
+ scope.pageNumber--;
+ scope.onPrev(scope.pageNumber);
+ }
+ };
+
+ scope.goToPage = function(pageNumber) {
+ if(scope.onGoToPage) {
+ scope.pageNumber = pageNumber + 1;
+ scope.onGoToPage(scope.pageNumber);
+ }
+ };
+
+ var unbindPageNumberWatcher = scope.$watch('pageNumber', function(newValue, oldValue){
+ activate();
+ });
+
+ scope.$on('$destroy', function(){
+ unbindPageNumberWatcher();
+ });
+
+ activate();
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-pagination.html',
+ scope: {
+ pageNumber: "=",
+ totalPages: "=",
+ onNext: "=",
+ onPrev: "=",
+ onGoToPage: "="
+ },
+ link: link
+ };
+
+ return directive;
+
+ }
+
+ angular.module('umbraco.directives').directive('umbPagination', PaginationDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbStickyBar
+@restrict A
+
+@description
+Use this directive make an element sticky and follow the page when scrolling.
+
+Markup example
+
+
+
+
+CSS example
+
+ .my-sticky-bar {
+ padding: 15px 0;
+ background: #000000;
+ position: relative;
+ top: 0;
+ }
+
+ .my-sticky-bar.-umb-sticky-bar {
+ top: 100px;
+ }
+
+
+@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
+**/
+
+(function() {
+ 'use strict';
+
+ function StickyBarDirective($rootScope) {
+
+ function link(scope, el, attr, ctrl) {
+
+ var bar = $(el);
+ var scrollableContainer = null;
+ var clonedBar = null;
+ var cloneIsMade = false;
+ var barTop = bar.context.offsetTop;
+
+ function activate() {
+
+ if (attr.scrollableContainer) {
+ scrollableContainer = $(attr.scrollableContainer);
+ } else {
+ scrollableContainer = $(window);
+ }
+
+ scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
+ $(window).on('resize.umbStickyBar', determineVisibility);
+
+ scope.$on('$destroy', function() {
+ scrollableContainer.off('.umbStickyBar');
+ $(window).off('.umbStickyBar');
+ });
+
+ }
+
+ function determineVisibility() {
+
+ var scrollTop = scrollableContainer.scrollTop();
+
+ if (scrollTop > barTop) {
+
+ if (!cloneIsMade) {
+
+ createClone();
+
+ clonedBar.css({
+ 'visibility': 'visible'
+ });
+
+ } else {
+
+ calculateSize();
+
+ }
+
+ } else {
+
+ if (cloneIsMade) {
+
+ //remove cloned element (switched places with original on creation)
+ bar.remove();
+ bar = clonedBar;
+ clonedBar = null;
+
+ bar.removeClass('-umb-sticky-bar');
+ bar.css({
+ position: 'relative',
+ 'width': 'auto',
+ 'height': 'auto',
+ 'z-index': 'auto',
+ 'visibility': 'visible'
+ });
+
+ cloneIsMade = false;
+
+ }
+
+ }
+
+ }
+
+ function calculateSize() {
+ clonedBar.css({
+ width: bar.outerWidth(),
+ height: bar.height()
+ });
+ }
+
+ function createClone() {
+ //switch place with cloned element, to keep binding intact
+ clonedBar = bar;
+ bar = clonedBar.clone();
+ clonedBar.after(bar);
+ clonedBar.addClass('-umb-sticky-bar');
+ clonedBar.css({
+ 'position': 'fixed',
+ 'z-index': 500,
+ 'visibility': 'hidden'
+ });
+
+ cloneIsMade = true;
+ calculateSize();
+
+ }
+
+ activate();
+
+ }
+
+ var directive = {
+ restrict: 'A',
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbStickyBar', StickyBarDirective);
+
+})();
+
+(function() {
+ 'use strict';
+
+ function TableDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.clickItem = function(item, $event) {
+ if(scope.onClick) {
+ scope.onClick(item);
+ $event.stopPropagation();
+ }
+ };
+
+ scope.selectItem = function(item, $index, $event) {
+ if(scope.onSelect) {
+ scope.onSelect(item, $index, $event);
+ $event.stopPropagation();
+ }
+ };
+
+ scope.selectAll = function($event) {
+ if(scope.onSelectAll) {
+ scope.onSelectAll($event);
+ }
+ };
+
+ scope.isSelectedAll = function() {
+ if(scope.onSelectedAll && scope.items && scope.items.length > 0) {
+ return scope.onSelectedAll();
+ }
+ };
+
+ scope.isSortDirection = function (col, direction) {
+ if (scope.onSortingDirection) {
+ return scope.onSortingDirection(col, direction);
+ }
+ };
+
+ scope.sort = function(field, allow) {
+ if(scope.onSort) {
+ scope.onSort(field, allow);
+ }
+ };
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'views/components/umb-table.html',
+ scope: {
+ items: '=',
+ itemProperties: '=',
+ allowSelectAll: '=',
+ onSelect: '=',
+ onClick: '=',
+ onSelectAll: '=',
+ onSelectedAll: '=',
+ onSortingDirection: '=',
+ onSort: '='
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbTable', TableDirective);
+
+})();
+
+/**
+@ngdoc directive
+@name umbraco.directives.directive:umbTooltip
+@restrict E
+@scope
+
+@description
+Use this directive to render a tooltip.
+
+Markup example
+
+
+
+
+ Hover me
+
+
+
+ // tooltip content here
+
+
+
+
+
+Controller example
+
+ (function () {
+ "use strict";
+
+ function Controller() {
+
+ var vm = this;
+ vm.tooltip = {
+ show: false,
+ event: null
+ };
+
+ vm.mouseOver = mouseOver;
+ vm.mouseLeave = mouseLeave;
+
+ function mouseOver($event) {
+ vm.tooltip = {
+ show: true,
+ event: $event
+ };
+ }
+
+ function mouseLeave() {
+ vm.tooltip = {
+ show: false,
+ event: null
+ };
+ }
+
+ }
+
+ angular.module("umbraco").controller("My.Controller", Controller);
+
+ })();
+
+
+@param {string} event Set the $event from the target element to position the tooltip relative to the mouse cursor.
+**/
+
+(function() {
+ 'use strict';
+
+ function TooltipDirective($timeout) {
+
+ function link(scope, el, attr, ctrl) {
+
+ scope.tooltipStyles = {};
+ scope.tooltipStyles.left = 0;
+ scope.tooltipStyles.top = 0;
+
+ function activate() {
+
+ $timeout(function() {
+ setTooltipPosition(scope.event);
+ });
+
+ }
+
+ function setTooltipPosition(event) {
+
+ var container = $("#contentwrapper");
+ var containerLeft = container[0].offsetLeft;
+ var containerRight = containerLeft + container[0].offsetWidth;
+ var containerTop = container[0].offsetTop;
+ var containerBottom = containerTop + container[0].offsetHeight;
+
+ var elementHeight = null;
+ var elementWidth = null;
+
+ var position = {
+ right: "inherit",
+ left: "inherit",
+ top: "inherit",
+ bottom: "inherit"
+ };
+
+ // element size
+ elementHeight = el.context.clientHeight;
+ elementWidth = el.context.clientWidth;
+
+ position.left = event.pageX - (elementWidth / 2);
+ position.top = event.pageY;
+
+ // check to see if element is outside screen
+ // outside right
+ if (position.left + elementWidth > containerRight) {
+ position.right = 10;
+ position.left = "inherit";
+ }
+
+ // outside bottom
+ if (position.top + elementHeight > containerBottom) {
+ position.bottom = 10;
+ position.top = "inherit";
+ }
+
+ // outside left
+ if (position.left < containerLeft) {
+ position.left = containerLeft + 10;
+ position.right = "inherit";
+ }
+
+ // outside top
+ if (position.top < containerTop) {
+ position.top = 10;
+ position.bottom = "inherit";
+ }
+
+ scope.tooltipStyles = position;
+
+ el.css(position);
+
+ }
+
+ activate();
+
+ }
+
+ var directive = {
+ restrict: 'E',
+ transclude: true,
+ replace: true,
+ templateUrl: 'views/components/umb-tooltip.html',
+ scope: {
+ event: "="
+ },
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbTooltip', TooltipDirective);
+
+})();
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbFileDropzone
+* @restrict E
+* @function
+* @description
+* Used by editors that require naming an entity. Shows a textbox/headline with a required validator within it's own form.
+**/
+
+/*
+TODO
+.directive("umbFileDrop", function ($timeout, $upload, localizationService, umbRequestHelper){
+
+ return{
+ restrict: "A",
+ link: function(scope, element, attrs){
+
+ //load in the options model
+
+
+ }
+ }
+})
+*/
+
+angular.module("umbraco.directives")
+
+.directive('umbFileDropzone', function ($timeout, Upload, localizationService, umbRequestHelper) {
+ return {
+
+ restrict: 'E',
+ replace: true,
+
+ templateUrl: 'views/components/upload/umb-file-dropzone.html',
+
+ scope: {
+ parentId: '@',
+ contentTypeAlias: '@',
+ propertyAlias: '@',
+ accept: '@',
+ maxFileSize: '@',
+
+ compact: '@',
+ hideDropzone: '@',
+
+ filesQueued: '=',
+ handleFile: '=',
+ filesUploaded: '='
+ },
+
+ link: function(scope, element, attrs) {
+
+ scope.queue = [];
+ scope.done = [];
+ scope.rejected = [];
+ scope.currentFile = undefined;
+
+ function _filterFile(file) {
+
+ var ignoreFileNames = ['Thumbs.db'];
+ var ignoreFileTypes = ['directory'];
+
+ // ignore files with names from the list
+ // ignore files with types from the list
+ // ignore files which starts with "."
+ if(ignoreFileNames.indexOf(file.name) === -1 &&
+ ignoreFileTypes.indexOf(file.type) === -1 &&
+ file.name.indexOf(".") !== 0) {
+ return true;
+ } else {
+ return false;
+ }
+
+ }
+
+ function _filesQueued(files, event){
+
+ //Push into the queue
+ angular.forEach(files, function(file){
+
+ if(_filterFile(file) === true) {
+
+ if(file.$error) {
+ scope.rejected.push(file);
+ } else {
+ scope.queue.push(file);
+ }
+
+ }
+
+ });
+
+ //when queue is done, kick the uploader
+ if(!scope.working){
+ _processQueueItem();
+ }
+ }
+
+
+ function _processQueueItem(){
+
+ if(scope.queue.length > 0){
+ scope.currentFile = scope.queue.shift();
+ _upload(scope.currentFile);
+ }else if(scope.done.length > 0){
+
+ if(scope.filesUploaded){
+ //queue is empty, trigger the done action
+ scope.filesUploaded(scope.done);
+ }
+
+ //auto-clear the done queue after 3 secs
+ var currentLength = scope.done.length;
+ $timeout(function(){
+ scope.done.splice(0, currentLength);
+ }, 3000);
+ }
+ }
+
+ function _upload(file) {
+
+ scope.propertyAlias = scope.propertyAlias ? scope.propertyAlias : "umbracoFile";
+ scope.contentTypeAlias = scope.contentTypeAlias ? scope.contentTypeAlias : "Image";
+
+ Upload.upload({
+ url: umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostAddFile"),
+ fields: {
+ 'currentFolder': scope.parentId,
+ 'contentTypeAlias': scope.contentTypeAlias,
+ 'propertyAlias': scope.propertyAlias,
+ 'path': file.path
+ },
+ file: file
+ }).progress(function (evt) {
+
+ // calculate progress in percentage
+ var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
+
+ // set percentage property on file
+ file.uploadProgress = progressPercentage;
+
+ // set uploading status on file
+ file.uploadStatus = "uploading";
+
+ }).success(function (data, status, headers, config) {
+
+ if(data.notifications && data.notifications.length > 0) {
+
+ // set error status on file
+ file.uploadStatus = "error";
+
+ // Throw message back to user with the cause of the error
+ file.serverErrorMessage = data.notifications[0].message;
+
+ // Put the file in the rejected pool
+ scope.rejected.push(file);
+
+ } else {
+
+ // set done status on file
+ file.uploadStatus = "done";
+
+ // set date/time for when done - used for sorting
+ file.doneDate = new Date();
+
+ // Put the file in the done pool
+ scope.done.push(file);
+
+ }
+
+ scope.currentFile = undefined;
+
+ //after processing, test if everthing is done
+ _processQueueItem();
+
+ }).error( function (evt, status, headers, config) {
+
+ // set status done
+ file.uploadStatus = "error";
+
+ //if the service returns a detailed error
+ if (evt.InnerException) {
+ file.serverErrorMessage = evt.InnerException.ExceptionMessage;
+
+ //Check if its the common "too large file" exception
+ if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
+ file.serverErrorMessage = "File too large to upload";
+ }
+
+ } else if (evt.Message) {
+ file.serverErrorMessage = evt.Message;
+ }
+
+ // If file not found, server will return a 404 and display this message
+ if(status === 404 ) {
+ file.serverErrorMessage = "File not found";
+ }
+
+ //after processing, test if everthing is done
+ scope.rejected.push(file);
+ scope.currentFile = undefined;
+
+ _processQueueItem();
+ });
+ }
+
+
+ scope.handleFiles = function(files, event){
+ if(scope.filesQueued){
+ scope.filesQueued(files, event);
+ }
+
+ _filesQueued(files, event);
+
+ };
+
+ }
+
+
+ };
+ });
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbFileUpload
+* @function
+* @restrict A
+* @scope
+* @description
+* Listens for file input control changes and emits events when files are selected for use in other controllers.
+**/
+function umbFileUpload() {
+ return {
+ restrict: "A",
+ scope: true, //create a new scope
+ link: function (scope, el, attrs) {
+ el.bind('change', function (event) {
+ var files = event.target.files;
+ //emit event upward
+ scope.$emit("filesSelected", { files: files });
+ });
+ }
+ };
+}
+
+angular.module('umbraco.directives').directive("umbFileUpload", umbFileUpload);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:umbSingleFileUpload
+* @function
+* @restrict A
+* @scope
+* @description
+* A single file upload field that will reset itself based on the object passed in for the rebuild parameter. This
+* is required because the only way to reset an upload control is to replace it's html.
+**/
+function umbSingleFileUpload($compile) {
+ return {
+ restrict: "E",
+ scope: {
+ rebuild: "="
+ },
+ replace: true,
+ template: "",
+ link: function (scope, el, attrs) {
+
+ scope.$watch("rebuild", function (newVal, oldVal) {
+ if (newVal && newVal !== oldVal) {
+ //recompile it!
+ el.html("");
+ $compile(el.contents())(scope);
+ }
+ });
+
+ }
+ };
+}
+
+angular.module('umbraco.directives').directive("umbSingleFileUpload", umbSingleFileUpload);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:noDirtyCheck
+* @restrict A
+* @description Can be attached to form inputs to prevent them from setting the form as dirty (http://stackoverflow.com/questions/17089090/prevent-input-from-setting-form-dirty-angularjs)
+**/
+function noDirtyCheck() {
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ link: function (scope, elm, attrs, ctrl) {
+ elm.focus(function () {
+ ctrl.$pristine = false;
+ });
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("noDirtyCheck", noDirtyCheck);
+(function() {
+ 'use strict';
+
+ function SetDirtyOnChange() {
+
+ function link(scope, el, attr, ctrl) {
+
+ var initValue = attr.umbSetDirtyOnChange;
+
+ attr.$observe("umbSetDirtyOnChange", function (newValue) {
+ if(newValue !== initValue) {
+ ctrl.$setDirty();
+ }
+ });
+
+ }
+
+ var directive = {
+ require: "^form",
+ restrict: 'A',
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('umbSetDirtyOnChange', SetDirtyOnChange);
+
+})();
+
+/**
+ * General-purpose validator for ngModel.
+ * angular.js comes with several built-in validation mechanism for input fields (ngRequired, ngPattern etc.) but using
+ * an arbitrary validation function requires creation of a custom formatters and / or parsers.
+ * The ui-validate directive makes it easy to use any function(s) defined in scope as a validator function(s).
+ * A validator function will trigger validation on both model and input changes.
+ *
+ * @example
+ * @example
+ * @example
+ * @example
+ *
+ * @param val-custom {string|object literal} If strings is passed it should be a scope's function to be used as a validator.
+ * If an object literal is passed a key denotes a validation error key while a value should be a validator function.
+ * In both cases validator function should take a value to validate as its argument and should return true/false indicating a validation result.
+ */
+
+ /*
+ This code comes from the angular UI project, we had to change the directive name and module
+ but other then that its unmodified
+ */
+angular.module('umbraco.directives.validation')
+.directive('valCustom', function () {
+
+ return {
+ restrict: 'A',
+ require: 'ngModel',
+ link: function (scope, elm, attrs, ctrl) {
+ var validateFn, watch, validators = {},
+ validateExpr = scope.$eval(attrs.valCustom);
+
+ if (!validateExpr){ return;}
+
+ if (angular.isString(validateExpr)) {
+ validateExpr = { validator: validateExpr };
+ }
+
+ angular.forEach(validateExpr, function (exprssn, key) {
+ validateFn = function (valueToValidate) {
+ var expression = scope.$eval(exprssn, { '$value' : valueToValidate });
+ if (angular.isObject(expression) && angular.isFunction(expression.then)) {
+ // expression is a promise
+ expression.then(function(){
+ ctrl.$setValidity(key, true);
+ }, function(){
+ ctrl.$setValidity(key, false);
+ });
+ return valueToValidate;
+ } else if (expression) {
+ // expression is true
+ ctrl.$setValidity(key, true);
+ return valueToValidate;
+ } else {
+ // expression is false
+ ctrl.$setValidity(key, false);
+ return undefined;
+ }
+ };
+ validators[key] = validateFn;
+
+ ctrl.$parsers.push(validateFn);
+ });
+
+ function apply_watch(watch)
+ {
+ //string - update all validators on expression change
+ if (angular.isString(watch))
+ {
+ scope.$watch(watch, function(){
+ angular.forEach(validators, function(validatorFn){
+ validatorFn(ctrl.$modelValue);
+ });
+ });
+ return;
+ }
+
+ //array - update all validators on change of any expression
+ if (angular.isArray(watch))
+ {
+ angular.forEach(watch, function(expression){
+ scope.$watch(expression, function()
+ {
+ angular.forEach(validators, function(validatorFn){
+ validatorFn(ctrl.$modelValue);
+ });
+ });
+ });
+ return;
+ }
+
+ //object - update appropriate validator
+ if (angular.isObject(watch))
+ {
+ angular.forEach(watch, function(expression, validatorKey)
+ {
+ //value is string - look after one expression
+ if (angular.isString(expression))
+ {
+ scope.$watch(expression, function(){
+ validators[validatorKey](ctrl.$modelValue);
+ });
+ }
+
+ //value is array - look after all expressions in array
+ if (angular.isArray(expression))
+ {
+ angular.forEach(expression, function(intExpression)
+ {
+ scope.$watch(intExpression, function(){
+ validators[validatorKey](ctrl.$modelValue);
+ });
+ });
+ }
+ });
+ }
+ }
+ // Support for val-custom-watch
+ if (attrs.valCustomWatch){
+ apply_watch( scope.$eval(attrs.valCustomWatch) );
+ }
+ }
+ };
+});
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valHighlight
+* @restrict A
+* @description Used on input fields when you want to signal that they are in error, this will highlight the item for 1 second
+**/
+function valHighlight($timeout) {
+ return {
+ restrict: "A",
+ link: function (scope, element, attrs, ctrl) {
+
+ attrs.$observe("valHighlight", function (newVal) {
+ if (newVal === "true") {
+ element.addClass("highlight-error");
+ $timeout(function () {
+ //set the bound scope property to false
+ scope[attrs.valHighlight] = false;
+ }, 1000);
+ }
+ else {
+ element.removeClass("highlight-error");
+ }
+ });
+
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valHighlight", valHighlight);
+
+angular.module('umbraco.directives.validation')
+ .directive('valCompare',function () {
+ return {
+ require: "ngModel",
+ link: function (scope, elem, attrs, ctrl) {
+
+ //TODO: Pretty sure this should be done using a requires ^form in the directive declaration
+ var otherInput = elem.inheritedData("$formController")[attrs.valCompare];
+
+ ctrl.$parsers.push(function(value) {
+ if(value === otherInput.$viewValue) {
+ ctrl.$setValidity("valCompare", true);
+ return value;
+ }
+ ctrl.$setValidity("valCompare", false);
+ });
+
+ otherInput.$parsers.push(function(value) {
+ ctrl.$setValidity("valCompare", value === ctrl.$viewValue);
+ return value;
+ });
+ }
+ };
+});
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:valEmail
+ * @restrict A
+ * @description A custom directive to validate an email address string, this is required because angular's default validator is incorrect.
+ **/
+function valEmail(valEmailExpression) {
+
+ return {
+ require: 'ngModel',
+ restrict: "A",
+ link: function (scope, elm, attrs, ctrl) {
+
+ var patternValidator = function (viewValue) {
+ //NOTE: we don't validate on empty values, use required validator for that
+ if (!viewValue || valEmailExpression.EMAIL_REGEXP.test(viewValue)) {
+ // it is valid
+ ctrl.$setValidity('valEmail', true);
+ //assign a message to the validator
+ ctrl.errorMsg = "";
+ return viewValue;
+ }
+ else {
+ // it is invalid, return undefined (no model update)
+ ctrl.$setValidity('valEmail', false);
+ //assign a message to the validator
+ ctrl.errorMsg = "Invalid email";
+ return undefined;
+ }
+ };
+
+ ctrl.$parsers.push(patternValidator);
+ }
+ };
+}
+
+angular.module('umbraco.directives.validation')
+ .directive("valEmail", valEmail)
+ .factory('valEmailExpression', function() {
+ return {
+ EMAIL_REGEXP: /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i
+ };
+ });
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valFormManager
+* @restrict A
+* @require formController
+* @description Used to broadcast an event to all elements inside this one to notify that form validation has
+* changed. If we don't use this that means you have to put a watch for each directive on a form's validation
+* changing which would result in much higher processing. We need to actually watch the whole $error collection of a form
+* because just watching $valid or $invalid doesn't acurrately trigger form validation changing.
+* This also sets the show-validation (or a custom) css class on the element when the form is invalid - this lets
+* us css target elements to be displayed when the form is submitting/submitted.
+* Another thing this directive does is to ensure that any .control-group that contains form elements that are invalid will
+* be marked with the 'error' css class. This ensures that labels included in that control group are styled correctly.
+**/
+function valFormManager(serverValidationManager, $rootScope, $log, $timeout, notificationsService, eventsService, $routeParams) {
+ return {
+ require: "form",
+ restrict: "A",
+ controller: function($scope) {
+ //This exposes an API for direct use with this directive
+
+ var unsubscribe = [];
+ var self = this;
+
+ //This is basically the same as a directive subscribing to an event but maybe a little
+ // nicer since the other directive can use this directive's API instead of a magical event
+ this.onValidationStatusChanged = function (cb) {
+ unsubscribe.push($scope.$on("valStatusChanged", function(evt, args) {
+ cb.apply(self, [evt, args]);
+ }));
+ };
+
+ //Ensure to remove the event handlers when this instance is destroyted
+ $scope.$on('$destroy', function () {
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
+ });
+ },
+ link: function (scope, element, attr, formCtrl) {
+
+ scope.$watch(function () {
+ return formCtrl.$error;
+ }, function (e) {
+ scope.$broadcast("valStatusChanged", { form: formCtrl });
+
+ //find all invalid elements' .control-group's and apply the error class
+ var inError = element.find(".control-group .ng-invalid").closest(".control-group");
+ inError.addClass("error");
+
+ //find all control group's that have no error and ensure the class is removed
+ var noInError = element.find(".control-group .ng-valid").closest(".control-group").not(inError);
+ noInError.removeClass("error");
+
+ }, true);
+
+ var className = attr.valShowValidation ? attr.valShowValidation : "show-validation";
+ var savingEventName = attr.savingEvent ? attr.savingEvent : "formSubmitting";
+ var savedEvent = attr.savedEvent ? attr.savingEvent : "formSubmitted";
+
+ //This tracks if the user is currently saving a new item, we use this to determine
+ // if we should display the warning dialog that they are leaving the page - if a new item
+ // is being saved we never want to display that dialog, this will also cause problems when there
+ // are server side validation issues.
+ var isSavingNewItem = false;
+
+ //we should show validation if there are any msgs in the server validation collection
+ if (serverValidationManager.items.length > 0) {
+ element.addClass(className);
+ }
+
+ var unsubscribe = [];
+
+ //listen for the forms saving event
+ unsubscribe.push(scope.$on(savingEventName, function(ev, args) {
+ element.addClass(className);
+
+ //set the flag so we can check to see if we should display the error.
+ isSavingNewItem = $routeParams.create;
+ }));
+
+ //listen for the forms saved event
+ unsubscribe.push(scope.$on(savedEvent, function(ev, args) {
+ //remove validation class
+ element.removeClass(className);
+
+ //clear form state as at this point we retrieve new data from the server
+ //and all validation will have cleared at this point
+ formCtrl.$setPristine();
+ }));
+
+ //This handles the 'unsaved changes' dialog which is triggered when a route is attempting to be changed but
+ // the form has pending changes
+ var locationEvent = $rootScope.$on('$locationChangeStart', function(event, nextLocation, currentLocation) {
+ if (!formCtrl.$dirty || isSavingNewItem) {
+ return;
+ }
+
+ var path = nextLocation.split("#")[1];
+ if (path) {
+ if (path.indexOf("%253") || path.indexOf("%252")) {
+ path = decodeURIComponent(path);
+ }
+
+ if (!notificationsService.hasView()) {
+ var msg = { view: "confirmroutechange", args: { path: path, listener: locationEvent } };
+ notificationsService.add(msg);
+ }
+
+ //prevent the route!
+ event.preventDefault();
+
+ //raise an event
+ eventsService.emit("valFormManager.pendingChanges", true);
+ }
+
+ });
+ unsubscribe.push(locationEvent);
+
+ //Ensure to remove the event handler when this instance is destroyted
+ scope.$on('$destroy', function() {
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
+ });
+
+ $timeout(function(){
+ formCtrl.$setPristine();
+ }, 1000);
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valFormManager", valFormManager);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valPropertyMsg
+* @restrict A
+* @element textarea
+* @requires formController
+* @description This directive is used to control the display of the property level validation message.
+* We will listen for server side validation changes
+* and when an error is detected for this property we'll show the error message.
+* In order for this directive to work, the valStatusChanged directive must be placed on the containing form.
+**/
+function valPropertyMsg(serverValidationManager) {
+
+ return {
+ scope: {
+ property: "="
+ },
+ require: "^form", //require that this directive is contained within an ngForm
+ replace: true, //replace the element with the template
+ restrict: "E", //restrict to element
+ template: "{{errorMsg}}
",
+
+ /**
+ Our directive requries a reference to a form controller
+ which gets passed in to this parameter
+ */
+ link: function (scope, element, attrs, formCtrl) {
+
+ var watcher = null;
+
+ // Gets the error message to display
+ function getErrorMsg() {
+ //this can be null if no property was assigned
+ if (scope.property) {
+ //first try to get the error msg from the server collection
+ var err = serverValidationManager.getPropertyError(scope.property.alias, "");
+ //if there's an error message use it
+ if (err && err.errorMsg) {
+ return err.errorMsg;
+ }
+ else {
+ return scope.property.propertyErrorMessage ? scope.property.propertyErrorMessage : "Property has errors";
+ }
+
+ }
+ return "Property has errors";
+ }
+
+ // We need to subscribe to any changes to our model (based on user input)
+ // This is required because when we have a server error we actually invalidate
+ // the form which means it cannot be resubmitted.
+ // So once a field is changed that has a server error assigned to it
+ // we need to re-validate it for the server side validator so the user can resubmit
+ // the form. Of course normal client-side validators will continue to execute.
+ function startWatch() {
+ //if there's not already a watch
+ if (!watcher) {
+ watcher = scope.$watch("property.value", function (newValue, oldValue) {
+
+ if (!newValue || angular.equals(newValue, oldValue)) {
+ return;
+ }
+
+ var errCount = 0;
+ for (var e in formCtrl.$error) {
+ if (angular.isArray(formCtrl.$error[e])) {
+ errCount++;
+ }
+ }
+
+ //we are explicitly checking for valServer errors here, since we shouldn't auto clear
+ // based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
+ // is the only one, then we'll clear.
+
+ if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
+ scope.errorMsg = "";
+ formCtrl.$setValidity('valPropertyMsg', true);
+ stopWatch();
+ }
+ }, true);
+ }
+ }
+
+ //clear the watch when the property validator is valid again
+ function stopWatch() {
+ if (watcher) {
+ watcher();
+ watcher = null;
+ }
+ }
+
+ //if there's any remaining errors in the server validation service then we should show them.
+ var showValidation = serverValidationManager.items.length > 0;
+ var hasError = false;
+
+ //create properties on our custom scope so we can use it in our template
+ scope.errorMsg = "";
+
+ var unsubscribe = [];
+
+ //listen for form error changes
+ unsubscribe.push(scope.$on("valStatusChanged", function(evt, args) {
+ if (args.form.$invalid) {
+
+ //first we need to check if the valPropertyMsg validity is invalid
+ if (formCtrl.$error.valPropertyMsg && formCtrl.$error.valPropertyMsg.length > 0) {
+ //since we already have an error we'll just return since this means we've already set the
+ // hasError and errorMsg properties which occurs below in the serverValidationManager.subscribe
+ return;
+ }
+ else if (element.closest(".umb-control-group").find(".ng-invalid").length > 0) {
+ //check if it's one of the properties that is invalid in the current content property
+ hasError = true;
+ //update the validation message if we don't already have one assigned.
+ if (showValidation && scope.errorMsg === "") {
+ scope.errorMsg = getErrorMsg();
+ }
+ }
+ else {
+ hasError = false;
+ scope.errorMsg = "";
+ }
+ }
+ else {
+ hasError = false;
+ scope.errorMsg = "";
+ }
+ }, true));
+
+ //listen for the forms saving event
+ unsubscribe.push(scope.$on("formSubmitting", function(ev, args) {
+ showValidation = true;
+ if (hasError && scope.errorMsg === "") {
+ scope.errorMsg = getErrorMsg();
+ }
+ else if (!hasError) {
+ scope.errorMsg = "";
+ stopWatch();
+ }
+ }));
+
+ //listen for the forms saved event
+ unsubscribe.push(scope.$on("formSubmitted", function(ev, args) {
+ showValidation = false;
+ scope.errorMsg = "";
+ formCtrl.$setValidity('valPropertyMsg', true);
+ stopWatch();
+ }));
+
+ //listen for server validation changes
+ // NOTE: we pass in "" in order to listen for all validation changes to the content property, not for
+ // validation changes to fields in the property this is because some server side validators may not
+ // return the field name for which the error belongs too, just the property for which it belongs.
+ // It's important to note that we need to subscribe to server validation changes here because we always must
+ // indicate that a content property is invalid at the property level since developers may not actually implement
+ // the correct field validation in their property editors.
+
+ if (scope.property) { //this can be null if no property was assigned
+ serverValidationManager.subscribe(scope.property.alias, "", function (isValid, propertyErrors, allErrors) {
+ hasError = !isValid;
+ if (hasError) {
+ //set the error message to the server message
+ scope.errorMsg = propertyErrors[0].errorMsg;
+ //flag that the current validator is invalid
+ formCtrl.$setValidity('valPropertyMsg', false);
+ startWatch();
+ }
+ else {
+ scope.errorMsg = "";
+ //flag that the current validator is valid
+ formCtrl.$setValidity('valPropertyMsg', true);
+ stopWatch();
+ }
+ });
+
+ //when the element is disposed we need to unsubscribe!
+ // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
+ // but they are a different callback instance than the above.
+ element.bind('$destroy', function () {
+ stopWatch();
+ serverValidationManager.unsubscribe(scope.property.alias, "");
+ });
+ }
+
+ //when the scope is disposed we need to unsubscribe
+ scope.$on('$destroy', function () {
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
+ });
+ }
+
+
+ };
+}
+angular.module('umbraco.directives.validation').directive("valPropertyMsg", valPropertyMsg);
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valPropertyValidator
+* @restrict A
+* @description Performs any custom property value validation checks on the client side. This allows property editors to be highly flexible when it comes to validation
+ on the client side. Typically if a property editor stores a primitive value (i.e. string) then the client side validation can easily be taken care of
+ with standard angular directives such as ng-required. However since some property editors store complex data such as JSON, a given property editor
+ might require custom validation. This directive can be used to validate an Umbraco property in any way that a developer would like by specifying a
+ callback method to perform the validation. The result of this method must return an object in the format of
+ {isValid: true, errorKey: 'required', errorMsg: 'Something went wrong' }
+ The error message returned will also be displayed for the property level validation message.
+ This directive should only be used when dealing with complex models, if custom validation needs to be performed with primitive values, use the simpler
+ angular validation directives instead since this will watch the entire model.
+**/
+
+function valPropertyValidator(serverValidationManager) {
+ return {
+ scope: {
+ valPropertyValidator: "="
+ },
+
+ // The element must have ng-model attribute and be inside an umbProperty directive
+ require: ['ngModel', '?^umbProperty'],
+
+ restrict: "A",
+
+ link: function (scope, element, attrs, ctrls) {
+
+ var modelCtrl = ctrls[0];
+ var propCtrl = ctrls.length > 1 ? ctrls[1] : null;
+
+ // Check whether the scope has a valPropertyValidator method
+ if (!scope.valPropertyValidator || !angular.isFunction(scope.valPropertyValidator)) {
+ throw new Error('val-property-validator directive must specify a function to call');
+ }
+
+ var initResult = scope.valPropertyValidator();
+
+ // Validation method
+ var validate = function (viewValue) {
+ // Calls the validition method
+ var result = scope.valPropertyValidator();
+ if (!result.errorKey || result.isValid === undefined || !result.errorMsg) {
+ throw "The result object from valPropertyValidator does not contain required properties: isValid, errorKey, errorMsg";
+ }
+ if (result.isValid === true) {
+ // Tell the controller that the value is valid
+ modelCtrl.$setValidity(result.errorKey, true);
+ if (propCtrl) {
+ propCtrl.setPropertyError(null);
+ }
+ }
+ else {
+ // Tell the controller that the value is invalid
+ modelCtrl.$setValidity(result.errorKey, false);
+ if (propCtrl) {
+ propCtrl.setPropertyError(result.errorMsg);
+ }
+ }
+ };
+
+ // Parsers are called as soon as the value in the form input is modified
+ modelCtrl.$parsers.push(validate);
+
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valPropertyValidator", valPropertyValidator);
+
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:valRegex
+ * @restrict A
+ * @description A custom directive to allow for matching a value against a regex string.
+ * NOTE: there's already an ng-pattern but this requires that a regex expression is set, not a regex string
+ **/
+function valRegex() {
+
+ return {
+ require: 'ngModel',
+ restrict: "A",
+ link: function (scope, elm, attrs, ctrl) {
+
+ var flags = "";
+ var regex;
+ var eventBindings = [];
+
+ attrs.$observe("valRegexFlags", function (newVal) {
+ if (newVal) {
+ flags = newVal;
+ }
+ });
+
+ attrs.$observe("valRegex", function (newVal) {
+ if (newVal) {
+ try {
+ var resolved = newVal;
+ if (resolved) {
+ regex = new RegExp(resolved, flags);
+ }
+ else {
+ regex = new RegExp(attrs.valRegex, flags);
+ }
+ }
+ catch (e) {
+ regex = new RegExp(attrs.valRegex, flags);
+ }
+ }
+ });
+
+ eventBindings.push(scope.$watch('ngModel', function(newValue, oldValue){
+ if(newValue && newValue !== oldValue) {
+ patternValidator(newValue);
+ }
+ }));
+
+ var patternValidator = function (viewValue) {
+ if (regex) {
+ //NOTE: we don't validate on empty values, use required validator for that
+ if (!viewValue || regex.test(viewValue.toString())) {
+ // it is valid
+ ctrl.$setValidity('valRegex', true);
+ //assign a message to the validator
+ ctrl.errorMsg = "";
+ return viewValue;
+ }
+ else {
+ // it is invalid, return undefined (no model update)
+ ctrl.$setValidity('valRegex', false);
+ //assign a message to the validator
+ ctrl.errorMsg = "Value is invalid, it does not match the correct pattern";
+ return undefined;
+ }
+ }
+ };
+
+ scope.$on('$destroy', function(){
+ // unbind watchers
+ for(var e in eventBindings) {
+ eventBindings[e]();
+ }
+ });
+
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valRegex", valRegex);
+
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:valServer
+ * @restrict A
+ * @description This directive is used to associate a content property with a server-side validation response
+ * so that the validators in angular are updated based on server-side feedback.
+ **/
+function valServer(serverValidationManager) {
+ return {
+ require: ['ngModel', '?^umbProperty'],
+ restrict: "A",
+ link: function (scope, element, attr, ctrls) {
+
+ var modelCtrl = ctrls[0];
+ var umbPropCtrl = ctrls.length > 1 ? ctrls[1] : null;
+ if (!umbPropCtrl) {
+ //we cannot proceed, this validator will be disabled
+ return;
+ }
+
+ var watcher = null;
+
+ //Need to watch the value model for it to change, previously we had subscribed to
+ //modelCtrl.$viewChangeListeners but this is not good enough if you have an editor that
+ // doesn't specifically have a 2 way ng binding. This is required because when we
+ // have a server error we actually invalidate the form which means it cannot be
+ // resubmitted. So once a field is changed that has a server error assigned to it
+ // we need to re-validate it for the server side validator so the user can resubmit
+ // the form. Of course normal client-side validators will continue to execute.
+ function startWatch() {
+ //if there's not already a watch
+ if (!watcher) {
+ watcher = scope.$watch(function () {
+ return modelCtrl.$modelValue;
+ }, function (newValue, oldValue) {
+
+ if (!newValue || angular.equals(newValue, oldValue)) {
+ return;
+ }
+
+ if (modelCtrl.$invalid) {
+ modelCtrl.$setValidity('valServer', true);
+ stopWatch();
+ }
+ }, true);
+ }
+ }
+
+ function stopWatch() {
+ if (watcher) {
+ watcher();
+ watcher = null;
+ }
+ }
+
+ var currentProperty = umbPropCtrl.property;
+
+ //default to 'value' if nothing is set
+ var fieldName = "value";
+ if (attr.valServer) {
+ fieldName = scope.$eval(attr.valServer);
+ if (!fieldName) {
+ //eval returned nothing so just use the string
+ fieldName = attr.valServer;
+ }
+ }
+
+ //subscribe to the server validation changes
+ serverValidationManager.subscribe(currentProperty.alias, fieldName, function (isValid, propertyErrors, allErrors) {
+ if (!isValid) {
+ modelCtrl.$setValidity('valServer', false);
+ //assign an error msg property to the current validator
+ modelCtrl.errorMsg = propertyErrors[0].errorMsg;
+ startWatch();
+ }
+ else {
+ modelCtrl.$setValidity('valServer', true);
+ //reset the error message
+ modelCtrl.errorMsg = "";
+ stopWatch();
+ }
+ });
+
+ //when the element is disposed we need to unsubscribe!
+ // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
+ // but they are a different callback instance than the above.
+ element.bind('$destroy', function () {
+ stopWatch();
+ serverValidationManager.unsubscribe(currentProperty.alias, fieldName);
+ });
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valServer", valServer);
+/**
+ * @ngdoc directive
+ * @name umbraco.directives.directive:valServerField
+ * @restrict A
+ * @description This directive is used to associate a content field (not user defined) with a server-side validation response
+ * so that the validators in angular are updated based on server-side feedback.
+ **/
+function valServerField(serverValidationManager) {
+ return {
+ require: 'ngModel',
+ restrict: "A",
+ link: function (scope, element, attr, ctrl) {
+
+ var fieldName = null;
+ var eventBindings = [];
+
+ attr.$observe("valServerField", function (newVal) {
+ if (newVal && fieldName === null) {
+ fieldName = newVal;
+
+ //subscribe to the changed event of the view model. This is required because when we
+ // have a server error we actually invalidate the form which means it cannot be
+ // resubmitted. So once a field is changed that has a server error assigned to it
+ // we need to re-validate it for the server side validator so the user can resubmit
+ // the form. Of course normal client-side validators will continue to execute.
+ eventBindings.push(scope.$watch('ngModel', function(newValue){
+ if (ctrl.$invalid) {
+ ctrl.$setValidity('valServerField', true);
+ }
+ }));
+
+ //subscribe to the server validation changes
+ serverValidationManager.subscribe(null, fieldName, function (isValid, fieldErrors, allErrors) {
+ if (!isValid) {
+ ctrl.$setValidity('valServerField', false);
+ //assign an error msg property to the current validator
+ ctrl.errorMsg = fieldErrors[0].errorMsg;
+ }
+ else {
+ ctrl.$setValidity('valServerField', true);
+ //reset the error message
+ ctrl.errorMsg = "";
+ }
+ });
+
+ //when the element is disposed we need to unsubscribe!
+ // NOTE: this is very important otherwise when this controller re-binds the previous subscriptsion will remain
+ // but they are a different callback instance than the above.
+ element.bind('$destroy', function () {
+ serverValidationManager.unsubscribe(null, fieldName);
+ });
+ }
+ });
+
+ scope.$on('$destroy', function(){
+ // unbind watchers
+ for(var e in eventBindings) {
+ eventBindings[e]();
+ }
+ });
+
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valServerField", valServerField);
+
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valTab
+* @restrict A
+* @description Used to show validation warnings for a tab to indicate that the tab content has validations errors in its data.
+* In order for this directive to work, the valFormManager directive must be placed on the containing form.
+**/
+function valTab() {
+ return {
+ require: ['^form', '^valFormManager'],
+ restrict: "A",
+ link: function (scope, element, attr, ctrs) {
+
+ var valFormManager = ctrs[1];
+ var tabId = "tab" + scope.tab.id;
+ scope.tabHasError = false;
+
+ //listen for form validation changes
+ valFormManager.onValidationStatusChanged(function (evt, args) {
+ if (!args.form.$valid) {
+ var tabContent = element.closest(".umb-panel").find("#" + tabId);
+ //check if the validation messages are contained inside of this tabs
+ if (tabContent.find(".ng-invalid").length > 0) {
+ scope.tabHasError = true;
+ } else {
+ scope.tabHasError = false;
+ }
+ }
+ else {
+ scope.tabHasError = false;
+ }
+ });
+
+ }
+ };
+}
+angular.module('umbraco.directives.validation').directive("valTab", valTab);
+function valToggleMsg(serverValidationManager) {
+ return {
+ require: "^form",
+ restrict: "A",
+
+ /**
+ Our directive requries a reference to a form controller which gets passed in to this parameter
+ */
+ link: function (scope, element, attr, formCtrl) {
+
+ if (!attr.valToggleMsg){
+ throw "valToggleMsg requires that a reference to a validator is specified";
+ }
+ if (!attr.valMsgFor){
+ throw "valToggleMsg requires that the attribute valMsgFor exists on the element";
+ }
+ if (!formCtrl[attr.valMsgFor]) {
+ throw "valToggleMsg cannot find field " + attr.valMsgFor + " on form " + formCtrl.$name;
+ }
+
+ //if there's any remaining errors in the server validation service then we should show them.
+ var showValidation = serverValidationManager.items.length > 0;
+ var hasCustomMsg = element.contents().length > 0;
+
+ //add a watch to the validator for the value (i.e. myForm.value.$error.required )
+ scope.$watch(function () {
+ //sometimes if a dialog closes in the middle of digest we can get null references here
+
+ return (formCtrl && formCtrl[attr.valMsgFor]) ? formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] : null;
+ }, function () {
+ //sometimes if a dialog closes in the middle of digest we can get null references here
+ if ((formCtrl && formCtrl[attr.valMsgFor])) {
+ if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg] && showValidation) {
+ element.show();
+ //display the error message if this element has no contents
+ if (!hasCustomMsg) {
+ element.html(formCtrl[attr.valMsgFor].errorMsg);
+ }
+ }
+ else {
+ element.hide();
+ }
+ }
+ });
+
+ var unsubscribe = [];
+
+ //listen for the saving event (the result is a callback method which is called to unsubscribe)
+ unsubscribe.push(scope.$on("formSubmitting", function(ev, args) {
+ showValidation = true;
+ if (formCtrl[attr.valMsgFor].$error[attr.valToggleMsg]) {
+ element.show();
+ //display the error message if this element has no contents
+ if (!hasCustomMsg) {
+ element.html(formCtrl[attr.valMsgFor].errorMsg);
+ }
+ }
+ else {
+ element.hide();
+ }
+ }));
+
+ //listen for the saved event (the result is a callback method which is called to unsubscribe)
+ unsubscribe.push(scope.$on("formSubmitted", function(ev, args) {
+ showValidation = false;
+ element.hide();
+ }));
+
+ //when the element is disposed we need to unsubscribe!
+ // NOTE: this is very important otherwise if this directive is part of a modal, the listener still exists because the dom
+ // element might still be there even after the modal has been hidden.
+ element.bind('$destroy', function () {
+ for (var u in unsubscribe) {
+ unsubscribe[u]();
+ }
+ });
+
+ }
+ };
+}
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valToggleMsg
+* @restrict A
+* @element input
+* @requires formController
+* @description This directive will show/hide an error based on: is the value + the given validator invalid? AND, has the form been submitted ?
+**/
+angular.module('umbraco.directives.validation').directive("valToggleMsg", valToggleMsg);
+angular.module('umbraco.directives.validation')
+.directive('valTriggerChange', function($sniffer) {
+ return {
+ link : function(scope, elem, attrs) {
+ elem.bind('click', function(){
+ $(attrs.valTriggerChange).trigger($sniffer.hasEvent('input') ? 'input' : 'change');
+ });
+ },
+ priority : 1
+ };
+});
+/**
+ * Konami Code directive for AngularJS
+ * @version v0.0.1
+ * @license MIT License, http://www.opensource.org/licenses/MIT
+ */
+
+angular.module('umbraco.directives')
+ .directive('konamiCode', ['$document', function ($document) {
+ var konamiKeysDefault = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
+
+ return {
+ restrict: 'A',
+ link: function (scope, element, attr) {
+
+ if (!attr.konamiCode) {
+ throw ('Konami directive must receive an expression as value.');
+ }
+
+ // Let user define a custom code.
+ var konamiKeys = attr.konamiKeys || konamiKeysDefault;
+ var keyIndex = 0;
+
+ /**
+ * Fired when konami code is type.
+ */
+ function activated() {
+ if ('konamiOnce' in attr) {
+ stopListening();
+ }
+ // Execute expression.
+ scope.$eval(attr.konamiCode);
+ }
+
+ /**
+ * Handle keydown events.
+ */
+ function keydown(e) {
+ if (e.keyCode === konamiKeys[keyIndex++]) {
+ if (keyIndex === konamiKeys.length) {
+ keyIndex = 0;
+ activated();
+ }
+ } else {
+ keyIndex = 0;
+ }
+ }
+
+ /**
+ * Stop to listen typing.
+ */
+ function stopListening() {
+ $document.off('keydown', keydown);
+ }
+
+ // Start listening to key typing.
+ $document.on('keydown', keydown);
+
+ // Stop listening when scope is destroyed.
+ scope.$on('$destroy', stopListening);
+ }
+ };
+ }]);
+(function() {
+ 'use strict';
+
+ function ValRequireComponentDirective() {
+
+ function link(scope, el, attr, ngModel) {
+
+ var unbindModelWatcher = scope.$watch(function () {
+ return ngModel.$modelValue;
+ }, function(newValue) {
+
+ if(newValue === undefined || newValue === null || newValue === "") {
+ ngModel.$setValidity("valRequiredComponent", false);
+ } else {
+ ngModel.$setValidity("valRequiredComponent", true);
+ }
+
+ });
+
+ // clean up
+ scope.$on('$destroy', function(){
+ unbindModelWatcher();
+ });
+
+ }
+
+ var directive = {
+ require: 'ngModel',
+ restrict: "A",
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('valRequireComponent', ValRequireComponentDirective);
+
+})();
+
+/**
+* @ngdoc directive
+* @name umbraco.directives.directive:valSubView
+* @restrict A
+* @description Used to show validation warnings for a editor sub view to indicate that the section content has validation errors in its data.
+* In order for this directive to work, the valFormManager directive must be placed on the containing form.
+**/
+(function() {
+ 'use strict';
+
+ function valSubViewDirective() {
+
+ function link(scope, el, attr, ctrl) {
+
+ var valFormManager = ctrl[1];
+ scope.subView.hasError = false;
+
+ //listen for form validation changes
+ valFormManager.onValidationStatusChanged(function (evt, args) {
+ if (!args.form.$valid) {
+
+ var subViewContent = el.find(".ng-invalid");
+
+ if (subViewContent.length > 0) {
+ scope.subView.hasError = true;
+ } else {
+ scope.subView.hasError = false;
+ }
+
+ }
+ else {
+ scope.subView.hasError = false;
+ }
+ });
+
+ }
+
+ var directive = {
+ require: ['^form', '^valFormManager'],
+ restrict: "A",
+ link: link
+ };
+
+ return directive;
+ }
+
+ angular.module('umbraco.directives').directive('valSubView', valSubViewDirective);
+
+})();
+
+
})();
\ No newline at end of file
diff --git a/OurUmbraco.Site/umbraco/js/umbraco.resources.js b/OurUmbraco.Site/umbraco/js/umbraco.resources.js
index 339373da..2b636522 100644
--- a/OurUmbraco.Site/umbraco/js/umbraco.resources.js
+++ b/OurUmbraco.Site/umbraco/js/umbraco.resources.js
@@ -730,6 +730,16 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
'Failed to check permission for item ' + id);
},
+ getPermissions: function (nodeIds) {
+ return umbRequestHelper.resourcePromise(
+ $http.post(
+ umbRequestHelper.getApiUrl(
+ "contentApiBaseUrl",
+ "GetPermissions"),
+ nodeIds),
+ 'Failed to get permissions');
+ },
+
/**
* @ngdoc method
* @name umbraco.resources.contentResource#save
@@ -899,7 +909,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
filterContentTypes: filterContentTypes,
filterPropertyTypes: filterPropertyTypes
};
-
+
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
@@ -1071,9 +1081,9 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
* .then(function() {
* alert("node was moved");
* }, function(err){
- * alert("node didnt move:" + err.data.Message);
+ * alert("node didnt move:" + err.data.Message);
* });
- *
+ *
* @param {Object} args arguments object
* @param {Int} args.idd the ID of the node to move
* @param {Int} args.parentId the ID of the parent node to move to
@@ -1100,6 +1110,26 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
'Failed to move content');
},
+ copy: function(args) {
+ if (!args) {
+ throw "args cannot be null";
+ }
+ if (!args.parentId) {
+ throw "args.parentId cannot be null";
+ }
+ if (!args.id) {
+ throw "args.id cannot be null";
+ }
+
+ return umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("contentTypeApiBaseUrl", "PostCopy"),
+ {
+ parentId: args.parentId,
+ id: args.id
+ }),
+ 'Failed to copy content');
+ },
+
createContainer: function(parentId, name) {
return umbRequestHelper.resourcePromise(
@@ -1107,7 +1137,7 @@ function contentTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
'Failed to create a folder under parent id ' + parentId);
}
-
+
};
}
angular.module('umbraco.resources').factory('contentTypeResource', contentTypeResource);
@@ -2643,7 +2673,7 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
* .then(function(array) {
* $scope.type = type;
* });
- *
+ *
* @param {Int} mediaId id of the media item to retrive allowed child types for
* @returns {Promise} resourcePromise object.
*
@@ -2735,9 +2765,9 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
* .then(function() {
* alert("node was moved");
* }, function(err){
- * alert("node didnt move:" + err.data.Message);
+ * alert("node didnt move:" + err.data.Message);
* });
- *
+ *
* @param {Object} args arguments object
* @param {Int} args.idd the ID of the node to move
* @param {Int} args.parentId the ID of the parent node to move to
@@ -2764,6 +2794,26 @@ function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
'Failed to move content');
},
+ copy: function (args) {
+ if (!args) {
+ throw "args cannot be null";
+ }
+ if (!args.parentId) {
+ throw "args.parentId cannot be null";
+ }
+ if (!args.id) {
+ throw "args.id cannot be null";
+ }
+
+ return umbRequestHelper.resourcePromise(
+ $http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"),
+ {
+ parentId: args.parentId,
+ id: args.id
+ }),
+ 'Failed to copy content');
+ },
+
createContainer: function(parentId, name) {
return umbRequestHelper.resourcePromise(
@@ -3416,7 +3466,7 @@ function treeResource($q, $http, umbRequestHelper) {
/** internal method to get the tree menu url */
function getTreeMenuUrl(node) {
if (!node.menuUrl) {
- throw "No menuUrl property found on the tree node, cannot load menu";
+ return null;
}
return node.menuUrl;
}
@@ -3426,10 +3476,16 @@ function treeResource($q, $http, umbRequestHelper) {
/** Loads in the data to display the nodes menu */
loadMenu: function (node) {
-
- return umbRequestHelper.resourcePromise(
- $http.get(getTreeMenuUrl(node)),
- "Failed to retrieve data for a node's menu " + node.id);
+ var treeMenuUrl = getTreeMenuUrl(node);
+ if (treeMenuUrl !== undefined && treeMenuUrl !== null && treeMenuUrl.length > 0) {
+ return umbRequestHelper.resourcePromise(
+ $http.get(getTreeMenuUrl(node)),
+ "Failed to retrieve data for a node's menu " + node.id);
+ } else {
+ return $q.reject({
+ errorMsg: "No tree menu url defined for node " + node.id
+ });
+ }
},
/** Loads in the data to display the nodes for an application */
diff --git a/OurUmbraco.Site/umbraco/js/umbraco.services.js b/OurUmbraco.Site/umbraco/js/umbraco.services.js
index 21d9d6bd..f75b1a11 100644
--- a/OurUmbraco.Site/umbraco/js/umbraco.services.js
+++ b/OurUmbraco.Site/umbraco/js/umbraco.services.js
@@ -164,303 +164,303 @@ function angularHelper($log, $q) {
};
}
angular.module('umbraco.services').factory('angularHelper', angularHelper);
-/**
- * @ngdoc service
- * @name umbraco.services.appState
- * @function
- *
- * @description
- * Tracks the various application state variables when working in the back office, raises events when state changes.
- *
- * ##Samples
- *
- * ####Subscribe to global state changes:
- *
- *
- * scope.showTree = appState.getGlobalState("showNavigation");
- *
- * eventsService.on("appState.globalState.changed", function (e, args) {
- * if (args.key === "showNavigation") {
- * scope.showTree = args.value;
- * }
- * });
- *
- *
- * ####Subscribe to section-state changes
- *
- *
- * scope.currentSection = appState.getSectionState("currentSection");
- *
- * eventsService.on("appState.sectionState.changed", function (e, args) {
- * if (args.key === "currentSection") {
- * scope.currentSection = args.value;
- * }
- * });
- *
- */
-function appState(eventsService) {
-
- //Define all variables here - we are never returning this objects so they cannot be publicly mutable
- // changed, we only expose methods to interact with the values.
-
- var globalState = {
- showNavigation: null,
- touchDevice: null,
- showTray: null,
- stickyNavigation: null,
- navMode: null,
- isReady: null,
- isTablet: null
- };
-
- var sectionState = {
- //The currently active section
- currentSection: null,
- showSearchResults: null
- };
-
- var treeState = {
- //The currently selected node
- selectedNode: null,
- //The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root.
- //We keep this reference so we can lookup nodes to interact with in the UI via the tree service
- currentRootNode: null
- };
-
- var menuState = {
- //this list of menu items to display
- menuActions: null,
- //the title to display in the context menu dialog
- dialogTitle: null,
- //The tree node that the ctx menu is launched for
- currentNode: null,
- //Whether the menu's dialog is being shown or not
- showMenuDialog: null,
- //Whether the context menu is being shown or not
- showMenu: null
- };
-
- /** function to validate and set the state on a state object */
- function setState(stateObj, key, value, stateObjName) {
- if (!_.has(stateObj, key)) {
- throw "The variable " + key + " does not exist in " + stateObjName;
- }
- var changed = stateObj[key] !== value;
- stateObj[key] = value;
- if (changed) {
- eventsService.emit("appState." + stateObjName + ".changed", { key: key, value: value });
- }
- }
-
- /** function to validate and set the state on a state object */
- function getState(stateObj, key, stateObjName) {
- if (!_.has(stateObj, key)) {
- throw "The variable " + key + " does not exist in " + stateObjName;
- }
- return stateObj[key];
- }
-
- return {
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#getGlobalState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Returns the current global state value by key - we do not return an object reference here - we do NOT want this
- * to be publicly mutable and allow setting arbitrary values
- *
- */
- getGlobalState: function (key) {
- return getState(globalState, key, "globalState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#setGlobalState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Sets a global state value by key
- *
- */
- setGlobalState: function (key, value) {
- setState(globalState, key, value, "globalState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#getSectionState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Returns the current section state value by key - we do not return an object here - we do NOT want this
- * to be publicly mutable and allow setting arbitrary values
- *
- */
- getSectionState: function (key) {
- return getState(sectionState, key, "sectionState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#setSectionState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Sets a section state value by key
- *
- */
- setSectionState: function(key, value) {
- setState(sectionState, key, value, "sectionState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#getTreeState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Returns the current tree state value by key - we do not return an object here - we do NOT want this
- * to be publicly mutable and allow setting arbitrary values
- *
- */
- getTreeState: function (key) {
- return getState(treeState, key, "treeState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#setTreeState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Sets a section state value by key
- *
- */
- setTreeState: function (key, value) {
- setState(treeState, key, value, "treeState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#getMenuState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Returns the current menu state value by key - we do not return an object here - we do NOT want this
- * to be publicly mutable and allow setting arbitrary values
- *
- */
- getMenuState: function (key) {
- return getState(menuState, key, "menuState");
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#setMenuState
- * @methodOf umbraco.services.appState
- * @function
- *
- * @description
- * Sets a section state value by key
- *
- */
- setMenuState: function (key, value) {
- setState(menuState, key, value, "menuState");
- },
-
- };
-}
-angular.module('umbraco.services').factory('appState', appState);
-
-/**
- * @ngdoc service
- * @name umbraco.services.editorState
- * @function
- *
- * @description
- * Tracks the parent object for complex editors by exposing it as
- * an object reference via editorState.current.entity
- *
- * it is possible to modify this object, so should be used with care
- */
-angular.module('umbraco.services').factory("editorState", function() {
-
- var current = null;
- var state = {
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#set
- * @methodOf umbraco.services.editorState
- * @function
- *
- * @description
- * Sets the current entity object for the currently active editor
- * This is only used when implementing an editor with a complex model
- * like the content editor, where the model is modified by several
- * child controllers.
- */
- set: function (entity) {
- current = entity;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#reset
- * @methodOf umbraco.services.editorState
- * @function
- *
- * @description
- * Since the editorstate entity is read-only, you cannot set it to null
- * only through the reset() method
- */
- reset: function() {
- current = null;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.angularHelper#getCurrent
- * @methodOf umbraco.services.editorState
- * @function
- *
- * @description
- * Returns an object reference to the current editor entity.
- * the entity is the root object of the editor.
- * EditorState is used by property/parameter editors that need
- * access to the entire entity being edited, not just the property/parameter
- *
- * editorState.current can not be overwritten, you should only read values from it
- * since modifying individual properties should be handled by the property editors
- */
- getCurrent: function() {
- return current;
- }
- };
-
- //TODO: This shouldn't be removed! use getCurrent() method instead of a hacked readonly property which is confusing.
-
- //create a get/set property but don't allow setting
- Object.defineProperty(state, "current", {
- get: function () {
- return current;
- },
- set: function (value) {
- throw "Use editorState.set to set the value of the current entity";
- },
- });
-
- return state;
+/**
+ * @ngdoc service
+ * @name umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Tracks the various application state variables when working in the back office, raises events when state changes.
+ *
+ * ##Samples
+ *
+ * ####Subscribe to global state changes:
+ *
+ *
+ * scope.showTree = appState.getGlobalState("showNavigation");
+ *
+ * eventsService.on("appState.globalState.changed", function (e, args) {
+ * if (args.key === "showNavigation") {
+ * scope.showTree = args.value;
+ * }
+ * });
+ *
+ *
+ * ####Subscribe to section-state changes
+ *
+ *
+ * scope.currentSection = appState.getSectionState("currentSection");
+ *
+ * eventsService.on("appState.sectionState.changed", function (e, args) {
+ * if (args.key === "currentSection") {
+ * scope.currentSection = args.value;
+ * }
+ * });
+ *
+ */
+function appState(eventsService) {
+
+ //Define all variables here - we are never returning this objects so they cannot be publicly mutable
+ // changed, we only expose methods to interact with the values.
+
+ var globalState = {
+ showNavigation: null,
+ touchDevice: null,
+ showTray: null,
+ stickyNavigation: null,
+ navMode: null,
+ isReady: null,
+ isTablet: null
+ };
+
+ var sectionState = {
+ //The currently active section
+ currentSection: null,
+ showSearchResults: null
+ };
+
+ var treeState = {
+ //The currently selected node
+ selectedNode: null,
+ //The currently loaded root node reference - depending on the section loaded this could be a section root or a normal root.
+ //We keep this reference so we can lookup nodes to interact with in the UI via the tree service
+ currentRootNode: null
+ };
+
+ var menuState = {
+ //this list of menu items to display
+ menuActions: null,
+ //the title to display in the context menu dialog
+ dialogTitle: null,
+ //The tree node that the ctx menu is launched for
+ currentNode: null,
+ //Whether the menu's dialog is being shown or not
+ showMenuDialog: null,
+ //Whether the context menu is being shown or not
+ showMenu: null
+ };
+
+ /** function to validate and set the state on a state object */
+ function setState(stateObj, key, value, stateObjName) {
+ if (!_.has(stateObj, key)) {
+ throw "The variable " + key + " does not exist in " + stateObjName;
+ }
+ var changed = stateObj[key] !== value;
+ stateObj[key] = value;
+ if (changed) {
+ eventsService.emit("appState." + stateObjName + ".changed", { key: key, value: value });
+ }
+ }
+
+ /** function to validate and set the state on a state object */
+ function getState(stateObj, key, stateObjName) {
+ if (!_.has(stateObj, key)) {
+ throw "The variable " + key + " does not exist in " + stateObjName;
+ }
+ return stateObj[key];
+ }
+
+ return {
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#getGlobalState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Returns the current global state value by key - we do not return an object reference here - we do NOT want this
+ * to be publicly mutable and allow setting arbitrary values
+ *
+ */
+ getGlobalState: function (key) {
+ return getState(globalState, key, "globalState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#setGlobalState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Sets a global state value by key
+ *
+ */
+ setGlobalState: function (key, value) {
+ setState(globalState, key, value, "globalState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#getSectionState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Returns the current section state value by key - we do not return an object here - we do NOT want this
+ * to be publicly mutable and allow setting arbitrary values
+ *
+ */
+ getSectionState: function (key) {
+ return getState(sectionState, key, "sectionState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#setSectionState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Sets a section state value by key
+ *
+ */
+ setSectionState: function(key, value) {
+ setState(sectionState, key, value, "sectionState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#getTreeState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Returns the current tree state value by key - we do not return an object here - we do NOT want this
+ * to be publicly mutable and allow setting arbitrary values
+ *
+ */
+ getTreeState: function (key) {
+ return getState(treeState, key, "treeState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#setTreeState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Sets a section state value by key
+ *
+ */
+ setTreeState: function (key, value) {
+ setState(treeState, key, value, "treeState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#getMenuState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Returns the current menu state value by key - we do not return an object here - we do NOT want this
+ * to be publicly mutable and allow setting arbitrary values
+ *
+ */
+ getMenuState: function (key) {
+ return getState(menuState, key, "menuState");
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#setMenuState
+ * @methodOf umbraco.services.appState
+ * @function
+ *
+ * @description
+ * Sets a section state value by key
+ *
+ */
+ setMenuState: function (key, value) {
+ setState(menuState, key, value, "menuState");
+ },
+
+ };
+}
+angular.module('umbraco.services').factory('appState', appState);
+
+/**
+ * @ngdoc service
+ * @name umbraco.services.editorState
+ * @function
+ *
+ * @description
+ * Tracks the parent object for complex editors by exposing it as
+ * an object reference via editorState.current.entity
+ *
+ * it is possible to modify this object, so should be used with care
+ */
+angular.module('umbraco.services').factory("editorState", function() {
+
+ var current = null;
+ var state = {
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#set
+ * @methodOf umbraco.services.editorState
+ * @function
+ *
+ * @description
+ * Sets the current entity object for the currently active editor
+ * This is only used when implementing an editor with a complex model
+ * like the content editor, where the model is modified by several
+ * child controllers.
+ */
+ set: function (entity) {
+ current = entity;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#reset
+ * @methodOf umbraco.services.editorState
+ * @function
+ *
+ * @description
+ * Since the editorstate entity is read-only, you cannot set it to null
+ * only through the reset() method
+ */
+ reset: function() {
+ current = null;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.angularHelper#getCurrent
+ * @methodOf umbraco.services.editorState
+ * @function
+ *
+ * @description
+ * Returns an object reference to the current editor entity.
+ * the entity is the root object of the editor.
+ * EditorState is used by property/parameter editors that need
+ * access to the entire entity being edited, not just the property/parameter
+ *
+ * editorState.current can not be overwritten, you should only read values from it
+ * since modifying individual properties should be handled by the property editors
+ */
+ getCurrent: function() {
+ return current;
+ }
+ };
+
+ //TODO: This shouldn't be removed! use getCurrent() method instead of a hacked readonly property which is confusing.
+
+ //create a get/set property but don't allow setting
+ Object.defineProperty(state, "current", {
+ get: function () {
+ return current;
+ },
+ set: function (value) {
+ throw "Use editorState.set to set the value of the current entity";
+ },
+ });
+
+ return state;
});
/**
* @ngdoc service
@@ -789,7 +789,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
var deferred = $q.defer();
- if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) {
+ if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage, action: args.action })) {
args.scope.busy = true;
@@ -1261,583 +1261,583 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
}
angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper);
-/**
- * @ngdoc service
- * @name umbraco.services.contentTypeHelper
- * @description A helper service for the content type editor
- **/
-function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $injector, $q) {
-
- var contentTypeHelperService = {
-
- createIdArray: function(array) {
-
- var newArray = [];
-
- angular.forEach(array, function(arrayItem){
-
- if(angular.isObject(arrayItem)) {
- newArray.push(arrayItem.id);
- } else {
- newArray.push(arrayItem);
- }
-
- });
-
- return newArray;
-
- },
-
- generateModels: function () {
- var deferred = $q.defer();
- var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null;
- var modelsBuilderEnabled = Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled;
- if (modelsBuilderEnabled && modelsResource) {
- modelsResource.buildModels().then(function(result) {
- deferred.resolve(result);
-
- //just calling this to get the servar back to life
- modelsResource.getModelsOutOfDateStatus();
-
- }, function(e) {
- deferred.reject(e);
- });
- }
- else {
- deferred.resolve(false);
- }
- return deferred.promise;
- },
-
- checkModelsBuilderStatus: function () {
- var deferred = $q.defer();
- var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null;
- var modelsBuilderEnabled = (Umbraco && Umbraco.Sys && Umbraco.Sys.ServerVariables && Umbraco.Sys.ServerVariables.umbracoPlugins && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled === true);
-
- if (modelsBuilderEnabled && modelsResource) {
- modelsResource.getModelsOutOfDateStatus().then(function(result) {
- //Generate models buttons should be enabled if it is 0
- deferred.resolve(result.status === 0);
- });
- }
- else {
- deferred.resolve(false);
- }
- return deferred.promise;
- },
-
- makeObjectArrayFromId: function (idArray, objectArray) {
- var newArray = [];
-
- for (var idIndex = 0; idArray.length > idIndex; idIndex++) {
- var id = idArray[idIndex];
-
- for (var objectIndex = 0; objectArray.length > objectIndex; objectIndex++) {
- var object = objectArray[objectIndex];
- if (id === object.id) {
- newArray.push(object);
- }
- }
-
- }
-
- return newArray;
- },
-
- validateAddingComposition: function(contentType, compositeContentType) {
-
- //Validate that by adding this group that we are not adding duplicate property type aliases
-
- var propertiesAdding = _.flatten(_.map(compositeContentType.groups, function(g) {
- return _.map(g.properties, function(p) {
- return p.alias;
- });
- }));
- var propAliasesExisting = _.filter(_.flatten(_.map(contentType.groups, function(g) {
- return _.map(g.properties, function(p) {
- return p.alias;
- });
- })), function(f) {
- return f !== null && f !== undefined;
- });
-
- var intersec = _.intersection(propertiesAdding, propAliasesExisting);
- if (intersec.length > 0) {
- //return the overlapping property aliases
- return intersec;
- }
-
- //no overlapping property aliases
- return [];
- },
-
- mergeCompositeContentType: function(contentType, compositeContentType) {
-
- //Validate that there are no overlapping aliases
- var overlappingAliases = this.validateAddingComposition(contentType, compositeContentType);
- if (overlappingAliases.length > 0) {
- throw new Error("Cannot add this composition, these properties already exist on the content type: " + overlappingAliases.join());
- }
-
- angular.forEach(compositeContentType.groups, function(compositionGroup) {
-
- // order composition groups based on sort order
- compositionGroup.properties = $filter('orderBy')(compositionGroup.properties, 'sortOrder');
-
- // get data type details
- angular.forEach(compositionGroup.properties, function(property) {
- dataTypeResource.getById(property.dataTypeId)
- .then(function(dataType) {
- property.dataTypeIcon = dataType.icon;
- property.dataTypeName = dataType.name;
- });
- });
-
- // set inherited state on tab
- compositionGroup.inherited = true;
-
- // set inherited state on properties
- angular.forEach(compositionGroup.properties, function(compositionProperty) {
- compositionProperty.inherited = true;
- });
-
- // set tab state
- compositionGroup.tabState = "inActive";
-
- // if groups are named the same - merge the groups
- angular.forEach(contentType.groups, function(contentTypeGroup) {
-
- if (contentTypeGroup.name === compositionGroup.name) {
-
- // set flag to show if properties has been merged into a tab
- compositionGroup.groupIsMerged = true;
-
- // make group inherited
- contentTypeGroup.inherited = true;
-
- // add properties to the top of the array
- contentTypeGroup.properties = compositionGroup.properties.concat(contentTypeGroup.properties);
-
- // update sort order on all properties in merged group
- contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties);
-
- // make parentTabContentTypeNames to an array so we can push values
- if (contentTypeGroup.parentTabContentTypeNames === null || contentTypeGroup.parentTabContentTypeNames === undefined) {
- contentTypeGroup.parentTabContentTypeNames = [];
- }
-
- // push name to array of merged composite content types
- contentTypeGroup.parentTabContentTypeNames.push(compositeContentType.name);
-
- // make parentTabContentTypes to an array so we can push values
- if (contentTypeGroup.parentTabContentTypes === null || contentTypeGroup.parentTabContentTypes === undefined) {
- contentTypeGroup.parentTabContentTypes = [];
- }
-
- // push id to array of merged composite content types
- contentTypeGroup.parentTabContentTypes.push(compositeContentType.id);
-
- // get sort order from composition
- contentTypeGroup.sortOrder = compositionGroup.sortOrder;
-
- // splice group to the top of the array
- var contentTypeGroupCopy = angular.copy(contentTypeGroup);
- var index = contentType.groups.indexOf(contentTypeGroup);
- contentType.groups.splice(index, 1);
- contentType.groups.unshift(contentTypeGroupCopy);
-
- }
-
- });
-
- // if group is not merged - push it to the end of the array - before init tab
- if (compositionGroup.groupIsMerged === false || compositionGroup.groupIsMerged === undefined) {
-
- // make parentTabContentTypeNames to an array so we can push values
- if (compositionGroup.parentTabContentTypeNames === null || compositionGroup.parentTabContentTypeNames === undefined) {
- compositionGroup.parentTabContentTypeNames = [];
- }
-
- // push name to array of merged composite content types
- compositionGroup.parentTabContentTypeNames.push(compositeContentType.name);
-
- // make parentTabContentTypes to an array so we can push values
- if (compositionGroup.parentTabContentTypes === null || compositionGroup.parentTabContentTypes === undefined) {
- compositionGroup.parentTabContentTypes = [];
- }
-
- // push id to array of merged composite content types
- compositionGroup.parentTabContentTypes.push(compositeContentType.id);
-
- // push group before placeholder tab
- contentType.groups.unshift(compositionGroup);
-
- }
-
- });
-
- // sort all groups by sortOrder property
- contentType.groups = $filter('orderBy')(contentType.groups, 'sortOrder');
-
- return contentType;
-
- },
-
- splitCompositeContentType: function (contentType, compositeContentType) {
-
- var groups = [];
-
- angular.forEach(contentType.groups, function(contentTypeGroup){
-
- if( contentTypeGroup.tabState !== "init" ) {
-
- var idIndex = contentTypeGroup.parentTabContentTypes.indexOf(compositeContentType.id);
- var nameIndex = contentTypeGroup.parentTabContentTypeNames.indexOf(compositeContentType.name);
- var groupIndex = contentType.groups.indexOf(contentTypeGroup);
-
-
- if( idIndex !== -1 ) {
-
- var properties = [];
-
- // remove all properties from composite content type
- angular.forEach(contentTypeGroup.properties, function(property){
- if(property.contentTypeId !== compositeContentType.id) {
- properties.push(property);
- }
- });
-
- // set new properties array to properties
- contentTypeGroup.properties = properties;
-
- // remove composite content type name and id from inherited arrays
- contentTypeGroup.parentTabContentTypes.splice(idIndex, 1);
- contentTypeGroup.parentTabContentTypeNames.splice(nameIndex, 1);
-
- // remove inherited state if there are no inherited properties
- if(contentTypeGroup.parentTabContentTypes.length === 0) {
- contentTypeGroup.inherited = false;
- }
-
- // remove group if there are no properties left
- if(contentTypeGroup.properties.length > 1) {
- //contentType.groups.splice(groupIndex, 1);
- groups.push(contentTypeGroup);
- }
-
- } else {
- groups.push(contentTypeGroup);
- }
-
- } else {
- groups.push(contentTypeGroup);
- }
-
- // update sort order on properties
- contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties);
-
- });
-
- contentType.groups = groups;
-
- },
-
- updatePropertiesSortOrder: function (properties) {
-
- var sortOrder = 0;
-
- angular.forEach(properties, function(property) {
- if( !property.inherited && property.propertyState !== "init") {
- property.sortOrder = sortOrder;
- }
- sortOrder++;
- });
-
- return properties;
-
- },
-
- getTemplatePlaceholder: function() {
-
- var templatePlaceholder = {
- "name": "",
- "icon": "icon-layout",
- "alias": "templatePlaceholder",
- "placeholder": true
- };
-
- return templatePlaceholder;
-
- },
-
- insertDefaultTemplatePlaceholder: function(defaultTemplate) {
-
- // get template placeholder
- var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder();
-
- // add as default template
- defaultTemplate = templatePlaceholder;
-
- return defaultTemplate;
-
- },
-
- insertTemplatePlaceholder: function(array) {
-
- // get template placeholder
- var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder();
-
- // add as selected item
- array.push(templatePlaceholder);
-
- return array;
-
- },
-
- insertChildNodePlaceholder: function (array, name, icon, id) {
-
- var placeholder = {
- "name": name,
- "icon": icon,
- "id": id
- };
-
- array.push(placeholder);
-
- }
-
- };
-
- return contentTypeHelperService;
-}
-angular.module('umbraco.services').factory('contentTypeHelper', contentTypeHelper);
+/**
+ * @ngdoc service
+ * @name umbraco.services.contentTypeHelper
+ * @description A helper service for the content type editor
+ **/
+function contentTypeHelper(contentTypeResource, dataTypeResource, $filter, $injector, $q) {
-/**
-* @ngdoc service
-* @name umbraco.services.cropperHelper
-* @description A helper object used for dealing with image cropper data
-**/
-function cropperHelper(umbRequestHelper, $http) {
- var service = {
-
- /**
- * @ngdoc method
- * @name umbraco.services.cropperHelper#configuration
- * @methodOf umbraco.services.cropperHelper
- *
- * @description
- * Returns a collection of plugins available to the tinyMCE editor
- *
- */
- configuration: function (mediaTypeAlias) {
- return umbRequestHelper.resourcePromise(
- $http.get(
- umbRequestHelper.getApiUrl(
- "imageCropperApiBaseUrl",
- "GetConfiguration",
- [{ mediaTypeAlias: mediaTypeAlias}])),
- 'Failed to retrieve tinymce configuration');
- },
-
-
- //utill for getting either min/max aspect ratio to scale image after
- calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
- var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
-
- if(maximize){
- ratio = Math.max(ratio[0], ratio[1]);
- }else{
- ratio = Math.min(ratio[0], ratio[1]);
- }
-
- return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
- },
-
- //utill for scaling width / height given a ratio
- calculateSizeToRatio : function(srcWidth, srcHeight, ratio) {
- return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
- },
-
- scaleToMaxSize : function(srcWidth, srcHeight, maxSize) {
-
- var retVal = {height: srcHeight, width: srcWidth};
-
- if(srcWidth > maxSize ||srcHeight > maxSize){
- var ratio = [maxSize / srcWidth, maxSize / srcHeight ];
- ratio = Math.min(ratio[0], ratio[1]);
-
- retVal.height = srcHeight * ratio;
- retVal.width = srcWidth * ratio;
- }
-
- return retVal;
- },
-
- //returns a ng-style object with top,left,width,height pixel measurements
- //expects {left,right,top,bottom} - {width,height}, {width,height}, int
- //offset is just to push the image position a number of pixels from top,left
- convertToStyle : function(coordinates, originalSize, viewPort, offset){
-
- var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset);
- var _offset = offset || 0;
-
- var x = 1 - (coordinates.x1 + Math.abs(coordinates.x2));
- var left_of_x = originalSize.width * x;
- var ratio = viewPort.width / left_of_x;
-
- var style = {
- position: "absolute",
- top: -(coordinates_px.y1*ratio)+ _offset,
- left: -(coordinates_px.x1* ratio)+ _offset,
- width: Math.floor(originalSize.width * ratio),
- height: Math.floor(originalSize.height * ratio),
- originalWidth: originalSize.width,
- originalHeight: originalSize.height,
- ratio: ratio
- };
-
- return style;
- },
-
-
- coordinatesToPixels : function(coordinates, originalSize, offset){
-
- var coordinates_px = {
- x1: Math.floor(coordinates.x1 * originalSize.width),
- y1: Math.floor(coordinates.y1 * originalSize.height),
- x2: Math.floor(coordinates.x2 * originalSize.width),
- y2: Math.floor(coordinates.y2 * originalSize.height)
- };
-
- return coordinates_px;
- },
-
- pixelsToCoordinates : function(image, width, height, offset){
-
- var x1_px = Math.abs(image.left-offset);
- var y1_px = Math.abs(image.top-offset);
-
- var x2_px = image.width - (x1_px + width);
- var y2_px = image.height - (y1_px + height);
-
-
- //crop coordinates in %
- var crop = {};
- crop.x1 = x1_px / image.width;
- crop.y1 = y1_px / image.height;
- crop.x2 = x2_px / image.width;
- crop.y2 = y2_px / image.height;
-
- for(var coord in crop){
- if(crop[coord] < 0){
- crop[coord] = 0;
- }
- }
-
- return crop;
- },
-
- centerInsideViewPort : function(img, viewport){
- var left = viewport.width/ 2 - img.width / 2,
- top = viewport.height / 2 - img.height / 2;
-
- return {left: left, top: top};
- },
-
- alignToCoordinates : function(image, center, viewport){
-
- var min_left = (image.width) - (viewport.width);
- var min_top = (image.height) - (viewport.height);
-
- var c_top = -(center.top * image.height) + (viewport.height / 2);
- var c_left = -(center.left * image.width) + (viewport.width / 2);
-
- if(c_top < -min_top){
- c_top = -min_top;
- }
- if(c_top > 0){
- c_top = 0;
- }
- if(c_left < -min_left){
- c_left = -min_left;
- }
- if(c_left > 0){
- c_left = 0;
- }
- return {left: c_left, top: c_top};
- },
-
-
- syncElements : function(source, target){
- target.height(source.height());
- target.width(source.width());
-
- target.css({
- "top": source[0].offsetTop,
- "left": source[0].offsetLeft
- });
- }
- };
-
- return service;
-}
-
-angular.module('umbraco.services').factory('cropperHelper', cropperHelper);
+ var contentTypeHelperService = {
-/**
- * @ngdoc service
- * @name umbraco.services.dataTypeHelper
- * @description A helper service for data types
- **/
-function dataTypeHelper() {
-
- var dataTypeHelperService = {
-
- createPreValueProps: function(preVals) {
-
- var preValues = [];
-
- for (var i = 0; i < preVals.length; i++) {
- preValues.push({
- hideLabel: preVals[i].hideLabel,
- alias: preVals[i].key,
- description: preVals[i].description,
- label: preVals[i].label,
- view: preVals[i].view,
- value: preVals[i].value
- });
- }
-
- return preValues;
-
- },
-
- rebindChangedProperties: function (origContent, savedContent) {
-
- //a method to ignore built-in prop changes
- var shouldIgnore = function (propName) {
- return _.some(["notifications", "ModelState"], function (i) {
- return i === propName;
- });
- };
- //check for changed built-in properties of the content
- for (var o in origContent) {
-
- //ignore the ones listed in the array
- if (shouldIgnore(o)) {
- continue;
- }
-
- if (!_.isEqual(origContent[o], savedContent[o])) {
- origContent[o] = savedContent[o];
- }
- }
- }
-
- };
-
- return dataTypeHelperService;
-}
+ createIdArray: function(array) {
+
+ var newArray = [];
+
+ angular.forEach(array, function(arrayItem){
+
+ if(angular.isObject(arrayItem)) {
+ newArray.push(arrayItem.id);
+ } else {
+ newArray.push(arrayItem);
+ }
+
+ });
+
+ return newArray;
+
+ },
+
+ generateModels: function () {
+ var deferred = $q.defer();
+ var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null;
+ var modelsBuilderEnabled = Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled;
+ if (modelsBuilderEnabled && modelsResource) {
+ modelsResource.buildModels().then(function(result) {
+ deferred.resolve(result);
+
+ //just calling this to get the servar back to life
+ modelsResource.getModelsOutOfDateStatus();
+
+ }, function(e) {
+ deferred.reject(e);
+ });
+ }
+ else {
+ deferred.resolve(false);
+ }
+ return deferred.promise;
+ },
+
+ checkModelsBuilderStatus: function () {
+ var deferred = $q.defer();
+ var modelsResource = $injector.has("modelsBuilderResource") ? $injector.get("modelsBuilderResource") : null;
+ var modelsBuilderEnabled = (Umbraco && Umbraco.Sys && Umbraco.Sys.ServerVariables && Umbraco.Sys.ServerVariables.umbracoPlugins && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder && Umbraco.Sys.ServerVariables.umbracoPlugins.modelsBuilder.enabled === true);
+
+ if (modelsBuilderEnabled && modelsResource) {
+ modelsResource.getModelsOutOfDateStatus().then(function(result) {
+ //Generate models buttons should be enabled if it is 0
+ deferred.resolve(result.status === 0);
+ });
+ }
+ else {
+ deferred.resolve(false);
+ }
+ return deferred.promise;
+ },
+
+ makeObjectArrayFromId: function (idArray, objectArray) {
+ var newArray = [];
+
+ for (var idIndex = 0; idArray.length > idIndex; idIndex++) {
+ var id = idArray[idIndex];
+
+ for (var objectIndex = 0; objectArray.length > objectIndex; objectIndex++) {
+ var object = objectArray[objectIndex];
+ if (id === object.id) {
+ newArray.push(object);
+ }
+ }
+
+ }
+
+ return newArray;
+ },
+
+ validateAddingComposition: function(contentType, compositeContentType) {
+
+ //Validate that by adding this group that we are not adding duplicate property type aliases
+
+ var propertiesAdding = _.flatten(_.map(compositeContentType.groups, function(g) {
+ return _.map(g.properties, function(p) {
+ return p.alias;
+ });
+ }));
+ var propAliasesExisting = _.filter(_.flatten(_.map(contentType.groups, function(g) {
+ return _.map(g.properties, function(p) {
+ return p.alias;
+ });
+ })), function(f) {
+ return f !== null && f !== undefined;
+ });
+
+ var intersec = _.intersection(propertiesAdding, propAliasesExisting);
+ if (intersec.length > 0) {
+ //return the overlapping property aliases
+ return intersec;
+ }
+
+ //no overlapping property aliases
+ return [];
+ },
+
+ mergeCompositeContentType: function(contentType, compositeContentType) {
+
+ //Validate that there are no overlapping aliases
+ var overlappingAliases = this.validateAddingComposition(contentType, compositeContentType);
+ if (overlappingAliases.length > 0) {
+ throw new Error("Cannot add this composition, these properties already exist on the content type: " + overlappingAliases.join());
+ }
+
+ angular.forEach(compositeContentType.groups, function(compositionGroup) {
+
+ // order composition groups based on sort order
+ compositionGroup.properties = $filter('orderBy')(compositionGroup.properties, 'sortOrder');
+
+ // get data type details
+ angular.forEach(compositionGroup.properties, function(property) {
+ dataTypeResource.getById(property.dataTypeId)
+ .then(function(dataType) {
+ property.dataTypeIcon = dataType.icon;
+ property.dataTypeName = dataType.name;
+ });
+ });
+
+ // set inherited state on tab
+ compositionGroup.inherited = true;
+
+ // set inherited state on properties
+ angular.forEach(compositionGroup.properties, function(compositionProperty) {
+ compositionProperty.inherited = true;
+ });
+
+ // set tab state
+ compositionGroup.tabState = "inActive";
+
+ // if groups are named the same - merge the groups
+ angular.forEach(contentType.groups, function(contentTypeGroup) {
+
+ if (contentTypeGroup.name === compositionGroup.name) {
+
+ // set flag to show if properties has been merged into a tab
+ compositionGroup.groupIsMerged = true;
+
+ // make group inherited
+ contentTypeGroup.inherited = true;
+
+ // add properties to the top of the array
+ contentTypeGroup.properties = compositionGroup.properties.concat(contentTypeGroup.properties);
+
+ // update sort order on all properties in merged group
+ contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties);
+
+ // make parentTabContentTypeNames to an array so we can push values
+ if (contentTypeGroup.parentTabContentTypeNames === null || contentTypeGroup.parentTabContentTypeNames === undefined) {
+ contentTypeGroup.parentTabContentTypeNames = [];
+ }
+
+ // push name to array of merged composite content types
+ contentTypeGroup.parentTabContentTypeNames.push(compositeContentType.name);
+
+ // make parentTabContentTypes to an array so we can push values
+ if (contentTypeGroup.parentTabContentTypes === null || contentTypeGroup.parentTabContentTypes === undefined) {
+ contentTypeGroup.parentTabContentTypes = [];
+ }
+
+ // push id to array of merged composite content types
+ contentTypeGroup.parentTabContentTypes.push(compositeContentType.id);
+
+ // get sort order from composition
+ contentTypeGroup.sortOrder = compositionGroup.sortOrder;
+
+ // splice group to the top of the array
+ var contentTypeGroupCopy = angular.copy(contentTypeGroup);
+ var index = contentType.groups.indexOf(contentTypeGroup);
+ contentType.groups.splice(index, 1);
+ contentType.groups.unshift(contentTypeGroupCopy);
+
+ }
+
+ });
+
+ // if group is not merged - push it to the end of the array - before init tab
+ if (compositionGroup.groupIsMerged === false || compositionGroup.groupIsMerged === undefined) {
+
+ // make parentTabContentTypeNames to an array so we can push values
+ if (compositionGroup.parentTabContentTypeNames === null || compositionGroup.parentTabContentTypeNames === undefined) {
+ compositionGroup.parentTabContentTypeNames = [];
+ }
+
+ // push name to array of merged composite content types
+ compositionGroup.parentTabContentTypeNames.push(compositeContentType.name);
+
+ // make parentTabContentTypes to an array so we can push values
+ if (compositionGroup.parentTabContentTypes === null || compositionGroup.parentTabContentTypes === undefined) {
+ compositionGroup.parentTabContentTypes = [];
+ }
+
+ // push id to array of merged composite content types
+ compositionGroup.parentTabContentTypes.push(compositeContentType.id);
+
+ // push group before placeholder tab
+ contentType.groups.unshift(compositionGroup);
+
+ }
+
+ });
+
+ // sort all groups by sortOrder property
+ contentType.groups = $filter('orderBy')(contentType.groups, 'sortOrder');
+
+ return contentType;
+
+ },
+
+ splitCompositeContentType: function (contentType, compositeContentType) {
+
+ var groups = [];
+
+ angular.forEach(contentType.groups, function(contentTypeGroup){
+
+ if( contentTypeGroup.tabState !== "init" ) {
+
+ var idIndex = contentTypeGroup.parentTabContentTypes.indexOf(compositeContentType.id);
+ var nameIndex = contentTypeGroup.parentTabContentTypeNames.indexOf(compositeContentType.name);
+ var groupIndex = contentType.groups.indexOf(contentTypeGroup);
+
+
+ if( idIndex !== -1 ) {
+
+ var properties = [];
+
+ // remove all properties from composite content type
+ angular.forEach(contentTypeGroup.properties, function(property){
+ if(property.contentTypeId !== compositeContentType.id) {
+ properties.push(property);
+ }
+ });
+
+ // set new properties array to properties
+ contentTypeGroup.properties = properties;
+
+ // remove composite content type name and id from inherited arrays
+ contentTypeGroup.parentTabContentTypes.splice(idIndex, 1);
+ contentTypeGroup.parentTabContentTypeNames.splice(nameIndex, 1);
+
+ // remove inherited state if there are no inherited properties
+ if(contentTypeGroup.parentTabContentTypes.length === 0) {
+ contentTypeGroup.inherited = false;
+ }
+
+ // remove group if there are no properties left
+ if(contentTypeGroup.properties.length > 1) {
+ //contentType.groups.splice(groupIndex, 1);
+ groups.push(contentTypeGroup);
+ }
+
+ } else {
+ groups.push(contentTypeGroup);
+ }
+
+ } else {
+ groups.push(contentTypeGroup);
+ }
+
+ // update sort order on properties
+ contentTypeGroup.properties = contentTypeHelperService.updatePropertiesSortOrder(contentTypeGroup.properties);
+
+ });
+
+ contentType.groups = groups;
+
+ },
+
+ updatePropertiesSortOrder: function (properties) {
+
+ var sortOrder = 0;
+
+ angular.forEach(properties, function(property) {
+ if( !property.inherited && property.propertyState !== "init") {
+ property.sortOrder = sortOrder;
+ }
+ sortOrder++;
+ });
+
+ return properties;
+
+ },
+
+ getTemplatePlaceholder: function() {
+
+ var templatePlaceholder = {
+ "name": "",
+ "icon": "icon-layout",
+ "alias": "templatePlaceholder",
+ "placeholder": true
+ };
+
+ return templatePlaceholder;
+
+ },
+
+ insertDefaultTemplatePlaceholder: function(defaultTemplate) {
+
+ // get template placeholder
+ var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder();
+
+ // add as default template
+ defaultTemplate = templatePlaceholder;
+
+ return defaultTemplate;
+
+ },
+
+ insertTemplatePlaceholder: function(array) {
+
+ // get template placeholder
+ var templatePlaceholder = contentTypeHelperService.getTemplatePlaceholder();
+
+ // add as selected item
+ array.push(templatePlaceholder);
+
+ return array;
+
+ },
+
+ insertChildNodePlaceholder: function (array, name, icon, id) {
+
+ var placeholder = {
+ "name": name,
+ "icon": icon,
+ "id": id
+ };
+
+ array.push(placeholder);
+
+ }
+
+ };
+
+ return contentTypeHelperService;
+}
+angular.module('umbraco.services').factory('contentTypeHelper', contentTypeHelper);
+
+/**
+* @ngdoc service
+* @name umbraco.services.cropperHelper
+* @description A helper object used for dealing with image cropper data
+**/
+function cropperHelper(umbRequestHelper, $http) {
+ var service = {
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.cropperHelper#configuration
+ * @methodOf umbraco.services.cropperHelper
+ *
+ * @description
+ * Returns a collection of plugins available to the tinyMCE editor
+ *
+ */
+ configuration: function (mediaTypeAlias) {
+ return umbRequestHelper.resourcePromise(
+ $http.get(
+ umbRequestHelper.getApiUrl(
+ "imageCropperApiBaseUrl",
+ "GetConfiguration",
+ [{ mediaTypeAlias: mediaTypeAlias}])),
+ 'Failed to retrieve tinymce configuration');
+ },
+
+
+ //utill for getting either min/max aspect ratio to scale image after
+ calculateAspectRatioFit : function(srcWidth, srcHeight, maxWidth, maxHeight, maximize) {
+ var ratio = [maxWidth / srcWidth, maxHeight / srcHeight ];
+
+ if(maximize){
+ ratio = Math.max(ratio[0], ratio[1]);
+ }else{
+ ratio = Math.min(ratio[0], ratio[1]);
+ }
+
+ return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
+ },
+
+ //utill for scaling width / height given a ratio
+ calculateSizeToRatio : function(srcWidth, srcHeight, ratio) {
+ return { width:srcWidth*ratio, height:srcHeight*ratio, ratio: ratio};
+ },
+
+ scaleToMaxSize : function(srcWidth, srcHeight, maxSize) {
+
+ var retVal = {height: srcHeight, width: srcWidth};
+
+ if(srcWidth > maxSize ||srcHeight > maxSize){
+ var ratio = [maxSize / srcWidth, maxSize / srcHeight ];
+ ratio = Math.min(ratio[0], ratio[1]);
+
+ retVal.height = srcHeight * ratio;
+ retVal.width = srcWidth * ratio;
+ }
+
+ return retVal;
+ },
+
+ //returns a ng-style object with top,left,width,height pixel measurements
+ //expects {left,right,top,bottom} - {width,height}, {width,height}, int
+ //offset is just to push the image position a number of pixels from top,left
+ convertToStyle : function(coordinates, originalSize, viewPort, offset){
+
+ var coordinates_px = service.coordinatesToPixels(coordinates, originalSize, offset);
+ var _offset = offset || 0;
+
+ var x = 1 - (coordinates.x1 + Math.abs(coordinates.x2));
+ var left_of_x = originalSize.width * x;
+ var ratio = viewPort.width / left_of_x;
+
+ var style = {
+ position: "absolute",
+ top: -(coordinates_px.y1*ratio)+ _offset,
+ left: -(coordinates_px.x1* ratio)+ _offset,
+ width: Math.floor(originalSize.width * ratio),
+ height: Math.floor(originalSize.height * ratio),
+ originalWidth: originalSize.width,
+ originalHeight: originalSize.height,
+ ratio: ratio
+ };
+
+ return style;
+ },
+
+
+ coordinatesToPixels : function(coordinates, originalSize, offset){
+
+ var coordinates_px = {
+ x1: Math.floor(coordinates.x1 * originalSize.width),
+ y1: Math.floor(coordinates.y1 * originalSize.height),
+ x2: Math.floor(coordinates.x2 * originalSize.width),
+ y2: Math.floor(coordinates.y2 * originalSize.height)
+ };
+
+ return coordinates_px;
+ },
+
+ pixelsToCoordinates : function(image, width, height, offset){
+
+ var x1_px = Math.abs(image.left-offset);
+ var y1_px = Math.abs(image.top-offset);
+
+ var x2_px = image.width - (x1_px + width);
+ var y2_px = image.height - (y1_px + height);
+
+
+ //crop coordinates in %
+ var crop = {};
+ crop.x1 = x1_px / image.width;
+ crop.y1 = y1_px / image.height;
+ crop.x2 = x2_px / image.width;
+ crop.y2 = y2_px / image.height;
+
+ for(var coord in crop){
+ if(crop[coord] < 0){
+ crop[coord] = 0;
+ }
+ }
+
+ return crop;
+ },
+
+ centerInsideViewPort : function(img, viewport){
+ var left = viewport.width/ 2 - img.width / 2,
+ top = viewport.height / 2 - img.height / 2;
+
+ return {left: left, top: top};
+ },
+
+ alignToCoordinates : function(image, center, viewport){
+
+ var min_left = (image.width) - (viewport.width);
+ var min_top = (image.height) - (viewport.height);
+
+ var c_top = -(center.top * image.height) + (viewport.height / 2);
+ var c_left = -(center.left * image.width) + (viewport.width / 2);
+
+ if(c_top < -min_top){
+ c_top = -min_top;
+ }
+ if(c_top > 0){
+ c_top = 0;
+ }
+ if(c_left < -min_left){
+ c_left = -min_left;
+ }
+ if(c_left > 0){
+ c_left = 0;
+ }
+ return {left: c_left, top: c_top};
+ },
+
+
+ syncElements : function(source, target){
+ target.height(source.height());
+ target.width(source.width());
+
+ target.css({
+ "top": source[0].offsetTop,
+ "left": source[0].offsetLeft
+ });
+ }
+ };
+
+ return service;
+}
+
+angular.module('umbraco.services').factory('cropperHelper', cropperHelper);
+
+/**
+ * @ngdoc service
+ * @name umbraco.services.dataTypeHelper
+ * @description A helper service for data types
+ **/
+function dataTypeHelper() {
+
+ var dataTypeHelperService = {
+
+ createPreValueProps: function(preVals) {
+
+ var preValues = [];
+
+ for (var i = 0; i < preVals.length; i++) {
+ preValues.push({
+ hideLabel: preVals[i].hideLabel,
+ alias: preVals[i].key,
+ description: preVals[i].description,
+ label: preVals[i].label,
+ view: preVals[i].view,
+ value: preVals[i].value
+ });
+ }
+
+ return preValues;
+
+ },
+
+ rebindChangedProperties: function (origContent, savedContent) {
+
+ //a method to ignore built-in prop changes
+ var shouldIgnore = function (propName) {
+ return _.some(["notifications", "ModelState"], function (i) {
+ return i === propName;
+ });
+ };
+ //check for changed built-in properties of the content
+ for (var o in origContent) {
+
+ //ignore the ones listed in the array
+ if (shouldIgnore(o)) {
+ continue;
+ }
+
+ if (!_.isEqual(origContent[o], savedContent[o])) {
+ origContent[o] = savedContent[o];
+ }
+ }
+ }
+
+ };
+
+ return dataTypeHelperService;
+}
angular.module('umbraco.services').factory('dataTypeHelper', dataTypeHelper);
/**
* @ngdoc service
@@ -2574,7 +2574,7 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
}
//the first thing any form must do is broadcast the formSubmitting event
- args.scope.$broadcast("formSubmitting", { scope: args.scope });
+ args.scope.$broadcast("formSubmitting", { scope: args.scope, action: args.action });
//then check if the form is valid
if (!args.skipValidation) {
@@ -2734,89 +2734,89 @@ function formHelper(angularHelper, serverValidationManager, $timeout, notificati
};
}
angular.module('umbraco.services').factory('formHelper', formHelper);
-angular.module('umbraco.services')
- .factory('gridService', function ($http, $q){
-
- var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig;
- var service = {
- getGridEditors: function () {
- return $http.get(configPath);
- }
- };
-
- return service;
-
- });
+angular.module('umbraco.services')
+ .factory('gridService', function ($http, $q){
+
+ var configPath = Umbraco.Sys.ServerVariables.umbracoUrls.gridConfig;
+ var service = {
+ getGridEditors: function () {
+ return $http.get(configPath);
+ }
+ };
+
+ return service;
+
+ });
+
+angular.module('umbraco.services')
+ .factory('helpService', function ($http, $q){
+ var helpTopics = {};
+
+ var defaultUrl = "http://our.umbraco.org/rss/help";
+ var tvUrl = "http://umbraco.tv/feeds/help";
+
+ function getCachedHelp(url){
+ if(helpTopics[url]){
+ return helpTopics[cacheKey];
+ }else{
+ return null;
+ }
+ }
+
+ function setCachedHelp(url, data){
+ helpTopics[url] = data;
+ }
+
+ function fetchUrl(url){
+ var deferred = $q.defer();
+ var found = getCachedHelp(url);
+
+ if(found){
+ deferred.resolve(found);
+ }else{
+
+ var proxyUrl = "dashboard/feedproxy.aspx?url=" + url;
+ $http.get(proxyUrl).then(function(data){
+ var feed = $(data.data);
+ var topics = [];
+
+ $('item', feed).each(function (i, item) {
+ var topic = {};
+ topic.thumbnail = $(item).find('thumbnail').attr('url');
+ topic.title = $("title", item).text();
+ topic.link = $("guid", item).text();
+ topic.description = $("description", item).text();
+ topics.push(topic);
+ });
+
+ setCachedHelp(topics);
+ deferred.resolve(topics);
+ });
+ }
+
+ return deferred.promise;
+ }
+
+
+
+ var service = {
+ findHelp: function (args) {
+ var url = service.getUrl(defaultUrl, args);
+ return fetchUrl(url);
+ },
+
+ findVideos: function (args) {
+ var url = service.getUrl(tvUrl, args);
+ return fetchUrl(url);
+ },
+
+ getUrl: function(url, args){
+ return url + "?" + $.param(args);
+ }
+ };
+
+ return service;
-angular.module('umbraco.services')
- .factory('helpService', function ($http, $q){
- var helpTopics = {};
-
- var defaultUrl = "http://our.umbraco.org/rss/help";
- var tvUrl = "http://umbraco.tv/feeds/help";
-
- function getCachedHelp(url){
- if(helpTopics[url]){
- return helpTopics[cacheKey];
- }else{
- return null;
- }
- }
-
- function setCachedHelp(url, data){
- helpTopics[url] = data;
- }
-
- function fetchUrl(url){
- var deferred = $q.defer();
- var found = getCachedHelp(url);
-
- if(found){
- deferred.resolve(found);
- }else{
-
- var proxyUrl = "dashboard/feedproxy.aspx?url=" + url;
- $http.get(proxyUrl).then(function(data){
- var feed = $(data.data);
- var topics = [];
-
- $('item', feed).each(function (i, item) {
- var topic = {};
- topic.thumbnail = $(item).find('thumbnail').attr('url');
- topic.title = $("title", item).text();
- topic.link = $("guid", item).text();
- topic.description = $("description", item).text();
- topics.push(topic);
- });
-
- setCachedHelp(topics);
- deferred.resolve(topics);
- });
- }
-
- return deferred.promise;
- }
-
-
-
- var service = {
- findHelp: function (args) {
- var url = service.getUrl(defaultUrl, args);
- return fetchUrl(url);
- },
-
- findVideos: function (args) {
- var url = service.getUrl(tvUrl, args);
- return fetchUrl(url);
- },
-
- getUrl: function(url, args){
- return url + "?" + $.param(args);
- }
- };
-
- return service;
-
});
/**
* @ngdoc service
@@ -2938,850 +2938,934 @@ angular.module('umbraco.services')
}
};
});
-/**
-* @ngdoc service
-* @name umbraco.services.iconHelper
-* @description A helper service for dealing with icons, mostly dealing with legacy tree icons
-**/
-function iconHelper($q, $timeout) {
-
- var converter = [
- { oldIcon: ".sprNew", newIcon: "add" },
- { oldIcon: ".sprDelete", newIcon: "remove" },
- { oldIcon: ".sprMove", newIcon: "enter" },
- { oldIcon: ".sprCopy", newIcon: "documents" },
- { oldIcon: ".sprSort", newIcon: "navigation-vertical" },
- { oldIcon: ".sprPublish", newIcon: "globe" },
- { oldIcon: ".sprRollback", newIcon: "undo" },
- { oldIcon: ".sprProtect", newIcon: "lock" },
- { oldIcon: ".sprAudit", newIcon: "time" },
- { oldIcon: ".sprNotify", newIcon: "envelope" },
- { oldIcon: ".sprDomain", newIcon: "home" },
- { oldIcon: ".sprPermission", newIcon: "lock" },
- { oldIcon: ".sprRefresh", newIcon: "refresh" },
- { oldIcon: ".sprBinEmpty", newIcon: "trash" },
- { oldIcon: ".sprExportDocumentType", newIcon: "download-alt" },
- { oldIcon: ".sprImportDocumentType", newIcon: "page-up" },
- { oldIcon: ".sprLiveEdit", newIcon: "edit" },
- { oldIcon: ".sprCreateFolder", newIcon: "add" },
- { oldIcon: ".sprPackage2", newIcon: "box" },
- { oldIcon: ".sprLogout", newIcon: "logout" },
- { oldIcon: ".sprSave", newIcon: "save" },
- { oldIcon: ".sprSendToTranslate", newIcon: "envelope-alt" },
- { oldIcon: ".sprToPublish", newIcon: "mail-forward" },
- { oldIcon: ".sprTranslate", newIcon: "comments" },
- { oldIcon: ".sprUpdate", newIcon: "save" },
-
- { oldIcon: ".sprTreeSettingDomain", newIcon: "icon-home" },
- { oldIcon: ".sprTreeDoc", newIcon: "icon-document" },
- { oldIcon: ".sprTreeDoc2", newIcon: "icon-diploma-alt" },
- { oldIcon: ".sprTreeDoc3", newIcon: "icon-notepad" },
- { oldIcon: ".sprTreeDoc4", newIcon: "icon-newspaper-alt" },
- { oldIcon: ".sprTreeDoc5", newIcon: "icon-notepad-alt" },
-
- { oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" },
- { oldIcon: ".sprTreeFolder", newIcon: "icon-folder" },
- { oldIcon: ".sprTreeFolder_o", newIcon: "icon-folder" },
- { oldIcon: ".sprTreeMediaFile", newIcon: "icon-music" },
- { oldIcon: ".sprTreeMediaMovie", newIcon: "icon-movie" },
- { oldIcon: ".sprTreeMediaPhoto", newIcon: "icon-picture" },
-
- { oldIcon: ".sprTreeMember", newIcon: "icon-user" },
- { oldIcon: ".sprTreeMemberGroup", newIcon: "icon-users" },
- { oldIcon: ".sprTreeMemberType", newIcon: "icon-users" },
-
- { oldIcon: ".sprTreeNewsletter", newIcon: "icon-file-text-alt" },
- { oldIcon: ".sprTreePackage", newIcon: "icon-box" },
- { oldIcon: ".sprTreeRepository", newIcon: "icon-server-alt" },
-
- { oldIcon: ".sprTreeSettingDataType", newIcon: "icon-autofill" },
-
- //TODO:
- /*
- { oldIcon: ".sprTreeSettingAgent", newIcon: "" },
- { oldIcon: ".sprTreeSettingCss", newIcon: "" },
- { oldIcon: ".sprTreeSettingCssItem", newIcon: "" },
-
- { oldIcon: ".sprTreeSettingDataTypeChild", newIcon: "" },
- { oldIcon: ".sprTreeSettingDomain", newIcon: "" },
- { oldIcon: ".sprTreeSettingLanguage", newIcon: "" },
- { oldIcon: ".sprTreeSettingScript", newIcon: "" },
- { oldIcon: ".sprTreeSettingTemplate", newIcon: "" },
- { oldIcon: ".sprTreeSettingXml", newIcon: "" },
- { oldIcon: ".sprTreeStatistik", newIcon: "" },
- { oldIcon: ".sprTreeUser", newIcon: "" },
- { oldIcon: ".sprTreeUserGroup", newIcon: "" },
- { oldIcon: ".sprTreeUserType", newIcon: "" },
- */
-
- { oldIcon: "folder.png", newIcon: "icon-folder" },
- { oldIcon: "mediaphoto.gif", newIcon: "icon-picture" },
- { oldIcon: "mediafile.gif", newIcon: "icon-document" },
-
- { oldIcon: ".sprTreeDeveloperCacheItem", newIcon: "icon-box" },
- { oldIcon: ".sprTreeDeveloperCacheTypes", newIcon: "icon-box" },
- { oldIcon: ".sprTreeDeveloperMacro", newIcon: "icon-cogs" },
- { oldIcon: ".sprTreeDeveloperRegistry", newIcon: "icon-windows" },
- { oldIcon: ".sprTreeDeveloperPython", newIcon: "icon-linux" }
- ];
-
- var imageConverter = [
- {oldImage: "contour.png", newIcon: "icon-umb-contour"}
- ];
-
- var collectedIcons;
-
- return {
-
- /** Used by the create dialogs for content/media types to format the data so that the thumbnails are styled properly */
- formatContentTypeThumbnails: function (contentTypes) {
- for (var i = 0; i < contentTypes.length; i++) {
-
- if (contentTypes[i].thumbnailIsClass === undefined || contentTypes[i].thumbnailIsClass) {
- contentTypes[i].cssClass = this.convertFromLegacyIcon(contentTypes[i].thumbnail);
- }else {
- contentTypes[i].style = "background-image: url('" + contentTypes[i].thumbnailFilePath + "');height:36px; background-position:4px 0px; background-repeat: no-repeat;background-size: 35px 35px;";
- //we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this
- contentTypes[i].cssClass = "custom-file";
- }
- }
- return contentTypes;
- },
- formatContentTypeIcons: function (contentTypes) {
- for (var i = 0; i < contentTypes.length; i++) {
- contentTypes[i].icon = this.convertFromLegacyIcon(contentTypes[i].icon);
-
- //couldnt find replacement
- if(contentTypes[i].icon.indexOf(".") > 0){
- contentTypes[i].icon = "icon-document-dashed-line";
- }
- }
- return contentTypes;
- },
- /** If the icon is file based (i.e. it has a file path) */
- isFileBasedIcon: function (icon) {
- //if it doesn't start with a '.' but contains one then we'll assume it's file based
- if (icon.startsWith('..') || (!icon.startsWith('.') && icon.indexOf('.') > 1)) {
- return true;
- }
- return false;
- },
- /** If the icon is legacy */
- isLegacyIcon: function (icon) {
- if(icon.startsWith('..')){
- return false;
- }
-
- if (icon.startsWith('.')) {
- return true;
- }
- return false;
- },
- /** If the tree node has a legacy icon */
- isLegacyTreeNodeIcon: function(treeNode){
- if (treeNode.iconIsClass) {
- return this.isLegacyIcon(treeNode.icon);
- }
- return false;
- },
-
- /** Return a list of icons, optionally filter them */
- /** It fetches them directly from the active stylesheets in the browser */
- getIcons: function(){
- var deferred = $q.defer();
- $timeout(function(){
- if(collectedIcons){
- deferred.resolve(collectedIcons);
- }else{
- collectedIcons = [];
- var c = ".icon-";
-
- for (var i = document.styleSheets.length - 1; i >= 0; i--) {
- var classes = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
-
- if (classes !== null) {
- for(var x=0;x0){
- s = s.substring(0, hasSpace);
- }
- var hasPseudo = s.indexOf(":");
- if(hasPseudo>0){
- s = s.substring(0, hasPseudo);
- }
-
- if(collectedIcons.indexOf(s) < 0){
- collectedIcons.push(s);
- }
- }
- }
- }
- }
- deferred.resolve(collectedIcons);
- }
- }, 100);
-
- return deferred.promise;
- },
-
- /** Converts the icon from legacy to a new one if an old one is detected */
- convertFromLegacyIcon: function (icon) {
- if (this.isLegacyIcon(icon)) {
- //its legacy so convert it if we can
- var found = _.find(converter, function (item) {
- return item.oldIcon.toLowerCase() === icon.toLowerCase();
- });
- return (found ? found.newIcon : icon);
- }
- return icon;
- },
-
- convertFromLegacyImage: function (icon) {
- var found = _.find(imageConverter, function (item) {
- return item.oldImage.toLowerCase() === icon.toLowerCase();
- });
- return (found ? found.newIcon : undefined);
- },
-
- /** If we detect that the tree node has legacy icons that can be converted, this will convert them */
- convertFromLegacyTreeNodeIcon: function (treeNode) {
- if (this.isLegacyTreeNodeIcon(treeNode)) {
- return this.convertFromLegacyIcon(treeNode.icon);
- }
- return treeNode.icon;
- }
- };
-}
+/**
+* @ngdoc service
+* @name umbraco.services.iconHelper
+* @description A helper service for dealing with icons, mostly dealing with legacy tree icons
+**/
+function iconHelper($q, $timeout) {
+
+ var converter = [
+ { oldIcon: ".sprNew", newIcon: "add" },
+ { oldIcon: ".sprDelete", newIcon: "remove" },
+ { oldIcon: ".sprMove", newIcon: "enter" },
+ { oldIcon: ".sprCopy", newIcon: "documents" },
+ { oldIcon: ".sprSort", newIcon: "navigation-vertical" },
+ { oldIcon: ".sprPublish", newIcon: "globe" },
+ { oldIcon: ".sprRollback", newIcon: "undo" },
+ { oldIcon: ".sprProtect", newIcon: "lock" },
+ { oldIcon: ".sprAudit", newIcon: "time" },
+ { oldIcon: ".sprNotify", newIcon: "envelope" },
+ { oldIcon: ".sprDomain", newIcon: "home" },
+ { oldIcon: ".sprPermission", newIcon: "lock" },
+ { oldIcon: ".sprRefresh", newIcon: "refresh" },
+ { oldIcon: ".sprBinEmpty", newIcon: "trash" },
+ { oldIcon: ".sprExportDocumentType", newIcon: "download-alt" },
+ { oldIcon: ".sprImportDocumentType", newIcon: "page-up" },
+ { oldIcon: ".sprLiveEdit", newIcon: "edit" },
+ { oldIcon: ".sprCreateFolder", newIcon: "add" },
+ { oldIcon: ".sprPackage2", newIcon: "box" },
+ { oldIcon: ".sprLogout", newIcon: "logout" },
+ { oldIcon: ".sprSave", newIcon: "save" },
+ { oldIcon: ".sprSendToTranslate", newIcon: "envelope-alt" },
+ { oldIcon: ".sprToPublish", newIcon: "mail-forward" },
+ { oldIcon: ".sprTranslate", newIcon: "comments" },
+ { oldIcon: ".sprUpdate", newIcon: "save" },
+
+ { oldIcon: ".sprTreeSettingDomain", newIcon: "icon-home" },
+ { oldIcon: ".sprTreeDoc", newIcon: "icon-document" },
+ { oldIcon: ".sprTreeDoc2", newIcon: "icon-diploma-alt" },
+ { oldIcon: ".sprTreeDoc3", newIcon: "icon-notepad" },
+ { oldIcon: ".sprTreeDoc4", newIcon: "icon-newspaper-alt" },
+ { oldIcon: ".sprTreeDoc5", newIcon: "icon-notepad-alt" },
+
+ { oldIcon: ".sprTreeDocPic", newIcon: "icon-picture" },
+ { oldIcon: ".sprTreeFolder", newIcon: "icon-folder" },
+ { oldIcon: ".sprTreeFolder_o", newIcon: "icon-folder" },
+ { oldIcon: ".sprTreeMediaFile", newIcon: "icon-music" },
+ { oldIcon: ".sprTreeMediaMovie", newIcon: "icon-movie" },
+ { oldIcon: ".sprTreeMediaPhoto", newIcon: "icon-picture" },
+
+ { oldIcon: ".sprTreeMember", newIcon: "icon-user" },
+ { oldIcon: ".sprTreeMemberGroup", newIcon: "icon-users" },
+ { oldIcon: ".sprTreeMemberType", newIcon: "icon-users" },
+
+ { oldIcon: ".sprTreeNewsletter", newIcon: "icon-file-text-alt" },
+ { oldIcon: ".sprTreePackage", newIcon: "icon-box" },
+ { oldIcon: ".sprTreeRepository", newIcon: "icon-server-alt" },
+
+ { oldIcon: ".sprTreeSettingDataType", newIcon: "icon-autofill" },
+
+ //TODO:
+ /*
+ { oldIcon: ".sprTreeSettingAgent", newIcon: "" },
+ { oldIcon: ".sprTreeSettingCss", newIcon: "" },
+ { oldIcon: ".sprTreeSettingCssItem", newIcon: "" },
+
+ { oldIcon: ".sprTreeSettingDataTypeChild", newIcon: "" },
+ { oldIcon: ".sprTreeSettingDomain", newIcon: "" },
+ { oldIcon: ".sprTreeSettingLanguage", newIcon: "" },
+ { oldIcon: ".sprTreeSettingScript", newIcon: "" },
+ { oldIcon: ".sprTreeSettingTemplate", newIcon: "" },
+ { oldIcon: ".sprTreeSettingXml", newIcon: "" },
+ { oldIcon: ".sprTreeStatistik", newIcon: "" },
+ { oldIcon: ".sprTreeUser", newIcon: "" },
+ { oldIcon: ".sprTreeUserGroup", newIcon: "" },
+ { oldIcon: ".sprTreeUserType", newIcon: "" },
+ */
+
+ { oldIcon: "folder.png", newIcon: "icon-folder" },
+ { oldIcon: "mediaphoto.gif", newIcon: "icon-picture" },
+ { oldIcon: "mediafile.gif", newIcon: "icon-document" },
+
+ { oldIcon: ".sprTreeDeveloperCacheItem", newIcon: "icon-box" },
+ { oldIcon: ".sprTreeDeveloperCacheTypes", newIcon: "icon-box" },
+ { oldIcon: ".sprTreeDeveloperMacro", newIcon: "icon-cogs" },
+ { oldIcon: ".sprTreeDeveloperRegistry", newIcon: "icon-windows" },
+ { oldIcon: ".sprTreeDeveloperPython", newIcon: "icon-linux" }
+ ];
+
+ var imageConverter = [
+ {oldImage: "contour.png", newIcon: "icon-umb-contour"}
+ ];
+
+ var collectedIcons;
+
+ return {
+
+ /** Used by the create dialogs for content/media types to format the data so that the thumbnails are styled properly */
+ formatContentTypeThumbnails: function (contentTypes) {
+ for (var i = 0; i < contentTypes.length; i++) {
+
+ if (contentTypes[i].thumbnailIsClass === undefined || contentTypes[i].thumbnailIsClass) {
+ contentTypes[i].cssClass = this.convertFromLegacyIcon(contentTypes[i].thumbnail);
+ }else {
+ contentTypes[i].style = "background-image: url('" + contentTypes[i].thumbnailFilePath + "');height:36px; background-position:4px 0px; background-repeat: no-repeat;background-size: 35px 35px;";
+ //we need an 'icon-' class in there for certain styles to work so if it is image based we'll add this
+ contentTypes[i].cssClass = "custom-file";
+ }
+ }
+ return contentTypes;
+ },
+ formatContentTypeIcons: function (contentTypes) {
+ for (var i = 0; i < contentTypes.length; i++) {
+ contentTypes[i].icon = this.convertFromLegacyIcon(contentTypes[i].icon);
+
+ //couldnt find replacement
+ if(contentTypes[i].icon.indexOf(".") > 0){
+ contentTypes[i].icon = "icon-document-dashed-line";
+ }
+ }
+ return contentTypes;
+ },
+ /** If the icon is file based (i.e. it has a file path) */
+ isFileBasedIcon: function (icon) {
+ //if it doesn't start with a '.' but contains one then we'll assume it's file based
+ if (icon.startsWith('..') || (!icon.startsWith('.') && icon.indexOf('.') > 1)) {
+ return true;
+ }
+ return false;
+ },
+ /** If the icon is legacy */
+ isLegacyIcon: function (icon) {
+ if(icon.startsWith('..')){
+ return false;
+ }
+
+ if (icon.startsWith('.')) {
+ return true;
+ }
+ return false;
+ },
+ /** If the tree node has a legacy icon */
+ isLegacyTreeNodeIcon: function(treeNode){
+ if (treeNode.iconIsClass) {
+ return this.isLegacyIcon(treeNode.icon);
+ }
+ return false;
+ },
+
+ /** Return a list of icons, optionally filter them */
+ /** It fetches them directly from the active stylesheets in the browser */
+ getIcons: function(){
+ var deferred = $q.defer();
+ $timeout(function(){
+ if(collectedIcons){
+ deferred.resolve(collectedIcons);
+ }else{
+ collectedIcons = [];
+ var c = ".icon-";
+
+ for (var i = document.styleSheets.length - 1; i >= 0; i--) {
+ var classes = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
+
+ if (classes !== null) {
+ for(var x=0;x0){
+ s = s.substring(0, hasSpace);
+ }
+ var hasPseudo = s.indexOf(":");
+ if(hasPseudo>0){
+ s = s.substring(0, hasPseudo);
+ }
+
+ if(collectedIcons.indexOf(s) < 0){
+ collectedIcons.push(s);
+ }
+ }
+ }
+ }
+ }
+ deferred.resolve(collectedIcons);
+ }
+ }, 100);
+
+ return deferred.promise;
+ },
+
+ /** Converts the icon from legacy to a new one if an old one is detected */
+ convertFromLegacyIcon: function (icon) {
+ if (this.isLegacyIcon(icon)) {
+ //its legacy so convert it if we can
+ var found = _.find(converter, function (item) {
+ return item.oldIcon.toLowerCase() === icon.toLowerCase();
+ });
+ return (found ? found.newIcon : icon);
+ }
+ return icon;
+ },
+
+ convertFromLegacyImage: function (icon) {
+ var found = _.find(imageConverter, function (item) {
+ return item.oldImage.toLowerCase() === icon.toLowerCase();
+ });
+ return (found ? found.newIcon : undefined);
+ },
+
+ /** If we detect that the tree node has legacy icons that can be converted, this will convert them */
+ convertFromLegacyTreeNodeIcon: function (treeNode) {
+ if (this.isLegacyTreeNodeIcon(treeNode)) {
+ return this.convertFromLegacyIcon(treeNode.icon);
+ }
+ return treeNode.icon;
+ }
+ };
+}
angular.module('umbraco.services').factory('iconHelper', iconHelper);
-/**
-* @ngdoc service
-* @name umbraco.services.imageHelper
-* @deprecated
-**/
-function imageHelper(umbRequestHelper, mediaHelper) {
- return {
- /**
- * @ngdoc function
- * @name umbraco.services.imageHelper#getImagePropertyValue
- * @methodOf umbraco.services.imageHelper
- * @function
- *
- * @deprecated
- */
- getImagePropertyValue: function (options) {
- return mediaHelper.getImagePropertyValue(options);
- },
- /**
- * @ngdoc function
- * @name umbraco.services.imageHelper#getThumbnail
- * @methodOf umbraco.services.imageHelper
- * @function
- *
- * @deprecated
- */
- getThumbnail: function (options) {
- return mediaHelper.getThumbnail(options);
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.imageHelper#scaleToMaxSize
- * @methodOf umbraco.services.imageHelper
- * @function
- *
- * @deprecated
- */
- scaleToMaxSize: function (maxSize, width, height) {
- return mediaHelper.scaleToMaxSize(maxSize, width, height);
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.imageHelper#getThumbnailFromPath
- * @methodOf umbraco.services.imageHelper
- * @function
- *
- * @deprecated
- */
- getThumbnailFromPath: function (imagePath) {
- return mediaHelper.getThumbnailFromPath(imagePath);
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.imageHelper#detectIfImageByExtension
- * @methodOf umbraco.services.imageHelper
- * @function
- *
- * @deprecated
- */
- detectIfImageByExtension: function (imagePath) {
- return mediaHelper.detectIfImageByExtension(imagePath);
- }
- };
-}
+/**
+* @ngdoc service
+* @name umbraco.services.imageHelper
+* @deprecated
+**/
+function imageHelper(umbRequestHelper, mediaHelper) {
+ return {
+ /**
+ * @ngdoc function
+ * @name umbraco.services.imageHelper#getImagePropertyValue
+ * @methodOf umbraco.services.imageHelper
+ * @function
+ *
+ * @deprecated
+ */
+ getImagePropertyValue: function (options) {
+ return mediaHelper.getImagePropertyValue(options);
+ },
+ /**
+ * @ngdoc function
+ * @name umbraco.services.imageHelper#getThumbnail
+ * @methodOf umbraco.services.imageHelper
+ * @function
+ *
+ * @deprecated
+ */
+ getThumbnail: function (options) {
+ return mediaHelper.getThumbnail(options);
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.imageHelper#scaleToMaxSize
+ * @methodOf umbraco.services.imageHelper
+ * @function
+ *
+ * @deprecated
+ */
+ scaleToMaxSize: function (maxSize, width, height) {
+ return mediaHelper.scaleToMaxSize(maxSize, width, height);
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.imageHelper#getThumbnailFromPath
+ * @methodOf umbraco.services.imageHelper
+ * @function
+ *
+ * @deprecated
+ */
+ getThumbnailFromPath: function (imagePath) {
+ return mediaHelper.getThumbnailFromPath(imagePath);
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.imageHelper#detectIfImageByExtension
+ * @methodOf umbraco.services.imageHelper
+ * @function
+ *
+ * @deprecated
+ */
+ detectIfImageByExtension: function (imagePath) {
+ return mediaHelper.detectIfImageByExtension(imagePath);
+ }
+ };
+}
angular.module('umbraco.services').factory('imageHelper', imageHelper);
// This service was based on OpenJS library available in BSD License
// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php
-angular.module('umbraco.services')
-.factory('keyboardService', ['$window', '$timeout', function ($window, $timeout) {
- var keyboardManagerService = {};
- var defaultOpt = {
- 'type': 'keydown',
- 'propagate': false,
- 'inputDisabled': false,
- 'target': $window.document,
- 'keyCode': false
- };
- var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
+function keyboardService($window, $timeout) {
+
+ var keyboardManagerService = {};
+
+ var defaultOpt = {
+ 'type': 'keydown',
+ 'propagate': false,
+ 'inputDisabled': false,
+ 'target': $window.document,
+ 'keyCode': false
+ };
- // Store all keyboard combination shortcuts
- keyboardManagerService.keyboardEvent = {};
+ // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
+ var shift_nums = {
+ "`": "~",
+ "1": "!",
+ "2": "@",
+ "3": "#",
+ "4": "$",
+ "5": "%",
+ "6": "^",
+ "7": "&",
+ "8": "*",
+ "9": "(",
+ "0": ")",
+ "-": "_",
+ "=": "+",
+ ";": ":",
+ "'": "\"",
+ ",": "<",
+ ".": ">",
+ "/": "?",
+ "\\": "|"
+ };
+ // Special Keys - and their codes
+ var special_keys = {
+ 'esc': 27,
+ 'escape': 27,
+ 'tab': 9,
+ 'space': 32,
+ 'return': 13,
+ 'enter': 13,
+ 'backspace': 8,
- // Add a new keyboard combination shortcut
- keyboardManagerService.bind = function (label, callback, opt) {
+ 'scrolllock': 145,
+ 'scroll_lock': 145,
+ 'scroll': 145,
+ 'capslock': 20,
+ 'caps_lock': 20,
+ 'caps': 20,
+ 'numlock': 144,
+ 'num_lock': 144,
+ 'num': 144,
- //replace ctrl key with meta key
- if(isMac && label !== "ctrl+space"){
- label = label.replace("ctrl","meta");
- }
+ 'pause': 19,
+ 'break': 19,
- var fct, elt, code, k;
- // Initialize opt object
- opt = angular.extend({}, defaultOpt, opt);
- label = label.toLowerCase();
- elt = opt.target;
- if(typeof opt.target === 'string'){
- elt = document.getElementById(opt.target);
- }
+ 'insert': 45,
+ 'home': 36,
+ 'delete': 46,
+ 'end': 35,
+ 'pageup': 33,
+ 'page_up': 33,
+ 'pu': 33,
- fct = function (e) {
- e = e || $window.event;
+ 'pagedown': 34,
+ 'page_down': 34,
+ 'pd': 34,
- // Disable event handler when focus input and textarea
- if (opt['inputDisabled']) {
- var elt;
- if (e.target){
- elt = e.target;
- }else if (e.srcElement){
- elt = e.srcElement;
- }
+ 'left': 37,
+ 'up': 38,
+ 'right': 39,
+ 'down': 40,
- if (elt.nodeType === 3){elt = elt.parentNode;}
- if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA'){return;}
- }
+ 'f1': 112,
+ 'f2': 113,
+ 'f3': 114,
+ 'f4': 115,
+ 'f5': 116,
+ 'f6': 117,
+ 'f7': 118,
+ 'f8': 119,
+ 'f9': 120,
+ 'f10': 121,
+ 'f11': 122,
+ 'f12': 123
+ };
- // Find out which key is pressed
- if (e.keyCode){
- code = e.keyCode;
- }else if (e.which){
- code = e.which;
- }
+ var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;
- var character = String.fromCharCode(code).toLowerCase();
+ // The event handler for bound element events
+ function eventHandler(e) {
+ e = e || $window.event;
- if (code === 188){character = ",";} // If the user presses , when the type is onkeydown
- if (code === 190){character = ".";} // If the user presses , when the type is onkeydown
+ var code, k;
- var keys = label.split("+");
- // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
- var kp = 0;
- // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
- var shift_nums = {
- "`":"~",
- "1":"!",
- "2":"@",
- "3":"#",
- "4":"$",
- "5":"%",
- "6":"^",
- "7":"&",
- "8":"*",
- "9":"(",
- "0":")",
- "-":"_",
- "=":"+",
- ";":":",
- "'":"\"",
- ",":"<",
- ".":">",
- "/":"?",
- "\\":"|"
- };
- // Special Keys - and their codes
- var special_keys = {
- 'esc':27,
- 'escape':27,
- 'tab':9,
- 'space':32,
- 'return':13,
- 'enter':13,
- 'backspace':8,
+ // Find out which key is pressed
+ if (e.keyCode)
+ {
+ code = e.keyCode;
+ }
+ else if (e.which) {
+ code = e.which;
+ }
- 'scrolllock':145,
- 'scroll_lock':145,
- 'scroll':145,
- 'capslock':20,
- 'caps_lock':20,
- 'caps':20,
- 'numlock':144,
- 'num_lock':144,
- 'num':144,
+ var character = String.fromCharCode(code).toLowerCase();
- 'pause':19,
- 'break':19,
+ if (code === 188){character = ",";} // If the user presses , when the type is onkeydown
+ if (code === 190){character = ".";} // If the user presses , when the type is onkeydown
- 'insert':45,
- 'home':36,
- 'delete':46,
- 'end':35,
+ var propagate = true;
- 'pageup':33,
- 'page_up':33,
- 'pu':33,
+ //Now we need to determine which shortcut this event is for, we'll do this by iterating over each
+ //registered shortcut to find the match. We use Find here so that the loop exits as soon
+ //as we've found the one we're looking for
+ _.find(_.keys(keyboardManagerService.keyboardEvent), function(key) {
- 'pagedown':34,
- 'page_down':34,
- 'pd':34,
+ var shortcutLabel = key;
+ var shortcutVal = keyboardManagerService.keyboardEvent[key];
- 'left':37,
- 'up':38,
- 'right':39,
- 'down':40,
+ // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
+ var kp = 0;
- 'f1':112,
- 'f2':113,
- 'f3':114,
- 'f4':115,
- 'f5':116,
- 'f6':117,
- 'f7':118,
- 'f8':119,
- 'f9':120,
- 'f10':121,
- 'f11':122,
- 'f12':123
- };
- // Some modifiers key
- var modifiers = {
- shift: {
- wanted: false,
- pressed: e.shiftKey ? true : false
- },
- ctrl : {
- wanted: false,
- pressed: e.ctrlKey ? true : false
- },
- alt : {
- wanted: false,
- pressed: e.altKey ? true : false
- },
- meta : { //Meta is Mac specific
- wanted: false,
- pressed: e.metaKey ? true : false
- }
- };
- // Foreach keys in label (split on +)
- var l = keys.length;
- for (var i = 0; i < l; i++) {
+ // Some modifiers key
+ var modifiers = {
+ shift: {
+ wanted: false,
+ pressed: e.shiftKey ? true : false
+ },
+ ctrl: {
+ wanted: false,
+ pressed: e.ctrlKey ? true : false
+ },
+ alt: {
+ wanted: false,
+ pressed: e.altKey ? true : false
+ },
+ meta: { //Meta is Mac specific
+ wanted: false,
+ pressed: e.metaKey ? true : false
+ }
+ };
- var k=keys[i];
- switch (k) {
- case 'ctrl':
- case 'control':
- kp++;
- modifiers.ctrl.wanted = true;
- break;
- case 'shift':
- case 'alt':
- case 'meta':
- kp++;
- modifiers[k].wanted = true;
- break;
- }
+ var keys = shortcutLabel.split("+");
+ var opt = shortcutVal.opt;
+ var callback = shortcutVal.callback;
- if (k.length > 1) { // If it is a special key
- if(special_keys[k] === code){
- kp++;
- }
+ // Foreach keys in label (split on +)
+ var l = keys.length;
+ for (var i = 0; i < l; i++) {
- } else if (opt['keyCode']) { // If a specific key is set into the config
- if (opt['keyCode'] === code) {
- kp++;
- }
+ var k = keys[i];
+ switch (k) {
+ case 'ctrl':
+ case 'control':
+ kp++;
+ modifiers.ctrl.wanted = true;
+ break;
+ case 'shift':
+ case 'alt':
+ case 'meta':
+ kp++;
+ modifiers[k].wanted = true;
+ break;
+ }
- } else { // The special keys did not match
- if(character === k) {
- kp++;
- }else {
- if(shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
- character = shift_nums[character];
- if(character === k){
- kp++;
- }
- }
- }
- }
+ if (k.length > 1) { // If it is a special key
+ if (special_keys[k] === code) {
+ kp++;
+ }
+ }
+ else if (opt['keyCode']) { // If a specific key is set into the config
+ if (opt['keyCode'] === code) {
+ kp++;
+ }
+ }
+ else { // The special keys did not match
+ if (character === k) {
+ kp++;
+ }
+ else {
+ if (shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase
+ character = shift_nums[character];
+ if (character === k) {
+ kp++;
+ }
+ }
+ }
+ }
- } //for end
+ } //for end
- if(kp === keys.length &&
- modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
- modifiers.shift.pressed === modifiers.shift.wanted &&
- modifiers.alt.pressed === modifiers.alt.wanted &&
- modifiers.meta.pressed === modifiers.meta.wanted) {
- $timeout(function() {
- callback(e);
- }, 1);
+ if (kp === keys.length &&
+ modifiers.ctrl.pressed === modifiers.ctrl.wanted &&
+ modifiers.shift.pressed === modifiers.shift.wanted &&
+ modifiers.alt.pressed === modifiers.alt.wanted &&
+ modifiers.meta.pressed === modifiers.meta.wanted) {
- if(!opt['propagate']) { // Stop the event
- // e.cancelBubble is supported by IE - this will kill the bubbling process.
- e.cancelBubble = true;
- e.returnValue = false;
+ //found the right callback!
- // e.stopPropagation works in Firefox.
- if (e.stopPropagation) {
- e.stopPropagation();
- e.preventDefault();
- }
- return false;
- }
- }
- };
- // Store shortcut
- keyboardManagerService.keyboardEvent[label] = {
- 'callback': fct,
- 'target': elt,
- 'event': opt['type']
- };
+ // Disable event handler when focus input and textarea
+ if (opt['inputDisabled']) {
+ var elt;
+ if (e.target) {
+ elt = e.target;
+ } else if (e.srcElement) {
+ elt = e.srcElement;
+ }
- //Attach the function with the event
- if(elt.addEventListener){
- elt.addEventListener(opt['type'], fct, false);
- }else if(elt.attachEvent){
- elt.attachEvent('on' + opt['type'], fct);
- }else{
- elt['on' + opt['type']] = fct;
- }
- };
- // Remove the shortcut - just specify the shortcut and I will remove the binding
- keyboardManagerService.unbind = function (label) {
- label = label.toLowerCase();
- var binding = keyboardManagerService.keyboardEvent[label];
- delete(keyboardManagerService.keyboardEvent[label]);
+ if (elt.nodeType === 3) { elt = elt.parentNode; }
+ if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA') {
+ //This exits the Find loop
+ return true;
+ }
+ }
- if(!binding){return;}
+ $timeout(function () {
+ callback(e);
+ }, 1);
- var type = binding['event'],
+ if (!opt['propagate']) { // Stop the event
+ propagate = false;
+ }
+
+ //This exits the Find loop
+ return true;
+ }
+
+ //we haven't found one so continue looking
+ return false;
+
+ });
+
+ // Stop the event if required
+ if (!propagate) {
+ // e.cancelBubble is supported by IE - this will kill the bubbling process.
+ e.cancelBubble = true;
+ e.returnValue = false;
+
+ // e.stopPropagation works in Firefox.
+ if (e.stopPropagation) {
+ e.stopPropagation();
+ e.preventDefault();
+ }
+ return false;
+ }
+ }
+
+ // Store all keyboard combination shortcuts
+ keyboardManagerService.keyboardEvent = {};
+
+ // Add a new keyboard combination shortcut
+ keyboardManagerService.bind = function (label, callback, opt) {
+
+ //replace ctrl key with meta key
+ if(isMac && label !== "ctrl+space"){
+ label = label.replace("ctrl","meta");
+ }
+
+ var elt;
+ // Initialize opt object
+ opt = angular.extend({}, defaultOpt, opt);
+ label = label.toLowerCase();
+ elt = opt.target;
+ if(typeof opt.target === 'string'){
+ elt = document.getElementById(opt.target);
+ }
+
+ //Ensure we aren't double binding to the same element + type otherwise we'll end up multi-binding
+ // and raising events for now reason. So here we'll check if the event is already registered for the element
+ var boundValues = _.values(keyboardManagerService.keyboardEvent);
+ var found = _.find(boundValues, function (i) {
+ return i.target === elt && i.event === opt['type'];
+ });
+
+ // Store shortcut
+ keyboardManagerService.keyboardEvent[label] = {
+ 'callback': callback,
+ 'target': elt,
+ 'opt': opt
+ };
+
+ if (!found) {
+ //Attach the function with the event
+ if (elt.addEventListener) {
+ elt.addEventListener(opt['type'], eventHandler, false);
+ } else if (elt.attachEvent) {
+ elt.attachEvent('on' + opt['type'], eventHandler);
+ } else {
+ elt['on' + opt['type']] = eventHandler;
+ }
+ }
+
+ };
+ // Remove the shortcut - just specify the shortcut and I will remove the binding
+ keyboardManagerService.unbind = function (label) {
+ label = label.toLowerCase();
+ var binding = keyboardManagerService.keyboardEvent[label];
+ delete(keyboardManagerService.keyboardEvent[label]);
+
+ if(!binding){return;}
+
+ var type = binding['event'],
elt = binding['target'],
callback = binding['callback'];
- if(elt.detachEvent){
- elt.detachEvent('on' + type, callback);
- }else if(elt.removeEventListener){
- elt.removeEventListener(type, callback, false);
- }else{
- elt['on'+type] = false;
- }
- };
- //
+ if(elt.detachEvent){
+ elt.detachEvent('on' + type, callback);
+ }else if(elt.removeEventListener){
+ elt.removeEventListener(type, callback, false);
+ }else{
+ elt['on'+type] = false;
+ }
+ };
+ //
- return keyboardManagerService;
-}]);
-(function() {
- 'use strict';
-
- function listViewHelper(localStorageService) {
-
- var firstSelectedIndex = 0;
- var localStorageKey = "umblistViewLayout";
-
- function getLayout(nodeId, availableLayouts) {
-
- var storedLayouts = [];
-
- if(localStorageService.get(localStorageKey)) {
- storedLayouts = localStorageService.get(localStorageKey);
- }
-
- if (storedLayouts && storedLayouts.length > 0) {
- for (var i = 0; storedLayouts.length > i; i++) {
- var layout = storedLayouts[i];
- if (layout.nodeId === nodeId) {
- return setLayout(nodeId, layout, availableLayouts);
- }
- }
-
- }
-
- return getFirstAllowedLayout(availableLayouts);
-
- }
-
- function setLayout(nodeId, selectedLayout, availableLayouts) {
-
- var activeLayout = {};
- var layoutFound = false;
-
- for (var i = 0; availableLayouts.length > i; i++) {
- var layout = availableLayouts[i];
- if (layout.path === selectedLayout.path) {
- activeLayout = layout;
- layout.active = true;
- layoutFound = true;
- } else {
- layout.active = false;
- }
- }
-
- if(!layoutFound) {
- activeLayout = getFirstAllowedLayout(availableLayouts);
- }
-
- saveLayoutInLocalStorage(nodeId, activeLayout);
-
- return activeLayout;
-
- }
-
- function saveLayoutInLocalStorage(nodeId, selectedLayout) {
- var layoutFound = false;
- var storedLayouts = [];
-
- if(localStorageService.get(localStorageKey)) {
- storedLayouts = localStorageService.get(localStorageKey);
- }
-
- if(storedLayouts.length > 0) {
- for(var i = 0; storedLayouts.length > i; i++) {
- var layout = storedLayouts[i];
- if(layout.nodeId === nodeId) {
- layout.path = selectedLayout.path;
- layoutFound = true;
- }
- }
- }
-
- if(!layoutFound) {
- var storageObject = {
- "nodeId": nodeId,
- "path": selectedLayout.path
- };
- storedLayouts.push(storageObject);
- }
-
- localStorageService.set(localStorageKey, storedLayouts);
-
- }
-
- function getFirstAllowedLayout(layouts) {
-
- var firstAllowedLayout = {};
-
- for (var i = 0; layouts.length > i; i++) {
- var layout = layouts[i];
- if (layout.selected === true) {
- firstAllowedLayout = layout;
- break;
- }
- }
-
- return firstAllowedLayout;
- }
-
- function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
-
- var start = 0;
- var end = 0;
- var item = null;
-
- if ($event.shiftKey === true) {
-
- if(selectedIndex > firstSelectedIndex) {
-
- start = firstSelectedIndex;
- end = selectedIndex;
-
- for (; end >= start; start++) {
- item = items[start];
- selectItem(item, selection);
- }
-
- } else {
-
- start = firstSelectedIndex;
- end = selectedIndex;
-
- for (; end <= start; start--) {
- item = items[start];
- selectItem(item, selection);
- }
-
- }
-
- } else {
-
- if(selectedItem.selected) {
- deselectItem(selectedItem, selection);
- } else {
- selectItem(selectedItem, selection);
- }
-
- firstSelectedIndex = selectedIndex;
-
- }
-
- }
-
- function selectItem(item, selection) {
- var isSelected = false;
- for (var i = 0; selection.length > i; i++) {
- var selectedItem = selection[i];
- if (item.id === selectedItem.id) {
- isSelected = true;
- }
- }
- if(!isSelected) {
- selection.push({id: item.id});
- item.selected = true;
- }
- }
-
- function deselectItem(item, selection) {
- for (var i = 0; selection.length > i; i++) {
- var selectedItem = selection[i];
- if (item.id === selectedItem.id) {
- selection.splice(i, 1);
- item.selected = false;
- }
- }
- }
-
- function clearSelection(items, folders, selection) {
-
- var i = 0;
-
- selection.length = 0;
-
- if(angular.isArray(items)) {
- for(i = 0; items.length > i; i++) {
- var item = items[i];
- item.selected = false;
- }
- }
-
- if(angular.isArray(items)) {
- for(i = 0; folders.length > i; i++) {
- var folder = folders[i];
- folder.selected = false;
- }
- }
- }
-
- function selectAllItems(items, selection, $event) {
-
- var checkbox = $event.target;
- var clearSelection = false;
-
- if (!angular.isArray(items)) {
- return;
- }
-
- selection.length = 0;
-
- for (var i = 0; i < items.length; i++) {
-
- var item = items[i];
-
- if (checkbox.checked) {
- selection.push({id: item.id});
- } else {
- clearSelection = true;
- }
-
- item.selected = checkbox.checked;
-
- }
-
- if (clearSelection) {
- selection.length = 0;
- }
-
- }
-
- function isSelectedAll(items, selection) {
-
- var numberOfSelectedItem = 0;
-
- for(var itemIndex = 0; items.length > itemIndex; itemIndex++) {
- var item = items[itemIndex];
-
- for(var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
- var selectedItem = selection[selectedIndex];
-
- if(item.id === selectedItem.id) {
- numberOfSelectedItem++;
- }
- }
-
- }
-
- if(numberOfSelectedItem === items.length) {
- return true;
- }
-
- }
-
-
- function setSortingDirection(col, direction, options) {
- return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
- }
-
-
- function setSorting(field, allow, options) {
- if (allow) {
- options.orderBy = field;
-
- if (options.orderDirection === "desc") {
- options.orderDirection = "asc";
- } else {
- options.orderDirection = "desc";
- }
- }
- }
-
-
-
- var service = {
- getLayout: getLayout,
- getFirstAllowedLayout: getFirstAllowedLayout,
- setLayout: setLayout,
- saveLayoutInLocalStorage: saveLayoutInLocalStorage,
- selectHandler: selectHandler,
- selectItem: selectItem,
- deselectItem: deselectItem,
- clearSelection: clearSelection,
- selectAllItems: selectAllItems,
- isSelectedAll: isSelectedAll,
- setSortingDirection: setSortingDirection,
- setSorting: setSorting
- };
-
- return service;
-
- }
-
-
- angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
-
-
-})();
+ return keyboardManagerService;
+}
angular.module('umbraco.services').factory('keyboardService', ['$window', '$timeout', keyboardService]);
+(function () {
+ 'use strict';
+
+ function listViewHelper(localStorageService) {
+
+ var firstSelectedIndex = 0;
+ var localStorageKey = "umblistViewLayout";
+
+ function getLayout(nodeId, availableLayouts) {
+
+ var storedLayouts = [];
+
+ if (localStorageService.get(localStorageKey)) {
+ storedLayouts = localStorageService.get(localStorageKey);
+ }
+
+ if (storedLayouts && storedLayouts.length > 0) {
+ for (var i = 0; storedLayouts.length > i; i++) {
+ var layout = storedLayouts[i];
+ if (layout.nodeId === nodeId) {
+ return setLayout(nodeId, layout, availableLayouts);
+ }
+ }
+
+ }
+
+ return getFirstAllowedLayout(availableLayouts);
+
+ }
+
+ function setLayout(nodeId, selectedLayout, availableLayouts) {
+
+ var activeLayout = {};
+ var layoutFound = false;
+
+ for (var i = 0; availableLayouts.length > i; i++) {
+ var layout = availableLayouts[i];
+ if (layout.path === selectedLayout.path) {
+ activeLayout = layout;
+ layout.active = true;
+ layoutFound = true;
+ } else {
+ layout.active = false;
+ }
+ }
+
+ if (!layoutFound) {
+ activeLayout = getFirstAllowedLayout(availableLayouts);
+ }
+
+ saveLayoutInLocalStorage(nodeId, activeLayout);
+
+ return activeLayout;
+
+ }
+
+ function saveLayoutInLocalStorage(nodeId, selectedLayout) {
+ var layoutFound = false;
+ var storedLayouts = [];
+
+ if (localStorageService.get(localStorageKey)) {
+ storedLayouts = localStorageService.get(localStorageKey);
+ }
+
+ if (storedLayouts.length > 0) {
+ for (var i = 0; storedLayouts.length > i; i++) {
+ var layout = storedLayouts[i];
+ if (layout.nodeId === nodeId) {
+ layout.path = selectedLayout.path;
+ layoutFound = true;
+ }
+ }
+ }
+
+ if (!layoutFound) {
+ var storageObject = {
+ "nodeId": nodeId,
+ "path": selectedLayout.path
+ };
+ storedLayouts.push(storageObject);
+ }
+
+ localStorageService.set(localStorageKey, storedLayouts);
+
+ }
+
+ function getFirstAllowedLayout(layouts) {
+
+ var firstAllowedLayout = {};
+
+ for (var i = 0; layouts.length > i; i++) {
+ var layout = layouts[i];
+ if (layout.selected === true) {
+ firstAllowedLayout = layout;
+ break;
+ }
+ }
+
+ return firstAllowedLayout;
+ }
+
+ function selectHandler(selectedItem, selectedIndex, items, selection, $event) {
+
+ var start = 0;
+ var end = 0;
+ var item = null;
+
+ if ($event.shiftKey === true) {
+
+ if (selectedIndex > firstSelectedIndex) {
+
+ start = firstSelectedIndex;
+ end = selectedIndex;
+
+ for (; end >= start; start++) {
+ item = items[start];
+ selectItem(item, selection);
+ }
+
+ } else {
+
+ start = firstSelectedIndex;
+ end = selectedIndex;
+
+ for (; end <= start; start--) {
+ item = items[start];
+ selectItem(item, selection);
+ }
+
+ }
+
+ } else {
+
+ if (selectedItem.selected) {
+ deselectItem(selectedItem, selection);
+ } else {
+ selectItem(selectedItem, selection);
+ }
+
+ firstSelectedIndex = selectedIndex;
+
+ }
+
+ }
+
+ function selectItem(item, selection) {
+ var isSelected = false;
+ for (var i = 0; selection.length > i; i++) {
+ var selectedItem = selection[i];
+ if (item.id === selectedItem.id) {
+ isSelected = true;
+ }
+ }
+ if (!isSelected) {
+ selection.push({ id: item.id });
+ item.selected = true;
+ }
+ }
+
+ function deselectItem(item, selection) {
+ for (var i = 0; selection.length > i; i++) {
+ var selectedItem = selection[i];
+ if (item.id === selectedItem.id) {
+ selection.splice(i, 1);
+ item.selected = false;
+ }
+ }
+ }
+
+ function clearSelection(items, folders, selection) {
+
+ var i = 0;
+
+ selection.length = 0;
+
+ if (angular.isArray(items)) {
+ for (i = 0; items.length > i; i++) {
+ var item = items[i];
+ item.selected = false;
+ }
+ }
+
+ if (angular.isArray(items)) {
+ for (i = 0; folders.length > i; i++) {
+ var folder = folders[i];
+ folder.selected = false;
+ }
+ }
+ }
+
+ function selectAllItems(items, selection, $event) {
+
+ var checkbox = $event.target;
+ var clearSelection = false;
+
+ if (!angular.isArray(items)) {
+ return;
+ }
+
+ selection.length = 0;
+
+ for (var i = 0; i < items.length; i++) {
+
+ var item = items[i];
+
+ if (checkbox.checked) {
+ selection.push({ id: item.id });
+ } else {
+ clearSelection = true;
+ }
+
+ item.selected = checkbox.checked;
+
+ }
+
+ if (clearSelection) {
+ selection.length = 0;
+ }
+
+ }
+
+ function isSelectedAll(items, selection) {
+
+ var numberOfSelectedItem = 0;
+
+ for (var itemIndex = 0; items.length > itemIndex; itemIndex++) {
+ var item = items[itemIndex];
+
+ for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
+ var selectedItem = selection[selectedIndex];
+
+ if (item.id === selectedItem.id) {
+ numberOfSelectedItem++;
+ }
+ }
+
+ }
+
+ if (numberOfSelectedItem === items.length) {
+ return true;
+ }
+
+ }
+
+ function setSortingDirection(col, direction, options) {
+ return options.orderBy.toUpperCase() === col.toUpperCase() && options.orderDirection === direction;
+ }
+
+ function setSorting(field, allow, options) {
+ if (allow) {
+ options.orderBy = field;
+
+ if (options.orderDirection === "desc") {
+ options.orderDirection = "asc";
+ } else {
+ options.orderDirection = "desc";
+ }
+ }
+ }
+
+ //This takes in a dictionary of Ids with Permissions and determines
+ // the intersect of all permissions to return an object representing the
+ // listview button permissions
+ function getButtonPermissions(unmergedPermissions, currentIdsWithPermissions) {
+
+ if (currentIdsWithPermissions == null) {
+ currentIdsWithPermissions = {};
+ }
+
+ //merge the newly retrieved permissions to the main dictionary
+ _.each(unmergedPermissions, function (value, key, list) {
+ currentIdsWithPermissions[key] = value;
+ });
+
+ //get the intersect permissions
+ var arr = [];
+ _.each(currentIdsWithPermissions, function (value, key, list) {
+ arr.push(value);
+ });
+
+ //we need to use 'apply' to call intersection with an array of arrays,
+ //see: http://stackoverflow.com/a/16229480/694494
+ var intersectPermissions = _.intersection.apply(_, arr);
+
+ return {
+ canCopy: _.contains(intersectPermissions, 'O'), //Magic Char = O
+ canCreate: _.contains(intersectPermissions, 'C'), //Magic Char = C
+ canDelete: _.contains(intersectPermissions, 'D'), //Magic Char = D
+ canMove: _.contains(intersectPermissions, 'M'), //Magic Char = M
+ canPublish: _.contains(intersectPermissions, 'U'), //Magic Char = U
+ canUnpublish: _.contains(intersectPermissions, 'U'), //Magic Char = Z (however UI says it can't be set, so if we can publish 'U' we can unpublish)
+ };
+ }
+
+ var service = {
+ getLayout: getLayout,
+ getFirstAllowedLayout: getFirstAllowedLayout,
+ setLayout: setLayout,
+ saveLayoutInLocalStorage: saveLayoutInLocalStorage,
+ selectHandler: selectHandler,
+ selectItem: selectItem,
+ deselectItem: deselectItem,
+ clearSelection: clearSelection,
+ selectAllItems: selectAllItems,
+ isSelectedAll: isSelectedAll,
+ setSortingDirection: setSortingDirection,
+ setSorting: setSorting,
+ getButtonPermissions: getButtonPermissions
+ };
+
+ return service;
+
+ }
+
+
+ angular.module('umbraco.services').factory('listViewHelper', listViewHelper);
+
+
+})();
angular.module('umbraco.services')
.factory('localizationService', function ($http, $q, eventsService, $window, $filter, userService) {
@@ -4108,377 +4192,377 @@ function macroService() {
angular.module('umbraco.services').factory('macroService', macroService);
-/**
-* @ngdoc service
-* @name umbraco.services.mediaHelper
-* @description A helper object used for dealing with media items
-**/
-function mediaHelper(umbRequestHelper) {
-
- //container of fileresolvers
- var _mediaFileResolvers = {};
-
- return {
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#getImagePropertyValue
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Returns the file path associated with the media property if there is one
- *
- * @param {object} options Options object
- * @param {object} options.mediaModel The media object to retrieve the image path from
- * @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image
- */
- getMediaPropertyValue: function (options) {
- if (!options || !options.mediaModel) {
- throw "The options objet does not contain the required parameters: mediaModel";
- }
-
- //combine all props, TODO: we really need a better way then this
- var props = [];
- if (options.mediaModel.properties) {
- props = options.mediaModel.properties;
- } else {
- $(options.mediaModel.tabs).each(function (i, tab) {
- props = props.concat(tab.properties);
- });
- }
-
- var mediaRoot = Umbraco.Sys.ServerVariables.umbracoSettings.mediaPath;
- var imageProp = _.find(props, function (item) {
- if (item.alias === "umbracoFile") {
- return true;
- }
-
- //this performs a simple check to see if we have a media file as value
- //it doesnt catch everything, but better then nothing
- if (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) {
- return true;
- }
-
- return false;
- });
-
- if (!imageProp) {
- return "";
- }
-
- var mediaVal;
-
- //our default images might store one or many images (as csv)
- var split = imageProp.value.split(',');
- var self = this;
- mediaVal = _.map(split, function (item) {
- return { file: item, isImage: self.detectIfImageByExtension(item) };
- });
-
- //for now we'll just return the first image in the collection.
- //TODO: we should enable returning many to be displayed in the picker if the uploader supports many.
- if (mediaVal.length && mediaVal.length > 0) {
- if (!options.imageOnly || (options.imageOnly === true && mediaVal[0].isImage)) {
- return mediaVal[0].file;
- }
- }
-
- return "";
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#getImagePropertyValue
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Returns the actual image path associated with the image property if there is one
- *
- * @param {object} options Options object
- * @param {object} options.imageModel The media object to retrieve the image path from
- */
- getImagePropertyValue: function (options) {
- if (!options || (!options.imageModel && !options.mediaModel)) {
- throw "The options objet does not contain the required parameters: imageModel";
- }
-
- //required to support backwards compatibility.
- options.mediaModel = options.imageModel ? options.imageModel : options.mediaModel;
-
- options.imageOnly = true;
-
- return this.getMediaPropertyValue(options);
- },
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#getThumbnail
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * formats the display model used to display the content to the model used to save the content
- *
- * @param {object} options Options object
- * @param {object} options.imageModel The media object to retrieve the image path from
- */
- getThumbnail: function (options) {
-
- if (!options || !options.imageModel) {
- throw "The options objet does not contain the required parameters: imageModel";
- }
-
- var imagePropVal = this.getImagePropertyValue(options);
- if (imagePropVal !== "") {
- return this.getThumbnailFromPath(imagePropVal);
- }
- return "";
- },
-
- registerFileResolver: function(propertyEditorAlias, func){
- _mediaFileResolvers[propertyEditorAlias] = func;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#resolveFileFromEntity
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Gets the media file url for a media entity returned with the entityResource
- *
- * @param {object} mediaEntity A media Entity returned from the entityResource
- * @param {boolean} thumbnail Whether to return the thumbnail url or normal url
- */
- resolveFileFromEntity : function(mediaEntity, thumbnail) {
-
- if (!angular.isObject(mediaEntity.metaData)) {
- throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData";
- }
-
- var values = _.values(mediaEntity.metaData);
- for (var i = 0; i < values.length; i++) {
- var val = values[i];
- if (angular.isObject(val) && val.PropertyEditorAlias) {
- for (var resolver in _mediaFileResolvers) {
- if (val.PropertyEditorAlias === resolver) {
- //we need to format a property variable that coincides with how the property would be structured
- // if it came from the mediaResource just to keep things slightly easier for the file resolvers.
- var property = { value: val.Value };
-
- return _mediaFileResolvers[resolver](property, mediaEntity, thumbnail);
- }
- }
- }
- }
-
- return "";
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#resolveFile
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Gets the media file url for a media object returned with the mediaResource
- *
- * @param {object} mediaEntity A media Entity returned from the entityResource
- * @param {boolean} thumbnail Whether to return the thumbnail url or normal url
- */
- /*jshint loopfunc: true */
- resolveFile : function(mediaItem, thumbnail){
-
- function iterateProps(props){
- var res = null;
- for(var resolver in _mediaFileResolvers) {
- var property = _.find(props, function(prop){ return prop.editor === resolver; });
- if(property){
- res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail);
- break;
- }
- }
-
- return res;
- }
-
- //we either have properties raw on the object, or spread out on tabs
- var result = "";
- if(mediaItem.properties){
- result = iterateProps(mediaItem.properties);
- }else if(mediaItem.tabs){
- for(var tab in mediaItem.tabs) {
- if(mediaItem.tabs[tab].properties){
- result = iterateProps(mediaItem.tabs[tab].properties);
- if(result){
- break;
- }
- }
- }
- }
- return result;
- },
-
- /*jshint loopfunc: true */
- hasFilePropertyType : function(mediaItem){
- function iterateProps(props){
- var res = false;
- for(var resolver in _mediaFileResolvers) {
- var property = _.find(props, function(prop){ return prop.editor === resolver; });
- if(property){
- res = true;
- break;
- }
- }
- return res;
- }
-
- //we either have properties raw on the object, or spread out on tabs
- var result = false;
- if(mediaItem.properties){
- result = iterateProps(mediaItem.properties);
- }else if(mediaItem.tabs){
- for(var tab in mediaItem.tabs) {
- if(mediaItem.tabs[tab].properties){
- result = iterateProps(mediaItem.tabs[tab].properties);
- if(result){
- break;
- }
- }
- }
- }
- return result;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#scaleToMaxSize
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Finds the corrct max width and max height, given maximum dimensions and keeping aspect ratios
- *
- * @param {number} maxSize Maximum width & height
- * @param {number} width Current width
- * @param {number} height Current height
- */
- scaleToMaxSize: function (maxSize, width, height) {
- var retval = { width: width, height: height };
-
- var maxWidth = maxSize; // Max width for the image
- var maxHeight = maxSize; // Max height for the image
- var ratio = 0; // Used for aspect ratio
-
- // Check if the current width is larger than the max
- if (width > maxWidth) {
- ratio = maxWidth / width; // get ratio for scaling image
-
- retval.width = maxWidth;
- retval.height = height * ratio;
-
- height = height * ratio; // Reset height to match scaled image
- width = width * ratio; // Reset width to match scaled image
- }
-
- // Check if current height is larger than max
- if (height > maxHeight) {
- ratio = maxHeight / height; // get ratio for scaling image
-
- retval.height = maxHeight;
- retval.width = width * ratio;
- width = width * ratio; // Reset width to match scaled image
- }
-
- return retval;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#getThumbnailFromPath
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Returns the path to the thumbnail version of a given media library image path
- *
- * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
- */
- getThumbnailFromPath: function (imagePath) {
-
- //If the path is not an image we cannot get a thumb
- if (!this.detectIfImageByExtension(imagePath)) {
- return null;
- }
-
- //get the proxy url for big thumbnails (this ensures one is always generated)
- var thumbnailUrl = umbRequestHelper.getApiUrl(
- "imagesApiBaseUrl",
- "GetBigThumbnail",
- [{ originalImagePath: imagePath }]);
-
- //var ext = imagePath.substr(imagePath.lastIndexOf('.'));
- //return imagePath.substr(0, imagePath.lastIndexOf('.')) + "_big-thumb" + ".jpg";
-
- return thumbnailUrl;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#detectIfImageByExtension
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Returns true/false, indicating if the given path has an allowed image extension
- *
- * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
- */
- detectIfImageByExtension: function (imagePath) {
-
- if (!imagePath) {
- return false;
- }
-
- var lowered = imagePath.toLowerCase();
- var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
- return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
- },
-
- /**
- * @ngdoc function
- * @name umbraco.services.mediaHelper#formatFileTypes
- * @methodOf umbraco.services.mediaHelper
- * @function
- *
- * @description
- * Returns a string with correctly formated file types for ng-file-upload
- *
- * @param {string} file types, ex: jpg,png,tiff
- */
- formatFileTypes: function(fileTypes) {
-
- var fileTypesArray = fileTypes.split(',');
- var newFileTypesArray = [];
-
- for (var i = 0; i < fileTypesArray.length; i++) {
- var fileType = fileTypesArray[i];
-
- if (fileType.indexOf(".") !== 0) {
- fileType = ".".concat(fileType);
- }
-
- newFileTypesArray.push(fileType);
- }
-
- return newFileTypesArray.join(",");
-
- }
-
- };
-}angular.module('umbraco.services').factory('mediaHelper', mediaHelper);
+/**
+* @ngdoc service
+* @name umbraco.services.mediaHelper
+* @description A helper object used for dealing with media items
+**/
+function mediaHelper(umbRequestHelper) {
+
+ //container of fileresolvers
+ var _mediaFileResolvers = {};
+
+ return {
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#getImagePropertyValue
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Returns the file path associated with the media property if there is one
+ *
+ * @param {object} options Options object
+ * @param {object} options.mediaModel The media object to retrieve the image path from
+ * @param {object} options.imageOnly Optional, if true then will only return a path if the media item is an image
+ */
+ getMediaPropertyValue: function (options) {
+ if (!options || !options.mediaModel) {
+ throw "The options objet does not contain the required parameters: mediaModel";
+ }
+
+ //combine all props, TODO: we really need a better way then this
+ var props = [];
+ if (options.mediaModel.properties) {
+ props = options.mediaModel.properties;
+ } else {
+ $(options.mediaModel.tabs).each(function (i, tab) {
+ props = props.concat(tab.properties);
+ });
+ }
+
+ var mediaRoot = Umbraco.Sys.ServerVariables.umbracoSettings.mediaPath;
+ var imageProp = _.find(props, function (item) {
+ if (item.alias === "umbracoFile") {
+ return true;
+ }
+
+ //this performs a simple check to see if we have a media file as value
+ //it doesnt catch everything, but better then nothing
+ if (angular.isString(item.value) && item.value.indexOf(mediaRoot) === 0) {
+ return true;
+ }
+
+ return false;
+ });
+
+ if (!imageProp) {
+ return "";
+ }
+
+ var mediaVal;
+
+ //our default images might store one or many images (as csv)
+ var split = imageProp.value.split(',');
+ var self = this;
+ mediaVal = _.map(split, function (item) {
+ return { file: item, isImage: self.detectIfImageByExtension(item) };
+ });
+
+ //for now we'll just return the first image in the collection.
+ //TODO: we should enable returning many to be displayed in the picker if the uploader supports many.
+ if (mediaVal.length && mediaVal.length > 0) {
+ if (!options.imageOnly || (options.imageOnly === true && mediaVal[0].isImage)) {
+ return mediaVal[0].file;
+ }
+ }
+
+ return "";
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#getImagePropertyValue
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Returns the actual image path associated with the image property if there is one
+ *
+ * @param {object} options Options object
+ * @param {object} options.imageModel The media object to retrieve the image path from
+ */
+ getImagePropertyValue: function (options) {
+ if (!options || (!options.imageModel && !options.mediaModel)) {
+ throw "The options objet does not contain the required parameters: imageModel";
+ }
+
+ //required to support backwards compatibility.
+ options.mediaModel = options.imageModel ? options.imageModel : options.mediaModel;
+
+ options.imageOnly = true;
+
+ return this.getMediaPropertyValue(options);
+ },
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#getThumbnail
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * formats the display model used to display the content to the model used to save the content
+ *
+ * @param {object} options Options object
+ * @param {object} options.imageModel The media object to retrieve the image path from
+ */
+ getThumbnail: function (options) {
+
+ if (!options || !options.imageModel) {
+ throw "The options objet does not contain the required parameters: imageModel";
+ }
+
+ var imagePropVal = this.getImagePropertyValue(options);
+ if (imagePropVal !== "") {
+ return this.getThumbnailFromPath(imagePropVal);
+ }
+ return "";
+ },
+
+ registerFileResolver: function(propertyEditorAlias, func){
+ _mediaFileResolvers[propertyEditorAlias] = func;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#resolveFileFromEntity
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Gets the media file url for a media entity returned with the entityResource
+ *
+ * @param {object} mediaEntity A media Entity returned from the entityResource
+ * @param {boolean} thumbnail Whether to return the thumbnail url or normal url
+ */
+ resolveFileFromEntity : function(mediaEntity, thumbnail) {
+
+ if (!angular.isObject(mediaEntity.metaData)) {
+ throw "Cannot resolve the file url from the mediaEntity, it does not contain the required metaData";
+ }
+
+ var values = _.values(mediaEntity.metaData);
+ for (var i = 0; i < values.length; i++) {
+ var val = values[i];
+ if (angular.isObject(val) && val.PropertyEditorAlias) {
+ for (var resolver in _mediaFileResolvers) {
+ if (val.PropertyEditorAlias === resolver) {
+ //we need to format a property variable that coincides with how the property would be structured
+ // if it came from the mediaResource just to keep things slightly easier for the file resolvers.
+ var property = { value: val.Value };
+
+ return _mediaFileResolvers[resolver](property, mediaEntity, thumbnail);
+ }
+ }
+ }
+ }
+
+ return "";
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#resolveFile
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Gets the media file url for a media object returned with the mediaResource
+ *
+ * @param {object} mediaEntity A media Entity returned from the entityResource
+ * @param {boolean} thumbnail Whether to return the thumbnail url or normal url
+ */
+ /*jshint loopfunc: true */
+ resolveFile : function(mediaItem, thumbnail){
+
+ function iterateProps(props){
+ var res = null;
+ for(var resolver in _mediaFileResolvers) {
+ var property = _.find(props, function(prop){ return prop.editor === resolver; });
+ if(property){
+ res = _mediaFileResolvers[resolver](property, mediaItem, thumbnail);
+ break;
+ }
+ }
+
+ return res;
+ }
+
+ //we either have properties raw on the object, or spread out on tabs
+ var result = "";
+ if(mediaItem.properties){
+ result = iterateProps(mediaItem.properties);
+ }else if(mediaItem.tabs){
+ for(var tab in mediaItem.tabs) {
+ if(mediaItem.tabs[tab].properties){
+ result = iterateProps(mediaItem.tabs[tab].properties);
+ if(result){
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ },
+
+ /*jshint loopfunc: true */
+ hasFilePropertyType : function(mediaItem){
+ function iterateProps(props){
+ var res = false;
+ for(var resolver in _mediaFileResolvers) {
+ var property = _.find(props, function(prop){ return prop.editor === resolver; });
+ if(property){
+ res = true;
+ break;
+ }
+ }
+ return res;
+ }
+
+ //we either have properties raw on the object, or spread out on tabs
+ var result = false;
+ if(mediaItem.properties){
+ result = iterateProps(mediaItem.properties);
+ }else if(mediaItem.tabs){
+ for(var tab in mediaItem.tabs) {
+ if(mediaItem.tabs[tab].properties){
+ result = iterateProps(mediaItem.tabs[tab].properties);
+ if(result){
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#scaleToMaxSize
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Finds the corrct max width and max height, given maximum dimensions and keeping aspect ratios
+ *
+ * @param {number} maxSize Maximum width & height
+ * @param {number} width Current width
+ * @param {number} height Current height
+ */
+ scaleToMaxSize: function (maxSize, width, height) {
+ var retval = { width: width, height: height };
+
+ var maxWidth = maxSize; // Max width for the image
+ var maxHeight = maxSize; // Max height for the image
+ var ratio = 0; // Used for aspect ratio
+
+ // Check if the current width is larger than the max
+ if (width > maxWidth) {
+ ratio = maxWidth / width; // get ratio for scaling image
+
+ retval.width = maxWidth;
+ retval.height = height * ratio;
+
+ height = height * ratio; // Reset height to match scaled image
+ width = width * ratio; // Reset width to match scaled image
+ }
+
+ // Check if current height is larger than max
+ if (height > maxHeight) {
+ ratio = maxHeight / height; // get ratio for scaling image
+
+ retval.height = maxHeight;
+ retval.width = width * ratio;
+ width = width * ratio; // Reset width to match scaled image
+ }
+
+ return retval;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#getThumbnailFromPath
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Returns the path to the thumbnail version of a given media library image path
+ *
+ * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
+ */
+ getThumbnailFromPath: function (imagePath) {
+
+ //If the path is not an image we cannot get a thumb
+ if (!this.detectIfImageByExtension(imagePath)) {
+ return null;
+ }
+
+ //get the proxy url for big thumbnails (this ensures one is always generated)
+ var thumbnailUrl = umbRequestHelper.getApiUrl(
+ "imagesApiBaseUrl",
+ "GetBigThumbnail",
+ [{ originalImagePath: imagePath }]);
+
+ //var ext = imagePath.substr(imagePath.lastIndexOf('.'));
+ //return imagePath.substr(0, imagePath.lastIndexOf('.')) + "_big-thumb" + ".jpg";
+
+ return thumbnailUrl;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#detectIfImageByExtension
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Returns true/false, indicating if the given path has an allowed image extension
+ *
+ * @param {string} imagePath Image path, ex: /media/1234/my-image.jpg
+ */
+ detectIfImageByExtension: function (imagePath) {
+
+ if (!imagePath) {
+ return false;
+ }
+
+ var lowered = imagePath.toLowerCase();
+ var ext = lowered.substr(lowered.lastIndexOf(".") + 1);
+ return ("," + Umbraco.Sys.ServerVariables.umbracoSettings.imageFileTypes + ",").indexOf("," + ext + ",") !== -1;
+ },
+
+ /**
+ * @ngdoc function
+ * @name umbraco.services.mediaHelper#formatFileTypes
+ * @methodOf umbraco.services.mediaHelper
+ * @function
+ *
+ * @description
+ * Returns a string with correctly formated file types for ng-file-upload
+ *
+ * @param {string} file types, ex: jpg,png,tiff
+ */
+ formatFileTypes: function(fileTypes) {
+
+ var fileTypesArray = fileTypes.split(',');
+ var newFileTypesArray = [];
+
+ for (var i = 0; i < fileTypesArray.length; i++) {
+ var fileType = fileTypesArray[i];
+
+ if (fileType.indexOf(".") !== 0) {
+ fileType = ".".concat(fileType);
+ }
+
+ newFileTypesArray.push(fileType);
+ }
+
+ return newFileTypesArray.join(",");
+
+ }
+
+ };
+}angular.module('umbraco.services').factory('mediaHelper', mediaHelper);
/**
* @ngdoc service
@@ -4557,741 +4641,741 @@ function umbracoMenuActions($q, treeService, $location, navigationService, appSt
}
angular.module('umbraco.services').factory('umbracoMenuActions', umbracoMenuActions);
-/**
- * @ngdoc service
- * @name umbraco.services.navigationService
- *
- * @requires $rootScope
- * @requires $routeParams
- * @requires $log
- * @requires $location
- * @requires dialogService
- * @requires treeService
- * @requires sectionResource
- *
- * @description
- * Service to handle the main application navigation. Responsible for invoking the tree
- * Section navigation and search, and maintain their state for the entire application lifetime
- *
- */
-function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) {
-
-
- //used to track the current dialog object
- var currentDialog = null;
-
- //the main tree event handler, which gets assigned via the setupTreeEvents method
- var mainTreeEventHandler = null;
- //tracks the user profile dialog
- var userDialog = null;
-
- function setMode(mode) {
- switch (mode) {
- case 'tree':
- appState.setGlobalState("navMode", "tree");
- appState.setGlobalState("showNavigation", true);
- appState.setMenuState("showMenu", false);
- appState.setMenuState("showMenuDialog", false);
- appState.setGlobalState("stickyNavigation", false);
- appState.setGlobalState("showTray", false);
-
- //$("#search-form input").focus();
- break;
- case 'menu':
- appState.setGlobalState("navMode", "menu");
- appState.setGlobalState("showNavigation", true);
- appState.setMenuState("showMenu", true);
- appState.setMenuState("showMenuDialog", false);
- appState.setGlobalState("stickyNavigation", true);
- break;
- case 'dialog':
- appState.setGlobalState("navMode", "dialog");
- appState.setGlobalState("stickyNavigation", true);
- appState.setGlobalState("showNavigation", true);
- appState.setMenuState("showMenu", false);
- appState.setMenuState("showMenuDialog", true);
- break;
- case 'search':
- appState.setGlobalState("navMode", "search");
- appState.setGlobalState("stickyNavigation", false);
- appState.setGlobalState("showNavigation", true);
- appState.setMenuState("showMenu", false);
- appState.setSectionState("showSearchResults", true);
- appState.setMenuState("showMenuDialog", false);
-
- //TODO: This would be much better off in the search field controller listening to appState changes
- $timeout(function() {
- $("#search-field").focus();
- });
-
- break;
- default:
- appState.setGlobalState("navMode", "default");
- appState.setMenuState("showMenu", false);
- appState.setMenuState("showMenuDialog", false);
- appState.setSectionState("showSearchResults", false);
- appState.setGlobalState("stickyNavigation", false);
- appState.setGlobalState("showTray", false);
-
- if (appState.getGlobalState("isTablet") === true) {
- appState.setGlobalState("showNavigation", false);
- }
-
- break;
- }
- }
-
- var service = {
-
- /** initializes the navigation service */
- init: function() {
-
- //keep track of the current section - initially this will always be undefined so
- // no point in setting it now until it changes.
- $rootScope.$watch(function () {
- return $routeParams.section;
- }, function (newVal, oldVal) {
- appState.setSectionState("currentSection", newVal);
- });
-
-
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#load
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Shows the legacy iframe and loads in the content based on the source url
- * @param {String} source The URL to load into the iframe
- */
- loadLegacyIFrame: function (source) {
- $location.path("/" + appState.getSectionState("currentSection") + "/framed/" + encodeURIComponent(source));
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#changeSection
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Changes the active section to a given section alias
- * If the navigation is 'sticky' this will load the associated tree
- * and load the dashboard related to the section
- * @param {string} sectionAlias The alias of the section
- */
- changeSection: function(sectionAlias, force) {
- setMode("default-opensection");
-
- if (force && appState.getSectionState("currentSection") === sectionAlias) {
- appState.setSectionState("currentSection", "");
- }
-
- appState.setSectionState("currentSection", sectionAlias);
- this.showTree(sectionAlias);
-
- $location.path(sectionAlias);
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showTree
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Displays the tree for a given section alias but turning on the containing dom element
- * only changes if the section is different from the current one
- * @param {string} sectionAlias The alias of the section to load
- * @param {Object} syncArgs Optional object of arguments for syncing the tree for the section being shown
- */
- showTree: function (sectionAlias, syncArgs) {
- if (sectionAlias !== appState.getSectionState("currentSection")) {
- appState.setSectionState("currentSection", sectionAlias);
-
- if (syncArgs) {
- this.syncTree(syncArgs);
- }
- }
- setMode("tree");
- },
-
- showTray: function () {
- appState.setGlobalState("showTray", true);
- },
-
- hideTray: function () {
- appState.setGlobalState("showTray", false);
- },
-
- /**
- Called to assign the main tree event handler - this is called by the navigation controller.
- TODO: Potentially another dev could call this which would kind of mung the whole app so potentially there's a better way.
- */
- setupTreeEvents: function(treeEventHandler) {
- mainTreeEventHandler = treeEventHandler;
-
- //when a tree is loaded into a section, we need to put it into appState
- mainTreeEventHandler.bind("treeLoaded", function(ev, args) {
- appState.setTreeState("currentRootNode", args.tree);
- });
-
- //when a tree node is synced this event will fire, this allows us to set the currentNode
- mainTreeEventHandler.bind("treeSynced", function (ev, args) {
-
- if (args.activate === undefined || args.activate === true) {
- //set the current selected node
- appState.setTreeState("selectedNode", args.node);
- //when a node is activated, this is the same as clicking it and we need to set the
- //current menu item to be this node as well.
- appState.setMenuState("currentNode", args.node);
- }
- });
-
- //this reacts to the options item in the tree
- mainTreeEventHandler.bind("treeOptionsClick", function(ev, args) {
- ev.stopPropagation();
- ev.preventDefault();
-
- //Set the current action node (this is not the same as the current selected node!)
- appState.setMenuState("currentNode", args.node);
-
- if (args.event && args.event.altKey) {
- args.skipDefault = true;
- }
-
- service.showMenu(ev, args);
- });
-
- mainTreeEventHandler.bind("treeNodeAltSelect", function(ev, args) {
- ev.stopPropagation();
- ev.preventDefault();
-
- args.skipDefault = true;
- service.showMenu(ev, args);
- });
-
- //this reacts to tree items themselves being clicked
- //the tree directive should not contain any handling, simply just bubble events
- mainTreeEventHandler.bind("treeNodeSelect", function (ev, args) {
- var n = args.node;
- ev.stopPropagation();
- ev.preventDefault();
-
- if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") {
- //this is a legacy tree node!
- var jsPrefix = "javascript:";
- var js;
- if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) {
- js = n.metaData["jsClickCallback"].substr(jsPrefix.length);
- }
- else {
- js = n.metaData["jsClickCallback"];
- }
- try {
- var func = eval(js);
- //this is normally not necessary since the eval above should execute the method and will return nothing.
- if (func != null && (typeof func === "function")) {
- func.call();
- }
- }
- catch(ex) {
- $log.error("Error evaluating js callback from legacy tree node: " + ex);
- }
- }
- else if (n.routePath) {
- //add action to the history service
- historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
-
- //put this node into the tree state
- appState.setTreeState("selectedNode", args.node);
- //when a node is clicked we also need to set the active menu node to this node
- appState.setMenuState("currentNode", args.node);
-
- //not legacy, lets just set the route value and clear the query string if there is one.
- $location.path(n.routePath).search("");
- }
- else if (args.element.section) {
- $location.path(args.element.section).search("");
- }
-
- service.hideNavigation();
- });
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#syncTree
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Syncs a tree with a given path, returns a promise
- * The path format is: ["itemId","itemId"], and so on
- * so to sync to a specific document type node do:
- *
- * navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
- *
- * @param {Object} args arguments passed to the function
- * @param {String} args.tree the tree alias to sync to
- * @param {Array} args.path the path to sync the tree to
- * @param {Boolean} args.forceReload optional, specifies whether to force reload the node data from the server even if it already exists in the tree currently
- * @param {Boolean} args.activate optional, specifies whether to set the synced node to be the active node, this will default to true if not specified
- */
- syncTree: function (args) {
- if (!args) {
- throw "args cannot be null";
- }
- if (!args.path) {
- throw "args.path cannot be null";
- }
- if (!args.tree) {
- throw "args.tree cannot be null";
- }
-
- if (mainTreeEventHandler) {
- //returns a promise
- return mainTreeEventHandler.syncTree(args);
- }
-
- //couldn't sync
- return angularHelper.rejectedPromise();
- },
-
- /**
- Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
- have to set an active tree and then sync, the new API does this in one method by using syncTree
- */
- _syncPath: function(path, forceReload) {
- if (mainTreeEventHandler) {
- mainTreeEventHandler.syncTree({ path: path, forceReload: forceReload });
- }
- },
-
- //TODO: This should return a promise
- reloadNode: function(node) {
- if (mainTreeEventHandler) {
- mainTreeEventHandler.reloadNode(node);
- }
- },
-
- //TODO: This should return a promise
- reloadSection: function(sectionAlias) {
- if (mainTreeEventHandler) {
- mainTreeEventHandler.clearCache({ section: sectionAlias });
- mainTreeEventHandler.load(sectionAlias);
- }
- },
-
- /**
- Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
- have to set an active tree and then sync, the new API does this in one method by using syncTreePath
- */
- _setActiveTreeType: function (treeAlias, loadChildren) {
- if (mainTreeEventHandler) {
- mainTreeEventHandler._setActiveTreeType(treeAlias, loadChildren);
- }
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideTree
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the tree by hiding the containing dom element
- */
- hideTree: function() {
-
- if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) {
- //reset it to whatever is in the url
- appState.setSectionState("currentSection", $routeParams.section);
- setMode("default-hidesectiontree");
- }
-
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showMenu
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the tree by hiding the containing dom element.
- * This always returns a promise!
- *
- * @param {Event} event the click event triggering the method, passed from the DOM element
- */
- showMenu: function(event, args) {
-
- var deferred = $q.defer();
- var self = this;
-
- treeService.getMenu({ treeNode: args.node })
- .then(function(data) {
-
- //check for a default
- //NOTE: event will be undefined when a call to hideDialog is made so it won't re-load the default again.
- // but perhaps there's a better way to deal with with an additional parameter in the args ? it works though.
- if (data.defaultAlias && !args.skipDefault) {
-
- var found = _.find(data.menuItems, function(item) {
- return item.alias = data.defaultAlias;
- });
-
- if (found) {
-
- //NOTE: This is assigning the current action node - this is not the same as the currently selected node!
- appState.setMenuState("currentNode", args.node);
-
- //ensure the current dialog is cleared before creating another!
- if (currentDialog) {
- dialogService.close(currentDialog);
- }
-
- var dialog = self.showDialog({
- node: args.node,
- action: found,
- section: appState.getSectionState("currentSection")
- });
-
- //return the dialog this is opening.
- deferred.resolve(dialog);
- return;
- }
- }
-
- //there is no default or we couldn't find one so just continue showing the menu
-
- setMode("menu");
-
- appState.setMenuState("currentNode", args.node);
- appState.setMenuState("menuActions", data.menuItems);
- appState.setMenuState("dialogTitle", args.node.name);
-
- //we're not opening a dialog, return null.
- deferred.resolve(null);
- });
-
- return deferred.promise;
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideMenu
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Hides the menu by hiding the containing dom element
- */
- hideMenu: function() {
- //SD: Would we ever want to access the last action'd node instead of clearing it here?
- appState.setMenuState("currentNode", null);
- appState.setMenuState("menuActions", []);
- setMode("tree");
- },
-
- /** Executes a given menu action */
- executeMenuAction: function (action, node, section) {
-
- if (!action) {
- throw "action cannot be null";
- }
- if (!node) {
- throw "node cannot be null";
- }
- if (!section) {
- throw "section cannot be null";
- }
-
- if (action.metaData && action.metaData["actionRoute"] && angular.isString(action.metaData["actionRoute"])) {
- //first check if the menu item simply navigates to a route
- var parts = action.metaData["actionRoute"].split("?");
- $location.path(parts[0]).search(parts.length > 1 ? parts[1] : "");
- this.hideNavigation();
- return;
- }
- else if (action.metaData && action.metaData["jsAction"] && angular.isString(action.metaData["jsAction"])) {
-
- //we'll try to get the jsAction from the injector
- var menuAction = action.metaData["jsAction"].split('.');
- if (menuAction.length !== 2) {
-
- //if it is not two parts long then this most likely means that it's a legacy action
- var js = action.metaData["jsAction"].replace("javascript:", "");
- //there's not really a different way to acheive this except for eval
- eval(js);
- }
- else {
- var menuActionService = $injector.get(menuAction[0]);
- if (!menuActionService) {
- throw "The angular service " + menuAction[0] + " could not be found";
- }
-
- var method = menuActionService[menuAction[1]];
-
- if (!method) {
- throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found";
- }
-
- method.apply(this, [{
- //map our content object to a basic entity to pass in to the menu handlers,
- //this is required for consistency since a menu item needs to be decoupled from a tree node since the menu can
- //exist standalone in the editor for which it can only pass in an entity (not tree node).
- entity: umbModelMapper.convertToEntityBasic(node),
- action: action,
- section: section,
- treeAlias: treeService.getTreeAlias(node)
- }]);
- }
- }
- else {
- service.showDialog({
- node: node,
- action: action,
- section: section
- });
- }
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showUserDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Opens the user dialog, next to the sections navigation
- * template is located in views/common/dialogs/user.html
- */
- showUserDialog: function () {
- // hide tray and close help dialog
- if (service.helpDialog) {
- service.helpDialog.close();
- }
- service.hideTray();
-
- if (service.userDialog) {
- service.userDialog.close();
- service.userDialog = undefined;
- }
-
- service.userDialog = dialogService.open(
- {
- template: "views/common/dialogs/user.html",
- modalClass: "umb-modal-left",
- show: true
- });
-
- return service.userDialog;
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showUserDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Opens the user dialog, next to the sections navigation
- * template is located in views/common/dialogs/user.html
- */
- showHelpDialog: function () {
- // hide tray and close user dialog
- service.hideTray();
- if (service.userDialog) {
- service.userDialog.close();
- }
-
- if(service.helpDialog){
- service.helpDialog.close();
- service.helpDialog = undefined;
- }
-
- service.helpDialog = dialogService.open(
- {
- template: "views/common/dialogs/help.html",
- modalClass: "umb-modal-left",
- show: true
- });
-
- return service.helpDialog;
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * Opens a dialog, for a given action on a given tree node
- * uses the dialogService to inject the selected action dialog
- * into #dialog div.umb-panel-body
- * the path to the dialog view is determined by:
- * "views/" + current tree + "/" + action alias + ".html"
- * The dialog controller will get passed a scope object that is created here with the properties:
- * scope.currentNode = the selected tree node
- * scope.currentAction = the selected menu item
- * so that the dialog controllers can use these properties
- *
- * @param {Object} args arguments passed to the function
- * @param {Scope} args.scope current scope passed to the dialog
- * @param {Object} args.action the clicked action containing `name` and `alias`
- */
- showDialog: function(args) {
-
- if (!args) {
- throw "showDialog is missing the args parameter";
- }
- if (!args.action) {
- throw "The args parameter must have an 'action' property as the clicked menu action object";
- }
- if (!args.node) {
- throw "The args parameter must have a 'node' as the active tree node";
- }
-
- //ensure the current dialog is cleared before creating another!
- if (currentDialog) {
- dialogService.close(currentDialog);
- currentDialog = null;
- }
-
- setMode("dialog");
-
- //NOTE: Set up the scope object and assign properties, this is legacy functionality but we have to live with it now.
- // we should be passing in currentNode and currentAction using 'dialogData' for the dialog, not attaching it to a scope.
- // This scope instance will be destroyed by the dialog so it cannot be a scope that exists outside of the dialog.
- // If a scope instance has been passed in, we'll have to create a child scope of it, otherwise a new root scope.
- var dialogScope = args.scope ? args.scope.$new() : $rootScope.$new();
- dialogScope.currentNode = args.node;
- dialogScope.currentAction = args.action;
-
- //the title might be in the meta data, check there first
- if (args.action.metaData["dialogTitle"]) {
- appState.setMenuState("dialogTitle", args.action.metaData["dialogTitle"]);
- }
- else {
- appState.setMenuState("dialogTitle", args.action.name);
- }
-
- var templateUrl;
- var iframe;
-
- if (args.action.metaData["actionUrl"]) {
- templateUrl = args.action.metaData["actionUrl"];
- iframe = true;
- }
- else if (args.action.metaData["actionView"]) {
- templateUrl = args.action.metaData["actionView"];
- iframe = false;
- }
- else {
-
- //by convention we will look into the /views/{treetype}/{action}.html
- // for example: /views/content/create.html
-
- //we will also check for a 'packageName' for the current tree, if it exists then the convention will be:
- // for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html
-
- var treeAlias = treeService.getTreeAlias(args.node);
- var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
-
- if (!treeAlias) {
- throw "Could not get tree alias for node " + args.node.id;
- }
-
- if (packageTreeFolder) {
- templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
- "/" + packageTreeFolder +
- "/backoffice/" + treeAlias + "/" + args.action.alias + ".html";
- }
- else {
- templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html";
- }
-
- iframe = false;
- }
-
- //TODO: some action's want to launch a new window like live editing, we support this in the menu item's metadata with
- // a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog
- // if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window,
- // though would be v-easy, just not sure we want to ever support that?
-
- var dialog = dialogService.open(
- {
- container: $("#dialog div.umb-modalcolumn-body"),
- //The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is
- // for backwards compatibility since many dialogs require $scope.currentNode or $scope.currentAction
- // to exist
- scope: dialogScope,
- inline: true,
- show: true,
- iframe: iframe,
- modalClass: "umb-dialog",
- template: templateUrl,
-
- //These will show up on the dialog controller's $scope under dialogOptions
- currentNode: args.node,
- currentAction: args.action,
- });
-
- //save the currently assigned dialog so it can be removed before a new one is created
- currentDialog = dialog;
- return dialog;
- },
-
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideDialog
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides the currently open dialog
- */
- hideDialog: function (showMenu) {
-
- setMode("default");
-
- if(showMenu){
- this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") });
- }
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#showSearch
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * shows the search pane
- */
- showSearch: function() {
- setMode("search");
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideSearch
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides the search pane
- */
- hideSearch: function() {
- setMode("default-hidesearch");
- },
- /**
- * @ngdoc method
- * @name umbraco.services.navigationService#hideNavigation
- * @methodOf umbraco.services.navigationService
- *
- * @description
- * hides any open navigation panes and resets the tree, actions and the currently selected node
- */
- hideNavigation: function() {
- appState.setMenuState("menuActions", []);
- setMode("default");
- }
- };
-
- return service;
-}
-
-angular.module('umbraco.services').factory('navigationService', navigationService);
+/**
+ * @ngdoc service
+ * @name umbraco.services.navigationService
+ *
+ * @requires $rootScope
+ * @requires $routeParams
+ * @requires $log
+ * @requires $location
+ * @requires dialogService
+ * @requires treeService
+ * @requires sectionResource
+ *
+ * @description
+ * Service to handle the main application navigation. Responsible for invoking the tree
+ * Section navigation and search, and maintain their state for the entire application lifetime
+ *
+ */
+function navigationService($rootScope, $routeParams, $log, $location, $q, $timeout, $injector, dialogService, umbModelMapper, treeService, notificationsService, historyService, appState, angularHelper) {
+
+
+ //used to track the current dialog object
+ var currentDialog = null;
+
+ //the main tree event handler, which gets assigned via the setupTreeEvents method
+ var mainTreeEventHandler = null;
+ //tracks the user profile dialog
+ var userDialog = null;
+
+ function setMode(mode) {
+ switch (mode) {
+ case 'tree':
+ appState.setGlobalState("navMode", "tree");
+ appState.setGlobalState("showNavigation", true);
+ appState.setMenuState("showMenu", false);
+ appState.setMenuState("showMenuDialog", false);
+ appState.setGlobalState("stickyNavigation", false);
+ appState.setGlobalState("showTray", false);
+
+ //$("#search-form input").focus();
+ break;
+ case 'menu':
+ appState.setGlobalState("navMode", "menu");
+ appState.setGlobalState("showNavigation", true);
+ appState.setMenuState("showMenu", true);
+ appState.setMenuState("showMenuDialog", false);
+ appState.setGlobalState("stickyNavigation", true);
+ break;
+ case 'dialog':
+ appState.setGlobalState("navMode", "dialog");
+ appState.setGlobalState("stickyNavigation", true);
+ appState.setGlobalState("showNavigation", true);
+ appState.setMenuState("showMenu", false);
+ appState.setMenuState("showMenuDialog", true);
+ break;
+ case 'search':
+ appState.setGlobalState("navMode", "search");
+ appState.setGlobalState("stickyNavigation", false);
+ appState.setGlobalState("showNavigation", true);
+ appState.setMenuState("showMenu", false);
+ appState.setSectionState("showSearchResults", true);
+ appState.setMenuState("showMenuDialog", false);
+
+ //TODO: This would be much better off in the search field controller listening to appState changes
+ $timeout(function() {
+ $("#search-field").focus();
+ });
+
+ break;
+ default:
+ appState.setGlobalState("navMode", "default");
+ appState.setMenuState("showMenu", false);
+ appState.setMenuState("showMenuDialog", false);
+ appState.setSectionState("showSearchResults", false);
+ appState.setGlobalState("stickyNavigation", false);
+ appState.setGlobalState("showTray", false);
+
+ if (appState.getGlobalState("isTablet") === true) {
+ appState.setGlobalState("showNavigation", false);
+ }
+
+ break;
+ }
+ }
+
+ var service = {
+
+ /** initializes the navigation service */
+ init: function() {
+
+ //keep track of the current section - initially this will always be undefined so
+ // no point in setting it now until it changes.
+ $rootScope.$watch(function () {
+ return $routeParams.section;
+ }, function (newVal, oldVal) {
+ appState.setSectionState("currentSection", newVal);
+ });
+
+
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#load
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Shows the legacy iframe and loads in the content based on the source url
+ * @param {String} source The URL to load into the iframe
+ */
+ loadLegacyIFrame: function (source) {
+ $location.path("/" + appState.getSectionState("currentSection") + "/framed/" + encodeURIComponent(source));
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#changeSection
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Changes the active section to a given section alias
+ * If the navigation is 'sticky' this will load the associated tree
+ * and load the dashboard related to the section
+ * @param {string} sectionAlias The alias of the section
+ */
+ changeSection: function(sectionAlias, force) {
+ setMode("default-opensection");
+
+ if (force && appState.getSectionState("currentSection") === sectionAlias) {
+ appState.setSectionState("currentSection", "");
+ }
+
+ appState.setSectionState("currentSection", sectionAlias);
+ this.showTree(sectionAlias);
+
+ $location.path(sectionAlias);
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Displays the tree for a given section alias but turning on the containing dom element
+ * only changes if the section is different from the current one
+ * @param {string} sectionAlias The alias of the section to load
+ * @param {Object} syncArgs Optional object of arguments for syncing the tree for the section being shown
+ */
+ showTree: function (sectionAlias, syncArgs) {
+ if (sectionAlias !== appState.getSectionState("currentSection")) {
+ appState.setSectionState("currentSection", sectionAlias);
+
+ if (syncArgs) {
+ this.syncTree(syncArgs);
+ }
+ }
+ setMode("tree");
+ },
+
+ showTray: function () {
+ appState.setGlobalState("showTray", true);
+ },
+
+ hideTray: function () {
+ appState.setGlobalState("showTray", false);
+ },
+
+ /**
+ Called to assign the main tree event handler - this is called by the navigation controller.
+ TODO: Potentially another dev could call this which would kind of mung the whole app so potentially there's a better way.
+ */
+ setupTreeEvents: function(treeEventHandler) {
+ mainTreeEventHandler = treeEventHandler;
+
+ //when a tree is loaded into a section, we need to put it into appState
+ mainTreeEventHandler.bind("treeLoaded", function(ev, args) {
+ appState.setTreeState("currentRootNode", args.tree);
+ });
+
+ //when a tree node is synced this event will fire, this allows us to set the currentNode
+ mainTreeEventHandler.bind("treeSynced", function (ev, args) {
+
+ if (args.activate === undefined || args.activate === true) {
+ //set the current selected node
+ appState.setTreeState("selectedNode", args.node);
+ //when a node is activated, this is the same as clicking it and we need to set the
+ //current menu item to be this node as well.
+ appState.setMenuState("currentNode", args.node);
+ }
+ });
+
+ //this reacts to the options item in the tree
+ mainTreeEventHandler.bind("treeOptionsClick", function(ev, args) {
+ ev.stopPropagation();
+ ev.preventDefault();
+
+ //Set the current action node (this is not the same as the current selected node!)
+ appState.setMenuState("currentNode", args.node);
+
+ if (args.event && args.event.altKey) {
+ args.skipDefault = true;
+ }
+
+ service.showMenu(ev, args);
+ });
+
+ mainTreeEventHandler.bind("treeNodeAltSelect", function(ev, args) {
+ ev.stopPropagation();
+ ev.preventDefault();
+
+ args.skipDefault = true;
+ service.showMenu(ev, args);
+ });
+
+ //this reacts to tree items themselves being clicked
+ //the tree directive should not contain any handling, simply just bubble events
+ mainTreeEventHandler.bind("treeNodeSelect", function (ev, args) {
+ var n = args.node;
+ ev.stopPropagation();
+ ev.preventDefault();
+
+ if (n.metaData && n.metaData["jsClickCallback"] && angular.isString(n.metaData["jsClickCallback"]) && n.metaData["jsClickCallback"] !== "") {
+ //this is a legacy tree node!
+ var jsPrefix = "javascript:";
+ var js;
+ if (n.metaData["jsClickCallback"].startsWith(jsPrefix)) {
+ js = n.metaData["jsClickCallback"].substr(jsPrefix.length);
+ }
+ else {
+ js = n.metaData["jsClickCallback"];
+ }
+ try {
+ var func = eval(js);
+ //this is normally not necessary since the eval above should execute the method and will return nothing.
+ if (func != null && (typeof func === "function")) {
+ func.call();
+ }
+ }
+ catch(ex) {
+ $log.error("Error evaluating js callback from legacy tree node: " + ex);
+ }
+ }
+ else if (n.routePath) {
+ //add action to the history service
+ historyService.add({ name: n.name, link: n.routePath, icon: n.icon });
+
+ //put this node into the tree state
+ appState.setTreeState("selectedNode", args.node);
+ //when a node is clicked we also need to set the active menu node to this node
+ appState.setMenuState("currentNode", args.node);
+
+ //not legacy, lets just set the route value and clear the query string if there is one.
+ $location.path(n.routePath).search("");
+ }
+ else if (args.element.section) {
+ $location.path(args.element.section).search("");
+ }
+
+ service.hideNavigation();
+ });
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#syncTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Syncs a tree with a given path, returns a promise
+ * The path format is: ["itemId","itemId"], and so on
+ * so to sync to a specific document type node do:
+ *
+ * navigationService.syncTree({tree: 'content', path: ["-1","123d"], forceReload: true});
+ *
+ * @param {Object} args arguments passed to the function
+ * @param {String} args.tree the tree alias to sync to
+ * @param {Array} args.path the path to sync the tree to
+ * @param {Boolean} args.forceReload optional, specifies whether to force reload the node data from the server even if it already exists in the tree currently
+ * @param {Boolean} args.activate optional, specifies whether to set the synced node to be the active node, this will default to true if not specified
+ */
+ syncTree: function (args) {
+ if (!args) {
+ throw "args cannot be null";
+ }
+ if (!args.path) {
+ throw "args.path cannot be null";
+ }
+ if (!args.tree) {
+ throw "args.tree cannot be null";
+ }
+
+ if (mainTreeEventHandler) {
+ //returns a promise
+ return mainTreeEventHandler.syncTree(args);
+ }
+
+ //couldn't sync
+ return angularHelper.rejectedPromise();
+ },
+
+ /**
+ Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
+ have to set an active tree and then sync, the new API does this in one method by using syncTree
+ */
+ _syncPath: function(path, forceReload) {
+ if (mainTreeEventHandler) {
+ mainTreeEventHandler.syncTree({ path: path, forceReload: forceReload });
+ }
+ },
+
+ //TODO: This should return a promise
+ reloadNode: function(node) {
+ if (mainTreeEventHandler) {
+ mainTreeEventHandler.reloadNode(node);
+ }
+ },
+
+ //TODO: This should return a promise
+ reloadSection: function(sectionAlias) {
+ if (mainTreeEventHandler) {
+ mainTreeEventHandler.clearCache({ section: sectionAlias });
+ mainTreeEventHandler.load(sectionAlias);
+ }
+ },
+
+ /**
+ Internal method that should ONLY be used by the legacy API wrapper, the legacy API used to
+ have to set an active tree and then sync, the new API does this in one method by using syncTreePath
+ */
+ _setActiveTreeType: function (treeAlias, loadChildren) {
+ if (mainTreeEventHandler) {
+ mainTreeEventHandler._setActiveTreeType(treeAlias, loadChildren);
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideTree
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the tree by hiding the containing dom element
+ */
+ hideTree: function() {
+
+ if (appState.getGlobalState("isTablet") === true && !appState.getGlobalState("stickyNavigation")) {
+ //reset it to whatever is in the url
+ appState.setSectionState("currentSection", $routeParams.section);
+ setMode("default-hidesectiontree");
+ }
+
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showMenu
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the tree by hiding the containing dom element.
+ * This always returns a promise!
+ *
+ * @param {Event} event the click event triggering the method, passed from the DOM element
+ */
+ showMenu: function(event, args) {
+
+ var deferred = $q.defer();
+ var self = this;
+
+ treeService.getMenu({ treeNode: args.node })
+ .then(function(data) {
+
+ //check for a default
+ //NOTE: event will be undefined when a call to hideDialog is made so it won't re-load the default again.
+ // but perhaps there's a better way to deal with with an additional parameter in the args ? it works though.
+ if (data.defaultAlias && !args.skipDefault) {
+
+ var found = _.find(data.menuItems, function(item) {
+ return item.alias = data.defaultAlias;
+ });
+
+ if (found) {
+
+ //NOTE: This is assigning the current action node - this is not the same as the currently selected node!
+ appState.setMenuState("currentNode", args.node);
+
+ //ensure the current dialog is cleared before creating another!
+ if (currentDialog) {
+ dialogService.close(currentDialog);
+ }
+
+ var dialog = self.showDialog({
+ node: args.node,
+ action: found,
+ section: appState.getSectionState("currentSection")
+ });
+
+ //return the dialog this is opening.
+ deferred.resolve(dialog);
+ return;
+ }
+ }
+
+ //there is no default or we couldn't find one so just continue showing the menu
+
+ setMode("menu");
+
+ appState.setMenuState("currentNode", args.node);
+ appState.setMenuState("menuActions", data.menuItems);
+ appState.setMenuState("dialogTitle", args.node.name);
+
+ //we're not opening a dialog, return null.
+ deferred.resolve(null);
+ });
+
+ return deferred.promise;
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideMenu
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Hides the menu by hiding the containing dom element
+ */
+ hideMenu: function() {
+ //SD: Would we ever want to access the last action'd node instead of clearing it here?
+ appState.setMenuState("currentNode", null);
+ appState.setMenuState("menuActions", []);
+ setMode("tree");
+ },
+
+ /** Executes a given menu action */
+ executeMenuAction: function (action, node, section) {
+
+ if (!action) {
+ throw "action cannot be null";
+ }
+ if (!node) {
+ throw "node cannot be null";
+ }
+ if (!section) {
+ throw "section cannot be null";
+ }
+
+ if (action.metaData && action.metaData["actionRoute"] && angular.isString(action.metaData["actionRoute"])) {
+ //first check if the menu item simply navigates to a route
+ var parts = action.metaData["actionRoute"].split("?");
+ $location.path(parts[0]).search(parts.length > 1 ? parts[1] : "");
+ this.hideNavigation();
+ return;
+ }
+ else if (action.metaData && action.metaData["jsAction"] && angular.isString(action.metaData["jsAction"])) {
+
+ //we'll try to get the jsAction from the injector
+ var menuAction = action.metaData["jsAction"].split('.');
+ if (menuAction.length !== 2) {
+
+ //if it is not two parts long then this most likely means that it's a legacy action
+ var js = action.metaData["jsAction"].replace("javascript:", "");
+ //there's not really a different way to acheive this except for eval
+ eval(js);
+ }
+ else {
+ var menuActionService = $injector.get(menuAction[0]);
+ if (!menuActionService) {
+ throw "The angular service " + menuAction[0] + " could not be found";
+ }
+
+ var method = menuActionService[menuAction[1]];
+
+ if (!method) {
+ throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found";
+ }
+
+ method.apply(this, [{
+ //map our content object to a basic entity to pass in to the menu handlers,
+ //this is required for consistency since a menu item needs to be decoupled from a tree node since the menu can
+ //exist standalone in the editor for which it can only pass in an entity (not tree node).
+ entity: umbModelMapper.convertToEntityBasic(node),
+ action: action,
+ section: section,
+ treeAlias: treeService.getTreeAlias(node)
+ }]);
+ }
+ }
+ else {
+ service.showDialog({
+ node: node,
+ action: action,
+ section: section
+ });
+ }
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showUserDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Opens the user dialog, next to the sections navigation
+ * template is located in views/common/dialogs/user.html
+ */
+ showUserDialog: function () {
+ // hide tray and close help dialog
+ if (service.helpDialog) {
+ service.helpDialog.close();
+ }
+ service.hideTray();
+
+ if (service.userDialog) {
+ service.userDialog.close();
+ service.userDialog = undefined;
+ }
+
+ service.userDialog = dialogService.open(
+ {
+ template: "views/common/dialogs/user.html",
+ modalClass: "umb-modal-left",
+ show: true
+ });
+
+ return service.userDialog;
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showUserDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Opens the user dialog, next to the sections navigation
+ * template is located in views/common/dialogs/user.html
+ */
+ showHelpDialog: function () {
+ // hide tray and close user dialog
+ service.hideTray();
+ if (service.userDialog) {
+ service.userDialog.close();
+ }
+
+ if(service.helpDialog){
+ service.helpDialog.close();
+ service.helpDialog = undefined;
+ }
+
+ service.helpDialog = dialogService.open(
+ {
+ template: "views/common/dialogs/help.html",
+ modalClass: "umb-modal-left",
+ show: true
+ });
+
+ return service.helpDialog;
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * Opens a dialog, for a given action on a given tree node
+ * uses the dialogService to inject the selected action dialog
+ * into #dialog div.umb-panel-body
+ * the path to the dialog view is determined by:
+ * "views/" + current tree + "/" + action alias + ".html"
+ * The dialog controller will get passed a scope object that is created here with the properties:
+ * scope.currentNode = the selected tree node
+ * scope.currentAction = the selected menu item
+ * so that the dialog controllers can use these properties
+ *
+ * @param {Object} args arguments passed to the function
+ * @param {Scope} args.scope current scope passed to the dialog
+ * @param {Object} args.action the clicked action containing `name` and `alias`
+ */
+ showDialog: function(args) {
+
+ if (!args) {
+ throw "showDialog is missing the args parameter";
+ }
+ if (!args.action) {
+ throw "The args parameter must have an 'action' property as the clicked menu action object";
+ }
+ if (!args.node) {
+ throw "The args parameter must have a 'node' as the active tree node";
+ }
+
+ //ensure the current dialog is cleared before creating another!
+ if (currentDialog) {
+ dialogService.close(currentDialog);
+ currentDialog = null;
+ }
+
+ setMode("dialog");
+
+ //NOTE: Set up the scope object and assign properties, this is legacy functionality but we have to live with it now.
+ // we should be passing in currentNode and currentAction using 'dialogData' for the dialog, not attaching it to a scope.
+ // This scope instance will be destroyed by the dialog so it cannot be a scope that exists outside of the dialog.
+ // If a scope instance has been passed in, we'll have to create a child scope of it, otherwise a new root scope.
+ var dialogScope = args.scope ? args.scope.$new() : $rootScope.$new();
+ dialogScope.currentNode = args.node;
+ dialogScope.currentAction = args.action;
+
+ //the title might be in the meta data, check there first
+ if (args.action.metaData["dialogTitle"]) {
+ appState.setMenuState("dialogTitle", args.action.metaData["dialogTitle"]);
+ }
+ else {
+ appState.setMenuState("dialogTitle", args.action.name);
+ }
+
+ var templateUrl;
+ var iframe;
+
+ if (args.action.metaData["actionUrl"]) {
+ templateUrl = args.action.metaData["actionUrl"];
+ iframe = true;
+ }
+ else if (args.action.metaData["actionView"]) {
+ templateUrl = args.action.metaData["actionView"];
+ iframe = false;
+ }
+ else {
+
+ //by convention we will look into the /views/{treetype}/{action}.html
+ // for example: /views/content/create.html
+
+ //we will also check for a 'packageName' for the current tree, if it exists then the convention will be:
+ // for example: /App_Plugins/{mypackage}/backoffice/{treetype}/create.html
+
+ var treeAlias = treeService.getTreeAlias(args.node);
+ var packageTreeFolder = treeService.getTreePackageFolder(treeAlias);
+
+ if (!treeAlias) {
+ throw "Could not get tree alias for node " + args.node.id;
+ }
+
+ if (packageTreeFolder) {
+ templateUrl = Umbraco.Sys.ServerVariables.umbracoSettings.appPluginsPath +
+ "/" + packageTreeFolder +
+ "/backoffice/" + treeAlias + "/" + args.action.alias + ".html";
+ }
+ else {
+ templateUrl = "views/" + treeAlias + "/" + args.action.alias + ".html";
+ }
+
+ iframe = false;
+ }
+
+ //TODO: some action's want to launch a new window like live editing, we support this in the menu item's metadata with
+ // a key called: "actionUrlMethod" which can be set to either: Dialog, BlankWindow. Normally this is always set to Dialog
+ // if a URL is specified in the "actionUrl" metadata. For now I'm not going to implement launching in a blank window,
+ // though would be v-easy, just not sure we want to ever support that?
+
+ var dialog = dialogService.open(
+ {
+ container: $("#dialog div.umb-modalcolumn-body"),
+ //The ONLY reason we're passing in scope to the dialogService (which is legacy functionality) is
+ // for backwards compatibility since many dialogs require $scope.currentNode or $scope.currentAction
+ // to exist
+ scope: dialogScope,
+ inline: true,
+ show: true,
+ iframe: iframe,
+ modalClass: "umb-dialog",
+ template: templateUrl,
+
+ //These will show up on the dialog controller's $scope under dialogOptions
+ currentNode: args.node,
+ currentAction: args.action,
+ });
+
+ //save the currently assigned dialog so it can be removed before a new one is created
+ currentDialog = dialog;
+ return dialog;
+ },
+
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideDialog
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides the currently open dialog
+ */
+ hideDialog: function (showMenu) {
+
+ setMode("default");
+
+ if(showMenu){
+ this.showMenu(undefined, { skipDefault: true, node: appState.getMenuState("currentNode") });
+ }
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#showSearch
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * shows the search pane
+ */
+ showSearch: function() {
+ setMode("search");
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideSearch
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides the search pane
+ */
+ hideSearch: function() {
+ setMode("default-hidesearch");
+ },
+ /**
+ * @ngdoc method
+ * @name umbraco.services.navigationService#hideNavigation
+ * @methodOf umbraco.services.navigationService
+ *
+ * @description
+ * hides any open navigation panes and resets the tree, actions and the currently selected node
+ */
+ hideNavigation: function() {
+ appState.setMenuState("menuActions", []);
+ setMode("default");
+ }
+ };
+
+ return service;
+}
+
+angular.module('umbraco.services').factory('navigationService', navigationService);
/**
* @ngdoc service
@@ -5593,43 +5677,43 @@ angular.module('umbraco.services')
return service;
});
-(function() {
- 'use strict';
-
- function overlayHelper() {
-
- var numberOfOverlays = 0;
-
- function registerOverlay() {
- numberOfOverlays++;
- return numberOfOverlays;
- }
-
- function unregisterOverlay() {
- numberOfOverlays--;
- return numberOfOverlays;
- }
-
- function getNumberOfOverlays() {
- return numberOfOverlays;
- }
-
- var service = {
- numberOfOverlays: numberOfOverlays,
- registerOverlay: registerOverlay,
- unregisterOverlay: unregisterOverlay,
- getNumberOfOverlays: getNumberOfOverlays
- };
-
- return service;
-
- }
-
-
- angular.module('umbraco.services').factory('overlayHelper', overlayHelper);
-
-
-})();
+(function() {
+ 'use strict';
+
+ function overlayHelper() {
+
+ var numberOfOverlays = 0;
+
+ function registerOverlay() {
+ numberOfOverlays++;
+ return numberOfOverlays;
+ }
+
+ function unregisterOverlay() {
+ numberOfOverlays--;
+ return numberOfOverlays;
+ }
+
+ function getNumberOfOverlays() {
+ return numberOfOverlays;
+ }
+
+ var service = {
+ numberOfOverlays: numberOfOverlays,
+ registerOverlay: registerOverlay,
+ unregisterOverlay: unregisterOverlay,
+ getNumberOfOverlays: getNumberOfOverlays
+ };
+
+ return service;
+
+ }
+
+
+ angular.module('umbraco.services').factory('overlayHelper', overlayHelper);
+
+
+})();
/**
* @ngdoc service
@@ -8376,6 +8460,7 @@ function packageHelper(assetsService, treeService, eventsService, $templateCache
}
angular.module('umbraco.services').factory('packageHelper', packageHelper);
+//TODO: I believe this is obsolete
function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, mediaHelper, umbRequestHelper) {
return {
/** sets the image's url, thumbnail and if its a folder */
@@ -8674,7 +8759,6 @@ function umbPhotoFolderHelper($compile, $log, $timeout, $filter, imageHelper, me
}
};
}
-
angular.module("umbraco.services").factory("umbPhotoFolderHelper", umbPhotoFolderHelper);
/**
@@ -9041,450 +9125,450 @@ angular.module('umbraco.services').factory('umbDataFormatter', umbDataFormatter)
-/**
- * @ngdoc service
- * @name umbraco.services.windowResizeListener
- * @function
- *
- * @description
- * A single window resize listener... we don't want to have more than one in theory to ensure that
- * there aren't too many events raised. This will debounce the event with 100 ms intervals and force
- * a $rootScope.$apply when changed and notify all listeners
- *
- */
-function windowResizeListener($rootScope) {
-
- var WinReszier = (function () {
- var registered = [];
- var inited = false;
- var resize = _.debounce(function(ev) {
- notify();
- }, 100);
- var notify = function () {
- var h = $(window).height();
- var w = $(window).width();
- //execute all registrations inside of a digest
- $rootScope.$apply(function() {
- for (var i = 0, cnt = registered.length; i < cnt; i++) {
- registered[i].apply($(window), [{ width: w, height: h }]);
- }
- });
- };
- return {
- register: function (fn) {
- registered.push(fn);
- if (inited === false) {
- $(window).bind('resize', resize);
- inited = true;
- }
- },
- unregister: function (fn) {
- var index = registered.indexOf(fn);
- if (index > -1) {
- registered.splice(index, 1);
- }
- }
- };
- }());
-
- return {
-
- /**
- * Register a callback for resizing
- * @param {Function} cb
- */
- register: function (cb) {
- WinReszier.register(cb);
- },
-
- /**
- * Removes a registered callback
- * @param {Function} cb
- */
- unregister: function(cb) {
- WinReszier.unregister(cb);
- }
-
- };
-}
+/**
+ * @ngdoc service
+ * @name umbraco.services.windowResizeListener
+ * @function
+ *
+ * @description
+ * A single window resize listener... we don't want to have more than one in theory to ensure that
+ * there aren't too many events raised. This will debounce the event with 100 ms intervals and force
+ * a $rootScope.$apply when changed and notify all listeners
+ *
+ */
+function windowResizeListener($rootScope) {
+
+ var WinReszier = (function () {
+ var registered = [];
+ var inited = false;
+ var resize = _.debounce(function(ev) {
+ notify();
+ }, 100);
+ var notify = function () {
+ var h = $(window).height();
+ var w = $(window).width();
+ //execute all registrations inside of a digest
+ $rootScope.$apply(function() {
+ for (var i = 0, cnt = registered.length; i < cnt; i++) {
+ registered[i].apply($(window), [{ width: w, height: h }]);
+ }
+ });
+ };
+ return {
+ register: function (fn) {
+ registered.push(fn);
+ if (inited === false) {
+ $(window).bind('resize', resize);
+ inited = true;
+ }
+ },
+ unregister: function (fn) {
+ var index = registered.indexOf(fn);
+ if (index > -1) {
+ registered.splice(index, 1);
+ }
+ }
+ };
+ }());
+
+ return {
+
+ /**
+ * Register a callback for resizing
+ * @param {Function} cb
+ */
+ register: function (cb) {
+ WinReszier.register(cb);
+ },
+
+ /**
+ * Removes a registered callback
+ * @param {Function} cb
+ */
+ unregister: function(cb) {
+ WinReszier.unregister(cb);
+ }
+
+ };
+}
angular.module('umbraco.services').factory('windowResizeListener', windowResizeListener);
-/**
- * @ngdoc service
- * @name umbraco.services.xmlhelper
- * @function
- *
- * @description
- * Used to convert legacy xml data to json and back again
- */
-function xmlhelper($http) {
- /*
- Copyright 2011 Abdulla Abdurakhmanov
- Original sources are available at https://code.google.com/p/x2js/
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
-
- function X2JS() {
- var VERSION = "1.0.11";
- var escapeMode = false;
-
- var DOMNodeTypes = {
- ELEMENT_NODE: 1,
- TEXT_NODE: 3,
- CDATA_SECTION_NODE: 4,
- DOCUMENT_NODE: 9
- };
-
- function getNodeLocalName(node) {
- var nodeLocalName = node.localName;
- if (nodeLocalName == null) {
- nodeLocalName = node.baseName;
- } // Yeah, this is IE!!
-
- if (nodeLocalName === null || nodeLocalName === "") {
- nodeLocalName = node.nodeName;
- } // =="" is IE too
-
- return nodeLocalName;
- }
-
- function getNodePrefix(node) {
- return node.prefix;
- }
-
- function escapeXmlChars(str) {
- if (typeof (str) === "string") {
- return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
- } else {
- return str;
- }
- }
-
- function unescapeXmlChars(str) {
- return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/');
- }
-
- function parseDOMChildren(node) {
- var result, child, childName;
-
- if (node.nodeType === DOMNodeTypes.DOCUMENT_NODE) {
- result = {};
- child = node.firstChild;
- childName = getNodeLocalName(child);
- result[childName] = parseDOMChildren(child);
- return result;
- }
- else {
-
- if (node.nodeType === DOMNodeTypes.ELEMENT_NODE) {
- result = {};
- result.__cnt = 0;
- var nodeChildren = node.childNodes;
-
- // Children nodes
- for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
- child = nodeChildren.item(cidx); // nodeChildren[cidx];
- childName = getNodeLocalName(child);
-
- result.__cnt++;
- if (result[childName] === null) {
- result[childName] = parseDOMChildren(child);
- result[childName + "_asArray"] = new Array(1);
- result[childName + "_asArray"][0] = result[childName];
- }
- else {
- if (result[childName] !== null) {
- if (!(result[childName] instanceof Array)) {
- var tmpObj = result[childName];
- result[childName] = [];
- result[childName][0] = tmpObj;
-
- result[childName + "_asArray"] = result[childName];
- }
- }
- var aridx = 0;
- while (result[childName][aridx] !== null) {
- aridx++;
- }
-
- (result[childName])[aridx] = parseDOMChildren(child);
- }
- }
-
- // Attributes
- for (var aidx = 0; aidx < node.attributes.length; aidx++) {
- var attr = node.attributes.item(aidx); // [aidx];
- result.__cnt++;
- result["_" + attr.name] = attr.value;
- }
-
- // Node namespace prefix
- var nodePrefix = getNodePrefix(node);
- if (nodePrefix !== null && nodePrefix !== "") {
- result.__cnt++;
- result.__prefix = nodePrefix;
- }
-
- if (result.__cnt === 1 && result["#text"] !== null) {
- result = result["#text"];
- }
-
- if (result["#text"] !== null) {
- result.__text = result["#text"];
- if (escapeMode) {
- result.__text = unescapeXmlChars(result.__text);
- }
-
- delete result["#text"];
- delete result["#text_asArray"];
- }
- if (result["#cdata-section"] != null) {
- result.__cdata = result["#cdata-section"];
- delete result["#cdata-section"];
- delete result["#cdata-section_asArray"];
- }
-
- if (result.__text != null || result.__cdata != null) {
- result.toString = function () {
- return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : '');
- };
- }
- return result;
- }
- else {
- if (node.nodeType === DOMNodeTypes.TEXT_NODE || node.nodeType === DOMNodeTypes.CDATA_SECTION_NODE) {
- return node.nodeValue;
- }
- }
- }
- }
-
- function startTag(jsonObj, element, attrList, closed) {
- var resultStr = "<" + ((jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element;
- if (attrList != null) {
- for (var aidx = 0; aidx < attrList.length; aidx++) {
- var attrName = attrList[aidx];
- var attrVal = jsonObj[attrName];
- resultStr += " " + attrName.substr(1) + "='" + attrVal + "'";
- }
- }
- if (!closed) {
- resultStr += ">";
- } else {
- resultStr += "/>";
- }
-
- return resultStr;
- }
-
- function endTag(jsonObj, elementName) {
- return "" + (jsonObj.__prefix !== null ? (jsonObj.__prefix + ":") : "") + elementName + ">";
- }
-
- function endsWith(str, suffix) {
- return str.indexOf(suffix, str.length - suffix.length) !== -1;
- }
-
- function jsonXmlSpecialElem(jsonObj, jsonObjField) {
- if (endsWith(jsonObjField.toString(), ("_asArray")) || jsonObjField.toString().indexOf("_") === 0 || (jsonObj[jsonObjField] instanceof Function)) {
- return true;
- } else {
- return false;
- }
- }
-
- function jsonXmlElemCount(jsonObj) {
- var elementsCnt = 0;
- if (jsonObj instanceof Object) {
- for (var it in jsonObj) {
- if (jsonXmlSpecialElem(jsonObj, it)) {
- continue;
- }
- elementsCnt++;
- }
- }
- return elementsCnt;
- }
-
- function parseJSONAttributes(jsonObj) {
- var attrList = [];
- if (jsonObj instanceof Object) {
- for (var ait in jsonObj) {
- if (ait.toString().indexOf("__") === -1 && ait.toString().indexOf("_") === 0) {
- attrList.push(ait);
- }
- }
- }
-
- return attrList;
- }
-
- function parseJSONTextAttrs(jsonTxtObj) {
- var result = "";
-
- if (jsonTxtObj.__cdata != null) {
- result += "";
- }
-
- if (jsonTxtObj.__text != null) {
- if (escapeMode) {
- result += escapeXmlChars(jsonTxtObj.__text);
- } else {
- result += jsonTxtObj.__text;
- }
- }
- return result;
- }
-
- function parseJSONTextObject(jsonTxtObj) {
- var result = "";
-
- if (jsonTxtObj instanceof Object) {
- result += parseJSONTextAttrs(jsonTxtObj);
- }
- else {
- if (jsonTxtObj != null) {
- if (escapeMode) {
- result += escapeXmlChars(jsonTxtObj);
- } else {
- result += jsonTxtObj;
- }
- }
- }
-
-
- return result;
- }
-
- function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
- var result = "";
- if (jsonArrRoot.length === 0) {
- result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
- }
- else {
- for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
- result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
- result += parseJSONObject(jsonArrRoot[arIdx]);
- result += endTag(jsonArrRoot[arIdx], jsonArrObj);
- }
- }
- return result;
- }
-
- function parseJSONObject(jsonObj) {
- var result = "";
-
- var elementsCnt = jsonXmlElemCount(jsonObj);
-
- if (elementsCnt > 0) {
- for (var it in jsonObj) {
- if (jsonXmlSpecialElem(jsonObj, it)) {
- continue;
- }
-
- var subObj = jsonObj[it];
- var attrList = parseJSONAttributes(subObj);
-
- if (subObj === null || subObj === undefined) {
- result += startTag(subObj, it, attrList, true);
- } else {
- if (subObj instanceof Object) {
-
- if (subObj instanceof Array) {
- result += parseJSONArray(subObj, it, attrList);
- } else {
- var subObjElementsCnt = jsonXmlElemCount(subObj);
- if (subObjElementsCnt > 0 || subObj.__text !== null || subObj.__cdata !== null) {
- result += startTag(subObj, it, attrList, false);
- result += parseJSONObject(subObj);
- result += endTag(subObj, it);
- } else {
- result += startTag(subObj, it, attrList, true);
- }
- }
-
- } else {
- result += startTag(subObj, it, attrList, false);
- result += parseJSONTextObject(subObj);
- result += endTag(subObj, it);
- }
- }
- }
- }
- result += parseJSONTextObject(jsonObj);
-
- return result;
- }
-
- this.parseXmlString = function (xmlDocStr) {
- var xmlDoc;
- if (window.DOMParser) {
- var parser = new window.DOMParser();
- xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");
- }
- else {
- // IE :(
- if (xmlDocStr.indexOf("") === 0) {
- xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
- }
- xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
- xmlDoc.async = "false";
- xmlDoc.loadXML(xmlDocStr);
- }
- return xmlDoc;
- };
-
- this.xml2json = function (xmlDoc) {
- return parseDOMChildren(xmlDoc);
- };
-
- this.xml_str2json = function (xmlDocStr) {
- var xmlDoc = this.parseXmlString(xmlDocStr);
- return this.xml2json(xmlDoc);
- };
-
- this.json2xml_str = function (jsonObj) {
- return parseJSONObject(jsonObj);
- };
-
- this.json2xml = function (jsonObj) {
- var xmlDocStr = this.json2xml_str(jsonObj);
- return this.parseXmlString(xmlDocStr);
- };
-
- this.getVersion = function () {
- return VERSION;
- };
-
- this.escapeMode = function (enabled) {
- escapeMode = enabled;
- };
- }
-
- var x2js = new X2JS();
- return {
- /** Called to load in the legacy tree js which is required on startup if a user is logged in or
- after login, but cannot be called until they are authenticated which is why it needs to be lazy loaded. */
- toJson: function (xml) {
- var json = x2js.xml_str2json(xml);
- return json;
- },
- fromJson: function (json) {
- var xml = x2js.json2xml_str(json);
- return xml;
- }
- };
-}
+/**
+ * @ngdoc service
+ * @name umbraco.services.xmlhelper
+ * @function
+ *
+ * @description
+ * Used to convert legacy xml data to json and back again
+ */
+function xmlhelper($http) {
+ /*
+ Copyright 2011 Abdulla Abdurakhmanov
+ Original sources are available at https://code.google.com/p/x2js/
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+
+ function X2JS() {
+ var VERSION = "1.0.11";
+ var escapeMode = false;
+
+ var DOMNodeTypes = {
+ ELEMENT_NODE: 1,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ DOCUMENT_NODE: 9
+ };
+
+ function getNodeLocalName(node) {
+ var nodeLocalName = node.localName;
+ if (nodeLocalName == null) {
+ nodeLocalName = node.baseName;
+ } // Yeah, this is IE!!
+
+ if (nodeLocalName === null || nodeLocalName === "") {
+ nodeLocalName = node.nodeName;
+ } // =="" is IE too
+
+ return nodeLocalName;
+ }
+
+ function getNodePrefix(node) {
+ return node.prefix;
+ }
+
+ function escapeXmlChars(str) {
+ if (typeof (str) === "string") {
+ return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g, '/');
+ } else {
+ return str;
+ }
+ }
+
+ function unescapeXmlChars(str) {
+ return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, "'").replace(///g, '\/');
+ }
+
+ function parseDOMChildren(node) {
+ var result, child, childName;
+
+ if (node.nodeType === DOMNodeTypes.DOCUMENT_NODE) {
+ result = {};
+ child = node.firstChild;
+ childName = getNodeLocalName(child);
+ result[childName] = parseDOMChildren(child);
+ return result;
+ }
+ else {
+
+ if (node.nodeType === DOMNodeTypes.ELEMENT_NODE) {
+ result = {};
+ result.__cnt = 0;
+ var nodeChildren = node.childNodes;
+
+ // Children nodes
+ for (var cidx = 0; cidx < nodeChildren.length; cidx++) {
+ child = nodeChildren.item(cidx); // nodeChildren[cidx];
+ childName = getNodeLocalName(child);
+
+ result.__cnt++;
+ if (result[childName] === null) {
+ result[childName] = parseDOMChildren(child);
+ result[childName + "_asArray"] = new Array(1);
+ result[childName + "_asArray"][0] = result[childName];
+ }
+ else {
+ if (result[childName] !== null) {
+ if (!(result[childName] instanceof Array)) {
+ var tmpObj = result[childName];
+ result[childName] = [];
+ result[childName][0] = tmpObj;
+
+ result[childName + "_asArray"] = result[childName];
+ }
+ }
+ var aridx = 0;
+ while (result[childName][aridx] !== null) {
+ aridx++;
+ }
+
+ (result[childName])[aridx] = parseDOMChildren(child);
+ }
+ }
+
+ // Attributes
+ for (var aidx = 0; aidx < node.attributes.length; aidx++) {
+ var attr = node.attributes.item(aidx); // [aidx];
+ result.__cnt++;
+ result["_" + attr.name] = attr.value;
+ }
+
+ // Node namespace prefix
+ var nodePrefix = getNodePrefix(node);
+ if (nodePrefix !== null && nodePrefix !== "") {
+ result.__cnt++;
+ result.__prefix = nodePrefix;
+ }
+
+ if (result.__cnt === 1 && result["#text"] !== null) {
+ result = result["#text"];
+ }
+
+ if (result["#text"] !== null) {
+ result.__text = result["#text"];
+ if (escapeMode) {
+ result.__text = unescapeXmlChars(result.__text);
+ }
+
+ delete result["#text"];
+ delete result["#text_asArray"];
+ }
+ if (result["#cdata-section"] != null) {
+ result.__cdata = result["#cdata-section"];
+ delete result["#cdata-section"];
+ delete result["#cdata-section_asArray"];
+ }
+
+ if (result.__text != null || result.__cdata != null) {
+ result.toString = function () {
+ return (this.__text != null ? this.__text : '') + (this.__cdata != null ? this.__cdata : '');
+ };
+ }
+ return result;
+ }
+ else {
+ if (node.nodeType === DOMNodeTypes.TEXT_NODE || node.nodeType === DOMNodeTypes.CDATA_SECTION_NODE) {
+ return node.nodeValue;
+ }
+ }
+ }
+ }
+
+ function startTag(jsonObj, element, attrList, closed) {
+ var resultStr = "<" + ((jsonObj != null && jsonObj.__prefix != null) ? (jsonObj.__prefix + ":") : "") + element;
+ if (attrList != null) {
+ for (var aidx = 0; aidx < attrList.length; aidx++) {
+ var attrName = attrList[aidx];
+ var attrVal = jsonObj[attrName];
+ resultStr += " " + attrName.substr(1) + "='" + attrVal + "'";
+ }
+ }
+ if (!closed) {
+ resultStr += ">";
+ } else {
+ resultStr += "/>";
+ }
+
+ return resultStr;
+ }
+
+ function endTag(jsonObj, elementName) {
+ return "" + (jsonObj.__prefix !== null ? (jsonObj.__prefix + ":") : "") + elementName + ">";
+ }
+
+ function endsWith(str, suffix) {
+ return str.indexOf(suffix, str.length - suffix.length) !== -1;
+ }
+
+ function jsonXmlSpecialElem(jsonObj, jsonObjField) {
+ if (endsWith(jsonObjField.toString(), ("_asArray")) || jsonObjField.toString().indexOf("_") === 0 || (jsonObj[jsonObjField] instanceof Function)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ function jsonXmlElemCount(jsonObj) {
+ var elementsCnt = 0;
+ if (jsonObj instanceof Object) {
+ for (var it in jsonObj) {
+ if (jsonXmlSpecialElem(jsonObj, it)) {
+ continue;
+ }
+ elementsCnt++;
+ }
+ }
+ return elementsCnt;
+ }
+
+ function parseJSONAttributes(jsonObj) {
+ var attrList = [];
+ if (jsonObj instanceof Object) {
+ for (var ait in jsonObj) {
+ if (ait.toString().indexOf("__") === -1 && ait.toString().indexOf("_") === 0) {
+ attrList.push(ait);
+ }
+ }
+ }
+
+ return attrList;
+ }
+
+ function parseJSONTextAttrs(jsonTxtObj) {
+ var result = "";
+
+ if (jsonTxtObj.__cdata != null) {
+ result += "";
+ }
+
+ if (jsonTxtObj.__text != null) {
+ if (escapeMode) {
+ result += escapeXmlChars(jsonTxtObj.__text);
+ } else {
+ result += jsonTxtObj.__text;
+ }
+ }
+ return result;
+ }
+
+ function parseJSONTextObject(jsonTxtObj) {
+ var result = "";
+
+ if (jsonTxtObj instanceof Object) {
+ result += parseJSONTextAttrs(jsonTxtObj);
+ }
+ else {
+ if (jsonTxtObj != null) {
+ if (escapeMode) {
+ result += escapeXmlChars(jsonTxtObj);
+ } else {
+ result += jsonTxtObj;
+ }
+ }
+ }
+
+
+ return result;
+ }
+
+ function parseJSONArray(jsonArrRoot, jsonArrObj, attrList) {
+ var result = "";
+ if (jsonArrRoot.length === 0) {
+ result += startTag(jsonArrRoot, jsonArrObj, attrList, true);
+ }
+ else {
+ for (var arIdx = 0; arIdx < jsonArrRoot.length; arIdx++) {
+ result += startTag(jsonArrRoot[arIdx], jsonArrObj, parseJSONAttributes(jsonArrRoot[arIdx]), false);
+ result += parseJSONObject(jsonArrRoot[arIdx]);
+ result += endTag(jsonArrRoot[arIdx], jsonArrObj);
+ }
+ }
+ return result;
+ }
+
+ function parseJSONObject(jsonObj) {
+ var result = "";
+
+ var elementsCnt = jsonXmlElemCount(jsonObj);
+
+ if (elementsCnt > 0) {
+ for (var it in jsonObj) {
+ if (jsonXmlSpecialElem(jsonObj, it)) {
+ continue;
+ }
+
+ var subObj = jsonObj[it];
+ var attrList = parseJSONAttributes(subObj);
+
+ if (subObj === null || subObj === undefined) {
+ result += startTag(subObj, it, attrList, true);
+ } else {
+ if (subObj instanceof Object) {
+
+ if (subObj instanceof Array) {
+ result += parseJSONArray(subObj, it, attrList);
+ } else {
+ var subObjElementsCnt = jsonXmlElemCount(subObj);
+ if (subObjElementsCnt > 0 || subObj.__text !== null || subObj.__cdata !== null) {
+ result += startTag(subObj, it, attrList, false);
+ result += parseJSONObject(subObj);
+ result += endTag(subObj, it);
+ } else {
+ result += startTag(subObj, it, attrList, true);
+ }
+ }
+
+ } else {
+ result += startTag(subObj, it, attrList, false);
+ result += parseJSONTextObject(subObj);
+ result += endTag(subObj, it);
+ }
+ }
+ }
+ }
+ result += parseJSONTextObject(jsonObj);
+
+ return result;
+ }
+
+ this.parseXmlString = function (xmlDocStr) {
+ var xmlDoc;
+ if (window.DOMParser) {
+ var parser = new window.DOMParser();
+ xmlDoc = parser.parseFromString(xmlDocStr, "text/xml");
+ }
+ else {
+ // IE :(
+ if (xmlDocStr.indexOf("") === 0) {
+ xmlDocStr = xmlDocStr.substr(xmlDocStr.indexOf("?>") + 2);
+ }
+ xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
+ xmlDoc.async = "false";
+ xmlDoc.loadXML(xmlDocStr);
+ }
+ return xmlDoc;
+ };
+
+ this.xml2json = function (xmlDoc) {
+ return parseDOMChildren(xmlDoc);
+ };
+
+ this.xml_str2json = function (xmlDocStr) {
+ var xmlDoc = this.parseXmlString(xmlDocStr);
+ return this.xml2json(xmlDoc);
+ };
+
+ this.json2xml_str = function (jsonObj) {
+ return parseJSONObject(jsonObj);
+ };
+
+ this.json2xml = function (jsonObj) {
+ var xmlDocStr = this.json2xml_str(jsonObj);
+ return this.parseXmlString(xmlDocStr);
+ };
+
+ this.getVersion = function () {
+ return VERSION;
+ };
+
+ this.escapeMode = function (enabled) {
+ escapeMode = enabled;
+ };
+ }
+
+ var x2js = new X2JS();
+ return {
+ /** Called to load in the legacy tree js which is required on startup if a user is logged in or
+ after login, but cannot be called until they are authenticated which is why it needs to be lazy loaded. */
+ toJson: function (xml) {
+ var json = x2js.xml_str2json(xml);
+ return json;
+ },
+ fromJson: function (json) {
+ var xml = x2js.json2xml_str(json);
+ return xml;
+ }
+ };
+}
angular.module('umbraco.services').factory('xmlhelper', xmlhelper);
})();
\ No newline at end of file
diff --git a/OurUmbraco.Site/umbraco/lib/angular-local-storage/angular-local-storage.min.js b/OurUmbraco.Site/umbraco/lib/angular-local-storage/angular-local-storage.min.js
index 95cbd07d..b0111f12 100644
--- a/OurUmbraco.Site/umbraco/lib/angular-local-storage/angular-local-storage.min.js
+++ b/OurUmbraco.Site/umbraco/lib/angular-local-storage/angular-local-storage.min.js
@@ -1,7 +1,9 @@
/**
* An Angular module that gives you access to the browsers local storage
- * @version v0.2.3 - 2015-10-11
+ * @version v0.2.6 - 2016-03-16
* @link https://github.com/grevory/angular-local-storage
* @author grevory
* @license MIT License, http://www.opensource.org/licenses/MIT
- */!function(a,b){"use strict";var c=b.isDefined,d=b.isUndefined,e=b.isNumber,f=b.isObject,g=b.isArray,h=b.extend,i=b.toJson,j=b.module("LocalStorageModule",[]);j.provider("localStorageService",function(){this.prefix="ls",this.storageType="localStorage",this.cookie={expiry:30,path:"/"},this.notify={setItem:!0,removeItem:!1},this.setPrefix=function(a){return this.prefix=a,this},this.setStorageType=function(a){return this.storageType=a,this},this.setStorageCookie=function(a,b){return this.cookie.expiry=a,this.cookie.path=b,this},this.setStorageCookieDomain=function(a){return this.cookie.domain=a,this},this.setNotify=function(a,b){return this.notify={setItem:a,removeItem:b},this},this.$get=["$rootScope","$window","$document","$parse",function(a,b,j,k){var l,m=this,n=m.prefix,o=m.cookie,p=m.notify,q=m.storageType;j?j[0]&&(j=j[0]):j=document,"."!==n.substr(-1)&&(n=n?n+".":"");var r=function(a){return n+a},s=function(){try{var c=q in b&&null!==b[q],d=r("__"+Math.round(1e7*Math.random()));return c&&(l=b[q],l.setItem(d,""),l.removeItem(d)),c}catch(e){return q="cookie",a.$broadcast("LocalStorageModule.notification.error",e.message),!1}}(),t=function(b,c){if(c=d(c)?null:i(c),!s||"cookie"===m.storageType)return s||a.$broadcast("LocalStorageModule.notification.warning","LOCAL_STORAGE_NOT_SUPPORTED"),p.setItem&&a.$broadcast("LocalStorageModule.notification.setitem",{key:b,newvalue:c,storageType:"cookie"}),z(b,c);try{l&&l.setItem(r(b),c),p.setItem&&a.$broadcast("LocalStorageModule.notification.setitem",{key:b,newvalue:c,storageType:m.storageType})}catch(e){return a.$broadcast("LocalStorageModule.notification.error",e.message),z(b,c)}return!0},u=function(b){if(!s||"cookie"===m.storageType)return s||a.$broadcast("LocalStorageModule.notification.warning","LOCAL_STORAGE_NOT_SUPPORTED"),A(b);var c=l?l.getItem(r(b)):null;if(!c||"null"===c)return null;try{return JSON.parse(c)}catch(d){return c}},v=function(){var b,c;for(b=0;b0||(j.cookie="test").indexOf.call(j.cookie,"test")>-1)}catch(c){return a.$broadcast("LocalStorageModule.notification.error",c.message),!1}}(),z=function(b,c,h){if(d(c))return!1;if((g(c)||f(c))&&(c=i(c)),!y)return a.$broadcast("LocalStorageModule.notification.error","COOKIES_NOT_SUPPORTED"),!1;try{var k="",l=new Date,m="";if(null===c?(l.setTime(l.getTime()+-864e5),k="; expires="+l.toGMTString(),c=""):e(h)&&0!==h?(l.setTime(l.getTime()+24*h*60*60*1e3),k="; expires="+l.toGMTString()):0!==o.expiry&&(l.setTime(l.getTime()+24*o.expiry*60*60*1e3),k="; expires="+l.toGMTString()),b){var n="; path="+o.path;o.domain&&(m="; domain="+o.domain),j.cookie=r(b)+"="+encodeURIComponent(c)+k+n+m}}catch(p){return a.$broadcast("LocalStorageModule.notification.error",p.message),!1}return!0},A=function(b){if(!y)return a.$broadcast("LocalStorageModule.notification.error","COOKIES_NOT_SUPPORTED"),!1;for(var c=j.cookie&&j.cookie.split(";")||[],d=0;d0||(j.cookie="test").indexOf.call(j.cookie,"test")>-1)}catch(c){return a.$broadcast("LocalStorageModule.notification.error",c.message),!1}}(),z=function(b,c,h){if(d(c))return!1;if((g(c)||f(c))&&(c=i(c)),!y)return a.$broadcast("LocalStorageModule.notification.error","COOKIES_NOT_SUPPORTED"),!1;try{var k="",l=new Date,m="";if(null===c?(l.setTime(l.getTime()+-864e5),k="; expires="+l.toGMTString(),c=""):e(h)&&0!==h?(l.setTime(l.getTime()+24*h*60*60*1e3),k="; expires="+l.toGMTString()):0!==o.expiry&&(l.setTime(l.getTime()+24*o.expiry*60*60*1e3),k="; expires="+l.toGMTString()),b){var n="; path="+o.path;o.domain&&(m="; domain="+o.domain),j.cookie=r(b)+"="+encodeURIComponent(c)+k+n+m}}catch(p){return a.$broadcast("LocalStorageModule.notification.error",p.message),!1}return!0},A=function(b){if(!y)return a.$broadcast("LocalStorageModule.notification.error","COOKIES_NOT_SUPPORTED"),!1;for(var c=j.cookie&&j.cookie.split(";")||[],d=0;d` or `` tags.
*
- * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
- * found in the document will be used to define the root element to auto-bootstrap as an
- * application. To run multiple applications in an HTML document you must manually bootstrap them using
- * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
+ * There are a few things to keep in mind when using `ngApp`:
+ * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
+ * found in the document will be used to define the root element to auto-bootstrap as an
+ * application. To run multiple applications in an HTML document you must manually bootstrap them using
+ * {@link angular.bootstrap} instead.
+ * - AngularJS applications cannot be nested within each other.
+ * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
+ * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
+ * {@link ngRoute.ngView `ngView`}.
+ * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
+ * causing animations to stop working and making the injector inaccessible from outside the app.
*
* You can specify an **AngularJS module** to be used as the root module for the application. This
* module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
@@ -1614,16 +1624,25 @@ function angularInit(element, bootstrap) {
* @description
* Use this function to manually start up angular application.
*
- * See: {@link guide/bootstrap Bootstrap}
- *
- * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
- * They must use {@link ng.directive:ngApp ngApp}.
+ * For more information, see the {@link guide/bootstrap Bootstrap guide}.
*
* Angular will detect if it has been loaded into the browser more than once and only allow the
* first loaded script to be bootstrapped and will report a warning to the browser console for
* each of the subsequent scripts. This prevents strange results in applications, where otherwise
* multiple instances of Angular try to work on the DOM.
*
+ *
+ * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
+ * They must use {@link ng.directive:ngApp ngApp}.
+ *
+ *
+ *
+ * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
+ * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
+ * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
+ * causing animations to stop working and making the injector inaccessible from outside the app.
+ *
+ *
* ```html
*
*
@@ -2424,11 +2443,11 @@ function toDebugString(obj) {
* - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
*/
var version = {
- full: '1.5.0', // all of these placeholder strings will be replaced by grunt's
+ full: '1.5.1', // all of these placeholder strings will be replaced by grunt's
major: 1, // package task
minor: 5,
- dot: 0,
- codeName: 'ennoblement-facilitation'
+ dot: 1,
+ codeName: 'equivocal-sophistication'
};
@@ -4287,14 +4306,13 @@ function annotate(fn, strictDi, name) {
* @description
*
* Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
- * number, an array, an object or a function. This is short for registering a service where its
+ * number, an array, an object or a function. This is short for registering a service where its
* provider's `$get` property is a factory function that takes no arguments and returns the **value
- * service**.
+ * service**. That also means it is not possible to inject other services into a value service.
*
* Value services are similar to constant services, except that they cannot be injected into a
* module configuration function (see {@link angular.Module#config}) but they can be overridden by
- * an Angular
- * {@link auto.$provide#decorator decorator}.
+ * an Angular {@link auto.$provide#decorator decorator}.
*
* @param {string} name The name of the instance.
* @param {*} value The value.
@@ -4319,8 +4337,11 @@ function annotate(fn, strictDi, name) {
* @name $provide#constant
* @description
*
- * Register a **constant service**, such as a string, a number, an array, an object or a function,
- * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
+ * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
+ * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
+ * possible to inject other services into a constant.
+ *
+ * But unlike {@link auto.$provide#value value}, a constant can be
* injected into a module configuration function (see {@link angular.Module#config}) and it cannot
* be overridden by an Angular {@link auto.$provide#decorator decorator}.
*
@@ -4959,7 +4980,7 @@ function prepareAnimateOptions(options) {
}
var $$CoreAnimateJsProvider = function() {
- this.$get = function() {};
+ this.$get = noop;
};
// this is prefixed with Core since it conflicts with
@@ -7367,6 +7388,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// The assumption is that future DOM event attribute names will begin with
// 'on' and be composed of only English letters.
var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
+ var bindingCache = createMap();
function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
@@ -7374,6 +7396,10 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
var bindings = {};
forEach(scope, function(definition, scopeName) {
+ if (definition in bindingCache) {
+ bindings[scopeName] = bindingCache[definition];
+ return;
+ }
var match = definition.match(LOCAL_REGEXP);
if (!match) {
@@ -7391,6 +7417,9 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
optional: match[3] === '?',
attrName: match[4] || scopeName
};
+ if (match[4]) {
+ bindingCache[definition] = bindings[scopeName];
+ }
});
return bindings;
@@ -7460,7 +7489,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
* @returns {ng.$compileProvider} Self for chaining.
*/
- this.directive = function registerDirective(name, directiveFactory) {
+ this.directive = function registerDirective(name, directiveFactory) {
assertNotHasOwnProperty(name, 'directive');
if (isString(name)) {
assertValidDirectiveName(name);
@@ -7483,11 +7512,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
directive.name = directive.name || name;
directive.require = directive.require || (directive.controller && directive.name);
directive.restrict = directive.restrict || 'EA';
- var bindings = directive.$$bindings =
- parseDirectiveBindings(directive, directive.name);
- if (isObject(bindings.isolateScope)) {
- directive.$$isolateBindings = bindings.isolateScope;
- }
directive.$$moduleName = directiveFactory.$$moduleName;
directives.push(directive);
} catch (e) {
@@ -7543,7 +7567,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* See {@link ng.$compile#-bindtocontroller- `bindToController`}.
* - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
* Disabled by default.
- * - `$...` – `{function()=}` – additional annotations to provide to the directive factory function.
+ * - `$...` – additional properties to attach to the directive factory function and the controller
+ * constructor function. (This is used by the component router to annotate)
*
* @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
* @description
@@ -7575,7 +7600,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
*
* myMod.component('myComp', {
* templateUrl: 'views/my-comp.html',
- * controller: 'MyCtrl as ctrl',
+ * controller: 'MyCtrl',
+ * controllerAs: 'ctrl',
* bindings: {name: '@'}
* });
*
@@ -7586,7 +7612,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
*/
this.component = function registerComponent(name, options) {
- var controller = options.controller || function() {};
+ var controller = options.controller || noop;
function factory($injector) {
function makeInjectable(fn) {
@@ -7618,6 +7644,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
forEach(options, function(val, key) {
if (key.charAt(0) === '$') {
factory[key] = val;
+ controller[key] = val;
}
});
@@ -8020,6 +8047,14 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
} : noop;
+ compile.$$createComment = function(directiveName, comment) {
+ var content = '';
+ if (debugInfoEnabled) {
+ content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
+ }
+ return document.createComment(content);
+ };
+
return compile;
//================================
@@ -8031,19 +8066,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
// modify it.
$compileNodes = jqLite($compileNodes);
}
-
- var NOT_EMPTY = /\S+/;
-
- // We can not compile top level text elements since text nodes can be merged and we will
- // not be able to attach scope data to them, so we will wrap them in
- for (var i = 0, len = $compileNodes.length; i < len; i++) {
- var domNode = $compileNodes[i];
-
- if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
- jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
- }
- }
-
var compositeLinkFn =
compileNodes($compileNodes, transcludeFn, $compileNodes,
maxPriority, ignoreDirective, previousCompileContext);
@@ -8429,23 +8451,21 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
* @returns {Function}
*/
function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
- if (eager) {
- return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
+ var compiled;
+
+ if (eager) {
+ return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
+ }
+ return function() {
+ if (!compiled) {
+ compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
+
+ // Null out all of these references in order to make them eligible for garbage collection
+ // since this is a potentially long lived closure
+ $compileNodes = transcludeFn = previousCompileContext = null;
}
-
- var compiled;
-
- return function() {
- if (!compiled) {
- compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
-
- // Null out all of these references in order to make them eligible for garbage collection
- // since this is a potentially long lived closure
- $compileNodes = transcludeFn = previousCompileContext = null;
- }
-
- return compiled.apply(this, arguments);
- };
+ return compiled.apply(this, arguments);
+ };
}
/**
@@ -8581,8 +8601,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
terminalPriority = directive.priority;
$template = $compileNode;
$compileNode = templateAttrs.$$element =
- jqLite(document.createComment(' ' + directiveName + ': ' +
- templateAttrs[directiveName] + ' '));
+ jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
compileNode = $compileNode[0];
replaceWith(jqCollection, sliceArgs($template), compileNode);
@@ -8788,82 +8807,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
-
- function getControllers(directiveName, require, $element, elementControllers) {
- var value;
-
- if (isString(require)) {
- var match = require.match(REQUIRE_PREFIX_REGEXP);
- var name = require.substring(match[0].length);
- var inheritType = match[1] || match[3];
- var optional = match[2] === '?';
-
- //If only parents then start at the parent element
- if (inheritType === '^^') {
- $element = $element.parent();
- //Otherwise attempt getting the controller from elementControllers in case
- //the element is transcluded (and has no data) and to avoid .data if possible
- } else {
- value = elementControllers && elementControllers[name];
- value = value && value.instance;
- }
-
- if (!value) {
- var dataName = '$' + name + 'Controller';
- value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
- }
-
- if (!value && !optional) {
- throw $compileMinErr('ctreq',
- "Controller '{0}', required by directive '{1}', can't be found!",
- name, directiveName);
- }
- } else if (isArray(require)) {
- value = [];
- for (var i = 0, ii = require.length; i < ii; i++) {
- value[i] = getControllers(directiveName, require[i], $element, elementControllers);
- }
- } else if (isObject(require)) {
- value = {};
- forEach(require, function(controller, property) {
- value[property] = getControllers(directiveName, controller, $element, elementControllers);
- });
- }
-
- return value || null;
- }
-
- function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
- var elementControllers = createMap();
- for (var controllerKey in controllerDirectives) {
- var directive = controllerDirectives[controllerKey];
- var locals = {
- $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
- $element: $element,
- $attrs: attrs,
- $transclude: transcludeFn
- };
-
- var controller = directive.controller;
- if (controller == '@') {
- controller = attrs[directive.name];
- }
-
- var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
-
- // For directives with element transclusion the element is a comment,
- // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
- // clean up (http://bugs.jquery.com/ticket/8335).
- // Instead, we save the controllers for the element in a local hash and attach to .data
- // later, once we have the actual element.
- elementControllers[directive.name] = controllerInstance;
- if (!hasElementTranscludeDirective) {
- $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
- }
- }
- return elementControllers;
- }
-
function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
attrs, removeScopeBindingWatches, removeControllerBindingWatches;
@@ -8895,7 +8838,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
if (controllerDirectives) {
- elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
+ elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
}
if (newIsolateScopeDirective) {
@@ -9023,6 +8966,78 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
}
}
+ function getControllers(directiveName, require, $element, elementControllers) {
+ var value;
+
+ if (isString(require)) {
+ var match = require.match(REQUIRE_PREFIX_REGEXP);
+ var name = require.substring(match[0].length);
+ var inheritType = match[1] || match[3];
+ var optional = match[2] === '?';
+
+ //If only parents then start at the parent element
+ if (inheritType === '^^') {
+ $element = $element.parent();
+ //Otherwise attempt getting the controller from elementControllers in case
+ //the element is transcluded (and has no data) and to avoid .data if possible
+ } else {
+ value = elementControllers && elementControllers[name];
+ value = value && value.instance;
+ }
+
+ if (!value) {
+ var dataName = '$' + name + 'Controller';
+ value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
+ }
+
+ if (!value && !optional) {
+ throw $compileMinErr('ctreq',
+ "Controller '{0}', required by directive '{1}', can't be found!",
+ name, directiveName);
+ }
+ } else if (isArray(require)) {
+ value = [];
+ for (var i = 0, ii = require.length; i < ii; i++) {
+ value[i] = getControllers(directiveName, require[i], $element, elementControllers);
+ }
+ } else if (isObject(require)) {
+ value = {};
+ forEach(require, function(controller, property) {
+ value[property] = getControllers(directiveName, controller, $element, elementControllers);
+ });
+ }
+
+ return value || null;
+ }
+
+ function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
+ var elementControllers = createMap();
+ for (var controllerKey in controllerDirectives) {
+ var directive = controllerDirectives[controllerKey];
+ var locals = {
+ $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
+ $element: $element,
+ $attrs: attrs,
+ $transclude: transcludeFn
+ };
+
+ var controller = directive.controller;
+ if (controller == '@') {
+ controller = attrs[directive.name];
+ }
+
+ var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
+
+ // For directives with element transclusion the element is a comment.
+ // In this case .data will not attach any data.
+ // Instead, we save the controllers for the element in a local hash and attach to .data
+ // later, once we have the actual element.
+ elementControllers[directive.name] = controllerInstance;
+ $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
+ }
+ return elementControllers;
+ }
+
// Depending upon the context in which a directive finds itself it might need to have a new isolated
// or child scope created. For instance:
// * if the directive has been pulled into a template because another directive with a higher priority
@@ -9063,6 +9078,13 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
if (startAttrName) {
directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
}
+ if (!directive.$$bindings) {
+ var bindings = directive.$$bindings =
+ parseDirectiveBindings(directive, directive.name);
+ if (isObject(bindings.isolateScope)) {
+ directive.$$isolateBindings = bindings.isolateScope;
+ }
+ }
tDirectives.push(directive);
match = directive;
}
@@ -9744,6 +9766,15 @@ function $ControllerProvider() {
var controllers = {},
globals = false;
+ /**
+ * @ngdoc method
+ * @name $controllerProvider#has
+ * @param {string} name Controller name to check.
+ */
+ this.has = function(name) {
+ return controllers.hasOwnProperty(name);
+ };
+
/**
* @ngdoc method
* @name $controllerProvider#register
@@ -10247,10 +10278,9 @@ function $HttpProvider() {
*
* Object containing default values for all {@link ng.$http $http} requests.
*
- * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
- * that will provide the cache for all requests who set their `cache` property to `true`.
- * If you set the `defaults.cache = false` then only requests that specify their own custom
- * cache object will be cached. See {@link $http#caching $http Caching} for more information.
+ * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
+ * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
+ * by default. See {@link $http#caching $http Caching} for more information.
*
* - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
* Defaults value is `'XSRF-TOKEN'`.
@@ -10541,6 +10571,15 @@ function $HttpProvider() {
* the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
* which allows you to `push` or `unshift` a new transformation function into the transformation chain.
*
+ *
+ * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
+ * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
+ * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
+ * function will be reflected on the scope and in any templates where the object is data-bound.
+ * To prevent his, transform functions should have no side-effects.
+ * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
+ *
+ *
* ### Default Transformations
*
* The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
@@ -10598,26 +10637,35 @@ function $HttpProvider() {
*
* ## Caching
*
- * To enable caching, set the request configuration `cache` property to `true` (to use default
- * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
- * When the cache is enabled, `$http` stores the response from the server in the specified
- * cache. The next time the same request is made, the response is served from the cache without
- * sending a request to the server.
+ * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
+ * set the config.cache value or the default cache value to TRUE or to a cache object (created
+ * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
+ * precedence over the default cache value.
*
- * Note that even if the response is served from cache, delivery of the data is asynchronous in
- * the same way that real requests are.
+ * In order to:
+ * * cache all responses - set the default cache value to TRUE or to a cache object
+ * * cache a specific response - set config.cache value to TRUE or to a cache object
*
- * If there are multiple GET requests for the same URL that should be cached using the same
- * cache, but the cache is not populated yet, only one request to the server will be made and
- * the remaining requests will be fulfilled using the response from the first request.
+ * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
+ * then the default `$cacheFactory($http)` object is used.
*
- * You can change the default cache to a new object (built with
- * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
- * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
- * their `cache` property to `true` will now use this cache object.
+ * The default cache value can be set by updating the
+ * {@link ng.$http#defaults `$http.defaults.cache`} property or the
+ * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
+ *
+ * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
+ * the relevant cache object. The next time the same request is made, the response is returned
+ * from the cache without sending a request to the server.
+ *
+ * Take note that:
+ *
+ * * Only GET and JSONP requests are cached.
+ * * The cache key is the request URL including search parameters; headers are not considered.
+ * * Cached responses are returned asynchronously, in the same way as responses from the server.
+ * * If multiple identical requests are made using the same cache, which is not yet populated,
+ * one request will be made to the server and remaining requests will return the same response.
+ * * A cache-control header on the response does not affect if or how responses are cached.
*
- * If you set the default cache to `false` then only requests that specify their own custom
- * cache object will be cached.
*
* ## Interceptors
*
@@ -10787,7 +10835,7 @@ function $HttpProvider() {
* transform function or an array of such functions. The transform function takes the http
* response body, headers and status and returns its transformed (typically deserialized) version.
* See {@link ng.$http#overriding-the-default-transformations-per-request
- * Overriding the Default TransformationjqLiks}
+ * Overriding the Default Transformations}
* - **paramSerializer** - `{string|function(Object):string}` - A function used to
* prepare the string representation of request parameters (specified as an object).
* If specified as string, it is interpreted as function registered with the
@@ -10795,10 +10843,9 @@ function $HttpProvider() {
* by registering it as a {@link auto.$provide#service service}.
* The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
* alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
- * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
- * GET request, otherwise if a cache instance built with
- * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
- * caching.
+ * - **cache** – `{boolean|Object}` – A boolean value or object created with
+ * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
+ * See {@link $http#caching $http Caching} for more information.
* - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
* that should abort the request when resolved.
* - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
@@ -13709,8 +13756,10 @@ AST.prototype = {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
- } else if (this.constants.hasOwnProperty(this.peek().text)) {
- primary = copy(this.constants[this.consume().text]);
+ } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
+ primary = copy(this.selfReferential[this.consume().text]);
+ } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
+ primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
} else if (this.peek().identifier) {
primary = this.identifier();
} else if (this.peek().constant) {
@@ -13862,15 +13911,7 @@ AST.prototype = {
return false;
},
-
- /* `undefined` is not a constant, it is an identifier,
- * but using it as an identifier is not supported
- */
- constants: {
- 'true': { type: AST.Literal, value: true },
- 'false': { type: AST.Literal, value: false },
- 'null': { type: AST.Literal, value: null },
- 'undefined': {type: AST.Literal, value: undefined },
+ selfReferential: {
'this': {type: AST.ThisExpression },
'$locals': {type: AST.LocalsExpression }
}
@@ -14560,7 +14601,7 @@ ASTInterpreter.prototype = {
forEach(ast.body, function(expression) {
expressions.push(self.recurse(expression.expression));
});
- var fn = ast.body.length === 0 ? function() {} :
+ var fn = ast.body.length === 0 ? noop :
ast.body.length === 1 ? expressions[0] :
function(scope, locals) {
var lastValue;
@@ -14915,7 +14956,7 @@ var Parser = function(lexer, $filter, options) {
this.lexer = lexer;
this.$filter = $filter;
this.options = options;
- this.ast = new AST(this.lexer);
+ this.ast = new AST(lexer, options);
this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
new ASTCompiler(this.ast, $filter);
};
@@ -14992,16 +15033,39 @@ function getValueOf(value) {
function $ParseProvider() {
var cacheDefault = createMap();
var cacheExpensive = createMap();
+ var literals = {
+ 'true': true,
+ 'false': false,
+ 'null': null,
+ 'undefined': undefined
+ };
+
+ /**
+ * @ngdoc method
+ * @name $parseProvider#addLiteral
+ * @description
+ *
+ * Configure $parse service to add literal values that will be present as literal at expressions.
+ *
+ * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
+ * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
+ *
+ **/
+ this.addLiteral = function(literalName, literalValue) {
+ literals[literalName] = literalValue;
+ };
this.$get = ['$filter', function($filter) {
var noUnsafeEval = csp().noUnsafeEval;
var $parseOptions = {
csp: noUnsafeEval,
- expensiveChecks: false
+ expensiveChecks: false,
+ literals: copy(literals)
},
$parseOptionsExpensive = {
csp: noUnsafeEval,
- expensiveChecks: true
+ expensiveChecks: true,
+ literals: copy(literals)
};
var runningChecksEnabled = false;
@@ -15423,7 +15487,7 @@ function $ParseProvider() {
* - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
* all the important functionality needed for common async tasks.
*
- * # Testing
+ * # Testing
*
* ```js
* it('should simulate promise', inject(function($q, $rootScope) {
@@ -19484,7 +19548,7 @@ function currencyFilter($locale) {
* Formats a number as text.
*
* If the input is null or undefined, it will just be returned.
- * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
+ * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
* If the input is not a number an empty string is returned.
*
*
@@ -19574,7 +19638,7 @@ function parse(numStr) {
}
// Count the number of leading zeros.
- for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++);
+ for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
if (i == (zeros = numStr.length)) {
// The digits are all zero.
@@ -19620,18 +19684,37 @@ function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
var digit = digits[roundAt];
if (roundAt > 0) {
- digits.splice(roundAt);
+ // Drop fractional digits beyond `roundAt`
+ digits.splice(Math.max(parsedNumber.i, roundAt));
+
+ // Set non-fractional digits beyond `roundAt` to 0
+ for (var j = roundAt; j < digits.length; j++) {
+ digits[j] = 0;
+ }
} else {
// We rounded to zero so reset the parsedNumber
+ fractionLen = Math.max(0, fractionLen);
parsedNumber.i = 1;
- digits.length = roundAt = fractionSize + 1;
- for (var i=0; i < roundAt; i++) digits[i] = 0;
+ digits.length = Math.max(1, roundAt = fractionSize + 1);
+ digits[0] = 0;
+ for (var i = 1; i < roundAt; i++) digits[i] = 0;
}
- if (digit >= 5) digits[roundAt - 1]++;
+ if (digit >= 5) {
+ if (roundAt - 1 < 0) {
+ for (var k = 0; k > roundAt; k--) {
+ digits.unshift(0);
+ parsedNumber.i++;
+ }
+ digits.unshift(1);
+ parsedNumber.i++;
+ } else {
+ digits[roundAt - 1]++;
+ }
+ }
// Pad out with zeros to get the required fraction length
- for (; fractionLen < fractionSize; fractionLen++) digits.push(0);
+ for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
// Do any carrying, e.g. a digit was rounded up to 10
@@ -19730,11 +19813,15 @@ function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
}
}
-function padNumber(num, digits, trim) {
+function padNumber(num, digits, trim, negWrap) {
var neg = '';
- if (num < 0) {
- neg = '-';
- num = -num;
+ if (num < 0 || (negWrap && num <= 0)) {
+ if (negWrap) {
+ num = -num + 1;
+ } else {
+ num = -num;
+ neg = '-';
+ }
}
num = '' + num;
while (num.length < digits) num = ZERO_CHAR + num;
@@ -19745,7 +19832,7 @@ function padNumber(num, digits, trim) {
}
-function dateGetter(name, size, offset, trim) {
+function dateGetter(name, size, offset, trim, negWrap) {
offset = offset || 0;
return function(date) {
var value = date['get' + name]();
@@ -19753,14 +19840,15 @@ function dateGetter(name, size, offset, trim) {
value += offset;
}
if (value === 0 && offset == -12) value = 12;
- return padNumber(value, size, trim);
+ return padNumber(value, size, trim, negWrap);
};
}
-function dateStrGetter(name, shortForm) {
+function dateStrGetter(name, shortForm, standAlone) {
return function(date, formats) {
var value = date['get' + name]();
- var get = uppercase(shortForm ? ('SHORT' + name) : name);
+ var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
+ var get = uppercase(propPrefix + name);
return formats[get][value];
};
@@ -19815,13 +19903,14 @@ function longEraGetter(date, formats) {
}
var DATE_FORMATS = {
- yyyy: dateGetter('FullYear', 4),
- yy: dateGetter('FullYear', 2, 0, true),
- y: dateGetter('FullYear', 1),
+ yyyy: dateGetter('FullYear', 4, 0, false, true),
+ yy: dateGetter('FullYear', 2, 0, true, true),
+ y: dateGetter('FullYear', 1, 0, false, true),
MMMM: dateStrGetter('Month'),
MMM: dateStrGetter('Month', true),
MM: dateGetter('Month', 2, 1),
M: dateGetter('Month', 1, 1),
+ LLLL: dateStrGetter('Month', false, true),
dd: dateGetter('Date', 2),
d: dateGetter('Date', 1),
HH: dateGetter('Hours', 2),
@@ -19847,7 +19936,7 @@ var DATE_FORMATS = {
GGGG: longEraGetter
};
-var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
+var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
NUMBER_STRING = /^\-?\d+$/;
/**
@@ -19867,6 +19956,7 @@ var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|
* * `'MMM'`: Month in year (Jan-Dec)
* * `'MM'`: Month in year, padded (01-12)
* * `'M'`: Month in year (1-12)
+ * * `'LLLL'`: Stand-alone month in year (January-December)
* * `'dd'`: Day in month, padded (01-31)
* * `'d'`: Day in month (1-31)
* * `'EEEE'`: Day in Week,(Sunday-Saturday)
@@ -21548,8 +21638,8 @@ var ngFormDirective = formDirectiveFactory(true);
ngModelMinErr: false,
*/
-// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
-var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
+// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
+var ISO_DATE_REGEXP = /^\d{4,}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+(?:[+-][0-2]\d:[0-5]\d|Z)$/;
// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
// Note: We are being more lenient, because browsers are too.
// 1. Scheme
@@ -21565,12 +21655,18 @@ var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-
var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
-var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
-var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
-var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
-var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
+var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
+var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
+var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
+var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
+var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
+var PARTIAL_VALIDATION_TYPES = createMap();
+forEach('date,datetime-local,month,time,week'.split(','), function(type) {
+ PARTIAL_VALIDATION_TYPES[type] = true;
+});
+
var inputType = {
/**
@@ -22657,6 +22753,8 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
});
}
+ var timeout;
+
var listener = function(ev) {
if (timeout) {
$browser.defer.cancel(timeout);
@@ -22686,8 +22784,6 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
if ($sniffer.hasEvent('input')) {
element.on('input', listener);
} else {
- var timeout;
-
var deferListener = function(ev, input, origValue) {
if (!timeout) {
timeout = $browser.defer(function() {
@@ -22719,6 +22815,26 @@ function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
// or form autocomplete on newer browser, we need "change" event to catch it
element.on('change', listener);
+ // Some native input types (date-family) have the ability to change validity without
+ // firing any input/change events.
+ // For these event types, when native validators are present and the browser supports the type,
+ // check for validity changes on various DOM events.
+ if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
+ element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
+ if (!timeout) {
+ var validity = this[VALIDITY_STATE_PROPERTY];
+ var origBadInput = validity.badInput;
+ var origTypeMismatch = validity.typeMismatch;
+ timeout = $browser.defer(function() {
+ timeout = null;
+ if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
+ listener(ev);
+ }
+ });
+ }
+ });
+ }
+
ctrl.$render = function() {
// Workaround for Firefox validation #12102.
var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
@@ -23739,9 +23855,10 @@ function classDirective(name, selector) {
* new classes added.
*
* @animations
- * **add** - happens just before the class is applied to the elements
- *
- * **remove** - happens just before the class is removed from the element
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
+ * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
*
* @element ANY
* @param {expression} ngClass {@link guide/expression Expression} to eval. The result
@@ -25000,8 +25117,10 @@ forEach(
* and `leave` effects.
*
* @animations
- * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
- * leave - happens just before the `ngIf` contents are removed from the DOM
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
+ * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
*
* @element ANY
* @scope
@@ -25042,7 +25161,7 @@ forEach(
*/
-var ngIfDirective = ['$animate', function($animate) {
+var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
return {
multiElement: true,
transclude: 'element',
@@ -25058,7 +25177,7 @@ var ngIfDirective = ['$animate', function($animate) {
if (!childScope) {
$transclude(function(clone, newScope) {
childScope = newScope;
- clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
+ clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
@@ -25113,8 +25232,10 @@ var ngIfDirective = ['$animate', function($animate) {
* access on some browsers.
*
* @animations
- * enter - animation is used to bring new content into the browser.
- * leave - animation is used to animate existing content away.
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
+ * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
*
* The enter and leave animation occur concurrently.
*
@@ -25882,7 +26003,7 @@ var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$
* the `$viewValue` are different from last time.
*
* Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
- * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
+ * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
* or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
* invoked if you only change a property on the objects.
*/
@@ -27517,14 +27638,20 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
var option = options.getOptionFromViewValue(value);
if (option && !option.disabled) {
+ // Don't update the option when it is already selected.
+ // For example, the browser will select the first option by default. In that case,
+ // most properties are set automatically - except the `selected` attribute, which we
+ // set always
+
if (selectElement[0].value !== option.selectValue) {
removeUnknownOption();
removeEmptyOption();
selectElement[0].value = option.selectValue;
option.element.selected = true;
- option.element.setAttribute('selected', 'selected');
}
+
+ option.element.setAttribute('selected', 'selected');
} else {
if (value === null || providedEmptyOption) {
removeUnknownOption();
@@ -28083,17 +28210,23 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* ...
* ```
*
- * You need to be aware that the JavaScript specification does not define the order of keys
- * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
- * used to sort the keys alphabetically.)
+ * However, there are a limitations compared to array iteration:
*
- * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
- * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
- * keys in the order in which they were defined, although there are exceptions when keys are deleted
- * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
+ * - The JavaScript specification does not define the order of keys
+ * returned for an object, so Angular relies on the order returned by the browser
+ * when running `for key in myObj`. Browsers generally follow the strategy of providing
+ * keys in the order in which they were defined, although there are exceptions when keys are deleted
+ * and reinstated. See the
+ * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
*
- * If this is not desired, the recommended workaround is to convert your object into an array
- * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
+ * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
+ * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
+ *
+ * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
+ * objects, and will throw if used with one.
+ *
+ * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
+ * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
* do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
* or implement a `$watch` on the object yourself.
*
@@ -28211,11 +28344,11 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
* as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
*
* @animations
- * **.enter** - when a new item is added to the list or when an item is revealed after a filter
- *
- * **.leave** - when an item is removed from the list or when an item is filtered out
- *
- * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
+ * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
+ * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
*
* See the example below for defining CSS animations with ngRepeat.
*
@@ -28363,7 +28496,7 @@ var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale,
*/
-var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
+var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
var NG_REMOVED = '$$NG_REMOVED';
var ngRepeatMinErr = minErr('ngRepeat');
@@ -28398,7 +28531,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
$$tlb: true,
compile: function ngRepeatCompile($element, $attr) {
var expression = $attr.ngRepeat;
- var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
+ var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
@@ -28562,7 +28695,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
if (getBlockStart(block) != nextNode) {
// existing item which got moved
- $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
+ $animate.move(getBlockNodes(block.clone), null, previousNode);
}
previousNode = getBlockEnd(block);
updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
@@ -28574,8 +28707,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var endNode = ngRepeatEndComment.cloneNode(false);
clone[clone.length++] = endNode;
- // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
- $animate.enter(clone, null, jqLite(previousNode));
+ $animate.enter(clone, null, previousNode);
previousNode = endNode;
// Note: We only need the first/last node of the cloned nodes.
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
@@ -28678,12 +28810,14 @@ var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
- * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
*
* @element ANY
* @param {expression} ngShow If the {@link guide/expression expression} is truthy
@@ -28842,12 +28976,15 @@ var ngShowDirective = ['$animate', function($animate) {
* .my-element.ng-hide-remove.ng-hide-remove-active { ... }
* ```
*
- * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
+ * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
* property to block during animation states--ngAnimate will handle the style toggling automatically for you.
*
* @animations
- * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
- * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
+ * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
+ *
*
* @element ANY
* @param {expression} ngHide If the {@link guide/expression expression} is truthy then
@@ -29009,8 +29146,10 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
*
* @animations
- * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
- * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
+ * | Animation | Occurs |
+ * |----------------------------------|-------------------------------------|
+ * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
+ * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
*
* @usage
*
@@ -29109,7 +29248,7 @@ var ngStyleDirective = ngDirective(function(scope, element, attr) {
*/
-var ngSwitchDirective = ['$animate', function($animate) {
+var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
return {
require: 'ngSwitch',
@@ -29150,7 +29289,7 @@ var ngSwitchDirective = ['$animate', function($animate) {
selectedTransclude.transclude(function(caseElement, selectedScope) {
selectedScopes.push(selectedScope);
var anchor = selectedTransclude.element;
- caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
+ caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
var block = { clone: caseElement };
selectedElements.push(block);
@@ -29232,7 +29371,7 @@ var ngSwitchDefaultDirective = ngDirective({
*
*
*
- *
{{text}}
+ *
{{text}}
*
*
*
@@ -30266,7 +30405,9 @@ var minlengthDirective = function() {
if (window.angular.bootstrap) {
//AngularJS is already loaded, so we can return here...
- console.log('WARNING: Tried to load angular more than once.');
+ if (window.console) {
+ console.log('WARNING: Tried to load angular more than once.');
+ }
return;
}
diff --git a/OurUmbraco.Site/umbraco/lib/tinymce/plugins/codemirror/source.html b/OurUmbraco.Site/umbraco/lib/tinymce/plugins/codemirror/source.html
index 807abbed..70cecb77 100644
--- a/OurUmbraco.Site/umbraco/lib/tinymce/plugins/codemirror/source.html
+++ b/OurUmbraco.Site/umbraco/lib/tinymce/plugins/codemirror/source.html
@@ -210,7 +210,7 @@ function submit()
if (findDepth(curLineHTML, cc) !== 0) {
// Cursor is inside a , don't set cursor:
curLineHTML = curLineHTML.replace(cc, '');
- doc.setLine(pos.line, curLineHTML);
+ doc.getLineHandle(pos.line).text = curLineHTML;
}
// Submit HTML to TinyMCE:
diff --git a/OurUmbraco.Site/umbraco_client/CodeArea/UmbracoEditor.js b/OurUmbraco.Site/umbraco_client/CodeArea/UmbracoEditor.js
index 8f0faf2a..96ef28f5 100644
--- a/OurUmbraco.Site/umbraco_client/CodeArea/UmbracoEditor.js
+++ b/OurUmbraco.Site/umbraco_client/CodeArea/UmbracoEditor.js
@@ -35,9 +35,18 @@ Umbraco.Sys.registerNamespace("Umbraco.Controls.CodeEditor");
this._control.val(code);
}
else {
- //this is a wrapper for CodeMirror
+ //this is a wrapper for CodeMirror
this._editor.focus();
+
+ var codeChanged = this._editor.getValue() != code;
+ var cursor = this._editor.doc.getCursor();
+
this._editor.setValue(code);
+
+ // Restore cursor position if the server saved code matches.
+ if (!codeChanged)
+ this._editor.setCursor(cursor);
+
this._editor.focus();
}
},
diff --git a/OurUmbraco.Site/umbraco_client/Dialogs/SortDialog.js b/OurUmbraco.Site/umbraco_client/Dialogs/SortDialog.js
index a78d8687..fe13968d 100644
--- a/OurUmbraco.Site/umbraco_client/Dialogs/SortDialog.js
+++ b/OurUmbraco.Site/umbraco_client/Dialogs/SortDialog.js
@@ -22,11 +22,11 @@
s = s.replace(/\-/g, "/");
//all of these basically transform the string into year-month-day since that
//is what JS understands when creating a Date object
- if (c.dateFormat.indexOf("dd/MM/yyyy") == 0 || c.dateFormat.indexOf("dd-MM-yyyy") == 0) {
- s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3-$2-$1");
+ if (c.dateFormat.indexOf("dd/MM/yyyy") == 0 || c.dateFormat.indexOf("dd-MM-yyyy") == 0 || c.dateFormat.indexOf("dd.MM.yyyy") == 0) {
+ s = s.replace(/(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{4})/, "$3-$2-$1");
}
- else if (c.dateFormat.indexOf("dd/MM/yy") == 0 || c.dateFormat.indexOf("dd-MM-yy") == 0) {
- s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$3-$2-$1");
+ else if (c.dateFormat.indexOf("dd/MM/yy") == 0 || c.dateFormat.indexOf("dd-MM-yy") == 0 || c.dateFormat.indexOf("dd.MM.yy") == 0) {
+ s = s.replace(/(\d{1,2})[\/\-\.](\d{1,2})[\/\-\.](\d{2})/, "$3-$2-$1");
}
else if (c.dateFormat.indexOf("MM/dd/yyyy") == 0 || c.dateFormat.indexOf("MM-dd-yyyy") == 0) {
s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3-$1-$2");
@@ -118,4 +118,4 @@
-})(jQuery);
\ No newline at end of file
+})(jQuery);
diff --git a/OurUmbraco.Site/umbraco_client/Editors/EditView.js b/OurUmbraco.Site/umbraco_client/Editors/EditView.js
index 96ea4d14..87b180ce 100644
--- a/OurUmbraco.Site/umbraco_client/Editors/EditView.js
+++ b/OurUmbraco.Site/umbraco_client/Editors/EditView.js
@@ -174,6 +174,9 @@
}
if (args.contents) {
UmbEditor.SetCode(args.contents);
+ } else if (!this.IsSimpleEditor) {
+ // Restore focuse to text region. SetCode also does this.
+ UmbEditor._editor.focus();
}
UmbClientMgr.mainTree().setActiveTreeType(this._opts.currentTreeType);
diff --git a/OurUmbraco.Site/web.template.config b/OurUmbraco.Site/web.template.config
index 4e437b8b..ad94ca23 100644
--- a/OurUmbraco.Site/web.template.config
+++ b/OurUmbraco.Site/web.template.config
@@ -37,7 +37,7 @@
Umbraco web.config configuration documentation can be found here:
http://our.umbraco.org/documentation/using-umbraco/config-files/#webconfig
-->
-
+
diff --git a/OurUmbraco/OurUmbraco.csproj b/OurUmbraco/OurUmbraco.csproj
index 395bb42a..8449e133 100644
--- a/OurUmbraco/OurUmbraco.csproj
+++ b/OurUmbraco/OurUmbraco.csproj
@@ -38,8 +38,8 @@
..\packages\AutoMapper.3.0.0\lib\net40\AutoMapper.Net4.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\businesslogic.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\businesslogic.dll
True
@@ -50,12 +50,12 @@
..\packages\ClientDependency-Mvc5.1.8.0.0\lib\net45\ClientDependency.Core.Mvc.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\cms.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\cms.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\controls.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\controls.dll
True
@@ -86,8 +86,8 @@
..\packages\ImageProcessor.Web.4.5.3.0\lib\net45\ImageProcessor.Web.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\interfaces.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\interfaces.dll
True
@@ -95,7 +95,7 @@
True
- ..\packages\UmbracoCms.Core.7.4.1\lib\log4net.dll
+ ..\packages\UmbracoCms.Core.7.4.2\lib\log4net.dll
True
@@ -111,7 +111,7 @@
True
- ..\packages\UmbracoCms.Core.7.4.1\lib\Microsoft.ApplicationBlocks.Data.dll
+ ..\packages\UmbracoCms.Core.7.4.2\lib\Microsoft.ApplicationBlocks.Data.dll
True
@@ -170,8 +170,8 @@
..\packages\semver.1.1.2\lib\net451\Semver.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\SQLCE4Umbraco.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\SQLCE4Umbraco.dll
True
@@ -180,11 +180,11 @@
- ..\packages\UmbracoCms.Core.7.4.1\lib\System.Data.SqlServerCe.dll
+ ..\packages\UmbracoCms.Core.7.4.2\lib\System.Data.SqlServerCe.dll
True
- ..\packages\UmbracoCms.Core.7.4.1\lib\System.Data.SqlServerCe.Entity.dll
+ ..\packages\UmbracoCms.Core.7.4.2\lib\System.Data.SqlServerCe.Entity.dll
True
@@ -222,7 +222,7 @@
..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.dll
True
-
+
..\packages\Microsoft.AspNet.WebPages.3.2.3\lib\net45\System.Web.WebPages.Deployment.dll
True
@@ -237,39 +237,39 @@
- ..\packages\UmbracoCms.Core.7.4.1\lib\TidyNet.dll
+ ..\packages\UmbracoCms.Core.7.4.2\lib\TidyNet.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\umbraco.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\Umbraco.Core.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Core.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\umbraco.DataLayer.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.DataLayer.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\umbraco.editorControls.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.editorControls.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\umbraco.MacroEngines.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.MacroEngines.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\umbraco.providers.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\umbraco.providers.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\Umbraco.Web.UI.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\Umbraco.Web.UI.dll
True
-
- ..\packages\UmbracoCms.Core.7.4.1\lib\UmbracoExamine.dll
+
+ ..\packages\UmbracoCms.Core.7.4.2\lib\UmbracoExamine.dll
True
diff --git a/OurUmbraco/packages.config b/OurUmbraco/packages.config
index 1ff28d78..4209fab5 100644
--- a/OurUmbraco/packages.config
+++ b/OurUmbraco/packages.config
@@ -37,7 +37,7 @@
-
+